(internal_getpwuid_r): Always set errno to ENOENT when returning
[kopensolaris-gnu/glibc.git] / nis / nss_compat / compat-pwd.c
1 /* Copyright (C) 1996, 1997, 1998, 1999 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         {
877           *errnop = ENOENT;
878           return NSS_STATUS_NOTFOUND;
879         }
880
881       if (yp_match (domain, "passwd.byname", name, strlen (name),
882                     &outval, &outvallen) != YPERR_SUCCESS)
883         {
884           *errnop = ENOENT;
885           return NSS_STATUS_NOTFOUND;
886         }
887
888       if (insert_passwd_adjunct (&outval, &outvallen, domain, errnop)
889           != NSS_STATUS_SUCCESS)
890         {
891           free (outval);
892           return NSS_STATUS_TRYAGAIN;
893         }
894
895       if (buflen < ((size_t) outvallen + 1))
896         {
897           free (outval);
898           *errnop = ERANGE;
899           return NSS_STATUS_TRYAGAIN;
900         }
901
902       ptr = strncpy (buffer, outval, buflen);
903       free (outval);
904
905       while (isspace (*ptr))
906         ptr++;
907
908       parse_res = _nss_files_parse_pwent (ptr, result, data, buflen, errnop);
909       if (parse_res == -1)
910         return NSS_STATUS_TRYAGAIN;
911
912       if (in_blacklist (result->pw_name, strlen (result->pw_name), ent))
913         {
914           *errnop = ENOENT;
915           return NSS_STATUS_NOTFOUND;
916         }
917     }
918
919   if (parse_res > 0)
920     {
921       copy_pwd_changes (result, &pwd, p, plen);
922       give_pwd_free (&pwd);
923       /* We found the entry.  */
924       return NSS_STATUS_SUCCESS;
925     }
926   else
927     {
928       /* Give buffer the old len back */
929       buflen += plen;
930       give_pwd_free (&pwd);
931     }
932   return NSS_STATUS_RETURN;
933 }
934
935 static enum nss_status
936 getpwent_next_file (struct passwd *result, ent_t *ent,
937                     char *buffer, size_t buflen, int *errnop)
938 {
939   struct parser_data *data = (void *) buffer;
940   while (1)
941     {
942       fpos_t pos;
943       char *p;
944       int parse_res;
945
946       do
947         {
948           fgetpos (ent->stream, &pos);
949           buffer[buflen - 1] = '\xff';
950           p = fgets (buffer, buflen, ent->stream);
951           if (p == NULL && feof (ent->stream))
952             {
953               *errnop = ENOENT;
954               return NSS_STATUS_NOTFOUND;
955             }
956           if (p == NULL || buffer[buflen - 1] != '\xff')
957             {
958               fsetpos (ent->stream, &pos);
959               *errnop = ERANGE;
960               return NSS_STATUS_TRYAGAIN;
961             }
962
963           /* Terminate the line for any case.  */
964           buffer[buflen - 1] = '\0';
965
966           /* Skip leading blanks.  */
967           while (isspace (*p))
968             ++p;
969         }
970       while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines.  */
971       /* Parse the line.  If it is invalid, loop to
972          get the next line of the file to parse.  */
973              !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
974                                                    errnop)));
975
976       if (parse_res == -1)
977         {
978           /* The parser ran out of space.  */
979           fsetpos (ent->stream, &pos);
980           *errnop = ERANGE;
981           return NSS_STATUS_TRYAGAIN;
982         }
983
984       if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
985         /* This is a real entry.  */
986         break;
987
988       /* -@netgroup */
989       if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
990           && result->pw_name[2] != '\0')
991         {
992           /* XXX Do not use fixed length buffer.  */
993           char buf2[1024];
994           char *user, *host, *domain;
995           struct __netgrent netgrdata;
996
997           bzero (&netgrdata, sizeof (struct __netgrent));
998           __internal_setnetgrent (&result->pw_name[2], &netgrdata);
999           while (__internal_getnetgrent_r (&host, &user, &domain, &netgrdata,
1000                                            buf2, sizeof (buf2), errnop))
1001             {
1002               if (user != NULL && user[0] != '-')
1003                 blacklist_store_name (user, ent);
1004             }
1005           __internal_endnetgrent (&netgrdata);
1006           continue;
1007         }
1008
1009       /* +@netgroup */
1010       if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
1011           && result->pw_name[2] != '\0')
1012         {
1013           enum nss_status status;
1014
1015           ent->netgroup = TRUE;
1016           ent->first = TRUE;
1017           copy_pwd_changes (&ent->pwd, result, NULL, 0);
1018
1019           if (use_nisplus)
1020             status =  getpwent_next_nisplus_netgr (NULL, result, ent,
1021                                                    &result->pw_name[2],
1022                                                    buffer, buflen, errnop);
1023           else
1024             status =  getpwent_next_nis_netgr (NULL, result, ent,
1025                                                &result->pw_name[2],
1026                                                buffer, buflen, errnop);
1027           if (status == NSS_STATUS_RETURN)
1028             continue;
1029           else
1030             {
1031               if (status == NSS_STATUS_NOTFOUND)
1032                 *errnop = ENOENT;
1033               return status;
1034             }
1035         }
1036
1037       /* -user */
1038       if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
1039           && result->pw_name[1] != '@')
1040         {
1041           blacklist_store_name (&result->pw_name[1], ent);
1042           continue;
1043         }
1044
1045       /* +user */
1046       if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
1047           && result->pw_name[1] != '@')
1048         {
1049           char buf[strlen (result->pw_name)];
1050           enum nss_status status;
1051
1052           /* Store the User in the blacklist for the "+" at the end of
1053              /etc/passwd */
1054           strcpy (buf, &result->pw_name[1]);
1055           status = getpwnam_plususer (&result->pw_name[1], result, ent,
1056                                       buffer, buflen, errnop);
1057           blacklist_store_name (buf, ent);
1058
1059           if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
1060             break;
1061           else
1062             if (status == NSS_STATUS_RETURN /* We couldn't parse the entry */
1063                 || status == NSS_STATUS_NOTFOUND) /* entry doesn't exist */
1064               continue;
1065             else
1066               {
1067                 if (status == NSS_STATUS_TRYAGAIN)
1068                   {
1069                     /* The parser ran out of space */
1070                     fsetpos (ent->stream, &pos);
1071                     *errnop = ERANGE;
1072                   }
1073                 return status;
1074               }
1075         }
1076
1077       /* +:... */
1078       if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
1079         {
1080           ent->nis = TRUE;
1081           ent->first = TRUE;
1082           copy_pwd_changes (&ent->pwd, result, NULL, 0);
1083
1084           if (use_nisplus)
1085             return getpwent_next_nisplus (result, ent, buffer, buflen, errnop);
1086           else
1087             return getpwent_next_nis (result, ent, buffer, buflen, errnop);
1088         }
1089     }
1090
1091   return NSS_STATUS_SUCCESS;
1092 }
1093
1094
1095 static enum nss_status
1096 internal_getpwent_r (struct passwd *pw, ent_t *ent, char *buffer,
1097                      size_t buflen, int *errnop)
1098 {
1099   if (ent->netgroup)
1100     {
1101       enum nss_status status;
1102
1103       /* We are searching members in a netgroup */
1104       /* Since this is not the first call, we don't need the group name */
1105       if (use_nisplus)
1106         status = getpwent_next_nisplus_netgr (NULL, pw, ent, NULL, buffer,
1107                                               buflen, errnop);
1108       else
1109         status = getpwent_next_nis_netgr (NULL, pw, ent, NULL, buffer, buflen,
1110                                           errnop);
1111       if (status == NSS_STATUS_RETURN)
1112         return getpwent_next_file (pw, ent, buffer, buflen, errnop);
1113       else
1114         return status;
1115     }
1116   else
1117     if (ent->nis)
1118       {
1119         if (use_nisplus)
1120           return getpwent_next_nisplus (pw, ent, buffer, buflen, errnop);
1121         else
1122           return getpwent_next_nis (pw, ent, buffer, buflen, errnop);
1123       }
1124     else
1125       return getpwent_next_file (pw, ent, buffer, buflen, errnop);
1126 }
1127
1128 enum nss_status
1129 _nss_compat_getpwent_r (struct passwd *pwd, char *buffer, size_t buflen,
1130                         int *errnop)
1131 {
1132   enum nss_status status = NSS_STATUS_SUCCESS;
1133
1134   __libc_lock_lock (lock);
1135
1136   if (ni == NULL)
1137     {
1138       __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
1139       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
1140     }
1141
1142   /* Be prepared that the setpwent function was not called before.  */
1143   if (ext_ent.stream == NULL)
1144     status = internal_setpwent (&ext_ent);
1145
1146   if (status == NSS_STATUS_SUCCESS)
1147     status = internal_getpwent_r (pwd, &ext_ent, buffer, buflen, errnop);
1148
1149   __libc_lock_unlock (lock);
1150
1151   return status;
1152 }
1153
1154 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user */
1155 static enum nss_status
1156 internal_getpwnam_r (const char *name, struct passwd *result, ent_t *ent,
1157                      char *buffer, size_t buflen, int *errnop)
1158 {
1159   struct parser_data *data = (void *) buffer;
1160
1161   while (1)
1162     {
1163       fpos_t pos;
1164       char *p;
1165       int parse_res;
1166
1167       do
1168         {
1169           fgetpos (ent->stream, &pos);
1170           buffer[buflen - 1] = '\xff';
1171           p = fgets (buffer, buflen, ent->stream);
1172           if (p == NULL && feof (ent->stream))
1173             {
1174               *errnop = ENOENT;
1175               return NSS_STATUS_NOTFOUND;
1176             }
1177           if (p == NULL || buffer[buflen - 1] != '\xff')
1178             {
1179               fsetpos (ent->stream, &pos);
1180               *errnop = ERANGE;
1181               return NSS_STATUS_TRYAGAIN;
1182             }
1183
1184           /* Terminate the line for any case.  */
1185           buffer[buflen - 1] = '\0';
1186
1187           /* Skip leading blanks.  */
1188           while (isspace (*p))
1189             ++p;
1190         }
1191       while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines.  */
1192              /* Parse the line.  If it is invalid, loop to
1193                 get the next line of the file to parse.  */
1194              !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
1195                                                    errnop)));
1196
1197       if (parse_res == -1)
1198         {
1199           /* The parser ran out of space.  */
1200           fsetpos (ent->stream, &pos);
1201           *errnop = ERANGE;
1202           return NSS_STATUS_TRYAGAIN;
1203         }
1204
1205       /* This is a real entry.  */
1206       if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
1207         {
1208           if (strcmp (result->pw_name, name) == 0)
1209             return NSS_STATUS_SUCCESS;
1210           else
1211             continue;
1212         }
1213
1214       /* -@netgroup */
1215       if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
1216           && result->pw_name[2] != '\0')
1217         {
1218           if (innetgr (&result->pw_name[2], NULL, name, NULL))
1219             return NSS_STATUS_NOTFOUND;
1220           continue;
1221         }
1222
1223       /* +@netgroup */
1224       if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
1225           && result->pw_name[2] != '\0')
1226         {
1227           enum nss_status status;
1228
1229           if (innetgr (&result->pw_name[2], NULL, name, NULL))
1230             {
1231               status = getpwnam_plususer (name, result, ent, buffer,
1232                                           buflen, errnop);
1233
1234               if (status == NSS_STATUS_RETURN)
1235                 continue;
1236
1237               return status;
1238             }
1239           continue;
1240         }
1241
1242       /* -user */
1243       if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
1244           && result->pw_name[1] != '@')
1245         {
1246           if (strcmp (&result->pw_name[1], name) == 0)
1247             {
1248               *errnop = ENOENT;
1249               return NSS_STATUS_NOTFOUND;
1250             }
1251           else
1252             continue;
1253         }
1254
1255       /* +user */
1256       if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
1257           && result->pw_name[1] != '@')
1258         {
1259           if (strcmp (name, &result->pw_name[1]) == 0)
1260             {
1261               enum nss_status status;
1262
1263               status = getpwnam_plususer (name, result, ent, buffer, buflen,
1264                                           errnop);
1265               if (status == NSS_STATUS_RETURN)
1266                 /* We couldn't parse the entry */
1267                 return NSS_STATUS_NOTFOUND;
1268               else
1269                 return status;
1270             }
1271         }
1272
1273       /* +:... */
1274       if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
1275         {
1276           enum nss_status status;
1277
1278           status = getpwnam_plususer (name, result, ent,
1279                                       buffer, buflen, errnop);
1280           if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
1281             break;
1282           else
1283             if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1284               return NSS_STATUS_NOTFOUND;
1285             else
1286               return status;
1287         }
1288     }
1289   return NSS_STATUS_SUCCESS;
1290 }
1291
1292 enum nss_status
1293 _nss_compat_getpwnam_r (const char *name, struct passwd *pwd,
1294                         char *buffer, size_t buflen, int *errnop)
1295 {
1296   ent_t ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
1297                {NULL, NULL, 0, 0, NULL, NULL, NULL}};
1298   enum nss_status status;
1299
1300   if (name[0] == '-' || name[0] == '+')
1301     {
1302       *errnop = ENOENT;
1303       return NSS_STATUS_NOTFOUND;
1304     }
1305
1306   __libc_lock_lock (lock);
1307
1308   if (ni == NULL)
1309     {
1310       __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
1311       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
1312     }
1313
1314   __libc_lock_unlock (lock);
1315
1316   status = internal_setpwent (&ent);
1317   if (status != NSS_STATUS_SUCCESS)
1318     return status;
1319
1320   status = internal_getpwnam_r (name, pwd, &ent, buffer, buflen, errnop);
1321
1322   internal_endpwent (&ent);
1323
1324   return status;
1325 }
1326
1327 /* This function handle the + entry in /etc/passwd for getpwuid */
1328 static enum nss_status
1329 getpwuid_plususer (uid_t uid, struct passwd *result, char *buffer,
1330                    size_t buflen, int *errnop)
1331 {
1332   struct parser_data *data = (void *) buffer;
1333   struct passwd pwd;
1334   int parse_res;
1335   char *p;
1336   size_t plen;
1337
1338   memset (&pwd, '\0', sizeof (struct passwd));
1339
1340   copy_pwd_changes (&pwd, result, NULL, 0);
1341
1342   plen = pwd_need_buflen (&pwd);
1343   if (plen > buflen)
1344     {
1345       *errnop = ERANGE;
1346       return NSS_STATUS_TRYAGAIN;
1347     }
1348   p = buffer + (buflen - plen);
1349   buflen -= plen;
1350
1351   if (use_nisplus) /* Do the NIS+ query here */
1352     {
1353       nis_result *res;
1354       char buf[1024 + pwdtablelen];
1355
1356       snprintf(buf, sizeof (buf), "[uid=%d],%s", uid, pwdtable);
1357       res = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
1358       if (niserr2nss (res->status) != NSS_STATUS_SUCCESS)
1359         {
1360           enum nss_status status =  niserr2nss (res->status);
1361
1362           nis_freeresult (res);
1363           return status;
1364         }
1365       if ((parse_res = _nss_nisplus_parse_pwent (res, result, buffer,
1366                                                  buflen, errnop)) == -1)
1367         {
1368           nis_freeresult (res);
1369           *errnop = ERANGE;
1370           return NSS_STATUS_TRYAGAIN;
1371         }
1372       nis_freeresult (res);
1373     }
1374   else /* Use NIS */
1375     {
1376       char buf[1024];
1377       char *domain, *outval, *ptr;
1378       int outvallen;
1379
1380       if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
1381         {
1382           *errnop = ENOENT;
1383           return NSS_STATUS_NOTFOUND;
1384         }
1385
1386       sprintf (buf, "%d", uid);
1387       if (yp_match (domain, "passwd.byuid", buf, strlen (buf),
1388                     &outval, &outvallen)
1389           != YPERR_SUCCESS)
1390         {
1391           *errnop = ENOENT;
1392           return NSS_STATUS_NOTFOUND;
1393         }
1394
1395       if (insert_passwd_adjunct (&outval, &outvallen, domain, errnop)
1396           != NSS_STATUS_SUCCESS)
1397         {
1398           free (outval);
1399           return NSS_STATUS_TRYAGAIN;
1400         }
1401
1402       if (buflen < ((size_t) outvallen + 1))
1403         {
1404           free (outval);
1405           *errnop = ERANGE;
1406           return NSS_STATUS_TRYAGAIN;
1407         }
1408
1409       ptr = strncpy (buffer, outval, buflen);
1410       free (outval);
1411
1412       while (isspace (*ptr))
1413         ptr++;
1414       parse_res = _nss_files_parse_pwent (ptr, result, data, buflen, errnop);
1415       if (parse_res == -1)
1416         return NSS_STATUS_TRYAGAIN;
1417     }
1418
1419   if (parse_res > 0)
1420     {
1421       copy_pwd_changes (result, &pwd, p, plen);
1422       give_pwd_free (&pwd);
1423       /* We found the entry.  */
1424       return NSS_STATUS_SUCCESS;
1425     }
1426   else
1427     {
1428       /* Give buffer the old len back */
1429       buflen += plen;
1430       give_pwd_free (&pwd);
1431     }
1432   return NSS_STATUS_RETURN;
1433 }
1434
1435 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user id */
1436 static enum nss_status
1437 internal_getpwuid_r (uid_t uid, struct passwd *result, ent_t *ent,
1438                      char *buffer, size_t buflen, int *errnop)
1439 {
1440   struct parser_data *data = (void *) buffer;
1441
1442   while (1)
1443     {
1444       fpos_t pos;
1445       char *p;
1446       int parse_res;
1447
1448       do
1449         {
1450           fgetpos (ent->stream, &pos);
1451           buffer[buflen - 1] = '\xff';
1452           p = fgets (buffer, buflen, ent->stream);
1453           if (p == NULL && feof (ent->stream))
1454             {
1455               *errnop = ENOENT;
1456               return NSS_STATUS_NOTFOUND;
1457             }
1458           if (p == NULL || buffer[buflen - 1] != '\xff')
1459             {
1460               fsetpos (ent->stream, &pos);
1461               *errnop = ERANGE;
1462               return NSS_STATUS_TRYAGAIN;
1463             }
1464
1465           /* Terminate the line for any case.  */
1466           buffer[buflen - 1] = '\0';
1467
1468           /* Skip leading blanks.  */
1469           while (isspace (*p))
1470             ++p;
1471         }
1472       while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines.  */
1473              /* Parse the line.  If it is invalid, loop to
1474                 get the next line of the file to parse.  */
1475              !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
1476                                                    errnop)));
1477
1478       if (parse_res == -1)
1479         {
1480           /* The parser ran out of space.  */
1481           fsetpos (ent->stream, &pos);
1482           *errnop = ERANGE;
1483           return NSS_STATUS_TRYAGAIN;
1484         }
1485
1486       /* This is a real entry.  */
1487       if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
1488         {
1489           if (result->pw_uid == uid)
1490             return NSS_STATUS_SUCCESS;
1491           else
1492             continue;
1493         }
1494
1495       /* -@netgroup */
1496       if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
1497           && result->pw_name[2] != '\0')
1498         {
1499           char buf[strlen (result->pw_name)];
1500           enum nss_status status;
1501
1502           strcpy (buf, &result->pw_name[2]);
1503
1504           status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
1505           if (status == NSS_STATUS_SUCCESS &&
1506               innetgr (buf, NULL, result->pw_name, NULL))
1507             {
1508               *errnop = ENOENT;
1509               return NSS_STATUS_NOTFOUND;
1510             }
1511           continue;
1512         }
1513
1514       /* +@netgroup */
1515       if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
1516           && result->pw_name[2] != '\0')
1517         {
1518           char buf[strlen (result->pw_name)];
1519           enum nss_status status;
1520
1521           strcpy (buf, &result->pw_name[2]);
1522
1523           status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
1524
1525           if (status == NSS_STATUS_RETURN)
1526             continue;
1527
1528           if (status == NSS_STATUS_SUCCESS)
1529             {
1530               if (innetgr (buf, NULL, result->pw_name, NULL))
1531                 return NSS_STATUS_SUCCESS;
1532             }
1533           else
1534             if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1535               {
1536                 *errnop = ENOENT;
1537                 return NSS_STATUS_NOTFOUND;
1538               }
1539             else
1540               return status;
1541
1542           continue;
1543         }
1544
1545       /* -user */
1546       if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
1547           && result->pw_name[1] != '@')
1548         {
1549           char buf[strlen (result->pw_name)];
1550           enum nss_status status;
1551
1552           strcpy (buf, &result->pw_name[1]);
1553
1554           status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
1555           if (status == NSS_STATUS_SUCCESS &&
1556               innetgr (buf, NULL, result->pw_name, NULL))
1557             {
1558               *errnop = ENOENT;
1559               return NSS_STATUS_NOTFOUND;
1560             }
1561           continue;
1562         }
1563
1564       /* +user */
1565       if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
1566           && result->pw_name[1] != '@')
1567         {
1568           char buf[strlen (result->pw_name)];
1569           enum nss_status status;
1570
1571           strcpy (buf, &result->pw_name[1]);
1572
1573           status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
1574
1575           if (status == NSS_STATUS_RETURN)
1576             continue;
1577
1578           if (status == NSS_STATUS_SUCCESS)
1579             {
1580               if (strcmp (buf, result->pw_name) == 0)
1581                 return NSS_STATUS_SUCCESS;
1582             }
1583           else
1584             if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1585               {
1586                 *errnop = ENOENT;
1587                 return NSS_STATUS_NOTFOUND;
1588               }
1589             else
1590               return status;
1591
1592           continue;
1593         }
1594
1595       /* +:... */
1596       if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
1597         {
1598           enum nss_status status;
1599
1600           status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
1601           if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
1602             break;
1603           else
1604             if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1605               {
1606                 *errnop = ENOENT;
1607                 return NSS_STATUS_NOTFOUND;
1608               }
1609             else
1610               return status;
1611         }
1612     }
1613   return NSS_STATUS_SUCCESS;
1614 }
1615
1616 enum nss_status
1617 _nss_compat_getpwuid_r (uid_t uid, struct passwd *pwd,
1618                         char *buffer, size_t buflen, int *errnop)
1619 {
1620   ent_t ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
1621                {NULL, NULL, 0, 0, NULL, NULL, NULL}};
1622   enum nss_status status;
1623
1624   __libc_lock_lock (lock);
1625
1626   if (ni == NULL)
1627     {
1628       __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
1629       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
1630     }
1631
1632   __libc_lock_unlock (lock);
1633
1634   status = internal_setpwent (&ent);
1635   if (status != NSS_STATUS_SUCCESS)
1636     return status;
1637
1638   status = internal_getpwuid_r (uid, pwd, &ent, buffer, buflen, errnop);
1639
1640   internal_endpwent (&ent);
1641
1642   return status;
1643 }
1644
1645
1646 /* Support routines for remembering -@netgroup and -user entries.
1647    The names are stored in a single string with `|' as separator. */
1648 static void
1649 blacklist_store_name (const char *name, ent_t *ent)
1650 {
1651   int namelen = strlen (name);
1652   char *tmp;
1653
1654   /* first call, setup cache */
1655   if (ent->blacklist.size == 0)
1656     {
1657       ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
1658       ent->blacklist.data = malloc (ent->blacklist.size);
1659       if (ent->blacklist.data == NULL)
1660         return;
1661       ent->blacklist.data[0] = '|';
1662       ent->blacklist.data[1] = '\0';
1663       ent->blacklist.current = 1;
1664     }
1665   else
1666     {
1667       if (in_blacklist (name, namelen, ent))
1668         return;                 /* no duplicates */
1669
1670       if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
1671         {
1672           ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
1673           tmp = realloc (ent->blacklist.data, ent->blacklist.size);
1674           if (tmp == NULL)
1675             {
1676               free (ent->blacklist.data);
1677               ent->blacklist.size = 0;
1678               return;
1679             }
1680           ent->blacklist.data = tmp;
1681         }
1682     }
1683
1684   tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
1685   *tmp++ = '|';
1686   *tmp = '\0';
1687   ent->blacklist.current += namelen + 1;
1688
1689   return;
1690 }
1691
1692 /* returns TRUE if ent->blacklist contains name, else FALSE */
1693 static bool_t
1694 in_blacklist (const char *name, int namelen, ent_t *ent)
1695 {
1696   char buf[namelen + 3];
1697   char *cp;
1698
1699   if (ent->blacklist.data == NULL)
1700     return FALSE;
1701
1702   buf[0] = '|';
1703   cp = stpcpy (&buf[1], name);
1704   *cp++= '|';
1705   *cp = '\0';
1706   return strstr (ent->blacklist.data, buf) != NULL;
1707 }