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