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