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