summaryrefslogtreecommitdiff
path: root/server/group.c
diff options
context:
space:
mode:
Diffstat (limited to 'server/group.c')
-rw-r--r--server/group.c1106
1 files changed, 1106 insertions, 0 deletions
diff --git a/server/group.c b/server/group.c
new file mode 100644
index 0000000..7eaf4a4
--- /dev/null
+++ b/server/group.c
@@ -0,0 +1,1106 @@
+/*
+ Copyright (C) 1997-2006 Luke Howard
+ This file is part of the nss_ldap library.
+ Contributed by Luke Howard, <lukeh@padl.com>, 1997.
+
+ The nss_ldap library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The nss_ldap library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the nss_ldap library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ $Id$
+*/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <grp.h>
+#include <errno.h>
+#ifdef HAVE_LBER_H
+#include <lber.h>
+#endif
+#ifdef HAVE_LDAP_H
+#include <ldap.h>
+#endif
+#if defined(HAVE_THREAD_H)
+#include <thread.h>
+#elif defined(HAVE_PTHREAD_H)
+#include <pthread.h>
+#endif
+
+#include "ldap-nss.h"
+#include "util.h"
+
+/* the context to use for {set,get,end}grent() calls */
+static struct ent_context *gr_context = NULL;
+
+#ifdef HAVE_USERSEC_H
+typedef struct ldap_initgroups_args
+{
+ char *grplist;
+ size_t listlen;
+ int depth;
+ struct name_list *known_groups;
+ int backlink;
+}
+ldap_initgroups_args_t;
+#else
+typedef struct ldap_initgroups_args
+{
+ gid_t group;
+ long int *start;
+ long int *size;
+ gid_t **groups;
+ long int limit;
+ int depth;
+ struct name_list *known_groups;
+ int backlink;
+}
+ldap_initgroups_args_t;
+#endif /* HAVE_USERSEC_H */
+
+static enum nss_status
+ng_chase (const char *dn, ldap_initgroups_args_t * lia);
+
+static enum nss_status
+ng_chase_backlink (const char ** membersOf, ldap_initgroups_args_t * lia);
+
+/*
+ * Range retrieval logic was reimplemented from example in
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/searching_using_range_retrieval.asp
+ */
+
+static enum nss_status
+do_parse_range (const char *attributeType,
+ const char *attributeDescription, int *start, int *end)
+{
+ enum nss_status stat = NSS_STATUS_NOTFOUND;
+ char *attribute;
+ size_t attributeTypeLength;
+ size_t attributeDescriptionLength;
+ char *p;
+#ifdef HAVE_STRTOK_R
+ char *st = NULL;
+#endif
+
+ *start = 0;
+ *end = -1;
+
+ if (strcasecmp (attributeType, attributeDescription) == 0)
+ {
+ return NSS_STATUS_SUCCESS;
+ }
+
+ attributeDescriptionLength = strlen (attributeDescription);
+ attributeTypeLength = strlen (attributeType);
+
+ if (attributeDescriptionLength < attributeTypeLength)
+ {
+ /* could not be a subtype */
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ /* XXX need to copy as strtok() is destructive */
+ attribute = strdup (attributeDescription);
+ if (attribute == NULL)
+ {
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+#ifndef HAVE_STRTOK_R
+ for (p = strtok (attribute, ";"); p != NULL; p = strtok (NULL, ";"))
+#else
+ for (p = strtok_r (attribute, ";", &st);
+ p != NULL; p = strtok_r (NULL, ";", &st))
+#endif /* !HAVE_STRTOK_R */
+ {
+ char *q;
+
+ if (p == attribute)
+ {
+ if (strcasecmp (p, attributeType) != 0)
+ {
+ free (attribute);
+ return NSS_STATUS_NOTFOUND;
+ }
+ }
+ else if (strncasecmp (p, "range=", sizeof ("range=") - 1) == 0)
+ {
+ p += sizeof ("range=") - 1;
+
+ q = strchr (p, '-');
+ if (q == NULL)
+ {
+ free (attribute);
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ *q++ = '\0';
+
+ *start = strtoul (p, (char **) NULL, 10);
+ if (strcmp (q, "*") == 0)
+ *end = -1;
+ else
+ *end = strtoul (q, (char **) NULL, 10);
+
+ stat = NSS_STATUS_SUCCESS;
+ break;
+ }
+ }
+
+ free (attribute);
+ return stat;
+}
+
+static enum nss_status
+do_get_range_values (LDAPMessage * e,
+ const char *attributeType,
+ int *start, int *end, char ***pGroupMembers)
+{
+ enum nss_status stat = NSS_STATUS_NOTFOUND;
+ BerElement *ber = NULL;
+ char *attribute;
+
+ *pGroupMembers = NULL;
+
+ for (attribute = _nss_ldap_first_attribute (e, &ber);
+ attribute != NULL; attribute = _nss_ldap_next_attribute (e, ber))
+ {
+ stat = do_parse_range (attributeType, attribute, start, end);
+ if (stat == NSS_STATUS_SUCCESS)
+ {
+ *pGroupMembers = _nss_ldap_get_values (e, attribute);
+ if (*pGroupMembers == NULL)
+ {
+ stat = NSS_STATUS_NOTFOUND;
+ }
+ else if ((*pGroupMembers)[0] == NULL)
+ {
+ ldap_value_free (*pGroupMembers);
+ *pGroupMembers = NULL;
+ stat = NSS_STATUS_NOTFOUND;
+ }
+ }
+
+#ifdef HAVE_LDAP_MEMFREE
+ ldap_memfree (attribute);
+#endif
+
+ if (stat == NSS_STATUS_SUCCESS)
+ break;
+ }
+
+ if (ber != NULL)
+ ber_free (ber, 0);
+
+ return stat;
+}
+
+/*
+ * Format an attribute with description as:
+ * attribute;range=START-END
+ */
+static enum nss_status
+do_construct_range_attribute (const char *attribute,
+ int start,
+ int end,
+ char **buffer,
+ size_t * buflen,
+ const char **pAttributeWithRange)
+{
+ size_t len;
+ char startbuf[32], endbuf[32];
+
+ snprintf (startbuf, sizeof (startbuf), "%u", start);
+
+ if (end != -1)
+ snprintf (endbuf, sizeof (endbuf), "%u", end);
+ else
+ snprintf (endbuf, sizeof (endbuf), "*");
+
+ len = strlen (attribute) + sizeof (";range=") - 1;
+ len += strlen (startbuf) + 1 /* - */ + strlen (endbuf);
+ len++; /* \0 */
+
+ if (*buflen < len)
+ return NSS_STATUS_TRYAGAIN;
+
+ *pAttributeWithRange = *buffer;
+
+ snprintf (*buffer, len, "%s;range=%s-%s", attribute, startbuf, endbuf);
+
+ *buffer += len;
+ *buflen -= len;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/*
+ * Expand group members, including nested groups
+ */
+static enum nss_status
+do_parse_group_members (LDAPMessage * e,
+ char ***pGroupMembers,
+ size_t * pGroupMembersCount,
+ size_t * pGroupMembersBufferSize,
+ int *pGroupMembersBufferIsMalloced,
+ char **buffer, size_t * buflen,
+ int *depth,
+ struct name_list **pKnownGroups) /* traversed groups */
+{
+ enum nss_status stat = NSS_STATUS_SUCCESS;
+ char **dnValues = NULL;
+ char **uidValues = NULL;
+ char **groupMembers;
+ size_t groupMembersCount, i;
+ char **valiter;
+ /* support for range retrieval */
+ const char *uniquemember_attr;
+ const char *uniquemember_attrs[2];
+ LDAPMessage *res = NULL;
+ int start, end = 0;
+ char *groupdn = NULL;
+
+ uniquemember_attr = ATM (LM_GROUP, uniqueMember);
+
+ uniquemember_attrs[0] = uniquemember_attr;
+ uniquemember_attrs[1] = NULL;
+
+ if (*depth > LDAP_NSS_MAXGR_DEPTH)
+ {
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ i = *pGroupMembersCount; /* index of next member */
+ groupMembers = *pGroupMembers;
+
+ groupdn = _nss_ldap_get_dn (e);
+ if (groupdn == NULL)
+ {
+ stat = NSS_STATUS_NOTFOUND;
+ goto out;
+ }
+
+ if (_nss_ldap_namelist_find (*pKnownGroups, groupdn))
+ {
+ stat = NSS_STATUS_NOTFOUND;
+ goto out;
+ }
+
+ /* store group DN for nested group loop detection */
+ stat = _nss_ldap_namelist_push (pKnownGroups, groupdn);
+ if (stat != NSS_STATUS_SUCCESS)
+ {
+ goto out;
+ }
+
+ do
+ {
+ if (e == NULL)
+ {
+ stat = NSS_STATUS_NOTFOUND;
+ goto out;
+ }
+
+ groupMembersCount = 0; /* number of members in this group */
+
+ (void) do_get_range_values (e, uniquemember_attrs[0], &start, &end, &dnValues);
+ if (dnValues != NULL)
+ {
+ groupMembersCount += ldap_count_values (dnValues);
+ }
+
+ uidValues = _nss_ldap_get_values (e, ATM (LM_GROUP, memberUid));
+ if (uidValues != NULL)
+ {
+ groupMembersCount += ldap_count_values (uidValues);
+ }
+
+ /*
+ * Check whether we need to increase the group membership buffer.
+ * As an optimization the buffer is preferentially allocated off
+ * the stack
+ */
+ if ((i + groupMembersCount) * sizeof (char *) >=
+ *pGroupMembersBufferSize)
+ {
+ *pGroupMembersBufferSize =
+ (i + groupMembersCount + 1) * sizeof (char *);
+ *pGroupMembersBufferSize +=
+ (LDAP_NSS_NGROUPS * sizeof (char *)) - 1;
+ *pGroupMembersBufferSize -=
+ (*pGroupMembersBufferSize %
+ (LDAP_NSS_NGROUPS * sizeof (char *)));
+
+ if (*pGroupMembersBufferIsMalloced == 0)
+ {
+ groupMembers = *pGroupMembers;
+ *pGroupMembers = NULL; /* force malloc() */
+ }
+
+ *pGroupMembers =
+ (char **) realloc (*pGroupMembers, *pGroupMembersBufferSize);
+ if (*pGroupMembers == NULL)
+ {
+ *pGroupMembersBufferIsMalloced = 0; /* don't try to free */
+ stat = NSS_STATUS_TRYAGAIN;
+ goto out;
+ }
+
+ if (*pGroupMembersBufferIsMalloced == 0)
+ {
+ memcpy (*pGroupMembers, groupMembers, i * sizeof (char *));
+ groupMembers = NULL; /* defensive programming */
+ *pGroupMembersBufferIsMalloced = 1;
+ }
+ }
+
+ groupMembers = *pGroupMembers;
+
+ /* Parse distinguished name members */
+ if (dnValues != NULL)
+ {
+ for (valiter = dnValues; *valiter != NULL; valiter++)
+ {
+ LDAPMessage *res;
+ enum nss_status parseStat;
+ int isNestedGroup = 0;
+ char *uid;
+
+ uid = strrchr (*valiter, '#');
+ if (uid != NULL)
+ {
+ *uid = '\0';
+ }
+
+ parseStat = _nss_ldap_dn2uid (*valiter, &groupMembers[i],
+ buffer, buflen, &isNestedGroup,
+ &res);
+ if (parseStat == NSS_STATUS_SUCCESS)
+ {
+ if (isNestedGroup == 0)
+ {
+ /* just a normal user which we have flattened */
+ i++;
+ continue;
+ }
+
+ (*depth)++;
+ parseStat =
+ do_parse_group_members (_nss_ldap_first_entry (res),
+ &groupMembers, &i,
+ pGroupMembersBufferSize,
+ pGroupMembersBufferIsMalloced,
+ buffer, buflen, depth,
+ pKnownGroups);
+ (*depth)--;
+
+ if (parseStat == NSS_STATUS_TRYAGAIN)
+ {
+ stat = NSS_STATUS_TRYAGAIN;
+ goto out;
+ }
+
+ ldap_msgfree (res);
+ }
+ else if (parseStat == NSS_STATUS_TRYAGAIN)
+ {
+ stat = NSS_STATUS_TRYAGAIN;
+ goto out;
+ }
+ }
+ }
+
+ /* Parse RFC 2307 (flat) members */
+ if (uidValues != NULL)
+ {
+ for (valiter = uidValues; *valiter != NULL; valiter++)
+ {
+ size_t len = strlen (*valiter) + 1;
+ if (*buflen < len)
+ {
+ stat = NSS_STATUS_TRYAGAIN;
+ goto out;
+ }
+ groupMembers[i] = *buffer;
+ *buffer += len;
+ *buflen -= len;
+
+ memcpy (groupMembers[i++], *valiter, len);
+ }
+ }
+
+ /* Get next range for Active Directory compat */
+ if (end != -1)
+ {
+ stat = do_construct_range_attribute (uniquemember_attr,
+ end + 1,
+ -1,
+ buffer,
+ buflen,
+ &uniquemember_attrs[0]);
+ if (stat == NSS_STATUS_SUCCESS)
+ {
+ if (dnValues != NULL)
+ {
+ ldap_value_free (dnValues);
+ dnValues = NULL;
+ }
+ if (uidValues != NULL)
+ {
+ ldap_value_free (uidValues);
+ uidValues = NULL;
+ }
+ if (res != NULL)
+ {
+ ldap_msgfree (res);
+ res = NULL;
+ }
+
+ stat = _nss_ldap_read (groupdn, uniquemember_attrs, &res);
+ if (stat != NSS_STATUS_SUCCESS)
+ goto out;
+
+ e = _nss_ldap_first_entry (res);
+ }
+ }
+ }
+ while (end != -1);
+
+out:
+ if (dnValues != NULL)
+ ldap_value_free (dnValues);
+ if (uidValues != NULL)
+ ldap_value_free (uidValues);
+ if (res != NULL)
+ ldap_msgfree (res);
+ if (groupdn != NULL)
+#ifdef HAVE_LDAP_MEMFREE
+ ldap_memfree (groupdn);
+#else
+ free (groupdn);
+#endif
+
+ *pGroupMembers = groupMembers;
+ *pGroupMembersCount = i;
+
+ return stat;
+}
+
+/*
+ * "Fix" group membership list into caller provided buffer,
+ * and NULL terminate.
+*/
+static enum nss_status
+do_fix_group_members_buffer (char **mallocedGroupMembers,
+ size_t groupMembersCount,
+ char ***pGroupMembers,
+ char **buffer, size_t * buflen)
+{
+ size_t len;
+
+ len = (groupMembersCount + 1) * sizeof (char *);
+
+ if (bytesleft (*buffer, *buflen, char *) < len)
+ {
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ align (*buffer, *buflen, char *);
+ *pGroupMembers = (char **) *buffer;
+ *buffer += len;
+ *buflen -= len;
+
+ memcpy (*pGroupMembers, mallocedGroupMembers,
+ groupMembersCount * sizeof (char *));
+ (*pGroupMembers)[groupMembersCount] = NULL;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+static enum nss_status
+_nss_ldap_parse_gr (LDAPMessage * e,
+ struct ldap_state * pvt,
+ void *result, char *buffer, size_t buflen)
+{
+ struct group *gr = (struct group *) result;
+ char *gid;
+ enum nss_status stat;
+ char **groupMembers;
+ size_t groupMembersCount;
+ size_t groupMembersBufferSize;
+ char *groupMembersBuffer[LDAP_NSS_NGROUPS];
+ int groupMembersBufferIsMalloced;
+ int depth;
+ struct name_list *knownGroups = NULL;
+
+ stat =
+ _nss_ldap_assign_attrval (e, ATM (LM_GROUP, gidNumber), &gid, &buffer,
+ &buflen);
+ if (stat != NSS_STATUS_SUCCESS)
+ return stat;
+
+ gr->gr_gid =
+ (*gid == '\0') ? (unsigned) GID_NOBODY : (gid_t) strtoul (gid,
+ (char **) NULL,
+ 10);
+
+ stat =
+ _nss_ldap_getrdnvalue (e, ATM (LM_GROUP, cn), &gr->gr_name, &buffer,
+ &buflen);
+ if (stat != NSS_STATUS_SUCCESS)
+ return stat;
+
+ stat =
+ _nss_ldap_assign_userpassword (e, ATM (LM_GROUP, userPassword),
+ &gr->gr_passwd, &buffer, &buflen);
+ if (stat != NSS_STATUS_SUCCESS)
+ return stat;
+
+ if (_nss_ldap_test_config_flag (NSS_LDAP_FLAGS_RFC2307BIS))
+ {
+ groupMembers = groupMembersBuffer;
+ groupMembersCount = 0;
+ groupMembersBufferSize = sizeof (groupMembers);
+ groupMembersBufferIsMalloced = 0;
+ depth = 0;
+
+ stat = do_parse_group_members (e, &groupMembers, &groupMembersCount,
+ &groupMembersBufferSize,
+ &groupMembersBufferIsMalloced, &buffer,
+ &buflen, &depth, &knownGroups);
+ if (stat != NSS_STATUS_SUCCESS)
+ {
+ if (groupMembersBufferIsMalloced)
+ free (groupMembers);
+ _nss_ldap_namelist_destroy (&knownGroups);
+ return stat;
+ }
+
+ stat = do_fix_group_members_buffer (groupMembers, groupMembersCount,
+ &gr->gr_mem, &buffer, &buflen);
+
+ if (groupMembersBufferIsMalloced)
+ free (groupMembers);
+ _nss_ldap_namelist_destroy (&knownGroups);
+ }
+ else
+ {
+ stat =
+ _nss_ldap_assign_attrvals (e, ATM (LM_GROUP, memberUid), NULL,
+ &gr->gr_mem, &buffer, &buflen, NULL);
+ }
+
+ return stat;
+}
+
+/*
+ * Add a group ID to a group list, and optionally the group IDs
+ * of any groups to which this group belongs (RFC2307bis nested
+ * group expansion is done by do_parse_initgroups_nested()).
+ */
+static enum nss_status
+do_parse_initgroups (LDAPMessage * e,
+ struct ldap_state * pvt, void *result,
+ char *buffer, size_t buflen)
+{
+ char **values;
+ ssize_t i;
+ gid_t gid;
+ ldap_initgroups_args_t *lia = (ldap_initgroups_args_t *) result;
+
+ values = _nss_ldap_get_values (e, ATM (LM_GROUP, gidNumber));
+ if (values == NULL)
+ {
+ /* invalid group; skip it */
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ if (values[0] == NULL)
+ {
+ /* invalid group; skip it */
+ ldap_value_free (values);
+ return NSS_STATUS_NOTFOUND;
+ }
+
+#ifdef HAVE_USERSEC_H
+ i = strlen (values[0]);
+ lia->grplist = realloc (lia->grplist, lia->listlen + i + 2);
+ if (lia->grplist == NULL)
+ {
+ ldap_value_free (values);
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy (lia->grplist + lia->listlen, values[0], i);
+ lia->grplist[lia->listlen + i] = ',';
+ lia->listlen += i + 1;
+ ldap_value_free (values);
+#else
+ gid = strtoul (values[0], (char **) NULL, 10);
+ ldap_value_free (values);
+
+ if (gid == LONG_MAX && errno == ERANGE)
+ {
+ /* invalid group, skip it */
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ if (gid == lia->group)
+ {
+ /* primary group, so skip it */
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ if (lia->limit > 0)
+ {
+ if (*(lia->start) >= lia->limit)
+ {
+ /* can't fit any more */
+ return NSS_STATUS_TRYAGAIN;
+ }
+ }
+ if (*(lia->start) == *(lia->size))
+ {
+ /* Need a bigger buffer */
+ *(lia->groups) = (gid_t *) realloc (*(lia->groups),
+ 2 * *(lia->size) * sizeof (gid_t));
+ if (*(lia->groups) == NULL)
+ {
+ return NSS_STATUS_TRYAGAIN;
+ }
+ *(lia->size) *= 2;
+ }
+
+ /* weed out duplicates; is this really our responsibility? */
+ for (i = 0; i < *(lia->start); i++)
+ {
+ if ((*(lia->groups))[i] == gid)
+ {
+ return NSS_STATUS_NOTFOUND;
+ }
+ }
+
+ /* add to group list */
+ (*(lia->groups))[*(lia->start)] = gid;
+ (*(lia->start)) += 1;
+#endif /* HAVE_USERSEC_H */
+
+ return NSS_STATUS_NOTFOUND;
+}
+
+static enum nss_status
+do_parse_initgroups_nested (LDAPMessage * e,
+ struct ldap_state * pvt, void *result,
+ char *buffer, size_t buflen)
+{
+ enum nss_status status;
+ ldap_initgroups_args_t *lia = (ldap_initgroups_args_t *) result;
+ char **values;
+ char *groupdn;
+
+ status = do_parse_initgroups (e, pvt, result, buffer, buflen);
+ if (status != NSS_STATUS_NOTFOUND)
+ {
+ return status;
+ }
+
+ if (!_nss_ldap_test_config_flag (NSS_LDAP_FLAGS_RFC2307BIS))
+ {
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ if (lia->backlink != 0)
+ {
+ /*
+ * Now add the GIDs of any groups of which this group is
+ * a member.
+ */
+ values = _nss_ldap_get_values (e, ATM (LM_GROUP, memberOf));
+ if (values != NULL)
+ {
+ lia->depth++;
+ status = ng_chase_backlink ((const char **)values, lia);
+ lia->depth--;
+
+ ldap_value_free (values);
+
+ return status;
+ }
+ }
+ else
+ {
+ /*
+ * Now add the GIDs of any groups which refer to this group
+ */
+ groupdn = _nss_ldap_get_dn (e);
+ if (groupdn != NULL)
+ {
+ /* Note: there was a problem here with stat in the orriginal code */
+ lia->depth++;
+ status = ng_chase (groupdn, lia);
+ lia->depth--;
+#ifdef HAVE_LDAP_MEMFREE
+ ldap_memfree (groupdn);
+#else
+ free (groupdn);
+#endif
+ }
+ }
+
+ return status;
+}
+
+static enum nss_status
+ng_chase (const char *dn, ldap_initgroups_args_t * lia)
+{
+ struct ldap_args a;
+ enum nss_status stat;
+ struct ent_context *ctx = NULL;
+ const char *gidnumber_attrs[2];
+ int erange;
+
+ if (lia->depth > LDAP_NSS_MAXGR_DEPTH)
+ return NSS_STATUS_NOTFOUND;
+
+ if (_nss_ldap_namelist_find (lia->known_groups, dn))
+ return NSS_STATUS_NOTFOUND;
+
+ gidnumber_attrs[0] = ATM (LM_GROUP, gidNumber);
+ gidnumber_attrs[1] = NULL;
+
+ LA_INIT (a);
+ LA_STRING (a) = dn;
+ LA_TYPE (a) = LA_TYPE_STRING;
+
+ if (_nss_ldap_ent_context_init_locked (&ctx) == NULL)
+ {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ stat = _nss_ldap_getent_ex (&a, &ctx, lia, NULL, 0,
+ &erange, _nss_ldap_filt_getgroupsbydn,
+ LM_GROUP, gidnumber_attrs,
+ do_parse_initgroups_nested);
+
+ if (stat == NSS_STATUS_SUCCESS)
+ {
+ stat = _nss_ldap_namelist_push (&lia->known_groups, dn);
+ }
+
+ _nss_ldap_ent_context_release (ctx);
+ free (ctx);
+
+ return stat;
+}
+
+static enum nss_status
+ng_chase_backlink (const char ** membersOf, ldap_initgroups_args_t * lia)
+{
+ struct ldap_args a;
+ enum nss_status stat;
+ struct ent_context *ctx = NULL;
+ const char *gidnumber_attrs[3];
+ const char **memberP;
+ const char **filteredMembersOf; /* remove already traversed groups */
+ size_t memberCount, i;
+ int erange;
+
+ if (lia->depth > LDAP_NSS_MAXGR_DEPTH)
+ return NSS_STATUS_NOTFOUND;
+
+ for (memberCount = 0; membersOf[memberCount] != NULL; memberCount++)
+ ;
+
+ /* Build a list of membersOf values without any already traversed groups */
+ filteredMembersOf = (const char **) malloc(sizeof(char *) * (memberCount + 1));
+ if (filteredMembersOf == NULL)
+ {
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ memberP = filteredMembersOf;
+
+ for (i = 0; i < memberCount; i++)
+ {
+ if (_nss_ldap_namelist_find (lia->known_groups, membersOf[i]))
+ continue;
+
+ *memberP = membersOf[i];
+ memberP++;
+ }
+
+ *memberP = NULL;
+
+ if (filteredMembersOf[0] == NULL)
+ {
+ free (filteredMembersOf);
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ gidnumber_attrs[0] = ATM (LM_GROUP, gidNumber);
+ gidnumber_attrs[1] = ATM (LM_GROUP, memberOf);
+ gidnumber_attrs[2] = NULL;
+
+ LA_INIT (a);
+ LA_STRING_LIST (a) = filteredMembersOf;
+ LA_TYPE (a) = LA_TYPE_STRING_LIST_OR;
+
+ if (_nss_ldap_ent_context_init_locked (&ctx) == NULL)
+ {
+ free (filteredMembersOf);
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ stat = _nss_ldap_getent_ex (&a, &ctx, lia, NULL, 0,
+ &erange, "(distinguishedName=%s)",
+ LM_GROUP, gidnumber_attrs,
+ do_parse_initgroups_nested);
+
+ if (stat == NSS_STATUS_SUCCESS)
+ {
+ enum nss_status stat2;
+
+ for (memberP = filteredMembersOf; *memberP != NULL; memberP++)
+ {
+ stat2 = _nss_ldap_namelist_push (&lia->known_groups, *memberP);
+ if (stat2 != NSS_STATUS_SUCCESS)
+ {
+ stat = stat2;
+ break;
+ }
+ }
+ }
+
+ free (filteredMembersOf);
+
+ _nss_ldap_ent_context_release (ctx);
+ free (ctx);
+
+ return stat;
+}
+
+
+#define NSS_LDAP_INITGROUPS_FUNCTION "_nss_ldap_initgroups_dyn"
+
+enum nss_status _nss_ldap_initgroups_dyn (const char *user, gid_t group, long int *start,
+ long int *size, gid_t ** groupsp, long int limit,
+ int *errnop)
+{
+ ldap_initgroups_args_t lia;
+ int erange = 0;
+ char *userdn = NULL;
+ LDAPMessage *res, *e;
+ static const char *no_attrs[] = { NULL };
+ const char *filter;
+ struct ldap_args a;
+ enum nss_status stat;
+ struct ent_context *ctx = NULL;
+ const char *gidnumber_attrs[3];
+ enum ldap_map_selector map = LM_GROUP;
+
+ LA_INIT (a);
+ LA_STRING (a) = user;
+ LA_TYPE (a) = LA_TYPE_STRING;
+
+ debug ("==> " NSS_LDAP_INITGROUPS_FUNCTION " (user=%s)", LA_STRING (a) );
+
+#ifdef INITGROUPS_ROOT_ONLY
+ /* XXX performance hack for old versions of KDE only */
+ if ((getuid() != 0) && (geteuid() != 0))
+ return NSS_STATUS_NOTFOUND;
+#endif
+
+#ifdef HAVE_USERSEC_H
+ lia.grplist = NULL;
+ lia.listlen = 0;
+ lia.group = group;
+ lia.start = start;
+ lia.size = size;
+ lia.groups = groupsp;
+ lia.limit = limit;
+#endif /* HAVE_USERSEC_H */
+ lia.depth = 0;
+ lia.known_groups = NULL;
+
+ _nss_ldap_enter ();
+
+ /* initialize schema */
+ stat = _nss_ldap_init ();
+ if (stat != NSS_STATUS_SUCCESS)
+ {
+ debug ("<== " NSS_LDAP_INITGROUPS_FUNCTION " (init failed)");
+ _nss_ldap_leave ();
+#ifdef HAVE_USERSEC_H
+ return NULL;
+#else
+ return stat;
+#endif /* !HAVE_USERSEC_H */
+ }
+
+ if (_nss_ldap_test_initgroups_ignoreuser (LA_STRING (a)))
+ {
+ debug ("<== " NSS_LDAP_INITGROUPS_FUNCTION " (user ignored)");
+ _nss_ldap_leave ();
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ lia.backlink = _nss_ldap_test_config_flag (NSS_LDAP_FLAGS_INITGROUPS_BACKLINK);
+
+ if (lia.backlink != 0)
+ {
+ filter = _nss_ldap_filt_getpwnam_groupsbymember;
+ LA_STRING2 (a) = LA_STRING (a);
+ LA_TYPE (a) = LA_TYPE_STRING_AND_STRING;
+
+ gidnumber_attrs[0] = ATM (LM_GROUP, gidNumber);
+ gidnumber_attrs[1] = ATM (LM_GROUP, memberOf);
+ gidnumber_attrs[2] = NULL;
+
+ map = LM_PASSWD;
+ }
+ else
+ {
+ if (_nss_ldap_test_config_flag (NSS_LDAP_FLAGS_RFC2307BIS))
+ {
+ /* lookup the user's DN. */
+ stat = _nss_ldap_search_s (&a, _nss_ldap_filt_getpwnam, LM_PASSWD,
+ no_attrs, 1, &res);
+ if (stat == NSS_STATUS_SUCCESS)
+ {
+ e = _nss_ldap_first_entry (res);
+ if (e != NULL)
+ {
+ userdn = _nss_ldap_get_dn (e);
+ }
+ ldap_msgfree (res);
+ }
+ }
+ else
+ {
+ userdn = NULL;
+ }
+
+ if (userdn != NULL)
+ {
+ LA_STRING2 (a) = userdn;
+ LA_TYPE (a) = LA_TYPE_STRING_AND_STRING;
+ filter = _nss_ldap_filt_getgroupsbymemberanddn;
+ }
+ else
+ {
+ filter = _nss_ldap_filt_getgroupsbymember;
+ }
+
+ gidnumber_attrs[0] = ATM (LM_GROUP, gidNumber);
+ gidnumber_attrs[1] = NULL;
+ }
+
+ if (_nss_ldap_ent_context_init_locked (&ctx) == NULL)
+ {
+ debug ("<== " NSS_LDAP_INITGROUPS_FUNCTION " (ent_context_init failed)");
+ _nss_ldap_leave ();
+#ifdef HAVE_USERSEC_H
+ return NULL;
+#else
+ return NSS_STATUS_UNAVAIL;
+#endif /* HAVE_USERSEC_H */
+ }
+
+ stat = _nss_ldap_getent_ex (&a, &ctx, (void *) &lia, NULL, 0,
+ errnop,
+ filter,
+ map,
+ gidnumber_attrs,
+ do_parse_initgroups_nested);
+
+ if (userdn != NULL)
+ {
+#ifdef HAVE_LDAP_MEMFREE
+ ldap_memfree (userdn);
+#else
+ free (userdn);
+#endif /* HAVE_LDAP_MEMFREE */
+ }
+
+ _nss_ldap_namelist_destroy (&lia.known_groups);
+ _nss_ldap_ent_context_release (ctx);
+ free (ctx);
+ _nss_ldap_leave ();
+
+ /*
+ * We return NSS_STATUS_NOTFOUND to force the parser to be called
+ * for as many entries (i.e. groups) as exist, for all
+ * search descriptors. So confusingly this means "success".
+ */
+ if (stat != NSS_STATUS_SUCCESS && stat != NSS_STATUS_NOTFOUND)
+ {
+ debug ("<== " NSS_LDAP_INITGROUPS_FUNCTION " (not found)");
+ if (erange)
+ errno = ERANGE;
+#ifndef HAVE_USERSEC_H
+ return stat;
+#else
+ return NULL;
+#endif /* HAVE_USERSEC_H */
+ }
+
+ debug ("<== " NSS_LDAP_INITGROUPS_FUNCTION " (success)");
+
+ return NSS_STATUS_SUCCESS;
+}
+
+enum nss_status _nss_ldap_initgroups (const char *user, gid_t group, long int *start,
+ long int *size, gid_t * groups, long int limit,
+ int *errnop)
+{
+ return (_nss_ldap_initgroups_dyn (user, group, start, size, &groups, limit,
+ errnop));
+}
+
+enum nss_status _nss_ldap_getgrnam_r (const char *name,
+ struct group * result,
+ char *buffer, size_t buflen, int *errnop)
+{
+ LOOKUP_NAME (name, result, buffer, buflen, errnop, _nss_ldap_filt_getgrnam,
+ LM_GROUP, _nss_ldap_parse_gr, LDAP_NSS_BUFLEN_GROUP);
+}
+
+enum nss_status _nss_ldap_getgrgid_r (gid_t gid,
+ struct group *result,
+ char *buffer, size_t buflen, int *errnop)
+{
+ LOOKUP_NUMBER (gid, result, buffer, buflen, errnop, _nss_ldap_filt_getgrgid,
+ LM_GROUP, _nss_ldap_parse_gr, LDAP_NSS_BUFLEN_GROUP);
+}
+
+enum nss_status _nss_ldap_setgrent (void)
+{
+ LOOKUP_SETENT (gr_context);
+}
+
+enum nss_status _nss_ldap_getgrent_r (struct group *result,
+ char *buffer, size_t buflen, int *errnop)
+{
+ LOOKUP_GETENT (gr_context, result, buffer, buflen, errnop,
+ _nss_ldap_filt_getgrent, LM_GROUP, _nss_ldap_parse_gr,
+ LDAP_NSS_BUFLEN_GROUP);
+}
+
+enum nss_status _nss_ldap_endgrent (void)
+{
+ LOOKUP_ENDENT (gr_context);
+}