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