Fix handling of default values for sp_warn, sp_inact, sp_expire and
[kopensolaris-gnu/glibc.git] / nis / nss_compat / compat-pwd.c
1 /* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <nss.h>
21 #include <pwd.h>
22 #include <errno.h>
23 #include <ctype.h>
24 #include <fcntl.h>
25 #include <netdb.h>
26 #include <string.h>
27 #include <bits/libc-lock.h>
28 #include <rpcsvc/yp.h>
29 #include <rpcsvc/ypclnt.h>
30 #include <rpcsvc/nis.h>
31 #include <nsswitch.h>
32
33 #include "netgroup.h"
34 #include "nss-nisplus.h"
35 #include "nisplus-parser.h"
36
37 static service_user *ni = NULL;
38 static bool_t use_nisplus = FALSE; /* default: passwd_compat: nis */
39 static nis_name pwdtable = NULL; /* Name of the pwd table */
40 static size_t pwdtablelen = 0;
41
42 /* Get the declaration of the parser function.  */
43 #define ENTNAME pwent
44 #define STRUCTURE passwd
45 #define EXTERN_PARSER
46 #include <nss/nss_files/files-parse.c>
47
48 /* Structure for remembering -@netgroup and -user members ... */
49 #define BLACKLIST_INITIAL_SIZE 512
50 #define BLACKLIST_INCREMENT 256
51 struct blacklist_t
52   {
53     char *data;
54     int current;
55     int size;
56   };
57
58 struct ent_t
59   {
60     bool_t netgroup;
61     bool_t nis;
62     bool_t first;
63     char *oldkey;
64     int oldkeylen;
65     nis_result *result;
66     FILE *stream;
67     struct blacklist_t blacklist;
68     struct passwd pwd;
69     struct __netgrent netgrdata;
70   };
71 typedef struct ent_t ent_t;
72
73 static ent_t ext_ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
74                         {NULL, NULL, 0, 0, NULL, NULL, NULL}};
75
76 /* Protect global state against multiple changers.  */
77 __libc_lock_define_initialized (static, lock)
78
79 /* Prototypes for local functions.  */
80 static void blacklist_store_name (const char *, ent_t *);
81 static int in_blacklist (const char *, int, ent_t *);
82
83 static void
84 give_pwd_free (struct passwd *pwd)
85 {
86   if (pwd->pw_name != NULL)
87     free (pwd->pw_name);
88   if (pwd->pw_passwd != NULL)
89     free (pwd->pw_passwd);
90   if (pwd->pw_gecos != NULL)
91     free (pwd->pw_gecos);
92   if (pwd->pw_dir != NULL)
93     free (pwd->pw_dir);
94   if (pwd->pw_shell != NULL)
95     free (pwd->pw_shell);
96
97   memset (pwd, '\0', sizeof (struct passwd));
98 }
99
100 static size_t
101 pwd_need_buflen (struct passwd *pwd)
102 {
103   size_t len = 0;
104
105   if (pwd->pw_passwd != NULL)
106     len += strlen (pwd->pw_passwd) + 1;
107
108   if (pwd->pw_gecos != NULL)
109     len += strlen (pwd->pw_gecos) + 1;
110
111   if (pwd->pw_dir != NULL)
112     len += strlen (pwd->pw_dir) + 1;
113
114   if (pwd->pw_shell != NULL)
115     len += strlen (pwd->pw_shell) + 1;
116
117   return len;
118 }
119
120 static void
121 copy_pwd_changes (struct passwd *dest, struct passwd *src,
122                   char *buffer, size_t buflen)
123 {
124   if (src->pw_passwd != NULL && strlen (src->pw_passwd))
125     {
126       if (buffer == NULL)
127         dest->pw_passwd = strdup (src->pw_passwd);
128       else if (dest->pw_passwd &&
129                strlen (dest->pw_passwd) >= strlen (src->pw_passwd))
130         strcpy (dest->pw_passwd, src->pw_passwd);
131       else
132         {
133           dest->pw_passwd = buffer;
134           strcpy (dest->pw_passwd, src->pw_passwd);
135           buffer += strlen (dest->pw_passwd) + 1;
136           buflen = buflen - (strlen (dest->pw_passwd) + 1);
137         }
138     }
139
140   if (src->pw_gecos != NULL && strlen (src->pw_gecos))
141     {
142       if (buffer == NULL)
143         dest->pw_gecos = strdup (src->pw_gecos);
144       else if (dest->pw_gecos &&
145                strlen (dest->pw_gecos) >= strlen (src->pw_gecos))
146         strcpy (dest->pw_gecos, src->pw_gecos);
147       else
148         {
149           dest->pw_gecos = buffer;
150           strcpy (dest->pw_gecos, src->pw_gecos);
151           buffer += strlen (dest->pw_gecos) + 1;
152           buflen = buflen - (strlen (dest->pw_gecos) + 1);
153         }
154     }
155   if (src->pw_dir != NULL && strlen (src->pw_dir))
156     {
157       if (buffer == NULL)
158         dest->pw_dir = strdup (src->pw_dir);
159       else if (dest->pw_dir &&
160                strlen (dest->pw_dir) >= strlen (src->pw_dir))
161         strcpy (dest->pw_dir, src->pw_dir);
162       else
163         {
164           dest->pw_dir = buffer;
165           strcpy (dest->pw_dir, src->pw_dir);
166           buffer += strlen (dest->pw_dir) + 1;
167           buflen = buflen - (strlen (dest->pw_dir) + 1);
168         }
169     }
170
171   if (src->pw_shell != NULL && strlen (src->pw_shell))
172     {
173       if (buffer == NULL)
174         dest->pw_shell = strdup (src->pw_shell);
175       else if (dest->pw_shell &&
176                strlen (dest->pw_shell) >= strlen (src->pw_shell))
177         strcpy (dest->pw_shell, src->pw_shell);
178       else
179         {
180           dest->pw_shell = buffer;
181           strcpy (dest->pw_shell, src->pw_shell);
182           buffer += strlen (dest->pw_shell) + 1;
183           buflen = buflen - (strlen (dest->pw_shell) + 1);
184         }
185     }
186 }
187
188 static enum nss_status
189 insert_passwd_adjunct (char **result, int *len, char *domain, int *errnop)
190 {
191   char *p1, *p2, *result2, *res;
192   int len2;
193   size_t namelen;
194
195   /* Check for adjunct style secret passwords.  They can be
196      recognized by a password starting with "##".  */
197   p1 = strchr (*result, ':');
198   if (p1 == NULL || p1[1] != '#' || p1[2] != '#')
199     return NSS_STATUS_SUCCESS;
200   p2 = strchr (p1 + 3, ':');
201
202   namelen = p2 - p1 - 3;
203
204   if (yp_match (domain, "passwd.adjunct.byname", &p1[3], namelen,
205                 &result2, &len2) == YPERR_SUCCESS)
206     {
207       /* We found a passwd.adjunct entry.  Merge encrypted
208          password therein into original result.  */
209       char *encrypted = strchr (result2, ':');
210       char *endp;
211       size_t restlen;
212
213       if (encrypted == NULL || (endp = strchr (++encrypted, ':')) == NULL)
214         {
215           /* Invalid format of the entry.  This never should happen
216              unless the data from which the NIS table is generated is
217              wrong.  We simply ignore it.  */
218           free (result2);
219           return NSS_STATUS_SUCCESS;
220         }
221
222       restlen = *len - (p2 - *result);
223       if ((res = malloc (namelen + restlen + (endp - encrypted) + 2)) == NULL)
224         {
225           free (result2);
226           *errnop = ENOMEM;
227           return NSS_STATUS_TRYAGAIN;
228         }
229
230       __mempcpy (__mempcpy (__mempcpy (__mempcpy
231                                        (res, *result, (p1 - *result)),
232                                        ":", 1),
233                             encrypted, endp - encrypted),
234                  p2, restlen + 1);
235
236       free (result2);
237       free (*result);
238       *result = res;
239       *len = strlen (res);
240     }
241   return NSS_STATUS_SUCCESS;
242 }
243
244 static enum nss_status
245 internal_setpwent (ent_t *ent)
246 {
247   enum nss_status status = NSS_STATUS_SUCCESS;
248
249   ent->nis = ent->first = ent->netgroup = 0;
250
251   /* If something was left over free it.  */
252   if (ent->netgroup)
253     __internal_endnetgrent (&ent->netgrdata);
254
255   if (ent->oldkey != NULL)
256     {
257       free (ent->oldkey);
258       ent->oldkey = NULL;
259       ent->oldkeylen = 0;
260     }
261
262   if (ent->result != NULL)
263     {
264       nis_freeresult (ent->result);
265       ent->result = NULL;
266     }
267
268   if (pwdtable == NULL)
269     {
270       static const char key[] = "passwd.org_dir.";
271       const char *local_dir = nis_local_directory ();
272       size_t len_local_dir = strlen (local_dir);
273
274       pwdtable = malloc (sizeof (key) + len_local_dir);
275       if (pwdtable == NULL)
276         return NSS_STATUS_TRYAGAIN;
277
278       pwdtablelen = ((char *) mempcpy (mempcpy (pwdtable,
279                                                 key, sizeof (key) - 1),
280                                        local_dir, len_local_dir + 1)
281                      - pwdtable) - 1;
282     }
283
284   if (ent->blacklist.data != NULL)
285     {
286       ent->blacklist.current = 1;
287       ent->blacklist.data[0] = '|';
288       ent->blacklist.data[1] = '\0';
289     }
290   else
291     ent->blacklist.current = 0;
292
293   if (ent->stream == NULL)
294     {
295       ent->stream = fopen ("/etc/passwd", "r");
296
297       if (ent->stream == NULL)
298         status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
299       else
300         {
301           /* We have to make sure the file is  `closed on exec'.  */
302           int result, flags;
303
304           result = flags = fcntl (fileno (ent->stream), F_GETFD, 0);
305           if (result >= 0)
306             {
307               flags |= FD_CLOEXEC;
308               result = fcntl (fileno (ent->stream), F_SETFD, flags);
309             }
310           if (result < 0)
311             {
312               /* Something went wrong.  Close the stream and return a
313                  failure.  */
314               fclose (ent->stream);
315               ent->stream = NULL;
316               status = NSS_STATUS_UNAVAIL;
317             }
318         }
319     }
320   else
321     rewind (ent->stream);
322
323   give_pwd_free (&ent->pwd);
324
325   return status;
326 }
327
328
329 enum nss_status
330 _nss_compat_setpwent (void)
331 {
332   enum nss_status result;
333
334   __libc_lock_lock (lock);
335
336   if (ni == NULL)
337     {
338       __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
339       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
340     }
341
342   result = internal_setpwent (&ext_ent);
343
344   __libc_lock_unlock (lock);
345
346   return result;
347 }
348
349
350 static enum nss_status
351 internal_endpwent (ent_t *ent)
352 {
353   if (ent->stream != NULL)
354     {
355       fclose (ent->stream);
356       ent->stream = NULL;
357     }
358
359   if (ent->netgroup)
360     __internal_endnetgrent (&ent->netgrdata);
361
362   ent->nis = ent->first = ent->netgroup = 0;
363
364   if (ent->oldkey != NULL)
365     {
366       free (ent->oldkey);
367       ent->oldkey = NULL;
368       ent->oldkeylen = 0;
369     }
370
371   if (ent->result != NULL)
372     {
373       nis_freeresult (ent->result);
374       ent->result = NULL;
375     }
376
377   if (ent->blacklist.data != NULL)
378     {
379       ent->blacklist.current = 1;
380       ent->blacklist.data[0] = '|';
381      ent->blacklist.data[1] = '\0';
382     }
383   else
384     ent->blacklist.current = 0;
385
386   give_pwd_free (&ent->pwd);
387
388   return NSS_STATUS_SUCCESS;
389 }
390
391 enum nss_status
392 _nss_compat_endpwent (void)
393 {
394   enum nss_status result;
395
396   __libc_lock_lock (lock);
397
398   result = internal_endpwent (&ext_ent);
399
400   __libc_lock_unlock (lock);
401
402   return result;
403 }
404
405 static enum nss_status
406 getpwent_next_nis_netgr (const char *name, struct passwd *result, ent_t *ent,
407                          char *group, char *buffer, size_t buflen, int *errnop)
408 {
409   struct parser_data *data = (void *) buffer;
410   char *ypdomain, *host, *user, *domain, *outval, *p, *p2;
411   int status, outvallen;
412   size_t p2len;
413
414   if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
415     {
416       ent->netgroup = 0;
417       ent->first = 0;
418       give_pwd_free (&ent->pwd);
419       return NSS_STATUS_UNAVAIL;
420     }
421
422   if (ent->first == TRUE)
423     {
424       memset (&ent->netgrdata, 0, sizeof (struct __netgrent));
425       __internal_setnetgrent (group, &ent->netgrdata);
426       ent->first = FALSE;
427     }
428
429   while (1)
430     {
431       char *saved_cursor;
432       int parse_res;
433
434       saved_cursor = ent->netgrdata.cursor;
435       status = __internal_getnetgrent_r (&host, &user, &domain,
436                                          &ent->netgrdata, buffer, buflen,
437                                          errnop);
438       if (status != 1)
439         {
440           __internal_endnetgrent (&ent->netgrdata);
441           ent->netgroup = 0;
442           give_pwd_free (&ent->pwd);
443           return NSS_STATUS_RETURN;
444         }
445
446       if (user == NULL || user[0] == '-')
447         continue;
448
449       if (domain != NULL && strcmp (ypdomain, domain) != 0)
450         continue;
451
452       /* If name != NULL, we are called from getpwnam.  */
453       if (name != NULL)
454         if (strcmp (user, name) != 0)
455           continue;
456
457       if (yp_match (ypdomain, "passwd.byname", user,
458                     strlen (user), &outval, &outvallen)
459           != YPERR_SUCCESS)
460         continue;
461
462       if (insert_passwd_adjunct (&outval, &outvallen, ypdomain, errnop)
463           != NSS_STATUS_SUCCESS)
464         {
465           free (outval);
466           return NSS_STATUS_TRYAGAIN;
467         }
468
469       p2len = pwd_need_buflen (&ent->pwd);
470       if (p2len > buflen)
471         {
472           free (outval);
473           *errnop = ERANGE;
474           return NSS_STATUS_TRYAGAIN;
475         }
476       p2 = buffer + (buflen - p2len);
477       buflen -= p2len;
478
479       if (buflen < ((size_t) outvallen + 1))
480         {
481           free (outval);
482           *errnop = ERANGE;
483           return NSS_STATUS_TRYAGAIN;
484         }
485       p = strncpy (buffer, outval, buflen);
486
487       while (isspace (*p))
488         p++;
489       free (outval);
490       parse_res = _nss_files_parse_pwent (p, result, data, buflen, errnop);
491       if (parse_res == -1)
492         {
493           ent->netgrdata.cursor = saved_cursor;
494           return NSS_STATUS_TRYAGAIN;
495         }
496
497       if (parse_res && !in_blacklist (result->pw_name,
498                                       strlen (result->pw_name), ent))
499         {
500           /* Store the User in the blacklist for the "+" at the end of
501              /etc/passwd */
502           blacklist_store_name (result->pw_name, ent);
503           copy_pwd_changes (result, &ent->pwd, p2, p2len);
504           break;
505         }
506     }
507
508   return NSS_STATUS_SUCCESS;
509 }
510
511 static enum nss_status
512 getpwent_next_nisplus_netgr (const char *name, struct passwd *result,
513                              ent_t *ent, char *group, char *buffer,
514                              size_t buflen, int *errnop)
515 {
516   char *ypdomain, *host, *user, *domain, *p2;
517   int status, parse_res;
518   size_t p2len;
519   nis_result *nisres;
520
521   /* Maybe we should use domainname here ? We need the current
522      domainname for the domain field in netgroups */
523   if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
524     {
525       ent->netgroup = 0;
526       ent->first = 0;
527       give_pwd_free (&ent->pwd);
528       return NSS_STATUS_UNAVAIL;
529     }
530
531   if (ent->first == TRUE)
532     {
533       bzero (&ent->netgrdata, sizeof (struct __netgrent));
534       __internal_setnetgrent (group, &ent->netgrdata);
535       ent->first = FALSE;
536     }
537
538   while (1)
539     {
540       char *saved_cursor;
541
542       saved_cursor = ent->netgrdata.cursor;
543       status = __internal_getnetgrent_r (&host, &user, &domain,
544                                          &ent->netgrdata, buffer, buflen,
545                                          errnop);
546       if (status != 1)
547         {
548           __internal_endnetgrent (&ent->netgrdata);
549           ent->netgroup = 0;
550           give_pwd_free (&ent->pwd);
551           return NSS_STATUS_RETURN;
552         }
553
554       if (user == NULL || user[0] == '-')
555         continue;
556
557       if (domain != NULL && strcmp (ypdomain, domain) != 0)
558         continue;
559
560       /* If name != NULL, we are called from getpwnam */
561       if (name != NULL)
562         if (strcmp (user, name) != 0)
563           continue;
564
565       p2len = pwd_need_buflen (&ent->pwd);
566       if (p2len > buflen)
567         {
568           *errnop = ERANGE;
569           return NSS_STATUS_TRYAGAIN;
570         }
571       p2 = buffer + (buflen - p2len);
572       buflen -= p2len;
573       {
574         char buf[strlen (user) + 30 + pwdtablelen];
575         sprintf(buf, "[name=%s],%s", user, pwdtable);
576         nisres = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
577       }
578       if (niserr2nss (nisres->status) != NSS_STATUS_SUCCESS)
579         {
580           nis_freeresult (nisres);
581           continue;
582         }
583       parse_res = _nss_nisplus_parse_pwent (nisres, result, buffer,
584                                             buflen, errnop);
585       if (parse_res == -1)
586         {
587           nis_freeresult (nisres);
588           ent->netgrdata.cursor = saved_cursor;
589           *errnop = ERANGE;
590           return NSS_STATUS_TRYAGAIN;
591         }
592       nis_freeresult (nisres);
593
594       if (parse_res && !in_blacklist (result->pw_name,
595                                       strlen (result->pw_name), ent))
596         {
597           /* Store the User in the blacklist for the "+" at the end of
598              /etc/passwd */
599           blacklist_store_name (result->pw_name, ent);
600           copy_pwd_changes (result, &ent->pwd, p2, p2len);
601           break;
602         }
603     }
604
605   return NSS_STATUS_SUCCESS;
606 }
607
608 /* get the next user from NIS+  (+ entry) */
609 static enum nss_status
610 getpwent_next_nisplus (struct passwd *result, ent_t *ent, char *buffer,
611                        size_t buflen, int *errnop)
612 {
613   int parse_res;
614   size_t p2len;
615   char *p2;
616
617   p2len = pwd_need_buflen (&ent->pwd);
618   if (p2len > buflen)
619     {
620       *errnop = ERANGE;
621       return NSS_STATUS_TRYAGAIN;
622     }
623   p2 = buffer + (buflen - p2len);
624   buflen -= p2len;
625   do
626     {
627       bool_t saved_first;
628       nis_result *saved_res;
629
630       if (ent->first)
631         {
632           saved_first = TRUE;
633           saved_res = ent->result;
634
635           ent->result = nis_first_entry (pwdtable);
636           if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
637             {
638               ent->nis = 0;
639               give_pwd_free (&ent->pwd);
640               return niserr2nss (ent->result->status);
641             }
642           ent->first = FALSE;
643         }
644       else
645         {
646           nis_result *res;
647
648           res = nis_next_entry (pwdtable, &ent->result->cookie);
649           saved_res = ent->result;
650           saved_first = FALSE;
651           ent->result = res;
652           if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
653             {
654               ent->nis = 0;
655               nis_freeresult (saved_res);
656               give_pwd_free (&ent->pwd);
657               return niserr2nss (ent->result->status);
658             }
659         }
660       parse_res = _nss_nisplus_parse_pwent (ent->result, result, buffer,
661                                             buflen, errnop);
662       if (parse_res == -1)
663         {
664           nis_freeresult (ent->result);
665           ent->result = saved_res;
666           ent->first = saved_first;
667           *errnop = ERANGE;
668           return NSS_STATUS_TRYAGAIN;
669         }
670       else
671         {
672           if (!saved_first)
673             nis_freeresult (saved_res);
674         }
675
676       if (parse_res &&
677           in_blacklist (result->pw_name, strlen (result->pw_name), ent))
678         parse_res = 0; /* if result->pw_name in blacklist,search next entry */
679     }
680   while (!parse_res);
681
682   copy_pwd_changes (result, &ent->pwd, p2, p2len);
683
684   return NSS_STATUS_SUCCESS;
685 }
686
687 static enum nss_status
688 getpwent_next_nis (struct passwd *result, ent_t *ent, char *buffer,
689                    size_t buflen, int *errnop)
690 {
691   struct parser_data *data = (void *) buffer;
692   char *domain, *outkey, *outval, *p, *p2;
693   int outkeylen, outvallen, parse_res;
694   size_t p2len;
695
696   if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
697     {
698       ent->nis = 0;
699       give_pwd_free (&ent->pwd);
700       return NSS_STATUS_UNAVAIL;
701     }
702
703   p2len = pwd_need_buflen (&ent->pwd);
704   if (p2len > buflen)
705     {
706       *errnop = ERANGE;
707       return NSS_STATUS_TRYAGAIN;
708     }
709   p2 = buffer + (buflen - p2len);
710   buflen -= p2len;
711   do
712     {
713       bool_t saved_first;
714       char *saved_oldkey;
715       int saved_oldlen;
716
717       if (ent->first)
718         {
719           if (yp_first (domain, "passwd.byname", &outkey, &outkeylen,
720                         &outval, &outvallen) != YPERR_SUCCESS)
721             {
722               ent->nis = 0;
723               give_pwd_free (&ent->pwd);
724               return NSS_STATUS_UNAVAIL;
725             }
726
727           if (insert_passwd_adjunct (&outval, &outvallen, domain, errnop) !=
728               NSS_STATUS_SUCCESS)
729             {
730               free (outval);
731               return NSS_STATUS_TRYAGAIN;
732             }
733
734           if (buflen < ((size_t) outvallen + 1))
735             {
736               free (outval);
737               *errnop = ERANGE;
738               return NSS_STATUS_TRYAGAIN;
739             }
740
741           saved_first = TRUE;
742           saved_oldkey = ent->oldkey;
743           saved_oldlen = ent->oldkeylen;
744           ent->oldkey = outkey;
745           ent->oldkeylen = outkeylen;
746           ent->first = FALSE;
747         }
748       else
749         {
750           if (yp_next (domain, "passwd.byname", ent->oldkey, ent->oldkeylen,
751                        &outkey, &outkeylen, &outval, &outvallen)
752               != YPERR_SUCCESS)
753             {
754               ent->nis = 0;
755               give_pwd_free (&ent->pwd);
756               *errnop = ENOENT;
757               return NSS_STATUS_NOTFOUND;
758             }
759
760           if (insert_passwd_adjunct (&outval, &outvallen, domain, errnop)
761               != NSS_STATUS_SUCCESS)
762             {
763               free (outval);
764               return NSS_STATUS_TRYAGAIN;
765             }
766
767           if (buflen < ((size_t) outvallen + 1))
768             {
769               free (outval);
770               *errnop = ERANGE;
771               return NSS_STATUS_TRYAGAIN;
772             }
773
774           saved_first = FALSE;
775           saved_oldkey = ent->oldkey;
776           saved_oldlen = ent->oldkeylen;
777           ent->oldkey = outkey;
778           ent->oldkeylen = outkeylen;
779         }
780
781       /* Copy the found data to our buffer  */
782       p = strncpy (buffer, outval, buflen);
783
784       /* ...and free the data.  */
785       free (outval);
786
787       while (isspace (*p))
788         ++p;
789       parse_res = _nss_files_parse_pwent (p, result, data, buflen, errnop);
790       if (parse_res == -1)
791         {
792           free (ent->oldkey);
793           ent->oldkey = saved_oldkey;
794           ent->oldkeylen = saved_oldlen;
795           ent->first = saved_first;
796           *errnop = ERANGE;
797           return NSS_STATUS_TRYAGAIN;
798         }
799       else
800         {
801           if (!saved_first)
802             free (saved_oldkey);
803         }
804       if (parse_res
805           && in_blacklist (result->pw_name, strlen (result->pw_name), ent))
806         parse_res = 0;
807     }
808   while (!parse_res);
809
810   copy_pwd_changes (result, &ent->pwd, p2, p2len);
811
812   return NSS_STATUS_SUCCESS;
813 }
814
815 /* This function handle the +user entrys in /etc/passwd */
816 static enum nss_status
817 getpwnam_plususer (const char *name, struct passwd *result, ent_t *ent,
818                    char *buffer, size_t buflen, int *errnop)
819 {
820   struct parser_data *data = (void *) buffer;
821   struct passwd pwd;
822   int parse_res;
823   char *p;
824   size_t plen;
825
826   memset (&pwd, '\0', sizeof (struct passwd));
827
828   copy_pwd_changes (&pwd, result, NULL, 0);
829
830   plen = pwd_need_buflen (&pwd);
831   if (plen > buflen)
832     {
833       *errnop = ERANGE;
834       return NSS_STATUS_TRYAGAIN;
835     }
836   p = buffer + (buflen - plen);
837   buflen -= plen;
838
839   if (use_nisplus) /* Do the NIS+ query here */
840     {
841       nis_result *res;
842       char buf[strlen (name) + 24 + pwdtablelen];
843
844       sprintf(buf, "[name=%s],%s", name, pwdtable);
845       res = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
846       if (niserr2nss (res->status) != NSS_STATUS_SUCCESS)
847         {
848           enum nss_status status =  niserr2nss (res->status);
849
850           nis_freeresult (res);
851           return status;
852         }
853       parse_res = _nss_nisplus_parse_pwent (res, result, buffer,
854                                             buflen, errnop);
855
856       nis_freeresult (res);
857
858       if (parse_res == -1)
859         {
860           *errnop = ERANGE;
861           return NSS_STATUS_TRYAGAIN;
862         }
863
864       if (in_blacklist (result->pw_name, strlen (result->pw_name), ent))
865         {
866           *errnop = ENOENT;
867           return NSS_STATUS_NOTFOUND;
868         }
869     }
870   else /* Use NIS */
871     {
872       char *domain, *outval, *ptr;
873       int outvallen;
874
875       if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
876         return NSS_STATUS_NOTFOUND;
877
878       if (yp_match (domain, "passwd.byname", name, strlen (name),
879                     &outval, &outvallen) != YPERR_SUCCESS)
880         {
881           *errnop = ENOENT;
882           return NSS_STATUS_NOTFOUND;
883         }
884
885       if (insert_passwd_adjunct (&outval, &outvallen, domain, errnop)
886           != NSS_STATUS_SUCCESS)
887         {
888           free (outval);
889           return NSS_STATUS_TRYAGAIN;
890         }
891
892       if (buflen < ((size_t) outvallen + 1))
893         {
894           free (outval);
895           *errnop = ERANGE;
896           return NSS_STATUS_TRYAGAIN;
897         }
898
899       ptr = strncpy (buffer, outval, buflen);
900       free (outval);
901
902       while (isspace (*ptr))
903         ptr++;
904
905       parse_res = _nss_files_parse_pwent (ptr, result, data, buflen, errnop);
906       if (parse_res == -1)
907         return NSS_STATUS_TRYAGAIN;
908
909       if (in_blacklist (result->pw_name, strlen (result->pw_name), ent))
910         {
911           *errnop = ENOENT;
912           return NSS_STATUS_NOTFOUND;
913         }
914     }
915
916   if (parse_res > 0)
917     {
918       copy_pwd_changes (result, &pwd, p, plen);
919       give_pwd_free (&pwd);
920       /* We found the entry.  */
921       return NSS_STATUS_SUCCESS;
922     }
923   else
924     {
925       /* Give buffer the old len back */
926       buflen += plen;
927       give_pwd_free (&pwd);
928     }
929   return NSS_STATUS_RETURN;
930 }
931
932 static enum nss_status
933 getpwent_next_file (struct passwd *result, ent_t *ent,
934                     char *buffer, size_t buflen, int *errnop)
935 {
936   struct parser_data *data = (void *) buffer;
937   while (1)
938     {
939       fpos_t pos;
940       char *p;
941       int parse_res;
942
943       do
944         {
945           fgetpos (ent->stream, &pos);
946           buffer[buflen - 1] = '\xff';
947           p = fgets (buffer, buflen, ent->stream);
948           if (p == NULL && feof (ent->stream))
949             {
950               *errnop = ENOENT;
951               return NSS_STATUS_NOTFOUND;
952             }
953           if (p == NULL || buffer[buflen - 1] != '\xff')
954             {
955               fsetpos (ent->stream, &pos);
956               *errnop = ERANGE;
957               return NSS_STATUS_TRYAGAIN;
958             }
959
960           /* Terminate the line for any case.  */
961           buffer[buflen - 1] = '\0';
962
963           /* Skip leading blanks.  */
964           while (isspace (*p))
965             ++p;
966         }
967       while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines.  */
968       /* Parse the line.  If it is invalid, loop to
969          get the next line of the file to parse.  */
970              !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
971                                                    errnop)));
972
973       if (parse_res == -1)
974         {
975           /* The parser ran out of space.  */
976           fsetpos (ent->stream, &pos);
977           *errnop = ERANGE;
978           return NSS_STATUS_TRYAGAIN;
979         }
980
981       if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
982         /* This is a real entry.  */
983         break;
984
985       /* -@netgroup */
986       if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
987           && result->pw_name[2] != '\0')
988         {
989           /* XXX Do not use fixed length buffer.  */
990           char buf2[1024];
991           char *user, *host, *domain;
992           struct __netgrent netgrdata;
993
994           bzero (&netgrdata, sizeof (struct __netgrent));
995           __internal_setnetgrent (&result->pw_name[2], &netgrdata);
996           while (__internal_getnetgrent_r (&host, &user, &domain, &netgrdata,
997                                            buf2, sizeof (buf2), errnop))
998             {
999               if (user != NULL && user[0] != '-')
1000                 blacklist_store_name (user, ent);
1001             }
1002           __internal_endnetgrent (&netgrdata);
1003           continue;
1004         }
1005
1006       /* +@netgroup */
1007       if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
1008           && result->pw_name[2] != '\0')
1009         {
1010           enum nss_status status;
1011
1012           ent->netgroup = TRUE;
1013           ent->first = TRUE;
1014           copy_pwd_changes (&ent->pwd, result, NULL, 0);
1015
1016           if (use_nisplus)
1017             status =  getpwent_next_nisplus_netgr (NULL, result, ent,
1018                                                    &result->pw_name[2],
1019                                                    buffer, buflen, errnop);
1020           else
1021             status =  getpwent_next_nis_netgr (NULL, result, ent,
1022                                                &result->pw_name[2],
1023                                                buffer, buflen, errnop);
1024           if (status == NSS_STATUS_RETURN)
1025             continue;
1026           else
1027             {
1028               if (status == NSS_STATUS_NOTFOUND)
1029                 *errnop = ENOENT;
1030               return status;
1031             }
1032         }
1033
1034       /* -user */
1035       if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
1036           && result->pw_name[1] != '@')
1037         {
1038           blacklist_store_name (&result->pw_name[1], ent);
1039           continue;
1040         }
1041
1042       /* +user */
1043       if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
1044           && result->pw_name[1] != '@')
1045         {
1046           char buf[strlen (result->pw_name)];
1047           enum nss_status status;
1048
1049           /* Store the User in the blacklist for the "+" at the end of
1050              /etc/passwd */
1051           strcpy (buf, &result->pw_name[1]);
1052           status = getpwnam_plususer (&result->pw_name[1], result, ent,
1053                                       buffer, buflen, errnop);
1054           blacklist_store_name (buf, ent);
1055
1056           if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
1057             break;
1058           else
1059             if (status == NSS_STATUS_RETURN /* We couldn't parse the entry */
1060                 || status == NSS_STATUS_NOTFOUND) /* entry doesn't exist */
1061               continue;
1062             else
1063               {
1064                 if (status == NSS_STATUS_TRYAGAIN)
1065                   {
1066                     /* The parser ran out of space */
1067                     fsetpos (ent->stream, &pos);
1068                     *errnop = ERANGE;
1069                   }
1070                 return status;
1071               }
1072         }
1073
1074       /* +:... */
1075       if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
1076         {
1077           ent->nis = TRUE;
1078           ent->first = TRUE;
1079           copy_pwd_changes (&ent->pwd, result, NULL, 0);
1080
1081           if (use_nisplus)
1082             return getpwent_next_nisplus (result, ent, buffer, buflen, errnop);
1083           else
1084             return getpwent_next_nis (result, ent, buffer, buflen, errnop);
1085         }
1086     }
1087
1088   return NSS_STATUS_SUCCESS;
1089 }
1090
1091
1092 static enum nss_status
1093 internal_getpwent_r (struct passwd *pw, ent_t *ent, char *buffer,
1094                      size_t buflen, int *errnop)
1095 {
1096   if (ent->netgroup)
1097     {
1098       enum nss_status status;
1099
1100       /* We are searching members in a netgroup */
1101       /* Since this is not the first call, we don't need the group name */
1102       if (use_nisplus)
1103         status = getpwent_next_nisplus_netgr (NULL, pw, ent, NULL, buffer,
1104                                               buflen, errnop);
1105       else
1106         status = getpwent_next_nis_netgr (NULL, pw, ent, NULL, buffer, buflen,
1107                                           errnop);
1108       if (status == NSS_STATUS_RETURN)
1109         return getpwent_next_file (pw, ent, buffer, buflen, errnop);
1110       else
1111         return status;
1112     }
1113   else
1114     if (ent->nis)
1115       {
1116         if (use_nisplus)
1117           return getpwent_next_nisplus (pw, ent, buffer, buflen, errnop);
1118         else
1119           return getpwent_next_nis (pw, ent, buffer, buflen, errnop);
1120       }
1121     else
1122       return getpwent_next_file (pw, ent, buffer, buflen, errnop);
1123 }
1124
1125 enum nss_status
1126 _nss_compat_getpwent_r (struct passwd *pwd, char *buffer, size_t buflen,
1127                         int *errnop)
1128 {
1129   enum nss_status status = NSS_STATUS_SUCCESS;
1130
1131   __libc_lock_lock (lock);
1132
1133   if (ni == NULL)
1134     {
1135       __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
1136       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
1137     }
1138
1139   /* Be prepared that the setpwent function was not called before.  */
1140   if (ext_ent.stream == NULL)
1141     status = internal_setpwent (&ext_ent);
1142
1143   if (status == NSS_STATUS_SUCCESS)
1144     status = internal_getpwent_r (pwd, &ext_ent, buffer, buflen, errnop);
1145
1146   __libc_lock_unlock (lock);
1147
1148   return status;
1149 }
1150
1151 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user */
1152 static enum nss_status
1153 internal_getpwnam_r (const char *name, struct passwd *result, ent_t *ent,
1154                      char *buffer, size_t buflen, int *errnop)
1155 {
1156   struct parser_data *data = (void *) buffer;
1157
1158   while (1)
1159     {
1160       fpos_t pos;
1161       char *p;
1162       int parse_res;
1163
1164       do
1165         {
1166           fgetpos (ent->stream, &pos);
1167           buffer[buflen - 1] = '\xff';
1168           p = fgets (buffer, buflen, ent->stream);
1169           if (p == NULL && feof (ent->stream))
1170             {
1171               *errnop = ENOENT;
1172               return NSS_STATUS_NOTFOUND;
1173             }
1174           if (p == NULL || buffer[buflen - 1] != '\xff')
1175             {
1176               fsetpos (ent->stream, &pos);
1177               *errnop = ERANGE;
1178               return NSS_STATUS_TRYAGAIN;
1179             }
1180
1181           /* Terminate the line for any case.  */
1182           buffer[buflen - 1] = '\0';
1183
1184           /* Skip leading blanks.  */
1185           while (isspace (*p))
1186             ++p;
1187         }
1188       while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines.  */
1189              /* Parse the line.  If it is invalid, loop to
1190                 get the next line of the file to parse.  */
1191              !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
1192                                                    errnop)));
1193
1194       if (parse_res == -1)
1195         {
1196           /* The parser ran out of space.  */
1197           fsetpos (ent->stream, &pos);
1198           *errnop = ERANGE;
1199           return NSS_STATUS_TRYAGAIN;
1200         }
1201
1202       /* This is a real entry.  */
1203       if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
1204         {
1205           if (strcmp (result->pw_name, name) == 0)
1206             return NSS_STATUS_SUCCESS;
1207           else
1208             continue;
1209         }
1210
1211       /* -@netgroup */
1212       if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
1213           && result->pw_name[2] != '\0')
1214         {
1215           if (innetgr (&result->pw_name[2], NULL, name, NULL))
1216             return NSS_STATUS_NOTFOUND;
1217           continue;
1218         }
1219
1220       /* +@netgroup */
1221       if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
1222           && result->pw_name[2] != '\0')
1223         {
1224           enum nss_status status;
1225
1226           if (innetgr (&result->pw_name[2], NULL, name, NULL))
1227             {
1228               status = getpwnam_plususer (name, result, ent, buffer,
1229                                           buflen, errnop);
1230
1231               if (status == NSS_STATUS_RETURN)
1232                 continue;
1233
1234               return status;
1235             }
1236           continue;
1237         }
1238
1239       /* -user */
1240       if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
1241           && result->pw_name[1] != '@')
1242         {
1243           if (strcmp (&result->pw_name[1], name) == 0)
1244             {
1245               *errnop = ENOENT;
1246               return NSS_STATUS_NOTFOUND;
1247             }
1248           else
1249             continue;
1250         }
1251
1252       /* +user */
1253       if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
1254           && result->pw_name[1] != '@')
1255         {
1256           if (strcmp (name, &result->pw_name[1]) == 0)
1257             {
1258               enum nss_status status;
1259
1260               status = getpwnam_plususer (name, result, ent, buffer, buflen,
1261                                           errnop);
1262               if (status == NSS_STATUS_RETURN)
1263                 /* We couldn't parse the entry */
1264                 return NSS_STATUS_NOTFOUND;
1265               else
1266                 return status;
1267             }
1268         }
1269
1270       /* +:... */
1271       if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
1272         {
1273           enum nss_status status;
1274
1275           status = getpwnam_plususer (name, result, ent,
1276                                       buffer, buflen, errnop);
1277           if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
1278             break;
1279           else
1280             if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1281               return NSS_STATUS_NOTFOUND;
1282             else
1283               return status;
1284         }
1285     }
1286   return NSS_STATUS_SUCCESS;
1287 }
1288
1289 enum nss_status
1290 _nss_compat_getpwnam_r (const char *name, struct passwd *pwd,
1291                         char *buffer, size_t buflen, int *errnop)
1292 {
1293   ent_t ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
1294                {NULL, NULL, 0, 0, NULL, NULL, NULL}};
1295   enum nss_status status;
1296
1297   if (name[0] == '-' || name[0] == '+')
1298     {
1299       *errnop = ENOENT;
1300       return NSS_STATUS_NOTFOUND;
1301     }
1302
1303   __libc_lock_lock (lock);
1304
1305   if (ni == NULL)
1306     {
1307       __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
1308       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
1309     }
1310
1311   __libc_lock_unlock (lock);
1312
1313   status = internal_setpwent (&ent);
1314   if (status != NSS_STATUS_SUCCESS)
1315     return status;
1316
1317   status = internal_getpwnam_r (name, pwd, &ent, buffer, buflen, errnop);
1318
1319   internal_endpwent (&ent);
1320
1321   return status;
1322 }
1323
1324 /* This function handle the + entry in /etc/passwd for getpwuid */
1325 static enum nss_status
1326 getpwuid_plususer (uid_t uid, struct passwd *result, char *buffer,
1327                    size_t buflen, int *errnop)
1328 {
1329   struct parser_data *data = (void *) buffer;
1330   struct passwd pwd;
1331   int parse_res;
1332   char *p;
1333   size_t plen;
1334
1335   memset (&pwd, '\0', sizeof (struct passwd));
1336
1337   copy_pwd_changes (&pwd, result, NULL, 0);
1338
1339   plen = pwd_need_buflen (&pwd);
1340   if (plen > buflen)
1341     {
1342       *errnop = ERANGE;
1343       return NSS_STATUS_TRYAGAIN;
1344     }
1345   p = buffer + (buflen - plen);
1346   buflen -= plen;
1347
1348   if (use_nisplus) /* Do the NIS+ query here */
1349     {
1350       nis_result *res;
1351       char buf[1024 + pwdtablelen];
1352
1353       snprintf(buf, sizeof (buf), "[uid=%d],%s", uid, pwdtable);
1354       res = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
1355       if (niserr2nss (res->status) != NSS_STATUS_SUCCESS)
1356         {
1357           enum nss_status status =  niserr2nss (res->status);
1358
1359           nis_freeresult (res);
1360           return status;
1361         }
1362       if ((parse_res = _nss_nisplus_parse_pwent (res, result, buffer,
1363                                                  buflen, errnop)) == -1)
1364         {
1365           nis_freeresult (res);
1366           *errnop = ERANGE;
1367           return NSS_STATUS_TRYAGAIN;
1368         }
1369       nis_freeresult (res);
1370     }
1371   else /* Use NIS */
1372     {
1373       char buf[1024];
1374       char *domain, *outval, *ptr;
1375       int outvallen;
1376
1377       if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
1378         {
1379           *errnop = errno;
1380           return NSS_STATUS_TRYAGAIN;
1381         }
1382
1383       sprintf (buf, "%d", uid);
1384       if (yp_match (domain, "passwd.byuid", buf, strlen (buf),
1385                     &outval, &outvallen)
1386           != YPERR_SUCCESS)
1387         {
1388           *errnop = errno;
1389           return NSS_STATUS_TRYAGAIN;
1390         }
1391
1392       if (insert_passwd_adjunct (&outval, &outvallen, domain, errnop)
1393           != NSS_STATUS_SUCCESS)
1394         {
1395           free (outval);
1396           return NSS_STATUS_TRYAGAIN;
1397         }
1398
1399       if (buflen < ((size_t) outvallen + 1))
1400         {
1401           free (outval);
1402           *errnop = ERANGE;
1403           return NSS_STATUS_TRYAGAIN;
1404         }
1405
1406       ptr = strncpy (buffer, outval, buflen);
1407       free (outval);
1408
1409       while (isspace (*ptr))
1410         ptr++;
1411       parse_res = _nss_files_parse_pwent (ptr, result, data, buflen, errnop);
1412       if (parse_res == -1)
1413         return NSS_STATUS_TRYAGAIN;
1414     }
1415
1416   if (parse_res > 0)
1417     {
1418       copy_pwd_changes (result, &pwd, p, plen);
1419       give_pwd_free (&pwd);
1420       /* We found the entry.  */
1421       return NSS_STATUS_SUCCESS;
1422     }
1423   else
1424     {
1425       /* Give buffer the old len back */
1426       buflen += plen;
1427       give_pwd_free (&pwd);
1428     }
1429   return NSS_STATUS_RETURN;
1430 }
1431
1432 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user id */
1433 static enum nss_status
1434 internal_getpwuid_r (uid_t uid, struct passwd *result, ent_t *ent,
1435                      char *buffer, size_t buflen, int *errnop)
1436 {
1437   struct parser_data *data = (void *) buffer;
1438
1439   while (1)
1440     {
1441       fpos_t pos;
1442       char *p;
1443       int parse_res;
1444
1445       do
1446         {
1447           fgetpos (ent->stream, &pos);
1448           buffer[buflen - 1] = '\xff';
1449           p = fgets (buffer, buflen, ent->stream);
1450           if (p == NULL && feof (ent->stream))
1451             return NSS_STATUS_NOTFOUND;
1452           if (p == NULL || buffer[buflen - 1] != '\xff')
1453             {
1454               fsetpos (ent->stream, &pos);
1455               *errnop = ERANGE;
1456               return NSS_STATUS_TRYAGAIN;
1457             }
1458
1459           /* Terminate the line for any case.  */
1460           buffer[buflen - 1] = '\0';
1461
1462           /* Skip leading blanks.  */
1463           while (isspace (*p))
1464             ++p;
1465         }
1466       while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines.  */
1467              /* Parse the line.  If it is invalid, loop to
1468                 get the next line of the file to parse.  */
1469              !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
1470                                                    errnop)));
1471
1472       if (parse_res == -1)
1473         {
1474           /* The parser ran out of space.  */
1475           fsetpos (ent->stream, &pos);
1476           *errnop = ERANGE;
1477           return NSS_STATUS_TRYAGAIN;
1478         }
1479
1480       /* This is a real entry.  */
1481       if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
1482         {
1483           if (result->pw_uid == uid)
1484             return NSS_STATUS_SUCCESS;
1485           else
1486             continue;
1487         }
1488
1489       /* -@netgroup */
1490       if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
1491           && result->pw_name[2] != '\0')
1492         {
1493           char buf[strlen (result->pw_name)];
1494           enum nss_status status;
1495
1496           strcpy (buf, &result->pw_name[2]);
1497
1498           status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
1499           if (status == NSS_STATUS_SUCCESS &&
1500               innetgr (buf, NULL, result->pw_name, NULL))
1501             return NSS_STATUS_NOTFOUND;
1502           continue;
1503         }
1504
1505       /* +@netgroup */
1506       if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
1507           && result->pw_name[2] != '\0')
1508         {
1509           char buf[strlen (result->pw_name)];
1510           enum nss_status status;
1511
1512           strcpy (buf, &result->pw_name[2]);
1513
1514           status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
1515
1516           if (status == NSS_STATUS_RETURN)
1517             continue;
1518
1519           if (status == NSS_STATUS_SUCCESS)
1520             {
1521               if (innetgr (buf, NULL, result->pw_name, NULL))
1522                 return NSS_STATUS_SUCCESS;
1523             }
1524           else
1525             if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1526               return NSS_STATUS_NOTFOUND;
1527             else
1528               return status;
1529
1530           continue;
1531         }
1532
1533       /* -user */
1534       if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
1535           && result->pw_name[1] != '@')
1536         {
1537           char buf[strlen (result->pw_name)];
1538           enum nss_status status;
1539
1540           strcpy (buf, &result->pw_name[1]);
1541
1542           status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
1543           if (status == NSS_STATUS_SUCCESS &&
1544               innetgr (buf, NULL, result->pw_name, NULL))
1545             return NSS_STATUS_NOTFOUND;
1546           continue;
1547         }
1548
1549       /* +user */
1550       if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
1551           && result->pw_name[1] != '@')
1552         {
1553           char buf[strlen (result->pw_name)];
1554           enum nss_status status;
1555
1556           strcpy (buf, &result->pw_name[1]);
1557
1558           status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
1559
1560           if (status == NSS_STATUS_RETURN)
1561             continue;
1562
1563           if (status == NSS_STATUS_SUCCESS)
1564             {
1565               if (strcmp (buf, result->pw_name) == 0)
1566                 return NSS_STATUS_SUCCESS;
1567             }
1568           else
1569             if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1570               return NSS_STATUS_NOTFOUND;
1571             else
1572               return status;
1573
1574           continue;
1575         }
1576
1577       /* +:... */
1578       if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
1579         {
1580           enum nss_status status;
1581
1582           status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
1583           if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
1584             break;
1585           else
1586             if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1587               return NSS_STATUS_NOTFOUND;
1588             else
1589               return status;
1590         }
1591     }
1592   return NSS_STATUS_SUCCESS;
1593 }
1594
1595 enum nss_status
1596 _nss_compat_getpwuid_r (uid_t uid, struct passwd *pwd,
1597                         char *buffer, size_t buflen, int *errnop)
1598 {
1599   ent_t ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
1600                {NULL, NULL, 0, 0, NULL, NULL, NULL}};
1601   enum nss_status status;
1602
1603   __libc_lock_lock (lock);
1604
1605   if (ni == NULL)
1606     {
1607       __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
1608       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
1609     }
1610
1611   __libc_lock_unlock (lock);
1612
1613   status = internal_setpwent (&ent);
1614   if (status != NSS_STATUS_SUCCESS)
1615     return status;
1616
1617   status = internal_getpwuid_r (uid, pwd, &ent, buffer, buflen, errnop);
1618
1619   internal_endpwent (&ent);
1620
1621   return status;
1622 }
1623
1624
1625 /* Support routines for remembering -@netgroup and -user entries.
1626    The names are stored in a single string with `|' as separator. */
1627 static void
1628 blacklist_store_name (const char *name, ent_t *ent)
1629 {
1630   int namelen = strlen (name);
1631   char *tmp;
1632
1633   /* first call, setup cache */
1634   if (ent->blacklist.size == 0)
1635     {
1636       ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
1637       ent->blacklist.data = malloc (ent->blacklist.size);
1638       if (ent->blacklist.data == NULL)
1639         return;
1640       ent->blacklist.data[0] = '|';
1641       ent->blacklist.data[1] = '\0';
1642       ent->blacklist.current = 1;
1643     }
1644   else
1645     {
1646       if (in_blacklist (name, namelen, ent))
1647         return;                 /* no duplicates */
1648
1649       if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
1650         {
1651           ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
1652           tmp = realloc (ent->blacklist.data, ent->blacklist.size);
1653           if (tmp == NULL)
1654             {
1655               free (ent->blacklist.data);
1656               ent->blacklist.size = 0;
1657               return;
1658             }
1659           ent->blacklist.data = tmp;
1660         }
1661     }
1662
1663   tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
1664   *tmp++ = '|';
1665   *tmp = '\0';
1666   ent->blacklist.current += namelen + 1;
1667
1668   return;
1669 }
1670
1671 /* returns TRUE if ent->blacklist contains name, else FALSE */
1672 static bool_t
1673 in_blacklist (const char *name, int namelen, ent_t *ent)
1674 {
1675   char buf[namelen + 3];
1676   char *cp;
1677
1678   if (ent->blacklist.data == NULL)
1679     return FALSE;
1680
1681   buf[0] = '|';
1682   cp = stpcpy (&buf[1], name);
1683   *cp++= '|';
1684   *cp = '\0';
1685   return strstr (ent->blacklist.data, buf) != NULL;
1686 }