aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'grp/initgroups.c')
-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
}