Fix handling of default values for sp_warn, sp_inact, sp_expire and
[kopensolaris-gnu/glibc.git] / nis / nss_compat / compat-grp.c
1 /* Copyright (C) 1996, 1997, 1998 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 <errno.h>
21 #include <fcntl.h>
22 #include <nss.h>
23 #include <grp.h>
24 #include <ctype.h>
25 #include <bits/libc-lock.h>
26 #include <string.h>
27 #include <rpcsvc/yp.h>
28 #include <rpcsvc/ypclnt.h>
29 #include <rpcsvc/nis.h>
30 #include <nsswitch.h>
31
32 #include "nss-nisplus.h"
33 #include "nisplus-parser.h"
34
35 static service_user *ni = NULL;
36 static bool_t use_nisplus = FALSE; /* default: group_compat: nis */
37 static nis_name grptable = NULL; /* Name of the group table */
38 static size_t grptablelen = 0;
39
40 /* Get the declaration of the parser function.  */
41 #define ENTNAME grent
42 #define STRUCTURE group
43 #define EXTERN_PARSER
44 #include <nss/nss_files/files-parse.c>
45
46 /* Structure for remembering -group members ... */
47 #define BLACKLIST_INITIAL_SIZE 512
48 #define BLACKLIST_INCREMENT 256
49 struct blacklist_t
50   {
51     char *data;
52     int current;
53     int size;
54   };
55
56 struct ent_t
57   {
58     bool_t nis;
59     bool_t nis_first;
60     char *oldkey;
61     int oldkeylen;
62     nis_result *result;
63     FILE *stream;
64     struct blacklist_t blacklist;
65 };
66 typedef struct ent_t ent_t;
67
68 static ent_t ext_ent = {0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0}};
69
70 /* Protect global state against multiple changers.  */
71 __libc_lock_define_initialized (static, lock)
72
73 /* Prototypes for local functions.  */
74 static void blacklist_store_name (const char *, ent_t *);
75 static int in_blacklist (const char *, int, ent_t *);
76
77 static enum nss_status
78 _nss_first_init (void)
79 {
80   if (ni == NULL)
81     {
82       __nss_database_lookup ("group_compat", NULL, "nis", &ni);
83       use_nisplus = (strcmp (ni->name, "nisplus") == 0);
84     }
85
86   if (grptable == NULL)
87     {
88       static const char key[] = "group.org_dir.";
89       const char *local_dir = nis_local_directory ();
90       size_t len_local_dir = strlen (local_dir);
91
92       grptable = malloc (sizeof (key) + len_local_dir);
93       if (grptable == NULL)
94         return NSS_STATUS_TRYAGAIN;
95
96       grptablelen = ((char *) mempcpy (mempcpy (grptable,
97                                                 key, sizeof (key) - 1),
98                                        local_dir, len_local_dir + 1)
99                      - grptable) - 1;
100     }
101
102   return NSS_STATUS_SUCCESS;
103 }
104
105 static enum nss_status
106 internal_setgrent (ent_t *ent)
107 {
108   enum nss_status status = NSS_STATUS_SUCCESS;
109
110   ent->nis = ent->nis_first = 0;
111
112   if (_nss_first_init () != NSS_STATUS_SUCCESS)
113     return NSS_STATUS_UNAVAIL;
114
115   if (ent->oldkey != NULL)
116     {
117       free (ent->oldkey);
118       ent->oldkey = NULL;
119       ent->oldkeylen = 0;
120     }
121
122   if (ent->result != NULL)
123     {
124       nis_freeresult (ent->result);
125       ent->result = NULL;
126     }
127
128   if (ent->blacklist.data != NULL)
129     {
130       ent->blacklist.current = 1;
131       ent->blacklist.data[0] = '|';
132       ent->blacklist.data[1] = '\0';
133     }
134   else
135     ent->blacklist.current = 0;
136
137   if (ent->stream == NULL)
138     {
139       ent->stream = fopen ("/etc/group", "r");
140
141       if (ent->stream == NULL)
142         status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
143       else
144         {
145           /* We have to make sure the file is  `closed on exec'.  */
146           int result, flags;
147
148           result = flags = fcntl (fileno (ent->stream), F_GETFD, 0);
149           if (result >= 0)
150             {
151               flags |= FD_CLOEXEC;
152               result = fcntl (fileno (ent->stream), F_SETFD, flags);
153             }
154           if (result < 0)
155             {
156               /* Something went wrong.  Close the stream and return a
157                  failure.  */
158               fclose (ent->stream);
159               ent->stream = NULL;
160               status = NSS_STATUS_UNAVAIL;
161             }
162         }
163     }
164   else
165     rewind (ent->stream);
166
167   return status;
168 }
169
170
171 enum nss_status
172 _nss_compat_setgrent (void)
173 {
174   enum nss_status result;
175
176   __libc_lock_lock (lock);
177
178   result = internal_setgrent (&ext_ent);
179
180   __libc_lock_unlock (lock);
181
182   return result;
183 }
184
185
186 static enum nss_status
187 internal_endgrent (ent_t *ent)
188 {
189   if (ent->stream != NULL)
190     {
191       fclose (ent->stream);
192       ent->stream = NULL;
193     }
194
195   ent->nis = ent->nis_first = 0;
196
197   if (ent->oldkey != NULL)
198     {
199       free (ent->oldkey);
200       ent->oldkey = NULL;
201       ent->oldkeylen = 0;
202     }
203
204   if (ent->result != NULL)
205     {
206       nis_freeresult (ent->result);
207       ent->result = NULL;
208     }
209
210   if (ent->blacklist.data != NULL)
211     {
212       ent->blacklist.current = 1;
213       ent->blacklist.data[0] = '|';
214       ent->blacklist.data[1] = '\0';
215     }
216   else
217     ent->blacklist.current = 0;
218
219   return NSS_STATUS_SUCCESS;
220 }
221
222 enum nss_status
223 _nss_compat_endgrent (void)
224 {
225   enum nss_status result;
226
227   __libc_lock_lock (lock);
228
229   result = internal_endgrent (&ext_ent);
230
231   __libc_lock_unlock (lock);
232
233   return result;
234 }
235
236 static enum nss_status
237 getgrent_next_nis (struct group *result, ent_t *ent, char *buffer,
238                    size_t buflen, int *errnop)
239 {
240   struct parser_data *data = (void *) buffer;
241   char *domain;
242   char *outkey, *outval;
243   int outkeylen, outvallen, parse_res;
244   char *p;
245
246   if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
247     {
248       ent->nis = 0;
249       *errnop = ENOENT;
250       return NSS_STATUS_NOTFOUND;
251     }
252
253   do
254     {
255       char *save_oldkey;
256       int save_oldlen;
257       bool_t save_nis_first;
258
259       if (ent->nis_first)
260         {
261           if (yp_first (domain, "group.byname", &outkey, &outkeylen,
262                         &outval, &outvallen) != YPERR_SUCCESS)
263             {
264               ent->nis = 0;
265               return NSS_STATUS_UNAVAIL;
266             }
267
268           if ( buflen < ((size_t) outvallen + 1))
269             {
270               free (outval);
271               *errnop = ERANGE;
272               return NSS_STATUS_TRYAGAIN;
273             }
274
275           save_oldkey = ent->oldkey;
276           save_oldlen = ent->oldkeylen;
277           save_nis_first = TRUE;
278           ent->oldkey = outkey;
279           ent->oldkeylen = outkeylen;
280           ent->nis_first = FALSE;
281         }
282       else
283         {
284           if (yp_next (domain, "group.byname", ent->oldkey, ent->oldkeylen,
285                        &outkey, &outkeylen, &outval, &outvallen)
286               != YPERR_SUCCESS)
287             {
288               ent->nis = 0;
289               *errnop = ENOENT;
290               return NSS_STATUS_NOTFOUND;
291             }
292
293           if ( buflen < ((size_t) outvallen + 1))
294             {
295               free (outval);
296               *errnop = ERANGE;
297               return NSS_STATUS_TRYAGAIN;
298             }
299
300           save_oldkey = ent->oldkey;
301           save_oldlen = ent->oldkeylen;
302           save_nis_first = FALSE;
303           ent->oldkey = outkey;
304           ent->oldkeylen = outkeylen;
305         }
306
307       /* Copy the found data to our buffer...  */
308       p = strncpy (buffer, outval, buflen);
309
310       /* ...and free the data.  */
311       free (outval);
312
313       while (isspace (*p))
314         ++p;
315
316       parse_res = _nss_files_parse_grent (p, result, data, buflen, errnop);
317       if (parse_res == -1)
318         {
319           free (ent->oldkey);
320           ent->oldkey = save_oldkey;
321           ent->oldkeylen = save_oldlen;
322           ent->nis_first = save_nis_first;
323           *errnop = ERANGE;
324           return NSS_STATUS_TRYAGAIN;
325         }
326       else
327         {
328           if (!save_nis_first)
329             free (save_oldkey);
330         }
331
332       if (parse_res &&
333           in_blacklist (result->gr_name, strlen (result->gr_name), ent))
334         parse_res = 0; /* if result->gr_name in blacklist,search next entry */
335     }
336   while (!parse_res);
337
338   return NSS_STATUS_SUCCESS;
339 }
340
341 static enum nss_status
342 getgrent_next_nisplus (struct group *result, ent_t *ent, char *buffer,
343                        size_t buflen, int *errnop)
344 {
345   int parse_res;
346
347   do
348     {
349       nis_result *save_oldres;
350       bool_t save_nis_first;
351
352       if (ent->nis_first)
353         {
354           save_oldres = ent->result;
355           save_nis_first = TRUE;
356           ent->result = nis_first_entry(grptable);
357           if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
358             {
359               ent->nis = 0;
360               return niserr2nss (ent->result->status);
361             }
362           ent->nis_first = FALSE;
363         }
364       else
365         {
366           nis_result *res;
367
368           save_oldres = ent->result;
369           save_nis_first = FALSE;
370           res = nis_next_entry(grptable, &ent->result->cookie);
371           ent->result = res;
372           if (niserr2nss (ent->result->status) != NSS_STATUS_SUCCESS)
373             {
374               ent->nis = 0;
375               return niserr2nss (ent->result->status);
376             }
377         }
378       parse_res = _nss_nisplus_parse_grent (ent->result, 0, result,
379                                             buffer, buflen, errnop);
380       if (parse_res == -1)
381         {
382           nis_freeresult (ent->result);
383           ent->result = save_oldres;
384           ent->nis_first = save_nis_first;
385           *errnop = ERANGE;
386           return NSS_STATUS_TRYAGAIN;
387         }
388       else
389         {
390           if (!save_nis_first)
391             nis_freeresult (save_oldres);
392         }
393
394       if (parse_res &&
395           in_blacklist (result->gr_name, strlen (result->gr_name), ent))
396         parse_res = 0; /* if result->gr_name in blacklist,search next entry */
397     }
398   while (!parse_res);
399
400   return NSS_STATUS_SUCCESS;
401 }
402
403 /* This function handle the +group entrys in /etc/group */
404 static enum nss_status
405 getgrnam_plusgroup (const char *name, struct group *result, char *buffer,
406                     size_t buflen, int *errnop)
407 {
408   struct parser_data *data = (void *) buffer;
409   int parse_res;
410
411   if (use_nisplus) /* Do the NIS+ query here */
412     {
413       nis_result *res;
414       char buf[strlen (name) + 24 + grptablelen];
415
416       sprintf(buf, "[name=%s],%s", name, grptable);
417       res = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
418       if (niserr2nss (res->status) != NSS_STATUS_SUCCESS)
419         {
420           enum nss_status status =  niserr2nss (res->status);
421
422           nis_freeresult (res);
423           return status;
424         }
425       parse_res = _nss_nisplus_parse_grent (res, 0, result, buffer, buflen,
426                                             errnop);
427       if (parse_res == -1)
428         {
429           nis_freeresult (res);
430           *errnop = ERANGE;
431           return NSS_STATUS_TRYAGAIN;
432         }
433       nis_freeresult (res);
434     }
435   else /* Use NIS */
436     {
437       char *domain, *outval, *p;
438       int outvallen;
439
440       if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
441         return NSS_STATUS_NOTFOUND;
442
443       if (yp_match (domain, "group.byname", name, strlen (name),
444                     &outval, &outvallen) != YPERR_SUCCESS)
445         {
446           *errnop = ENOENT;
447           return NSS_STATUS_NOTFOUND;
448         }
449
450       if (buflen < ((size_t) outvallen + 1))
451         {
452           free (outval);
453           *errnop = ERANGE;
454           return NSS_STATUS_TRYAGAIN;
455         }
456
457       /* Copy the found data to our buffer...  */
458       p = strncpy (buffer, outval, buflen);
459
460       /* ... and free the data.  */
461       free (outval);
462       while (isspace (*p))
463         ++p;
464       parse_res = _nss_files_parse_grent (p, result, data, buflen, errnop);
465       if (parse_res == -1)
466         return NSS_STATUS_TRYAGAIN;
467     }
468
469   if (parse_res)
470     /* We found the entry.  */
471     return NSS_STATUS_SUCCESS;
472   else
473     return NSS_STATUS_RETURN;
474 }
475
476 static enum nss_status
477 getgrent_next_file (struct group *result, ent_t *ent,
478                     char *buffer, size_t buflen, int *errnop)
479 {
480   struct parser_data *data = (void *) buffer;
481   while (1)
482     {
483       fpos_t pos;
484       int parse_res = 0;
485       char *p;
486
487       do
488         {
489           fgetpos (ent->stream, &pos);
490           buffer[buflen - 1] = '\xff';
491           p = fgets (buffer, buflen, ent->stream);
492           if (p == NULL && feof (ent->stream))
493             {
494               *errnop = ENOENT;
495               return NSS_STATUS_NOTFOUND;
496             }
497           if (p == NULL || buffer[buflen - 1] != '\xff')
498             {
499               fsetpos (ent->stream, &pos);
500               *errnop = ERANGE;
501               return NSS_STATUS_TRYAGAIN;
502             }
503
504           /* Terminate the line for any case.  */
505           buffer[buflen - 1] = '\0';
506
507           /* Skip leading blanks.  */
508           while (isspace (*p))
509             ++p;
510         }
511       while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
512       /* Parse the line.  If it is invalid, loop to
513          get the next line of the file to parse.  */
514              !(parse_res = _nss_files_parse_grent (p, result, data, buflen,
515                                                    errnop)));
516
517       if (parse_res == -1)
518         {
519           /* The parser ran out of space.  */
520           fsetpos (ent->stream, &pos);
521           *errnop = ERANGE;
522           return NSS_STATUS_TRYAGAIN;
523         }
524
525       if (result->gr_name[0] != '+' && result->gr_name[0] != '-')
526         /* This is a real entry.  */
527         break;
528
529       /* -group */
530       if (result->gr_name[0] == '-' && result->gr_name[1] != '\0'
531           && result->gr_name[1] != '@')
532         {
533           blacklist_store_name (&result->gr_name[1], ent);
534           continue;
535         }
536
537       /* +group */
538       if (result->gr_name[0] == '+' && result->gr_name[1] != '\0'
539           && result->gr_name[1] != '@')
540         {
541           enum nss_status status;
542
543           /* Store the group in the blacklist for the "+" at the end of
544              /etc/group */
545           blacklist_store_name (&result->gr_name[1], ent);
546           status = getgrnam_plusgroup (&result->gr_name[1], result, buffer,
547                                        buflen, errnop);
548           if (status == NSS_STATUS_SUCCESS) /* We found the entry. */
549             break;
550           else
551             if (status == NSS_STATUS_RETURN /* We couldn't parse the entry */
552                 || status == NSS_STATUS_NOTFOUND) /* No group in NIS */
553               continue;
554             else
555               {
556                 if (status == NSS_STATUS_TRYAGAIN)
557                   {
558                     /* The parser ran out of space.  */
559                     fsetpos (ent->stream, &pos);
560                     *errnop = ERANGE;
561                   }
562                 return status;
563               }
564         }
565
566       /* +:... */
567       if (result->gr_name[0] == '+' && result->gr_name[1] == '\0')
568         {
569           ent->nis = TRUE;
570           ent->nis_first = TRUE;
571
572           if (use_nisplus)
573             return getgrent_next_nisplus (result, ent, buffer, buflen, errnop);
574           else
575             return getgrent_next_nis (result, ent, buffer, buflen, errnop);
576         }
577     }
578
579   return NSS_STATUS_SUCCESS;
580 }
581
582
583 static enum nss_status
584 internal_getgrent_r (struct group *gr, ent_t *ent, char *buffer,
585                      size_t buflen, int *errnop)
586 {
587   if (ent->nis)
588     {
589       if (use_nisplus)
590         return getgrent_next_nisplus (gr, ent, buffer, buflen, errnop);
591       else
592         return getgrent_next_nis (gr, ent, buffer, buflen, errnop);
593     }
594   else
595     return getgrent_next_file (gr, ent, buffer, buflen, errnop);
596 }
597
598 enum nss_status
599 _nss_compat_getgrent_r (struct group *grp, char *buffer, size_t buflen,
600                         int *errnop)
601 {
602   enum nss_status status = NSS_STATUS_SUCCESS;
603
604   __libc_lock_lock (lock);
605
606   /* Be prepared that the setgrent function was not called before.  */
607   if (ext_ent.stream == NULL)
608     status = internal_setgrent (&ext_ent);
609
610   if (status == NSS_STATUS_SUCCESS)
611     status = internal_getgrent_r (grp, &ext_ent, buffer, buflen, errnop);
612
613   __libc_lock_unlock (lock);
614
615   return status;
616 }
617
618 /* Searches in /etc/group and the NIS/NIS+ map for a special group */
619 static enum nss_status
620 internal_getgrnam_r (const char *name, struct group *result, ent_t *ent,
621                      char *buffer, size_t buflen, int *errnop)
622 {
623   struct parser_data *data = (void *) buffer;
624   while (1)
625     {
626       fpos_t pos;
627       int parse_res = 0;
628       char *p;
629
630       do
631         {
632           fgetpos (ent->stream, &pos);
633           buffer[buflen - 1] = '\xff';
634           p = fgets (buffer, buflen, ent->stream);
635           if (p == NULL && feof (ent->stream))
636             {
637               *errnop = ENOENT;
638               return NSS_STATUS_NOTFOUND;
639             }
640           if (p == NULL || buffer[buflen - 1] != '\xff')
641             {
642               fsetpos (ent->stream, &pos);
643               *errnop = ERANGE;
644               return NSS_STATUS_TRYAGAIN;
645             }
646
647           /* Terminate the line for any case.  */
648           buffer[buflen - 1] = '\0';
649
650           /* Skip leading blanks.  */
651           while (isspace (*p))
652             ++p;
653         }
654       while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
655       /* Parse the line.  If it is invalid, loop to
656          get the next line of the file to parse.  */
657              !(parse_res = _nss_files_parse_grent (p, result, data, buflen,
658                                                    errnop)));
659
660       if (parse_res == -1)
661         {
662           /* The parser ran out of space.  */
663           fsetpos (ent->stream, &pos);
664           *errnop = ERANGE;
665           return NSS_STATUS_TRYAGAIN;
666         }
667
668       /* This is a real entry.  */
669       if (result->gr_name[0] != '+' && result->gr_name[0] != '-')
670         {
671           if (strcmp (result->gr_name, name) == 0)
672             return NSS_STATUS_SUCCESS;
673           else
674             continue;
675         }
676
677       /* -group */
678       if (result->gr_name[0] == '-' && result->gr_name[1] != '\0')
679         {
680           if (strcmp (&result->gr_name[1], name) == 0)
681             {
682               *errnop = ENOENT;
683               return NSS_STATUS_NOTFOUND;
684             }
685           else
686             continue;
687         }
688
689       /* +group */
690       if (result->gr_name[0] == '+' && result->gr_name[1] != '\0')
691         {
692           if (strcmp (name, &result->gr_name[1]) == 0)
693             {
694               enum nss_status status;
695
696               status = getgrnam_plusgroup (name, result, buffer, buflen,
697                                            errnop);
698               if (status == NSS_STATUS_RETURN)
699                 /* We couldn't parse the entry */
700                 continue;
701               else
702                 return status;
703             }
704         }
705       /* +:... */
706       if (result->gr_name[0] == '+' && result->gr_name[1] == '\0')
707         {
708           enum nss_status status;
709
710           status = getgrnam_plusgroup (name, result, buffer, buflen, errnop);
711           if (status == NSS_STATUS_RETURN)
712             /* We couldn't parse the entry */
713             continue;
714           else
715             return status;
716         }
717     }
718
719   return NSS_STATUS_SUCCESS;
720 }
721
722 enum nss_status
723 _nss_compat_getgrnam_r (const char *name, struct group *grp,
724                         char *buffer, size_t buflen, int *errnop)
725 {
726   ent_t ent = {0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0}};
727   enum nss_status status;
728
729   if (name[0] == '-' || name[0] == '+')
730     return NSS_STATUS_NOTFOUND;
731
732   __libc_lock_lock (lock);
733
734   status = internal_setgrent (&ent);
735
736   __libc_lock_unlock (lock);
737
738   if (status != NSS_STATUS_SUCCESS)
739     return status;
740
741   status = internal_getgrnam_r (name, grp, &ent, buffer, buflen, errnop);
742
743   internal_endgrent (&ent);
744
745   return status;
746 }
747
748 /* This function handle the + entry in /etc/group */
749 static enum nss_status
750 getgrgid_plusgroup (gid_t gid, struct group *result, char *buffer,
751                     size_t buflen, int *errnop)
752 {
753   struct parser_data *data = (void *) buffer;
754   int parse_res;
755
756   if (use_nisplus) /* Do the NIS+ query here */
757     {
758       nis_result *res;
759       char buf[24 + grptablelen];
760
761       sprintf(buf, "[gid=%d],%s", gid, grptable);
762       res = nis_list(buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
763       if (niserr2nss (res->status) != NSS_STATUS_SUCCESS)
764         {
765           enum nss_status status =  niserr2nss (res->status);
766
767           nis_freeresult (res);
768           return status;
769         }
770       if ((parse_res = _nss_nisplus_parse_grent (res, 0, result, buffer,
771                                                  buflen, errnop)) == -1)
772         {
773           nis_freeresult (res);
774           *errnop = ERANGE;
775           return NSS_STATUS_TRYAGAIN;
776         }
777       nis_freeresult (res);
778     }
779   else /* Use NIS */
780     {
781       char buf[24];
782       char *domain, *outval, *p;
783       int outvallen;
784
785       if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
786         {
787           *errnop = errno;
788           return NSS_STATUS_TRYAGAIN;
789         }
790
791       snprintf (buf, sizeof (buf), "%d", gid);
792
793       if (yp_match (domain, "group.bygid", buf, strlen (buf),
794                     &outval, &outvallen) != YPERR_SUCCESS)
795         {
796           *errnop = errno;
797           return NSS_STATUS_TRYAGAIN;
798         }
799
800       if (buflen < ((size_t) outvallen + 1))
801         {
802           free (outval);
803           *errnop = ERANGE;
804           return NSS_STATUS_TRYAGAIN;
805         }
806
807       /* Copy the found data to our buffer...  */
808       p = strncpy (buffer, outval, buflen);
809
810       /* ... and free the data.  */
811       free (outval);
812
813       while (isspace (*p))
814         p++;
815       parse_res = _nss_files_parse_grent (p, result, data, buflen, errnop);
816       if (parse_res == -1)
817         return NSS_STATUS_TRYAGAIN;
818     }
819
820   if (parse_res)
821     /* We found the entry.  */
822     return NSS_STATUS_SUCCESS;
823   else
824     return NSS_STATUS_RETURN;
825 }
826
827 /* Searches in /etc/group and the NIS/NIS+ map for a special group id */
828 static enum nss_status
829 internal_getgrgid_r (gid_t gid, struct group *result, ent_t *ent,
830                      char *buffer, size_t buflen, int *errnop)
831 {
832   struct parser_data *data = (void *) buffer;
833   while (1)
834     {
835       fpos_t pos;
836       int parse_res = 0;
837       char *p;
838
839       do
840         {
841           fgetpos (ent->stream, &pos);
842           buffer[buflen - 1] = '\xff';
843           p = fgets (buffer, buflen, ent->stream);
844           if (p == NULL && feof (ent->stream))
845             {
846               *errnop = ENOENT;
847               return NSS_STATUS_NOTFOUND;
848             }
849           if (p == NULL || buffer[buflen - 1] != '\xff')
850             {
851               fsetpos (ent->stream, &pos);
852               *errnop = ERANGE;
853               return NSS_STATUS_TRYAGAIN;
854             }
855
856           /* Terminate the line for any case.  */
857           buffer[buflen - 1] = '\0';
858
859           /* Skip leading blanks.  */
860           while (isspace (*p))
861             ++p;
862         }
863       while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */
864       /* Parse the line.  If it is invalid, loop to
865          get the next line of the file to parse.  */
866              !(parse_res = _nss_files_parse_grent (p, result, data, buflen,
867                                                    errnop)));
868
869       if (parse_res == -1)
870         {
871           /* The parser ran out of space.  */
872           fsetpos (ent->stream, &pos);
873           *errnop = ERANGE;
874           return NSS_STATUS_TRYAGAIN;
875         }
876
877       /* This is a real entry.  */
878       if (result->gr_name[0] != '+' && result->gr_name[0] != '-')
879         {
880           if (result->gr_gid == gid)
881             return NSS_STATUS_SUCCESS;
882           else
883             continue;
884         }
885
886       /* -group */
887       if (result->gr_name[0] == '-' && result->gr_name[1] != '\0')
888         {
889           blacklist_store_name (&result->gr_name[1], ent);
890           continue;
891         }
892
893       /* +group */
894       if (result->gr_name[0] == '+' && result->gr_name[1] != '\0')
895         {
896           enum nss_status status;
897
898           /* Store the group in the blacklist for the "+" at the end of
899              /etc/group */
900           blacklist_store_name (&result->gr_name[1], ent);
901           status = getgrnam_plusgroup (&result->gr_name[1], result, buffer,
902                                       buflen, errnop);
903           if (status == NSS_STATUS_SUCCESS && result->gr_gid == gid)
904             break;
905           else
906             continue;
907         }
908       /* +:... */
909       if (result->gr_name[0] == '+' && result->gr_name[1] == '\0')
910         {
911           enum nss_status status;
912
913           status = getgrgid_plusgroup (gid, result, buffer, buflen, errnop);
914           if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
915             return NSS_STATUS_NOTFOUND;
916           else
917             return status;
918         }
919     }
920
921   return NSS_STATUS_SUCCESS;
922 }
923
924 enum nss_status
925 _nss_compat_getgrgid_r (gid_t gid, struct group *grp,
926                         char *buffer, size_t buflen, int *errnop)
927 {
928   ent_t ent = {0, 0, NULL, 0, NULL, NULL, {NULL, 0, 0}};
929   enum nss_status status;
930
931   __libc_lock_lock (lock);
932
933   status = internal_setgrent (&ent);
934
935   __libc_lock_unlock (lock);
936
937   if (status != NSS_STATUS_SUCCESS)
938     return status;
939
940   status = internal_getgrgid_r (gid, grp, &ent, buffer, buflen, errnop);
941
942   internal_endgrent (&ent);
943
944   return status;
945 }
946
947
948 /* Support routines for remembering -@netgroup and -user entries.
949    The names are stored in a single string with `|' as separator. */
950 static void
951 blacklist_store_name (const char *name, ent_t *ent)
952 {
953   int namelen = strlen (name);
954   char *tmp;
955
956   /* first call, setup cache */
957   if (ent->blacklist.size == 0)
958     {
959       ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
960       ent->blacklist.data = malloc (ent->blacklist.size);
961       if (ent->blacklist.data == NULL)
962         return;
963       ent->blacklist.data[0] = '|';
964       ent->blacklist.data[1] = '\0';
965       ent->blacklist.current = 1;
966     }
967   else
968     {
969       if (in_blacklist (name, namelen, ent))
970         return;                 /* no duplicates */
971
972       if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
973         {
974           ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
975           tmp = realloc (ent->blacklist.data, ent->blacklist.size);
976           if (tmp == NULL)
977             {
978               free (ent->blacklist.data);
979               ent->blacklist.size = 0;
980               return;
981             }
982           ent->blacklist.data = tmp;
983         }
984     }
985
986   tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
987   *tmp++ = '|';
988   *tmp = '\0';
989   ent->blacklist.current += namelen + 1;
990
991   return;
992 }
993
994 /* returns TRUE if ent->blacklist contains name, else FALSE */
995 static bool_t
996 in_blacklist (const char *name, int namelen, ent_t *ent)
997 {
998   char buf[namelen + 3];
999   char *cp;
1000
1001   if (ent->blacklist.data == NULL)
1002     return FALSE;
1003
1004   buf[0] = '|';
1005   cp = stpcpy (&buf[1], name);
1006   *cp++= '|';
1007   *cp = '\0';
1008   return strstr (ent->blacklist.data, buf) != NULL;
1009 }