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