diff options
Diffstat (limited to 'usr/src/lib/nsswitch/ldap/common/getgrent.c')
| -rw-r--r-- | usr/src/lib/nsswitch/ldap/common/getgrent.c | 355 |
1 files changed, 223 insertions, 132 deletions
diff --git a/usr/src/lib/nsswitch/ldap/common/getgrent.c b/usr/src/lib/nsswitch/ldap/common/getgrent.c index 291d16dbc6..e65a741efe 100644 --- a/usr/src/lib/nsswitch/ldap/common/getgrent.c +++ b/usr/src/lib/nsswitch/ldap/common/getgrent.c @@ -37,36 +37,41 @@ #define _G_NAME "cn" #define _G_GID "gidnumber" #define _G_PASSWD "userpassword" -#define _G_MEM "memberuid" +#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))" -/* - * 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? + * 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_GETPWNAM "(&(objectClass=posixAccount)(uid=%s))" -#define _F_GETPWNAM_SSD "(&(%%s)(uid=%s))" +#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_MEM, + _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 @@ -85,13 +90,11 @@ _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); @@ -146,48 +149,20 @@ _nss_ldap_group2str(ldap_backend_ptr be, nss_XbyY_args_t *argp) 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; + 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; } - for (i = 0; i < members->value_count; i++) { - if (members->attrvalue[i] == NULL) { - nss_result = NSS_STR_PARSE_PARSE; + 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; - } - /* - * 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); @@ -197,6 +172,128 @@ result_grp2str: } /* + * 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 distinguished 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 * search filter using the name invocation parameter and the getgrnam search * filter defined. Once the filter is constructed, we searche for a matching @@ -267,6 +364,18 @@ getbygid(ldap_backend_ptr be, void *a) /* + * 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 @@ -284,20 +393,21 @@ getbygid(ldap_backend_ptr be, void *a) 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, **membervalue, *member_str; - char *strtok_state; + 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 *username, **dn_attr, *dn; + char *dn; gid_t gid; - int ret; + int ret1, ret2; if (strcmp(argp->username, "") == 0 || strcmp(argp->username, "root") == 0) @@ -306,101 +416,82 @@ getbymember(ldap_backend_ptr be, void *a) 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"))) + 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; - else - dn = dn_attr[0]; + } + /* Note: must free dn if != name */ - ret = snprintf(searchfilter, sizeof (searchfilter), _F_GETGRMEM, name, - dn); - if (ret >= sizeof (searchfilter) || ret < 0) + /* + * 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); - - ret = snprintf(userdata, sizeof (userdata), _F_GETGRMEM_SSD, name, - dn); - if (ret >= sizeof (userdata) || ret < 0) + if (ret2 >= sizeof (userdata) || ret2 < 0) return ((nss_status_t)NSS_NOTFOUND); /* - * Free up resources from user DN search before performing group - * search. + * Query for groups matching the filter. */ - (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); + _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); - username = (char *)argp->username; + + /* + * Walk the query result, collecting GIDs. + */ 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; + 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; } - 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; + + /* + * 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; } - break; } + argp->gid_array[argp->numgids++] = gid; } + + next_group: curEntry = curEntry->next; } @@ -431,7 +522,7 @@ static ldap_backend_op_t gr_ops[] = { /*ARGSUSED0*/ nss_backend_t * _nss_ldap_group_constr(const char *dummy1, const char *dummy2, - const char *dummy3) + const char *dummy3) { return ((nss_backend_t *)_nss_ldap_constr(gr_ops, |
