about summary refs log tree commit diff
path: root/grp
diff options
context:
space:
mode:
Diffstat (limited to 'grp')
-rw-r--r--grp/initgroups.c184
1 files changed, 130 insertions, 54 deletions
diff --git a/grp/initgroups.c b/grp/initgroups.c
index 2ca90ab18d..2150fa968c 100644
--- a/grp/initgroups.c
+++ b/grp/initgroups.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1989, 1991, 1993, 1996, 1997 Free Software Foundation, Inc.
+/* Copyright (C) 1989, 91, 93, 96, 97, 98 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -23,8 +23,102 @@
 #include <string.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <nsswitch.h>
 
 
+/* Type of the lookup function.  */
+typedef enum nss_status (*initgroups_function) (const char *, gid_t,
+						long int *, long int *,
+						gid_t *, long int, int *);
+/* Prototype for the setgrent functions we use here.  */
+typedef enum nss_status (*set_function) (void);
+
+/* Prototype for the endgrent functions we use here.  */
+typedef enum nss_status (*end_function) (void);
+
+/* Prototype for the setgrent functions we use here.  */
+typedef enum nss_status (*get_function) (struct group *, char *,
+					 size_t, int *);
+
+/* The lookup function for the first entry of this service.  */
+extern int __nss_group_lookup (service_user **nip, const char *name,
+				   void **fctp);
+extern void *__nss_lookup_function (service_user *ni, const char *fct_name);
+
+extern service_user *__nss_group_database;
+
+static enum nss_status
+compat_call (service_user *nip, const char *user, gid_t group, long int *start,
+	     long int *size, gid_t *groups, long int limit, int *errnop)
+{
+  struct group grpbuf, *g;
+  size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
+  char *tmpbuf;
+  enum nss_status status;
+  set_function setgrent_fct;
+  get_function getgrent_fct;
+  end_function endgrent_fct;
+
+  setgrent_fct = __nss_lookup_function (nip, "setgrent");
+  status = (*setgrent_fct) ();
+  if (status != NSS_STATUS_SUCCESS)
+    return status;
+
+  getgrent_fct = __nss_lookup_function (nip, "getgrent_r");
+  endgrent_fct = __nss_lookup_function (nip, "endgrent");
+
+  tmpbuf = __alloca (buflen);
+
+  do
+    {
+      while ((status =
+	      (*getgrent_fct) (&grpbuf, tmpbuf, buflen, errnop)) ==
+	     NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
+        {
+          buflen *= 2;
+          tmpbuf = __alloca (buflen);
+        }
+
+      if (status != NSS_STATUS_SUCCESS)
+        goto done;
+
+      g = &grpbuf;
+      if (g->gr_gid != group)
+        {
+          char **m;
+
+          for (m = g->gr_mem; *m != NULL; ++m)
+            if (strcmp (*m, user) == 0)
+              {
+                /* Matches user.  Insert this group.  */
+                if (*start == *size && limit <= 0)
+                  {
+                    /* Need a bigger buffer.  */
+                    groups = realloc (groups, *size * sizeof (*groups));
+                    if (groups == NULL)
+                      goto done;
+                    *size *= 2;
+                  }
+
+                groups[*start] = g->gr_gid;
+                *start += 1;
+
+                if (*start == limit)
+                  /* Can't take any more groups; stop searching.  */
+                  goto done;
+
+                break;
+              }
+        }
+    }
+  while (status == NSS_STATUS_SUCCESS);
+
+ done:
+  (*endgrent_fct) ();
+
+  return NSS_STATUS_SUCCESS;
+}
+
 /* Initialize the group set for the current user
    by reading the group database and using all groups
    of which USER is a member.  Also include GROUP.  */
@@ -40,76 +134,58 @@ initgroups (user, group)
 
 #else
 
-  struct group grpbuf, *g;
-  size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
-  char *tmpbuf;
-  size_t n;
-  size_t ngroups;
+  service_user *nip = NULL;
+  initgroups_function fct;
+  enum nss_status status = NSS_STATUS_UNAVAIL;
+  int no_more;
+  /* Start is one, because we have the first group as parameter.  */
+  long int start = 1;
+  long int size;
   gid_t *groups;
-  int status;
 #ifdef NGROUPS_MAX
 # define limit NGROUPS_MAX
 
-  ngroups = limit;
+  size = limit;
 #else
   long int limit = sysconf (_SC_NGROUPS_MAX);
 
   if (limit > 0)
-    ngroups = limit;
+    size = limit;
   else
     /* No fixed limit on groups.  Pick a starting buffer size.  */
-    ngroups = 16;
+    size = 16;
 #endif
 
-  groups = __alloca (ngroups * sizeof *groups);
-  tmpbuf = __alloca (buflen);
-
-  setgrent ();
+  groups = malloc (size * sizeof (gid_t *));
 
-  n = 0;
-  groups[n++] = group;
+  groups[0] = group;
 
-  do
+  if (__nss_group_database != NULL)
     {
-      while ((status = __getgrent_r (&grpbuf, tmpbuf, buflen, &g)) != 0
-	     && errno == ERANGE)
-	{
-	  buflen *= 2;
-	  tmpbuf = __alloca (buflen);
-	}
-
-      if (status == 0 && g->gr_gid != group)
-	{
-	  char **m;
-
-	  for (m = g->gr_mem; *m != NULL; ++m)
-	    if (strcmp (*m, user) == 0)
-	      {
-		/* Matches user.  Insert this group.  */
-		if (n == ngroups && limit <= 0)
-		  {
-		    /* Need a bigger buffer.  */
-		    gid_t *newgrp;
-		    newgrp = __alloca (ngroups * 2 * sizeof *groups);
-		    groups = memcpy (newgrp, groups, ngroups * sizeof *groups);
-		    ngroups *= 2;
-		  }
-
-		groups[n++] = g->gr_gid;
-
-		if (n == limit)
-		  /* Can't take any more groups; stop searching.  */
-		  goto done;
-
-		break;
-	      }
-	}
+      no_more = 0;
+      nip = __nss_group_database;
     }
-  while (status == 0);
+  else
+    no_more = __nss_database_lookup ("group", NULL,
+				     "compat [NOTFOUND=return] files", &nip);
 
-done:
-  endgrent ();
+  while (! no_more)
+    {
+      fct = __nss_lookup_function (nip, "initgroups");
+
+      if (fct == NULL)
+	status = compat_call (nip, user, group, &start, &size, groups,
+			      limit, &errno);
+      else
+	status = (*fct) (user, group, &start, &size, groups, limit,
+			 &errno);
+
+      if (nip->next == NULL)
+	no_more = -1;
+      else
+	nip = nip->next;
+    }
 
-  return setgroups (n, groups);
+  return setgroups (start, groups);
 #endif
 }