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