Fix "buffer to small" problems and memory leaks.
[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 <netdb.h>
25 #include <string.h>
26 #include <bits/libc-lock.h>
27 #include <rpcsvc/yp.h>
28 #include <rpcsvc/ypclnt.h>
29 #include <rpcsvc/nis.h>
30 #include <rpcsvc/nislib.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       char buf [20 + strlen (nis_local_directory ())];
215       char *p;
216
217       p = stpcpy (buf, "passwd.org_dir.");
218       p = stpcpy (p, nis_local_directory ());
219       pwdtable = strdup (buf);
220       if (pwdtable == NULL)
221         return NSS_STATUS_TRYAGAIN;
222       pwdtablelen = strlen (pwdtable);
223     }
224
225   ent->blacklist.current = 0;
226   if (ent->blacklist.data != NULL)
227     ent->blacklist.data[0] = '\0';
228
229   if (ent->stream == NULL)
230     {
231       ent->stream = fopen ("/etc/passwd", "r");
232
233       if (ent->stream == NULL)
234         status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
235     }
236   else
237     rewind (ent->stream);
238
239   give_pwd_free (&ent->pwd);
240
241   return status;
242 }
243
244
245 enum nss_status
246 _nss_compat_setpwent (void)
247 {
248   enum nss_status result;
249
250   __libc_lock_lock (lock);
251
252   if (ni == NULL)
253     {
254       __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
255       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
256     }
257
258   result = internal_setpwent (&ext_ent);
259
260   __libc_lock_unlock (lock);
261
262   return result;
263 }
264
265
266 static enum nss_status
267 internal_endpwent (ent_t *ent)
268 {
269   if (ent->stream != NULL)
270     {
271       fclose (ent->stream);
272       ent->stream = NULL;
273     }
274
275   if (ent->netgroup)
276     __internal_endnetgrent (&ent->netgrdata);
277   
278   ent->nis = ent->first = ent->netgroup = 0;
279
280   if (ent->oldkey != NULL)
281     {
282       free (ent->oldkey);
283       ent->oldkey = NULL;
284       ent->oldkeylen = 0;
285     }
286
287   if (ent->result != NULL)
288     {
289       nis_freeresult (ent->result);
290       ent->result = NULL;
291     }
292
293   ent->blacklist.current = 0;
294   if (ent->blacklist.data != NULL)
295     ent->blacklist.data[0] = '\0';
296
297   give_pwd_free (&ent->pwd);
298
299   return NSS_STATUS_SUCCESS;
300 }
301
302 enum nss_status
303 _nss_compat_endpwent (void)
304 {
305   enum nss_status result;
306
307   __libc_lock_lock (lock);
308
309   result = internal_endpwent (&ext_ent);
310
311   __libc_lock_unlock (lock);
312
313   return result;
314 }
315
316 static enum nss_status
317 getpwent_next_nis_netgr (struct passwd *result, ent_t *ent, char *group,
318                          char *buffer, size_t buflen)
319 {
320   struct parser_data *data = (void *) buffer;
321   char *ypdomain, *host, *user, *domain, *outval, *p, *p2;
322   int status, outvallen;
323   size_t p2len;
324
325   if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
326     {
327       ent->netgroup = 0;
328       ent->first = 0;
329       give_pwd_free (&ent->pwd);
330       return NSS_STATUS_UNAVAIL;
331     }
332
333   if (ent->first == TRUE)
334     {
335       memset (&ent->netgrdata, 0, sizeof (struct __netgrent));
336       __internal_setnetgrent (group, &ent->netgrdata);
337       ent->first = FALSE;
338     }
339
340   while (1)
341     {
342       char *saved_cursor;
343       int parse_res;
344
345       saved_cursor = ent->netgrdata.cursor;
346       status = __internal_getnetgrent_r (&host, &user, &domain,
347                                          &ent->netgrdata, buffer, buflen);
348       if (status != 1)
349         {
350           __internal_endnetgrent (&ent->netgrdata);
351           ent->netgroup = 0;
352           give_pwd_free (&ent->pwd);
353           return NSS_STATUS_RETURN;
354         }
355
356       if (user == NULL || user[0] == '-')
357         continue;
358
359       if (domain != NULL && strcmp (ypdomain, domain) != 0)
360         continue;
361
362       if (yp_match (ypdomain, "passwd.byname", user,
363                     strlen (user), &outval, &outvallen)
364           != YPERR_SUCCESS)
365         continue;
366
367       p2len = pwd_need_buflen (&ent->pwd);
368       if (p2len > buflen)
369         {
370           __set_errno (ERANGE);
371           return NSS_STATUS_TRYAGAIN;
372         }
373       p2 = buffer + (buflen - p2len);
374       buflen -= p2len;
375       p = strncpy (buffer, outval, buflen);
376       while (isspace (*p))
377         p++;
378       free (outval);
379       if ((parse_res = _nss_files_parse_pwent (p, result, data, buflen)) == -1)
380         {
381           ent->netgrdata.cursor = saved_cursor;
382           return NSS_STATUS_TRYAGAIN;
383         }
384
385       if (parse_res)
386         {
387           copy_pwd_changes (result, &ent->pwd, p2, p2len);
388           break;
389         }
390     }
391
392   return NSS_STATUS_SUCCESS;
393 }
394
395 static enum nss_status
396 getpwent_next_nisplus_netgr (struct passwd *result, ent_t *ent, char *group,
397                              char *buffer, size_t buflen)
398 {
399   char *ypdomain, *host, *user, *domain, *p2;
400   int status, parse_res;
401   size_t p2len;
402   nis_result *nisres;
403
404   /* Maybe we should use domainname here ? We need the current
405      domainname for the domain field in netgroups */
406   if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
407     {
408       ent->netgroup = 0;
409       ent->first = 0;
410       give_pwd_free (&ent->pwd);
411       return NSS_STATUS_UNAVAIL;
412     }
413
414   if (ent->first == TRUE)
415     {
416       bzero (&ent->netgrdata, sizeof (struct __netgrent));
417       __internal_setnetgrent (group, &ent->netgrdata);
418       ent->first = FALSE;
419     }
420
421   while (1)
422     {
423       char *saved_cursor;
424
425       saved_cursor = ent->netgrdata.cursor;
426       status = __internal_getnetgrent_r (&host, &user, &domain,
427                                          &ent->netgrdata, buffer, buflen);
428       if (status != 1)
429         {
430           __internal_endnetgrent (&ent->netgrdata);
431           ent->netgroup = 0;
432           give_pwd_free (&ent->pwd);
433           return NSS_STATUS_RETURN;
434         }
435       
436       if (user == NULL || user[0] == '-')
437         continue;
438       
439       if (domain != NULL && strcmp (ypdomain, domain) != 0)
440         continue;
441       
442       p2len = pwd_need_buflen (&ent->pwd);
443       if (p2len > buflen)
444         {
445           __set_errno (ERANGE);
446           return NSS_STATUS_TRYAGAIN;
447         }
448       p2 = buffer + (buflen - p2len);
449       buflen -= p2len;
450       {
451         char buf[strlen (user) + 30 + pwdtablelen];
452         sprintf(buf, "[name=%s],%s", user, pwdtable);
453         nisres = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
454       }
455       if (niserr2nss (nisres->status) != NSS_STATUS_SUCCESS)
456         {
457           nis_freeresult (nisres);
458           continue;
459         }
460       if ((parse_res = _nss_nisplus_parse_pwent (nisres, result, buffer, 
461                                                  buflen)) == -1)
462         {
463           nis_freeresult (nisres);
464           ent->netgrdata.cursor = saved_cursor;
465           return NSS_STATUS_TRYAGAIN;
466         }
467       nis_freeresult (nisres);
468
469       if (parse_res)
470         {
471           copy_pwd_changes (result, &ent->pwd, p2, p2len);
472           break;
473         }
474     }
475
476   return NSS_STATUS_SUCCESS;
477 }
478
479 static enum nss_status
480 getpwent_next_netgr (struct passwd *result, ent_t *ent, char *group,
481                      char *buffer, size_t buflen)
482 {
483   if (use_nisplus)
484     return getpwent_next_nisplus_netgr (result, ent, group, buffer, buflen);
485   else
486     return getpwent_next_nis_netgr (result, ent, group, buffer, buflen);
487 }
488
489 static enum nss_status
490 getpwent_next_nisplus (struct passwd *result, ent_t *ent, char *buffer,
491                        size_t buflen)
492 {
493   int parse_res;
494   size_t p2len;
495   char *p2;
496
497   p2len = pwd_need_buflen (&ent->pwd);
498   if (p2len > buflen)
499     {
500       __set_errno (ERANGE);
501       return NSS_STATUS_TRYAGAIN;
502     }
503   p2 = buffer + (buflen - p2len);
504   buflen -= p2len;
505   do
506     {
507       bool_t saved_first;
508       nis_result *saved_res;
509
510       if (ent->first)
511         {
512           saved_first = TRUE;
513           saved_res = ent->result;
514
515           ent->result = nis_first_entry(pwdtable);
516           if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
517             {
518               ent->nis = 0;
519               give_pwd_free (&ent->pwd);
520               return niserr2nss (ent->result->status);
521             }
522           ent->first = FALSE;
523         }
524       else
525         {
526           nis_result *res;
527
528           res = nis_next_entry(pwdtable, &ent->result->cookie);
529           saved_res = ent->result;
530           saved_first = FALSE;
531           ent->result = res;
532           if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
533             {
534               ent->nis = 0;
535               nis_freeresult (saved_res);
536               give_pwd_free (&ent->pwd);
537               return niserr2nss (ent->result->status);
538             }
539         }
540       if ((parse_res = _nss_nisplus_parse_pwent (ent->result, result, buffer,
541                                                  buflen)) == -1)
542         {
543           nis_freeresult (ent->result);
544           ent->result = saved_res;
545           ent->first = saved_first;
546           __set_errno (ERANGE);
547           return NSS_STATUS_TRYAGAIN;
548         }
549       else
550         {
551           if (!saved_first)
552             nis_freeresult (saved_res);
553         }
554       
555       if (parse_res &&
556           in_blacklist (result->pw_name, strlen (result->pw_name), ent))
557         parse_res = 0; /* if result->pw_name in blacklist,search next entry */
558     }
559   while (!parse_res);
560
561   copy_pwd_changes (result, &ent->pwd, p2, p2len);
562
563   return NSS_STATUS_SUCCESS;
564 }
565
566 static enum nss_status
567 getpwent_next_nis (struct passwd *result, ent_t *ent, char *buffer,
568                    size_t buflen)
569 {
570   struct parser_data *data = (void *) buffer;
571   char *domain, *outkey, *outval, *p, *p2;
572   int outkeylen, outvallen, parse_res;
573   size_t p2len;
574
575   if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
576     {
577       ent->nis = 0;
578       give_pwd_free (&ent->pwd);
579       return NSS_STATUS_UNAVAIL;
580     }
581
582   p2len = pwd_need_buflen (&ent->pwd);
583   if (p2len > buflen)
584     {
585       __set_errno (ERANGE);
586       return NSS_STATUS_TRYAGAIN;
587     }
588   p2 = buffer + (buflen - p2len);
589   buflen -= p2len;
590   do
591     {
592       bool_t saved_first;
593       char *saved_oldkey;
594       int saved_oldlen;
595       
596       if (ent->first)
597         {
598           if (yp_first (domain, "passwd.byname", &outkey, &outkeylen,
599                         &outval, &outvallen) != YPERR_SUCCESS)
600             {
601               ent->nis = 0;
602               give_pwd_free (&ent->pwd);
603               return NSS_STATUS_UNAVAIL;
604             }
605           
606           saved_first = TRUE;
607           saved_oldkey = ent->oldkey;
608           saved_oldlen = ent->oldkeylen;
609           ent->oldkey = outkey;
610           ent->oldkeylen = outkeylen;
611           ent->first = FALSE;
612         }
613       else
614         {
615           if (yp_next (domain, "passwd.byname", ent->oldkey, ent->oldkeylen,
616                        &outkey, &outkeylen, &outval, &outvallen)
617               != YPERR_SUCCESS)
618             {
619               ent->nis = 0;
620               give_pwd_free (&ent->pwd);
621               return NSS_STATUS_NOTFOUND;
622             }
623
624           saved_first = FALSE;
625           saved_oldkey = ent->oldkey;
626           saved_oldlen = ent->oldkeylen;
627           ent->oldkey = outkey;
628           ent->oldkeylen = outkeylen;
629         }
630
631       /* Copy the found data to our buffer  */
632       p = strncpy (buffer, outval, buflen);
633
634       /* ...and free the data.  */
635       free (outval);
636
637       while (isspace (*p))
638         ++p;
639       if ((parse_res = _nss_files_parse_pwent (p, result, data, buflen)) == -1)
640         {
641           free (ent->oldkey);
642           ent->oldkey = saved_oldkey;
643           ent->oldkeylen = saved_oldlen;
644           ent->first = saved_first;
645           __set_errno (ERANGE);
646           return NSS_STATUS_TRYAGAIN;
647         }
648       else
649         {
650           if (!saved_first)
651             free (saved_oldkey);
652         }
653       if (parse_res &&
654           in_blacklist (result->pw_name, strlen (result->pw_name), ent))
655         parse_res = 0;
656     }
657   while (!parse_res);
658
659   copy_pwd_changes (result, &ent->pwd, p2, p2len);
660
661   return NSS_STATUS_SUCCESS;
662 }
663
664 /* This function handle the +user entrys in /etc/passwd */
665 static enum nss_status
666 getpwent_next_file_plususer (struct passwd *result, char *buffer,
667                              size_t buflen)
668 {
669   struct parser_data *data = (void *) buffer;
670   struct passwd pwd;
671   int parse_res;
672   char *p;
673   size_t plen;
674
675   memset (&pwd, '\0', sizeof (struct passwd));
676
677   copy_pwd_changes (&pwd, result, NULL, 0);
678
679   plen = pwd_need_buflen (&pwd);
680   if (plen > buflen)
681     {
682       __set_errno (ERANGE);
683       return NSS_STATUS_TRYAGAIN;
684     }
685   p = buffer + (buflen - plen);
686   buflen -= plen;
687
688   if (use_nisplus) /* Do the NIS+ query here */
689     {
690       nis_result *res;
691       char buf[strlen (result->pw_name) + 24 + pwdtablelen];
692
693       sprintf(buf, "[name=%s],%s", &result->pw_name[1], pwdtable);
694       res = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
695       if (niserr2nss (res->status) != NSS_STATUS_SUCCESS)
696         {
697           enum nss_status status =  niserr2nss (res->status);
698
699           nis_freeresult (res);
700           return status;
701         }
702       if ((parse_res = _nss_nisplus_parse_pwent (res, result, buffer, 
703                                                  buflen)) == -1)
704         {
705           nis_freeresult (res);
706           __set_errno (ERANGE);
707           return NSS_STATUS_TRYAGAIN;
708         }
709       nis_freeresult (res);
710     }
711   else /* Use NIS */
712     {
713       char *domain;
714       char *outval;
715       int outvallen;
716       
717       if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
718         return NSS_STATUS_TRYAGAIN;
719       
720       if (yp_match (domain, "passwd.byname", &result->pw_name[1],
721                     strlen (result->pw_name) - 1, &outval, &outvallen)
722           != YPERR_SUCCESS)
723         return NSS_STATUS_TRYAGAIN;
724       p = strncpy (buffer, outval,
725                    buflen < (size_t) outvallen ? buflen : (size_t) outvallen);
726       free (outval);
727       while (isspace (*p))
728         p++;
729       if ((parse_res = _nss_files_parse_pwent (p, result, data, buflen)) == -1)
730         {
731           __set_errno (ERANGE);
732           return NSS_STATUS_TRYAGAIN;
733         }
734     }
735
736   if (parse_res > 0)
737     {
738       copy_pwd_changes (result, &pwd, p, plen);
739       give_pwd_free (&pwd);
740       /* We found the entry.  */
741       return NSS_STATUS_SUCCESS;
742     }
743   else
744     {
745       /* Give buffer the old len back */
746       buflen += plen;
747       give_pwd_free (&pwd);
748     }
749   return NSS_STATUS_RETURN;
750 }
751
752 static enum nss_status
753 getpwent_next_file (struct passwd *result, ent_t *ent,
754                     char *buffer, size_t buflen)
755 {
756   struct parser_data *data = (void *) buffer;
757   while (1)
758     {
759       fpos_t pos;
760       char *p;
761       int parse_res;
762
763       do
764         {
765           fgetpos (ent->stream, &pos);
766           p = fgets (buffer, buflen, ent->stream);
767           if (p == NULL)
768             return NSS_STATUS_NOTFOUND;
769
770           /* Terminate the line for any case.  */
771           buffer[buflen - 1] = '\0';
772
773           /* Skip leading blanks.  */
774           while (isspace (*p))
775             ++p;
776         }
777       while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines.  */
778       /* Parse the line.  If it is invalid, loop to
779          get the next line of the file to parse.  */
780              !(parse_res = _nss_files_parse_pwent (p, result, data, buflen)));
781
782       if (parse_res == -1)
783         {
784           /* The parser ran out of space.  */
785           fsetpos (ent->stream, &pos);
786           __set_errno (ERANGE);
787           return NSS_STATUS_TRYAGAIN;
788         }
789
790       if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
791         /* This is a real entry.  */
792         break;
793
794       /* -@netgroup */
795       if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
796           && result->pw_name[2] != '\0')
797         {
798           char buf2[1024];
799           char *user, *host, *domain;
800           struct __netgrent netgrdata;
801
802           bzero (&netgrdata, sizeof (struct __netgrent));
803           __internal_setnetgrent (&result->pw_name[2], &netgrdata);
804           while (__internal_getnetgrent_r (&host, &user, &domain,
805                                            &netgrdata, buf2, sizeof (buf2)))
806             {
807               if (user != NULL && user[0] != '-')
808                 blacklist_store_name (user, ent);
809             }
810           __internal_endnetgrent (&netgrdata);
811           continue;
812         }
813
814       /* +@netgroup */
815       if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
816           && result->pw_name[2] != '\0')
817         {
818           int status;
819
820           ent->netgroup = TRUE;
821           ent->first = TRUE;
822           copy_pwd_changes (&ent->pwd, result, NULL, 0);
823
824           status = getpwent_next_netgr (result, ent, &result->pw_name[2],
825                                         buffer, buflen);
826           if (status == NSS_STATUS_RETURN)
827             continue;
828           else
829             return status;
830         }
831
832       /* -user */
833       if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
834           && result->pw_name[1] != '@')
835         {
836           blacklist_store_name (&result->pw_name[1], ent);
837           continue;
838         }
839
840       /* +user */
841       if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
842           && result->pw_name[1] != '@')
843         {
844           enum nss_status status;
845
846           status = getpwent_next_file_plususer (result, buffer, buflen);
847           if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
848             break;
849           else
850             if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
851               continue;
852             else
853               return status;
854         }
855
856       /* +:... */
857       if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
858         {
859           ent->nis = TRUE;
860           ent->first = TRUE;
861           copy_pwd_changes (&ent->pwd, result, NULL, 0);
862
863           if (use_nisplus)
864             return getpwent_next_nisplus (result, ent, buffer, buflen);
865           else
866             return getpwent_next_nis (result, ent, buffer, buflen);
867         }
868     }
869
870   return NSS_STATUS_SUCCESS;
871 }
872
873
874 static enum nss_status
875 internal_getpwent_r (struct passwd *pw, ent_t *ent, char *buffer,
876                      size_t buflen)
877 {
878   if (ent->netgroup)
879     {
880       int status;
881
882       /* We are searching members in a netgroup */
883       /* Since this is not the first call, we don't need the group name */
884       status = getpwent_next_netgr (pw, ent, NULL, buffer, buflen);
885       if (status == NSS_STATUS_RETURN)
886         return getpwent_next_file (pw, ent, buffer, buflen);
887       else
888         return status;
889     }
890   else if (ent->nis)
891     {
892       if (use_nisplus)
893         return getpwent_next_nisplus (pw, ent, buffer, buflen);
894       else
895         return getpwent_next_nis (pw, ent, buffer, buflen);
896     }
897   else
898     return getpwent_next_file (pw, ent, buffer, buflen);
899 }
900
901 enum nss_status
902 _nss_compat_getpwent_r (struct passwd *pwd, char *buffer,
903                         size_t buflen)
904 {
905   enum nss_status status = NSS_STATUS_SUCCESS;
906
907   __libc_lock_lock (lock);
908
909   if (ni == NULL)
910     {
911       __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
912       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
913     }
914
915   /* Be prepared that the setpwent function was not called before.  */
916   if (ext_ent.stream == NULL)
917     status = internal_setpwent (&ext_ent);
918
919   if (status == NSS_STATUS_SUCCESS)
920     status = internal_getpwent_r (pwd, &ext_ent, buffer, buflen);
921
922   __libc_lock_unlock (lock);
923
924   return status;
925 }
926
927
928 enum nss_status
929 _nss_compat_getpwnam_r (const char *name, struct passwd *pwd,
930                         char *buffer, size_t buflen)
931 {
932   ent_t ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
933                {NULL, NULL, 0, 0, NULL, NULL, NULL}};
934   enum nss_status status;
935
936   if (name[0] == '-' || name[0] == '+')
937     return NSS_STATUS_NOTFOUND;
938
939   __libc_lock_lock (lock);
940
941   if (ni == NULL)
942     {
943       __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
944       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
945     }
946
947   __libc_lock_unlock (lock);
948
949   status = internal_setpwent (&ent);
950   if (status != NSS_STATUS_SUCCESS)
951     return status;
952
953   while ((status = internal_getpwent_r (pwd, &ent, buffer, buflen))
954          == NSS_STATUS_SUCCESS)
955     if (strcmp (pwd->pw_name, name) == 0)
956       break;
957
958   internal_endpwent (&ent);
959   return status;
960 }
961
962
963 enum nss_status
964 _nss_compat_getpwuid_r (uid_t uid, struct passwd *pwd,
965                         char *buffer, size_t buflen)
966 {
967   ent_t ent = {0, 0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0},
968                {NULL, NULL, 0, 0, NULL, NULL, NULL}};
969   enum nss_status status;
970
971   __libc_lock_lock (lock);
972
973   if (ni == NULL)
974     {
975       __nss_database_lookup ("passwd_compat", NULL, "nis", &ni);
976       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
977     }
978
979   __libc_lock_unlock (lock);
980
981   status = internal_setpwent (&ent);
982   if (status != NSS_STATUS_SUCCESS)
983     return status;
984
985   while ((status = internal_getpwent_r (pwd, &ent, buffer, buflen))
986          == NSS_STATUS_SUCCESS)
987     if (pwd->pw_uid == uid && pwd->pw_name[0] != '+' && pwd->pw_name[0] != '-')
988       break;
989
990   internal_endpwent (&ent);
991   return status;
992 }
993
994
995 /* Support routines for remembering -@netgroup and -user entries.
996    The names are stored in a single string with `|' as separator. */
997 static void
998 blacklist_store_name (const char *name, ent_t *ent)
999 {
1000   int namelen = strlen (name);
1001   char *tmp;
1002
1003   /* first call, setup cache */
1004   if (ent->blacklist.size == 0)
1005     {
1006       ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
1007       ent->blacklist.data = malloc (ent->blacklist.size);
1008       if (ent->blacklist.data == NULL)
1009         return;
1010       ent->blacklist.data[0] = '|';
1011       ent->blacklist.data[1] = '\0';
1012       ent->blacklist.current = 1;
1013     }
1014   else
1015     {
1016       if (in_blacklist (name, namelen, ent))
1017         return;                 /* no duplicates */
1018
1019       if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
1020         {
1021           ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
1022           tmp = realloc (ent->blacklist.data, ent->blacklist.size);
1023           if (tmp == NULL)
1024             {
1025               free (ent->blacklist.data);
1026               ent->blacklist.size = 0;
1027               return;
1028             }
1029           ent->blacklist.data = tmp;
1030         }
1031     }
1032
1033   tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
1034   *tmp++ = '|';
1035   *tmp = '\0';
1036   ent->blacklist.current += namelen + 1;
1037
1038   return;
1039 }
1040
1041 /* returns TRUE if ent->blacklist contains name, else FALSE */
1042 static bool_t
1043 in_blacklist (const char *name, int namelen, ent_t *ent)
1044 {
1045   char buf[namelen + 3];
1046   char *cp;
1047
1048   if (ent->blacklist.data == NULL)
1049     return FALSE;
1050
1051   buf[0] = '|';
1052   cp = stpcpy (&buf[1], name);
1053   *cp++= '|';
1054   *cp = '\0';
1055   return strstr (ent->blacklist.data, buf) != NULL;
1056 }