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