Back to index

glibc  2.9
compat-spwd.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 <shadow.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_setspent) (int stayopen);
00038 static enum nss_status (*nss_getspnam_r) (const char *name, struct spwd * sp,
00039                                      char *buffer, size_t buflen,
00040                                      int *errnop);
00041 static enum nss_status (*nss_getspent_r) (struct spwd * sp, char *buffer,
00042                                      size_t buflen, int *errnop);
00043 static enum nss_status (*nss_endspent) (void);
00044 
00045 /* Get the declaration of the parser function.  */
00046 #define ENTNAME spent
00047 #define STRUCTURE spwd
00048 #define EXTERN_PARSER
00049 #include <nss/nss_files/files-parse.c>
00050 
00051 /* Structure for remembering -@netgroup and -user members ... */
00052 #define BLACKLIST_INITIAL_SIZE 512
00053 #define BLACKLIST_INCREMENT 256
00054 struct blacklist_t
00055 {
00056   char *data;
00057   int current;
00058   int size;
00059 };
00060 
00061 struct ent_t
00062 {
00063   bool netgroup;
00064   bool files;
00065   bool first;
00066   enum nss_status setent_status;
00067   FILE *stream;
00068   struct blacklist_t blacklist;
00069   struct spwd pwd;
00070   struct __netgrent netgrdata;
00071 };
00072 typedef struct ent_t ent_t;
00073 
00074 static ent_t ext_ent = { false, true, false, NSS_STATUS_SUCCESS, NULL,
00075                       { NULL, 0, 0},
00076                       { NULL, NULL, 0, 0, 0, 0, 0, 0, 0}};
00077 
00078 /* Protect global state against multiple changers.  */
00079 __libc_lock_define_initialized (static, lock)
00080 
00081 /* Positive if O_CLOEXEC is supported, negative if it is not supported,
00082    zero if it is still undecided.  This variable is shared with the
00083    other compat functions.  */
00084 #ifdef __ASSUME_O_CLOEXEC
00085 # define __compat_have_cloexec 1
00086 #else
00087 # ifdef O_CLOEXEC
00088 extern int __compat_have_cloexec;
00089 # else
00090 #  define __compat_have_cloexec -1
00091 # endif
00092 #endif
00093 
00094 /* Prototypes for local functions.  */
00095 static void blacklist_store_name (const char *, ent_t *);
00096 static int in_blacklist (const char *, int, ent_t *);
00097 
00098 /* Initialize the NSS interface/functions. The calling function must
00099    hold the lock.  */
00100 static void
00101 init_nss_interface (void)
00102 {
00103   if (__nss_database_lookup ("shadow_compat", "passwd_compat",
00104                           "nis", &ni) >= 0)
00105     {
00106       nss_setspent = __nss_lookup_function (ni, "setspent");
00107       nss_getspnam_r = __nss_lookup_function (ni, "getspnam_r");
00108       nss_getspent_r = __nss_lookup_function (ni, "getspent_r");
00109       nss_endspent = __nss_lookup_function (ni, "endspent");
00110     }
00111 }
00112 
00113 static void
00114 give_spwd_free (struct spwd *pwd)
00115 {
00116   free (pwd->sp_namp);
00117   free (pwd->sp_pwdp);
00118 
00119   memset (pwd, '\0', sizeof (struct spwd));
00120   pwd->sp_warn = -1;
00121   pwd->sp_inact = -1;
00122   pwd->sp_expire = -1;
00123   pwd->sp_flag = ~0ul;
00124 }
00125 
00126 static int
00127 spwd_need_buflen (struct spwd *pwd)
00128 {
00129   int len = 0;
00130 
00131   if (pwd->sp_pwdp != NULL)
00132     len += strlen (pwd->sp_pwdp) + 1;
00133 
00134   return len;
00135 }
00136 
00137 static void
00138 copy_spwd_changes (struct spwd *dest, struct spwd *src,
00139                  char *buffer, size_t buflen)
00140 {
00141   if (src->sp_pwdp != NULL && strlen (src->sp_pwdp))
00142     {
00143       if (buffer == NULL)
00144        dest->sp_pwdp = strdup (src->sp_pwdp);
00145       else if (dest->sp_pwdp &&
00146               strlen (dest->sp_pwdp) >= strlen (src->sp_pwdp))
00147        strcpy (dest->sp_pwdp, src->sp_pwdp);
00148       else
00149        {
00150          dest->sp_pwdp = buffer;
00151          strcpy (dest->sp_pwdp, src->sp_pwdp);
00152          buffer += strlen (dest->sp_pwdp) + 1;
00153          buflen = buflen - (strlen (dest->sp_pwdp) + 1);
00154        }
00155     }
00156   if (src->sp_lstchg != 0)
00157     dest->sp_lstchg = src->sp_lstchg;
00158   if (src->sp_min != 0)
00159     dest->sp_min = src->sp_min;
00160   if (src->sp_max != 0)
00161     dest->sp_max = src->sp_max;
00162   if (src->sp_warn != -1)
00163     dest->sp_warn = src->sp_warn;
00164   if (src->sp_inact != -1)
00165     dest->sp_inact = src->sp_inact;
00166   if (src->sp_expire != -1)
00167     dest->sp_expire = src->sp_expire;
00168   if (src->sp_flag != ~0ul)
00169     dest->sp_flag = src->sp_flag;
00170 }
00171 
00172 static enum nss_status
00173 internal_setspent (ent_t *ent, int stayopen)
00174 {
00175   enum nss_status status = NSS_STATUS_SUCCESS;
00176 
00177   ent->first = ent->netgroup = 0;
00178   ent->files = true;
00179 
00180   /* If something was left over free it.  */
00181   if (ent->netgroup)
00182     __internal_endnetgrent (&ent->netgrdata);
00183 
00184   if (ent->blacklist.data != NULL)
00185     {
00186       ent->blacklist.current = 1;
00187       ent->blacklist.data[0] = '|';
00188       ent->blacklist.data[1] = '\0';
00189     }
00190   else
00191     ent->blacklist.current = 0;
00192 
00193   if (ent->stream == NULL)
00194     {
00195       ent->stream = fopen ("/etc/shadow", "rme");
00196 
00197       if (ent->stream == NULL)
00198        status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
00199       else
00200        {
00201          /* We have to make sure the file is  `closed on exec'.  */
00202          int result = 0;
00203 
00204          if (__compat_have_cloexec <= 0)
00205            {
00206              int flags;
00207              result = flags = fcntl (fileno_unlocked (ent->stream), F_GETFD,
00208                                   0);
00209              if (result >= 0)
00210               {
00211 #if defined O_CLOEXEC && !defined __ASSUME_O_CLOEXEC
00212                 if (__compat_have_cloexec == 0)
00213                   __compat_have_cloexec = (flags & FD_CLOEXEC) ? 1 : -1;
00214 
00215                 if (__compat_have_cloexec < 0)
00216 #endif
00217                   {
00218                     flags |= FD_CLOEXEC;
00219                     result = fcntl (fileno_unlocked (ent->stream), F_SETFD,
00220                                   flags);
00221                   }
00222               }
00223            }
00224 
00225          if (result < 0)
00226            {
00227              /* Something went wrong.  Close the stream and return a
00228                 failure.  */
00229              fclose (ent->stream);
00230              ent->stream = NULL;
00231              status = NSS_STATUS_UNAVAIL;
00232            }
00233          else
00234            /* We take care of locking ourself.  */
00235            __fsetlocking (ent->stream, FSETLOCKING_BYCALLER);
00236        }
00237     }
00238   else
00239     rewind (ent->stream);
00240 
00241   give_spwd_free (&ent->pwd);
00242 
00243   if (status == NSS_STATUS_SUCCESS && nss_setspent)
00244     ent->setent_status = nss_setspent (stayopen);
00245 
00246   return status;
00247 }
00248 
00249 
00250 enum nss_status
00251 _nss_compat_setspent (int stayopen)
00252 {
00253   enum nss_status result;
00254 
00255   __libc_lock_lock (lock);
00256 
00257   if (ni == NULL)
00258     init_nss_interface ();
00259 
00260   result = internal_setspent (&ext_ent, stayopen);
00261 
00262   __libc_lock_unlock (lock);
00263 
00264   return result;
00265 }
00266 
00267 
00268 static enum nss_status
00269 internal_endspent (ent_t *ent)
00270 {
00271   if (nss_endspent)
00272     nss_endspent ();
00273 
00274   if (ent->stream != NULL)
00275     {
00276       fclose (ent->stream);
00277       ent->stream = NULL;
00278     }
00279 
00280   if (ent->netgroup)
00281     __internal_endnetgrent (&ent->netgrdata);
00282 
00283   ent->first = ent->netgroup = false;
00284   ent->files = true;
00285 
00286   if (ent->blacklist.data != NULL)
00287     {
00288       ent->blacklist.current = 1;
00289       ent->blacklist.data[0] = '|';
00290       ent->blacklist.data[1] = '\0';
00291     }
00292   else
00293     ent->blacklist.current = 0;
00294 
00295   give_spwd_free (&ent->pwd);
00296 
00297   return NSS_STATUS_SUCCESS;
00298 }
00299 
00300 enum nss_status
00301 _nss_compat_endspent (void)
00302 {
00303   enum nss_status result;
00304 
00305   __libc_lock_lock (lock);
00306 
00307   result = internal_endspent (&ext_ent);
00308 
00309   __libc_lock_unlock (lock);
00310 
00311   return result;
00312 }
00313 
00314 
00315 static enum nss_status
00316 getspent_next_nss_netgr (const char *name, struct spwd *result, ent_t *ent,
00317                       char *group, char *buffer, size_t buflen,
00318                       int *errnop)
00319 {
00320   char *curdomain, *host, *user, *domain, *p2;
00321   size_t p2len;
00322 
00323   if (!nss_getspnam_r)
00324     return NSS_STATUS_UNAVAIL;
00325 
00326   /* If the setpwent call failed, say so.  */
00327   if (ent->setent_status != NSS_STATUS_SUCCESS)
00328     return ent->setent_status;
00329 
00330   if (yp_get_default_domain (&curdomain) != YPERR_SUCCESS)
00331     {
00332       ent->netgroup = false;
00333       ent->first = false;
00334       give_spwd_free (&ent->pwd);
00335       return NSS_STATUS_UNAVAIL;
00336     }
00337 
00338   if (ent->first == true)
00339     {
00340       memset (&ent->netgrdata, 0, sizeof (struct __netgrent));
00341       __internal_setnetgrent (group, &ent->netgrdata);
00342       ent->first = false;
00343     }
00344 
00345   while (1)
00346     {
00347       char *saved_cursor;
00348       enum nss_status status;
00349 
00350       saved_cursor = ent->netgrdata.cursor;
00351       status = __internal_getnetgrent_r (&host, &user, &domain,
00352                                     &ent->netgrdata, buffer, buflen,
00353                                     errnop);
00354       if (status != 1)
00355        {
00356          __internal_endnetgrent (&ent->netgrdata);
00357          ent->netgroup = false;
00358          give_spwd_free (&ent->pwd);
00359          return NSS_STATUS_RETURN;
00360        }
00361 
00362       if (user == NULL || user[0] == '-')
00363        continue;
00364 
00365       if (domain != NULL && strcmp (curdomain, domain) != 0)
00366        continue;
00367 
00368       /* If name != NULL, we are called from getpwnam */
00369       if (name != NULL)
00370        if (strcmp (user, name) != 0)
00371          continue;
00372 
00373       p2len = spwd_need_buflen (&ent->pwd);
00374       if (p2len > buflen)
00375        {
00376          *errnop = ERANGE;
00377          return NSS_STATUS_TRYAGAIN;
00378        }
00379       p2 = buffer + (buflen - p2len);
00380       buflen -= p2len;
00381 
00382       if (nss_getspnam_r (user, result, buffer, buflen, errnop) !=
00383          NSS_STATUS_SUCCESS)
00384        continue;
00385 
00386       if (!in_blacklist (result->sp_namp, strlen (result->sp_namp), ent))
00387        {
00388          /* Store the User in the blacklist for possible the "+" at the
00389             end of /etc/passwd */
00390          blacklist_store_name (result->sp_namp, ent);
00391          copy_spwd_changes (result, &ent->pwd, p2, p2len);
00392          break;
00393        }
00394     }
00395 
00396   return NSS_STATUS_SUCCESS;
00397 }
00398 
00399 
00400 static enum nss_status
00401 getspent_next_nss (struct spwd *result, ent_t *ent,
00402                  char *buffer, size_t buflen, int *errnop)
00403 {
00404   enum nss_status status;
00405   char *p2;
00406   size_t p2len;
00407 
00408   if (!nss_getspent_r)
00409     return NSS_STATUS_UNAVAIL;
00410 
00411   p2len = spwd_need_buflen (&ent->pwd);
00412   if (p2len > buflen)
00413     {
00414       *errnop = ERANGE;
00415       return NSS_STATUS_TRYAGAIN;
00416     }
00417   p2 = buffer + (buflen - p2len);
00418   buflen -= p2len;
00419   do
00420     {
00421       if ((status = nss_getspent_r (result, buffer, buflen, errnop)) !=
00422          NSS_STATUS_SUCCESS)
00423        return status;
00424     }
00425   while (in_blacklist (result->sp_namp, strlen (result->sp_namp), ent));
00426 
00427   copy_spwd_changes (result, &ent->pwd, p2, p2len);
00428 
00429   return NSS_STATUS_SUCCESS;
00430 }
00431 
00432 
00433 /* This function handle the +user entrys in /etc/shadow */
00434 static enum nss_status
00435 getspnam_plususer (const char *name, struct spwd *result, ent_t *ent,
00436                  char *buffer, size_t buflen, int *errnop)
00437 {
00438   if (!nss_getspnam_r)
00439     return NSS_STATUS_UNAVAIL;
00440 
00441   struct spwd pwd;
00442   memset (&pwd, '\0', sizeof (struct spwd));
00443   pwd.sp_warn = -1;
00444   pwd.sp_inact = -1;
00445   pwd.sp_expire = -1;
00446   pwd.sp_flag = ~0ul;
00447 
00448   copy_spwd_changes (&pwd, result, NULL, 0);
00449 
00450   size_t plen = spwd_need_buflen (&pwd);
00451   if (plen > buflen)
00452     {
00453       *errnop = ERANGE;
00454       return NSS_STATUS_TRYAGAIN;
00455     }
00456   char *p = buffer + (buflen - plen);
00457   buflen -= plen;
00458 
00459   enum nss_status status = nss_getspnam_r (name, result, buffer, buflen,
00460                                       errnop);
00461   if (status != NSS_STATUS_SUCCESS)
00462     return status;
00463 
00464   if (in_blacklist (result->sp_namp, strlen (result->sp_namp), ent))
00465     return NSS_STATUS_NOTFOUND;
00466 
00467   copy_spwd_changes (result, &pwd, p, plen);
00468   give_spwd_free (&pwd);
00469   /* We found the entry.  */
00470   return NSS_STATUS_SUCCESS;
00471 }
00472 
00473 
00474 static enum nss_status
00475 getspent_next_file (struct spwd *result, ent_t *ent,
00476                   char *buffer, size_t buflen, int *errnop)
00477 {
00478   struct parser_data *data = (void *) buffer;
00479   while (1)
00480     {
00481       fpos_t pos;
00482       int parse_res = 0;
00483       char *p;
00484 
00485       do
00486        {
00487          /* We need at least 3 characters for one line.  */
00488          if (__builtin_expect (buflen < 3, 0))
00489            {
00490            erange:
00491              *errnop = ERANGE;
00492              return NSS_STATUS_TRYAGAIN;
00493            }
00494 
00495          fgetpos (ent->stream, &pos);
00496          buffer[buflen - 1] = '\xff';
00497          p = fgets_unlocked (buffer, buflen, ent->stream);
00498          if (p == NULL && feof_unlocked (ent->stream))
00499            return NSS_STATUS_NOTFOUND;
00500 
00501          if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
00502            {
00503            erange_reset:
00504              fsetpos (ent->stream, &pos);
00505              goto erange;
00506            }
00507 
00508          /* Skip leading blanks.  */
00509          while (isspace (*p))
00510            ++p;
00511        }
00512       while (*p == '\0' || *p == '#'      /* Ignore empty and comment lines.  */
00513             /* Parse the line.  If it is invalid, loop to
00514                get the next line of the file to parse.  */
00515             || !(parse_res = _nss_files_parse_spent (p, result, data,
00516                                                 buflen, errnop)));
00517 
00518       if (__builtin_expect (parse_res == -1, 0))
00519        /* The parser ran out of space.  */
00520        goto erange_reset;
00521 
00522       if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-')
00523        /* This is a real entry.  */
00524        break;
00525 
00526       /* -@netgroup */
00527       if (result->sp_namp[0] == '-' && result->sp_namp[1] == '@'
00528          && result->sp_namp[2] != '\0')
00529        {
00530          /* XXX Do not use fixed length buffers.  */
00531          char buf2[1024];
00532          char *user, *host, *domain;
00533          struct __netgrent netgrdata;
00534 
00535          bzero (&netgrdata, sizeof (struct __netgrent));
00536          __internal_setnetgrent (&result->sp_namp[2], &netgrdata);
00537          while (__internal_getnetgrent_r (&host, &user, &domain,
00538                                       &netgrdata, buf2, sizeof (buf2),
00539                                       errnop))
00540            {
00541              if (user != NULL && user[0] != '-')
00542               blacklist_store_name (user, ent);
00543            }
00544          __internal_endnetgrent (&netgrdata);
00545          continue;
00546        }
00547 
00548       /* +@netgroup */
00549       if (result->sp_namp[0] == '+' && result->sp_namp[1] == '@'
00550          && result->sp_namp[2] != '\0')
00551        {
00552          int status;
00553 
00554          ent->netgroup = true;
00555          ent->first = true;
00556          copy_spwd_changes (&ent->pwd, result, NULL, 0);
00557 
00558          status = getspent_next_nss_netgr (NULL, result, ent,
00559                                        &result->sp_namp[2],
00560                                        buffer, buflen, errnop);
00561          if (status == NSS_STATUS_RETURN)
00562            continue;
00563          else
00564            return status;
00565        }
00566 
00567       /* -user */
00568       if (result->sp_namp[0] == '-' && result->sp_namp[1] != '\0'
00569          && result->sp_namp[1] != '@')
00570        {
00571          blacklist_store_name (&result->sp_namp[1], ent);
00572          continue;
00573        }
00574 
00575       /* +user */
00576       if (result->sp_namp[0] == '+' && result->sp_namp[1] != '\0'
00577          && result->sp_namp[1] != '@')
00578        {
00579          size_t len = strlen (result->sp_namp);
00580          char buf[len];
00581          enum nss_status status;
00582 
00583          /* Store the User in the blacklist for the "+" at the end of
00584             /etc/passwd */
00585          memcpy (buf, &result->sp_namp[1], len);
00586          status = getspnam_plususer (&result->sp_namp[1], result, ent,
00587                                   buffer, buflen, errnop);
00588          blacklist_store_name (buf, ent);
00589 
00590          if (status == NSS_STATUS_SUCCESS)       /* We found the entry. */
00591            break;
00592          /* We couldn't parse the entry */
00593          else if (status == NSS_STATUS_RETURN
00594                  /* entry doesn't exist */
00595                  || status == NSS_STATUS_NOTFOUND)
00596            continue;
00597          else
00598            {
00599              if (status == NSS_STATUS_TRYAGAIN)
00600               {
00601                 fsetpos (ent->stream, &pos);
00602                 *errnop = ERANGE;
00603               }
00604              return status;
00605            }
00606        }
00607 
00608       /* +:... */
00609       if (result->sp_namp[0] == '+' && result->sp_namp[1] == '\0')
00610        {
00611          ent->files = false;
00612          ent->first = true;
00613          copy_spwd_changes (&ent->pwd, result, NULL, 0);
00614 
00615          return getspent_next_nss (result, ent, buffer, buflen, errnop);
00616        }
00617     }
00618 
00619   return NSS_STATUS_SUCCESS;
00620 }
00621 
00622 
00623 static enum nss_status
00624 internal_getspent_r (struct spwd *pw, ent_t *ent,
00625                    char *buffer, size_t buflen, int *errnop)
00626 {
00627   if (ent->netgroup)
00628     {
00629       enum nss_status status;
00630 
00631       /* We are searching members in a netgroup */
00632       /* Since this is not the first call, we don't need the group name */
00633       status = getspent_next_nss_netgr (NULL, pw, ent, NULL, buffer,
00634                                    buflen, errnop);
00635 
00636       if (status == NSS_STATUS_RETURN)
00637        return getspent_next_file (pw, ent, buffer, buflen, errnop);
00638       else
00639        return status;
00640     }
00641   else if (ent->files)
00642     return getspent_next_file (pw, ent, buffer, buflen, errnop);
00643   else
00644     return getspent_next_nss (pw, ent, buffer, buflen, errnop);
00645 }
00646 
00647 
00648 enum nss_status
00649 _nss_compat_getspent_r (struct spwd *pwd, char *buffer, size_t buflen,
00650                      int *errnop)
00651 {
00652   enum nss_status result = NSS_STATUS_SUCCESS;
00653 
00654   __libc_lock_lock (lock);
00655 
00656   /* Be prepared that the setpwent function was not called before.  */
00657   if (ni == NULL)
00658     init_nss_interface ();
00659 
00660   if (ext_ent.stream == NULL)
00661     result = internal_setspent (&ext_ent, 1);
00662 
00663   if (result == NSS_STATUS_SUCCESS)
00664     result = internal_getspent_r (pwd, &ext_ent, buffer, buflen, errnop);
00665 
00666   __libc_lock_unlock (lock);
00667 
00668   return result;
00669 }
00670 
00671 
00672 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user */
00673 static enum nss_status
00674 internal_getspnam_r (const char *name, struct spwd *result, ent_t *ent,
00675                    char *buffer, size_t buflen, int *errnop)
00676 {
00677   struct parser_data *data = (void *) buffer;
00678 
00679   while (1)
00680     {
00681       fpos_t pos;
00682       char *p;
00683       int parse_res;
00684 
00685       do
00686        {
00687          /* We need at least 3 characters for one line.  */
00688          if (__builtin_expect (buflen < 3, 0))
00689            {
00690            erange:
00691              *errnop = ERANGE;
00692              return NSS_STATUS_TRYAGAIN;
00693            }
00694 
00695          fgetpos (ent->stream, &pos);
00696          buffer[buflen - 1] = '\xff';
00697          p = fgets_unlocked (buffer, buflen, ent->stream);
00698          if (p == NULL && feof_unlocked (ent->stream))
00699            return NSS_STATUS_NOTFOUND;
00700 
00701          if (p == NULL || buffer[buflen - 1] != '\xff')
00702            {
00703            erange_reset:
00704              fsetpos (ent->stream, &pos);
00705              goto erange;
00706            }
00707 
00708           /* Terminate the line for any case.  */
00709          buffer[buflen - 1] = '\0';
00710 
00711          /* Skip leading blanks.  */
00712          while (isspace (*p))
00713            ++p;
00714        }
00715       while (*p == '\0' || *p == '#' ||   /* Ignore empty and comment lines.  */
00716             /* Parse the line.  If it is invalid, loop to
00717                get the next line of the file to parse.  */
00718             !(parse_res = _nss_files_parse_spent (p, result, data, buflen,
00719                                              errnop)));
00720 
00721       if (__builtin_expect (parse_res == -1, 0))
00722        /* The parser ran out of space.  */
00723        goto erange_reset;
00724 
00725       /* This is a real entry.  */
00726       if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-')
00727        {
00728          if (strcmp (result->sp_namp, name) == 0)
00729            return NSS_STATUS_SUCCESS;
00730          else
00731            continue;
00732        }
00733 
00734       /* -@netgroup */
00735       /* If the loaded NSS module does not support this service, add
00736          all users from a +@netgroup entry to the blacklist, too.  */
00737       if (result->sp_namp[0] == '-' && result->sp_namp[1] == '@'
00738          && result->sp_namp[2] != '\0')
00739        {
00740          if (innetgr (&result->sp_namp[2], NULL, name, NULL))
00741            return NSS_STATUS_NOTFOUND;
00742          continue;
00743        }
00744 
00745       /* +@netgroup */
00746       if (result->sp_namp[0] == '+' && result->sp_namp[1] == '@'
00747          && result->sp_namp[2] != '\0')
00748        {
00749          enum nss_status status;
00750 
00751          if (innetgr (&result->sp_namp[2], NULL, name, NULL))
00752            {
00753              status = getspnam_plususer (name, result, ent, buffer,
00754                                      buflen, errnop);
00755 
00756              if (status == NSS_STATUS_RETURN)
00757               continue;
00758 
00759              return status;
00760            }
00761          continue;
00762        }
00763 
00764       /* -user */
00765       if (result->sp_namp[0] == '-' && result->sp_namp[1] != '\0'
00766          && result->sp_namp[1] != '@')
00767        {
00768          if (strcmp (&result->sp_namp[1], name) == 0)
00769            return NSS_STATUS_NOTFOUND;
00770          else
00771            continue;
00772        }
00773 
00774       /* +user */
00775       if (result->sp_namp[0] == '+' && result->sp_namp[1] != '\0'
00776          && result->sp_namp[1] != '@')
00777        {
00778          if (strcmp (name, &result->sp_namp[1]) == 0)
00779            {
00780              enum nss_status status;
00781 
00782              status = getspnam_plususer (name, result, ent,
00783                                      buffer, buflen, errnop);
00784 
00785              if (status == NSS_STATUS_RETURN)
00786               /* We couldn't parse the entry */
00787               return NSS_STATUS_NOTFOUND;
00788              else
00789               return status;
00790            }
00791        }
00792 
00793       /* +:... */
00794       if (result->sp_namp[0] == '+' && result->sp_namp[1] == '\0')
00795        {
00796          enum nss_status status;
00797 
00798          status = getspnam_plususer (name, result, ent,
00799                                   buffer, buflen, errnop);
00800 
00801          if (status == NSS_STATUS_SUCCESS)
00802            /* We found the entry. */
00803             break;
00804           else if (status == NSS_STATUS_RETURN)
00805            /* We couldn't parse the entry */
00806             return NSS_STATUS_NOTFOUND;
00807          else
00808            return status;
00809        }
00810     }
00811   return NSS_STATUS_SUCCESS;
00812 }
00813 
00814 
00815 enum nss_status
00816 _nss_compat_getspnam_r (const char *name, struct spwd *pwd,
00817                      char *buffer, size_t buflen, int *errnop)
00818 {
00819   enum nss_status result;
00820   ent_t ent = { false, true, false, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0},
00821               { NULL, NULL, 0, 0, 0, 0, 0, 0, 0}};
00822 
00823   if (name[0] == '-' || name[0] == '+')
00824     return NSS_STATUS_NOTFOUND;
00825 
00826   __libc_lock_lock (lock);
00827 
00828   if (ni == NULL)
00829     init_nss_interface ();
00830 
00831   __libc_lock_unlock (lock);
00832 
00833   result = internal_setspent (&ent, 0);
00834 
00835   if (result == NSS_STATUS_SUCCESS)
00836     result = internal_getspnam_r (name, pwd, &ent, buffer, buflen, errnop);
00837 
00838   internal_endspent (&ent);
00839 
00840   return result;
00841 }
00842 
00843 
00844 /* Support routines for remembering -@netgroup and -user entries.
00845    The names are stored in a single string with `|' as separator. */
00846 static void
00847 blacklist_store_name (const char *name, ent_t *ent)
00848 {
00849   int namelen = strlen (name);
00850   char *tmp;
00851 
00852   /* first call, setup cache */
00853   if (ent->blacklist.size == 0)
00854     {
00855       ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
00856       ent->blacklist.data = malloc (ent->blacklist.size);
00857       if (ent->blacklist.data == NULL)
00858        return;
00859       ent->blacklist.data[0] = '|';
00860       ent->blacklist.data[1] = '\0';
00861       ent->blacklist.current = 1;
00862     }
00863   else
00864     {
00865       if (in_blacklist (name, namelen, ent))
00866        return;                     /* no duplicates */
00867 
00868       if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
00869        {
00870          ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
00871          tmp = realloc (ent->blacklist.data, ent->blacklist.size);
00872          if (tmp == NULL)
00873            {
00874              free (ent->blacklist.data);
00875              ent->blacklist.size = 0;
00876              return;
00877            }
00878          ent->blacklist.data = tmp;
00879        }
00880     }
00881 
00882   tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
00883   *tmp++ = '|';
00884   *tmp = '\0';
00885   ent->blacklist.current += namelen + 1;
00886 
00887   return;
00888 }
00889 
00890 
00891 /* Returns TRUE if ent->blacklist contains name, else FALSE.  */
00892 static bool_t
00893 in_blacklist (const char *name, int namelen, ent_t *ent)
00894 {
00895   char buf[namelen + 3];
00896   char *cp;
00897 
00898   if (ent->blacklist.data == NULL)
00899     return false;
00900 
00901   buf[0] = '|';
00902   cp = stpcpy (&buf[1], name);
00903   *cp++ = '|';
00904   *cp = '\0';
00905   return strstr (ent->blacklist.data, buf) != NULL;
00906 }