Back to index

glibc  2.9
compat-pwd.c
Go to the documentation of this file.
00001 /* Copyright (C) 1996-1999, 2001-2006, 2007 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
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 <ctype.h>
00021 #include <errno.h>
00022 #include <fcntl.h>
00023 #include <netdb.h>
00024 #include <nss.h>
00025 #include <nsswitch.h>
00026 #include <pwd.h>
00027 #include <stdio_ext.h>
00028 #include <string.h>
00029 #include <rpc/types.h>
00030 #include <rpcsvc/ypclnt.h>
00031 #include <bits/libc-lock.h>
00032 #include <kernel-features.h>
00033 
00034 #include "netgroup.h"
00035 
00036 static service_user *ni;
00037 static enum nss_status (*nss_setpwent) (int stayopen);
00038 static enum nss_status (*nss_getpwnam_r) (const char *name,
00039                                      struct passwd * pwd, char *buffer,
00040                                      size_t buflen, int *errnop);
00041 static enum nss_status (*nss_getpwuid_r) (uid_t uid, struct passwd * pwd,
00042                                      char *buffer, size_t buflen,
00043                                      int *errnop);
00044 static enum nss_status (*nss_getpwent_r) (struct passwd * pwd, char *buffer,
00045                                      size_t buflen, int *errnop);
00046 static enum nss_status (*nss_endpwent) (void);
00047 
00048 /* Get the declaration of the parser function.  */
00049 #define ENTNAME pwent
00050 #define STRUCTURE passwd
00051 #define EXTERN_PARSER
00052 #include <nss/nss_files/files-parse.c>
00053 
00054 /* Structure for remembering -@netgroup and -user members ... */
00055 #define BLACKLIST_INITIAL_SIZE 512
00056 #define BLACKLIST_INCREMENT 256
00057 struct blacklist_t
00058 {
00059   char *data;
00060   int current;
00061   int size;
00062 };
00063 
00064 struct ent_t
00065 {
00066   bool netgroup;
00067   bool first;
00068   bool files;
00069   enum nss_status setent_status;
00070   FILE *stream;
00071   struct blacklist_t blacklist;
00072   struct passwd pwd;
00073   struct __netgrent netgrdata;
00074 };
00075 typedef struct ent_t ent_t;
00076 
00077 static ent_t ext_ent = { false, false, true, NSS_STATUS_SUCCESS, NULL,
00078                       { NULL, 0, 0 },
00079                       { NULL, NULL, 0, 0, NULL, NULL, NULL }};
00080 
00081 /* Protect global state against multiple changers.  */
00082 __libc_lock_define_initialized (static, lock)
00083 
00084 /* Positive if O_CLOEXEC is supported, negative if it is not supported,
00085    zero if it is still undecided.  This variable is shared with the
00086    other compat functions.  */
00087 #ifdef __ASSUME_O_CLOEXEC
00088 # define __compat_have_cloexec 1
00089 #else
00090 # ifdef O_CLOEXEC
00091 extern int __compat_have_cloexec;
00092 # else
00093 #  define __compat_have_cloexec -1
00094 # endif
00095 #endif
00096 
00097 /* Prototypes for local functions.  */
00098 static void blacklist_store_name (const char *, ent_t *);
00099 static int in_blacklist (const char *, int, ent_t *);
00100 
00101 /* Initialize the NSS interface/functions. The calling function must
00102    hold the lock.  */
00103 static void
00104 init_nss_interface (void)
00105 {
00106   if (__nss_database_lookup ("passwd_compat", NULL, "nis", &ni) >= 0)
00107     {
00108       nss_setpwent = __nss_lookup_function (ni, "setpwent");
00109       nss_getpwnam_r = __nss_lookup_function (ni, "getpwnam_r");
00110       nss_getpwuid_r = __nss_lookup_function (ni, "getpwuid_r");
00111       nss_getpwent_r = __nss_lookup_function (ni, "getpwent_r");
00112       nss_endpwent = __nss_lookup_function (ni, "endpwent");
00113     }
00114 }
00115 
00116 static void
00117 give_pwd_free (struct passwd *pwd)
00118 {
00119   free (pwd->pw_name);
00120   free (pwd->pw_passwd);
00121   free (pwd->pw_gecos);
00122   free (pwd->pw_dir);
00123   free (pwd->pw_shell);
00124 
00125   memset (pwd, '\0', sizeof (struct passwd));
00126 }
00127 
00128 static size_t
00129 pwd_need_buflen (struct passwd *pwd)
00130 {
00131   size_t len = 0;
00132 
00133   if (pwd->pw_passwd != NULL)
00134     len += strlen (pwd->pw_passwd) + 1;
00135 
00136   if (pwd->pw_gecos != NULL)
00137     len += strlen (pwd->pw_gecos) + 1;
00138 
00139   if (pwd->pw_dir != NULL)
00140     len += strlen (pwd->pw_dir) + 1;
00141 
00142   if (pwd->pw_shell != NULL)
00143     len += strlen (pwd->pw_shell) + 1;
00144 
00145   return len;
00146 }
00147 
00148 static void
00149 copy_pwd_changes (struct passwd *dest, struct passwd *src,
00150                 char *buffer, size_t buflen)
00151 {
00152   if (src->pw_passwd != NULL && strlen (src->pw_passwd))
00153     {
00154       if (buffer == NULL)
00155        dest->pw_passwd = strdup (src->pw_passwd);
00156       else if (dest->pw_passwd &&
00157               strlen (dest->pw_passwd) >= strlen (src->pw_passwd))
00158        strcpy (dest->pw_passwd, src->pw_passwd);
00159       else
00160        {
00161          dest->pw_passwd = buffer;
00162          strcpy (dest->pw_passwd, src->pw_passwd);
00163          buffer += strlen (dest->pw_passwd) + 1;
00164          buflen = buflen - (strlen (dest->pw_passwd) + 1);
00165        }
00166     }
00167 
00168   if (src->pw_gecos != NULL && strlen (src->pw_gecos))
00169     {
00170       if (buffer == NULL)
00171        dest->pw_gecos = strdup (src->pw_gecos);
00172       else if (dest->pw_gecos &&
00173               strlen (dest->pw_gecos) >= strlen (src->pw_gecos))
00174        strcpy (dest->pw_gecos, src->pw_gecos);
00175       else
00176        {
00177          dest->pw_gecos = buffer;
00178          strcpy (dest->pw_gecos, src->pw_gecos);
00179          buffer += strlen (dest->pw_gecos) + 1;
00180          buflen = buflen - (strlen (dest->pw_gecos) + 1);
00181        }
00182     }
00183   if (src->pw_dir != NULL && strlen (src->pw_dir))
00184     {
00185       if (buffer == NULL)
00186        dest->pw_dir = strdup (src->pw_dir);
00187       else if (dest->pw_dir && strlen (dest->pw_dir) >= strlen (src->pw_dir))
00188        strcpy (dest->pw_dir, src->pw_dir);
00189       else
00190        {
00191          dest->pw_dir = buffer;
00192          strcpy (dest->pw_dir, src->pw_dir);
00193          buffer += strlen (dest->pw_dir) + 1;
00194          buflen = buflen - (strlen (dest->pw_dir) + 1);
00195        }
00196     }
00197 
00198   if (src->pw_shell != NULL && strlen (src->pw_shell))
00199     {
00200       if (buffer == NULL)
00201        dest->pw_shell = strdup (src->pw_shell);
00202       else if (dest->pw_shell &&
00203               strlen (dest->pw_shell) >= strlen (src->pw_shell))
00204        strcpy (dest->pw_shell, src->pw_shell);
00205       else
00206        {
00207          dest->pw_shell = buffer;
00208          strcpy (dest->pw_shell, src->pw_shell);
00209          buffer += strlen (dest->pw_shell) + 1;
00210          buflen = buflen - (strlen (dest->pw_shell) + 1);
00211        }
00212     }
00213 }
00214 
00215 static enum nss_status
00216 internal_setpwent (ent_t *ent, int stayopen, int needent)
00217 {
00218   enum nss_status status = NSS_STATUS_SUCCESS;
00219 
00220   ent->first = ent->netgroup = false;
00221   ent->files = true;
00222   ent->setent_status = NSS_STATUS_SUCCESS;
00223 
00224   /* If something was left over free it.  */
00225   if (ent->netgroup)
00226     __internal_endnetgrent (&ent->netgrdata);
00227 
00228   if (ent->blacklist.data != NULL)
00229     {
00230       ent->blacklist.current = 1;
00231       ent->blacklist.data[0] = '|';
00232       ent->blacklist.data[1] = '\0';
00233     }
00234   else
00235     ent->blacklist.current = 0;
00236 
00237   if (ent->stream == NULL)
00238     {
00239       ent->stream = fopen ("/etc/passwd", "rme");
00240 
00241       if (ent->stream == NULL)
00242        status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
00243       else
00244        {
00245          /* We have to make sure the file is  `closed on exec'.  */
00246          int result = 0;
00247 
00248          if (__compat_have_cloexec <= 0)
00249            {
00250              int flags;
00251              result = flags = fcntl (fileno_unlocked (ent->stream), F_GETFD,
00252                                   0);
00253              if (result >= 0)
00254               {
00255 #if defined O_CLOEXEC && !defined __ASSUME_O_CLOEXEC
00256                 if (__compat_have_cloexec == 0)
00257                   __compat_have_cloexec = (flags & FD_CLOEXEC) ? 1 : -1;
00258 
00259                 if (__compat_have_cloexec < 0)
00260 #endif
00261                   {
00262                     flags |= FD_CLOEXEC;
00263                     result = fcntl (fileno_unlocked (ent->stream), F_SETFD,
00264                                   flags);
00265                   }
00266               }
00267            }
00268 
00269          if (result < 0)
00270            {
00271              /* Something went wrong.  Close the stream and return a
00272                 failure.  */
00273              fclose (ent->stream);
00274              ent->stream = NULL;
00275              status = NSS_STATUS_UNAVAIL;
00276            }
00277          else
00278            /* We take care of locking ourself.  */
00279            __fsetlocking (ent->stream, FSETLOCKING_BYCALLER);
00280        }
00281     }
00282   else
00283     rewind (ent->stream);
00284 
00285   give_pwd_free (&ent->pwd);
00286 
00287   if (needent && status == NSS_STATUS_SUCCESS && nss_setpwent)
00288     ent->setent_status = nss_setpwent (stayopen);
00289 
00290   return status;
00291 }
00292 
00293 
00294 enum nss_status
00295 _nss_compat_setpwent (int stayopen)
00296 {
00297   enum nss_status result;
00298 
00299   __libc_lock_lock (lock);
00300 
00301   if (ni == NULL)
00302     init_nss_interface ();
00303 
00304   result = internal_setpwent (&ext_ent, stayopen, 1);
00305 
00306   __libc_lock_unlock (lock);
00307 
00308   return result;
00309 }
00310 
00311 
00312 static enum nss_status
00313 internal_endpwent (ent_t *ent)
00314 {
00315   if (nss_endpwent)
00316     nss_endpwent ();
00317 
00318   if (ent->stream != NULL)
00319     {
00320       fclose (ent->stream);
00321       ent->stream = NULL;
00322     }
00323 
00324   if (ent->netgroup)
00325     __internal_endnetgrent (&ent->netgrdata);
00326 
00327   ent->first = ent->netgroup = false;
00328 
00329   if (ent->blacklist.data != NULL)
00330     {
00331       ent->blacklist.current = 1;
00332       ent->blacklist.data[0] = '|';
00333       ent->blacklist.data[1] = '\0';
00334     }
00335   else
00336     ent->blacklist.current = 0;
00337 
00338   give_pwd_free (&ent->pwd);
00339 
00340   return NSS_STATUS_SUCCESS;
00341 }
00342 
00343 enum nss_status
00344 _nss_compat_endpwent (void)
00345 {
00346   enum nss_status result;
00347 
00348   __libc_lock_lock (lock);
00349 
00350   result = internal_endpwent (&ext_ent);
00351 
00352   __libc_lock_unlock (lock);
00353 
00354   return result;
00355 }
00356 
00357 
00358 static enum nss_status
00359 getpwent_next_nss_netgr (const char *name, struct passwd *result, ent_t *ent,
00360                       char *group, char *buffer, size_t buflen,
00361                       int *errnop)
00362 {
00363   char *curdomain, *host, *user, *domain, *p2;
00364   int status;
00365   size_t p2len;
00366 
00367   /* Leave function if NSS module does not support getpwnam_r,
00368      we need this function here.  */
00369   if (!nss_getpwnam_r)
00370     return NSS_STATUS_UNAVAIL;
00371 
00372   if (yp_get_default_domain (&curdomain) != YPERR_SUCCESS)
00373     {
00374       ent->netgroup = false;
00375       ent->first = false;
00376       give_pwd_free (&ent->pwd);
00377       return NSS_STATUS_UNAVAIL;
00378     }
00379 
00380   if (ent->first == true)
00381     {
00382       memset (&ent->netgrdata, 0, sizeof (struct __netgrent));
00383       __internal_setnetgrent (group, &ent->netgrdata);
00384       ent->first = false;
00385     }
00386 
00387   while (1)
00388     {
00389       char *saved_cursor;
00390 
00391       saved_cursor = ent->netgrdata.cursor;
00392       status = __internal_getnetgrent_r (&host, &user, &domain,
00393                                     &ent->netgrdata, buffer, buflen,
00394                                     errnop);
00395       if (status != 1)
00396        {
00397          __internal_endnetgrent (&ent->netgrdata);
00398          ent->netgroup = 0;
00399          give_pwd_free (&ent->pwd);
00400          return NSS_STATUS_RETURN;
00401        }
00402 
00403       if (user == NULL || user[0] == '-')
00404        continue;
00405 
00406       if (domain != NULL && strcmp (curdomain, domain) != 0)
00407        continue;
00408 
00409       /* If name != NULL, we are called from getpwnam.  */
00410       if (name != NULL)
00411        if (strcmp (user, name) != 0)
00412          continue;
00413 
00414       p2len = pwd_need_buflen (&ent->pwd);
00415       if (p2len > buflen)
00416        {
00417          *errnop = ERANGE;
00418          return NSS_STATUS_TRYAGAIN;
00419        }
00420       p2 = buffer + (buflen - p2len);
00421       buflen -= p2len;
00422 
00423       if (nss_getpwnam_r (user, result, buffer, buflen, errnop) !=
00424          NSS_STATUS_SUCCESS)
00425        continue;
00426 
00427       if (!in_blacklist (result->pw_name, strlen (result->pw_name), ent))
00428        {
00429          /* Store the User in the blacklist for possible the "+" at the
00430             end of /etc/passwd */
00431          blacklist_store_name (result->pw_name, ent);
00432          copy_pwd_changes (result, &ent->pwd, p2, p2len);
00433          break;
00434        }
00435     }
00436 
00437   return NSS_STATUS_SUCCESS;
00438 }
00439 
00440 /* get the next user from NSS  (+ entry) */
00441 static enum nss_status
00442 getpwent_next_nss (struct passwd *result, ent_t *ent, char *buffer,
00443                  size_t buflen, int *errnop)
00444 {
00445   enum nss_status status;
00446   char *p2;
00447   size_t p2len;
00448 
00449   /* Return if NSS module does not support getpwent_r.  */
00450   if (!nss_getpwent_r)
00451     return NSS_STATUS_UNAVAIL;
00452 
00453   /* If the setpwent call failed, say so.  */
00454   if (ent->setent_status != NSS_STATUS_SUCCESS)
00455     return ent->setent_status;
00456 
00457   p2len = pwd_need_buflen (&ent->pwd);
00458   if (p2len > buflen)
00459     {
00460       *errnop = ERANGE;
00461       return NSS_STATUS_TRYAGAIN;
00462     }
00463   p2 = buffer + (buflen - p2len);
00464   buflen -= p2len;
00465 
00466   if (ent->first)
00467     ent->first = false;
00468 
00469   do
00470     {
00471       if ((status = nss_getpwent_r (result, buffer, buflen, errnop)) !=
00472          NSS_STATUS_SUCCESS)
00473        return status;
00474     }
00475   while (in_blacklist (result->pw_name, strlen (result->pw_name), ent));
00476 
00477   copy_pwd_changes (result, &ent->pwd, p2, p2len);
00478 
00479   return NSS_STATUS_SUCCESS;
00480 }
00481 
00482 /* This function handle the +user entrys in /etc/passwd */
00483 static enum nss_status
00484 getpwnam_plususer (const char *name, struct passwd *result, ent_t *ent,
00485                  char *buffer, size_t buflen, int *errnop)
00486 {
00487   if (!nss_getpwnam_r)
00488     return NSS_STATUS_UNAVAIL;
00489 
00490   struct passwd pwd;
00491   memset (&pwd, '\0', sizeof (struct passwd));
00492 
00493   copy_pwd_changes (&pwd, result, NULL, 0);
00494 
00495   size_t plen = pwd_need_buflen (&pwd);
00496   if (plen > buflen)
00497     {
00498       *errnop = ERANGE;
00499       return NSS_STATUS_TRYAGAIN;
00500     }
00501   char *p = buffer + (buflen - plen);
00502   buflen -= plen;
00503 
00504   enum nss_status status = nss_getpwnam_r (name, result, buffer, buflen,
00505                                       errnop);
00506   if (status != NSS_STATUS_SUCCESS)
00507     return status;
00508 
00509   if (in_blacklist (result->pw_name, strlen (result->pw_name), ent))
00510     return NSS_STATUS_NOTFOUND;
00511 
00512   copy_pwd_changes (result, &pwd, p, plen);
00513   give_pwd_free (&pwd);
00514   /* We found the entry.  */
00515   return NSS_STATUS_SUCCESS;
00516 }
00517 
00518 static enum nss_status
00519 getpwent_next_file (struct passwd *result, ent_t *ent,
00520                   char *buffer, size_t buflen, int *errnop)
00521 {
00522   struct parser_data *data = (void *) buffer;
00523   while (1)
00524     {
00525       fpos_t pos;
00526       char *p;
00527       int parse_res;
00528 
00529       do
00530        {
00531          /* We need at least 3 characters for one line.  */
00532          if (__builtin_expect (buflen < 3, 0))
00533            {
00534            erange:
00535              *errnop = ERANGE;
00536              return NSS_STATUS_TRYAGAIN;
00537            }
00538 
00539          fgetpos (ent->stream, &pos);
00540          buffer[buflen - 1] = '\xff';
00541          p = fgets_unlocked (buffer, buflen, ent->stream);
00542          if (p == NULL && feof_unlocked (ent->stream))
00543            return NSS_STATUS_NOTFOUND;
00544 
00545          if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
00546            {
00547            erange_reset:
00548              fsetpos (ent->stream, &pos);
00549              goto erange;
00550            }
00551 
00552          /* Terminate the line for any case.  */
00553          buffer[buflen - 1] = '\0';
00554 
00555          /* Skip leading blanks.  */
00556          while (isspace (*p))
00557            ++p;
00558        }
00559       while (*p == '\0' || *p == '#' ||   /* Ignore empty and comment lines.  */
00560             /* Parse the line.  If it is invalid, loop to
00561                get the next line of the file to parse.  */
00562             !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
00563                                              errnop)));
00564 
00565       if (__builtin_expect (parse_res == -1, 0))
00566        /* The parser ran out of space.  */
00567        goto erange_reset;
00568 
00569       if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
00570        /* This is a real entry.  */
00571        break;
00572 
00573       /* -@netgroup */
00574       if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
00575          && result->pw_name[2] != '\0')
00576        {
00577          /* XXX Do not use fixed length buffer.  */
00578          char buf2[1024];
00579          char *user, *host, *domain;
00580          struct __netgrent netgrdata;
00581 
00582          bzero (&netgrdata, sizeof (struct __netgrent));
00583          __internal_setnetgrent (&result->pw_name[2], &netgrdata);
00584          while (__internal_getnetgrent_r (&host, &user, &domain, &netgrdata,
00585                                       buf2, sizeof (buf2), errnop))
00586            {
00587              if (user != NULL && user[0] != '-')
00588               blacklist_store_name (user, ent);
00589            }
00590          __internal_endnetgrent (&netgrdata);
00591          continue;
00592        }
00593 
00594       /* +@netgroup */
00595       if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
00596          && result->pw_name[2] != '\0')
00597        {
00598          enum nss_status status;
00599 
00600          ent->netgroup = true;
00601          ent->first = true;
00602          copy_pwd_changes (&ent->pwd, result, NULL, 0);
00603 
00604          status = getpwent_next_nss_netgr (NULL, result, ent,
00605                                        &result->pw_name[2],
00606                                        buffer, buflen, errnop);
00607          if (status == NSS_STATUS_RETURN)
00608            continue;
00609          else
00610            return status;
00611        }
00612 
00613       /* -user */
00614       if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
00615          && result->pw_name[1] != '@')
00616        {
00617          blacklist_store_name (&result->pw_name[1], ent);
00618          continue;
00619        }
00620 
00621       /* +user */
00622       if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
00623          && result->pw_name[1] != '@')
00624        {
00625          size_t len = strlen (result->pw_name);
00626          char buf[len];
00627          enum nss_status status;
00628 
00629          /* Store the User in the blacklist for the "+" at the end of
00630             /etc/passwd */
00631          memcpy (buf, &result->pw_name[1], len);
00632          status = getpwnam_plususer (&result->pw_name[1], result, ent,
00633                                   buffer, buflen, errnop);
00634          blacklist_store_name (buf, ent);
00635 
00636          if (status == NSS_STATUS_SUCCESS)       /* We found the entry. */
00637            break;
00638          else if (status == NSS_STATUS_RETURN    /* We couldn't parse the entry */
00639                  || status == NSS_STATUS_NOTFOUND)      /* entry doesn't exist */
00640            continue;
00641          else
00642            {
00643              if (status == NSS_STATUS_TRYAGAIN)
00644               {
00645                 /* The parser ran out of space */
00646                 fsetpos (ent->stream, &pos);
00647                 *errnop = ERANGE;
00648               }
00649              return status;
00650            }
00651        }
00652 
00653       /* +:... */
00654       if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
00655        {
00656          ent->files = false;
00657          ent->first = true;
00658          copy_pwd_changes (&ent->pwd, result, NULL, 0);
00659 
00660          return getpwent_next_nss (result, ent, buffer, buflen, errnop);
00661        }
00662     }
00663 
00664   return NSS_STATUS_SUCCESS;
00665 }
00666 
00667 
00668 static enum nss_status
00669 internal_getpwent_r (struct passwd *pw, ent_t *ent, char *buffer,
00670                    size_t buflen, int *errnop)
00671 {
00672   if (ent->netgroup)
00673     {
00674       enum nss_status status;
00675 
00676       /* We are searching members in a netgroup */
00677       /* Since this is not the first call, we don't need the group name */
00678       status = getpwent_next_nss_netgr (NULL, pw, ent, NULL, buffer, buflen,
00679                                    errnop);
00680       if (status == NSS_STATUS_RETURN)
00681        return getpwent_next_file (pw, ent, buffer, buflen, errnop);
00682       else
00683        return status;
00684     }
00685   else if (ent->files)
00686     return getpwent_next_file (pw, ent, buffer, buflen, errnop);
00687   else
00688     return getpwent_next_nss (pw, ent, buffer, buflen, errnop);
00689 
00690 }
00691 
00692 enum nss_status
00693 _nss_compat_getpwent_r (struct passwd *pwd, char *buffer, size_t buflen,
00694                      int *errnop)
00695 {
00696   enum nss_status result = NSS_STATUS_SUCCESS;
00697 
00698   __libc_lock_lock (lock);
00699 
00700   /* Be prepared that the setpwent function was not called before.  */
00701   if (ni == NULL)
00702     init_nss_interface ();
00703 
00704   if (ext_ent.stream == NULL)
00705     result = internal_setpwent (&ext_ent, 1, 1);
00706 
00707   if (result == NSS_STATUS_SUCCESS)
00708     result = internal_getpwent_r (pwd, &ext_ent, buffer, buflen, errnop);
00709 
00710   __libc_lock_unlock (lock);
00711 
00712   return result;
00713 }
00714 
00715 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user */
00716 static enum nss_status
00717 internal_getpwnam_r (const char *name, struct passwd *result, ent_t *ent,
00718                    char *buffer, size_t buflen, int *errnop)
00719 {
00720   struct parser_data *data = (void *) buffer;
00721 
00722   while (1)
00723     {
00724       fpos_t pos;
00725       char *p;
00726       int parse_res;
00727 
00728       do
00729        {
00730          /* We need at least 3 characters for one line.  */
00731          if (__builtin_expect (buflen < 3, 0))
00732            {
00733            erange:
00734              *errnop = ERANGE;
00735              return NSS_STATUS_TRYAGAIN;
00736            }
00737 
00738          fgetpos (ent->stream, &pos);
00739          buffer[buflen - 1] = '\xff';
00740          p = fgets_unlocked (buffer, buflen, ent->stream);
00741          if (p == NULL && feof_unlocked (ent->stream))
00742            {
00743              return NSS_STATUS_NOTFOUND;
00744            }
00745          if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
00746            {
00747            erange_reset:
00748              fsetpos (ent->stream, &pos);
00749              goto erange;
00750            }
00751 
00752          /* Terminate the line for any case.  */
00753          buffer[buflen - 1] = '\0';
00754 
00755          /* Skip leading blanks.  */
00756          while (isspace (*p))
00757            ++p;
00758        }
00759       while (*p == '\0' || *p == '#' ||   /* Ignore empty and comment lines.  */
00760             /* Parse the line.  If it is invalid, loop to
00761                get the next line of the file to parse.  */
00762             !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
00763                                              errnop)));
00764 
00765       if (__builtin_expect (parse_res == -1, 0))
00766        /* The parser ran out of space.  */
00767        goto erange_reset;
00768 
00769       /* This is a real entry.  */
00770       if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
00771        {
00772          if (strcmp (result->pw_name, name) == 0)
00773            return NSS_STATUS_SUCCESS;
00774          else
00775            continue;
00776        }
00777 
00778       /* -@netgroup */
00779       if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
00780          && result->pw_name[2] != '\0')
00781        {
00782          if (innetgr (&result->pw_name[2], NULL, name, NULL))
00783            return NSS_STATUS_NOTFOUND;
00784          continue;
00785        }
00786 
00787       /* +@netgroup */
00788       if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
00789          && result->pw_name[2] != '\0')
00790        {
00791          enum nss_status status;
00792 
00793          if (innetgr (&result->pw_name[2], NULL, name, NULL))
00794            {
00795              status = getpwnam_plususer (name, result, ent, buffer,
00796                                      buflen, errnop);
00797 
00798              if (status == NSS_STATUS_RETURN)
00799               continue;
00800 
00801              return status;
00802            }
00803          continue;
00804        }
00805 
00806       /* -user */
00807       if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
00808          && result->pw_name[1] != '@')
00809        {
00810          if (strcmp (&result->pw_name[1], name) == 0)
00811            return NSS_STATUS_NOTFOUND;
00812          else
00813            continue;
00814        }
00815 
00816       /* +user */
00817       if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
00818          && result->pw_name[1] != '@')
00819        {
00820          if (strcmp (name, &result->pw_name[1]) == 0)
00821            {
00822              enum nss_status status;
00823 
00824              status = getpwnam_plususer (name, result, ent, buffer, buflen,
00825                                      errnop);
00826              if (status == NSS_STATUS_RETURN)
00827               /* We couldn't parse the entry */
00828               return NSS_STATUS_NOTFOUND;
00829              else
00830               return status;
00831            }
00832        }
00833 
00834       /* +:... */
00835       if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
00836        {
00837          enum nss_status status;
00838 
00839          status = getpwnam_plususer (name, result, ent,
00840                                   buffer, buflen, errnop);
00841          if (status == NSS_STATUS_SUCCESS)       /* We found the entry. */
00842            break;
00843          else if (status == NSS_STATUS_RETURN)   /* We couldn't parse the entry */
00844            return NSS_STATUS_NOTFOUND;
00845          else
00846            return status;
00847        }
00848     }
00849   return NSS_STATUS_SUCCESS;
00850 }
00851 
00852 enum nss_status
00853 _nss_compat_getpwnam_r (const char *name, struct passwd *pwd,
00854                      char *buffer, size_t buflen, int *errnop)
00855 {
00856   enum nss_status result;
00857   ent_t ent = { false, false, true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 },
00858               { NULL, NULL, 0, 0, NULL, NULL, NULL }};
00859 
00860   if (name[0] == '-' || name[0] == '+')
00861     return NSS_STATUS_NOTFOUND;
00862 
00863   __libc_lock_lock (lock);
00864 
00865   if (ni == NULL)
00866     init_nss_interface ();
00867 
00868   __libc_lock_unlock (lock);
00869 
00870   result = internal_setpwent (&ent, 0, 0);
00871 
00872   if (result == NSS_STATUS_SUCCESS)
00873     result = internal_getpwnam_r (name, pwd, &ent, buffer, buflen, errnop);
00874 
00875   internal_endpwent (&ent);
00876 
00877   return result;
00878 }
00879 
00880 /* This function handle the + entry in /etc/passwd for getpwuid */
00881 static enum nss_status
00882 getpwuid_plususer (uid_t uid, struct passwd *result, char *buffer,
00883                  size_t buflen, int *errnop)
00884 {
00885   struct passwd pwd;
00886   char *p;
00887   size_t plen;
00888 
00889   if (!nss_getpwuid_r)
00890     return NSS_STATUS_UNAVAIL;
00891 
00892   memset (&pwd, '\0', sizeof (struct passwd));
00893 
00894   copy_pwd_changes (&pwd, result, NULL, 0);
00895 
00896   plen = pwd_need_buflen (&pwd);
00897   if (plen > buflen)
00898     {
00899       *errnop = ERANGE;
00900       return NSS_STATUS_TRYAGAIN;
00901     }
00902   p = buffer + (buflen - plen);
00903   buflen -= plen;
00904 
00905   if (nss_getpwuid_r (uid, result, buffer, buflen, errnop) ==
00906       NSS_STATUS_SUCCESS)
00907     {
00908       copy_pwd_changes (result, &pwd, p, plen);
00909       give_pwd_free (&pwd);
00910       /* We found the entry.  */
00911       return NSS_STATUS_SUCCESS;
00912     }
00913   else
00914     {
00915       /* Give buffer the old len back */
00916       buflen += plen;
00917       give_pwd_free (&pwd);
00918     }
00919   return NSS_STATUS_RETURN;
00920 }
00921 
00922 /* Searches in /etc/passwd and the NSS subsystem for a special user id */
00923 static enum nss_status
00924 internal_getpwuid_r (uid_t uid, struct passwd *result, ent_t *ent,
00925                    char *buffer, size_t buflen, int *errnop)
00926 {
00927   struct parser_data *data = (void *) buffer;
00928 
00929   while (1)
00930     {
00931       fpos_t pos;
00932       char *p;
00933       int parse_res;
00934 
00935       do
00936        {
00937          /* We need at least 3 characters for one line.  */
00938          if (__builtin_expect (buflen < 3, 0))
00939            {
00940            erange:
00941              *errnop = ERANGE;
00942              return NSS_STATUS_TRYAGAIN;
00943            }
00944 
00945          fgetpos (ent->stream, &pos);
00946          buffer[buflen - 1] = '\xff';
00947          p = fgets_unlocked (buffer, buflen, ent->stream);
00948          if (p == NULL && feof_unlocked (ent->stream))
00949            return NSS_STATUS_NOTFOUND;
00950 
00951          if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
00952            {
00953            erange_reset:
00954              fsetpos (ent->stream, &pos);
00955              goto erange;
00956            }
00957 
00958          /* Terminate the line for any case.  */
00959          buffer[buflen - 1] = '\0';
00960 
00961          /* Skip leading blanks.  */
00962          while (isspace (*p))
00963            ++p;
00964        }
00965       while (*p == '\0' || *p == '#' ||   /* Ignore empty and comment lines.  */
00966             /* Parse the line.  If it is invalid, loop to
00967                get the next line of the file to parse.  */
00968             !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
00969                                              errnop)));
00970 
00971       if (__builtin_expect (parse_res == -1, 0))
00972        /* The parser ran out of space.  */
00973        goto erange_reset;
00974 
00975       /* This is a real entry.  */
00976       if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
00977        {
00978          if (result->pw_uid == uid)
00979            return NSS_STATUS_SUCCESS;
00980          else
00981            continue;
00982        }
00983 
00984       /* -@netgroup */
00985       if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
00986          && result->pw_name[2] != '\0')
00987        {
00988          /* -1, because we remove first two character of pw_name.  */
00989          size_t len = strlen (result->pw_name) - 1;
00990          char buf[len];
00991          enum nss_status status;
00992 
00993          memcpy (buf, &result->pw_name[2], len);
00994 
00995          status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
00996          if (status == NSS_STATUS_SUCCESS &&
00997              innetgr (buf, NULL, result->pw_name, NULL))
00998            return NSS_STATUS_NOTFOUND;
00999 
01000          continue;
01001        }
01002 
01003       /* +@netgroup */
01004       if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
01005          && result->pw_name[2] != '\0')
01006        {
01007          /* -1, because we remove first two characters of pw_name.  */
01008          size_t len = strlen (result->pw_name) - 1;
01009          char buf[len];
01010          enum nss_status status;
01011 
01012          memcpy (buf, &result->pw_name[2], len);
01013 
01014          status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
01015 
01016          if (status == NSS_STATUS_RETURN)
01017            continue;
01018 
01019          if (status == NSS_STATUS_SUCCESS)
01020            {
01021              if (innetgr (buf, NULL, result->pw_name, NULL))
01022               return NSS_STATUS_SUCCESS;
01023            }
01024          else if (status == NSS_STATUS_RETURN)   /* We couldn't parse the entry */
01025            return NSS_STATUS_NOTFOUND;
01026          else
01027            return status;
01028 
01029          continue;
01030        }
01031 
01032       /* -user */
01033       if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
01034          && result->pw_name[1] != '@')
01035        {
01036          size_t len = strlen (result->pw_name);
01037          char buf[len];
01038          enum nss_status status;
01039 
01040          memcpy (buf, &result->pw_name[1], len);
01041 
01042          status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
01043          if (status == NSS_STATUS_SUCCESS &&
01044              innetgr (buf, NULL, result->pw_name, NULL))
01045            return NSS_STATUS_NOTFOUND;
01046          continue;
01047        }
01048 
01049       /* +user */
01050       if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
01051          && result->pw_name[1] != '@')
01052        {
01053          size_t len = strlen (result->pw_name);
01054          char buf[len];
01055          enum nss_status status;
01056 
01057          memcpy (buf, &result->pw_name[1], len);
01058 
01059          status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
01060 
01061          if (status == NSS_STATUS_RETURN)
01062            continue;
01063 
01064          if (status == NSS_STATUS_SUCCESS)
01065            {
01066              if (strcmp (buf, result->pw_name) == 0)
01067               return NSS_STATUS_SUCCESS;
01068            }
01069          else if (status == NSS_STATUS_RETURN)   /* We couldn't parse the entry */
01070            return NSS_STATUS_NOTFOUND;
01071          else
01072            return status;
01073 
01074          continue;
01075        }
01076 
01077       /* +:... */
01078       if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
01079        {
01080          enum nss_status status;
01081 
01082          status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
01083          if (status == NSS_STATUS_SUCCESS)       /* We found the entry. */
01084            break;
01085          else if (status == NSS_STATUS_RETURN)   /* We couldn't parse the entry */
01086            return NSS_STATUS_NOTFOUND;
01087          else
01088            return status;
01089        }
01090     }
01091   return NSS_STATUS_SUCCESS;
01092 }
01093 
01094 enum nss_status
01095 _nss_compat_getpwuid_r (uid_t uid, struct passwd *pwd,
01096                      char *buffer, size_t buflen, int *errnop)
01097 {
01098   enum nss_status result;
01099   ent_t ent = { false, false, true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 },
01100               { NULL, NULL, 0, 0, NULL, NULL, NULL }};
01101 
01102   __libc_lock_lock (lock);
01103 
01104   if (ni == NULL)
01105     init_nss_interface ();
01106 
01107   __libc_lock_unlock (lock);
01108 
01109   result = internal_setpwent (&ent, 0, 0);
01110 
01111   if (result == NSS_STATUS_SUCCESS)
01112     result = internal_getpwuid_r (uid, pwd, &ent, buffer, buflen, errnop);
01113 
01114   internal_endpwent (&ent);
01115 
01116   return result;
01117 }
01118 
01119 
01120 /* Support routines for remembering -@netgroup and -user entries.
01121    The names are stored in a single string with `|' as separator. */
01122 static void
01123 blacklist_store_name (const char *name, ent_t *ent)
01124 {
01125   int namelen = strlen (name);
01126   char *tmp;
01127 
01128   /* first call, setup cache */
01129   if (ent->blacklist.size == 0)
01130     {
01131       ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
01132       ent->blacklist.data = malloc (ent->blacklist.size);
01133       if (ent->blacklist.data == NULL)
01134        return;
01135       ent->blacklist.data[0] = '|';
01136       ent->blacklist.data[1] = '\0';
01137       ent->blacklist.current = 1;
01138     }
01139   else
01140     {
01141       if (in_blacklist (name, namelen, ent))
01142        return;                     /* no duplicates */
01143 
01144       if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
01145        {
01146          ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
01147          tmp = realloc (ent->blacklist.data, ent->blacklist.size);
01148          if (tmp == NULL)
01149            {
01150              free (ent->blacklist.data);
01151              ent->blacklist.size = 0;
01152              return;
01153            }
01154          ent->blacklist.data = tmp;
01155        }
01156     }
01157 
01158   tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
01159   *tmp++ = '|';
01160   *tmp = '\0';
01161   ent->blacklist.current += namelen + 1;
01162 
01163   return;
01164 }
01165 
01166 /* Returns TRUE if ent->blacklist contains name, else FALSE.  */
01167 static bool_t
01168 in_blacklist (const char *name, int namelen, ent_t *ent)
01169 {
01170   char buf[namelen + 3];
01171   char *cp;
01172 
01173   if (ent->blacklist.data == NULL)
01174     return FALSE;
01175 
01176   buf[0] = '|';
01177   cp = stpcpy (&buf[1], name);
01178   *cp++ = '|';
01179   *cp = '\0';
01180   return strstr (ent->blacklist.data, buf) != NULL;
01181 }