Remove buggy assert call.
[kopensolaris-gnu/glibc.git] / nis / nss_compat / compat-pwd.c
1 /* Copyright (C) 1996, 1997 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 internal_setpwent (ent_t *ent)
190 {
191   enum nss_status status = NSS_STATUS_SUCCESS;
192
193   ent->nis = ent->first = ent->netgroup = 0;
194
195   /* If something was left over free it.  */
196   if (ent->netgroup)
197     __internal_endnetgrent (&ent->netgrdata);
198
199   if (ent->oldkey != NULL)
200     {
201       free (ent->oldkey);
202       ent->oldkey = NULL;
203       ent->oldkeylen = 0;
204     }
205
206   if (ent->result != NULL)
207     {
208       nis_freeresult (ent->result);
209       ent->result = NULL;
210     }
211
212   if (pwdtable == NULL)
213     {
214       static const char key[] = "passwd.org_dir.";
215       const char *local_dir = nis_local_directory ();
216       size_t len_local_dir = strlen (local_dir);
217
218       pwdtable = malloc (sizeof (key) + len_local_dir);
219       if (pwdtable == NULL)
220         return NSS_STATUS_TRYAGAIN;
221
222       pwdtablelen = ((char *) mempcpy (mempcpy (pwdtable,
223                                                 key, sizeof (key) - 1),
224                                        local_dir, len_local_dir + 1)
225                      - pwdtable) - 1;
226     }
227
228   ent->blacklist.current = 0;
229   if (ent->blacklist.data != NULL)
230     ent->blacklist.data[0] = '\0';
231
232   if (ent->stream == NULL)
233     {
234       ent->stream = fopen ("/etc/passwd", "r");
235
236       if (ent->stream == NULL)
237         status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
238       else
239         {
240           /* We have to make sure the file is  `closed on exec'.  */
241           int result, flags;
242
243           result = flags = fcntl (fileno (ent->stream), F_GETFD, 0);
244           if (result >= 0)
245             {
246               flags |= FD_CLOEXEC;
247               result = fcntl (fileno (ent->stream), F_SETFD, flags);
248             }
249           if (result < 0)
250             {
251               /* Something went wrong.  Close the stream and return a
252                  failure.  */
253               fclose (ent->stream);
254               ent->stream = NULL;
255               status = NSS_STATUS_UNAVAIL;
256             }
257         }
258     }
259   else
260     rewind (ent->stream);
261
262   give_pwd_free (&ent->pwd);
263
264   return status;
265 }
266
267
268 enum nss_status
269 _nss_compat_setpwent (void)
270 {
271   enum nss_status result;
272
273   __libc_lock_lock (lock);
274
275   if (ni == NULL)
276     {
277       __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
278       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
279     }
280
281   result = internal_setpwent (&ext_ent);
282
283   __libc_lock_unlock (lock);
284
285   return result;
286 }
287
288
289 static enum nss_status
290 internal_endpwent (ent_t *ent)
291 {
292   if (ent->stream != NULL)
293     {
294       fclose (ent->stream);
295       ent->stream = NULL;
296     }
297
298   if (ent->netgroup)
299     __internal_endnetgrent (&ent->netgrdata);
300
301   ent->nis = ent->first = ent->netgroup = 0;
302
303   if (ent->oldkey != NULL)
304     {
305       free (ent->oldkey);
306       ent->oldkey = NULL;
307       ent->oldkeylen = 0;
308     }
309
310   if (ent->result != NULL)
311     {
312       nis_freeresult (ent->result);
313       ent->result = NULL;
314     }
315
316   ent->blacklist.current = 0;
317   if (ent->blacklist.data != NULL)
318     ent->blacklist.data[0] = '\0';
319
320   give_pwd_free (&ent->pwd);
321
322   return NSS_STATUS_SUCCESS;
323 }
324
325 enum nss_status
326 _nss_compat_endpwent (void)
327 {
328   enum nss_status result;
329
330   __libc_lock_lock (lock);
331
332   result = internal_endpwent (&ext_ent);
333
334   __libc_lock_unlock (lock);
335
336   return result;
337 }
338
339 static enum nss_status
340 getpwent_next_nis_netgr (const char *name, struct passwd *result, ent_t *ent,
341                          char *group, char *buffer, size_t buflen)
342 {
343   struct parser_data *data = (void *) buffer;
344   char *ypdomain, *host, *user, *domain, *outval, *p, *p2;
345   int status, outvallen;
346   size_t p2len;
347
348   if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
349     {
350       ent->netgroup = 0;
351       ent->first = 0;
352       give_pwd_free (&ent->pwd);
353       return NSS_STATUS_UNAVAIL;
354     }
355
356   if (ent->first == TRUE)
357     {
358       memset (&ent->netgrdata, 0, sizeof (struct __netgrent));
359       __internal_setnetgrent (group, &ent->netgrdata);
360       ent->first = FALSE;
361     }
362
363   while (1)
364     {
365       char *saved_cursor;
366       int parse_res;
367
368       saved_cursor = ent->netgrdata.cursor;
369       status = __internal_getnetgrent_r (&host, &user, &domain,
370                                          &ent->netgrdata, buffer, buflen);
371       if (status != 1)
372         {
373           __internal_endnetgrent (&ent->netgrdata);
374           ent->netgroup = 0;
375           give_pwd_free (&ent->pwd);
376           return NSS_STATUS_RETURN;
377         }
378
379       if (user == NULL || user[0] == '-')
380         continue;
381
382       if (domain != NULL && strcmp (ypdomain, domain) != 0)
383         continue;
384
385       /* If name != NULL, we are called from getpwnam */
386       if (name != NULL)
387         if (strcmp (user, name) != 0)
388           continue;
389
390       if (yp_match (ypdomain, "passwd.byname", user,
391                     strlen (user), &outval, &outvallen)
392           != YPERR_SUCCESS)
393         continue;
394
395       p2len = pwd_need_buflen (&ent->pwd);
396       if (p2len > buflen)
397         {
398           __set_errno (ERANGE);
399           return NSS_STATUS_TRYAGAIN;
400         }
401       p2 = buffer + (buflen - p2len);
402       buflen -= p2len;
403       p = strncpy (buffer, outval, buflen);
404       while (isspace (*p))
405         p++;
406       free (outval);
407       if ((parse_res = _nss_files_parse_pwent (p, result, data, buflen)) == -1)
408         {
409           ent->netgrdata.cursor = saved_cursor;
410           return NSS_STATUS_TRYAGAIN;
411         }
412
413       if (parse_res)
414         {
415           /* Store the User in the blacklist for the "+" at the end of
416              /etc/passwd */
417           blacklist_store_name (result->pw_name, ent);
418           copy_pwd_changes (result, &ent->pwd, p2, p2len);
419           break;
420         }
421     }
422
423   return NSS_STATUS_SUCCESS;
424 }
425
426 static enum nss_status
427 getpwent_next_nisplus_netgr (const char *name, struct passwd *result,
428                              ent_t *ent, char *group, char *buffer,
429                              size_t buflen)
430 {
431   char *ypdomain, *host, *user, *domain, *p2;
432   int status, parse_res;
433   size_t p2len;
434   nis_result *nisres;
435
436   /* Maybe we should use domainname here ? We need the current
437      domainname for the domain field in netgroups */
438   if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
439     {
440       ent->netgroup = 0;
441       ent->first = 0;
442       give_pwd_free (&ent->pwd);
443       return NSS_STATUS_UNAVAIL;
444     }
445
446   if (ent->first == TRUE)
447     {
448       bzero (&ent->netgrdata, sizeof (struct __netgrent));
449       __internal_setnetgrent (group, &ent->netgrdata);
450       ent->first = FALSE;
451     }
452
453   while (1)
454     {
455       char *saved_cursor;
456
457       saved_cursor = ent->netgrdata.cursor;
458       status = __internal_getnetgrent_r (&host, &user, &domain,
459                                          &ent->netgrdata, buffer, buflen);
460       if (status != 1)
461         {
462           __internal_endnetgrent (&ent->netgrdata);
463           ent->netgroup = 0;
464           give_pwd_free (&ent->pwd);
465           return NSS_STATUS_RETURN;
466         }
467
468       if (user == NULL || user[0] == '-')
469         continue;
470
471       if (domain != NULL && strcmp (ypdomain, domain) != 0)
472         continue;
473
474       /* If name != NULL, we are called from getpwnam */
475       if (name != NULL)
476         if (strcmp (user, name) != 0)
477           continue;
478
479       p2len = pwd_need_buflen (&ent->pwd);
480       if (p2len > buflen)
481         {
482           __set_errno (ERANGE);
483           return NSS_STATUS_TRYAGAIN;
484         }
485       p2 = buffer + (buflen - p2len);
486       buflen -= p2len;
487       {
488         char buf[strlen (user) + 30 + pwdtablelen];
489         sprintf(buf, "[name=%s],%s", user, pwdtable);
490         nisres = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
491       }
492       if (niserr2nss (nisres->status) != NSS_STATUS_SUCCESS)
493         {
494           nis_freeresult (nisres);
495           continue;
496         }
497       if ((parse_res = _nss_nisplus_parse_pwent (nisres, result, buffer,
498                                                  buflen)) == -1)
499         {
500           nis_freeresult (nisres);
501           ent->netgrdata.cursor = saved_cursor;
502           return NSS_STATUS_TRYAGAIN;
503         }
504       nis_freeresult (nisres);
505
506       if (parse_res)
507         {
508           /* Store the User in the blacklist for the "+" at the end of
509              /etc/passwd */
510           blacklist_store_name (result->pw_name, ent);
511           copy_pwd_changes (result, &ent->pwd, p2, p2len);
512           break;
513         }
514     }
515
516   return NSS_STATUS_SUCCESS;
517 }
518
519 static enum nss_status
520 getpwent_next_nisplus (struct passwd *result, ent_t *ent, char *buffer,
521                        size_t buflen)
522 {
523   int parse_res;
524   size_t p2len;
525   char *p2;
526
527   p2len = pwd_need_buflen (&ent->pwd);
528   if (p2len > buflen)
529     {
530       __set_errno (ERANGE);
531       return NSS_STATUS_TRYAGAIN;
532     }
533   p2 = buffer + (buflen - p2len);
534   buflen -= p2len;
535   do
536     {
537       bool_t saved_first;
538       nis_result *saved_res;
539
540       if (ent->first)
541         {
542           saved_first = TRUE;
543           saved_res = ent->result;
544
545           ent->result = nis_first_entry(pwdtable);
546           if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
547             {
548               ent->nis = 0;
549               give_pwd_free (&ent->pwd);
550               return niserr2nss (ent->result->status);
551             }
552           ent->first = FALSE;
553         }
554       else
555         {
556           nis_result *res;
557
558           res = nis_next_entry(pwdtable, &ent->result->cookie);
559           saved_res = ent->result;
560           saved_first = FALSE;
561           ent->result = res;
562           if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
563             {
564               ent->nis = 0;
565               nis_freeresult (saved_res);
566               give_pwd_free (&ent->pwd);
567               return niserr2nss (ent->result->status);
568             }
569         }
570       if ((parse_res = _nss_nisplus_parse_pwent (ent->result, result, buffer,
571                                                  buflen)) == -1)
572         {
573           nis_freeresult (ent->result);
574           ent->result = saved_res;
575           ent->first = saved_first;
576           __set_errno (ERANGE);
577           return NSS_STATUS_TRYAGAIN;
578         }
579       else
580         {
581           if (!saved_first)
582             nis_freeresult (saved_res);
583         }
584
585       if (parse_res &&
586           in_blacklist (result->pw_name, strlen (result->pw_name), ent))
587         parse_res = 0; /* if result->pw_name in blacklist,search next entry */
588     }
589   while (!parse_res);
590
591   copy_pwd_changes (result, &ent->pwd, p2, p2len);
592
593   return NSS_STATUS_SUCCESS;
594 }
595
596 static enum nss_status
597 getpwent_next_nis (struct passwd *result, ent_t *ent, char *buffer,
598                    size_t buflen)
599 {
600   struct parser_data *data = (void *) buffer;
601   char *domain, *outkey, *outval, *p, *p2;
602   int outkeylen, outvallen, parse_res;
603   size_t p2len;
604
605   if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
606     {
607       ent->nis = 0;
608       give_pwd_free (&ent->pwd);
609       return NSS_STATUS_UNAVAIL;
610     }
611
612   p2len = pwd_need_buflen (&ent->pwd);
613   if (p2len > buflen)
614     {
615       __set_errno (ERANGE);
616       return NSS_STATUS_TRYAGAIN;
617     }
618   p2 = buffer + (buflen - p2len);
619   buflen -= p2len;
620   do
621     {
622       bool_t saved_first;
623       char *saved_oldkey;
624       int saved_oldlen;
625
626       if (ent->first)
627         {
628           if (yp_first (domain, "passwd.byname", &outkey, &outkeylen,
629                         &outval, &outvallen) != YPERR_SUCCESS)
630             {
631               ent->nis = 0;
632               give_pwd_free (&ent->pwd);
633               return NSS_STATUS_UNAVAIL;
634             }
635
636           saved_first = TRUE;
637           saved_oldkey = ent->oldkey;
638           saved_oldlen = ent->oldkeylen;
639           ent->oldkey = outkey;
640           ent->oldkeylen = outkeylen;
641           ent->first = FALSE;
642         }
643       else
644         {
645           if (yp_next (domain, "passwd.byname", ent->oldkey, ent->oldkeylen,
646                        &outkey, &outkeylen, &outval, &outvallen)
647               != YPERR_SUCCESS)
648             {
649               ent->nis = 0;
650               give_pwd_free (&ent->pwd);
651               return NSS_STATUS_NOTFOUND;
652             }
653
654           saved_first = FALSE;
655           saved_oldkey = ent->oldkey;
656           saved_oldlen = ent->oldkeylen;
657           ent->oldkey = outkey;
658           ent->oldkeylen = outkeylen;
659         }
660
661       /* Copy the found data to our buffer  */
662       p = strncpy (buffer, outval, buflen);
663
664       /* ...and free the data.  */
665       free (outval);
666
667       while (isspace (*p))
668         ++p;
669       if ((parse_res = _nss_files_parse_pwent (p, result, data, buflen)) == -1)
670         {
671           free (ent->oldkey);
672           ent->oldkey = saved_oldkey;
673           ent->oldkeylen = saved_oldlen;
674           ent->first = saved_first;
675           __set_errno (ERANGE);
676           return NSS_STATUS_TRYAGAIN;
677         }
678       else
679         {
680           if (!saved_first)
681             free (saved_oldkey);
682         }
683       if (parse_res &&
684           in_blacklist (result->pw_name, strlen (result->pw_name), ent))
685         parse_res = 0;
686     }
687   while (!parse_res);
688
689   copy_pwd_changes (result, &ent->pwd, p2, p2len);
690
691   return NSS_STATUS_SUCCESS;
692 }
693
694 /* This function handle the +user entrys in /etc/passwd */
695 static enum nss_status
696 getpwnam_plususer (const char *name, struct passwd *result, char *buffer,
697                    size_t buflen)
698 {
699   struct parser_data *data = (void *) buffer;
700   struct passwd pwd;
701   int parse_res;
702   char *p;
703   size_t plen;
704
705   memset (&pwd, '\0', sizeof (struct passwd));
706
707   copy_pwd_changes (&pwd, result, NULL, 0);
708
709   plen = pwd_need_buflen (&pwd);
710   if (plen > buflen)
711     {
712       __set_errno (ERANGE);
713       return NSS_STATUS_TRYAGAIN;
714     }
715   p = buffer + (buflen - plen);
716   buflen -= plen;
717
718   if (use_nisplus) /* Do the NIS+ query here */
719     {
720       nis_result *res;
721       char buf[strlen (name) + 24 + pwdtablelen];
722
723       sprintf(buf, "[name=%s],%s", name, pwdtable);
724       res = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
725       if (niserr2nss (res->status) != NSS_STATUS_SUCCESS)
726         {
727           enum nss_status status =  niserr2nss (res->status);
728
729           nis_freeresult (res);
730           return status;
731         }
732       if ((parse_res = _nss_nisplus_parse_pwent (res, result, buffer,
733                                                  buflen)) == -1)
734         {
735           nis_freeresult (res);
736           __set_errno (ERANGE);
737           return NSS_STATUS_TRYAGAIN;
738         }
739       nis_freeresult (res);
740     }
741   else /* Use NIS */
742     {
743       char *domain, *outval, *ptr;
744       int outvallen;
745
746       if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
747         return NSS_STATUS_TRYAGAIN;
748
749       if (yp_match (domain, "passwd.byname", name, strlen (name),
750                     &outval, &outvallen)
751           != YPERR_SUCCESS)
752         return NSS_STATUS_TRYAGAIN;
753       ptr = strncpy (buffer, outval, buflen < (size_t) outvallen ?
754                      buflen : (size_t) outvallen);
755       buffer[buflen < (size_t) outvallen ? buflen : (size_t) outvallen] = '\0';
756       free (outval);
757       while (isspace (*ptr))
758         ptr++;
759       if ((parse_res = _nss_files_parse_pwent (ptr, result, data, buflen))
760           == -1)
761         {
762           __set_errno (ERANGE);
763           return NSS_STATUS_TRYAGAIN;
764         }
765     }
766
767   if (parse_res > 0)
768     {
769       copy_pwd_changes (result, &pwd, p, plen);
770       give_pwd_free (&pwd);
771       /* We found the entry.  */
772       return NSS_STATUS_SUCCESS;
773     }
774   else
775     {
776       /* Give buffer the old len back */
777       buflen += plen;
778       give_pwd_free (&pwd);
779     }
780   return NSS_STATUS_RETURN;
781 }
782
783 static enum nss_status
784 getpwent_next_file (struct passwd *result, ent_t *ent,
785                     char *buffer, size_t buflen)
786 {
787   struct parser_data *data = (void *) buffer;
788   while (1)
789     {
790       fpos_t pos;
791       char *p;
792       int parse_res;
793
794       do
795         {
796           fgetpos (ent->stream, &pos);
797           p = fgets (buffer, buflen, ent->stream);
798           if (p == NULL)
799             return NSS_STATUS_NOTFOUND;
800
801           /* Terminate the line for any case.  */
802           buffer[buflen - 1] = '\0';
803
804           /* Skip leading blanks.  */
805           while (isspace (*p))
806             ++p;
807         }
808       while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines.  */
809       /* Parse the line.  If it is invalid, loop to
810          get the next line of the file to parse.  */
811              !(parse_res = _nss_files_parse_pwent (p, result, data, buflen)));
812
813       if (parse_res == -1)
814         {
815           /* The parser ran out of space.  */
816           fsetpos (ent->stream, &pos);
817           __set_errno (ERANGE);
818           return NSS_STATUS_TRYAGAIN;
819         }
820
821       if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
822         /* This is a real entry.  */
823         break;
824
825       /* -@netgroup */
826       if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
827           && result->pw_name[2] != '\0')
828         {
829           char buf2[1024];
830           char *user, *host, *domain;
831           struct __netgrent netgrdata;
832
833           bzero (&netgrdata, sizeof (struct __netgrent));
834           __internal_setnetgrent (&result->pw_name[2], &netgrdata);
835           while (__internal_getnetgrent_r (&host, &user, &domain,
836                                            &netgrdata, buf2, sizeof (buf2)))
837             {
838               if (user != NULL && user[0] != '-')
839                 blacklist_store_name (user, ent);
840             }
841           __internal_endnetgrent (&netgrdata);
842           continue;
843         }
844
845       /* +@netgroup */
846       if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
847           && result->pw_name[2] != '\0')
848         {
849           int status;
850
851           ent->netgroup = TRUE;
852           ent->first = TRUE;
853           copy_pwd_changes (&ent->pwd, result, NULL, 0);
854
855           if (use_nisplus)
856             status =  getpwent_next_nisplus_netgr (NULL, result, ent,
857                                                    &result->pw_name[2],
858                                                    buffer, buflen);
859           else
860             status =  getpwent_next_nis_netgr (NULL, result, ent,
861                                                &result->pw_name[2],
862                                                buffer, buflen);
863           if (status == NSS_STATUS_RETURN)
864             continue;
865           else
866             return status;
867         }
868
869       /* -user */
870       if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
871           && result->pw_name[1] != '@')
872         {
873           blacklist_store_name (&result->pw_name[1], ent);
874           continue;
875         }
876
877       /* +user */
878       if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
879           && result->pw_name[1] != '@')
880         {
881           enum nss_status status;
882
883           /* Store the User in the blacklist for the "+" at the end of
884              /etc/passwd */
885           blacklist_store_name (&result->pw_name[1], ent);
886           status = getpwnam_plususer (&result->pw_name[1], result, buffer,
887                                       buflen);
888           if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
889             break;
890           else
891             if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
892               continue;
893             else
894               return status;
895         }
896
897       /* +:... */
898       if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
899         {
900           ent->nis = TRUE;
901           ent->first = TRUE;
902           copy_pwd_changes (&ent->pwd, result, NULL, 0);
903
904           if (use_nisplus)
905             return getpwent_next_nisplus (result, ent, buffer, buflen);
906           else
907             return getpwent_next_nis (result, ent, buffer, buflen);
908         }
909     }
910
911   return NSS_STATUS_SUCCESS;
912 }
913
914
915 static enum nss_status
916 internal_getpwent_r (struct passwd *pw, ent_t *ent, char *buffer,
917                      size_t buflen)
918 {
919   if (ent->netgroup)
920     {
921       int status;
922
923       /* We are searching members in a netgroup */
924       /* Since this is not the first call, we don't need the group name */
925       if (use_nisplus)
926         status = getpwent_next_nisplus_netgr (NULL, pw, ent, NULL, buffer,
927                                               buflen);
928       else
929         status = getpwent_next_nis_netgr (NULL, pw, ent, NULL, buffer, buflen);
930       if (status == NSS_STATUS_RETURN)
931         return getpwent_next_file (pw, ent, buffer, buflen);
932       else
933         return status;
934     }
935   else
936     if (ent->nis)
937       {
938         if (use_nisplus)
939           return getpwent_next_nisplus (pw, ent, buffer, buflen);
940         else
941           return getpwent_next_nis (pw, ent, buffer, buflen);
942       }
943     else
944       return getpwent_next_file (pw, ent, buffer, buflen);
945 }
946
947 enum nss_status
948 _nss_compat_getpwent_r (struct passwd *pwd, char *buffer, size_t buflen)
949 {
950   enum nss_status status = NSS_STATUS_SUCCESS;
951
952   __libc_lock_lock (lock);
953
954   if (ni == NULL)
955     {
956       __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
957       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
958     }
959
960   /* Be prepared that the setpwent function was not called before.  */
961   if (ext_ent.stream == NULL)
962     status = internal_setpwent (&ext_ent);
963
964   if (status == NSS_STATUS_SUCCESS)
965     status = internal_getpwent_r (pwd, &ext_ent, buffer, buflen);
966
967   __libc_lock_unlock (lock);
968
969   return status;
970 }
971
972 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user */
973 static enum nss_status
974 internal_getpwnam_r (const char *name, struct passwd *result, ent_t *ent,
975                      char *buffer, size_t buflen)
976 {
977   struct parser_data *data = (void *) buffer;
978
979   while (1)
980     {
981       fpos_t pos;
982       char *p;
983       int parse_res;
984
985       do
986         {
987           fgetpos (ent->stream, &pos);
988           p = fgets (buffer, buflen, ent->stream);
989           if (p == NULL)
990             {
991               if (feof (ent->stream))
992                 return NSS_STATUS_NOTFOUND;
993               else
994                 {
995                   __set_errno (ERANGE);
996                   return NSS_STATUS_TRYAGAIN;
997                 }
998             }
999
1000           /* Terminate the line for any case.  */
1001           buffer[buflen - 1] = '\0';
1002
1003           /* Skip leading blanks.  */
1004           while (isspace (*p))
1005             ++p;
1006         }
1007       while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines.  */
1008              /* Parse the line.  If it is invalid, loop to
1009                 get the next line of the file to parse.  */
1010              !(parse_res = _nss_files_parse_pwent (p, result, data, buflen)));
1011
1012       if (parse_res == -1)
1013         {
1014           /* The parser ran out of space.  */
1015           fsetpos (ent->stream, &pos);
1016           __set_errno (ERANGE);
1017           return NSS_STATUS_TRYAGAIN;
1018         }
1019
1020       /* This is a real entry.  */
1021       if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
1022         {
1023           if (strcmp (result->pw_name, name) == 0)
1024             return NSS_STATUS_SUCCESS;
1025           else
1026             continue;
1027         }
1028
1029       /* -@netgroup */
1030       if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
1031           && result->pw_name[2] != '\0')
1032         {
1033           char buf2[1024];
1034           char *user, *host, *domain;
1035           struct __netgrent netgrdata;
1036
1037           bzero (&netgrdata, sizeof (struct __netgrent));
1038           __internal_setnetgrent (&result->pw_name[2], &netgrdata);
1039           while (__internal_getnetgrent_r (&host, &user, &domain,
1040                                            &netgrdata, buf2, sizeof (buf2)))
1041             {
1042               if (user != NULL && user[0] != '-')
1043                 if (strcmp (user, name) == 0)
1044                   return NSS_STATUS_NOTFOUND;
1045             }
1046           __internal_endnetgrent (&netgrdata);
1047           continue;
1048         }
1049
1050       /* +@netgroup */
1051       if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
1052           && result->pw_name[2] != '\0')
1053         {
1054           char buf[strlen (result->pw_name)];
1055           int status;
1056
1057           strcpy (buf, &result->pw_name[2]);
1058           ent->netgroup = TRUE;
1059           ent->first = TRUE;
1060           copy_pwd_changes (&ent->pwd, result, NULL, 0);
1061
1062           do
1063             {
1064               if (use_nisplus)
1065                 status = getpwent_next_nisplus_netgr (name, result, ent, buf,
1066                                                       buffer, buflen);
1067               else
1068                 status = getpwent_next_nis_netgr (name, result, ent, buf,
1069                                                   buffer, buflen);
1070               if (status == NSS_STATUS_RETURN)
1071                 continue;
1072
1073               if (status == NSS_STATUS_SUCCESS &&
1074                   strcmp (result->pw_name, name) == 0)
1075                 return NSS_STATUS_SUCCESS;
1076             } while (status == NSS_STATUS_SUCCESS);
1077           continue;
1078         }
1079
1080       /* -user */
1081       if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
1082           && result->pw_name[1] != '@')
1083         {
1084           if (strcmp (&result->pw_name[1], name) == 0)
1085             return NSS_STATUS_NOTFOUND;
1086           else
1087             continue;
1088         }
1089
1090       /* +user */
1091       if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
1092           && result->pw_name[1] != '@')
1093         {
1094           if (strcmp (name, &result->pw_name[1]) == 0)
1095             {
1096               enum nss_status status;
1097
1098               status = getpwnam_plususer (name, result, buffer, buflen);
1099               if (status == NSS_STATUS_RETURN)
1100                 /* We couldn't parse the entry */
1101                 return NSS_STATUS_NOTFOUND;
1102               else
1103                 return status;
1104             }
1105         }
1106
1107       /* +:... */
1108       if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
1109         {
1110           enum nss_status status;
1111
1112           status = getpwnam_plususer (name, result, buffer, buflen);
1113           if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
1114             break;
1115           else
1116             if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1117               return NSS_STATUS_NOTFOUND;
1118             else
1119               return status;
1120         }
1121     }
1122   return NSS_STATUS_SUCCESS;
1123 }
1124
1125 enum nss_status
1126 _nss_compat_getpwnam_r (const char *name, struct passwd *pwd,
1127                         char *buffer, size_t buflen)
1128 {
1129   ent_t ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
1130                {NULL, NULL, 0, 0, NULL, NULL, NULL}};
1131   enum nss_status status;
1132
1133   if (name[0] == '-' || name[0] == '+')
1134     return NSS_STATUS_NOTFOUND;
1135
1136   __libc_lock_lock (lock);
1137
1138   if (ni == NULL)
1139     {
1140       __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
1141       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
1142     }
1143
1144   __libc_lock_unlock (lock);
1145
1146   status = internal_setpwent (&ent);
1147   if (status != NSS_STATUS_SUCCESS)
1148     return status;
1149
1150   status = internal_getpwnam_r (name, pwd, &ent, buffer, buflen);
1151
1152   internal_endpwent (&ent);
1153
1154   return status;
1155 }
1156
1157 /* This function handle the + entry in /etc/passwd for getpwuid */
1158 static enum nss_status
1159 getpwuid_plususer (uid_t uid, struct passwd *result, char *buffer,
1160                    size_t buflen)
1161 {
1162   struct parser_data *data = (void *) buffer;
1163   struct passwd pwd;
1164   int parse_res;
1165   char *p;
1166   size_t plen;
1167
1168   memset (&pwd, '\0', sizeof (struct passwd));
1169
1170   copy_pwd_changes (&pwd, result, NULL, 0);
1171
1172   plen = pwd_need_buflen (&pwd);
1173   if (plen > buflen)
1174     {
1175       __set_errno (ERANGE);
1176       return NSS_STATUS_TRYAGAIN;
1177     }
1178   p = buffer + (buflen - plen);
1179   buflen -= plen;
1180
1181   if (use_nisplus) /* Do the NIS+ query here */
1182     {
1183       nis_result *res;
1184       char buf[1024 + pwdtablelen];
1185
1186       sprintf(buf, "[uid=%d],%s", uid, pwdtable);
1187       res = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
1188       if (niserr2nss (res->status) != NSS_STATUS_SUCCESS)
1189         {
1190           enum nss_status status =  niserr2nss (res->status);
1191
1192           nis_freeresult (res);
1193           return status;
1194         }
1195       if ((parse_res = _nss_nisplus_parse_pwent (res, result, buffer,
1196                                                  buflen)) == -1)
1197         {
1198           nis_freeresult (res);
1199           __set_errno (ERANGE);
1200           return NSS_STATUS_TRYAGAIN;
1201         }
1202       nis_freeresult (res);
1203     }
1204   else /* Use NIS */
1205     {
1206       char buf[1024];
1207       char *domain, *outval, *ptr;
1208       int outvallen;
1209
1210       if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
1211         return NSS_STATUS_TRYAGAIN;
1212
1213       sprintf (buf, "%d", uid);
1214       if (yp_match (domain, "passwd.byuid", buf, strlen (buf),
1215                     &outval, &outvallen)
1216           != YPERR_SUCCESS)
1217         return NSS_STATUS_TRYAGAIN;
1218       ptr = strncpy (buffer, outval, buflen < (size_t) outvallen ?
1219                      buflen : (size_t) outvallen);
1220       buffer[buflen < (size_t) outvallen ? buflen : (size_t) outvallen] = '\0';
1221       free (outval);
1222       while (isspace (*ptr))
1223         ptr++;
1224       if ((parse_res = _nss_files_parse_pwent (ptr, result, data, buflen))
1225           == -1)
1226         {
1227           __set_errno (ERANGE);
1228           return NSS_STATUS_TRYAGAIN;
1229         }
1230     }
1231
1232   if (parse_res > 0)
1233     {
1234       copy_pwd_changes (result, &pwd, p, plen);
1235       give_pwd_free (&pwd);
1236       /* We found the entry.  */
1237       return NSS_STATUS_SUCCESS;
1238     }
1239   else
1240     {
1241       /* Give buffer the old len back */
1242       buflen += plen;
1243       give_pwd_free (&pwd);
1244     }
1245   return NSS_STATUS_RETURN;
1246 }
1247
1248 /* Searches in /etc/passwd and the NIS/NIS+ map for a special user id */
1249 static enum nss_status
1250 internal_getpwuid_r (uid_t uid, struct passwd *result, ent_t *ent,
1251                      char *buffer, size_t buflen)
1252 {
1253   struct parser_data *data = (void *) buffer;
1254
1255   while (1)
1256     {
1257       fpos_t pos;
1258       char *p;
1259       int parse_res;
1260
1261       do
1262         {
1263           fgetpos (ent->stream, &pos);
1264           p = fgets (buffer, buflen, ent->stream);
1265           if (p == NULL)
1266             return NSS_STATUS_NOTFOUND;
1267
1268           /* Terminate the line for any case.  */
1269           buffer[buflen - 1] = '\0';
1270
1271           /* Skip leading blanks.  */
1272           while (isspace (*p))
1273             ++p;
1274         }
1275       while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines.  */
1276              /* Parse the line.  If it is invalid, loop to
1277                 get the next line of the file to parse.  */
1278              !(parse_res = _nss_files_parse_pwent (p, result, data, buflen)));
1279
1280       if (parse_res == -1)
1281         {
1282           /* The parser ran out of space.  */
1283           fsetpos (ent->stream, &pos);
1284           __set_errno (ERANGE);
1285           return NSS_STATUS_TRYAGAIN;
1286         }
1287
1288       /* This is a real entry.  */
1289       if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
1290         {
1291           if (result->pw_uid == uid)
1292             return NSS_STATUS_SUCCESS;
1293           else
1294             continue;
1295         }
1296
1297       /* -@netgroup */
1298       if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
1299           && result->pw_name[2] != '\0')
1300         {
1301           char buf2[1024];
1302           char *user, *host, *domain;
1303           struct __netgrent netgrdata;
1304
1305           bzero (&netgrdata, sizeof (struct __netgrent));
1306           __internal_setnetgrent (&result->pw_name[2], &netgrdata);
1307           while (__internal_getnetgrent_r (&host, &user, &domain,
1308                                            &netgrdata, buf2, sizeof (buf2)))
1309             {
1310               if (user != NULL && user[0] != '-')
1311                 blacklist_store_name (user, ent);
1312             }
1313           __internal_endnetgrent (&netgrdata);
1314           continue;
1315         }
1316
1317       /* +@netgroup */
1318       if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
1319           && result->pw_name[2] != '\0')
1320         {
1321           char buf[strlen (result->pw_name)];
1322           int status;
1323
1324           strcpy (buf, &result->pw_name[2]);
1325           ent->netgroup = TRUE;
1326           ent->first = TRUE;
1327           copy_pwd_changes (&ent->pwd, result, NULL, 0);
1328
1329           do
1330             {
1331               if (use_nisplus)
1332                 status = getpwent_next_nisplus_netgr (NULL, result, ent, buf,
1333                                                       buffer, buflen);
1334               else
1335                 status = getpwent_next_nis_netgr (NULL, result, ent, buf,
1336                                                   buffer, buflen);
1337               if (status == NSS_STATUS_RETURN)
1338                 continue;
1339
1340               if (status == NSS_STATUS_SUCCESS && uid == result->pw_uid)
1341                 return NSS_STATUS_SUCCESS;
1342             } while (status == NSS_STATUS_SUCCESS);
1343           continue;
1344         }
1345
1346       /* -user */
1347       if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
1348           && result->pw_name[1] != '@')
1349         {
1350           blacklist_store_name (&result->pw_name[1], ent);
1351           continue;
1352         }
1353
1354       /* +user */
1355       if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
1356           && result->pw_name[1] != '@')
1357         {
1358           enum nss_status status;
1359
1360           /* Store the User in the blacklist for the "+" at the end of
1361              /etc/passwd */
1362           blacklist_store_name (&result->pw_name[1], ent);
1363           status = getpwnam_plususer (&result->pw_name[1], result, buffer,
1364                                       buflen);
1365           if (status == NSS_STATUS_SUCCESS && result->pw_uid == uid)
1366             break;
1367           else
1368             continue;
1369         }
1370
1371       /* +:... */
1372       if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
1373         {
1374           enum nss_status status;
1375
1376           status = getpwuid_plususer (uid, result, buffer, buflen);
1377           if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
1378             break;
1379           else
1380             if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
1381               return NSS_STATUS_NOTFOUND;
1382             else
1383               return status;
1384         }
1385     }
1386   return NSS_STATUS_SUCCESS;
1387 }
1388
1389 enum nss_status
1390 _nss_compat_getpwuid_r (uid_t uid, struct passwd *pwd,
1391                         char *buffer, size_t buflen)
1392 {
1393   ent_t ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
1394                {NULL, NULL, 0, 0, NULL, NULL, NULL}};
1395   enum nss_status status;
1396
1397   __libc_lock_lock (lock);
1398
1399   if (ni == NULL)
1400     {
1401       __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
1402       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
1403     }
1404
1405   __libc_lock_unlock (lock);
1406
1407   status = internal_setpwent (&ent);
1408   if (status != NSS_STATUS_SUCCESS)
1409     return status;
1410
1411   status = internal_getpwuid_r (uid, pwd, &ent, buffer, buflen);
1412
1413   internal_endpwent (&ent);
1414
1415   return status;
1416 }
1417
1418
1419 /* Support routines for remembering -@netgroup and -user entries.
1420    The names are stored in a single string with `|' as separator. */
1421 static void
1422 blacklist_store_name (const char *name, ent_t *ent)
1423 {
1424   int namelen = strlen (name);
1425   char *tmp;
1426
1427   /* first call, setup cache */
1428   if (ent->blacklist.size == 0)
1429     {
1430       ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
1431       ent->blacklist.data = malloc (ent->blacklist.size);
1432       if (ent->blacklist.data == NULL)
1433         return;
1434       ent->blacklist.data[0] = '|';
1435       ent->blacklist.data[1] = '\0';
1436       ent->blacklist.current = 1;
1437     }
1438   else
1439     {
1440       if (in_blacklist (name, namelen, ent))
1441         return;                 /* no duplicates */
1442
1443       if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
1444         {
1445           ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
1446           tmp = realloc (ent->blacklist.data, ent->blacklist.size);
1447           if (tmp == NULL)
1448             {
1449               free (ent->blacklist.data);
1450               ent->blacklist.size = 0;
1451               return;
1452             }
1453           ent->blacklist.data = tmp;
1454         }
1455     }
1456
1457   tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
1458   *tmp++ = '|';
1459   *tmp = '\0';
1460   ent->blacklist.current += namelen + 1;
1461
1462   return;
1463 }
1464
1465 /* returns TRUE if ent->blacklist contains name, else FALSE */
1466 static bool_t
1467 in_blacklist (const char *name, int namelen, ent_t *ent)
1468 {
1469   char buf[namelen + 3];
1470   char *cp;
1471
1472   if (ent->blacklist.data == NULL)
1473     return FALSE;
1474
1475   buf[0] = '|';
1476   cp = stpcpy (&buf[1], name);
1477   *cp++= '|';
1478   *cp = '\0';
1479   return strstr (ent->blacklist.data, buf) != NULL;
1480 }