diff options
Diffstat (limited to 'grp/initgroups.c')
-rw-r--r-- | grp/initgroups.c | 184 |
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 } |