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