Print this page
NEX-14547 Get UNIX group info. from AD/LDAP with partial RFC2307 schema
NEX-13132 smbd dumping core in nss_ldap.so.1`getbymember
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-13132 smbd dumping core in nss_ldap.so.1`getbymember disables ALL shares
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>

*** 35,75 **** /* Group attributes filters */ #define _G_NAME "cn" #define _G_GID "gidnumber" #define _G_PASSWD "userpassword" ! #define _G_MEM "memberuid" #define _F_GETGRNAM "(&(objectClass=posixGroup)(cn=%s))" #define _F_GETGRNAM_SSD "(&(%%s)(cn=%s))" #define _F_GETGRGID "(&(objectClass=posixGroup)(gidNumber=%u))" #define _F_GETGRGID_SSD "(&(%%s)(gidNumber=%u))" - /* - * Group membership can be defined by either username or DN, so when searching - * for groups by member we need to consider both. The first parameter in the - * filter is replaced by username, the second by DN. - */ - #define _F_GETGRMEM \ - "(&(objectClass=posixGroup)(|(memberUid=%s)(memberUid=%s)))" - #define _F_GETGRMEM_SSD "(&(%%s)(|(memberUid=%s)(memberUid=%s)))" /* ! * Copied from getpwnam.c, needed to look up user DN. ! * Would it be better to move to ldap_common.h rather than duplicate? */ ! #define _F_GETPWNAM "(&(objectClass=posixAccount)(uid=%s))" ! #define _F_GETPWNAM_SSD "(&(%%s)(uid=%s))" static const char *gr_attrs[] = { _G_NAME, _G_GID, _G_PASSWD, ! _G_MEM, (char *)NULL }; /* * _nss_ldap_group2str is the data marshaling method for the group getXbyY * (e.g., getgrnam(), getgrgid(), getgrent()) backend processes. This method * is called after a successful ldap search has been performed. This method * will parse the ldap search values into the file format. --- 35,80 ---- /* Group attributes filters */ #define _G_NAME "cn" #define _G_GID "gidnumber" #define _G_PASSWD "userpassword" ! #define _G_MEMUID "memberuid" ! #define _G_MEM_DN "member" /* DN */ #define _F_GETGRNAM "(&(objectClass=posixGroup)(cn=%s))" #define _F_GETGRNAM_SSD "(&(%%s)(cn=%s))" #define _F_GETGRGID "(&(objectClass=posixGroup)(gidNumber=%u))" #define _F_GETGRGID_SSD "(&(%%s)(gidNumber=%u))" /* ! * When searching for groups in which a specified user is a member, ! * there are a few different membership schema that might be in use. ! * We'll use a filter that should work with an of the common ones: ! * "memberUid=NAME", or "member=DN" (try uniquemember too?) ! * The first parameter in the filter string is replaced by username, ! * and the remaining ones by the full DN. */ ! #define _F_GETGRMEM "(&(objectClass=posixGroup)" \ ! "(|(memberUid=%s)(member=%s)))" ! #define _F_GETGRMEM_SSD "(&(%%s)" \ ! "(|(memberUid=%s)(member=%s)))" static const char *gr_attrs[] = { _G_NAME, _G_GID, _G_PASSWD, ! _G_MEMUID, ! _G_MEM_DN, (char *)NULL }; + static int + getmembers_UID(char **bufpp, int *lenp, ns_ldap_attr_t *members); + static int + getmembers_DN(char **bufpp, int *lenp, ns_ldap_attr_t *members); + /* * _nss_ldap_group2str is the data marshaling method for the group getXbyY * (e.g., getgrnam(), getgrgid(), getgrent()) backend processes. This method * is called after a successful ldap search has been performed. This method * will parse the ldap search values into the file format.
*** 83,99 **** _nss_ldap_group2str(ldap_backend_ptr be, nss_XbyY_args_t *argp) { int i; int nss_result; int buflen = 0, len; - int firstime = 1; char *buffer = NULL; ns_ldap_result_t *result = be->result; char **gname, **passwd, **gid, *password, *end; char gid_nobody[NOBODY_STR_LEN]; char *gid_nobody_v[1]; - char *member_str, *strtok_state; ns_ldap_attr_t *members; (void) snprintf(gid_nobody, sizeof (gid_nobody), "%u", GID_NOBODY); gid_nobody_v[0] = gid_nobody; --- 88,102 ----
*** 144,200 **** if (strtoul(gid[0], &end, 10) > MAXUID) gid = gid_nobody_v; len = snprintf(buffer, buflen, "%s:%s:%s:", gname[0], password, gid[0]); TEST_AND_ADJUST(len, buffer, buflen, result_grp2str); ! members = __ns_ldap_getAttrStruct(result->entry, _G_MEM); ! if (members == NULL || members->attrvalue == NULL) { ! /* no member is fine, skip processing the member list */ ! goto nomember; } ! for (i = 0; i < members->value_count; i++) { ! if (members->attrvalue[i] == NULL) { ! nss_result = NSS_STR_PARSE_PARSE; goto result_grp2str; } ! /* ! * If we find an '=' in the member attribute value, treat it as ! * a DN, otherwise as a username. */ ! if (member_str = strchr(members->attrvalue[i], '=')) { ! member_str++; /* skip over the '=' */ ! /* Fail if we can't pull a username out of the RDN */ ! if (! (member_str = strtok_r(member_str, ! ",", &strtok_state))) { ! nss_result = NSS_STR_PARSE_PARSE; ! goto result_grp2str; ! } ! } else { member_str = members->attrvalue[i]; ! } ! if (*member_str != '\0') { ! if (firstime) { ! len = snprintf(buffer, buflen, "%s", member_str); ! TEST_AND_ADJUST(len, buffer, buflen, ! result_grp2str); ! firstime = 0; ! } else { ! len = snprintf(buffer, buflen, ",%s", ! member_str); ! TEST_AND_ADJUST(len, buffer, buflen, ! result_grp2str); } } } ! nomember: ! /* The front end marshaller doesn't need the trailing nulls */ ! if (argp->buf.result != NULL) ! be->buflen = strlen(be->buffer); ! result_grp2str: ! (void) __ns_ldap_freeResult(&be->result); return (nss_result); } /* * getbynam gets a group entry by name. This function constructs an ldap --- 147,297 ---- if (strtoul(gid[0], &end, 10) > MAXUID) gid = gid_nobody_v; len = snprintf(buffer, buflen, "%s:%s:%s:", gname[0], password, gid[0]); TEST_AND_ADJUST(len, buffer, buflen, result_grp2str); ! members = __ns_ldap_getAttrStruct(result->entry, _G_MEMUID); ! if (members != NULL && members->attrvalue != NULL) { ! nss_result = getmembers_UID(&buffer, &buflen, members); ! if (nss_result != 0) ! goto result_grp2str; } ! members = __ns_ldap_getAttrStruct(result->entry, _G_MEM_DN); ! if (members != NULL && members->attrvalue != NULL) { ! nss_result = getmembers_DN(&buffer, &buflen, members); ! if (nss_result != 0) goto result_grp2str; } ! ! /* The front end marshaller doesn't need the trailing nulls */ ! if (argp->buf.result != NULL) ! be->buflen = strlen(be->buffer); ! result_grp2str: ! (void) __ns_ldap_freeResult(&be->result); ! return (nss_result); ! } ! ! /* ! * Process the list values from the "memberUid" attribute of the ! * current group. Note that this list is often empty, and we ! * get the real list of members via getmember_DN (see below). */ ! static int ! getmembers_UID(char **bufpp, int *lenp, ns_ldap_attr_t *members) ! { ! char *member_str, *strtok_state; ! char *buffer; ! int buflen; ! int i, len; ! int nss_result = 0; ! int firsttime; ! ! buffer = *bufpp; ! buflen = *lenp; ! firsttime = (buffer[-1] == ':'); ! ! for (i = 0; i < members->value_count; i++) { member_str = members->attrvalue[i]; ! if (member_str == NULL) ! goto out; ! ! #ifdef DEBUG ! (void) fprintf(stdout, "getmembers_UID: uid=<%s>\n", member_str); ! #endif ! /* ! * If not a valid Unix user name, or ! * not valid in ldap, just skip. ! */ ! if (member_str[0] == '\0' || ! strpbrk(member_str, " ,:=") != NULL) ! continue; ! ! if (firsttime) ! len = snprintf(buffer, buflen, "%s", member_str); ! else ! len = snprintf(buffer, buflen, ",%s", member_str); ! TEST_AND_ADJUST(len, buffer, buflen, out); } + + out: + *bufpp = buffer; + *lenp = buflen; + return (nss_result); + } + + /* + * Process the list values from the "member" attribute of the + * current group. Note that this list is ONLY one that can be + * assumed to be non-empty. The problem here is that this list + * contains the list of members as "distinguished names" (DN), + * and we want the Unix names (known here as "uid"). We must + * lookup the "uid" for each DN in the member list. Example: + * CN=Doe\, John,OU=Users,DC=contoso,DC=com => john.doe + */ + static int + getmembers_DN(char **bufpp, int *lenp, ns_ldap_attr_t *members) + { + ns_ldap_error_t *error = NULL; + char *member_dn, *member_uid; + char *buffer; + int buflen; + int i, len; + int nss_result = 0; + int firsttime; + + buffer = *bufpp; + buflen = *lenp; + firsttime = (buffer[-1] == ':'); + + for (i = 0; i < members->value_count; i++) { + member_dn = members->attrvalue[i]; + if (member_dn == NULL) + goto out; + + /* + * The attribute name was "member", so these should be + * full distinguisned names (DNs). We need to loookup + * the Unix UID (name) for each. + */ + #ifdef DEBUG + (void) fprintf(stdout, "getmembers_DN: dn=%s\n", + member_dn); + #endif + if (member_dn[0] == '\0') + continue; + + nss_result = __ns_ldap_dn2uid(member_dn, + &member_uid, NULL, &error); + if (nss_result != NS_LDAP_SUCCESS) { + (void) __ns_ldap_freeError(&error); + error = NULL; + continue; } + #ifdef DEBUG + (void) fprintf(stdout, "getmembers_DN: uid=<%s>\n", + member_uid); + #endif + /* Skip invalid names. */ + if (member_uid[0] == '\0' || + strpbrk(member_uid, " ,:=") != NULL) { + free(member_uid); + continue; } ! ! if (firsttime) ! len = snprintf(buffer, buflen, "%s", member_uid); ! else ! len = snprintf(buffer, buflen, ",%s", member_uid); ! free(member_uid); ! TEST_AND_ADJUST(len, buffer, buflen, out); ! } ! ! out: ! *bufpp = buffer; ! *lenp = buflen; return (nss_result); } /* * getbynam gets a group entry by name. This function constructs an ldap
*** 265,274 **** --- 362,383 ---- } /* + * Use a custom attributes list for getbymember, because the LDAP + * query for this requests a list of groups, and the result can be + * very large if it includes the list of members with each group. + * We don't need or want the list of members in this case. + */ + static const char *grbymem_attrs[] = { + _G_NAME, /* cn */ + _G_GID, /* gidnumber */ + (char *)NULL + }; + + /* * getbymember returns all groups a user is defined in. This function * uses different architectural procedures than the other group backend * system calls because it's a private interface. This function constructs * an ldap search filter using the name invocation parameter. Once the * filter is constructed, we search for all matching groups counting
*** 282,408 **** */ static nss_status_t getbymember(ldap_backend_ptr be, void *a) { int i, j, k; int gcnt = (int)0; ! char **groupvalue, **membervalue, *member_str; ! char *strtok_state; nss_status_t lstat; struct nss_groupsbymem *argp = (struct nss_groupsbymem *)a; char searchfilter[SEARCHFILTERLEN]; char userdata[SEARCHFILTERLEN]; char name[SEARCHFILTERLEN]; ns_ldap_result_t *result; ns_ldap_entry_t *curEntry; ! char *username, **dn_attr, *dn; gid_t gid; ! int ret; if (strcmp(argp->username, "") == 0 || strcmp(argp->username, "root") == 0) return ((nss_status_t)NSS_NOTFOUND); if (_ldap_filter_name(name, argp->username, sizeof (name)) != 0) return ((nss_status_t)NSS_NOTFOUND); - ret = snprintf(searchfilter, sizeof (searchfilter), _F_GETPWNAM, name); - if (ret >= sizeof (searchfilter) || ret < 0) - return ((nss_status_t)NSS_NOTFOUND); - - ret = snprintf(userdata, sizeof (userdata), _F_GETPWNAM_SSD, name); - if (ret >= sizeof (userdata) || ret < 0) - return ((nss_status_t)NSS_NOTFOUND); - /* * Look up the user DN in ldap. If it's not found, search solely by * username. */ ! lstat = (nss_status_t)_nss_ldap_nocb_lookup(be, NULL, ! _PASSWD, searchfilter, NULL, _merge_SSD_filter, userdata); ! if (lstat != (nss_status_t)NS_LDAP_SUCCESS) ! return ((nss_status_t)lstat); ! ! if (be->result == NULL || ! !(dn_attr = __ns_ldap_getAttr(be->result->entry, "dn"))) dn = name; ! else ! dn = dn_attr[0]; ! ret = snprintf(searchfilter, sizeof (searchfilter), _F_GETGRMEM, name, ! dn); ! if (ret >= sizeof (searchfilter) || ret < 0) return ((nss_status_t)NSS_NOTFOUND); ! ! ret = snprintf(userdata, sizeof (userdata), _F_GETGRMEM_SSD, name, ! dn); ! if (ret >= sizeof (userdata) || ret < 0) return ((nss_status_t)NSS_NOTFOUND); /* ! * Free up resources from user DN search before performing group ! * search. */ - (void) __ns_ldap_freeResult((ns_ldap_result_t **)&be->result); - - gcnt = (int)argp->numgids; lstat = (nss_status_t)_nss_ldap_nocb_lookup(be, NULL, ! _GROUP, searchfilter, NULL, _merge_SSD_filter, userdata); if (lstat != (nss_status_t)NS_LDAP_SUCCESS) return ((nss_status_t)lstat); if (be->result == NULL) return (NSS_NOTFOUND); ! username = (char *)argp->username; result = (ns_ldap_result_t *)be->result; curEntry = (ns_ldap_entry_t *)result->entry; ! for (i = 0; i < result->entries_count && curEntry != NULL; i++) { ! membervalue = __ns_ldap_getAttr(curEntry, "memberUid"); ! if (membervalue == NULL) { ! curEntry = curEntry->next; ! continue; ! } ! for (j = 0; membervalue[j]; j++) { /* ! * If we find an '=' in the member attribute ! * value, treat it as a DN, otherwise as a ! * username. */ ! if (member_str = strchr(membervalue[j], '=')) { ! member_str++; /* skip over the '=' */ ! member_str = strtok_r(member_str, ",", ! &strtok_state); ! } else { ! member_str = membervalue[j]; ! } ! if (member_str != NULL && ! strcmp(member_str, username) == 0) { ! groupvalue = __ns_ldap_getAttr(curEntry, ! "gidnumber"); ! if (groupvalue == NULL || ! groupvalue[0] == NULL) { /* Drop this group from the list */ ! break; } errno = 0; ! gid = (gid_t)strtol(groupvalue[0], ! (char **)NULL, 10); ! if (errno == 0 && ! argp->numgids < argp->maxgids) { for (k = 0; k < argp->numgids; k++) { ! if (argp->gid_array[k] == gid) ! /* already exists */ ! break; } - if (k == argp->numgids) - argp->gid_array[argp->numgids++] - = gid; } ! break; } ! } curEntry = curEntry->next; } (void) __ns_ldap_freeResult((ns_ldap_result_t **)&be->result); if (gcnt == argp->numgids) --- 391,499 ---- */ static nss_status_t getbymember(ldap_backend_ptr be, void *a) { + ns_ldap_error_t *error = NULL; int i, j, k; int gcnt = (int)0; ! char **groupvalue; nss_status_t lstat; struct nss_groupsbymem *argp = (struct nss_groupsbymem *)a; char searchfilter[SEARCHFILTERLEN]; char userdata[SEARCHFILTERLEN]; char name[SEARCHFILTERLEN]; + char escdn[SEARCHFILTERLEN]; ns_ldap_result_t *result; ns_ldap_entry_t *curEntry; ! char *dn; gid_t gid; ! int ret1, ret2; if (strcmp(argp->username, "") == 0 || strcmp(argp->username, "root") == 0) return ((nss_status_t)NSS_NOTFOUND); if (_ldap_filter_name(name, argp->username, sizeof (name)) != 0) return ((nss_status_t)NSS_NOTFOUND); /* * Look up the user DN in ldap. If it's not found, search solely by * username. */ ! lstat = __ns_ldap_uid2dn(name, &dn, NULL, &error); ! if (lstat != (nss_status_t)NS_LDAP_SUCCESS) { ! /* Can't get DN. Use bare name */ ! (void) __ns_ldap_freeError(&error); dn = name; ! } ! /* Note: must free dn if != name */ ! /* ! * Compose filter patterns ! */ ! ret1 = snprintf(searchfilter, sizeof (searchfilter), ! _F_GETGRMEM, name, dn); ! ret2 = snprintf(userdata, sizeof (userdata), ! _F_GETGRMEM_SSD, name, dn); ! if (dn != name) ! free(dn); ! if (ret1 >= sizeof (searchfilter) || ret1 < 0) return ((nss_status_t)NSS_NOTFOUND); ! if (ret2 >= sizeof (userdata) || ret2 < 0) return ((nss_status_t)NSS_NOTFOUND); /* ! * Query for groups matching the filter. */ lstat = (nss_status_t)_nss_ldap_nocb_lookup(be, NULL, ! _GROUP, searchfilter, grbymem_attrs, ! _merge_SSD_filter, userdata); if (lstat != (nss_status_t)NS_LDAP_SUCCESS) return ((nss_status_t)lstat); if (be->result == NULL) return (NSS_NOTFOUND); ! ! /* ! * Walk the query result, collecting GIDs. ! */ result = (ns_ldap_result_t *)be->result; curEntry = (ns_ldap_entry_t *)result->entry; ! gcnt = (int)argp->numgids; ! for (i = 0; i < result->entries_count; i++) { ! /* ! * Does this group have a gidNumber attr? */ ! groupvalue = __ns_ldap_getAttr(curEntry, _G_GID); ! if (groupvalue == NULL || groupvalue[0] == NULL) { /* Drop this group from the list */ ! goto next_group; } + + /* + * Convert it to a numeric GID + */ errno = 0; ! gid = (gid_t)strtol(groupvalue[0], (char **)NULL, 10); ! if (errno != 0) ! goto next_group; ! /* ! * If we don't already have this GID, add it. ! */ ! if (argp->numgids < argp->maxgids) { for (k = 0; k < argp->numgids; k++) { ! if (argp->gid_array[k] == gid) { ! /* already have it */ ! goto next_group; } } ! argp->gid_array[argp->numgids++] = gid; } ! ! next_group: curEntry = curEntry->next; } (void) __ns_ldap_freeResult((ns_ldap_result_t **)&be->result); if (gcnt == argp->numgids)