Merge branch 'vendor/TNFTP'
[dragonfly.git] / lib / libc / gen / getgrent.c
1 /*-
2  * Copyright (c) 2003 Networks Associates Technology, Inc.
3  * All rights reserved.
4  *
5  * This software was developed for the FreeBSD Project by
6  * Jacques A. Vidrine, Safeport Network Services, and Network
7  * Associates Laboratories, the Security Research Division of Network
8  * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
9  * ("CBOSS"), as part of the DARPA CHATS research program.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $FreeBSD: src/lib/libc/gen/getgrent.c,v 1.37 2007/12/12 10:08:02 bushman Exp $
33  */
34
35 #include "namespace.h"
36 #include <sys/param.h>
37 #ifdef YP
38 #include <rpc/rpc.h>
39 #include <rpcsvc/yp_prot.h>
40 #include <rpcsvc/ypclnt.h>
41 #endif
42 #include <assert.h>
43 #include <ctype.h>
44 #include <errno.h>
45 #ifdef HESIOD
46 #include <hesiod.h>
47 #endif
48 #include <grp.h>
49 #include <nsswitch.h>
50 #include <pthread.h>
51 #include <pthread_np.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <syslog.h>
56 #include <unistd.h>
57 #include "un-namespace.h"
58 #include "libc_private.h"
59 #include "nss_tls.h"
60 #ifdef NS_CACHING
61 #include "nscache.h"
62 #endif
63
64 enum constants {
65         GRP_STORAGE_INITIAL     = 1 << 10, /* 1 KByte */
66         GRP_STORAGE_MAX         = 1 << 20, /* 1 MByte */
67         SETGRENT                = 1,
68         ENDGRENT                = 2,
69         HESIOD_NAME_MAX         = 256,
70 };
71
72 static const ns_src defaultsrc[] = {
73         { NSSRC_COMPAT, NS_SUCCESS },
74         { NULL, 0 }
75 };
76
77 int      __gr_match_entry(const char *, size_t, enum nss_lookup_type,
78             const char *, gid_t);
79 int      __gr_parse_entry(char *, size_t, struct group *, char *, size_t,
80             int *);
81
82 static  int      is_comment_line(const char *, size_t);
83
84 union key {
85         const char      *name;
86         gid_t            gid;
87 };
88 static  struct group *getgr(int (*)(union key, struct group *, char *, size_t,
89                     struct group **), union key);
90 static  int      wrap_getgrnam_r(union key, struct group *, char *, size_t,
91                     struct group **);
92 static  int      wrap_getgrgid_r(union key, struct group *, char *, size_t,
93                     struct group **);
94 static  int      wrap_getgrent_r(union key, struct group *, char *, size_t,
95                     struct group **);
96
97 struct files_state {
98         FILE    *fp;
99         int      stayopen;
100 };
101 static  void     files_endstate(void *);
102 NSS_TLS_HANDLING(files);
103 static  int      files_setgrent(void *, void *, va_list);
104 static  int      files_group(void *, void *, va_list);
105
106
107 #ifdef HESIOD
108 struct dns_state {
109         long    counter;
110 };
111 static  void     dns_endstate(void *);
112 NSS_TLS_HANDLING(dns);
113 static  int      dns_setgrent(void *, void *, va_list);
114 static  int      dns_group(void *, void *, va_list);
115 #endif
116
117
118 #ifdef YP
119 struct nis_state {
120         char     domain[MAXHOSTNAMELEN];
121         int      done;
122         char    *key;
123         int      keylen;
124 };
125 static  void     nis_endstate(void *);
126 NSS_TLS_HANDLING(nis);
127 static  int      nis_setgrent(void *, void *, va_list);
128 static  int      nis_group(void *, void *, va_list);
129 #endif
130
131 struct compat_state {
132         FILE    *fp;
133         int      stayopen;
134         char    *name;
135         enum _compat {
136                 COMPAT_MODE_OFF = 0,
137                 COMPAT_MODE_ALL,
138                 COMPAT_MODE_NAME
139         }        compat;
140 };
141 static  void     compat_endstate(void *);
142 NSS_TLS_HANDLING(compat);
143 static  int      compat_setgrent(void *, void *, va_list);
144 static  int      compat_group(void *, void *, va_list);
145
146 static  int     gr_addgid(gid_t, gid_t *, int, int *);
147 static  int     getgroupmembership_fallback(void *, void *, va_list);
148
149 #ifdef NS_CACHING
150 static  int      grp_id_func(char *, size_t *, va_list, void *);
151 static  int      grp_marshal_func(char *, size_t *, void *, va_list, void *);
152 static  int      grp_unmarshal_func(char *, size_t, void *, va_list, void *);
153
154 static int
155 grp_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata)
156 {
157         char    *name;
158         gid_t   gid;
159
160         size_t  desired_size, size;
161         int     res = NS_UNAVAIL;
162         enum nss_lookup_type lookup_type;
163
164
165         lookup_type = (enum nss_lookup_type)cache_mdata;
166         switch (lookup_type) {
167         case nss_lt_name:
168                 name = va_arg(ap, char *);
169                 size = strlen(name);
170                 desired_size = sizeof(enum nss_lookup_type) + size + 1;
171                 if (desired_size > *buffer_size) {
172                         res = NS_RETURN;
173                         goto fin;
174                 }
175
176                 memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
177                 memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1);
178
179                 res = NS_SUCCESS;
180                 break;
181         case nss_lt_id:
182                 gid = va_arg(ap, gid_t);
183                 desired_size = sizeof(enum nss_lookup_type) + sizeof(gid_t);
184                 if (desired_size > *buffer_size) {
185                         res = NS_RETURN;
186                         goto fin;
187                 }
188
189                 memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
190                 memcpy(buffer + sizeof(enum nss_lookup_type), &gid,
191                     sizeof(gid_t));
192
193                 res = NS_SUCCESS;
194                 break;
195         default:
196                 /* should be unreachable */
197                 return (NS_UNAVAIL);
198         }
199
200 fin:
201         *buffer_size = desired_size;
202         return (res);
203 }
204
205 static int
206 grp_marshal_func(char *buffer, size_t *buffer_size, void *retval __unused,
207     va_list ap, void *cache_mdata)
208 {
209         char *name;
210         gid_t gid;
211         struct group *grp;
212         char *orig_buf;
213         size_t orig_buf_size;
214
215         struct group new_grp;
216         size_t desired_size, size, mem_size;
217         char *p, **mem;
218
219         switch ((enum nss_lookup_type)cache_mdata) {
220         case nss_lt_name:
221                 name = va_arg(ap, char *);
222                 break;
223         case nss_lt_id:
224                 gid = va_arg(ap, gid_t);
225                 break;
226         case nss_lt_all:
227                 break;
228         default:
229                 /* should be unreachable */
230                 return (NS_UNAVAIL);
231         }
232
233         grp = va_arg(ap, struct group *);
234         orig_buf = va_arg(ap, char *);
235         orig_buf_size = va_arg(ap, size_t);
236
237         desired_size = _ALIGNBYTES + sizeof(struct group) + sizeof(char *);
238
239         if (grp->gr_name != NULL)
240                 desired_size += strlen(grp->gr_name) + 1;
241         if (grp->gr_passwd != NULL)
242                 desired_size += strlen(grp->gr_passwd) + 1;
243
244         if (grp->gr_mem != NULL) {
245                 mem_size = 0;
246                 for (mem = grp->gr_mem; *mem; ++mem) {
247                         desired_size += strlen(*mem) + 1;
248                         ++mem_size;
249                 }
250
251                 desired_size += _ALIGNBYTES + (mem_size + 1) * sizeof(char *);
252         }
253
254         if (desired_size > *buffer_size) {
255                 /* this assignment is here for future use */
256                 *buffer_size = desired_size;
257                 return (NS_RETURN);
258         }
259
260         memcpy(&new_grp, grp, sizeof(struct group));
261         memset(buffer, 0, desired_size);
262
263         *buffer_size = desired_size;
264         p = buffer + sizeof(struct group) + sizeof(char *);
265         memcpy(buffer + sizeof(struct group), &p, sizeof(char *));
266         p = (char *)_ALIGN(p);
267
268         if (new_grp.gr_name != NULL) {
269                 size = strlen(new_grp.gr_name);
270                 memcpy(p, new_grp.gr_name, size);
271                 new_grp.gr_name = p;
272                 p += size + 1;
273         }
274
275         if (new_grp.gr_passwd != NULL) {
276                 size = strlen(new_grp.gr_passwd);
277                 memcpy(p, new_grp.gr_passwd, size);
278                 new_grp.gr_passwd = p;
279                 p += size + 1;
280         }
281
282         if (new_grp.gr_mem != NULL) {
283                 p = (char *)_ALIGN(p);
284                 memcpy(p, new_grp.gr_mem, sizeof(char *) * mem_size);
285                 new_grp.gr_mem = (char **)p;
286                 p += sizeof(char *) * (mem_size + 1);
287
288                 for (mem = new_grp.gr_mem; *mem; ++mem) {
289                         size = strlen(*mem);
290                         memcpy(p, *mem, size);
291                         *mem = p;
292                         p += size + 1;
293                 }
294         }
295
296         memcpy(buffer, &new_grp, sizeof(struct group));
297         return (NS_SUCCESS);
298 }
299
300 static int
301 grp_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
302     void *cache_mdata)
303 {
304         char *name;
305         gid_t gid;
306         struct group *grp;
307         char *orig_buf;
308         size_t orig_buf_size;
309         int *ret_errno;
310
311         char *p;
312         char **mem;
313
314         switch ((enum nss_lookup_type)cache_mdata) {
315         case nss_lt_name:
316                 name = va_arg(ap, char *);
317                 break;
318         case nss_lt_id:
319                 gid = va_arg(ap, gid_t);
320                 break;
321         case nss_lt_all:
322                 break;
323         default:
324                 /* should be unreachable */
325                 return (NS_UNAVAIL);
326         }
327
328         grp = va_arg(ap, struct group *);
329         orig_buf = va_arg(ap, char *);
330         orig_buf_size = va_arg(ap, size_t);
331         ret_errno = va_arg(ap, int *);
332
333         if (orig_buf_size <
334             buffer_size - sizeof(struct group) - sizeof(char *)) {
335                 *ret_errno = ERANGE;
336                 return (NS_RETURN);
337         }
338
339         memcpy(grp, buffer, sizeof(struct group));
340         memcpy(&p, buffer + sizeof(struct group), sizeof(char *));
341
342         orig_buf = (char *)_ALIGN(orig_buf);
343         memcpy(orig_buf, buffer + sizeof(struct group) + sizeof(char *) +
344             _ALIGN(p) - (size_t)p,
345             buffer_size - sizeof(struct group) - sizeof(char *) -
346             _ALIGN(p) + (size_t)p);
347         p = (char *)_ALIGN(p);
348
349         NS_APPLY_OFFSET(grp->gr_name, orig_buf, p, char *);
350         NS_APPLY_OFFSET(grp->gr_passwd, orig_buf, p, char *);
351         if (grp->gr_mem != NULL) {
352                 NS_APPLY_OFFSET(grp->gr_mem, orig_buf, p, char **);
353
354                 for (mem = grp->gr_mem; *mem; ++mem)
355                         NS_APPLY_OFFSET(*mem, orig_buf, p, char *);
356         }
357
358         if (retval != NULL)
359                 *((struct group **)retval) = grp;
360
361         return (NS_SUCCESS);
362 }
363
364 NSS_MP_CACHE_HANDLING(group);
365 #endif /* NS_CACHING */
366
367 #ifdef NS_CACHING
368 static const nss_cache_info setgrent_cache_info = NS_MP_CACHE_INFO_INITIALIZER(
369         group, (void *)nss_lt_all,
370         NULL, NULL);
371 #endif
372
373 static const ns_dtab setgrent_dtab[] = {
374         { NSSRC_FILES, files_setgrent, (void *)SETGRENT },
375 #ifdef HESIOD
376         { NSSRC_DNS, dns_setgrent, (void *)SETGRENT },
377 #endif
378 #ifdef YP
379         { NSSRC_NIS, nis_setgrent, (void *)SETGRENT },
380 #endif
381         { NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT },
382 #ifdef NS_CACHING
383         NS_CACHE_CB(&setgrent_cache_info)
384 #endif
385         { NULL, NULL, NULL }
386 };
387
388 #ifdef NS_CACHING
389 static const nss_cache_info endgrent_cache_info = NS_MP_CACHE_INFO_INITIALIZER(
390         group, (void *)nss_lt_all,
391         NULL, NULL);
392 #endif
393
394 static const ns_dtab endgrent_dtab[] = {
395         { NSSRC_FILES, files_setgrent, (void *)ENDGRENT },
396 #ifdef HESIOD
397         { NSSRC_DNS, dns_setgrent, (void *)ENDGRENT },
398 #endif
399 #ifdef YP
400         { NSSRC_NIS, nis_setgrent, (void *)ENDGRENT },
401 #endif
402         { NSSRC_COMPAT, compat_setgrent, (void *)ENDGRENT },
403 #ifdef NS_CACHING
404         NS_CACHE_CB(&endgrent_cache_info)
405 #endif
406         { NULL, NULL, NULL }
407 };
408
409 #ifdef NS_CACHING
410 static const nss_cache_info getgrent_r_cache_info = NS_MP_CACHE_INFO_INITIALIZER(
411         group, (void *)nss_lt_all,
412         grp_marshal_func, grp_unmarshal_func);
413 #endif
414
415 static const ns_dtab getgrent_r_dtab[] = {
416         { NSSRC_FILES, files_group, (void *)nss_lt_all },
417 #ifdef HESIOD
418         { NSSRC_DNS, dns_group, (void *)nss_lt_all },
419 #endif
420 #ifdef YP
421         { NSSRC_NIS, nis_group, (void *)nss_lt_all },
422 #endif
423         { NSSRC_COMPAT, compat_group, (void *)nss_lt_all },
424 #ifdef NS_CACHING
425         NS_CACHE_CB(&getgrent_r_cache_info)
426 #endif
427         { NULL, NULL, NULL }
428 };
429
430 static int
431 gr_addgid(gid_t gid, gid_t *groups, int maxgrp, int *grpcnt)
432 {
433         int     ret, dupc;
434
435         for (dupc = 0; dupc < MIN(maxgrp, *grpcnt); dupc++) {
436                 if (groups[dupc] == gid)
437                         return 1;
438         }
439
440         ret = 1;
441         if (*grpcnt < maxgrp)
442                 groups[*grpcnt] = gid;
443         else
444                 ret = 0;
445
446         (*grpcnt)++;
447
448         return ret;
449 }
450
451 static int
452 getgroupmembership_fallback(void *retval __unused, void *mdata, va_list ap)
453 {
454         const ns_src src[] = {
455                 { mdata, NS_SUCCESS },
456                 { NULL, 0}
457         };
458         struct group    grp;
459         struct group    *grp_p;
460         char            *buf;
461         size_t          bufsize;
462         const char      *uname;
463         gid_t           *groups;
464         gid_t           agroup;
465         int             maxgrp, *grpcnt;
466         int             i, rv, ret_errno;
467
468         /*
469          * As this is a fallback method, only provided src
470          * list will be respected during methods search.
471          */
472         assert(src[0].name != NULL);
473
474         uname = va_arg(ap, const char *);
475         agroup = va_arg(ap, gid_t);
476         groups = va_arg(ap, gid_t *);
477         maxgrp = va_arg(ap, int);
478         grpcnt = va_arg(ap, int *);
479
480         rv = NS_UNAVAIL;
481
482         buf = malloc(GRP_STORAGE_INITIAL);
483         if (buf == NULL)
484                 goto out;
485
486         bufsize = GRP_STORAGE_INITIAL;
487
488         gr_addgid(agroup, groups, maxgrp, grpcnt);
489
490         _nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", src, 0);
491         for (;;) {
492                 do {
493                         ret_errno = 0;
494                         grp_p = NULL;
495                         rv = _nsdispatch(&grp_p, getgrent_r_dtab, NSDB_GROUP,
496                             "getgrent_r", src, &grp, buf, bufsize, &ret_errno);
497
498                         if (grp_p == NULL && ret_errno == ERANGE) {
499                                 free(buf);
500                                 if ((bufsize << 1) > GRP_STORAGE_MAX) {
501                                         buf = NULL;
502                                         errno = ERANGE;
503                                         goto out;
504                                 }
505
506                                 bufsize <<= 1;
507                                 buf = malloc(bufsize);
508                                 if (buf == NULL) {
509                                         goto out;
510                                 }
511                         }
512                 } while (grp_p == NULL && ret_errno == ERANGE);
513
514                 if (ret_errno != 0) {
515                         errno = ret_errno;
516                         goto out;
517                 }
518
519                 if (grp_p == NULL)
520                         break;
521
522                 for (i = 0; grp.gr_mem[i]; i++) {
523                         if (strcmp(grp.gr_mem[i], uname) == 0)
524                             gr_addgid(grp.gr_gid, groups, maxgrp, grpcnt);
525                 }
526         }
527
528         _nsdispatch(NULL, endgrent_dtab, NSDB_GROUP, "endgrent", src);
529 out:
530         free(buf);
531         return (rv);
532 }
533
534 void
535 setgrent(void)
536 {
537         _nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", defaultsrc, 0);
538 }
539
540
541 int
542 setgroupent(int stayopen)
543 {
544         _nsdispatch(NULL, setgrent_dtab, NSDB_GROUP, "setgrent", defaultsrc,
545             stayopen);
546         return (1);
547 }
548
549
550 void
551 endgrent(void)
552 {
553         _nsdispatch(NULL, endgrent_dtab, NSDB_GROUP, "endgrent", defaultsrc);
554 }
555
556
557 int
558 getgrent_r(struct group *grp, char *buffer, size_t bufsize,
559     struct group **result)
560 {
561         int     rv, ret_errno;
562
563         ret_errno = 0;
564         *result = NULL;
565         rv = _nsdispatch(result, getgrent_r_dtab, NSDB_GROUP, "getgrent_r", defaultsrc,
566             grp, buffer, bufsize, &ret_errno);
567         if (rv == NS_SUCCESS)
568                 return (0);
569         else
570                 return (ret_errno);
571 }
572
573
574 int
575 getgrnam_r(const char *name, struct group *grp, char *buffer, size_t bufsize,
576     struct group **result)
577 {
578 #ifdef NS_CACHING
579         static const nss_cache_info cache_info =
580                 NS_COMMON_CACHE_INFO_INITIALIZER(
581                 group, (void *)nss_lt_name,
582                 grp_id_func, grp_marshal_func, grp_unmarshal_func);
583 #endif
584
585         static const ns_dtab dtab[] = {
586                 { NSSRC_FILES, files_group, (void *)nss_lt_name },
587 #ifdef HESIOD
588                 { NSSRC_DNS, dns_group, (void *)nss_lt_name },
589 #endif
590 #ifdef YP
591                 { NSSRC_NIS, nis_group, (void *)nss_lt_name },
592 #endif
593                 { NSSRC_COMPAT, compat_group, (void *)nss_lt_name },
594 #ifdef NS_CACHING
595                 NS_CACHE_CB(&cache_info)
596 #endif
597                 { NULL, NULL, NULL }
598         };
599         int     rv, ret_errno;
600
601         ret_errno = 0;
602         *result = NULL;
603         rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrnam_r", defaultsrc,
604             name, grp, buffer, bufsize, &ret_errno);
605         if (rv == NS_SUCCESS)
606                 return (0);
607         else
608                 return (ret_errno);
609 }
610
611
612 int
613 getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize,
614     struct group **result)
615 {
616 #ifdef NS_CACHING
617         static const nss_cache_info cache_info =
618                 NS_COMMON_CACHE_INFO_INITIALIZER(
619                 group, (void *)nss_lt_id,
620                 grp_id_func, grp_marshal_func, grp_unmarshal_func);
621 #endif
622
623         static const ns_dtab dtab[] = {
624                 { NSSRC_FILES, files_group, (void *)nss_lt_id },
625 #ifdef HESIOD
626                 { NSSRC_DNS, dns_group, (void *)nss_lt_id },
627 #endif
628 #ifdef YP
629                 { NSSRC_NIS, nis_group, (void *)nss_lt_id },
630 #endif
631                 { NSSRC_COMPAT, compat_group, (void *)nss_lt_id },
632 #ifdef NS_CACHING
633                 NS_CACHE_CB(&cache_info)
634 #endif
635                 { NULL, NULL, NULL }
636         };
637         int     rv, ret_errno;
638
639         ret_errno = 0;
640         *result = NULL;
641         rv = _nsdispatch(result, dtab, NSDB_GROUP, "getgrgid_r", defaultsrc,
642             gid, grp, buffer, bufsize, &ret_errno);
643         if (rv == NS_SUCCESS)
644                 return (0);
645         else
646                 return (ret_errno);
647 }
648
649
650
651 int
652 __getgroupmembership(const char *uname, gid_t agroup, gid_t *groups,
653         int maxgrp, int *grpcnt)
654 {
655         static const ns_dtab dtab[] = {
656                 NS_FALLBACK_CB(getgroupmembership_fallback)
657                 { NULL, NULL, NULL }
658         };
659         int rv;
660
661         assert(uname != NULL);
662         /* groups may be NULL if just sizing when invoked with maxgrp = 0 */
663         assert(grpcnt != NULL);
664
665         *grpcnt = 0;
666         rv = _nsdispatch(NULL, dtab, NSDB_GROUP, "getgroupmembership",
667             defaultsrc, uname, agroup, groups, maxgrp, grpcnt);
668
669         /* too many groups found? */
670         return (*grpcnt > maxgrp ? -1 : 0);
671 }
672
673
674 static struct group      grp;
675 static char             *grp_storage;
676 static size_t            grp_storage_size;
677
678 static struct group *
679 getgr(int (*fn)(union key, struct group *, char *, size_t, struct group **),
680     union key key)
681 {
682         int              rv;
683         struct group    *res;
684
685         if (grp_storage == NULL) {
686                 grp_storage = malloc(GRP_STORAGE_INITIAL);
687                 if (grp_storage == NULL)
688                         return (NULL);
689                 grp_storage_size = GRP_STORAGE_INITIAL;
690         }
691         do {
692                 rv = fn(key, &grp, grp_storage, grp_storage_size, &res);
693                 if (res == NULL && rv == ERANGE) {
694                         free(grp_storage);
695                         if ((grp_storage_size << 1) > GRP_STORAGE_MAX) {
696                                 grp_storage = NULL;
697                                 errno = ERANGE;
698                                 return (NULL);
699                         }
700                         grp_storage_size <<= 1;
701                         grp_storage = malloc(grp_storage_size);
702                         if (grp_storage == NULL)
703                                 return (NULL);
704                 }
705         } while (res == NULL && rv == ERANGE);
706         if (rv != 0)
707                 errno = rv;
708         return (res);
709 }
710
711
712 static int
713 wrap_getgrnam_r(union key key, struct group *grp, char *buffer, size_t bufsize,
714     struct group **res)
715 {
716         return (getgrnam_r(key.name, grp, buffer, bufsize, res));
717 }
718
719
720 static int
721 wrap_getgrgid_r(union key key, struct group *grp, char *buffer, size_t bufsize,
722     struct group **res)
723 {
724         return (getgrgid_r(key.gid, grp, buffer, bufsize, res));
725 }
726
727
728 static int
729 wrap_getgrent_r(union key key __unused, struct group *grp, char *buffer,
730     size_t bufsize, struct group **res)
731 {
732         return (getgrent_r(grp, buffer, bufsize, res));
733 }
734
735
736 struct group *
737 getgrnam(const char *name)
738 {
739         union key key;
740
741         key.name = name;
742         return (getgr(wrap_getgrnam_r, key));
743 }
744
745
746 struct group *
747 getgrgid(gid_t gid)
748 {
749         union key key;
750
751         key.gid = gid;
752         return (getgr(wrap_getgrgid_r, key));
753 }
754
755
756 struct group *
757 getgrent(void)
758 {
759         union key key;
760
761         key.gid = 0; /* not used */
762         return (getgr(wrap_getgrent_r, key));
763 }
764
765
766 static int
767 is_comment_line(const char *s, size_t n)
768 {
769         const char      *eom;
770
771         eom = &s[n];
772
773         for (; s < eom; s++)
774                 if (*s == '#' || !isspace((unsigned char)*s))
775                         break;
776         return (*s == '#' || s == eom);
777 }
778
779
780 /*
781  * files backend
782  */
783 static void
784 files_endstate(void *p)
785 {
786
787         if (p == NULL)
788                 return;
789         if (((struct files_state *)p)->fp != NULL)
790                 fclose(((struct files_state *)p)->fp);
791         free(p);
792 }
793
794
795 static int
796 files_setgrent(void *retval __unused, void *mdata, va_list ap)
797 {
798         struct files_state *st;
799         int              rv, stayopen;
800
801         rv = files_getstate(&st);
802         if (rv != 0)
803                 return (NS_UNAVAIL);
804         switch ((enum constants)mdata) {
805         case SETGRENT:
806                 stayopen = va_arg(ap, int);
807                 if (st->fp != NULL)
808                         rewind(st->fp);
809                 else if (stayopen)
810                         st->fp = fopen(_PATH_GROUP, "r");
811                 break;
812         case ENDGRENT:
813                 if (st->fp != NULL) {
814                         fclose(st->fp);
815                         st->fp = NULL;
816                 }
817                 break;
818         default:
819                 break;
820         }
821         return (NS_UNAVAIL);
822 }
823
824
825 static int
826 files_group(void *retval, void *mdata, va_list ap)
827 {
828         struct files_state      *st;
829         enum nss_lookup_type     how;
830         const char              *name, *line;
831         struct group            *grp;
832         gid_t                    gid;
833         char                    *buffer;
834         size_t                   bufsize, linesize;
835         off_t                    pos;
836         int                      rv, stayopen, *errnop;
837
838         name = NULL;
839         gid = (gid_t)-1;
840         how = (enum nss_lookup_type)mdata;
841         switch (how) {
842         case nss_lt_name:
843                 name = va_arg(ap, const char *);
844                 break;
845         case nss_lt_id:
846                 gid = va_arg(ap, gid_t);
847                 break;
848         case nss_lt_all:
849                 break;
850         default:
851                 return (NS_NOTFOUND);
852         }
853         grp = va_arg(ap, struct group *);
854         buffer = va_arg(ap, char *);
855         bufsize = va_arg(ap, size_t);
856         errnop = va_arg(ap, int *);
857         *errnop = files_getstate(&st);
858         if (*errnop != 0)
859                 return (NS_UNAVAIL);
860         if (st->fp == NULL &&
861             ((st->fp = fopen(_PATH_GROUP, "r")) == NULL)) {
862                 *errnop = errno;
863                 return (NS_UNAVAIL);
864         }
865         if (how == nss_lt_all)
866                 stayopen = 1;
867         else {
868                 rewind(st->fp);
869                 stayopen = st->stayopen;
870         }
871         rv = NS_NOTFOUND;
872         pos = ftello(st->fp);
873         while ((line = fgetln(st->fp, &linesize)) != NULL) {
874                 if (line[linesize-1] == '\n')
875                         linesize--;
876                 rv = __gr_match_entry(line, linesize, how, name, gid);
877                 if (rv != NS_SUCCESS)
878                         continue;
879                 /* We need room at least for the line, a string NUL
880                  * terminator, alignment padding, and one (char *)
881                  * pointer for the member list terminator.
882                  */
883                 if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) {
884                         *errnop = ERANGE;
885                         rv = NS_RETURN;
886                         break;
887                 }
888                 memcpy(buffer, line, linesize);
889                 buffer[linesize] = '\0';
890                 rv = __gr_parse_entry(buffer, linesize, grp,
891                     &buffer[linesize + 1], bufsize - linesize - 1, errnop);
892                 if (rv & NS_TERMINATE)
893                         break;
894                 pos = ftello(st->fp);
895         }
896         if (!stayopen && st->fp != NULL) {
897                 fclose(st->fp);
898                 st->fp = NULL;
899         }
900         if (rv == NS_SUCCESS && retval != NULL)
901                 *(struct group **)retval = grp;
902         else if (rv == NS_RETURN && *errnop == ERANGE && st->fp != NULL)
903                 fseeko(st->fp, pos, SEEK_SET);
904         return (rv);
905 }
906
907
908 #ifdef HESIOD
909 /*
910  * dns backend
911  */
912 static void
913 dns_endstate(void *p)
914 {
915
916         free(p);
917 }
918
919
920 static int
921 dns_setgrent(void *retval, void *cb_data, va_list ap)
922 {
923         struct dns_state        *st;
924         int                      rv;
925
926         rv = dns_getstate(&st);
927         if (rv != 0)
928                 return (NS_UNAVAIL);
929         st->counter = 0;
930         return (NS_UNAVAIL);
931 }
932
933
934 static int
935 dns_group(void *retval, void *mdata, va_list ap)
936 {
937         char                     buf[HESIOD_NAME_MAX];
938         struct dns_state        *st;
939         struct group            *grp;
940         const char              *name, *label;
941         void                    *ctx;
942         char                    *buffer, **hes;
943         size_t                   bufsize, adjsize, linesize;
944         gid_t                    gid;
945         enum nss_lookup_type     how;
946         int                      rv, *errnop;
947
948         ctx = NULL;
949         hes = NULL;
950         name = NULL;
951         gid = (gid_t)-1;
952         how = (enum nss_lookup_type)mdata;
953         switch (how) {
954         case nss_lt_name:
955                 name = va_arg(ap, const char *);
956                 break;
957         case nss_lt_id:
958                 gid = va_arg(ap, gid_t);
959                 break;
960         case nss_lt_all:
961                 break;
962         }
963         grp     = va_arg(ap, struct group *);
964         buffer  = va_arg(ap, char *);
965         bufsize = va_arg(ap, size_t);
966         errnop  = va_arg(ap, int *);
967         *errnop = dns_getstate(&st);
968         if (*errnop != 0)
969                 return (NS_UNAVAIL);
970         if (hesiod_init(&ctx) != 0) {
971                 *errnop = errno;
972                 rv = NS_UNAVAIL;
973                 goto fin;
974         }
975         do {
976                 rv = NS_NOTFOUND;
977                 switch (how) {
978                 case nss_lt_name:
979                         label = name;
980                         break;
981                 case nss_lt_id:
982                         if (snprintf(buf, sizeof(buf), "%lu",
983                             (unsigned long)gid) >= sizeof(buf))
984                                 goto fin;
985                         label = buf;
986                         break;
987                 case nss_lt_all:
988                         if (st->counter < 0)
989                                 goto fin;
990                         if (snprintf(buf, sizeof(buf), "group-%ld",
991                             st->counter++) >= sizeof(buf))
992                                 goto fin;
993                         label = buf;
994                         break;
995                 }
996                 hes = hesiod_resolve(ctx, label,
997                     how == nss_lt_id ? "gid" : "group");
998                 if ((how == nss_lt_id && hes == NULL &&
999                     (hes = hesiod_resolve(ctx, buf, "group")) == NULL) ||
1000                     hes == NULL) {
1001                         if (how == nss_lt_all)
1002                                 st->counter = -1;
1003                         if (errno != ENOENT)
1004                                 *errnop = errno;
1005                         goto fin;
1006                 }
1007                 rv = __gr_match_entry(hes[0], strlen(hes[0]), how, name, gid);
1008                 if (rv != NS_SUCCESS) {
1009                         hesiod_free_list(ctx, hes);
1010                         hes = NULL;
1011                         continue;
1012                 }
1013                 /* We need room at least for the line, a string NUL
1014                  * terminator, alignment padding, and one (char *)
1015                  * pointer for the member list terminator.
1016                  */
1017                 adjsize = bufsize - _ALIGNBYTES - sizeof(char *);
1018                 linesize = strlcpy(buffer, hes[0], adjsize);
1019                 if (linesize >= adjsize) {
1020                         *errnop = ERANGE;
1021                         rv = NS_RETURN;
1022                         goto fin;
1023                 }
1024                 hesiod_free_list(ctx, hes);
1025                 hes = NULL;
1026                 rv = __gr_parse_entry(buffer, linesize, grp,
1027                     &buffer[linesize + 1], bufsize - linesize - 1, errnop);
1028         } while (how == nss_lt_all && !(rv & NS_TERMINATE));
1029 fin:
1030         if (hes != NULL)
1031                 hesiod_free_list(ctx, hes);
1032         if (ctx != NULL)
1033                 hesiod_end(ctx);
1034         if (rv == NS_SUCCESS && retval != NULL)
1035                 *(struct group **)retval = grp;
1036         return (rv);
1037 }
1038 #endif /* HESIOD */
1039
1040
1041 #ifdef YP
1042 /*
1043  * nis backend
1044  */
1045 static void
1046 nis_endstate(void *p)
1047 {
1048
1049         if (p == NULL)
1050                 return;
1051         free(((struct nis_state *)p)->key);
1052         free(p);
1053 }
1054
1055
1056 static int
1057 nis_setgrent(void *retval __unused, void *cb_data __unused, va_list ap __unused)
1058 {
1059         struct nis_state        *st;
1060         int                      rv;
1061
1062         rv = nis_getstate(&st);
1063         if (rv != 0)
1064                 return (NS_UNAVAIL);
1065         st->done = 0;
1066         free(st->key);
1067         st->key = NULL;
1068         return (NS_UNAVAIL);
1069 }
1070
1071
1072 static int
1073 nis_group(void *retval, void *mdata, va_list ap)
1074 {
1075         char             *map;
1076         struct nis_state *st;
1077         struct group    *grp;
1078         const char      *name;
1079         char            *buffer, *key, *result;
1080         size_t           bufsize;
1081         gid_t            gid;
1082         enum nss_lookup_type how;
1083         int             *errnop, keylen, resultlen, rv;
1084
1085         name = NULL;
1086         gid = (gid_t)-1;
1087         how = (enum nss_lookup_type)mdata;
1088         switch (how) {
1089         case nss_lt_name:
1090                 name = va_arg(ap, const char *);
1091                 map = "group.byname";
1092                 break;
1093         case nss_lt_id:
1094                 gid = va_arg(ap, gid_t);
1095                 map = "group.bygid";
1096                 break;
1097         case nss_lt_all:
1098                 map = "group.byname";
1099                 break;
1100         }
1101         grp     = va_arg(ap, struct group *);
1102         buffer  = va_arg(ap, char *);
1103         bufsize = va_arg(ap, size_t);
1104         errnop  = va_arg(ap, int *);
1105         *errnop = nis_getstate(&st);
1106         if (*errnop != 0)
1107                 return (NS_UNAVAIL);
1108         if (st->domain[0] == '\0') {
1109                 if (getdomainname(st->domain, sizeof(st->domain)) != 0) {
1110                         *errnop = errno;
1111                         return (NS_UNAVAIL);
1112                 }
1113         }
1114         result = NULL;
1115         do {
1116                 rv = NS_NOTFOUND;
1117                 switch (how) {
1118                 case nss_lt_name:
1119                         if (strlcpy(buffer, name, bufsize) >= bufsize)
1120                                 goto erange;
1121                         break;
1122                 case nss_lt_id:
1123                         if (snprintf(buffer, bufsize, "%lu",
1124                             (unsigned long)gid) >= bufsize)
1125                                 goto erange;
1126                         break;
1127                 case nss_lt_all:
1128                         if (st->done)
1129                                 goto fin;
1130                         break;
1131                 }
1132                 result = NULL;
1133                 if (how == nss_lt_all) {
1134                         if (st->key == NULL)
1135                                 rv = yp_first(st->domain, map, &st->key,
1136                                     &st->keylen, &result, &resultlen);
1137                         else {
1138                                 key = st->key;
1139                                 keylen = st->keylen;
1140                                 st->key = NULL;
1141                                 rv = yp_next(st->domain, map, key, keylen,
1142                                     &st->key, &st->keylen, &result,
1143                                     &resultlen);
1144                                 free(key);
1145                         }
1146                         if (rv != 0) {
1147                                 free(result);
1148                                 free(st->key);
1149                                 st->key = NULL;
1150                                 if (rv == YPERR_NOMORE) {
1151                                         st->done = 1;
1152                                         rv = NS_NOTFOUND;
1153                                 } else
1154                                         rv = NS_UNAVAIL;
1155                                 goto fin;
1156                         }
1157                 } else {
1158                         rv = yp_match(st->domain, map, buffer, strlen(buffer),
1159                             &result, &resultlen);
1160                         if (rv == YPERR_KEY) {
1161                                 rv = NS_NOTFOUND;
1162                                 continue;
1163                         } else if (rv != 0) {
1164                                 free(result);
1165                                 rv = NS_UNAVAIL;
1166                                 continue;
1167                         }
1168                 }
1169                 /* We need room at least for the line, a string NUL
1170                  * terminator, alignment padding, and one (char *)
1171                  * pointer for the member list terminator.
1172                  */
1173                 if (resultlen >= bufsize - _ALIGNBYTES - sizeof(char *))
1174                         goto erange;
1175                 memcpy(buffer, result, resultlen);
1176                 buffer[resultlen] = '\0';
1177                 free(result);
1178                 rv = __gr_match_entry(buffer, resultlen, how, name, gid);
1179                 if (rv == NS_SUCCESS)
1180                         rv = __gr_parse_entry(buffer, resultlen, grp,
1181                             &buffer[resultlen+1], bufsize - resultlen - 1,
1182                             errnop);
1183         } while (how == nss_lt_all && !(rv & NS_TERMINATE));
1184 fin:
1185         if (rv == NS_SUCCESS && retval != NULL)
1186                 *(struct group **)retval = grp;
1187         return (rv);
1188 erange:
1189         *errnop = ERANGE;
1190         return (NS_RETURN);
1191 }
1192 #endif /* YP */
1193
1194
1195
1196 /*
1197  * compat backend
1198  */
1199 static void
1200 compat_endstate(void *p)
1201 {
1202         struct compat_state *st;
1203
1204         if (p == NULL)
1205                 return;
1206         st = (struct compat_state *)p;
1207         free(st->name);
1208         if (st->fp != NULL)
1209                 fclose(st->fp);
1210         free(p);
1211 }
1212
1213
1214 static int
1215 compat_setgrent(void *retval __unused, void *mdata, va_list ap)
1216 {
1217         static const ns_src compatsrc[] = {
1218 #ifdef YP
1219                 { NSSRC_NIS, NS_SUCCESS },
1220 #endif
1221                 { NULL, 0 }
1222         };
1223         ns_dtab dtab[] = {
1224 #ifdef HESIOD
1225                 { NSSRC_DNS, dns_setgrent, NULL },
1226 #endif
1227 #ifdef YP
1228                 { NSSRC_NIS, nis_setgrent, NULL },
1229 #endif
1230                 { NULL, NULL, NULL }
1231         };
1232         struct compat_state *st;
1233         int              rv, stayopen;
1234
1235 #define set_setent(x, y) do {                                   \
1236         int i;                                                  \
1237                                                                 \
1238         for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++)      \
1239                 x[i].mdata = (void *)y;                         \
1240 } while (0)
1241
1242         rv = compat_getstate(&st);
1243         if (rv != 0)
1244                 return (NS_UNAVAIL);
1245         switch ((enum constants)mdata) {
1246         case SETGRENT:
1247                 stayopen = va_arg(ap, int);
1248                 if (st->fp != NULL)
1249                         rewind(st->fp);
1250                 else if (stayopen)
1251                         st->fp = fopen(_PATH_GROUP, "r");
1252                 set_setent(dtab, mdata);
1253                 _nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgrent",
1254                     compatsrc, 0);
1255                 break;
1256         case ENDGRENT:
1257                 if (st->fp != NULL) {
1258                         fclose(st->fp);
1259                         st->fp = NULL;
1260                 }
1261                 set_setent(dtab, mdata);
1262                 _nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "endgrent",
1263                     compatsrc, 0);
1264                 break;
1265         default:
1266                 break;
1267         }
1268         st->compat = COMPAT_MODE_OFF;
1269         free(st->name);
1270         st->name = NULL;
1271         return (NS_UNAVAIL);
1272 #undef set_setent
1273 }
1274
1275
1276 static int
1277 compat_group(void *retval, void *mdata, va_list ap)
1278 {
1279         static const ns_src compatsrc[] = {
1280 #ifdef YP
1281                 { NSSRC_NIS, NS_SUCCESS },
1282 #endif
1283                 { NULL, 0 }
1284         };
1285         ns_dtab dtab[] = {
1286 #ifdef YP
1287                 { NSSRC_NIS, nis_group, NULL },
1288 #endif
1289 #ifdef HESIOD
1290                 { NSSRC_DNS, dns_group, NULL },
1291 #endif
1292                 { NULL, NULL, NULL }
1293         };
1294         struct compat_state     *st;
1295         enum nss_lookup_type     how;
1296         const char              *name, *line;
1297         struct group            *grp;
1298         gid_t                    gid;
1299         char                    *buffer, *p;
1300         void                    *discard;
1301         size_t                   bufsize, linesize;
1302         off_t                    pos;
1303         int                      rv, stayopen, *errnop;
1304
1305 #define set_lookup_type(x, y) do {                              \
1306         int i;                                                  \
1307                                                                 \
1308         for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++)      \
1309                 x[i].mdata = (void *)y;                         \
1310 } while (0)
1311
1312         name = NULL;
1313         gid = (gid_t)-1;
1314         how = (enum nss_lookup_type)mdata;
1315         switch (how) {
1316         case nss_lt_name:
1317                 name = va_arg(ap, const char *);
1318                 break;
1319         case nss_lt_id:
1320                 gid = va_arg(ap, gid_t);
1321                 break;
1322         case nss_lt_all:
1323                 break;
1324         default:
1325                 return (NS_NOTFOUND);
1326         }
1327         grp = va_arg(ap, struct group *);
1328         buffer = va_arg(ap, char *);
1329         bufsize = va_arg(ap, size_t);
1330         errnop = va_arg(ap, int *);
1331         *errnop = compat_getstate(&st);
1332         if (*errnop != 0)
1333                 return (NS_UNAVAIL);
1334         if (st->fp == NULL &&
1335             ((st->fp = fopen(_PATH_GROUP, "r")) == NULL)) {
1336                 *errnop = errno;
1337                 rv = NS_UNAVAIL;
1338                 goto fin;
1339         }
1340         if (how == nss_lt_all)
1341                 stayopen = 1;
1342         else {
1343                 rewind(st->fp);
1344                 stayopen = st->stayopen;
1345         }
1346 docompat:
1347         switch (st->compat) {
1348         case COMPAT_MODE_ALL:
1349                 set_lookup_type(dtab, how);
1350                 switch (how) {
1351                 case nss_lt_all:
1352                         rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
1353                             "getgrent_r", compatsrc, grp, buffer, bufsize,
1354                             errnop);
1355                         break;
1356                 case nss_lt_id:
1357                         rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
1358                             "getgrgid_r", compatsrc, gid, grp, buffer, bufsize,
1359                             errnop);
1360                         break;
1361                 case nss_lt_name:
1362                         rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
1363                             "getgrnam_r", compatsrc, name, grp, buffer,
1364                             bufsize, errnop);
1365                         break;
1366                 }
1367                 if (rv & NS_TERMINATE)
1368                         goto fin;
1369                 st->compat = COMPAT_MODE_OFF;
1370                 break;
1371         case COMPAT_MODE_NAME:
1372                 set_lookup_type(dtab, nss_lt_name);
1373                 rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
1374                     "getgrnam_r", compatsrc, st->name, grp, buffer, bufsize,
1375                     errnop);
1376                 switch (rv) {
1377                 case NS_SUCCESS:
1378                         switch (how) {
1379                         case nss_lt_name:
1380                                 if (strcmp(name, grp->gr_name) != 0)
1381                                         rv = NS_NOTFOUND;
1382                                 break;
1383                         case nss_lt_id:
1384                                 if (gid != grp->gr_gid)
1385                                         rv = NS_NOTFOUND;
1386                                 break;
1387                         default:
1388                                 break;
1389                         }
1390                         break;
1391                 case NS_RETURN:
1392                         goto fin;
1393                 default:
1394                         break;
1395                 }
1396                 free(st->name);
1397                 st->name = NULL;
1398                 st->compat = COMPAT_MODE_OFF;
1399                 if (rv == NS_SUCCESS)
1400                         goto fin;
1401                 break;
1402         default:
1403                 break;
1404         }
1405         rv = NS_NOTFOUND;
1406         pos = ftello(st->fp);
1407         while ((line = fgetln(st->fp, &linesize)) != NULL) {
1408                 if (line[linesize-1] == '\n')
1409                         linesize--;
1410                 if (linesize > 2 && line[0] == '+') {
1411                         p = memchr(&line[1], ':', linesize);
1412                         if (p == NULL || p == &line[1])
1413                                 st->compat = COMPAT_MODE_ALL;
1414                         else {
1415                                 st->name = malloc(p - line);
1416                                 if (st->name == NULL) {
1417                                         syslog(LOG_ERR,
1418                                          "getgrent memory allocation failure");
1419                                         *errnop = ENOMEM;
1420                                         rv = NS_UNAVAIL;
1421                                         break;
1422                                 }
1423                                 memcpy(st->name, &line[1], p - line - 1);
1424                                 st->name[p - line - 1] = '\0';
1425                                 st->compat = COMPAT_MODE_NAME;
1426                         }
1427                         goto docompat;
1428                 }
1429                 rv = __gr_match_entry(line, linesize, how, name, gid);
1430                 if (rv != NS_SUCCESS)
1431                         continue;
1432                 /* We need room at least for the line, a string NUL
1433                  * terminator, alignment padding, and one (char *)
1434                  * pointer for the member list terminator.
1435                  */
1436                 if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) {
1437                         *errnop = ERANGE;
1438                         rv = NS_RETURN;
1439                         break;
1440                 }
1441                 memcpy(buffer, line, linesize);
1442                 buffer[linesize] = '\0';
1443                 rv = __gr_parse_entry(buffer, linesize, grp,
1444                     &buffer[linesize + 1], bufsize - linesize - 1, errnop);
1445                 if (rv & NS_TERMINATE)
1446                         break;
1447                 pos = ftello(st->fp);
1448         }
1449 fin:
1450         if (!stayopen && st->fp != NULL) {
1451                 fclose(st->fp);
1452                 st->fp = NULL;
1453         }
1454         if (rv == NS_SUCCESS && retval != NULL)
1455                 *(struct group **)retval = grp;
1456         else if (rv == NS_RETURN && *errnop == ERANGE && st->fp != NULL)
1457                 fseeko(st->fp, pos, SEEK_SET);
1458         return (rv);
1459 #undef set_lookup_type
1460 }
1461
1462
1463 /*
1464  * common group line matching and parsing
1465  */
1466 int
1467 __gr_match_entry(const char *line, size_t linesize, enum nss_lookup_type how,
1468     const char *name, gid_t gid)
1469 {
1470         size_t           namesize;
1471         const char      *p, *eol;
1472         char            *q;
1473         unsigned long    n;
1474         int              i, needed;
1475
1476         if (linesize == 0 || is_comment_line(line, linesize))
1477                 return (NS_NOTFOUND);
1478         switch (how) {
1479         case nss_lt_name:       needed = 1; break;
1480         case nss_lt_id:         needed = 2; break;
1481         default:                needed = 2; break;
1482         }
1483         eol = &line[linesize];
1484         for (p = line, i = 0; i < needed && p < eol; p++)
1485                 if (*p == ':')
1486                         i++;
1487         if (i < needed)
1488                 return (NS_NOTFOUND);
1489         switch (how) {
1490         case nss_lt_name:
1491                 namesize = strlen(name);
1492                 if (namesize + 1 == (size_t)(p - line) &&
1493                     memcmp(line, name, namesize) == 0)
1494                         return (NS_SUCCESS);
1495                 break;
1496         case nss_lt_id:
1497                 n = strtoul(p, &q, 10);
1498                 if (q < eol && *q == ':' && gid == (gid_t)n)
1499                         return (NS_SUCCESS);
1500                 break;
1501         case nss_lt_all:
1502                 return (NS_SUCCESS);
1503         default:
1504                 break;
1505         }
1506         return (NS_NOTFOUND);
1507 }
1508
1509
1510 int
1511 __gr_parse_entry(char *line, size_t linesize __unused, struct group *grp,
1512     char *membuf, size_t membufsize, int *errnop)
1513 {
1514         char           *s_gid, *s_mem, *p, **members;
1515         unsigned long   n;
1516         int             maxmembers;
1517
1518         memset(grp, 0, sizeof(*grp));
1519         members = (char **)_ALIGN(membuf);
1520         membufsize -= (char *)members - membuf;
1521         maxmembers = membufsize / sizeof(*members);
1522         if (maxmembers <= 0 ||
1523             (grp->gr_name = strsep(&line, ":")) == NULL ||
1524             grp->gr_name[0] == '\0' ||
1525             (grp->gr_passwd = strsep(&line, ":")) == NULL ||
1526             (s_gid = strsep(&line, ":")) == NULL ||
1527             s_gid[0] == '\0')
1528                 return (NS_NOTFOUND);
1529         s_mem = line;
1530         n = strtoul(s_gid, &s_gid, 10);
1531         if (s_gid[0] != '\0')
1532                 return (NS_NOTFOUND);
1533         grp->gr_gid = (gid_t)n;
1534         grp->gr_mem = members;
1535         while (maxmembers > 1 && s_mem != NULL) {
1536                 p = strsep(&s_mem, ",");
1537                 if (p != NULL && *p != '\0') {
1538                         *members++ = p;
1539                         maxmembers--;
1540                 }
1541         }
1542         *members = NULL;
1543         if (s_mem == NULL)
1544                 return (NS_SUCCESS);
1545         else {
1546                 *errnop = ERANGE;
1547                 return (NS_RETURN);
1548         }
1549 }