2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <stdlib.h>
2N/A#include <grp.h>
2N/A#include <note.h>
2N/A#include "ldap_common.h"
2N/A#include <alloca.h> /* alloca */
2N/A
2N/A/* String which may need to be removed from beginning of group password */
2N/A#define _CRYPT "{CRYPT}"
2N/A#define _NO_PASSWD_VAL ""
2N/A
2N/A/* AD-specific, used in search filter to resolve group memberships. */
2N/A#define LDAP_MATCHING_RULE_IN_CHAIN_OID "1.2.840.113556.1.4.1941"
2N/A
2N/A/* Group attributes filters */
2N/A#define _G_NAME "cn"
2N/A#define _G_UID "uid"
2N/A#define _G_GIDNUMBER "gidNumber"
2N/A#define _G_PASSWD "userPassword"
2N/A#define _G_MEMBERUID "memberUid"
2N/A#define _G_MEMBER "member"
2N/A#define _G_UNIQUEMEMBER "uniqueMember"
2N/A#define _G_MEMBEROF "memberOf"
2N/A#define _G_MEMBEROF_ODSEE "isMemberOf"
2N/A#define _G_OBJECTCLASS "objectClass"
2N/A
2N/A#define _OC_ACCOUNT "posixAccount"
2N/A#define _OC_GROUP "posixGroup"
2N/A
2N/A#define _F_GETGRNAM "(&(objectClass=posixGroup)(cn=%s))"
2N/A#define _F_GETGRNAM_SSD "(&(%%s)(cn=%s))"
2N/A#define _F_GETGRGID "(&(objectClass=posixGroup)(gidNumber=%u))"
2N/A#define _F_GETGRGID_SSD "(&(%%s)(gidNumber=%u))"
2N/A#define _F_GETGRMEM "(&(objectClass=posixGroup)(memberUid=%s))"
2N/A#define _F_GETGRMEM_SSD "(&(%%s)(memberUid=%s))"
2N/A
2N/A/*
2N/A * These filters are used to get all the containing groups (immediate parents)
2N/A * of a user by searching with memberUid (user name) and member/uniqueMember
2N/A * DNs (user's full DN) in just one ldap search request. The reason why
2N/A * the uniqueMember part is repeated is to cover different servers' behavior.
2N/A * Some allow a wild card search to match DNs with optional unique identifier
2N/A * and some would only match DN exactly (i.e., optional unique ID not allowed).
2N/A */
2N/A#define _UID_MBR_UMBR "(member=%s)(uniqueMember=%s)(uniqueMember=%s*)"
2N/A#define _MUID_MBRS "(|(memberUid=%s)" _UID_MBR_UMBR ")"
2N/A#define _F_GETGRUSR "(&(objectClass=posixGroup)" _MUID_MBRS ")"
2N/A#define _F_GETGRUSR_SSD "(&(%%s)" _MUID_MBRS ")"
2N/A
2N/A/*
2N/A * These filters are used to get all the containing groups (immediate parents)
2N/A * of a group by searching with member and uniqueMember DNs (user's full DN)
2N/A * in just one ldap search request. The reason why the uniqueMember part is
2N/A * repeated is the same as the above filters.
2N/A */
2N/A#define _CN_MBR_UMBR "(|(member=%s)(uniqueMember=%s)(uniqueMember=%s*))"
2N/A#define _F_GETGRGRP "(&(objectClass=posixGroup)" _CN_MBR_UMBR ")"
2N/A#define _F_GETGRGRP_SSD "(&(%%s)" _CN_MBR_UMBR ")"
2N/A
2N/A/*
2N/A * The next two filters are used to retrieve entries from AD. Both cause
2N/A * server side group expansion. The first one asks for all group member
2N/A * entries to be returned in just one search. The second one requests
2N/A * all parent groups (including nested ones) to be returned in one search.
2N/A */
2N/A#define _F_GETGRMEM_BL "(memberOf:" LDAP_MATCHING_RULE_IN_CHAIN_OID ":=%s)"
2N/A#define _F_GETGRMOF_BL "(member:" LDAP_MATCHING_RULE_IN_CHAIN_OID ":=%s)"
2N/A
2N/A/* Each reallocation adds _PSEARCH_BUF_BLK more bytes to a packed buffer */
2N/A#define _PSEARCH_BUF_BLK (16 * 1024)
2N/A
2N/A/*
2N/A * Max. number of recursive calls to go down to find child groups
2N/A * or to go up to find all the parent groups. This limit only
2N/A * affects the users who use the ODSEE or OpenLDAP servers.
2N/A * AD is not affected due to the LDAP_MATCHING_RULE_IN_CHAIN
2N/A * server-side group expansion.
2N/A */
2N/A#define DN2UID_MAX_RECURSIVE_CALL 20
2N/A#define MEMBEROF_MAX_RECURSIVE_CALL 20
2N/A
2N/A#define _OC_ACCOUNT_T 1
2N/A#define _OC_GROUP_T 2
2N/A
2N/A/*
2N/A * The values of the member and uniqueMember attributes are in DN
2N/A * syntax, and specify either user members or group members. If group,
2N/A * then they are nested groups. To evaluate group membership that is
2N/A * defined using the member and/or uniqueMember attributes, nss_ldap
2N/A * needs to do dn2uid searches (by calling __ns_ldap_read_dn) with the
2N/A * member DNs to obtain the names of the user members or the DNs of
2N/A * the nested groups. For nested groups, the dn2uid searches will be
2N/A * done recursively until all members have been resolved to user names
2N/A * or until the maximum level of recursion has been reached. To
2N/A * improve performance, the dn2uid search results need to be cached.
2N/A *
2N/A * nss_ldap will resolve the group membership specified by any or all
2N/A * of the memberUid, member, and uniqueMember attributes. The result
2N/A * will be that a group has membership that is the union of all three
2N/A * with duplicates removed.
2N/A *
2N/A * A dn2uid search with a user DN would return the name of the user.
2N/A * A dn2uid search with a group DN may return a series of user names
2N/A * a series of user DNs (user members of the group), and/or a series
2N/A * of group DNs (group members of the group).
2N/A *
2N/A * The result of dn2uid search is placed in the data section of the
2N/A * packed buffer. See below where the nscd dn2uid cache is described
2N/A * about how the nscd is tapped into to cache the dn2uid search results.
2N/A *
2N/A * The packed result of the dn2uid search has up to four sections:
2N/A * - a structure, _dn2uid_res_t
2N/A * - a series of user names
2N/A * - a base DN
2N/A * - a series of (short or full user/group) DNs
2N/A *
2N/A * The _dn2uid_res_t structure has the following elements:
2N/A * -- ocType
2N/A * indicates a user or group entry
2N/A * -- users
2N/A * number of user names that follows this structure
2N/A * -- dns
2N/A * Number of DNs that follows the base DN. These DNs
2N/A * are either the DN of a user or that of a nested group.
2N/A * If a DN has the same base DN as that stored in this
2N/A * structure, it will be truncated to have only the non-base
2N/A * DN part, with a ',' added to the end to indicate that it's
2N/A * a short version.
2N/A * -- basedn_len
2N/A * length of the base DN
2N/A * -- dnlen_max
2N/A * max. length of member DN. This is used to allocate one shared
2N/A * space big enough to hold the member DNs one at a time.
2N/A * -- basedn_offset
2N/A * Where in the buffer the base DN starts. The first
2N/A * byte of this structure is offset 0.
2N/A * -- next_byte_offset
2N/A * Where in the buffer to add the next data item. The first
2N/A * byte of this structure is offset 0.
2N/A * -- dbuf_size
2N/A * Current size of the data area in the packed buffer. This
2N/A * size includes the size of the _dn2uid_res_t structure.
2N/A *
2N/A * The user names, base DN, and user/group DNs are all null
2N/A * terminated strings. If the member/uniqueMember attributes
2N/A * are not used, the user name list is created entirely from the
2N/A * values of the memberuid attribute returned in the group
2N/A * search. Otherwise, it could also be from the user name (uid
2N/A * attribute) of the dn2uid search of a user DN. The base DN
2N/A * and the user/group DNs will only be set if there are member
2N/A * or uniqueMember attributes in the search results.
2N/A *
2N/A * So the data layout in the data section of the packed buffer is
2N/A * as follows:
2N/A *
2N/A * struct [ "username1" [ "username2 " ...]] ["baseDN"]
2N/A * [ "user or group DN 1" [ "user or group DN 2" ...]]
2N/A *
2N/A */
2N/Atypedef struct {
2N/A int ocType;
2N/A int users;
2N/A int dns;
2N/A int basedn_len;
2N/A int dnlen_max;
2N/A size_t basedn_offset;
2N/A size_t next_byte_offset;
2N/A size_t dbuf_size;
2N/A} _dn2uid_res_t;
2N/A
2N/A/*
2N/A * When processing a getGroupsbyMember request, nss_ldap first
2N/A * translates the given member (a user name) into the DN of the
2N/A * user entry. It then does a group search with a filter
2N/A * (|(memberuid=<user name>)((member=<user DN>)(uniqueMember=<user DN>))
2N/A * and asks for the gidNumber, memberUid, member, uniqueMember, and
2N/A * memberOf attributes. The returned groups are all the groups having
2N/A * this user as a member. The gidNumber of the groups will be added
2N/A * to the gid number list to be returned as the output. The present of
2N/A * the memberOf attribute in a group entry is used as an indication that
2N/A * the group is a member of a nested group, so the DN of the group
2N/A * will be used to do further memberof searches. This will be done
2N/A * recursively until all parent groups are found or the maximum level
2N/A * of recursion is reached.
2N/A *
2N/A * The result of a memberof search is placed in the data section
2N/A * of the packed buffer. The packed result of the search has up to
2N/A * three sections:
2N/A * - a structure, _memberof_res_t
2N/A * - a base DN
2N/A * - a series of gid number and group DN pairs
2N/A *
2N/A * The _memberof_res_t structure has the following elements:
2N/A * -- parents
2N/A * number of the containing groups
2N/A * -- group_dns
2N/A * number of group DNs that need a further group search
2N/A * -- basedn_len
2N/A * length of the base DN
2N/A * -- dnlen_max
2N/A * max. length of group DN. This is used to allocate one shared
2N/A * space big enough to hold the group DNs one at a time.
2N/A * -- no_sort_search
2N/A * indicate a no sorting search should be done next
2N/A * -- basedn_offset
2N/A * Where in the buffer the base DN starts. The first
2N/A * byte of this structure is offset 0.
2N/A * -- next_byte_offset
2N/A * Where in the buffer to add the next data item. The first
2N/A * byte of this structure is offset 0.
2N/A * -- dbuf_size
2N/A * Current size of the data area in the packed buffer. This
2N/A * size includes the size of the _memberof_res_t structure.
2N/A *
2N/A * In the gidNumber/groupDN pair section, there is one pair for each
2N/A * containing group found. The gid umber will always be set but the DN
2N/A * of the group may be an empty string, which means no further group
2N/A * search is necessary. The gid numbers and group DNs are null-terminated
2N/A * strings. The DNs are also truncated if they have the same base DNs
2N/A * as that stored in the _memberof_res_t structure.
2N/A *
2N/A * So the layout of the search result in the data section of the
2N/A * packed buffer is as follows:
2N/A *
2N/A * struct ["baseDN"] [ "gidnumber1" "groupDN1" [ "gidnumber1" "groupDN2" ...]]
2N/A */
2N/Atypedef struct {
2N/A int parents;
2N/A int group_dns;
2N/A int basedn_len;
2N/A int dnlen_max;
2N/A boolean_t no_sort_search;
2N/A size_t basedn_offset;
2N/A size_t next_byte_offset;
2N/A size_t dbuf_size;
2N/A} _memberof_res_t;
2N/A
2N/A/*
2N/A * gr_attrs lists the attributes needed for generating a group(4) entry.
2N/A */
2N/Astatic const char *gr_attrs[] = {
2N/A _G_NAME,
2N/A _G_GIDNUMBER,
2N/A _G_PASSWD,
2N/A _G_MEMBERUID,
2N/A _G_MEMBER,
2N/A _G_UNIQUEMEMBER,
2N/A _G_OBJECTCLASS,
2N/A (char *)NULL
2N/A};
2N/A
2N/A/* gr_attrs2 lists the group attributes that may need schema mapping. */
2N/Astatic const char *gr_attrs2[] = {
2N/A _G_UID,
2N/A _G_MEMBERUID,
2N/A _G_MEMBER,
2N/A _G_UNIQUEMEMBER,
2N/A _G_OBJECTCLASS,
2N/A (char *)NULL
2N/A};
2N/A
2N/A/*
2N/A * gr_attrs3 is for processing the member DN list from a dn2uid lookup.
2N/A * Do not change the order, _G_UNIQUEMEMBER should always be the second one.
2N/A */
2N/Astatic const char *gr_attrs3[] = {
2N/A _G_MEMBER,
2N/A _G_UNIQUEMEMBER,
2N/A (char *)NULL
2N/A};
2N/A
2N/A/*
2N/A * gr_attrs4 is used for schema mapping a group entry returned
2N/A * from a dn2uid lookup.
2N/A */
2N/Astatic const char *gr_attrs4[] = {
2N/A _G_MEMBERUID,
2N/A _G_MEMBER,
2N/A _G_UNIQUEMEMBER,
2N/A (char *)NULL
2N/A};
2N/A
2N/A/*
2N/A * gr_attrs5 is used when searching groups with both memberuid and DN.
2N/A * The presence of any memberOf attributes indicates whether a group
2N/A * is a nested one.
2N/A */
2N/Astatic const char *gr_attrs5[] = {
2N/A _G_GIDNUMBER,
2N/A _G_MEMBERUID,
2N/A _G_MEMBER,
2N/A _G_UNIQUEMEMBER,
2N/A _G_MEMBEROF,
2N/A _G_MEMBEROF_ODSEE,
2N/A (char *)NULL
2N/A};
2N/A
2N/A/*
2N/A * gr_attrs6 is used when searching groups with a member DN.
2N/A * The presence of any memberOf attributes indicates whether
2N/A * a group is a nested one.
2N/A */
2N/Astatic const char *gr_attrs6[] = {
2N/A _G_GIDNUMBER,
2N/A _G_MEMBEROF,
2N/A _G_MEMBEROF_ODSEE,
2N/A (char *)NULL
2N/A};
2N/A
2N/A/* pw_attrs is used to translate a user's uid to DN */
2N/Astatic const char *pw_attrs[] = {
2N/A "uid",
2N/A (char *)NULL
2N/A};
2N/A
2N/A/* Use this attribute to ask for server type from libsldap. */
2N/Astatic const char *gr_extra_info_attr[] = {
2N/A "__ns_ldap_op_attr_server_type",
2N/A (char *)NULL
2N/A};
2N/A
2N/Atypedef nss_status_t (*getkey_func)(void *, size_t, char **, int *,
2N/A nss_XbyY_args_t *);
2N/Atypedef void (*psearch_func)(void **, size_t);
2N/A
2N/A#pragma weak _nss_cache_create
2N/Aextern nss_status_t _nss_cache_create(char *, int, int, int);
2N/A
2N/A#pragma weak _nss_cache_get
2N/Aextern nss_status_t _nss_cache_get(void **, size_t, getkey_func, psearch_func,
2N/A int, char *);
2N/A
2N/Anss_ldap_cache_state_t dn2uid_cache_state = NSS_LDAP_CACHE_UNINITED;
2N/Astatic mutex_t dn2uid_init_lock = DEFAULTMUTEX;
2N/Anss_ldap_cache_state_t memberof_cache_state = NSS_LDAP_CACHE_UNINITED;
2N/Astatic mutex_t memberof_init_lock = DEFAULTMUTEX;
2N/Astatic nss_status_t (*cache_get)(void **, size_t, getkey_func, psearch_func,
2N/A int, char *);
2N/A
2N/A/* nss_ldap's dn2uid cache */
2N/A#define DN2UID_POS_TTL 600
2N/A#define LDAP_DN2UID_CACHE_NM ":ldap:dn2uid"
2N/A#define LDAP_DN2UID_CACHE_LEN (sizeof (LDAP_DN2UID_CACHE_NM))
2N/A
2N/A/* nss_ldap's memberof cache */
2N/A#define MEMBEROF_POS_TTL 600
2N/A#define LDAP_MEMBEROF_CACHE_NM ":ldap:memberof"
2N/A#define LDAP_MEMBEROF_CACHE_LEN (sizeof (LDAP_MEMBEROF_CACHE_NM))
2N/A
2N/A/*
2N/A * For memberof cache search, the UID tag indicates a search with a uid
2N/A * as the key is being performed. The GROUP_DN tag indicates a group DN
2N/A * as the key. GROUP_DN_NS indicates a group DN key but with no result
2N/A * sorting on the server side.
2N/A */
2N/A#define PSEARCH_TAG_LEN 2
2N/A#define PSEARCH_TAG_UID "u:"
2N/A#define PSEARCH_TAG_GROUP_DN "d:"
2N/A#define PSEARCH_TAG_GROUP_DN_NS "n:"
2N/A
2N/A/*
2N/A * userdata consumed by the TLE_group_member_cb callback function
2N/A */
2N/Atypedef struct {
2N/A _nss_ldap_list_t **listpp;
2N/A int cnt;
2N/A} _member_tl_cb_t;
2N/A
2N/A/*
2N/A * userdata consumed by the TLE_memberof_cb callback function
2N/A */
2N/Atypedef struct {
2N/A void **bufpp;
2N/A _memberof_res_t **grespp;
2N/A nss_pheader_t **pbufpp;
2N/A} _memberof_tl_cb_t;
2N/A
2N/A/*
2N/A * attribute list used by an AD member Transitive Link
2N/A * Expansion search
2N/A */
2N/Astatic const char *gr_attrs_mbr_tl[] = {
2N/A _G_UID,
2N/A _G_OBJECTCLASS,
2N/A (char *)NULL
2N/A};
2N/A
2N/A/*
2N/A * attribute list used by an AD memberof Transitive Link
2N/A * Expansion search
2N/A */
2N/Astatic const char *gr_attrs_mof_tl[] = {
2N/A _G_GIDNUMBER,
2N/A (char *)NULL
2N/A};
2N/A
2N/A/* Test to see if string 'a' begins with string 'b'. */
2N/Astatic boolean_t
2N/Astrbw(const char *a, const char *b)
2N/A{
2N/A return (strncmp(a, b, strlen(b)) == 0);
2N/A}
2N/A
2N/A/*
2N/A * See if string 'a' ends with string 'b', and is longer.
2N/A * Return the pointer to where in 'a' string 'b' begins.
2N/A */
2N/Astatic char *
2N/Astrew(const char *a, const char *b)
2N/A{
2N/A char *p;
2N/A
2N/A p = strstr(a, b);
2N/A if (p != NULL && p[strlen(b)] == '\0' && (p > a))
2N/A return (p);
2N/A return (NULL);
2N/A}
2N/A
2N/A/* Test to see if a group is a nested group. */
2N/Astatic boolean_t
2N/Ais_nested_group(const ns_ldap_entry_t *group_entry)
2N/A{
2N/A ns_ldap_attr_t *members;
2N/A
2N/A /*
2N/A * Use the memberOf attribute (or for DSEE, isMemberOf) to
2N/A * determine if this group is a nested group. If present,
2N/A * then yes.
2N/A */
2N/A members = __ns_ldap_getAttrStruct(group_entry, _G_MEMBEROF);
2N/A if (members != NULL && members->attrvalue != NULL)
2N/A return (B_TRUE);
2N/A members = __ns_ldap_getAttrStruct(group_entry, _G_MEMBEROF_ODSEE);
2N/A if (members != NULL && members->attrvalue != NULL)
2N/A return (B_TRUE);
2N/A return (B_FALSE);
2N/A}
2N/A
2N/Avoid
2N/Afree2dArray(char ***inppp)
2N/A{
2N/A char **temptr;
2N/A
2N/A if (inppp == NULL || *inppp == NULL)
2N/A return;
2N/A for (temptr = *inppp; *temptr != NULL; temptr++) {
2N/A free(*temptr);
2N/A }
2N/A free(*inppp);
2N/A *inppp = NULL;
2N/A}
2N/A
2N/A/* Loop and compare to see if an item is in a counted list. */
2N/Astatic boolean_t
2N/Ais_in_counted_list(char **list, int count, char *item)
2N/A{
2N/A int i;
2N/A
2N/A for (i = 0; i < count; i++) {
2N/A if (strcasecmp(list[i], item) == 0)
2N/A return (B_TRUE);
2N/A }
2N/A return (B_FALSE);
2N/A}
2N/A
2N/A/*
2N/A * The following code enables and uses/fills the dn2uid cache.
2N/A * The dn2uid cache uses the caching features of nscd if nscd
2N/A * is enabled.
2N/A *
2N/A * How it works:
2N/A * The nss backend taps into nscd cache using 2 APIs:
2N/A * _nss_cache_create - to init a backend cache
2N/A * _nss_cache_get - to search the cache. This API requires 2 callbacks
2N/A * getkey - performs the nss_packed_getkey operation
2N/A * psearch- performs the nss_psearch operation
2N/A * The expectation is that get receives a properly formed packed buffer
2N/A * and processes the specific search request, packing the results into
2N/A * the buffer. psearch performs the actual lookup, while getkey returns
2N/A * the necessary key information for psearch and for cache test operations.
2N/A *
2N/A * So that the system does not fail if nscd is disabled, the real APIs
2N/A * are provided as weak symbols to APIs that return error on create
2N/A * and process the lookup using getkey/psearch (aka an uncached lookup).
2N/A *
2N/A * The format of the result packing is project private and subject to
2N/A * change. See the comments on the structures, _dn2uid_res_t and
2N/A * _memberof_res_t, above for the layout of the search results.
2N/A *
2N/A */
2N/A/*
2N/A * Local _nss_cache_get performs the actual lookup if not running in the
2N/A * nscd process.
2N/A */
2N/A/*ARGSUSED0*/
2N/Astatic nss_status_t
2N/A_nss_cache_get_local(void **bufpp, size_t length, getkey_func getkey,
2N/A psearch_func psearch, int nscd_cache, char *from_backend)
2N/A{
2N/A nss_pheader_t *pbuf = (nss_pheader_t *)*bufpp;
2N/A
2N/A if (bufpp == NULL || *bufpp == NULL || length == 0 ||
2N/A getkey == NULL || psearch == NULL)
2N/A return (NSS_ERROR);
2N/A
2N/A /* fake _nss_cache_get just calls psearch (no caching) */
2N/A (*psearch)(bufpp, length);
2N/A pbuf = (nss_pheader_t *)*bufpp;
2N/A return (pbuf->p_status);
2N/A}
2N/A
2N/A/*
2N/A * Setup the dn2uid cache if this is the first time call.
2N/A */
2N/Astatic void
2N/A_nss_ldap_dn2uid_init()
2N/A{
2N/A /* Return if the dn2uid cache setup has been done of tried. */
2N/A if (dn2uid_cache_state != NSS_LDAP_CACHE_UNINITED)
2N/A return;
2N/A
2N/A (void) mutex_lock(&dn2uid_init_lock);
2N/A /* Return if just done of tried. */
2N/A if (dn2uid_cache_state != NSS_LDAP_CACHE_UNINITED) {
2N/A (void) mutex_unlock(&dn2uid_init_lock);
2N/A return;
2N/A }
2N/A
2N/A /* Not running in nscd ? */
2N/A if (_nss_cache_create == NULL) {
2N/A dn2uid_cache_state = NSS_LDAP_CACHE_NOTNSCD;
2N/A cache_get = _nss_cache_get_local;
2N/A } else {
2N/A if (_nss_cache_create(LDAP_DN2UID_CACHE_NM,
2N/A 0, 1, DN2UID_POS_TTL) == NSS_SUCCESS) {
2N/A dn2uid_cache_state = NSS_LDAP_CACHE_INITED;
2N/A cache_get = _nss_cache_get;
2N/A } else
2N/A dn2uid_cache_state = NSS_LDAP_CACHE_FAILED;
2N/A }
2N/A (void) mutex_unlock(&dn2uid_init_lock);
2N/A}
2N/A
2N/A/*
2N/A * Setup the memberof cache if this is the first time call.
2N/A */
2N/Astatic void
2N/A_nss_ldap_memberof_init()
2N/A{
2N/A /* Return if the memberof cache setup has been done of tried. */
2N/A if (memberof_cache_state != NSS_LDAP_CACHE_UNINITED)
2N/A return;
2N/A
2N/A (void) mutex_lock(&memberof_init_lock);
2N/A /* Return if just done of tried. */
2N/A if (memberof_cache_state != NSS_LDAP_CACHE_UNINITED) {
2N/A (void) mutex_unlock(&memberof_init_lock);
2N/A return;
2N/A }
2N/A
2N/A /* Not running in nscd ? */
2N/A if (_nss_cache_create == NULL) {
2N/A memberof_cache_state = NSS_LDAP_CACHE_NOTNSCD;
2N/A cache_get = _nss_cache_get_local;
2N/A } else {
2N/A if (_nss_cache_create(LDAP_MEMBEROF_CACHE_NM,
2N/A 0, 1, MEMBEROF_POS_TTL) == NSS_SUCCESS) {
2N/A memberof_cache_state = NSS_LDAP_CACHE_INITED;
2N/A cache_get = _nss_cache_get;
2N/A } else
2N/A memberof_cache_state = NSS_LDAP_CACHE_FAILED;
2N/A }
2N/A (void) mutex_unlock(&memberof_init_lock);
2N/A}
2N/A
2N/A/* Find the search key from the packed buffer header. */
2N/Astatic nss_status_t
2N/A_nss_ldap_grgetkey(void *buffer, size_t buflen,
2N/A char **dbnamepp, int *dbop, nss_XbyY_args_t *arg)
2N/A{
2N/A nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
2N/A nss_dbd_t *pdbd;
2N/A nssuint_t off;
2N/A
2N/A if (buffer == NULL || buflen == 0 || dbop == NULL ||
2N/A arg == NULL || dbnamepp == NULL)
2N/A return (NSS_ERROR);
2N/A *dbop = pbuf->nss_dbop;
2N/A off = pbuf->dbd_off;
2N/A pdbd = (nss_dbd_t *)((char *)buffer + off);
2N/A *dbnamepp = (char *)buffer + off + pdbd->o_name;
2N/A arg->key.name = (char *)buffer + pbuf->key_off;
2N/A return (NSS_SUCCESS);
2N/A}
2N/A
2N/A/* Add a member uid to the member list. */
2N/Astatic int
2N/A_mlist_add(_nss_ldap_list_t **mlpp, char *member)
2N/A{
2N/A nss_ldap_list_rc_t rc;
2N/A
2N/A /* Add member to the list */
2N/A rc = nss_ldap_list_add(mlpp, member);
2N/A if (rc == NSS_LDAP_LIST_EXISTED)
2N/A /* It is fine if member is already in the list */
2N/A return (1);
2N/A else if (rc != NSS_LDAP_LIST_SUCCESS)
2N/A return (-1);
2N/A
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * Add a dn2uid data string 'str' to the data area in the packed buffer,
2N/A * '*bufpp', expand the packed buffer if necessary.
2N/A */
2N/Astatic nss_status_t
2N/Adn2uid_add_string(void **bufpp, size_t *length, _dn2uid_res_t **respp,
2N/A nss_pheader_t **pbufpp, const char *str)
2N/A{
2N/A _dn2uid_res_t *pres = *respp;
2N/A char *bptr;
2N/A size_t left;
2N/A int slen;
2N/A
2N/A bptr = (char *)pres + pres->next_byte_offset;
2N/A left = pres->dbuf_size - pres->next_byte_offset;
2N/A slen = strlen(str) + 1;
2N/A
2N/A /* expand the buffer if not enough room to add the string */
2N/A while (slen > left) {
2N/A size_t res_off, bufsize;
2N/A void *tmp;
2N/A
2N/A bufsize = (*pbufpp)->pbufsiz + _PSEARCH_BUF_BLK;
2N/A tmp = realloc(*bufpp, bufsize);
2N/A if (tmp == NULL)
2N/A return (NSS_ERROR);
2N/A
2N/A /* adjust pointers */
2N/A res_off = (char *)(*respp) - (char *)(*bufpp);
2N/A /* Cast to void * eliminates lint warning */
2N/A *respp = (_dn2uid_res_t *)(void *)((char *)tmp + res_off);
2N/A pres = *respp;
2N/A *bufpp = tmp;
2N/A *pbufpp = (nss_pheader_t *)tmp;
2N/A
2N/A /* adjust buffer length */
2N/A *length = bufsize;
2N/A (*pbufpp)->pbufsiz = bufsize;
2N/A pres->dbuf_size += _PSEARCH_BUF_BLK;
2N/A
2N/A left += _PSEARCH_BUF_BLK;
2N/A bptr = (char *)pres + pres->next_byte_offset;
2N/A }
2N/A
2N/A (void) strlcpy(bptr, str, left);
2N/A pres->next_byte_offset += slen;
2N/A
2N/A return (NSS_SUCCESS);
2N/A}
2N/A
2N/A/* Generate an attributed mapped attr list to be used by a dn2uid lookup. */
2N/Astatic char **
2N/Aattr_map_dn2uid_group_attrs()
2N/A{
2N/A int i;
2N/A char **app;
2N/A char **amap;
2N/A
2N/A for (i = 0; gr_attrs2[i] != NULL; i++)
2N/A continue;
2N/A app = (char **)calloc(i + 1, sizeof (char *));
2N/A if (app == NULL)
2N/A return (NULL);
2N/A
2N/A for (i = 0; gr_attrs2[i] != NULL; i++) {
2N/A amap = __ns_ldap_getMappedAttributes(_GROUP, gr_attrs2[i]);
2N/A if (amap != NULL && amap[0] != NULL)
2N/A app[i] = strdup(amap[0]);
2N/A else
2N/A app[i] = strdup(gr_attrs2[i]);
2N/A if (app[i] == NULL) {
2N/A free2dArray(&app);
2N/A return (NULL);
2N/A }
2N/A if (amap != NULL)
2N/A free2dArray(&amap);
2N/A }
2N/A return (app);
2N/A}
2N/A
2N/A/* Perform schema mapping on a group entry returned from a dn2uid lookup. */
2N/Astatic nss_status_t
2N/Aschema_map_dn2uid_group(ns_ldap_entry_t *entry, ns_ldap_attr_t *goc,
2N/A char **in_attrs)
2N/A{
2N/A int i, j, cnt;
2N/A char **attrs, *group_oc, *mapped_attr;
2N/A char **omap;
2N/A char *attrname;
2N/A nss_status_t rc = NSS_SUCCESS;
2N/A
2N/A cnt = goc->value_count;
2N/A attrs = goc->attrvalue;
2N/A
2N/A /* Make sure it's a group entry. */
2N/A omap = __ns_ldap_getMappedObjectClass(_GROUP, _OC_GROUP);
2N/A if (omap != NULL && omap[0] != NULL)
2N/A group_oc = omap[0];
2N/A else
2N/A group_oc = _OC_GROUP;
2N/A
2N/A if (!is_in_counted_list(attrs, cnt, group_oc)) {
2N/A /* not a posixGroup entry, return NSS_NOTFOUND */
2N/A rc = NSS_NOTFOUND;
2N/A goto cleanup;
2N/A }
2N/A
2N/A /*
2N/A * Find the memberUid, member, and uniqueMember attributes,
2N/A * that could be schema mapped to use a different name, to
2N/A * change the name back. gr_attrs4[] contains "memberUid",
2N/A * "member", and "uniqueMember". gr_attrs2[] contains the
2N/A * name of the attributes that may be schema mapped.
2N/A * in_attrs[] contains the corresponding mapped gr_attrs2[]
2N/A * names that may be seen in the result group entry.
2N/A */
2N/A for (i = 0; gr_attrs4[i] != NULL; i++) {
2N/A mapped_attr = NULL;
2N/A for (j = 0; gr_attrs2[j] != NULL; j++) {
2N/A if (strcasecmp(gr_attrs4[i], gr_attrs2[j]) != 0)
2N/A continue;
2N/A if (strcasecmp(in_attrs[j], gr_attrs2[j]) == 0)
2N/A continue;
2N/A mapped_attr = in_attrs[j];
2N/A break;
2N/A }
2N/A if (mapped_attr == NULL)
2N/A continue;
2N/A
2N/A for (j = 0; j < entry->attr_count; j++) {
2N/A attrname = entry->attr_pair[j]->attrname;
2N/A if (strcasecmp(attrname, mapped_attr) == 0) {
2N/A if (strlen(attrname) < strlen(gr_attrs4[i])) {
2N/A free(attrname);
2N/A entry->attr_pair[j]->attrname = NULL;
2N/A attrname = strdup(gr_attrs4[i]);
2N/A if (attrname == NULL) {
2N/A rc = NSS_ERROR;
2N/A goto cleanup;
2N/A }
2N/A entry->attr_pair[j]->attrname =
2N/A attrname;
2N/A } else
2N/A (void) strcpy(attrname, gr_attrs4[i]);
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A
2N/Acleanup:
2N/A if (omap != NULL)
2N/A free2dArray(&omap);
2N/A
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * A value for the uniqueMember attribute is a DN followed
2N/A * by an optional hash (#) and uniqueIdentifier.
2N/A * For example, uniqueMember: cn=aaa,dc=bbb,dc=ccc #1234
2N/A * This function remove the optional # and uniqueIdentifier.
2N/A */
2N/Astatic void
2N/Aremove_uniqueID(char *dn)
2N/A{
2N/A char *hash, *eq;
2N/A
2N/A if ((hash = strrchr(dn, '#')) == NULL)
2N/A return;
2N/A /* invalid DN if no '=' and '#' should be after all RDNs */
2N/A if ((eq = strrchr(dn, '=')) == NULL || eq > hash)
2N/A return;
2N/A /* Remove '# ID' */
2N/A *hash = '\0';
2N/A}
2N/A
2N/A/*
2N/A * Perform a dn2uid lookup using the input packed buffer.
2N/A * A dn2uid lookup does an LDAP search with the input
2N/A * user or group DN and returns the dn2uid result data
2N/A * in the data area of the packed buffer. See the comments
2N/A * above about the specific of a dn2uid lookup and the layout
2N/A * of the dn2uid result data.
2N/A */
2N/Astatic void
2N/A_nss_ldap_dn2uid_psearch(void **bufpp, size_t length)
2N/A{
2N/A nss_pheader_t *pbuf;
2N/A nss_XbyY_args_t arg;
2N/A nss_status_t ret;
2N/A ns_ldap_result_t *result = NULL;
2N/A ns_ldap_error_t *error = NULL, *ep;
2N/A ns_ldap_attr_t *members, *oc, *username;
2N/A _dn2uid_res_t *pres;
2N/A const char *dn;
2N/A char *dbname, *user;
2N/A char **classes;
2N/A char **mapping;
2N/A char **search_attrs = NULL;
2N/A int dbop = 0, rc, i, cnt;
2N/A char *bptr;
2N/A size_t slen;
2N/A int mi;
2N/A int oc_type;
2N/A char **param;
2N/A
2N/A pbuf = (nss_pheader_t *)*bufpp;
2N/A
2N/A ret = _nss_ldap_grgetkey(*bufpp, length, &dbname, &dbop, &arg);
2N/A if (ret != NSS_SUCCESS) {
2N/A pbuf->p_status = ret;
2N/A return;
2N/A }
2N/A dn = arg.key.name;
2N/A if (dn == NULL || (nssuint_t)(strlen(dn) + 1) != pbuf->key_len) {
2N/A /* malformed/improperly sized key/dn */
2N/A pbuf->p_status = NSS_ERROR;
2N/A return;
2N/A }
2N/A
2N/A /*
2N/A * Get the DN entry from the server. Specify 'passwd' as the service
2N/A * first, in case this DN is that of a posixAccount (user) entry, so
2N/A * that schema mapping for the passwd service will be done by libsldap.
2N/A * Also need to map the requested group attributes here, in case this DN
2N/A * points to a posixGroup (group) entry, in which case, libsldap won't
2N/A * do the schema mapping because the service specified is not 'group'.
2N/A * We do this extra schema mapping because we want to try to get
2N/A * the user or group entry in just one call to __ns_ldap_read_dn().
2N/A */
2N/A mapping = __ns_ldap_getOrigAttribute(_GROUP,
2N/A NS_HASH_SCHEMA_MAPPING_EXISTED);
2N/A if (mapping != NULL) {
2N/A /*
2N/A * There is schema mapping for the group database, so map the
2N/A * group attributes so that the ldap server knows which attrs
2N/A * to return.
2N/A */
2N/A search_attrs = attr_map_dn2uid_group_attrs();
2N/A if (search_attrs == NULL) {
2N/A pbuf->p_status = NSS_ERROR;
2N/A goto cleanup;
2N/A }
2N/A }
2N/A rc = __ns_ldap_read_dn(dn, _PASSWD, NULL,
2N/A (const char **)search_attrs,
2N/A NULL, 0, &result, &error, NULL, NULL, NULL, NULL);
2N/A if (rc != NS_LDAP_SUCCESS) {
2N/A if (rc == NS_LDAP_NOTFOUND)
2N/A ret = NSS_NOTFOUND;
2N/A else {
2N/A ret = NSS_ERROR;
2N/A pbuf->p_errno = errno;
2N/A }
2N/A goto cleanup;
2N/A }
2N/A
2N/A /* posixAccount or posixGroup dn ? */
2N/A oc = __ns_ldap_getAttrStruct(result->entry, _G_OBJECTCLASS);
2N/A if (oc == NULL) {
2N/A ret = NSS_NOTFOUND;
2N/A goto cleanup;
2N/A }
2N/A cnt = oc->value_count;
2N/A classes = oc->attrvalue;
2N/A if (is_in_counted_list(classes, cnt, _OC_ACCOUNT)) {
2N/A oc_type = _OC_ACCOUNT_T;
2N/A } else {
2N/A /*
2N/A * Not a posixAccount entry, should be a posixGroup one.
2N/A * Perform schema mapping as it's not done by libsldap.
2N/A */
2N/A if (mapping != NULL) {
2N/A rc = schema_map_dn2uid_group(result->entry, oc,
2N/A search_attrs);
2N/A free2dArray(&mapping);
2N/A if (rc != NSS_SUCCESS) {
2N/A ret = rc;
2N/A goto cleanup;
2N/A }
2N/A } else {
2N/A if (!is_in_counted_list(classes, cnt, _OC_GROUP)) {
2N/A ret = NSS_NOTFOUND;
2N/A goto cleanup;
2N/A }
2N/A }
2N/A oc_type = _OC_GROUP_T;
2N/A }
2N/A
2N/A /* pack up and return results */
2N/A bptr = (char *)*bufpp + pbuf->data_off;
2N/A (void) memset(bptr, 0, sizeof (_dn2uid_res_t));
2N/A pres = (_dn2uid_res_t *)bptr;
2N/A pres->ocType = oc_type;
2N/A pres->next_byte_offset = sizeof (_dn2uid_res_t);
2N/A pres->dbuf_size = pbuf->data_len;
2N/A
2N/A /* found a posixAccount (user) dn, pack up the user name */
2N/A if (pres->ocType == _OC_ACCOUNT_T) {
2N/A username = __ns_ldap_getAttrStruct(result->entry, _G_UID);
2N/A if (username != NULL && username->attrvalue != NULL) {
2N/A user = username->attrvalue[0];
2N/A if ((ret = dn2uid_add_string(bufpp, &length, &pres,
2N/A &pbuf, user)) == NSS_SUCCESS) {
2N/A pres->users = 1;
2N/A pbuf->data_len = pres->next_byte_offset;
2N/A }
2N/A } else
2N/A ret = NSS_NOTFOUND;
2N/A goto cleanup;
2N/A }
2N/A
2N/A /* found a posixGroup dn, pack up the users and dns */
2N/A members = __ns_ldap_getAttrStruct(result->entry, _G_MEMBERUID);
2N/A if (members != NULL && members->attrvalue != NULL) {
2N/A cnt = members->value_count;
2N/A for (i = 0; i < cnt; i++) {
2N/A user = members->attrvalue[i];
2N/A if ((ret = dn2uid_add_string(bufpp, &length, &pres,
2N/A &pbuf, user)) == NSS_SUCCESS) {
2N/A pres->users++;
2N/A } else
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/A /* for attrs: _G_MEMBER & _G_UNIQUEMEMBER */
2N/A for (mi = 0; gr_attrs3[mi] != NULL; mi++) {
2N/A members = __ns_ldap_getAttrStruct(result->entry, gr_attrs3[mi]);
2N/A if (members == NULL || members->attrvalue == NULL)
2N/A continue;
2N/A
2N/A /* Process member (DN) */
2N/A cnt = members->value_count;
2N/A
2N/A /*
2N/A * get the configured base DN so that it's not repeated
2N/A * in every member DN saved
2N/A */
2N/A if (pres->basedn_len == 0) {
2N/A rc = __ns_ldap_getParam(NS_LDAP_SEARCH_BASEDN_P,
2N/A (void ***)&param, &ep);
2N/A if (rc == NS_LDAP_SUCCESS) {
2N/A slen = strlen(param[0]) + 1;
2N/A if ((ret = dn2uid_add_string(bufpp, &length,
2N/A &pres, &pbuf, param[0])) == NSS_SUCCESS) {
2N/A /* point to start of base DN */
2N/A pres->basedn_offset =
2N/A pres->next_byte_offset - slen;
2N/A pres->basedn_len = slen - 1;
2N/A (void) __ns_ldap_freeParam(
2N/A (void ***)&param);
2N/A } else
2N/A goto cleanup;
2N/A } else
2N/A (void) __ns_ldap_freeError(&ep);
2N/A }
2N/A
2N/A for (i = 0; i < cnt; i++) {
2N/A /* use non-basedn part if possible */
2N/A dn = members->attrvalue[i];
2N/A /*
2N/A * If uniqueMember, drop the optional uniqueIdentifier.
2N/A */
2N/A if (mi == 1)
2N/A remove_uniqueID((char *)dn);
2N/A if (pres->basedn_len != 0) {
2N/A int dnlen;
2N/A char *p;
2N/A char *basedn = (char *)pres +
2N/A pres->basedn_offset;
2N/A
2N/A /*
2N/A * remember max. DN length seen so far
2N/A */
2N/A dnlen = strlen(dn) + 1;
2N/A if (dnlen > pres->dnlen_max)
2N/A pres->dnlen_max = dnlen;
2N/A
2N/A /* if dn ends with basedn */
2N/A if ((p = strew(dn, basedn)) != NULL) {
2N/A /*
2N/A * This last ',' denotes basedn.
2N/A */
2N/A *p = ',';
2N/A *(p + 1) = '\0';
2N/A }
2N/A }
2N/A if ((ret = dn2uid_add_string(bufpp, &length, &pres,
2N/A &pbuf, dn)) == NSS_SUCCESS) {
2N/A pres->dns++;
2N/A } else
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/A /* Return NSS_NOTFOUND if no new data added. */
2N/A if (pres->next_byte_offset != sizeof (_dn2uid_res_t)) {
2N/A pbuf->data_len = pres->next_byte_offset;
2N/A ret = NSS_SUCCESS;
2N/A } else {
2N/A /*
2N/A * No group member found. Returning NSS_NOTFOUND
2N/A * makes it a negative nscd cache if running in
2N/A * nscd.
2N/A */
2N/A ret = NSS_NOTFOUND;
2N/A }
2N/A
2N/Acleanup:
2N/A if (mapping != NULL)
2N/A free2dArray(&mapping);
2N/A if (search_attrs != NULL)
2N/A free2dArray(&search_attrs);
2N/A (void) __ns_ldap_freeResult(&result);
2N/A pbuf->p_status = ret;
2N/A}
2N/A
2N/A/* Set up a packed buffer and perform a cache search. */
2N/Astatic nss_status_t
2N/Ado_lookup(char *cache_name, const char *tag, const char *key, int dbop,
2N/A int start_size, int nscd_cache, char **outputp,
2N/A getkey_func getkey, psearch_func psearch)
2N/A{
2N/A nss_status_t res;
2N/A char *buffer, *bptr;
2N/A size_t buflen, off, len;
2N/A size_t taglen = 0;
2N/A size_t keylen;
2N/A size_t keylen_round_up;
2N/A size_t cache_name_len;
2N/A size_t cache_name_len_round_up;
2N/A nss_pheader_t *pbuf;
2N/A nss_dbd_t *pdbd;
2N/A
2N/A *outputp = NULL;
2N/A
2N/A /* space needed for cache name: cache_name_len_round_up */
2N/A cache_name_len = strlen(cache_name) + 1;
2N/A cache_name_len_round_up =
2N/A ROUND_UP(cache_name_len, sizeof (nssuint_t));
2N/A
2N/A /* space needed for tag and key: keylen_round_up */
2N/A keylen = strlen(key) + 1;
2N/A if (tag != NULL) {
2N/A taglen = strlen(tag);
2N/A keylen += taglen;
2N/A }
2N/A keylen_round_up = ROUND_UP(keylen, sizeof (nssuint_t));
2N/A
2N/A /*
2N/A * Space needed for the packed buffer header:
2N/A * sizeof (nss_pheader_t) + sizeof (nss_dbd_t) +
2N/A * cache_name_len_round_up + keylen_round_up.
2N/A *
2N/A * Total space needed: packed buffer header size +
2N/A * requested data size (start_size). See code below
2N/A * for the consturction of the packed buffer header.
2N/A */
2N/A buflen = sizeof (nss_pheader_t) + sizeof (nss_dbd_t) +
2N/A cache_name_len_round_up + keylen_round_up + start_size;
2N/A
2N/A if ((buffer = calloc(buflen, 1)) == NULL)
2N/A return (NSS_ERROR);
2N/A
2N/A pbuf = (nss_pheader_t *)buffer;
2N/A pbuf->pbufsiz = buflen;
2N/A
2N/A /* Populate the packed buffer with a request */
2N/A /* packed header */
2N/A pbuf->nss_dbop = dbop;
2N/A off = sizeof (nss_pheader_t);
2N/A bptr = buffer + off;
2N/A
2N/A /* nss_dbd_t */
2N/A pdbd = (nss_dbd_t *)bptr;
2N/A pbuf->dbd_off = off;
2N/A len = sizeof (nss_dbd_t);
2N/A bptr += len;
2N/A off += len;
2N/A
2N/A /* cache name */
2N/A pbuf->dbd_len = sizeof (nss_dbd_t) + cache_name_len;
2N/A pdbd->o_name = sizeof (nss_dbd_t);
2N/A (void) strcpy(bptr, cache_name);
2N/A bptr += cache_name_len_round_up;
2N/A off += cache_name_len_round_up;
2N/A
2N/A /* tag and key */
2N/A pbuf->key_off = off;
2N/A if (tag != NULL) {
2N/A (void) strcpy(bptr, tag);
2N/A (void) strcpy(bptr + PSEARCH_TAG_LEN, key);
2N/A } else
2N/A (void) strcpy(bptr, key);
2N/A pbuf->key_len = keylen;
2N/A off += keylen_round_up;
2N/A
2N/A pbuf->data_off = off;
2N/A pbuf->data_len = buflen - off;
2N/A
2N/A /* perform the cache search */
2N/A res = (*cache_get)((void **)&buffer, buflen, getkey, psearch,
2N/A nscd_cache, "ldap");
2N/A if (res == NSS_SUCCESS)
2N/A *outputp = buffer;
2N/A else
2N/A (void) free(buffer);
2N/A
2N/A return (res);
2N/A}
2N/A
2N/A/*
2N/A * Given a group or member (user) DN, search the nscd group or passwd cache
2N/A * to find a group or passwd entry with a matching DN.
2N/A */
2N/Astatic nss_status_t
2N/Alookup_nscd_cache(char *dn, _nss_ldap_list_t **mlpp)
2N/A{
2N/A nss_status_t res;
2N/A nss_pheader_t *pbuf;
2N/A char key[256];
2N/A char *buffer, *cp, *mp, *ep;
2N/A char *sptr, *optr, *dptr;
2N/A int rc;
2N/A boolean_t dn_matched;
2N/A
2N/A /*
2N/A * Check to see if the member or group is already in nscd cache.
2N/A * Search key is the rdn value.
2N/A */
2N/A *key = '\0';
2N/A /* skip rdn attr type and use the attr value as the search key */
2N/A cp = strchr(dn, '=');
2N/A if (cp != NULL) {
2N/A ep = strchr(cp + 1, ',');
2N/A if (ep != NULL) {
2N/A *ep = '\0';
2N/A (void) strlcpy(key, cp + 1, sizeof (key));
2N/A *ep = ',';
2N/A }
2N/A }
2N/A
2N/A if (*key == '\0')
2N/A return (NSS_NOTFOUND);
2N/A
2N/A /* try search user first */
2N/A res = do_lookup(NSS_DBNAM_PASSWD, NULL, key,
2N/A NSS_DBOP_PASSWD_BYNAME, NSS_LINELEN_PASSWD, 1,
2N/A &buffer, NULL, NULL);
2N/A
2N/A /*
2N/A * If user found in nscd cache and with the same DN,
2N/A * add it to the member list.
2N/A */
2N/A dn_matched = B_FALSE;
2N/A if (res == NSS_SUCCESS) {
2N/A pbuf = (nss_pheader_t *)buffer;
2N/A sptr = buffer + pbuf->data_off;
2N/A /*
2N/A * The optional DN string is right after the regular database
2N/A * result string. pbuf->data_len indicates the sum of these
2N/A * two strings. If it's not larger than the length of the
2N/A * first string, then there is no optional DN.
2N/A */
2N/A if (pbuf->data_len > strlen(sptr)) {
2N/A optr = sptr + strlen(sptr) + 1;
2N/A if (strbw(optr, NSS_LDAP_DN_TAG)) {
2N/A dptr = optr + NSS_LDAP_DN_TAG_LEN;
2N/A /* skip server type */
2N/A dptr = strchr(dptr, ':');
2N/A if (dptr != NULL &&
2N/A strcmp(dptr + 1, dn) == 0)
2N/A dn_matched = B_TRUE;
2N/A }
2N/A }
2N/A
2N/A free(buffer);
2N/A
2N/A if (dn_matched) {
2N/A rc = _mlist_add(mlpp, key);
2N/A if (rc < 0)
2N/A return (NSS_ERROR);
2N/A } else
2N/A return (NSS_NOTFOUND);
2N/A
2N/A return (NSS_SUCCESS);
2N/A }
2N/A
2N/A /* next try searching group */
2N/A res = do_lookup(NSS_DBNAM_GROUP, NULL, key,
2N/A NSS_DBOP_GROUP_BYNAME, NSS_LINELEN_GROUP, 1,
2N/A &buffer, NULL, NULL);
2N/A
2N/A /*
2N/A * If group found in nscd cache and with the same DN,
2N/A * add all members of the group to the member list.
2N/A */
2N/A dn_matched = B_FALSE;
2N/A if (res == NSS_SUCCESS) {
2N/A pbuf = (nss_pheader_t *)buffer;
2N/A sptr = buffer + pbuf->data_off;
2N/A /*
2N/A * The optional DN string is right after the regular database
2N/A * result string. pbuf->data_len indicates the sum of these
2N/A * two strings. If it's not larger than the length of the
2N/A * first string, then there is no optional DN.
2N/A */
2N/A if (pbuf->data_len > strlen(sptr)) {
2N/A optr = sptr + strlen(sptr) + 1;
2N/A if (strbw(optr, NSS_LDAP_DN_TAG)) {
2N/A dptr = optr + NSS_LDAP_DN_TAG_LEN;
2N/A /* skip server type */
2N/A dptr = strchr(dptr, ':');
2N/A if (dptr != NULL &&
2N/A strcmp(dptr + 1, dn) == 0)
2N/A dn_matched = B_TRUE;
2N/A }
2N/A }
2N/A
2N/A if (dn_matched) {
2N/A res = NSS_SUCCESS;
2N/A for (mp = strrchr(sptr, ':'); mp != NULL; mp = ep) {
2N/A mp++;
2N/A ep = strchr(mp, ',');
2N/A if (ep != NULL)
2N/A *ep = '\0';
2N/A if (*mp == '\0')
2N/A continue;
2N/A if (_mlist_add(mlpp, mp) < 0) {
2N/A res = NSS_ERROR;
2N/A break;
2N/A }
2N/A }
2N/A free(buffer);
2N/A return (res);
2N/A }
2N/A free(buffer);
2N/A }
2N/A
2N/A return (NSS_NOTFOUND);
2N/A}
2N/A
2N/A/*
2N/A * Given a member DN, find the corresponding user, or if the DN is that
2N/A * of a group, find all the members recursively until the maximum level
2N/A * of recursion is reached. The users or groups could be from the nscd
2N/A * cache or from the ldap servers.
2N/A */
2N/Astatic nss_status_t
2N/A_nss_ldap_proc_memberDN(char *dn, ns_ldap_server_type_t serverType,
2N/A _nss_ldap_list_t **dlpp, _nss_ldap_list_t **mlpp, int *rec_callp)
2N/A{
2N/A nss_status_t res;
2N/A nss_pheader_t *pbuf;
2N/A _dn2uid_res_t *pres;
2N/A char *buffer = NULL;
2N/A char *sptr, *osptr, *pp, *mdn;
2N/A char *basedn;
2N/A char *wbdn = NULL;
2N/A int i, mdn_len;
2N/A
2N/A /*
2N/A * If the depth of recursion reaches DN2UID_MAX_RECURSIVE_CALL
2N/A * then stop and return NSS_SUCCESS.
2N/A */
2N/A *rec_callp += 1;
2N/A if (*rec_callp == DN2UID_MAX_RECURSIVE_CALL) {
2N/A res = NSS_SUCCESS;
2N/A goto out;
2N/A }
2N/A
2N/A /* create the dn2uid nscd backend cache if not done yet */
2N/A if (dn2uid_cache_state == NSS_LDAP_CACHE_UNINITED)
2N/A _nss_ldap_dn2uid_init();
2N/A
2N/A /*
2N/A * For loop detection, check to see if dn has already been
2N/A * processed by adding it to the dnlist. It not able to add,
2N/A * then it has been processed or error occurred, ignore it.
2N/A */
2N/A if (nss_ldap_list_add(dlpp, dn) != NSS_LDAP_LIST_SUCCESS) {
2N/A res = NSS_SUCCESS;
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * If running in nscd, check to see if member or
2N/A * group is already in nscd cache.
2N/A */
2N/A if (_nss_cache_create != NULL) {
2N/A res = lookup_nscd_cache(dn, mlpp);
2N/A if (res != NSS_NOTFOUND)
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * Check to see if dn has already been searched and in nscd's
2N/A * dn2uid cache. If not running in nscd, then the local
2N/A * _nss_cache_get will be called to search the DN directly.
2N/A */
2N/A res = do_lookup(LDAP_DN2UID_CACHE_NM, NULL, dn, 0,
2N/A _PSEARCH_BUF_BLK, 0, &buffer, _nss_ldap_grgetkey,
2N/A _nss_ldap_dn2uid_psearch);
2N/A if (res != NSS_SUCCESS)
2N/A goto out;
2N/A
2N/A pbuf = (nss_pheader_t *)buffer;
2N/A pp = buffer + pbuf->data_off;
2N/A pres = (_dn2uid_res_t *)pp;
2N/A osptr = sptr = (char *)&pres[1];
2N/A if (pres->ocType == _OC_ACCOUNT_T) {
2N/A /* a single user id; add it to the member list */
2N/A if (_mlist_add(mlpp, sptr) < 0) {
2N/A res = NSS_ERROR;
2N/A goto out;
2N/A }
2N/A } else { /* a group entry */
2N/A /* add all users to the member list */
2N/A for (i = pres->users; --i >= 0; ) {
2N/A if (_mlist_add(mlpp, sptr) < 0) {
2N/A res = NSS_ERROR;
2N/A goto out;
2N/A }
2N/A sptr += strlen(sptr) + 1;
2N/A }
2N/A
2N/A /*
2N/A * If base DN is present, set pointer to it and
2N/A * go pass it to process the next set of data,
2N/A * group DNs.
2N/A */
2N/A if (pres->basedn_len > 0) {
2N/A basedn = (char *)pres + pres->basedn_offset;
2N/A sptr += pres->basedn_len + 1;
2N/A }
2N/A
2N/A /*
2N/A * If there are group DNs, get all members recursively
2N/A * by calling _nss_ldap_proc_memberDN again.
2N/A */
2N/A for (i = pres->dns; --i >= 0; ) {
2N/A /*
2N/A * Restore member DN if it was truncated to save
2N/A * space.
2N/A */
2N/A mdn = sptr;
2N/A mdn_len = strlen(sptr);
2N/A sptr += mdn_len + 1;
2N/A if (pres->basedn_len > 0 && mdn[mdn_len - 1] == ',') {
2N/A /*
2N/A * Get a work buffer from the stack
2N/A * that can be reused.
2N/A */
2N/A if (wbdn == NULL)
2N/A wbdn = (char *)alloca(pres->dnlen_max);
2N/A
2N/A /* drop the last ',' */
2N/A (void) strlcpy(wbdn, mdn, mdn_len);
2N/A (void) strlcat(wbdn, basedn, pres->dnlen_max);
2N/A mdn = wbdn;
2N/A }
2N/A
2N/A res = _nss_ldap_proc_memberDN(mdn, serverType, dlpp,
2N/A mlpp, rec_callp);
2N/A if (res != NSS_SUCCESS && res != NSS_NOTFOUND)
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A /* If no new data added, return NSS_NOTFOUND. */
2N/A if (osptr == sptr)
2N/A res = NSS_NOTFOUND;
2N/A else
2N/A res = NSS_SUCCESS;
2N/A
2N/Aout:
2N/A if (buffer != NULL)
2N/A (void) free(buffer);
2N/A *rec_callp -= 1;
2N/A return (res);
2N/A}
2N/A
2N/A/*
2N/A * TLE_group_member_cb is a callback function callable by libsldap
2N/A * to process each one of the member entres returned by an AD
2N/A * Transitive Link Expansion search that request all members of
2N/A * a group.
2N/A */
2N/Aint
2N/ATLE_group_member_cb(const ns_ldap_entry_t *entry, const void *userdata)
2N/A{
2N/A int cnt;
2N/A char **username;
2N/A char **classes;
2N/A ns_ldap_attr_t *oc;
2N/A _member_tl_cb_t *tc = (_member_tl_cb_t *)userdata;
2N/A
2N/A /* posixAccount or posixGroup dn ? */
2N/A oc = __ns_ldap_getAttrStruct(entry, _G_OBJECTCLASS);
2N/A if (oc == NULL)
2N/A return (NS_LDAP_NOTFOUND);
2N/A
2N/A cnt = oc->value_count;
2N/A classes = oc->attrvalue;
2N/A if (!is_in_counted_list(classes, cnt, _OC_ACCOUNT)) {
2N/A /* Not an posixAccount entry, skip. */
2N/A return (NS_LDAP_CB_NEXT);
2N/A
2N/A }
2N/A
2N/A username = __ns_ldap_getAttr(entry, _G_UID);
2N/A if (username == NULL || username[0] == NULL) {
2N/A /* No user name, just ignore the entry. */
2N/A return (NS_LDAP_CB_NEXT);
2N/A }
2N/A
2N/A if (_mlist_add(tc->listpp, username[0]) < 0) {
2N/A return (NS_LDAP_OP_FAILED);
2N/A }
2N/A
2N/A tc->cnt++;
2N/A
2N/A return (NS_LDAP_CB_NEXT);
2N/A}
2N/A
2N/A/*
2N/A * Perform an AD Transitive Link Expansion search to get all members
2N/A * of a group entry. All member entries including those of the child
2N/A * nested groups will be returned in one search. The callback function
2N/A * TLE_group_member_cb is used to process each one of the entries.
2N/A */
2N/Astatic nss_status_t
2N/Aget_TLE_group_member(ns_ldap_entry_t *entry, _member_tl_cb_t *member_tc)
2N/A{
2N/A char **dn;
2N/A int rc;
2N/A int ocnt;
2N/A char searchfilter[SEARCHFILTERLEN];
2N/A char searchDN[SEARCHFILTERLEN * 4];
2N/A ns_ldap_result_t *result;
2N/A ns_ldap_error_t *error;
2N/A
2N/A dn = __ns_ldap_getAttr(entry, "dn");
2N/A if (dn == NULL || dn[0] == NULL)
2N/A return (NSS_ERROR);
2N/A /* Escape any special characters in the dn first. */
2N/A if (_ldap_filter_name(searchDN, dn[0], sizeof (searchDN)) != 0)
2N/A return (NSS_NOTFOUND);
2N/A
2N/A ocnt = member_tc->cnt;
2N/A rc = snprintf(searchfilter, sizeof (searchfilter), _F_GETGRMEM_BL,
2N/A searchDN);
2N/A if (rc >= sizeof (searchfilter) || rc < 0)
2N/A return (NSS_NOTFOUND);
2N/A
2N/A rc = __ns_ldap_list_sort(_PASSWD, searchfilter, _G_UID,
2N/A _merge_SSD_filter, gr_attrs_mbr_tl, NULL, NS_LDAP_PAGE_CTRL,
2N/A &result, &error, TLE_group_member_cb, member_tc);
2N/A
2N/A if (result != NULL)
2N/A (void) __ns_ldap_freeResult(&result);
2N/A if (error != NULL)
2N/A (void) __ns_ldap_freeError(&error);
2N/A
2N/A /*
2N/A * libsldap returns NS_LDAP_NOTFOUND if
2N/A * all result entries were consumed by
2N/A * the callback function.
2N/A */
2N/A if (rc == NS_LDAP_NOTFOUND && ocnt != member_tc->cnt)
2N/A return (NSS_SUCCESS);
2N/A else
2N/A return (NSS_ERROR);
2N/A
2N/A}
2N/A
2N/A/*
2N/A * _nss_ldap_memberlist collects all members of a group
2N/A * (recursively if necessary) processing all:
2N/A * _G_MEMBERUID, G_MEMBER, G_UNIQUEMEMBER
2N/A * starting with the current results. Follow on
2N/A * dn2uid results may be cached.
2N/A */
2N/Astatic nss_status_t
2N/A_nss_ldap_memberlist(ns_ldap_result_t *result, ns_ldap_entry_t *extra_info,
2N/A _nss_ldap_list_t **dlpp, _nss_ldap_list_t **mlpp, int *rec_callp)
2N/A{
2N/A ns_ldap_attr_t *members;
2N/A ns_ldap_server_type_t serverType;
2N/A char *dn, **users;
2N/A nss_status_t res;
2N/A int i, cnt;
2N/A int mi;
2N/A _member_tl_cb_t member_tc;
2N/A
2N/A /* process member uid, attr: _G_MEMBERUID */
2N/A members = __ns_ldap_getAttrStruct(result->entry, _G_MEMBERUID);
2N/A if (members != NULL && members->attrvalue != NULL) {
2N/A cnt = members->value_count;
2N/A users = members->attrvalue;
2N/A for (i = 0; i < cnt; i++) {
2N/A if (_mlist_add(mlpp, users[i]) < 0) {
2N/A return (NSS_ERROR);
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* Determine which type of server the entry is from. */
2N/A serverType = _nss_ldap_get_server_type(extra_info, NULL);
2N/A
2N/A /*
2N/A * If from AD, do a Transitive Link Expansion search
2N/A * to get all member entries with just one search.
2N/A */
2N/A if (serverType == NS_LDAP_SERVERTYPE_AD) {
2N/A member_tc.listpp = mlpp;
2N/A member_tc.cnt = 0;
2N/A res = get_TLE_group_member(result->entry, &member_tc);
2N/A if (res == NSS_SUCCESS)
2N/A return (NSS_SUCCESS);
2N/A }
2N/A
2N/A /* process member (or group) DN, attrs: _G_MEMBER & _G_UNIQUEMEMBER */
2N/A for (mi = 0; gr_attrs3[mi] != NULL; mi++) {
2N/A members = __ns_ldap_getAttrStruct(result->entry, gr_attrs3[mi]);
2N/A if (members != NULL && members->attrvalue != NULL) {
2N/A /* Process member (DN) */
2N/A cnt = members->value_count;
2N/A for (i = 0; i < cnt; i++) {
2N/A dn = members->attrvalue[i];
2N/A /*
2N/A * If uniqueMember, drop the optional
2N/A * uniqueIdentifier.
2N/A */
2N/A if (mi == 1)
2N/A remove_uniqueID((char *)dn);
2N/A res = _nss_ldap_proc_memberDN(dn, serverType,
2N/A dlpp, mlpp, rec_callp);
2N/A /*
2N/A * No group member is not an error,
2N/A * as groups having no users are allowed.
2N/A */
2N/A if (res == NSS_NOTFOUND)
2N/A continue;
2N/A if (res != NSS_SUCCESS)
2N/A return (res);
2N/A }
2N/A }
2N/A }
2N/A return (NSS_SUCCESS);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * _nss_ldap_group2str is the data marshaling method for the group getXbyY
2N/A * (e.g., getgrnam(), getgrgid(), getgrent()) backend processes. This method
2N/A * is called after a successful ldap search has been performed. This method
2N/A * will parse the ldap search values into the file format.
2N/A * e.g.
2N/A *
2N/A * adm::4:root,adm,daemon
2N/A *
2N/A */
2N/A
2N/Astatic int
2N/A_nss_ldap_group2str(ldap_backend_ptr be, nss_XbyY_args_t *argp)
2N/A{
2N/A int nss_result;
2N/A int buflen = 0, len;
2N/A char *buffer = NULL;
2N/A ns_ldap_result_t *result = be->result;
2N/A ns_ldap_entry_t *extra_info = be->extra_info;
2N/A char **gname, **passwd, **gid, *password, *end;
2N/A char gid_nobody[NOBODY_STR_LEN];
2N/A char *gid_nobody_v[1];
2N/A char **dn_v;
2N/A char *server_type;
2N/A int rec_call = -1;
2N/A nss_status_t ret;
2N/A _nss_ldap_list_t *memlist = NULL;
2N/A _nss_ldap_list_t *dnlist = NULL;
2N/A nss_ldap_list_rc_t list_rc;
2N/A
2N/A (void) snprintf(gid_nobody, sizeof (gid_nobody), "%u", GID_NOBODY);
2N/A gid_nobody_v[0] = gid_nobody;
2N/A
2N/A if (result == NULL) {
2N/A if (be->extra_info != NULL) {
2N/A __ns_ldap_freeEntry(be->extra_info);
2N/A be->extra_info = NULL;
2N/A }
2N/A return (NSS_STR_PARSE_PARSE);
2N/A }
2N/A buflen = argp->buf.buflen;
2N/A
2N/A if (argp->buf.result != NULL) {
2N/A if ((be->buffer = calloc(1, buflen)) == NULL) {
2N/A nss_result = NSS_STR_PARSE_PARSE;
2N/A goto result_grp2str;
2N/A }
2N/A buffer = be->buffer;
2N/A } else
2N/A buffer = argp->buf.buffer;
2N/A
2N/A nss_result = NSS_STR_PARSE_SUCCESS;
2N/A (void) memset(buffer, 0, buflen);
2N/A
2N/A gname = __ns_ldap_getAttr(result->entry, _G_NAME);
2N/A if (gname == NULL || gname[0] == NULL || (strlen(gname[0]) < 1)) {
2N/A nss_result = NSS_STR_PARSE_PARSE;
2N/A goto result_grp2str;
2N/A }
2N/A passwd = __ns_ldap_getAttr(result->entry, _G_PASSWD);
2N/A if (passwd == NULL || passwd[0] == NULL || (strlen(passwd[0]) == 0)) {
2N/A /* group password could be NULL, replace it with "" */
2N/A password = _NO_PASSWD_VAL;
2N/A } else {
2N/A /*
2N/A * Preen "{crypt}" if necessary.
2N/A * If the password does not include the {crypt} prefix
2N/A * then the password may be plain text. And thus
2N/A * perhaps crypt(3c) should be used to encrypt it.
2N/A * Currently the password is copied verbatim.
2N/A */
2N/A if (strncasecmp(passwd[0], _CRYPT, strlen(_CRYPT)) == 0)
2N/A password = passwd[0] + strlen(_CRYPT);
2N/A else
2N/A password = passwd[0];
2N/A }
2N/A gid = __ns_ldap_getAttr(result->entry, _G_GIDNUMBER);
2N/A if (gid == NULL || gid[0] == NULL || (strlen(gid[0]) < 1)) {
2N/A nss_result = NSS_STR_PARSE_PARSE;
2N/A goto result_grp2str;
2N/A }
2N/A /* Validate GID */
2N/A if (strtoul(gid[0], &end, 10) > MAXUID)
2N/A gid = gid_nobody_v;
2N/A len = snprintf(buffer, buflen, "%s:%s:%s:", gname[0], password, gid[0]);
2N/A TEST_AND_ADJUST(len, buffer, buflen, result_grp2str);
2N/A
2N/A /* Rescursive member list processing */
2N/A ret = _nss_ldap_memberlist(result, extra_info, &dnlist, &memlist,
2N/A &rec_call);
2N/A if (ret != NSS_SUCCESS) {
2N/A nss_result = NSS_STR_PARSE_PARSE;
2N/A goto result_grp2str;
2N/A }
2N/A
2N/A /*
2N/A * Copy all the members to the output buffer.
2N/A * If no members, nss_ldap_list_dump will do
2N/A * nothing.
2N/A */
2N/A list_rc = nss_ldap_list_dump(&memlist, &buffer, &buflen);
2N/A if (list_rc != NSS_LDAP_LIST_SUCCESS &&
2N/A list_rc != NSS_LDAP_LIST_NOLIST) {
2N/A if (list_rc == NSS_LDAP_LIST_ERANGE)
2N/A nss_result = NSS_STR_PARSE_ERANGE;
2N/A else
2N/A nss_result = NSS_STR_PARSE_PARSE;
2N/A goto result_grp2str;
2N/A }
2N/A
2N/Anomember:
2N/A /* The front end marshaller doesn't need the trailing nulls */
2N/A if (argp->buf.result != NULL) {
2N/A be->buflen = strlen(be->buffer);
2N/A } else {
2N/A /*
2N/A * Save the entry DN as an optional data, if
2N/A * dn to uid caching is being performed and
2N/A * not processing enumeration results and
2N/A * there's enough room in the buffer.
2N/A * Format is: (group data followed by optional DN data)
2N/A * <group data> + '\0' + '#dn:<server_type>:' + <DN> + '\0'
2N/A */
2N/A
2N/A /* Terminate the primary result. */
2N/A *buffer++ = '\0';
2N/A buflen--;
2N/A
2N/A be->have_dn = B_FALSE;
2N/A if (dn2uid_cache_state == NSS_LDAP_CACHE_INITED &&
2N/A be->enumcookie == NULL) {
2N/A dn_v = __ns_ldap_getAttr(result->entry, "dn");
2N/A (void) _nss_ldap_get_server_type(be->extra_info,
2N/A &server_type);
2N/A len = snprintf(buffer, buflen, "%s%s:%s",
2N/A NSS_LDAP_DN_TAG, server_type, dn_v[0]);
2N/A if (len < buflen && len >= 0) {
2N/A len++; /* Include the \0 */
2N/A buffer += len;
2N/A buflen -= len;
2N/A be->have_dn = B_TRUE;
2N/A }
2N/A }
2N/A }
2N/A
2N/Aresult_grp2str:
2N/A nss_ldap_list_free(&memlist);
2N/A nss_ldap_list_free(&dnlist);
2N/A if (be->extra_info != NULL) {
2N/A __ns_ldap_freeEntry(be->extra_info);
2N/A be->extra_info = NULL;
2N/A }
2N/A (void) __ns_ldap_freeResult(&be->result);
2N/A return (nss_result);
2N/A}
2N/A
2N/A/*
2N/A * getbynam gets a group entry by name. This function constructs an ldap
2N/A * search filter using the name invocation parameter and the getgrnam search
2N/A * filter defined. Once the filter is constructed, we searche for a matching
2N/A * entry and marshal the data results into struct group for the frontend
2N/A * process. The function _nss_ldap_group2ent performs the data marshaling.
2N/A */
2N/A
2N/Astatic nss_status_t
2N/Agetbynam(ldap_backend_ptr be, void *a)
2N/A{
2N/A nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a;
2N/A char searchfilter[SEARCHFILTERLEN];
2N/A char userdata[SEARCHFILTERLEN];
2N/A char groupname[SEARCHFILTERLEN];
2N/A int ret;
2N/A
2N/A if (_ldap_filter_name(groupname, argp->key.name, sizeof (groupname)) !=
2N/A 0)
2N/A return ((nss_status_t)NSS_NOTFOUND);
2N/A
2N/A ret = snprintf(searchfilter, sizeof (searchfilter),
2N/A _F_GETGRNAM, groupname);
2N/A if (ret >= sizeof (searchfilter) || ret < 0)
2N/A return ((nss_status_t)NSS_NOTFOUND);
2N/A
2N/A ret = snprintf(userdata, sizeof (userdata), _F_GETGRNAM_SSD, groupname);
2N/A if (ret >= sizeof (userdata) || ret < 0)
2N/A return ((nss_status_t)NSS_NOTFOUND);
2N/A
2N/A be->extra_info_attr = gr_extra_info_attr;
2N/A return (_nss_ldap_lookup(be, argp, _GROUP, searchfilter, NULL,
2N/A _merge_SSD_filter, userdata));
2N/A}
2N/A
2N/A
2N/A/*
2N/A * getbygid gets a group entry by number. This function constructs an ldap
2N/A * search filter using the name invocation parameter and the getgrgid search
2N/A * filter defined. Once the filter is constructed, we searche for a matching
2N/A * entry and marshal the data results into struct group for the frontend
2N/A * process. The function _nss_ldap_group2ent performs the data marshaling.
2N/A */
2N/A
2N/Astatic nss_status_t
2N/Agetbygid(ldap_backend_ptr be, void *a)
2N/A{
2N/A nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a;
2N/A char searchfilter[SEARCHFILTERLEN];
2N/A char userdata[SEARCHFILTERLEN];
2N/A int ret;
2N/A
2N/A if (argp->key.uid > MAXUID)
2N/A return ((nss_status_t)NSS_NOTFOUND);
2N/A
2N/A ret = snprintf(searchfilter, sizeof (searchfilter),
2N/A _F_GETGRGID, argp->key.uid);
2N/A if (ret >= sizeof (searchfilter) || ret < 0)
2N/A return ((nss_status_t)NSS_NOTFOUND);
2N/A
2N/A ret = snprintf(userdata, sizeof (userdata),
2N/A _F_GETGRGID_SSD, argp->key.uid);
2N/A if (ret >= sizeof (userdata) || ret < 0)
2N/A return ((nss_status_t)NSS_NOTFOUND);
2N/A
2N/A be->extra_info_attr = gr_extra_info_attr;
2N/A return (_nss_ldap_lookup(be, argp, _GROUP, searchfilter, NULL,
2N/A _merge_SSD_filter, userdata));
2N/A
2N/A}
2N/A
2N/A/*
2N/A * setup_buf_memberof_res sets up a control block for doing
2N/A * memberof searches. The control block locates at the
2N/A * beginning of the result data area in the packed buffer.
2N/A */
2N/Astatic void
2N/Asetup_buf_memberof_res(void *buffer, _memberof_res_t **grespp)
2N/A{
2N/A char *bptr;
2N/A size_t glen;
2N/A nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
2N/A _memberof_res_t *gres;
2N/A ns_ldap_error_t *errorp;
2N/A void **param;
2N/A int rc, len;
2N/A
2N/A bptr = (char *)buffer + pbuf->data_off;
2N/A glen = sizeof (_memberof_res_t);
2N/A *grespp = (_memberof_res_t *)bptr;
2N/A gres = *grespp;
2N/A gres->parents = 0;
2N/A gres->group_dns = 0;
2N/A gres->dnlen_max = 0;
2N/A gres->next_byte_offset = glen;
2N/A gres->dbuf_size = pbuf->data_len;
2N/A gres->no_sort_search = B_FALSE;
2N/A
2N/A /* get the configured baseDN */
2N/A rc = __ns_ldap_getParam(NS_LDAP_SEARCH_BASEDN_P, &param, &errorp);
2N/A if (rc == NS_LDAP_SUCCESS) {
2N/A len = strlen(param[0]);
2N/A (void) strlcpy(((char *)gres + gres->next_byte_offset),
2N/A param[0], gres->dbuf_size - gres->next_byte_offset);
2N/A gres->basedn_len = len;
2N/A gres->basedn_offset = glen;
2N/A gres->next_byte_offset += len + 1;
2N/A (void) __ns_ldap_freeParam(&param);
2N/A } else {
2N/A gres->basedn_len = 0;
2N/A gres->basedn_offset = 0;
2N/A (void) __ns_ldap_freeError(&errorp);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Realloc the packed buffer for a memberof search if not
2N/A * enough space left.
2N/A */
2N/Astatic void *
2N/Amemberof_realloc_buf(void **bufpp, _memberof_res_t **grespp,
2N/A nss_pheader_t **pbufpp, char **bptrpp, size_t *bufsizepp)
2N/A{
2N/A void *tmp;
2N/A size_t res_off, bufsize;
2N/A nss_pheader_t *pbuf = (nss_pheader_t *)*bufpp;
2N/A _memberof_res_t *gres = *grespp;
2N/A
2N/A bufsize = pbuf->pbufsiz + _PSEARCH_BUF_BLK;
2N/A tmp = realloc(*bufpp, bufsize);
2N/A if (tmp == NULL)
2N/A return (NULL);
2N/A
2N/A /*
2N/A * Buffer pointer may be changed, make sure all related
2N/A * data in the packed buffer are updated.
2N/A */
2N/A res_off = (char *)gres - (char *)pbuf;
2N/A *grespp = (_memberof_res_t *)((char *)tmp + res_off);
2N/A (*grespp)->dbuf_size = (*grespp)->dbuf_size + _PSEARCH_BUF_BLK;
2N/A pbuf = (nss_pheader_t *)tmp;
2N/A *pbufpp = tmp;
2N/A pbuf->pbufsiz = bufsize;
2N/A *bufpp = tmp;
2N/A *bufsizepp = (*grespp)->dbuf_size;
2N/A *bptrpp = (char *)(*grespp) + (*grespp)->next_byte_offset;
2N/A
2N/A return (tmp);
2N/A}
2N/A
2N/A/*
2N/A * pack_group_gid_dn adds the gid and DN of a group to the
2N/A * result area. If not a nested group, its DN will be stored
2N/A * as an empty string, as an indication that parent group
2N/A * searching needs not be done.
2N/A */
2N/Astatic int
2N/Apack_group_gid_dn(void **bufpp, const ns_ldap_entry_t *entry,
2N/A _memberof_res_t **grespp, nss_pheader_t **pbufpp,
2N/A boolean_t nested_group)
2N/A{
2N/A char **gidvalue;
2N/A char *gid, *bptr;
2N/A char **dn, *entry_dn;
2N/A size_t gid_dn_slen, dlen, dbuf_size;
2N/A _memberof_res_t *gres = *grespp;
2N/A
2N/A bptr = (char *)gres + gres->next_byte_offset;
2N/A dlen = gres->next_byte_offset;
2N/A dbuf_size = gres->dbuf_size;
2N/A
2N/A entry_dn = "";
2N/A if (nested_group) {
2N/A dn = __ns_ldap_getAttr(entry, "dn");
2N/A if (dn != NULL && dn[0] != NULL)
2N/A entry_dn = dn[0];
2N/A /* store only non-basedn part if possible */
2N/A if (gres->basedn_len != 0) {
2N/A int dnlen;
2N/A char *p;
2N/A char *basedn = (char *)gres +
2N/A gres->basedn_offset;
2N/A
2N/A /*
2N/A * remember max. DN length seen so far
2N/A */
2N/A dnlen = strlen(entry_dn) + 1;
2N/A if (dnlen > gres->dnlen_max)
2N/A gres->dnlen_max = dnlen;
2N/A
2N/A /* if entry_dn ends with basedn */
2N/A if ((p = strew(entry_dn, basedn)) != NULL) {
2N/A /* This last ',' denotes basedn. */
2N/A *p = ',';
2N/A *(p + 1) = '\0';
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* get group gid */
2N/A gidvalue = __ns_ldap_getAttr(entry, "gidnumber");
2N/A if (gidvalue == NULL)
2N/A return (NSS_STR_PARSE_NO_RESULT);
2N/A gid = gidvalue[0];
2N/A /* length including 2 '\0' */
2N/A gid_dn_slen = strlen(gid) + strlen(entry_dn) + 2;
2N/A while (gid_dn_slen + dlen > dbuf_size) {
2N/A if (memberof_realloc_buf(bufpp, grespp, pbufpp,
2N/A &bptr, &dbuf_size) == NULL)
2N/A return (NSS_STR_PARSE_PARSE);
2N/A else
2N/A gres = *grespp;
2N/A }
2N/A
2N/A (void) snprintf(bptr, dbuf_size - dlen, "%s%c%s%c", gid, '\0',
2N/A entry_dn, '\0');
2N/A dlen += gid_dn_slen;
2N/A gres->next_byte_offset = dlen;
2N/A gres->parents++;
2N/A if (*entry_dn != '\0')
2N/A gres->group_dns++;
2N/A return (NSS_STR_PARSE_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * TLE_memberof_cb is a callback function callable by libsldap
2N/A * to process each one of the group entries returned by an AD
2N/A * memberof Transitive Link Expansion search.
2N/A */
2N/Aint
2N/ATLE_memberof_cb(const ns_ldap_entry_t *entry, const void *userdata)
2N/A{
2N/A _memberof_tl_cb_t *bc = (_memberof_tl_cb_t *)userdata;
2N/A int rc;
2N/A
2N/A rc = pack_group_gid_dn(bc->bufpp, entry,
2N/A bc->grespp, bc->pbufpp, 0);
2N/A if (rc == NSS_STR_PARSE_SUCCESS)
2N/A return (NS_LDAP_CB_NEXT);
2N/A else
2N/A return (NS_LDAP_OP_FAILED);
2N/A}
2N/A
2N/A/*
2N/A * key_groupDN_psearch is a memberof psearch function that finds all
2N/A * the groups that the group identified by dn is a member of. This
2N/A * is used to resolve nested group membership.
2N/A */
2N/Astatic void
2N/Akey_groupDN_psearch(const char *tag, const char *dn, void **bufpp)
2N/A{
2N/A int i, ret;
2N/A int no_sort_search = B_FALSE;
2N/A char searchfilter[SEARCHFILTERLEN * 4];
2N/A char userdata[SEARCHFILTERLEN * 4];
2N/A char searchDN[SEARCHFILTERLEN * 4];
2N/A ns_ldap_result_t *result = NULL;
2N/A ns_ldap_error_t *error = NULL;
2N/A ns_ldap_entry_t *curEntry;
2N/A _memberof_res_t *gres;
2N/A nss_pheader_t *pbuf = (nss_pheader_t *)*bufpp;
2N/A size_t old_dlen;
2N/A
2N/A /* Escape any special characters in the DN first. */
2N/A if (_ldap_filter_name(searchDN, dn, sizeof (searchDN)) != 0) {
2N/A ret = NSS_NOTFOUND;
2N/A goto cleanup;
2N/A }
2N/A
2N/A /*
2N/A * Do a search to get all the group entries which have the
2N/A * group DN as one of the values of its member or uniqueMember
2N/A * attribute.
2N/A */
2N/A
2N/A ret = snprintf(searchfilter, sizeof (searchfilter), _F_GETGRGRP,
2N/A searchDN, searchDN, searchDN);
2N/A if (ret >= sizeof (searchfilter) || ret < 0) {
2N/A ret = NSS_NOTFOUND;
2N/A goto cleanup;
2N/A }
2N/A ret = snprintf(userdata, sizeof (userdata), _F_GETGRGRP_SSD,
2N/A searchDN, searchDN, searchDN);
2N/A if (ret >= sizeof (userdata) || ret < 0) {
2N/A ret = NSS_NOTFOUND;
2N/A goto cleanup;
2N/A }
2N/A
2N/A /*
2N/A * Use __ns_ldap_list() if not able to get VLV results from
2N/A * the server.
2N/A */
2N/A if (strbw(tag, PSEARCH_TAG_GROUP_DN_NS)) {
2N/A no_sort_search = B_TRUE;
2N/A ret = __ns_ldap_list(_GROUP, searchfilter,
2N/A _merge_SSD_filter, gr_attrs6, NULL, 0,
2N/A &result, &error, NULL, userdata);
2N/A } else {
2N/A ret = __ns_ldap_list_sort(_GROUP, searchfilter, _G_NAME,
2N/A _merge_SSD_filter, gr_attrs6, NULL, NS_LDAP_PAGE_CTRL,
2N/A &result, &error, NULL, userdata);
2N/A }
2N/A if (ret == NS_LDAP_NOTFOUND) {
2N/A ret = NSS_NOTFOUND;
2N/A goto cleanup;
2N/A } else if (ret != NS_LDAP_SUCCESS) {
2N/A ret = NSS_ERROR;
2N/A goto cleanup;
2N/A }
2N/A
2N/A /*
2N/A * Set up the group result control block which is at the start
2N/A * of result area.
2N/A */
2N/A setup_buf_memberof_res(*bufpp, &gres);
2N/A pbuf = (nss_pheader_t *)*bufpp;
2N/A old_dlen = gres->next_byte_offset;
2N/A gres->no_sort_search = no_sort_search;
2N/A
2N/A
2N/A /*
2N/A * Store gidnumber and entry DN from each result entry in the
2N/A * result area.
2N/A */
2N/A for (curEntry = result->entry, i = 0; i < result->entries_count;
2N/A i++, curEntry = curEntry->next) {
2N/A ret = pack_group_gid_dn(bufpp, curEntry, &gres,
2N/A &pbuf, is_nested_group(curEntry));
2N/A }
2N/A
2N/A /* Return NSS_NOTFOUND if no new data added. */
2N/A if (old_dlen == gres->next_byte_offset) {
2N/A ret = NSS_NOTFOUND;
2N/A } else {
2N/A ret = NSS_SUCCESS;
2N/A pbuf->data_len = gres->next_byte_offset;
2N/A }
2N/A
2N/Acleanup:
2N/A if (result != NULL)
2N/A (void) __ns_ldap_freeResult(&result);
2N/A if (error != NULL)
2N/A (void) __ns_ldap_freeError(&error);
2N/A pbuf->p_status = ret;
2N/A}
2N/A
2N/A/*
2N/A * key_uid_psearch is a memberof psearch function that finds all
2N/A * the groups that the user identified by key is a member of.
2N/A */
2N/Astatic void
2N/Akey_uid_psearch(const char *key, void **bufpp)
2N/A{
2N/A int i, k, ret;
2N/A char **membervalue;
2N/A char searchfilter[SEARCHFILTERLEN * 8];
2N/A char userdata[SEARCHFILTERLEN * 8];
2N/A char searchDN[SEARCHFILTERLEN * 4];
2N/A char **dn;
2N/A char *user_dn = NULL;
2N/A char *buffer = NULL;
2N/A char *sptr, *optr, *dptr;
2N/A size_t old_dlen;
2N/A ns_ldap_result_t *result = NULL;
2N/A ns_ldap_result_t *result1 = NULL;
2N/A ns_ldap_entry_t *curEntry;
2N/A ns_ldap_attr_t *members;
2N/A ns_ldap_error_t *error = NULL;
2N/A ns_ldap_entry_t *extra_info = NULL;
2N/A nss_pheader_t *pbuf;
2N/A _memberof_res_t *gres;
2N/A _memberof_tl_cb_t memberof_tc;
2N/A ns_ldap_server_type_t serverType = NS_LDAP_SERVERTYPE_UNKNOWN;
2N/A
2N/A pbuf = (nss_pheader_t *)*bufpp;
2N/A if (key == NULL || *key == '\0') {
2N/A pbuf->p_status = NSS_ERROR;
2N/A return;
2N/A }
2N/A
2N/A /*
2N/A * Try searching the user in nscd's passwd cache, in case
2N/A * user DN is available there.
2N/A */
2N/A ret = do_lookup(NSS_DBNAM_PASSWD, NULL, key,
2N/A NSS_DBOP_PASSWD_BYNAME, NSS_LINELEN_PASSWD, 1,
2N/A &buffer, NULL, NULL);
2N/A
2N/A /*
2N/A * If found in nscd cache, get the user's DN and server type
2N/A * if available.
2N/A */
2N/A if (ret == NSS_SUCCESS) {
2N/A int slen;
2N/A pbuf = (nss_pheader_t *)buffer;
2N/A sptr = buffer + pbuf->data_off;
2N/A slen = strlen(sptr);
2N/A if (pbuf->data_len != slen) {
2N/A optr = sptr + slen + 1;
2N/A /* check for optional data's DN tag */
2N/A if (strbw(optr, NSS_LDAP_DN_TAG)) { /* user DN */
2N/A /* first get server type string */
2N/A optr = optr + NSS_LDAP_DN_TAG_LEN;
2N/A dptr = strchr(optr, ':');
2N/A *dptr = '\0';
2N/A
2N/A /* then set server type */
2N/A if (strcmp(optr,
2N/A NS_LDAP_ATTR_VAL_SERVER_AD) == 0)
2N/A serverType = NS_LDAP_SERVERTYPE_AD;
2N/A else if (strcmp(optr,
2N/A NS_LDAP_ATTR_VAL_SERVER_OPENLDAP) == 0)
2N/A serverType =
2N/A NS_LDAP_SERVERTYPE_OPENLDAP;
2N/A
2N/A /* user DN follows server type */
2N/A user_dn = dptr + 1;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * If no user DN found in cache, call __ns_ldap_list_ext to get it.
2N/A */
2N/A if (user_dn == NULL) {
2N/A ret = snprintf(searchfilter, sizeof (searchfilter),
2N/A _F_GETPWNAM, key);
2N/A if (ret >= sizeof (searchfilter) || ret < 0) {
2N/A ret = NSS_NOTFOUND;
2N/A goto cleanup;
2N/A }
2N/A ret = snprintf(userdata, sizeof (userdata), _F_GETPWNAM_SSD,
2N/A key);
2N/A if (ret >= sizeof (userdata) || ret < 0) {
2N/A ret = NSS_NOTFOUND;
2N/A goto cleanup;
2N/A }
2N/A
2N/A ret = __ns_ldap_list_ext(_PASSWD, searchfilter,
2N/A _merge_SSD_filter, pw_attrs, NULL, NS_LDAP_NOT_CVT_DN,
2N/A &result1, &error, NULL, userdata, gr_extra_info_attr,
2N/A &extra_info);
2N/A if (ret == NS_LDAP_NOTFOUND) {
2N/A ret = NSS_NOTFOUND;
2N/A goto cleanup;
2N/A } else if (ret != NS_LDAP_SUCCESS) {
2N/A pbuf->p_errno = errno;
2N/A ret = NSS_ERROR;
2N/A goto cleanup;
2N/A }
2N/A
2N/A dn = __ns_ldap_getAttr(result1->entry, "dn");
2N/A if (dn == NULL || dn[0] == NULL) {
2N/A ret = NSS_ERROR;
2N/A goto cleanup;
2N/A }
2N/A user_dn = dn[0];
2N/A
2N/A /* Determine which type of server the entry is from. */
2N/A if (extra_info != NULL) {
2N/A serverType = _nss_ldap_get_server_type(extra_info,
2N/A NULL);
2N/A __ns_ldap_freeEntry(extra_info);
2N/A extra_info = NULL;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Set up the group result control block which is at the start
2N/A * of result area.
2N/A */
2N/A setup_buf_memberof_res(*bufpp, &gres);
2N/A pbuf = (nss_pheader_t *)*bufpp;
2N/A old_dlen = gres->next_byte_offset;
2N/A
2N/A /* Escape any special characters in the user_dn first. */
2N/A if (_ldap_filter_name(searchDN, user_dn, sizeof (searchDN)) != 0) {
2N/A ret = NSS_NOTFOUND;
2N/A goto cleanup;
2N/A }
2N/A
2N/A /*
2N/A * If the ldap server is an AD, do a Transitive Link Expansion
2N/A * search to get all parent groups in one search.
2N/A */
2N/A if (serverType == NS_LDAP_SERVERTYPE_AD) {
2N/A ret = snprintf(searchfilter, sizeof (searchfilter),
2N/A _F_GETGRMOF_BL, searchDN);
2N/A if (ret >= sizeof (searchfilter) || ret < 0) {
2N/A ret = NSS_NOTFOUND;
2N/A goto cleanup;
2N/A }
2N/A
2N/A /*
2N/A * Use a callback function to process each result entry.
2N/A * userdata is the callback control block.
2N/A */
2N/A memberof_tc.bufpp = bufpp;
2N/A memberof_tc.grespp = &gres;
2N/A memberof_tc.pbufpp = &pbuf;
2N/A
2N/A ret = __ns_ldap_list_sort(_GROUP, searchfilter,
2N/A _G_NAME, NULL, gr_attrs_mof_tl, NULL,
2N/A NS_LDAP_PAGE_CTRL, &result, &error,
2N/A TLE_memberof_cb, &memberof_tc);
2N/A if (result != NULL)
2N/A (void) __ns_ldap_freeResult(&result);
2N/A if (error != NULL)
2N/A (void) __ns_ldap_freeError(&error);
2N/A
2N/A /*
2N/A * Done if no error and have result data.
2N/A * libsldap returns NS_LDAP_NOTFOUND if
2N/A * all result entries were consumed by
2N/A * the callback function.
2N/A */
2N/A if (ret == NS_LDAP_NOTFOUND &&
2N/A old_dlen != gres->next_byte_offset) {
2N/A pbuf->data_len = gres->next_byte_offset;
2N/A ret = NSS_SUCCESS;
2N/A goto cleanup;
2N/A }
2N/A
2N/A /*
2N/A * Otherwise clean up incomplete result data in the buffer
2N/A * by resetting the result control block.
2N/A */
2N/A if (old_dlen != gres->next_byte_offset) {
2N/A setup_buf_memberof_res(*bufpp, &gres);
2N/A pbuf = (nss_pheader_t *)*bufpp;
2N/A old_dlen = gres->next_byte_offset;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Not AD, or the Transitive Link Expansion search didn't work,
2N/A * do a search to get all the group entries which have the
2N/A * key as a memberuid or user_dn as one of the values of its
2N/A * member or uniqueMember attribute.
2N/A */
2N/A
2N/A ret = snprintf(searchfilter, sizeof (searchfilter), _F_GETGRUSR,
2N/A key, searchDN, searchDN, searchDN);
2N/A if (ret >= sizeof (searchfilter) || ret < 0) {
2N/A ret = NSS_NOTFOUND;
2N/A goto cleanup;
2N/A }
2N/A ret = snprintf(userdata, sizeof (userdata), _F_GETGRUSR_SSD,
2N/A key, searchDN, searchDN, searchDN);
2N/A if (ret >= sizeof (userdata) || ret < 0) {
2N/A ret = NSS_NOTFOUND;
2N/A goto cleanup;
2N/A }
2N/A
2N/A /*
2N/A * If the ldap server is an openLDAP, don't sort the results
2N/A * as there's no order rule for most attributes.
2N/A */
2N/A if (serverType == NS_LDAP_SERVERTYPE_OPENLDAP) {
2N/A gres->no_sort_search = B_TRUE;
2N/A ret = __ns_ldap_list(_GROUP, searchfilter,
2N/A _merge_SSD_filter, gr_attrs5, NULL, 0,
2N/A &result, &error, NULL, userdata);
2N/A } else {
2N/A ret = __ns_ldap_list_sort(_GROUP, searchfilter, _G_NAME,
2N/A _merge_SSD_filter, gr_attrs5, NULL, NS_LDAP_PAGE_CTRL,
2N/A &result, &error, NULL, userdata);
2N/A }
2N/A
2N/A if (ret == NS_LDAP_NOTFOUND) {
2N/A ret = NSS_NOTFOUND;
2N/A goto cleanup;
2N/A } else if (ret != NS_LDAP_SUCCESS) {
2N/A ret = NSS_ERROR;
2N/A goto cleanup;
2N/A }
2N/A
2N/A /*
2N/A * Store gidnumber and entry DN from each result entry in the
2N/A * result area.
2N/A */
2N/A for (curEntry = result->entry, i = 0; i < result->entries_count;
2N/A i++, curEntry = curEntry->next) {
2N/A boolean_t username_matched = B_FALSE;
2N/A boolean_t memberDN_matched = B_FALSE;
2N/A
2N/A /*
2N/A * Check to see if this group is found by a matched username.
2N/A */
2N/A members = __ns_ldap_getAttrStruct(curEntry, _G_MEMBERUID);
2N/A if (members != NULL && members->attrvalue != NULL) {
2N/A membervalue = members->attrvalue;
2N/A for (k = 0; k < members->value_count; k++) {
2N/A if (strcmp(membervalue[k], key) == 0) {
2N/A username_matched = B_TRUE;
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* Otherwise, found by a matched member/uniqueMember DN ? */
2N/A if (!username_matched) {
2N/A members = __ns_ldap_getAttrStruct(curEntry, _G_MEMBER);
2N/A if (members != NULL && members->attrvalue != NULL)
2N/A memberDN_matched = B_TRUE;
2N/A else {
2N/A members = __ns_ldap_getAttrStruct(curEntry,
2N/A _G_UNIQUEMEMBER);
2N/A if (members != NULL &&
2N/A members->attrvalue != NULL)
2N/A memberDN_matched = B_TRUE;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Not matched ? Most likely, unamename not listed as
2N/A * one of the memberUid attribute values, skip.
2N/A */
2N/A if (!username_matched && !memberDN_matched)
2N/A continue;
2N/A
2N/A ret = pack_group_gid_dn(bufpp, curEntry, &gres,
2N/A &pbuf, is_nested_group(curEntry));
2N/A }
2N/A
2N/A /* Return NSS_NOTFOUND if no new data added. */
2N/A if (old_dlen == gres->next_byte_offset) {
2N/A ret = NSS_NOTFOUND;
2N/A } else {
2N/A ret = NSS_SUCCESS;
2N/A pbuf->data_len = gres->next_byte_offset;
2N/A }
2N/A
2N/Acleanup:
2N/A if (buffer != NULL)
2N/A free(buffer);
2N/A if (result != NULL)
2N/A (void) __ns_ldap_freeResult(&result);
2N/A if (result1 != NULL)
2N/A (void) __ns_ldap_freeResult(&result1);
2N/A if (error != NULL)
2N/A (void) __ns_ldap_freeError(&error);
2N/A pbuf->p_status = ret;
2N/A}
2N/A
2N/A/*
2N/A * _nss_ldap_memberof_psearch is the top level psearch function for
2N/A * memberof searches. The search key stored in the packed buffer
2N/A * header is tagged to indicate whether the search key is a member uid
2N/A * or group DN. If uid, key_uid_psearch will be called. Otherwise,
2N/A * key_groupDN_psearch will be called.
2N/A */
2N/Astatic void
2N/A_nss_ldap_memberof_psearch(void **bufpp, size_t length)
2N/A{
2N/A nss_pheader_t *pbuf;
2N/A nss_XbyY_args_t arg;
2N/A nss_status_t ret;
2N/A const char *key;
2N/A char *dbname;
2N/A int dbop = 0;
2N/A
2N/A pbuf = (nss_pheader_t *)*bufpp;
2N/A
2N/A ret = _nss_ldap_grgetkey(*bufpp, length, &dbname, &dbop, &arg);
2N/A if (ret != NSS_SUCCESS) {
2N/A pbuf->p_status = ret;
2N/A return;
2N/A }
2N/A key = arg.key.name;
2N/A
2N/A /* Searching with a member uid or group DN ? */
2N/A if (strbw(key, PSEARCH_TAG_UID)) {
2N/A /* member uid */
2N/A key_uid_psearch(key + PSEARCH_TAG_LEN, bufpp);
2N/A } else if (strbw(key, PSEARCH_TAG_GROUP_DN) ||
2N/A strbw(key, PSEARCH_TAG_GROUP_DN_NS)) {
2N/A /* group DN */
2N/A key_groupDN_psearch(key, key + PSEARCH_TAG_LEN, bufpp);
2N/A } else {
2N/A pbuf->p_status = NSS_NOTFOUND;
2N/A return;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * get_parent_gids find all groups, including nested ones, that a user
2N/A * is a member of and write the gid number of these groups in the output
2N/A * gid array. It may call itself recursively when dealing with nested
2N/A * group. The nest level is limited by MEMBEROF_MAX_RECURSIVE_CALL.
2N/A */
2N/Astatic nss_status_t
2N/Aget_parent_gids(struct nss_groupsbymem *argp, const char *tag,
2N/A const char *key, _nss_ldap_list_t **gl, int *rec_callp)
2N/A{
2N/A nss_pheader_t *pbuf;
2N/A nss_status_t res;
2N/A _memberof_res_t *gres;
2N/A char *sptr, *sptr_s, *gp;
2N/A char *buffer = NULL;
2N/A int i, k;
2N/A gid_t gid;
2N/A char *new_tag;
2N/A char *gdn;
2N/A int gdn_len;
2N/A char *wbdn = NULL;
2N/A
2N/A /*
2N/A * If the depth of recursion reaches MEMBEROF_MAX_RECURSIVE_CALL
2N/A * then stop and return NSS_SUCCESS.
2N/A */
2N/A *rec_callp += 1;
2N/A if (*rec_callp == MEMBEROF_MAX_RECURSIVE_CALL) {
2N/A res = NSS_SUCCESS;
2N/A goto out;
2N/A }
2N/A
2N/A /* create the memberof nscd backend cache if not done yet */
2N/A if (memberof_cache_state == NSS_LDAP_CACHE_UNINITED)
2N/A _nss_ldap_memberof_init();
2N/A
2N/A /*
2N/A * For loop detection, check to see if key has already been
2N/A * processed by adding it to the grlist. If not able to add,
2N/A * then it has been processed or error occurred, ignore it.
2N/A */
2N/A if (nss_ldap_list_add(gl, key) != NSS_LDAP_LIST_SUCCESS) {
2N/A res = NSS_SUCCESS;
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * Check to see if memberof result available in nscd's memberof cache.
2N/A * If not, get it from the LDAP server.
2N/A */
2N/A res = do_lookup(LDAP_MEMBEROF_CACHE_NM, tag, key,
2N/A 0, _PSEARCH_BUF_BLK, 0, &buffer,
2N/A _nss_ldap_grgetkey, _nss_ldap_memberof_psearch);
2N/A if (res != NSS_SUCCESS)
2N/A goto out;
2N/A
2N/A /* add gids to the output gid array */
2N/A pbuf = (nss_pheader_t *)buffer;
2N/A gp = buffer + pbuf->data_off;
2N/A gres = (void *)gp;
2N/A sptr = (char *)gres + sizeof (_memberof_res_t);
2N/A if (gres->basedn_len != 0)
2N/A sptr += gres->basedn_len + 1;
2N/A sptr_s = sptr;
2N/A for (i = 0; i < gres->parents; i++) {
2N/A gid = (gid_t)strtol(sptr, (char **)NULL, 10);
2N/A /* move pass gid */
2N/A sptr += strlen(sptr) + 1;
2N/A /* Skip DN of the group entry, it will be processed later. */
2N/A sptr += strlen(sptr) + 1;
2N/A /* no need to add if already in the output */
2N/A for (k = 0; k < argp->numgids; k++) {
2N/A if (argp->gid_array[k] == gid) {
2N/A /* already exists */
2N/A break;
2N/A }
2N/A }
2N/A /* ignore duplicate gid */
2N/A if (k < argp->numgids)
2N/A continue;
2N/A
2N/A argp->gid_array[argp->numgids++] = gid;
2N/A if (argp->numgids >= argp->maxgids)
2N/A break;
2N/A }
2N/A
2N/A /*
2N/A * Done if no group DNs to process, i.e., no nested groups.
2N/A */
2N/A if (gres->group_dns == 0) {
2N/A res = NSS_SUCCESS;
2N/A goto out;
2N/A }
2N/A
2N/A sptr = sptr_s;
2N/A for (i = gres->parents; --i >= 0; ) {
2N/A if (argp->numgids >= argp->maxgids)
2N/A break;
2N/A /* group DN always follows the gid string, so move pass gid */
2N/A gdn = sptr += strlen(sptr) + 1;
2N/A /* if NULL group DN, not a nested group, nothing to do */
2N/A if (*gdn == '\0') {
2N/A sptr++;
2N/A continue;
2N/A }
2N/A
2N/A /* Restore group DN if it was truncated to save space. */
2N/A if (gres->basedn_len > 0) {
2N/A gdn_len = strlen(gdn);
2N/A if (gdn[gdn_len - 1] == ',') {
2N/A /*
2N/A * Get a work buffer from the stack
2N/A * that can be reused.
2N/A */
2N/A if (wbdn == NULL)
2N/A wbdn = (char *)alloca(gres->dnlen_max);
2N/A
2N/A /* drop the last ',' */
2N/A (void) strlcpy(wbdn, gdn, gdn_len);
2N/A /*
2N/A * basedn is the first string after the
2N/A * control struct
2N/A */
2N/A (void) strlcat(wbdn, (char *)gres +
2N/A gres->basedn_offset, gres->dnlen_max);
2N/A gdn = wbdn;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * If server is an openLDAP, do a no result sorting
2N/A * search.
2N/A */
2N/A if (gres->no_sort_search)
2N/A new_tag = PSEARCH_TAG_GROUP_DN_NS;
2N/A else
2N/A new_tag = PSEARCH_TAG_GROUP_DN;
2N/A res = get_parent_gids(argp, new_tag, gdn, gl, rec_callp);
2N/A if (res != NSS_SUCCESS && res != NSS_NOTFOUND)
2N/A goto out;
2N/A
2N/A /* move pass group DN */
2N/A sptr += strlen(sptr) + 1;
2N/A }
2N/A
2N/A res = NSS_SUCCESS;
2N/A
2N/Aout:
2N/A if (buffer != NULL)
2N/A free(buffer);
2N/A *rec_callp -= 1;
2N/A return (res);
2N/A}
2N/A
2N/A/*
2N/A * getbymember returns all groups a user is defined in. This function
2N/A * uses different architectural procedures than the other group backend
2N/A * system calls because it's a private interface. This function constructs
2N/A * an ldap search filter using the name invocation parameter. Once the
2N/A * filter is constructed, we search for all matching groups counting
2N/A * and storing each group name, gid, etc. Data marshaling is used for
2N/A * group processing. The function _nss_ldap_group2ent() performs the
2N/A * data marshaling.
2N/A *
2N/A * (const char *)argp->username; (size_t)strlen(argp->username);
2N/A * (gid_t)argp->gid_array; (int)argp->maxgids;
2N/A * (int)argp->numgids;
2N/A */
2N/A
2N/Astatic nss_status_t
2N/Agetbymember(ldap_backend_ptr be, void *a)
2N/A{
2N/A NOTE(ARGUNUSED(be))
2N/A
2N/A int gcnt = (int)0;
2N/A struct nss_groupsbymem *argp = (struct nss_groupsbymem *)a;
2N/A char name[SEARCHFILTERLEN];
2N/A int rec_call = -1;
2N/A _nss_ldap_list_t *grlist = NULL;
2N/A nss_status_t res;
2N/A
2N/A if (strcmp(argp->username, "") == 0 ||
2N/A strcmp(argp->username, "root") == 0)
2N/A return ((nss_status_t)NSS_NOTFOUND);
2N/A
2N/A if (_ldap_filter_name(name, argp->username, sizeof (name)) != 0)
2N/A return ((nss_status_t)NSS_NOTFOUND);
2N/A
2N/A gcnt = (int)argp->numgids;
2N/A
2N/A res = get_parent_gids(argp, PSEARCH_TAG_UID, name, &grlist, &rec_call);
2N/A if (grlist != NULL)
2N/A nss_ldap_list_free(&grlist);
2N/A if (res == NSS_ERROR)
2N/A return (res);
2N/A
2N/A if (gcnt == argp->numgids)
2N/A return ((nss_status_t)NSS_NOTFOUND);
2N/A
2N/A /*
2N/A * Return NSS_SUCCESS only if array is full.
2N/A * Explained in <nss_dbdefs.h>.
2N/A */
2N/A return ((nss_status_t)((argp->numgids == argp->maxgids)
2N/A ? NSS_SUCCESS
2N/A : NSS_NOTFOUND));
2N/A}
2N/A
2N/Astatic ldap_backend_op_t gr_ops[] = {
2N/A _nss_ldap_destr,
2N/A _nss_ldap_endent,
2N/A _nss_ldap_setent,
2N/A _nss_ldap_getent,
2N/A getbynam,
2N/A getbygid,
2N/A getbymember
2N/A};
2N/A
2N/A
2N/A/*ARGSUSED0*/
2N/Anss_backend_t *
2N/A_nss_ldap_group_constr(const char *dummy1, const char *dummy2,
2N/A const char *dummy3)
2N/A{
2N/A
2N/A return ((nss_backend_t *)_nss_ldap_constr(gr_ops,
2N/A sizeof (gr_ops)/sizeof (gr_ops[0]), _GROUP, gr_attrs,
2N/A _nss_ldap_group2str));
2N/A}