(getgrent_next_file_plususer): Add cast to prevent warning.
[kopensolaris-gnu/glibc.git] / nis / nss_compat / compat-spwd.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 <errno.h>
22 #include <ctype.h>
23 #include <netdb.h>
24 #include <shadow.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 spent
41 #define STRUCTURE spwd
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 spwd 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, 0, 0, 0, 0, 0}};
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_spent (nis_result *, struct spwd *,
82                                      char *, size_t);
83 static void
84 give_spwd_free (struct spwd *pwd)
85 {
86   if (pwd->sp_namp != NULL)
87     free (pwd->sp_namp);
88   if (pwd->sp_pwdp != NULL)
89     free (pwd->sp_pwdp);
90
91   memset (pwd, '\0', sizeof (struct spwd));
92 }
93
94 static int
95 spwd_need_buflen (struct spwd *pwd)
96 {
97   int len = 0;
98
99   if (pwd->sp_pwdp != NULL)
100     len += strlen (pwd->sp_pwdp) + 1;
101
102   return len;
103 }
104
105 static void
106 copy_spwd_changes (struct spwd *dest, struct spwd *src,
107                    char *buffer, size_t buflen)
108 {
109   if (src->sp_pwdp != NULL && strlen (src->sp_pwdp))
110     {
111       if (buffer == NULL)
112         dest->sp_pwdp = strdup (src->sp_pwdp);
113       else if (dest->sp_pwdp &&
114                strlen (dest->sp_pwdp) >= strlen (src->sp_pwdp))
115         strcpy (dest->sp_pwdp, src->sp_pwdp);
116       else
117         {
118           dest->sp_pwdp = buffer;
119           strcpy (dest->sp_pwdp, src->sp_pwdp);
120           buffer += strlen (dest->sp_pwdp) + 1;
121           buflen = buflen - (strlen (dest->sp_pwdp) + 1);
122         }
123     }
124   if (src->sp_lstchg != 0)
125     dest->sp_lstchg = src->sp_lstchg;
126   if (src->sp_min != 0)
127     dest->sp_min = src->sp_min;
128   if (src->sp_max != 0)
129     dest->sp_max = src->sp_max;
130   if (src->sp_warn != 0)
131     dest->sp_warn = src->sp_warn;
132   if (src->sp_inact != 0)
133     dest->sp_inact = src->sp_inact;
134   if (src->sp_expire != 0)
135     dest->sp_expire = src->sp_expire;
136   if (src->sp_flag != 0)
137     dest->sp_flag = src->sp_flag;
138 }
139
140 static enum nss_status
141 internal_setspent (ent_t *ent)
142 {
143   enum nss_status status = NSS_STATUS_SUCCESS;
144
145   ent->nis = ent->first = ent->netgroup = 0;
146
147   /* If something was left over free it.  */
148   if (ent->netgroup)
149     __internal_endnetgrent (&ent->netgrdata);
150
151   if (ent->oldkey != NULL)
152     {
153       free (ent->oldkey);
154       ent->oldkey = NULL;
155       ent->oldkeylen = 0;
156     }
157
158   if (ent->result != NULL)
159     {
160       nis_freeresult (ent->result);
161       ent->result = NULL;
162     }
163   if (ent->names != NULL)
164     {
165       nis_freenames (ent->names);
166       ent->names = NULL;
167     }
168   ent->names_nr = 0;
169
170   ent->blacklist.current = 0;
171   if (ent->blacklist.data != NULL)
172     ent->blacklist.data[0] = '\0';
173
174   if (ent->stream == NULL)
175     {
176       ent->stream = fopen ("/etc/shadow", "r");
177
178       if (ent->stream == NULL)
179         status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
180     }
181   else
182     rewind (ent->stream);
183
184   give_spwd_free (&ent->pwd);
185
186   return status;
187 }
188
189
190 enum nss_status
191 _nss_compat_setspent (void)
192 {
193   enum nss_status result;
194
195   __libc_lock_lock (lock);
196
197   if (ni == NULL)
198     {
199       __nss_database_lookup ("shadow_compat", "passwd_compat", "nis", &ni);
200       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
201     }
202
203   result = internal_setspent (&ext_ent);
204
205   __libc_lock_unlock (lock);
206
207   return result;
208 }
209
210
211 static enum nss_status
212 internal_endspent (ent_t *ent)
213 {
214   if (ent->stream != NULL)
215     {
216       fclose (ent->stream);
217       ent->stream = NULL;
218     }
219
220   if (ent->netgroup)
221     __internal_endnetgrent (&ent->netgrdata);
222
223   ent->nis = ent->first = ent->netgroup = 0;
224
225   if (ent->oldkey != NULL)
226     {
227       free (ent->oldkey);
228       ent->oldkey = NULL;
229       ent->oldkeylen = 0;
230     }
231
232   if (ent->result != NULL)
233     {
234       nis_freeresult (ent->result);
235       ent->result = NULL;
236     }
237   if (ent->names != NULL)
238     {
239       nis_freenames (ent->names);
240       ent->names = NULL;
241     }
242   ent->names_nr = 0;
243
244   ent->blacklist.current = 0;
245   if (ent->blacklist.data != NULL)
246     ent->blacklist.data[0] = '\0';
247
248   give_spwd_free (&ent->pwd);
249
250   return NSS_STATUS_SUCCESS;
251 }
252
253 enum nss_status
254 _nss_compat_endspent (void)
255 {
256   enum nss_status result;
257
258   __libc_lock_lock (lock);
259
260   result = internal_endspent (&ext_ent);
261
262   __libc_lock_unlock (lock);
263
264   return result;
265 }
266
267
268 static enum nss_status
269 getspent_next_nis_netgr (struct spwd *result, ent_t *ent, char *group,
270                          char *buffer, size_t buflen)
271 {
272   struct parser_data *data = (void *) buffer;
273   char *ypdomain, *host, *user, *domain, *outval, *p, *p2;
274   int status, outvallen;
275   size_t p2len;
276
277   if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
278     {
279       ent->netgroup = 0;
280       ent->first = 0;
281       give_spwd_free (&ent->pwd);
282       return NSS_STATUS_UNAVAIL;
283     }
284
285   if (ent->first == TRUE)
286     {
287       bzero (&ent->netgrdata, sizeof (struct __netgrent));
288       __internal_setnetgrent (group, &ent->netgrdata);
289       ent->first = FALSE;
290     }
291
292   while (1)
293     {
294       status = __internal_getnetgrent_r (&host, &user, &domain,
295                                          &ent->netgrdata, buffer, buflen);
296       if (status != 1)
297         {
298           __internal_endnetgrent (&ent->netgrdata);
299           ent->netgroup = 0;
300           give_spwd_free (&ent->pwd);
301           return NSS_STATUS_RETURN;
302         }
303
304       if (user == NULL || user[0] == '-')
305         continue;
306
307       if (domain != NULL && strcmp (ypdomain, domain) != 0)
308         continue;
309
310       if (yp_match (ypdomain, "shadow.byname", user,
311                     strlen (user), &outval, &outvallen)
312           != YPERR_SUCCESS)
313         continue;
314
315       p2len = spwd_need_buflen (&ent->pwd);
316       if (p2len > buflen)
317         {
318           __set_errno (ERANGE);
319           return NSS_STATUS_TRYAGAIN;
320         }
321       p2 = buffer + (buflen - p2len);
322       buflen -= p2len;
323       p = strncpy (buffer, outval, buflen);
324       while (isspace (*p))
325         p++;
326       free (outval);
327       if (_nss_files_parse_spent (p, result, data, buflen))
328         {
329           copy_spwd_changes (result, &ent->pwd, p2, p2len);
330           break;
331         }
332     }
333
334   return NSS_STATUS_SUCCESS;
335 }
336
337 static enum nss_status
338 getspent_next_nisplus_netgr (struct spwd *result, ent_t *ent, char *group,
339                              char *buffer, size_t buflen)
340 {
341   char *ypdomain, *host, *user, *domain, *p2;
342   int status, parse_res;
343   size_t p2len;
344   nis_result *nisres;
345
346   /* Maybe we should use domainname here ? We need the current
347      domainname for the domain field in netgroups */
348   if (yp_get_default_domain (&ypdomain) != YPERR_SUCCESS)
349     {
350       ent->netgroup = 0;
351       ent->first = 0;
352       give_spwd_free (&ent->pwd);
353       return NSS_STATUS_UNAVAIL;
354     }
355
356   if (ent->first == TRUE)
357     {
358       bzero (&ent->netgrdata, sizeof (struct __netgrent));
359       __internal_setnetgrent (group, &ent->netgrdata);
360       ent->first = FALSE;
361     }
362
363   while (1)
364     {
365       status = __internal_getnetgrent_r (&host, &user, &domain,
366                                          &ent->netgrdata, buffer, buflen);
367       if (status != 1)
368         {
369           __internal_endnetgrent (&ent->netgrdata);
370           ent->netgroup = 0;
371           give_spwd_free (&ent->pwd);
372           return NSS_STATUS_RETURN;
373         }
374
375       if (user == NULL || user[0] == '-')
376         continue;
377
378       if (domain != NULL && strcmp (ypdomain, domain) != 0)
379         continue;
380
381       p2len = spwd_need_buflen (&ent->pwd);
382       if (p2len > buflen)
383         {
384           __set_errno (ERANGE);
385           return NSS_STATUS_TRYAGAIN;
386         }
387       p2 = buffer + (buflen - p2len);
388       buflen -= p2len;
389       {
390         char buf[strlen (user) + 30];
391         sprintf(buf, "[name=%s],passwd.org_dir", user);
392         nisres = nis_list(buf, EXPAND_NAME, NULL, NULL);
393       }
394       if (niserr2nss (nisres->status) != NSS_STATUS_SUCCESS)
395         {
396           nis_freeresult (nisres);
397           continue;
398         }
399       parse_res = _nss_nisplus_parse_spent (nisres, result, buffer, buflen);
400       nis_freeresult (nisres);
401
402       if (parse_res)
403         {
404           copy_spwd_changes (result, &ent->pwd, p2, p2len);
405           break;
406         }
407     }
408
409   return NSS_STATUS_SUCCESS;
410 }
411
412 static enum nss_status
413 getspent_next_netgr (struct spwd *result, ent_t *ent, char *group,
414                      char *buffer, size_t buflen)
415 {
416   if (use_nisplus)
417     return getspent_next_nisplus_netgr (result, ent, group, buffer, buflen);
418   else
419     return getspent_next_nis_netgr (result, ent, group, buffer, buflen);
420 }
421
422 static enum nss_status
423 getspent_next_nisplus (struct spwd *result, ent_t *ent, char *buffer,
424                        size_t buflen)
425 {
426   int parse_res;
427   size_t p2len;
428   char *p2;
429
430   if (ent->names == NULL)
431     {
432       ent->names = nis_getnames ("passwd.org_dir");
433       if (ent->names == NULL || ent->names[0] == NULL)
434         {
435           ent->nis = 0;
436           return NSS_STATUS_UNAVAIL;
437         }
438     }
439
440   p2len = spwd_need_buflen (&ent->pwd);
441   if (p2len > buflen)
442     {
443       __set_errno (ERANGE);
444       return NSS_STATUS_TRYAGAIN;
445     }
446   p2 = buffer + (buflen - p2len);
447   buflen -= p2len;
448   do
449     {
450       if (ent->first)
451         {
452         next_name:
453           ent->result = nis_first_entry(ent->names[ent->names_nr]);
454           if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
455             {
456               ent->nis = 0;
457               give_spwd_free (&ent->pwd);
458               return niserr2nss (ent->result->status);
459             }
460           ent->first = FALSE;
461         }
462       else
463         {
464           nis_result *res;
465
466           res = nis_next_entry(ent->names[ent->names_nr],
467                                &ent->result->cookie);
468           nis_freeresult (ent->result);
469           ent->result = res;
470           if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
471             {
472               if ((ent->result->status == NIS_NOTFOUND) &&
473                   ent->names[ent->names_nr + 1] != NULL)
474                 {
475                   nis_freeresult (ent->result);
476                   ent->names_nr += 1;
477                   goto next_name;
478                 }
479               else
480                 {
481                   ent->nis = 0;
482                   give_spwd_free (&ent->pwd);
483                   return niserr2nss (ent->result->status);
484                 }
485             }
486         }
487       parse_res = _nss_nisplus_parse_spent (ent->result, result, buffer,
488                                             buflen);
489       if (parse_res &&
490           in_blacklist (result->sp_namp, strlen (result->sp_namp), ent))
491         parse_res = 0; /* if result->pw_name in blacklist,search next entry */
492     }
493   while (!parse_res);
494
495   copy_spwd_changes (result, &ent->pwd, p2, p2len);
496
497   return NSS_STATUS_SUCCESS;
498 }
499
500
501 static enum nss_status
502 getspent_next_nis (struct spwd *result, ent_t *ent,
503                    char *buffer, size_t buflen)
504 {
505   struct parser_data *data = (void *) buffer;
506   char *domain, *outkey, *outval, *p, *p2;
507   int outkeylen, outvallen, parse_res;
508   size_t p2len;
509
510   if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
511     {
512       ent->nis = 0;
513       give_spwd_free (&ent->pwd);
514       return NSS_STATUS_UNAVAIL;
515     }
516
517   p2len = spwd_need_buflen (&ent->pwd);
518   if (p2len > buflen)
519     {
520       __set_errno (ERANGE);
521       return NSS_STATUS_TRYAGAIN;
522     }
523   p2 = buffer + (buflen - p2len);
524   buflen -= p2len;
525   do
526     {
527       if (ent->first)
528         {
529           if (yp_first (domain, "shadow.byname", &outkey, &outkeylen,
530                         &outval, &outvallen) != YPERR_SUCCESS)
531             {
532               ent->nis = 0;
533               give_spwd_free (&ent->pwd);
534               return NSS_STATUS_UNAVAIL;
535             }
536
537           ent->oldkey = outkey;
538           ent->oldkeylen = outkeylen;
539           ent->first = FALSE;
540         }
541       else
542         {
543           if (yp_next (domain, "shadow.byname", ent->oldkey, ent->oldkeylen,
544                        &outkey, &outkeylen, &outval, &outvallen)
545               != YPERR_SUCCESS)
546             {
547               ent->nis = 0;
548               give_spwd_free (&ent->pwd);
549               return NSS_STATUS_NOTFOUND;
550             }
551
552           free (ent->oldkey);
553           ent->oldkey = outkey;
554           ent->oldkeylen = outkeylen;
555         }
556
557       /* Copy the found data to our buffer  */
558       p = strncpy (buffer, outval, buflen);
559
560       /* ...and free the data.  */
561       free (outval);
562
563       while (isspace (*p))
564         ++p;
565       parse_res = _nss_files_parse_spent (p, result, data, buflen);
566       if (parse_res &&
567           in_blacklist (result->sp_namp, strlen (result->sp_namp), ent))
568         parse_res = 0;
569     }
570   while (!parse_res);
571
572   copy_spwd_changes (result, &ent->pwd, p2, p2len);
573
574   return NSS_STATUS_SUCCESS;
575 }
576
577 /* This function handle the +user entrys in /etc/shadow */
578 static enum nss_status
579 getspent_next_file_plususer (struct spwd *result, char *buffer,
580                              size_t buflen)
581 {
582   struct parser_data *data = (void *) buffer;
583   struct spwd pwd;
584   int parse_res;
585   char *p;
586   size_t plen;
587
588   memset (&pwd, '\0', sizeof (struct spwd));
589
590   copy_spwd_changes (&pwd, result, NULL, 0);
591
592   plen = spwd_need_buflen (&pwd);
593   if (plen > buflen)
594     {
595       __set_errno (ERANGE);
596       return NSS_STATUS_TRYAGAIN;
597     }
598   p = buffer + (buflen - plen);
599   buflen -= plen;
600
601   if (use_nisplus) /* Do the NIS+ query here */
602     {
603       nis_result *res;
604       char buf[strlen (result->sp_namp) + 24];
605
606       sprintf(buf, "[name=%s],passwd.org_dir",
607               &result->sp_namp[1]);
608       res = nis_list(buf, EXPAND_NAME, NULL, NULL);
609       if (niserr2nss (res->status) != NSS_STATUS_SUCCESS)
610         {
611           enum nss_status status =  niserr2nss (res->status);
612
613           nis_freeresult (res);
614           return status;
615         }
616       parse_res = _nss_nisplus_parse_spent (res, result, buffer, buflen);
617       nis_freeresult (res);
618     }
619   else /* Use NIS */
620     {
621       char *domain;
622       char *outval;
623       int outvallen;
624
625       if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
626         return NSS_STATUS_TRYAGAIN;
627
628       if (yp_match (domain, "passwd.byname", &result->sp_namp[1],
629                     strlen (result->sp_namp) - 1, &outval, &outvallen)
630           != YPERR_SUCCESS)
631         return NSS_STATUS_TRYAGAIN;
632       p = strncpy (buffer, outval,
633                    buflen < outvallen ? buflen : outvallen);
634       free (outval);
635       while (isspace (*p))
636         p++;
637       parse_res = _nss_files_parse_spent (p, result, data, buflen);
638     }
639
640   if (parse_res)
641     {
642       copy_spwd_changes (result, &pwd, p, plen);
643       give_spwd_free (&pwd);
644       /* We found the entry.  */
645       return NSS_STATUS_SUCCESS;
646     }
647   else
648     {
649       /* Give buffer the old len back */
650       buflen += plen;
651       give_spwd_free (&pwd);
652     }
653   return NSS_STATUS_RETURN;
654 }
655
656 static enum nss_status
657 getspent_next_file (struct spwd *result, ent_t *ent,
658                     char *buffer, size_t buflen)
659 {
660   struct parser_data *data = (void *) buffer;
661   while (1)
662     {
663       char *p;
664
665       do
666         {
667           p = fgets (buffer, buflen, ent->stream);
668           if (p == NULL)
669             return NSS_STATUS_NOTFOUND;
670
671           /* Terminate the line for any case.  */
672           buffer[buflen - 1] = '\0';
673
674           /* Skip leading blanks.  */
675           while (isspace (*p))
676             ++p;
677         }
678       while (*p == '\0' || *p == '#'    /* Ignore empty and comment lines.  */
679       /* Parse the line.  If it is invalid, loop to
680          get the next line of the file to parse.  */
681              || !_nss_files_parse_spent (p, result, data, buflen));
682
683       if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-')
684         /* This is a real entry.  */
685         break;
686
687       /* -@netgroup */
688       if (result->sp_namp[0] == '-' && result->sp_namp[1] == '@'
689           && result->sp_namp[2] != '\0')
690         {
691           char buf2[1024];
692           char *user, *host, *domain;
693           struct __netgrent netgrdata;
694
695           bzero (&netgrdata, sizeof (struct __netgrent));
696           __internal_setnetgrent (&result->sp_namp[2], &netgrdata);
697           while (__internal_getnetgrent_r (&host, &user, &domain,
698                                            &netgrdata, buf2, sizeof (buf2)))
699             {
700               if (user != NULL && user[0] != '-')
701                 blacklist_store_name (user, ent);
702             }
703           __internal_endnetgrent (&netgrdata);
704           continue;
705         }
706
707       /* +@netgroup */
708       if (result->sp_namp[0] == '+' && result->sp_namp[1] == '@'
709           && result->sp_namp[2] != '\0')
710         {
711           int status;
712
713           ent->netgroup = TRUE;
714           ent->first = TRUE;
715           copy_spwd_changes (&ent->pwd, result, NULL, 0);
716
717           status = getspent_next_netgr (result, ent, &result->sp_namp[2],
718                                         buffer, buflen);
719           if (status == NSS_STATUS_RETURN)
720             continue;
721           else
722             return status;
723         }
724
725       /* -user */
726       if (result->sp_namp[0] == '-' && result->sp_namp[1] != '\0'
727           && result->sp_namp[1] != '@')
728         {
729           blacklist_store_name (&result->sp_namp[1], ent);
730           continue;
731         }
732
733       /* +user */
734       if (result->sp_namp[0] == '+' && result->sp_namp[1] != '\0'
735           && result->sp_namp[1] != '@')
736         {
737           enum nss_status status;
738
739           status = getspent_next_file_plususer (result, buffer, buflen);
740           if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
741             break;
742           else
743             if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
744               continue;
745             else
746               return status;
747         }
748
749       /* +:... */
750       if (result->sp_namp[0] == '+' && result->sp_namp[1] == '\0')
751         {
752           ent->nis = TRUE;
753           ent->first = TRUE;
754           copy_spwd_changes (&ent->pwd, result, NULL, 0);
755
756           if (use_nisplus)
757             return getspent_next_nisplus (result, ent, buffer, buflen);
758           else
759             return getspent_next_nis (result, ent, buffer, buflen);
760         }
761     }
762
763   return NSS_STATUS_SUCCESS;
764 }
765
766
767 static enum nss_status
768 internal_getspent_r (struct spwd *pw, ent_t *ent,
769                      char *buffer, size_t buflen)
770 {
771   if (ent->netgroup)
772     {
773       int status;
774
775       /* We are searching members in a netgroup */
776       /* Since this is not the first call, we don't need the group name */
777       status = getspent_next_netgr (pw, ent, NULL, buffer, buflen);
778       if (status == NSS_STATUS_RETURN)
779         return getspent_next_file (pw, ent, buffer, buflen);
780       else
781         return status;
782     }
783   else if (ent->nis)
784     {
785       if (use_nisplus)
786         return getspent_next_nisplus (pw, ent, buffer, buflen);
787       else
788         return getspent_next_nis (pw, ent, buffer, buflen);
789     }
790   else
791     return getspent_next_file (pw, ent, buffer, buflen);
792 }
793
794 enum nss_status
795 _nss_compat_getspent_r (struct spwd *pwd, char *buffer, size_t buflen)
796 {
797   enum nss_status status = NSS_STATUS_SUCCESS;
798
799   __libc_lock_lock (lock);
800
801   if (ni == NULL)
802     {
803       __nss_database_lookup ("shadow_compat", "passwd_compat", "nis", &ni);
804       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
805     }
806
807   /* Be prepared that the setspent function was not called before.  */
808   if (ext_ent.stream == NULL)
809     status = internal_setspent (&ext_ent);
810
811   if (status == NSS_STATUS_SUCCESS)
812     status = internal_getspent_r (pwd, &ext_ent, buffer, buflen);
813
814   __libc_lock_unlock (lock);
815
816   return status;
817 }
818
819
820 enum nss_status
821 _nss_compat_getspnam_r (const char *name, struct spwd *pwd,
822                         char *buffer, size_t buflen)
823 {
824   ent_t ent = {0, 0, 0, NULL, 0, NULL, NULL, 0, NULL, {NULL, 0, 0},
825                {NULL, NULL, 0, 0, 0, 0, 0, 0, 0}};
826   enum nss_status status;
827
828   if (name[0] == '-' || name[0] == '+')
829     return NSS_STATUS_NOTFOUND;
830
831   if (ni == NULL)
832     {
833       __nss_database_lookup ("shadow_compat", "passwd_compat", "nis", &ni);
834       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
835     }
836
837   status = internal_setspent (&ent);
838   if (status != NSS_STATUS_SUCCESS)
839     return status;
840
841   while ((status = internal_getspent_r (pwd, &ent, buffer, buflen))
842          == NSS_STATUS_SUCCESS)
843     if (strcmp (pwd->sp_namp, name) == 0)
844       break;
845
846   internal_endspent (&ent);
847   return status;
848 }
849
850 /* Support routines for remembering -@netgroup and -user entries.
851    The names are stored in a single string with `|' as separator. */
852 static void
853 blacklist_store_name (const char *name, ent_t *ent)
854 {
855   int namelen = strlen (name);
856   char *tmp;
857
858   /* first call, setup cache */
859   if (ent->blacklist.size == 0)
860     {
861       ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
862       ent->blacklist.data = malloc (ent->blacklist.size);
863       if (ent->blacklist.data == NULL)
864         return;
865       ent->blacklist.data[0] = '|';
866       ent->blacklist.data[1] = '\0';
867       ent->blacklist.current = 1;
868     }
869   else
870     {
871       if (in_blacklist (name, namelen, ent))
872         return;                 /* no duplicates */
873
874       if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
875         {
876           ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
877           tmp = realloc (ent->blacklist.data, ent->blacklist.size);
878           if (tmp == NULL)
879             {
880               free (ent->blacklist.data);
881               ent->blacklist.size = 0;
882               return;
883             }
884           ent->blacklist.data = tmp;
885         }
886     }
887
888   tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
889   *tmp++ = '|';
890   *tmp = '\0';
891   ent->blacklist.current += namelen + 1;
892
893   return;
894 }
895
896 /* Returns TRUE if ent->blacklist contains name, else FALSE.  */
897 static bool_t
898 in_blacklist (const char *name, int namelen, ent_t *ent)
899 {
900   char buf[namelen + 3];
901   char *cp;
902
903   if (ent->blacklist.data == NULL)
904     return FALSE;
905
906   buf[0] = '|';
907   cp = stpcpy (&buf[1], name);
908   *cp++= '|';
909   *cp = '\0';
910   return strstr (ent->blacklist.data, buf) != NULL;
911 }