(linenum→info "unix/slp.c:2238")

glibc/2.7/grp/initgroups.c

    1: /* Copyright (C) 1989,91,93,1996-2005,2006 Free Software Foundation, Inc.
    2:    This file is part of the GNU C Library.
    3: 
    4:    The GNU C Library is free software; you can redistribute it and/or
    5:    modify it under the terms of the GNU Lesser General Public
    6:    License as published by the Free Software Foundation; either
    7:    version 2.1 of the License, or (at your option) any later version.
    8: 
    9:    The GNU C Library is distributed in the hope that it will be useful,
   10:    but WITHOUT ANY WARRANTY; without even the implied warranty of
   11:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   12:    Lesser General Public License for more details.
   13: 
   14:    You should have received a copy of the GNU Lesser General Public
   15:    License along with the GNU C Library; if not, write to the Free
   16:    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   17:    02111-1307 USA.  */
   18: 
   19: #include <alloca.h>
   20: #include <assert.h>
   21: #include <errno.h>
   22: #include <grp.h>
   23: #include <limits.h>
   24: #include <stdlib.h>
   25: #include <string.h>
   26: #include <unistd.h>
   27: #include <sys/param.h>
   28: #include <sys/types.h>
   29: #include <nsswitch.h>
   30: 
   31: #include "../nscd/nscd-client.h"
   32: #include "../nscd/nscd_proto.h"
   33: 
   34: 
   35: /* Type of the lookup function.  */
   36: typedef enum nss_status (*initgroups_dyn_function) (const char *, gid_t,
   37:                                                     long int *, long int *,
   38:                                                     gid_t **, long int, int *);
   39: 
   40: /* The lookup function for the first entry of this service.  */
   41: extern int __nss_group_lookup (service_user **nip, const char *name,
   42:                                    void **fctp);
   43: extern void *__nss_lookup_function (service_user *ni, const char *fct_name);
   44: 
   45: extern service_user *__nss_group_database attribute_hidden;
   46: 
   47: 
   48: #include "compat-initgroups.c"
   49: 
   50: 
   51: static int
   52: internal_getgrouplist (const char *user, gid_t group, long int *size,
   53:                        gid_t **groupsp, long int limit)
   54: {
   55: #ifdef USE_NSCD
   56:   if (__nss_not_use_nscd_group > 0
   57:       && ++__nss_not_use_nscd_group > NSS_NSCD_RETRY)
   58:     __nss_not_use_nscd_group = 0;
   59:   if (!__nss_not_use_nscd_group)
   60:     {
   61:       int n = __nscd_getgrouplist (user, group, size, groupsp, limit);
   62:       if (n >= 0)
   63:         return n;
   64: 
   65:       /* nscd is not usable.  */
   66:       __nss_not_use_nscd_group = 1;
   67:     }
   68: #endif
   69: 
   70:   service_user *nip = NULL;
   71:   initgroups_dyn_function fct;
   72:   enum nss_status status = NSS_STATUS_UNAVAIL;
   73:   int no_more;
   74:   /* Start is one, because we have the first group as parameter.  */
   75:   long int start = 1;
   76: 
   77:   /* Never store more than the starting *SIZE number of elements.  */
   78:   assert (*size > 0);
   79:   (*groupsp)[0] = group;
   80: 
   81:   if (__nss_group_database != NULL)
   82:     {
   83:       no_more = 0;
   84:       nip = __nss_group_database;
   85:     }
   86:   else
   87:     no_more = __nss_database_lookup ("group", NULL,
   88:                                      "compat [NOTFOUND=return] files", &nip);
   89: 
   90:   while (! no_more)
   91:     {
   92:       long int prev_start = start;
   93: 
   94:       fct = __nss_lookup_function (nip, "initgroups_dyn");
   95: 
   96:       if (fct == NULL)
   97:         {
   98:           status = compat_call (nip, user, group, &start, size, groupsp,
   99:                                 limit, &errno);
  100: 
  101:           if (nss_next_action (nip, NSS_STATUS_UNAVAIL) != NSS_ACTION_CONTINUE)
  102:             break;
  103:         }
  104:       else
  105:         status = DL_CALL_FCT (fct, (user, group, &start, size, groupsp,
  106:                                     limit, &errno));
  107: 
  108:       /* Remove duplicates.  */
  109:       long int cnt = prev_start;
  110:       while (cnt < start)
  111:         {
  112:           long int inner;
  113:           for (inner = 0; inner < prev_start; ++inner)
  114:             if ((*groupsp)[inner] == (*groupsp)[cnt])
  115:               break;
  116: 
  117:           if (inner < prev_start)
  118:             (*groupsp)[cnt] = (*groupsp)[--start];
  119:           else
  120:             ++cnt;
  121:         }
  122: 
  123:       /* This is really only for debugging.  */
  124:       if (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_RETURN)
  125:         __libc_fatal ("illegal status in internal_getgrouplist");
  126: 
  127:       if (status != NSS_STATUS_SUCCESS
  128:           && nss_next_action (nip, status) == NSS_ACTION_RETURN)
  129:          break;
  130: 
  131:       if (nip->next == NULL)
  132:         no_more = -1;
  133:       else
  134:         nip = nip->next;
  135:     }
  136: 
  137:   return start;
  138: }
  139: 
  140: /* Store at most *NGROUPS members of the group set for USER into
  141:    *GROUPS.  Also include GROUP.  The actual number of groups found is
  142:    returned in *NGROUPS.  Return -1 if the if *NGROUPS is too small.  */
  143: int
  144: getgrouplist (const char *user, gid_t group, gid_t *groups, int *ngroups)
  145: {
  146:   long int size = MAX (1, *ngroups);
  147: 
  148:   gid_t *newgroups = (gid_t *) malloc (size * sizeof (gid_t));
  149:   if (__builtin_expect (newgroups == NULL, 0))
  150:     /* No more memory.  */
  151:     // XXX This is wrong.  The user provided memory, we have to use
  152:     // XXX it.  The internal functions must be called with the user
  153:     // XXX provided buffer and not try to increase the size if it is
  154:     // XXX too small.  For initgroups a flag could say: increase size.
  155:     return -1;
  156: 
  157:   int total = internal_getgrouplist (user, group, &size, &newgroups, -1);
  158: 
  159:   memcpy (groups, newgroups, MIN (*ngroups, total) * sizeof (gid_t));
  160: 
  161:   free (newgroups);
  162: 
  163:   int retval = total > *ngroups ? -1 : total;
  164:   *ngroups = total;
  165: 
  166:   return retval;
  167: }
  168: 
  169: static_link_warning (getgrouplist)
  170: 
  171: /* Initialize the group set for the current user
  172:    by reading the group database and using all groups
  173:    of which USER is a member.  Also include GROUP.  */
  174: int
  175: initgroups (const char *user, gid_t group)
  176: {
  177: #if defined NGROUPS_MAX && NGROUPS_MAX == 0
  178: 
  179:   /* No extra groups allowed.  */
  180:   return 0;
  181: 
  182: #else
  183: 
  184:   long int size;
  185:   gid_t *groups;
  186:   int ngroups;
  187:   int result;
  188: 
  189:  /* We always use sysconf even if NGROUPS_MAX is defined.  That way, the
  190:      limit can be raised in the kernel configuration without having to
  191:      recompile libc.  */
  192:   long int limit = __sysconf (_SC_NGROUPS_MAX);
  193: 
  194:   if (limit > 0)
  195:     /* We limit the size of the intially allocated array.  */
  196:     size = MIN (limit, 64);
  197:   else
  198:     /* No fixed limit on groups.  Pick a starting buffer size.  */
  199:     size = 16;
  200: 
  201:   groups = (gid_t *) malloc (size * sizeof (gid_t));
  202:   if (__builtin_expect (groups == NULL, 0))
  203:     /* No more memory.  */
  204:     return -1;
  205: 
  206:   ngroups = internal_getgrouplist (user, group, &size, &groups, limit);
  207: 
  208:   /* Try to set the maximum number of groups the kernel can handle.  */
  209:   do
  210:     result = setgroups (ngroups, groups);
  211:   while (result == -1 && errno == EINVAL && --ngroups > 0);
  212: 
  213:   free (groups);
  214: 
  215:   return result;
  216: #endif
  217: }
  218: 
  219: static_link_warning (initgroups)
Syntax (Markdown)