/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <stdlib.h>
#include <grp.h>
#include <note.h>
#include "ldap_common.h"
#include <alloca.h> /* alloca */
/* String which may need to be removed from beginning of group password */
/* AD-specific, used in search filter to resolve group memberships. */
/* Group attributes filters */
/*
* These filters are used to get all the containing groups (immediate parents)
* of a user by searching with memberUid (user name) and member/uniqueMember
* DNs (user's full DN) in just one ldap search request. The reason why
* the uniqueMember part is repeated is to cover different servers' behavior.
* Some allow a wild card search to match DNs with optional unique identifier
* and some would only match DN exactly (i.e., optional unique ID not allowed).
*/
/*
* These filters are used to get all the containing groups (immediate parents)
* of a group by searching with member and uniqueMember DNs (user's full DN)
* in just one ldap search request. The reason why the uniqueMember part is
* repeated is the same as the above filters.
*/
/*
* The next two filters are used to retrieve entries from AD. Both cause
* server side group expansion. The first one asks for all group member
* entries to be returned in just one search. The second one requests
* all parent groups (including nested ones) to be returned in one search.
*/
/* Each reallocation adds _PSEARCH_BUF_BLK more bytes to a packed buffer */
/*
* Max. number of recursive calls to go down to find child groups
* or to go up to find all the parent groups. This limit only
* affects the users who use the ODSEE or OpenLDAP servers.
* AD is not affected due to the LDAP_MATCHING_RULE_IN_CHAIN
* server-side group expansion.
*/
/*
* The values of the member and uniqueMember attributes are in DN
* syntax, and specify either user members or group members. If group,
* then they are nested groups. To evaluate group membership that is
* needs to do dn2uid searches (by calling __ns_ldap_read_dn) with the
* member DNs to obtain the names of the user members or the DNs of
* the nested groups. For nested groups, the dn2uid searches will be
* done recursively until all members have been resolved to user names
* or until the maximum level of recursion has been reached. To
* improve performance, the dn2uid search results need to be cached.
*
* nss_ldap will resolve the group membership specified by any or all
* of the memberUid, member, and uniqueMember attributes. The result
* will be that a group has membership that is the union of all three
* with duplicates removed.
*
* A dn2uid search with a user DN would return the name of the user.
* A dn2uid search with a group DN may return a series of user names
* of group DNs (group members of the group).
*
* The result of dn2uid search is placed in the data section of the
* packed buffer. See below where the nscd dn2uid cache is described
* about how the nscd is tapped into to cache the dn2uid search results.
*
* The packed result of the dn2uid search has up to four sections:
* - a structure, _dn2uid_res_t
* - a series of user names
* - a base DN
*
* The _dn2uid_res_t structure has the following elements:
* -- ocType
* indicates a user or group entry
* -- users
* number of user names that follows this structure
* -- dns
* Number of DNs that follows the base DN. These DNs
* are either the DN of a user or that of a nested group.
* If a DN has the same base DN as that stored in this
* structure, it will be truncated to have only the non-base
* DN part, with a ',' added to the end to indicate that it's
* a short version.
* -- basedn_len
* length of the base DN
* -- dnlen_max
* max. length of member DN. This is used to allocate one shared
* space big enough to hold the member DNs one at a time.
* -- basedn_offset
* Where in the buffer the base DN starts. The first
* byte of this structure is offset 0.
* -- next_byte_offset
* Where in the buffer to add the next data item. The first
* byte of this structure is offset 0.
* -- dbuf_size
* Current size of the data area in the packed buffer. This
* size includes the size of the _dn2uid_res_t structure.
*
* terminated strings. If the member/uniqueMember attributes
* are not used, the user name list is created entirely from the
* values of the memberuid attribute returned in the group
* search. Otherwise, it could also be from the user name (uid
* attribute) of the dn2uid search of a user DN. The base DN
* or uniqueMember attributes in the search results.
*
* So the data layout in the data section of the packed buffer is
* as follows:
*
* struct [ "username1" [ "username2 " ...]] ["baseDN"]
* [ "user or group DN 1" [ "user or group DN 2" ...]]
*
*/
typedef struct {
int ocType;
int users;
int dns;
int basedn_len;
int dnlen_max;
/*
* When processing a getGroupsbyMember request, nss_ldap first
* translates the given member (a user name) into the DN of the
* user entry. It then does a group search with a filter
* (|(memberuid=<user name>)((member=<user DN>)(uniqueMember=<user DN>))
* and asks for the gidNumber, memberUid, member, uniqueMember, and
* memberOf attributes. The returned groups are all the groups having
* this user as a member. The gidNumber of the groups will be added
* to the gid number list to be returned as the output. The present of
* the memberOf attribute in a group entry is used as an indication that
* the group is a member of a nested group, so the DN of the group
* will be used to do further memberof searches. This will be done
* recursively until all parent groups are found or the maximum level
* of recursion is reached.
*
* The result of a memberof search is placed in the data section
* of the packed buffer. The packed result of the search has up to
* three sections:
* - a structure, _memberof_res_t
* - a base DN
* - a series of gid number and group DN pairs
*
* The _memberof_res_t structure has the following elements:
* -- parents
* number of the containing groups
* -- group_dns
* number of group DNs that need a further group search
* -- basedn_len
* length of the base DN
* -- dnlen_max
* max. length of group DN. This is used to allocate one shared
* space big enough to hold the group DNs one at a time.
* -- no_sort_search
* indicate a no sorting search should be done next
* -- basedn_offset
* Where in the buffer the base DN starts. The first
* byte of this structure is offset 0.
* -- next_byte_offset
* Where in the buffer to add the next data item. The first
* byte of this structure is offset 0.
* -- dbuf_size
* Current size of the data area in the packed buffer. This
* size includes the size of the _memberof_res_t structure.
*
* containing group found. The gid umber will always be set but the DN
* of the group may be an empty string, which means no further group
* search is necessary. The gid numbers and group DNs are null-terminated
* strings. The DNs are also truncated if they have the same base DNs
* as that stored in the _memberof_res_t structure.
*
* So the layout of the search result in the data section of the
* packed buffer is as follows:
*
* struct ["baseDN"] [ "gidnumber1" "groupDN1" [ "gidnumber1" "groupDN2" ...]]
*/
typedef struct {
int parents;
int group_dns;
int basedn_len;
int dnlen_max;
/*
* gr_attrs lists the attributes needed for generating a group(4) entry.
*/
static const char *gr_attrs[] = {
(char *)NULL
};
/* gr_attrs2 lists the group attributes that may need schema mapping. */
static const char *gr_attrs2[] = {
(char *)NULL
};
/*
* gr_attrs3 is for processing the member DN list from a dn2uid lookup.
* Do not change the order, _G_UNIQUEMEMBER should always be the second one.
*/
static const char *gr_attrs3[] = {
(char *)NULL
};
/*
* gr_attrs4 is used for schema mapping a group entry returned
* from a dn2uid lookup.
*/
static const char *gr_attrs4[] = {
(char *)NULL
};
/*
* gr_attrs5 is used when searching groups with both memberuid and DN.
* The presence of any memberOf attributes indicates whether a group
* is a nested one.
*/
static const char *gr_attrs5[] = {
(char *)NULL
};
/*
* gr_attrs6 is used when searching groups with a member DN.
* The presence of any memberOf attributes indicates whether
* a group is a nested one.
*/
static const char *gr_attrs6[] = {
(char *)NULL
};
/* pw_attrs is used to translate a user's uid to DN */
static const char *pw_attrs[] = {
"uid",
(char *)NULL
};
/* Use this attribute to ask for server type from libsldap. */
static const char *gr_extra_info_attr[] = {
"__ns_ldap_op_attr_server_type",
(char *)NULL
};
nss_XbyY_args_t *);
#pragma weak _nss_cache_create
extern nss_status_t _nss_cache_create(char *, int, int, int);
#pragma weak _nss_cache_get
int, char *);
int, char *);
/* nss_ldap's dn2uid cache */
/* nss_ldap's memberof cache */
/*
* For memberof cache search, the UID tag indicates a search with a uid
* as the key is being performed. The GROUP_DN tag indicates a group DN
* as the key. GROUP_DN_NS indicates a group DN key but with no result
* sorting on the server side.
*/
/*
* userdata consumed by the TLE_group_member_cb callback function
*/
typedef struct {
int cnt;
/*
* userdata consumed by the TLE_memberof_cb callback function
*/
typedef struct {
void **bufpp;
/*
* attribute list used by an AD member Transitive Link
* Expansion search
*/
static const char *gr_attrs_mbr_tl[] = {
(char *)NULL
};
/*
* attribute list used by an AD memberof Transitive Link
* Expansion search
*/
static const char *gr_attrs_mof_tl[] = {
(char *)NULL
};
/* Test to see if string 'a' begins with string 'b'. */
static boolean_t
strbw(const char *a, const char *b)
{
}
/*
* See if string 'a' ends with string 'b', and is longer.
* Return the pointer to where in 'a' string 'b' begins.
*/
static char *
strew(const char *a, const char *b)
{
char *p;
p = strstr(a, b);
return (p);
return (NULL);
}
/* Test to see if a group is a nested group. */
static boolean_t
{
/*
* Use the memberOf attribute (or for DSEE, isMemberOf) to
* determine if this group is a nested group. If present,
* then yes.
*/
return (B_TRUE);
return (B_TRUE);
return (B_FALSE);
}
void
{
char **temptr;
return;
}
}
/* Loop and compare to see if an item is in a counted list. */
static boolean_t
{
int i;
for (i = 0; i < count; i++) {
return (B_TRUE);
}
return (B_FALSE);
}
/*
* The dn2uid cache uses the caching features of nscd if nscd
* is enabled.
*
* How it works:
* The nss backend taps into nscd cache using 2 APIs:
* _nss_cache_create - to init a backend cache
* _nss_cache_get - to search the cache. This API requires 2 callbacks
* getkey - performs the nss_packed_getkey operation
* psearch- performs the nss_psearch operation
* The expectation is that get receives a properly formed packed buffer
* and processes the specific search request, packing the results into
* the buffer. psearch performs the actual lookup, while getkey returns
* the necessary key information for psearch and for cache test operations.
*
* So that the system does not fail if nscd is disabled, the real APIs
* are provided as weak symbols to APIs that return error on create
*
* The format of the result packing is project private and subject to
* change. See the comments on the structures, _dn2uid_res_t and
* _memberof_res_t, above for the layout of the search results.
*
*/
/*
* Local _nss_cache_get performs the actual lookup if not running in the
* nscd process.
*/
/*ARGSUSED0*/
static nss_status_t
{
return (NSS_ERROR);
/* fake _nss_cache_get just calls psearch (no caching) */
}
/*
* Setup the dn2uid cache if this is the first time call.
*/
static void
{
/* Return if the dn2uid cache setup has been done of tried. */
return;
(void) mutex_lock(&dn2uid_init_lock);
/* Return if just done of tried. */
if (dn2uid_cache_state != NSS_LDAP_CACHE_UNINITED) {
(void) mutex_unlock(&dn2uid_init_lock);
return;
}
/* Not running in nscd ? */
if (_nss_cache_create == NULL) {
} else {
} else
}
(void) mutex_unlock(&dn2uid_init_lock);
}
/*
* Setup the memberof cache if this is the first time call.
*/
static void
{
/* Return if the memberof cache setup has been done of tried. */
return;
(void) mutex_lock(&memberof_init_lock);
/* Return if just done of tried. */
if (memberof_cache_state != NSS_LDAP_CACHE_UNINITED) {
(void) mutex_unlock(&memberof_init_lock);
return;
}
/* Not running in nscd ? */
if (_nss_cache_create == NULL) {
} else {
} else
}
(void) mutex_unlock(&memberof_init_lock);
}
/* Find the search key from the packed buffer header. */
static nss_status_t
{
return (NSS_ERROR);
return (NSS_SUCCESS);
}
/* Add a member uid to the member list. */
static int
{
/* Add member to the list */
if (rc == NSS_LDAP_LIST_EXISTED)
/* It is fine if member is already in the list */
return (1);
else if (rc != NSS_LDAP_LIST_SUCCESS)
return (-1);
return (1);
}
/*
* Add a dn2uid data string 'str' to the data area in the packed buffer,
* '*bufpp', expand the packed buffer if necessary.
*/
static nss_status_t
{
char *bptr;
int slen;
/* expand the buffer if not enough room to add the string */
void *tmp;
return (NSS_ERROR);
/* adjust pointers */
/* Cast to void * eliminates lint warning */
/* adjust buffer length */
left += _PSEARCH_BUF_BLK;
}
return (NSS_SUCCESS);
}
/* Generate an attributed mapped attr list to be used by a dn2uid lookup. */
static char **
{
int i;
char **app;
char **amap;
continue;
return (NULL);
else
free2dArray(&app);
return (NULL);
}
free2dArray(&amap);
}
return (app);
}
/* Perform schema mapping on a group entry returned from a dn2uid lookup. */
static nss_status_t
char **in_attrs)
{
int i, j, cnt;
char **omap;
char *attrname;
/* Make sure it's a group entry. */
else
/* not a posixGroup entry, return NSS_NOTFOUND */
rc = NSS_NOTFOUND;
goto cleanup;
}
/*
* Find the memberUid, member, and uniqueMember attributes,
* that could be schema mapped to use a different name, to
* change the name back. gr_attrs4[] contains "memberUid",
* "member", and "uniqueMember". gr_attrs2[] contains the
* name of the attributes that may be schema mapped.
* in_attrs[] contains the corresponding mapped gr_attrs2[]
* names that may be seen in the result group entry.
*/
mapped_attr = NULL;
continue;
continue;
mapped_attr = in_attrs[j];
break;
}
if (mapped_attr == NULL)
continue;
for (j = 0; j < entry->attr_count; j++) {
goto cleanup;
}
} else
break;
}
}
}
free2dArray(&omap);
return (rc);
}
/*
* A value for the uniqueMember attribute is a DN followed
* by an optional hash (#) and uniqueIdentifier.
* For example, uniqueMember: cn=aaa,dc=bbb,dc=ccc #1234
* This function remove the optional # and uniqueIdentifier.
*/
static void
{
return;
/* invalid DN if no '=' and '#' should be after all RDNs */
return;
/* Remove '# ID' */
*hash = '\0';
}
/*
* Perform a dn2uid lookup using the input packed buffer.
* A dn2uid lookup does an LDAP search with the input
* user or group DN and returns the dn2uid result data
* in the data area of the packed buffer. See the comments
* above about the specific of a dn2uid lookup and the layout
* of the dn2uid result data.
*/
static void
{
const char *dn;
char **classes;
char **mapping;
char *bptr;
int mi;
int oc_type;
char **param;
if (ret != NSS_SUCCESS) {
return;
}
/* malformed/improperly sized key/dn */
return;
}
/*
* Get the DN entry from the server. Specify 'passwd' as the service
* first, in case this DN is that of a posixAccount (user) entry, so
* that schema mapping for the passwd service will be done by libsldap.
* Also need to map the requested group attributes here, in case this DN
* points to a posixGroup (group) entry, in which case, libsldap won't
* do the schema mapping because the service specified is not 'group'.
* We do this extra schema mapping because we want to try to get
* the user or group entry in just one call to __ns_ldap_read_dn().
*/
/*
* There is schema mapping for the group database, so map the
* group attributes so that the ldap server knows which attrs
* to return.
*/
if (search_attrs == NULL) {
goto cleanup;
}
}
(const char **)search_attrs,
if (rc != NS_LDAP_SUCCESS) {
if (rc == NS_LDAP_NOTFOUND)
ret = NSS_NOTFOUND;
else {
}
goto cleanup;
}
/* posixAccount or posixGroup dn ? */
ret = NSS_NOTFOUND;
goto cleanup;
}
} else {
/*
* Not a posixAccount entry, should be a posixGroup one.
* Perform schema mapping as it's not done by libsldap.
*/
if (rc != NSS_SUCCESS) {
goto cleanup;
}
} else {
ret = NSS_NOTFOUND;
goto cleanup;
}
}
}
/* pack up and return results */
/* found a posixAccount (user) dn, pack up the user name */
}
} else
ret = NSS_NOTFOUND;
goto cleanup;
}
/* found a posixGroup dn, pack up the users and dns */
for (i = 0; i < cnt; i++) {
} else
goto cleanup;
}
}
/* for attrs: _G_MEMBER & _G_UNIQUEMEMBER */
continue;
/* Process member (DN) */
/*
* get the configured base DN so that it's not repeated
* in every member DN saved
*/
if (pres->basedn_len == 0) {
if (rc == NS_LDAP_SUCCESS) {
/* point to start of base DN */
(void) __ns_ldap_freeParam(
(void ***)¶m);
} else
goto cleanup;
} else
(void) __ns_ldap_freeError(&ep);
}
for (i = 0; i < cnt; i++) {
/* use non-basedn part if possible */
/*
* If uniqueMember, drop the optional uniqueIdentifier.
*/
if (mi == 1)
remove_uniqueID((char *)dn);
if (pres->basedn_len != 0) {
int dnlen;
char *p;
/*
* remember max. DN length seen so far
*/
/* if dn ends with basedn */
/*
* This last ',' denotes basedn.
*/
*p = ',';
*(p + 1) = '\0';
}
}
} else
goto cleanup;
}
}
/* Return NSS_NOTFOUND if no new data added. */
ret = NSS_SUCCESS;
} else {
/*
* No group member found. Returning NSS_NOTFOUND
* makes it a negative nscd cache if running in
* nscd.
*/
ret = NSS_NOTFOUND;
}
if (search_attrs != NULL)
(void) __ns_ldap_freeResult(&result);
}
/* Set up a packed buffer and perform a cache search. */
static nss_status_t
{
/* space needed for cache name: cache_name_len_round_up */
/* space needed for tag and key: keylen_round_up */
}
/*
* Space needed for the packed buffer header:
* sizeof (nss_pheader_t) + sizeof (nss_dbd_t) +
* cache_name_len_round_up + keylen_round_up.
*
* Total space needed: packed buffer header size +
* requested data size (start_size). See code below
* for the consturction of the packed buffer header.
*/
return (NSS_ERROR);
/* Populate the packed buffer with a request */
/* packed header */
off = sizeof (nss_pheader_t);
/* nss_dbd_t */
/* cache name */
/* tag and key */
} else
off += keylen_round_up;
/* perform the cache search */
nscd_cache, "ldap");
if (res == NSS_SUCCESS)
else
return (res);
}
/*
* Given a group or member (user) DN, search the nscd group or passwd cache
* to find a group or passwd entry with a matching DN.
*/
static nss_status_t
{
int rc;
/*
* Check to see if the member or group is already in nscd cache.
* Search key is the rdn value.
*/
*key = '\0';
/* skip rdn attr type and use the attr value as the search key */
*ep = '\0';
*ep = ',';
}
}
if (*key == '\0')
return (NSS_NOTFOUND);
/* try search user first */
/*
* If user found in nscd cache and with the same DN,
* add it to the member list.
*/
if (res == NSS_SUCCESS) {
/*
* The optional DN string is right after the regular database
* result string. pbuf->data_len indicates the sum of these
* two strings. If it's not larger than the length of the
* first string, then there is no optional DN.
*/
/* skip server type */
dn_matched = B_TRUE;
}
}
if (dn_matched) {
if (rc < 0)
return (NSS_ERROR);
} else
return (NSS_NOTFOUND);
return (NSS_SUCCESS);
}
/* next try searching group */
/*
* If group found in nscd cache and with the same DN,
* add all members of the group to the member list.
*/
if (res == NSS_SUCCESS) {
/*
* The optional DN string is right after the regular database
* result string. pbuf->data_len indicates the sum of these
* two strings. If it's not larger than the length of the
* first string, then there is no optional DN.
*/
/* skip server type */
dn_matched = B_TRUE;
}
}
if (dn_matched) {
res = NSS_SUCCESS;
mp++;
*ep = '\0';
if (*mp == '\0')
continue;
break;
}
}
return (res);
}
}
return (NSS_NOTFOUND);
}
/*
* Given a member DN, find the corresponding user, or if the DN is that
* of a group, find all the members recursively until the maximum level
* of recursion is reached. The users or groups could be from the nscd
* cache or from the ldap servers.
*/
static nss_status_t
{
char *basedn;
int i, mdn_len;
/*
* If the depth of recursion reaches DN2UID_MAX_RECURSIVE_CALL
* then stop and return NSS_SUCCESS.
*/
*rec_callp += 1;
if (*rec_callp == DN2UID_MAX_RECURSIVE_CALL) {
res = NSS_SUCCESS;
goto out;
}
/* create the dn2uid nscd backend cache if not done yet */
/*
* For loop detection, check to see if dn has already been
* processed by adding it to the dnlist. It not able to add,
* then it has been processed or error occurred, ignore it.
*/
res = NSS_SUCCESS;
goto out;
}
/*
* If running in nscd, check to see if member or
* group is already in nscd cache.
*/
if (_nss_cache_create != NULL) {
if (res != NSS_NOTFOUND)
goto out;
}
/*
* Check to see if dn has already been searched and in nscd's
* dn2uid cache. If not running in nscd, then the local
* _nss_cache_get will be called to search the DN directly.
*/
if (res != NSS_SUCCESS)
goto out;
/* a single user id; add it to the member list */
goto out;
}
} else { /* a group entry */
/* add all users to the member list */
goto out;
}
}
/*
* If base DN is present, set pointer to it and
* go pass it to process the next set of data,
* group DNs.
*/
if (pres->basedn_len > 0) {
}
/*
* If there are group DNs, get all members recursively
* by calling _nss_ldap_proc_memberDN again.
*/
/*
* Restore member DN if it was truncated to save
* space.
*/
/*
* Get a work buffer from the stack
* that can be reused.
*/
/* drop the last ',' */
}
goto out;
}
}
/* If no new data added, return NSS_NOTFOUND. */
res = NSS_NOTFOUND;
else
res = NSS_SUCCESS;
out:
*rec_callp -= 1;
return (res);
}
/*
* TLE_group_member_cb is a callback function callable by libsldap
* to process each one of the member entres returned by an AD
* Transitive Link Expansion search that request all members of
* a group.
*/
int
{
int cnt;
char **username;
char **classes;
/* posixAccount or posixGroup dn ? */
return (NS_LDAP_NOTFOUND);
/* Not an posixAccount entry, skip. */
return (NS_LDAP_CB_NEXT);
}
/* No user name, just ignore the entry. */
return (NS_LDAP_CB_NEXT);
}
return (NS_LDAP_OP_FAILED);
}
return (NS_LDAP_CB_NEXT);
}
/*
* Perform an AD Transitive Link Expansion search to get all members
* of a group entry. All member entries including those of the child
* nested groups will be returned in one search. The callback function
* TLE_group_member_cb is used to process each one of the entries.
*/
static nss_status_t
{
char **dn;
int rc;
int ocnt;
return (NSS_ERROR);
/* Escape any special characters in the dn first. */
return (NSS_NOTFOUND);
searchDN);
return (NSS_NOTFOUND);
(void) __ns_ldap_freeResult(&result);
(void) __ns_ldap_freeError(&error);
/*
* libsldap returns NS_LDAP_NOTFOUND if
* all result entries were consumed by
* the callback function.
*/
return (NSS_SUCCESS);
else
return (NSS_ERROR);
}
/*
* _nss_ldap_memberlist collects all members of a group
* (recursively if necessary) processing all:
* _G_MEMBERUID, G_MEMBER, G_UNIQUEMEMBER
* starting with the current results. Follow on
* dn2uid results may be cached.
*/
static nss_status_t
{
int i, cnt;
int mi;
/* process member uid, attr: _G_MEMBERUID */
for (i = 0; i < cnt; i++) {
return (NSS_ERROR);
}
}
}
/* Determine which type of server the entry is from. */
/*
* If from AD, do a Transitive Link Expansion search
* to get all member entries with just one search.
*/
if (serverType == NS_LDAP_SERVERTYPE_AD) {
if (res == NSS_SUCCESS)
return (NSS_SUCCESS);
}
/* process member (or group) DN, attrs: _G_MEMBER & _G_UNIQUEMEMBER */
/* Process member (DN) */
for (i = 0; i < cnt; i++) {
/*
* If uniqueMember, drop the optional
* uniqueIdentifier.
*/
if (mi == 1)
remove_uniqueID((char *)dn);
/*
* No group member is not an error,
* as groups having no users are allowed.
*/
if (res == NSS_NOTFOUND)
continue;
if (res != NSS_SUCCESS)
return (res);
}
}
}
return (NSS_SUCCESS);
}
/*
* _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.
* e.g.
*
* adm::4:root,adm,daemon
*
*/
static int
{
int nss_result;
char **dn_v;
char *server_type;
gid_nobody_v[0] = gid_nobody;
}
return (NSS_STR_PARSE_PARSE);
}
goto result_grp2str;
}
} else
goto result_grp2str;
}
/* group password could be NULL, replace it with "" */
} else {
/*
* Preen "{crypt}" if necessary.
* If the password does not include the {crypt} prefix
* then the password may be plain text. And thus
* perhaps crypt(3c) should be used to encrypt it.
* Currently the password is copied verbatim.
*/
else
}
goto result_grp2str;
}
/* Validate GID */
gid = gid_nobody_v;
/* Rescursive member list processing */
&rec_call);
if (ret != NSS_SUCCESS) {
goto result_grp2str;
}
/*
* Copy all the members to the output buffer.
* If no members, nss_ldap_list_dump will do
* nothing.
*/
if (list_rc != NSS_LDAP_LIST_SUCCESS &&
list_rc != NSS_LDAP_LIST_NOLIST) {
if (list_rc == NSS_LDAP_LIST_ERANGE)
else
goto result_grp2str;
}
/* The front end marshaller doesn't need the trailing nulls */
} else {
/*
* Save the entry DN as an optional data, if
* dn to uid caching is being performed and
* not processing enumeration results and
* there's enough room in the buffer.
* Format is: (group data followed by optional DN data)
* <group data> + '\0' + '#dn:<server_type>:' + <DN> + '\0'
*/
/* Terminate the primary result. */
*buffer++ = '\0';
buflen--;
if (dn2uid_cache_state == NSS_LDAP_CACHE_INITED &&
&server_type);
len++; /* Include the \0 */
}
}
}
}
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
* entry and marshal the data results into struct group for the frontend
* process. The function _nss_ldap_group2ent performs the data marshaling.
*/
static nss_status_t
{
int ret;
0)
return ((nss_status_t)NSS_NOTFOUND);
return ((nss_status_t)NSS_NOTFOUND);
return ((nss_status_t)NSS_NOTFOUND);
}
/*
* getbygid gets a group entry by number. This function constructs an ldap
* search filter using the name invocation parameter and the getgrgid search
* filter defined. Once the filter is constructed, we searche for a matching
* entry and marshal the data results into struct group for the frontend
* process. The function _nss_ldap_group2ent performs the data marshaling.
*/
static nss_status_t
{
int ret;
return ((nss_status_t)NSS_NOTFOUND);
return ((nss_status_t)NSS_NOTFOUND);
return ((nss_status_t)NSS_NOTFOUND);
}
/*
* setup_buf_memberof_res sets up a control block for doing
* memberof searches. The control block locates at the
* beginning of the result data area in the packed buffer.
*/
static void
{
char *bptr;
void **param;
glen = sizeof (_memberof_res_t);
/* get the configured baseDN */
if (rc == NS_LDAP_SUCCESS) {
(void) __ns_ldap_freeParam(¶m);
} else {
gres->basedn_len = 0;
gres->basedn_offset = 0;
(void) __ns_ldap_freeError(&errorp);
}
}
/*
* Realloc the packed buffer for a memberof search if not
* enough space left.
*/
static void *
{
void *tmp;
return (NULL);
/*
* Buffer pointer may be changed, make sure all related
* data in the packed buffer are updated.
*/
return (tmp);
}
/*
* pack_group_gid_dn adds the gid and DN of a group to the
* result area. If not a nested group, its DN will be stored
* as an empty string, as an indication that parent group
* searching needs not be done.
*/
static int
{
char **gidvalue;
entry_dn = "";
if (nested_group) {
/* store only non-basedn part if possible */
if (gres->basedn_len != 0) {
int dnlen;
char *p;
/*
* remember max. DN length seen so far
*/
/* if entry_dn ends with basedn */
/* This last ',' denotes basedn. */
*p = ',';
*(p + 1) = '\0';
}
}
}
/* get group gid */
return (NSS_STR_PARSE_NO_RESULT);
/* length including 2 '\0' */
return (NSS_STR_PARSE_PARSE);
else
}
entry_dn, '\0');
dlen += gid_dn_slen;
if (*entry_dn != '\0')
return (NSS_STR_PARSE_SUCCESS);
}
/*
* TLE_memberof_cb is a callback function callable by libsldap
* to process each one of the group entries returned by an AD
* memberof Transitive Link Expansion search.
*/
int
{
int rc;
if (rc == NSS_STR_PARSE_SUCCESS)
return (NS_LDAP_CB_NEXT);
else
return (NS_LDAP_OP_FAILED);
}
/*
* key_groupDN_psearch is a memberof psearch function that finds all
* the groups that the group identified by dn is a member of. This
* is used to resolve nested group membership.
*/
static void
{
int i, ret;
/* Escape any special characters in the DN first. */
ret = NSS_NOTFOUND;
goto cleanup;
}
/*
* Do a search to get all the group entries which have the
* group DN as one of the values of its member or uniqueMember
* attribute.
*/
ret = NSS_NOTFOUND;
goto cleanup;
}
ret = NSS_NOTFOUND;
goto cleanup;
}
/*
* Use __ns_ldap_list() if not able to get VLV results from
* the server.
*/
} else {
}
if (ret == NS_LDAP_NOTFOUND) {
ret = NSS_NOTFOUND;
goto cleanup;
} else if (ret != NS_LDAP_SUCCESS) {
goto cleanup;
}
/*
* Set up the group result control block which is at the start
* of result area.
*/
/*
* Store gidnumber and entry DN from each result entry in the
* result area.
*/
}
/* Return NSS_NOTFOUND if no new data added. */
ret = NSS_NOTFOUND;
} else {
ret = NSS_SUCCESS;
}
(void) __ns_ldap_freeResult(&result);
(void) __ns_ldap_freeError(&error);
}
/*
* key_uid_psearch is a memberof psearch function that finds all
* the groups that the user identified by key is a member of.
*/
static void
{
int i, k, ret;
char **membervalue;
char **dn;
return;
}
/*
* Try searching the user in nscd's passwd cache, in case
* user DN is available there.
*/
/*
* If found in nscd cache, get the user's DN and server type
* if available.
*/
if (ret == NSS_SUCCESS) {
int slen;
/* check for optional data's DN tag */
/* first get server type string */
*dptr = '\0';
/* then set server type */
NS_LDAP_ATTR_VAL_SERVER_AD) == 0)
/* user DN follows server type */
}
}
}
/*
* If no user DN found in cache, call __ns_ldap_list_ext to get it.
*/
_F_GETPWNAM, key);
ret = NSS_NOTFOUND;
goto cleanup;
}
key);
ret = NSS_NOTFOUND;
goto cleanup;
}
&extra_info);
if (ret == NS_LDAP_NOTFOUND) {
ret = NSS_NOTFOUND;
goto cleanup;
} else if (ret != NS_LDAP_SUCCESS) {
goto cleanup;
}
goto cleanup;
}
/* Determine which type of server the entry is from. */
if (extra_info != NULL) {
NULL);
extra_info = NULL;
}
}
/*
* Set up the group result control block which is at the start
* of result area.
*/
/* Escape any special characters in the user_dn first. */
ret = NSS_NOTFOUND;
goto cleanup;
}
/*
* If the ldap server is an AD, do a Transitive Link Expansion
* search to get all parent groups in one search.
*/
if (serverType == NS_LDAP_SERVERTYPE_AD) {
ret = NSS_NOTFOUND;
goto cleanup;
}
/*
* Use a callback function to process each result entry.
* userdata is the callback control block.
*/
(void) __ns_ldap_freeResult(&result);
(void) __ns_ldap_freeError(&error);
/*
* Done if no error and have result data.
* libsldap returns NS_LDAP_NOTFOUND if
* all result entries were consumed by
* the callback function.
*/
if (ret == NS_LDAP_NOTFOUND &&
ret = NSS_SUCCESS;
goto cleanup;
}
/*
* Otherwise clean up incomplete result data in the buffer
* by resetting the result control block.
*/
}
}
/*
* Not AD, or the Transitive Link Expansion search didn't work,
* do a search to get all the group entries which have the
* key as a memberuid or user_dn as one of the values of its
* member or uniqueMember attribute.
*/
ret = NSS_NOTFOUND;
goto cleanup;
}
ret = NSS_NOTFOUND;
goto cleanup;
}
/*
* If the ldap server is an openLDAP, don't sort the results
* as there's no order rule for most attributes.
*/
if (serverType == NS_LDAP_SERVERTYPE_OPENLDAP) {
} else {
}
if (ret == NS_LDAP_NOTFOUND) {
ret = NSS_NOTFOUND;
goto cleanup;
} else if (ret != NS_LDAP_SUCCESS) {
goto cleanup;
}
/*
* Store gidnumber and entry DN from each result entry in the
* result area.
*/
/*
* Check to see if this group is found by a matched username.
*/
for (k = 0; k < members->value_count; k++) {
break;
}
}
}
/* Otherwise, found by a matched member/uniqueMember DN ? */
if (!username_matched) {
else {
}
}
/*
* Not matched ? Most likely, unamename not listed as
* one of the memberUid attribute values, skip.
*/
if (!username_matched && !memberDN_matched)
continue;
}
/* Return NSS_NOTFOUND if no new data added. */
ret = NSS_NOTFOUND;
} else {
ret = NSS_SUCCESS;
}
(void) __ns_ldap_freeResult(&result);
(void) __ns_ldap_freeResult(&result1);
(void) __ns_ldap_freeError(&error);
}
/*
* _nss_ldap_memberof_psearch is the top level psearch function for
* memberof searches. The search key stored in the packed buffer
* header is tagged to indicate whether the search key is a member uid
* or group DN. If uid, key_uid_psearch will be called. Otherwise,
* key_groupDN_psearch will be called.
*/
static void
{
const char *key;
char *dbname;
int dbop = 0;
if (ret != NSS_SUCCESS) {
return;
}
/* Searching with a member uid or group DN ? */
/* member uid */
/* group DN */
} else {
return;
}
}
/*
* get_parent_gids find all groups, including nested ones, that a user
* is a member of and write the gid number of these groups in the output
* gid array. It may call itself recursively when dealing with nested
* group. The nest level is limited by MEMBEROF_MAX_RECURSIVE_CALL.
*/
static nss_status_t
{
int i, k;
char *new_tag;
char *gdn;
int gdn_len;
/*
* If the depth of recursion reaches MEMBEROF_MAX_RECURSIVE_CALL
* then stop and return NSS_SUCCESS.
*/
*rec_callp += 1;
if (*rec_callp == MEMBEROF_MAX_RECURSIVE_CALL) {
res = NSS_SUCCESS;
goto out;
}
/* create the memberof nscd backend cache if not done yet */
/*
* For loop detection, check to see if key has already been
* processed by adding it to the grlist. If not able to add,
* then it has been processed or error occurred, ignore it.
*/
res = NSS_SUCCESS;
goto out;
}
/*
* Check to see if memberof result available in nscd's memberof cache.
* If not, get it from the LDAP server.
*/
0, _PSEARCH_BUF_BLK, 0, &buffer,
if (res != NSS_SUCCESS)
goto out;
/* add gids to the output gid array */
if (gres->basedn_len != 0)
/* move pass gid */
/* Skip DN of the group entry, it will be processed later. */
/* no need to add if already in the output */
/* already exists */
break;
}
}
/* ignore duplicate gid */
continue;
break;
}
/*
* Done if no group DNs to process, i.e., no nested groups.
*/
res = NSS_SUCCESS;
goto out;
}
break;
/* group DN always follows the gid string, so move pass gid */
/* if NULL group DN, not a nested group, nothing to do */
if (*gdn == '\0') {
sptr++;
continue;
}
/* Restore group DN if it was truncated to save space. */
if (gres->basedn_len > 0) {
/*
* Get a work buffer from the stack
* that can be reused.
*/
/* drop the last ',' */
/*
* basedn is the first string after the
* control struct
*/
}
}
/*
* If server is an openLDAP, do a no result sorting
* search.
*/
if (gres->no_sort_search)
else
goto out;
/* move pass group DN */
}
res = NSS_SUCCESS;
out:
*rec_callp -= 1;
return (res);
}
/*
* 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
* and storing each group name, gid, etc. Data marshaling is used for
* group processing. The function _nss_ldap_group2ent() performs the
* data marshaling.
*
* (const char *)argp->username; (size_t)strlen(argp->username);
* (gid_t)argp->gid_array; (int)argp->maxgids;
* (int)argp->numgids;
*/
static nss_status_t
{
int gcnt = (int)0;
return ((nss_status_t)NSS_NOTFOUND);
return ((nss_status_t)NSS_NOTFOUND);
return (res);
return ((nss_status_t)NSS_NOTFOUND);
/*
* Return NSS_SUCCESS only if array is full.
* Explained in <nss_dbdefs.h>.
*/
: NSS_NOTFOUND));
}
};
/*ARGSUSED0*/
const char *dummy3)
{
}