Back to index

glibc  2.9
nis-initgroups.c
Go to the documentation of this file.
00001 /* Copyright (C) 1998-2000,2002,2003,2004,2006 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <alloca.h>
00021 #include <ctype.h>
00022 #include <errno.h>
00023 #include <grp.h>
00024 #include <nss.h>
00025 #include <pwd.h>
00026 #include <string.h>
00027 #include <unistd.h>
00028 #include <rpcsvc/yp.h>
00029 #include <rpcsvc/ypclnt.h>
00030 #include <sys/param.h>
00031 
00032 #include "nss-nis.h"
00033 #include <libnsl.h>
00034 
00035 /* Get the declaration of the parser function.  */
00036 #define ENTNAME grent
00037 #define STRUCTURE group
00038 #define EXTERN_PARSER
00039 #include <nss/nss_files/files-parse.c>
00040 
00041 
00042 static enum nss_status
00043 internal_setgrent (char *domainname, intern_t *intern)
00044 {
00045   struct ypall_callback ypcb;
00046   enum nss_status status;
00047 
00048   ypcb.foreach = _nis_saveit;
00049   ypcb.data = (char *) intern;
00050   status = yperr2nss (yp_all (domainname, "group.byname", &ypcb));
00051 
00052   /* Mark the last buffer as full.  */
00053   if (intern->next != NULL)
00054     intern->next->size = intern->offset;
00055 
00056   intern->next = intern->start;
00057   intern->offset = 0;
00058 
00059   return status;
00060 }
00061 
00062 
00063 static enum nss_status
00064 internal_getgrent_r (struct group *grp, char *buffer, size_t buflen,
00065                    int *errnop, intern_t *intern)
00066 {
00067   if (intern->start == NULL)
00068     return NSS_STATUS_NOTFOUND;
00069 
00070   /* Get the next entry until we found a correct one. */
00071   int parse_res;
00072   do
00073     {
00074       struct response_t *bucket = intern->next;
00075 
00076       if (__builtin_expect (intern->offset >= bucket->size, 0))
00077        {
00078          if (bucket->next == NULL)
00079            return NSS_STATUS_NOTFOUND;
00080 
00081          /* We look at all the content in the current bucket.  Go on
00082             to the next.  */
00083          bucket = intern->next = bucket->next;
00084          intern->offset = 0;
00085        }
00086 
00087       char *p;
00088       for (p = &bucket->mem[intern->offset]; isspace (*p); ++p)
00089         ++intern->offset;
00090 
00091       size_t len = strlen (p) + 1;
00092       if (__builtin_expect (len > buflen, 0))
00093        {
00094          *errnop = ERANGE;
00095          return NSS_STATUS_TRYAGAIN;
00096        }
00097 
00098       /* We unfortunately have to copy the data in the user-provided
00099         buffer because that buffer might be around for a very long
00100         time and the servent structure must remain valid.  If we would
00101         rely on the BUCKET memory the next 'setservent' or 'endservent'
00102         call would destroy it.
00103 
00104         The important thing is that it is a single NUL-terminated
00105         string.  This is what the parsing routine expects.  */
00106       p = memcpy (buffer, &bucket->mem[intern->offset], len);
00107 
00108       parse_res = _nss_files_parse_grent (p, grp, (void *) buffer, buflen,
00109                                      errnop);
00110       if (__builtin_expect (parse_res == -1, 0))
00111         return NSS_STATUS_TRYAGAIN;
00112 
00113       intern->offset += len;
00114     }
00115   while (!parse_res);
00116 
00117   return NSS_STATUS_SUCCESS;
00118 }
00119 
00120 
00121 static int
00122 get_uid (const char *user, uid_t *uidp)
00123 {
00124   size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
00125   char *buf = (char *) alloca (buflen);
00126 
00127   while (1)
00128     {
00129       struct passwd result;
00130       struct passwd *resp;
00131 
00132       int r = getpwnam_r (user, &result, buf, buflen, &resp);
00133       if (r == 0 && resp != NULL)
00134        {
00135          *uidp = resp->pw_uid;
00136          return 0;
00137        }
00138 
00139       if (r != ERANGE)
00140        break;
00141 
00142       extend_alloca (buf, buflen, 2 * buflen);
00143     }
00144 
00145   return 1;
00146 }
00147 
00148 
00149 static enum nss_status
00150 initgroups_netid (uid_t uid, gid_t group, long int *start, long int *size,
00151                 gid_t **groupsp, long int limit, int *errnop,
00152                 const char *domainname)
00153 {
00154   /* Prepare the key.  The form is "unix.UID@DOMAIN" with the UID and
00155      DOMAIN field filled in appropriately.  */
00156   char key[sizeof ("unix.@") + sizeof (uid_t) * 3 + strlen (domainname)];
00157   ssize_t keylen = snprintf (key, sizeof (key), "unix.%lu@%s",
00158                           (unsigned long int) uid, domainname);
00159 
00160   char *result;
00161   int reslen;
00162   int yperr = yp_match (domainname, "netid.byname", key, keylen, &result,
00163                      &reslen);
00164   if (__builtin_expect (yperr != YPERR_SUCCESS, 0))
00165     return yperr2nss (yperr);
00166 
00167   /* Parse the result: following the colon is a comma separated list of
00168      group IDs.  */
00169   char *cp = strchr (result, ':');
00170   if (cp == NULL)
00171     {
00172     errout:
00173       free (result);
00174       return NSS_STATUS_NOTFOUND;
00175     }
00176   /* Skip the colon.  */
00177   ++cp;
00178 
00179   gid_t *groups = *groupsp;
00180   while (*cp != '\0')
00181     {
00182       char *endp;
00183       unsigned long int gid = strtoul (cp, &endp, 0);
00184       if (cp == endp)
00185        goto errout;
00186       if (*endp == ',')
00187        ++endp;
00188       else if (*endp != '\0')
00189        goto errout;
00190       cp = endp;
00191 
00192       if (gid == group)
00193        /* We do not need this group again.  */
00194        continue;
00195 
00196       /* Insert this group.  */
00197       if (*start == *size)
00198        {
00199          /* Need a bigger buffer.  */
00200          long int newsize;
00201 
00202          if (limit > 0 && *size == limit)
00203            /* We reached the maximum.  */
00204            break;
00205 
00206          if (limit <= 0)
00207            newsize = 2 * *size;
00208          else
00209            newsize = MIN (limit, 2 * *size);
00210 
00211          gid_t *newgroups = realloc (groups, newsize * sizeof (*groups));
00212          if (newgroups == NULL)
00213            goto errout;
00214          *groupsp = groups = newgroups;
00215          *size = newsize;
00216        }
00217 
00218       groups[*start] = gid;
00219       *start += 1;
00220     }
00221 
00222   free (result);
00223 
00224   return NSS_STATUS_SUCCESS;
00225 }
00226 
00227 
00228 enum nss_status
00229 _nss_nis_initgroups_dyn (const char *user, gid_t group, long int *start,
00230                       long int *size, gid_t **groupsp, long int limit,
00231                       int *errnop)
00232 {
00233   /* We always need the domain name.  */
00234   char *domainname;
00235   if (yp_get_default_domain (&domainname))
00236     return NSS_STATUS_UNAVAIL;
00237 
00238   /* Check whether we are supposed to use the netid.byname map.  */
00239   if (_nsl_default_nss () & NSS_FLAG_NETID_AUTHORITATIVE)
00240     {
00241       /* We need the user ID.  */
00242       uid_t uid;
00243 
00244       if (get_uid (user, &uid) == 0
00245          && initgroups_netid (uid, group, start, size, groupsp, limit,
00246                             errnop, domainname) == NSS_STATUS_SUCCESS)
00247        return NSS_STATUS_SUCCESS;
00248     }
00249 
00250   struct group grpbuf, *g;
00251   size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
00252   char *tmpbuf;
00253   enum nss_status status;
00254   intern_t intern = { NULL, NULL, 0 };
00255   gid_t *groups = *groupsp;
00256 
00257   status = internal_setgrent (domainname, &intern);
00258   if (status != NSS_STATUS_SUCCESS)
00259     return status;
00260 
00261   tmpbuf = __alloca (buflen);
00262 
00263   do
00264     {
00265       while ((status =
00266              internal_getgrent_r (&grpbuf, tmpbuf, buflen, errnop,
00267                                &intern)) == NSS_STATUS_TRYAGAIN
00268              && *errnop == ERANGE)
00269        tmpbuf = extend_alloca (tmpbuf, buflen, 2 * buflen);
00270 
00271       if (status != NSS_STATUS_SUCCESS)
00272        goto done;
00273 
00274 
00275       g = &grpbuf;
00276       if (g->gr_gid != group)
00277         {
00278           char **m;
00279 
00280           for (m = g->gr_mem; *m != NULL; ++m)
00281             if (strcmp (*m, user) == 0)
00282               {
00283                 /* Matches user.  Insert this group.  */
00284                 if (*start == *size)
00285                   {
00286                     /* Need a bigger buffer.  */
00287                   gid_t *newgroups;
00288                   long int newsize;
00289 
00290                   if (limit > 0 && *size == limit)
00291                     /* We reached the maximum.  */
00292                     goto done;
00293 
00294                   if (limit <= 0)
00295                     newsize = 2 * *size;
00296                   else
00297                     newsize = MIN (limit, 2 * *size);
00298 
00299                   newgroups = realloc (groups, newsize * sizeof (*groups));
00300                   if (newgroups == NULL)
00301                     goto done;
00302                   *groupsp = groups = newgroups;
00303                     *size = newsize;
00304                   }
00305 
00306                 groups[*start] = g->gr_gid;
00307               *start += 1;
00308 
00309                 break;
00310               }
00311         }
00312     }
00313   while (status == NSS_STATUS_SUCCESS);
00314 
00315 done:
00316   while (intern.start != NULL)
00317     {
00318       intern.next = intern.start;
00319       intern.start = intern.start->next;
00320       free (intern.next);
00321     }
00322 
00323   return NSS_STATUS_SUCCESS;
00324 }