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$");
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 = 0; 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	int rv;
663
664	assert(uname != NULL);
665	/* groups may be NULL if just sizing when invoked with maxgrp = 0 */
666	assert(grpcnt != NULL);
667
668	*grpcnt = 0;
669	rv = _nsdispatch(NULL, dtab, NSDB_GROUP, "getgroupmembership",
670	    defaultsrc, uname, agroup, groups, maxgrp, grpcnt);
671
672	/* too many groups found? */
673	return (*grpcnt > maxgrp ? -1 : 0);
674}
675
676
677static struct group	 grp;
678static char		*grp_storage;
679static size_t		 grp_storage_size;
680
681static struct group *
682getgr(int (*fn)(union key, struct group *, char *, size_t, struct group **),
683    union key key)
684{
685	int		 rv;
686	struct group	*res;
687
688	if (grp_storage == NULL) {
689		grp_storage = malloc(GRP_STORAGE_INITIAL);
690		if (grp_storage == NULL)
691			return (NULL);
692		grp_storage_size = GRP_STORAGE_INITIAL;
693	}
694	do {
695		rv = fn(key, &grp, grp_storage, grp_storage_size, &res);
696		if (res == NULL && rv == ERANGE) {
697			free(grp_storage);
698			if ((grp_storage_size << 1) > GRP_STORAGE_MAX) {
699				grp_storage = NULL;
700				errno = ERANGE;
701				return (NULL);
702			}
703			grp_storage_size <<= 1;
704			grp_storage = malloc(grp_storage_size);
705			if (grp_storage == NULL)
706				return (NULL);
707		}
708	} while (res == NULL && rv == ERANGE);
709	if (rv != 0)
710		errno = rv;
711	return (res);
712}
713
714
715static int
716wrap_getgrnam_r(union key key, struct group *grp, char *buffer, size_t bufsize,
717    struct group **res)
718{
719	return (getgrnam_r(key.name, grp, buffer, bufsize, res));
720}
721
722
723static int
724wrap_getgrgid_r(union key key, struct group *grp, char *buffer, size_t bufsize,
725    struct group **res)
726{
727	return (getgrgid_r(key.gid, grp, buffer, bufsize, res));
728}
729
730
731static int
732wrap_getgrent_r(union key key __unused, struct group *grp, char *buffer,
733    size_t bufsize, struct group **res)
734{
735	return (getgrent_r(grp, buffer, bufsize, res));
736}
737
738
739struct group *
740getgrnam(const char *name)
741{
742	union key key;
743
744	key.name = name;
745	return (getgr(wrap_getgrnam_r, key));
746}
747
748
749struct group *
750getgrgid(gid_t gid)
751{
752	union key key;
753
754	key.gid = gid;
755	return (getgr(wrap_getgrgid_r, key));
756}
757
758
759struct group *
760getgrent(void)
761{
762	union key key;
763
764	key.gid = 0; /* not used */
765	return (getgr(wrap_getgrent_r, key));
766}
767
768
769static int
770is_comment_line(const char *s, size_t n)
771{
772	const char	*eom;
773
774	eom = &s[n];
775
776	for (; s < eom; s++)
777		if (*s == '#' || !isspace((unsigned char)*s))
778			break;
779	return (*s == '#' || s == eom);
780}
781
782
783/*
784 * files backend
785 */
786static void
787files_endstate(void *p)
788{
789
790	if (p == NULL)
791		return;
792	if (((struct files_state *)p)->fp != NULL)
793		fclose(((struct files_state *)p)->fp);
794	free(p);
795}
796
797
798static int
799files_setgrent(void *retval, void *mdata, va_list ap)
800{
801	struct files_state *st;
802	int		 rv, stayopen;
803
804	rv = files_getstate(&st);
805	if (rv != 0)
806		return (NS_UNAVAIL);
807	switch ((enum constants)mdata) {
808	case SETGRENT:
809		stayopen = va_arg(ap, int);
810		if (st->fp != NULL)
811			rewind(st->fp);
812		else if (stayopen)
813			st->fp = fopen(_PATH_GROUP, "r");
814		break;
815	case ENDGRENT:
816		if (st->fp != NULL) {
817			fclose(st->fp);
818			st->fp = NULL;
819		}
820		break;
821	default:
822		break;
823	}
824	return (NS_UNAVAIL);
825}
826
827
828static int
829files_group(void *retval, void *mdata, va_list ap)
830{
831	struct files_state	*st;
832	enum nss_lookup_type	 how;
833	const char		*name, *line;
834	struct group		*grp;
835	gid_t			 gid;
836	char			*buffer;
837	size_t			 bufsize, linesize;
838	off_t			 pos;
839	int			 rv, stayopen, *errnop;
840
841	name = NULL;
842	gid = (gid_t)-1;
843	how = (enum nss_lookup_type)mdata;
844	switch (how) {
845	case nss_lt_name:
846		name = va_arg(ap, const char *);
847		break;
848	case nss_lt_id:
849		gid = va_arg(ap, gid_t);
850		break;
851	case nss_lt_all:
852		break;
853	default:
854		return (NS_NOTFOUND);
855	}
856	grp = va_arg(ap, struct group *);
857	buffer = va_arg(ap, char *);
858	bufsize = va_arg(ap, size_t);
859	errnop = va_arg(ap, int *);
860	*errnop = files_getstate(&st);
861	if (*errnop != 0)
862		return (NS_UNAVAIL);
863	if (st->fp == NULL &&
864	    ((st->fp = fopen(_PATH_GROUP, "r")) == NULL)) {
865		*errnop = errno;
866		return (NS_UNAVAIL);
867	}
868	if (how == nss_lt_all)
869		stayopen = 1;
870	else {
871		rewind(st->fp);
872		stayopen = st->stayopen;
873	}
874	rv = NS_NOTFOUND;
875	pos = ftello(st->fp);
876	while ((line = fgetln(st->fp, &linesize)) != NULL) {
877		if (line[linesize-1] == '\n')
878			linesize--;
879		rv = __gr_match_entry(line, linesize, how, name, gid);
880		if (rv != NS_SUCCESS)
881			continue;
882		/* We need room at least for the line, a string NUL
883		 * terminator, alignment padding, and one (char *)
884		 * pointer for the member list terminator.
885		 */
886		if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) {
887			*errnop = ERANGE;
888			rv = NS_RETURN;
889			break;
890		}
891		memcpy(buffer, line, linesize);
892		buffer[linesize] = '\0';
893		rv = __gr_parse_entry(buffer, linesize, grp,
894		    &buffer[linesize + 1], bufsize - linesize - 1, errnop);
895		if (rv & NS_TERMINATE)
896			break;
897		pos = ftello(st->fp);
898	}
899	if (!stayopen && st->fp != NULL) {
900		fclose(st->fp);
901		st->fp = NULL;
902	}
903	if (rv == NS_SUCCESS && retval != NULL)
904		*(struct group **)retval = grp;
905	else if (rv == NS_RETURN && *errnop == ERANGE && st->fp != NULL)
906		fseeko(st->fp, pos, SEEK_SET);
907	return (rv);
908}
909
910
911#ifdef HESIOD
912/*
913 * dns backend
914 */
915static void
916dns_endstate(void *p)
917{
918
919	free(p);
920}
921
922
923static int
924dns_setgrent(void *retval, void *cb_data, va_list ap)
925{
926	struct dns_state	*st;
927	int			 rv;
928
929	rv = dns_getstate(&st);
930	if (rv != 0)
931		return (NS_UNAVAIL);
932	st->counter = 0;
933	return (NS_UNAVAIL);
934}
935
936
937static int
938dns_group(void *retval, void *mdata, va_list ap)
939{
940	char			 buf[HESIOD_NAME_MAX];
941	struct dns_state	*st;
942	struct group		*grp;
943	const char		*name, *label;
944	void			*ctx;
945	char			*buffer, **hes;
946	size_t			 bufsize, adjsize, linesize;
947	gid_t			 gid;
948	enum nss_lookup_type	 how;
949	int			 rv, *errnop;
950
951	ctx = NULL;
952	hes = NULL;
953	name = NULL;
954	gid = (gid_t)-1;
955	how = (enum nss_lookup_type)mdata;
956	switch (how) {
957	case nss_lt_name:
958		name = va_arg(ap, const char *);
959		break;
960	case nss_lt_id:
961		gid = va_arg(ap, gid_t);
962		break;
963	case nss_lt_all:
964		break;
965	}
966	grp     = va_arg(ap, struct group *);
967	buffer  = va_arg(ap, char *);
968	bufsize = va_arg(ap, size_t);
969	errnop  = va_arg(ap, int *);
970	*errnop = dns_getstate(&st);
971	if (*errnop != 0)
972		return (NS_UNAVAIL);
973	if (hesiod_init(&ctx) != 0) {
974		*errnop = errno;
975		rv = NS_UNAVAIL;
976		goto fin;
977	}
978	do {
979		rv = NS_NOTFOUND;
980		switch (how) {
981		case nss_lt_name:
982			label = name;
983			break;
984		case nss_lt_id:
985			if (snprintf(buf, sizeof(buf), "%lu",
986			    (unsigned long)gid) >= sizeof(buf))
987				goto fin;
988			label = buf;
989			break;
990		case nss_lt_all:
991			if (st->counter < 0)
992				goto fin;
993			if (snprintf(buf, sizeof(buf), "group-%ld",
994			    st->counter++) >= sizeof(buf))
995				goto fin;
996			label = buf;
997			break;
998		}
999		hes = hesiod_resolve(ctx, label,
1000		    how == nss_lt_id ? "gid" : "group");
1001		if ((how == nss_lt_id && hes == NULL &&
1002		    (hes = hesiod_resolve(ctx, buf, "group")) == NULL) ||
1003		    hes == NULL) {
1004			if (how == nss_lt_all)
1005				st->counter = -1;
1006			if (errno != ENOENT)
1007				*errnop = errno;
1008			goto fin;
1009		}
1010		rv = __gr_match_entry(hes[0], strlen(hes[0]), how, name, gid);
1011		if (rv != NS_SUCCESS) {
1012			hesiod_free_list(ctx, hes);
1013			hes = NULL;
1014			continue;
1015		}
1016		/* We need room at least for the line, a string NUL
1017		 * terminator, alignment padding, and one (char *)
1018		 * pointer for the member list terminator.
1019		 */
1020		adjsize = bufsize - _ALIGNBYTES - sizeof(char *);
1021		linesize = strlcpy(buffer, hes[0], adjsize);
1022		if (linesize >= adjsize) {
1023			*errnop = ERANGE;
1024			rv = NS_RETURN;
1025			goto fin;
1026		}
1027		hesiod_free_list(ctx, hes);
1028		hes = NULL;
1029		rv = __gr_parse_entry(buffer, linesize, grp,
1030		    &buffer[linesize + 1], bufsize - linesize - 1, errnop);
1031	} while (how == nss_lt_all && !(rv & NS_TERMINATE));
1032fin:
1033	if (hes != NULL)
1034		hesiod_free_list(ctx, hes);
1035	if (ctx != NULL)
1036		hesiod_end(ctx);
1037	if (rv == NS_SUCCESS && retval != NULL)
1038		*(struct group **)retval = grp;
1039	return (rv);
1040}
1041#endif /* HESIOD */
1042
1043
1044#ifdef YP
1045/*
1046 * nis backend
1047 */
1048static void
1049nis_endstate(void *p)
1050{
1051
1052	if (p == NULL)
1053		return;
1054	free(((struct nis_state *)p)->key);
1055	free(p);
1056}
1057
1058
1059static int
1060nis_setgrent(void *retval, void *cb_data, va_list ap)
1061{
1062	struct nis_state	*st;
1063	int			 rv;
1064
1065	rv = nis_getstate(&st);
1066	if (rv != 0)
1067		return (NS_UNAVAIL);
1068	st->done = 0;
1069	free(st->key);
1070	st->key = NULL;
1071	return (NS_UNAVAIL);
1072}
1073
1074
1075static int
1076nis_group(void *retval, void *mdata, va_list ap)
1077{
1078	char		 *map;
1079	struct nis_state *st;
1080	struct group	*grp;
1081	const char	*name;
1082	char		*buffer, *key, *result;
1083	size_t		 bufsize;
1084	gid_t		 gid;
1085	enum nss_lookup_type how;
1086	int		*errnop, keylen, resultlen, rv;
1087
1088	name = NULL;
1089	gid = (gid_t)-1;
1090	how = (enum nss_lookup_type)mdata;
1091	switch (how) {
1092	case nss_lt_name:
1093		name = va_arg(ap, const char *);
1094		map = "group.byname";
1095		break;
1096	case nss_lt_id:
1097		gid = va_arg(ap, gid_t);
1098		map = "group.bygid";
1099		break;
1100	case nss_lt_all:
1101		map = "group.byname";
1102		break;
1103	}
1104	grp     = va_arg(ap, struct group *);
1105	buffer  = va_arg(ap, char *);
1106	bufsize = va_arg(ap, size_t);
1107	errnop  = va_arg(ap, int *);
1108	*errnop = nis_getstate(&st);
1109	if (*errnop != 0)
1110		return (NS_UNAVAIL);
1111	if (st->domain[0] == '\0') {
1112		if (getdomainname(st->domain, sizeof(st->domain)) != 0) {
1113			*errnop = errno;
1114			return (NS_UNAVAIL);
1115		}
1116	}
1117	result = NULL;
1118	do {
1119		rv = NS_NOTFOUND;
1120		switch (how) {
1121		case nss_lt_name:
1122			if (strlcpy(buffer, name, bufsize) >= bufsize)
1123				goto erange;
1124			break;
1125		case nss_lt_id:
1126			if (snprintf(buffer, bufsize, "%lu",
1127			    (unsigned long)gid) >= bufsize)
1128				goto erange;
1129			break;
1130		case nss_lt_all:
1131			if (st->done)
1132				goto fin;
1133			break;
1134		}
1135		result = NULL;
1136		if (how == nss_lt_all) {
1137			if (st->key == NULL)
1138				rv = yp_first(st->domain, map, &st->key,
1139				    &st->keylen, &result, &resultlen);
1140			else {
1141				key = st->key;
1142				keylen = st->keylen;
1143				st->key = NULL;
1144				rv = yp_next(st->domain, map, key, keylen,
1145				    &st->key, &st->keylen, &result,
1146				    &resultlen);
1147				free(key);
1148			}
1149			if (rv != 0) {
1150				free(result);
1151				free(st->key);
1152				st->key = NULL;
1153				if (rv == YPERR_NOMORE) {
1154					st->done = 1;
1155					rv = NS_NOTFOUND;
1156				} else
1157					rv = NS_UNAVAIL;
1158				goto fin;
1159			}
1160		} else {
1161			rv = yp_match(st->domain, map, buffer, strlen(buffer),
1162			    &result, &resultlen);
1163			if (rv == YPERR_KEY) {
1164				rv = NS_NOTFOUND;
1165				continue;
1166			} else if (rv != 0) {
1167				free(result);
1168				rv = NS_UNAVAIL;
1169				continue;
1170			}
1171		}
1172		/* We need room at least for the line, a string NUL
1173		 * terminator, alignment padding, and one (char *)
1174		 * pointer for the member list terminator.
1175		 */
1176		if (resultlen >= bufsize - _ALIGNBYTES - sizeof(char *))
1177			goto erange;
1178		memcpy(buffer, result, resultlen);
1179		buffer[resultlen] = '\0';
1180		free(result);
1181		rv = __gr_match_entry(buffer, resultlen, how, name, gid);
1182		if (rv == NS_SUCCESS)
1183			rv = __gr_parse_entry(buffer, resultlen, grp,
1184			    &buffer[resultlen+1], bufsize - resultlen - 1,
1185			    errnop);
1186	} while (how == nss_lt_all && !(rv & NS_TERMINATE));
1187fin:
1188	if (rv == NS_SUCCESS && retval != NULL)
1189		*(struct group **)retval = grp;
1190	return (rv);
1191erange:
1192	*errnop = ERANGE;
1193	return (NS_RETURN);
1194}
1195#endif /* YP */
1196
1197
1198
1199/*
1200 * compat backend
1201 */
1202static void
1203compat_endstate(void *p)
1204{
1205	struct compat_state *st;
1206
1207	if (p == NULL)
1208		return;
1209	st = (struct compat_state *)p;
1210	free(st->name);
1211	if (st->fp != NULL)
1212		fclose(st->fp);
1213	free(p);
1214}
1215
1216
1217static int
1218compat_setgrent(void *retval, void *mdata, va_list ap)
1219{
1220	static const ns_src compatsrc[] = {
1221#ifdef YP
1222		{ NSSRC_NIS, NS_SUCCESS },
1223#endif
1224		{ NULL, 0 }
1225	};
1226	ns_dtab dtab[] = {
1227#ifdef HESIOD
1228		{ NSSRC_DNS, dns_setgrent, NULL },
1229#endif
1230#ifdef YP
1231		{ NSSRC_NIS, nis_setgrent, NULL },
1232#endif
1233		{ NULL, NULL, NULL }
1234	};
1235	struct compat_state *st;
1236	int		 rv, stayopen;
1237
1238#define set_setent(x, y) do {	 				\
1239	int i;							\
1240								\
1241	for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++)	\
1242		x[i].mdata = (void *)y;				\
1243} while (0)
1244
1245	rv = compat_getstate(&st);
1246	if (rv != 0)
1247		return (NS_UNAVAIL);
1248	switch ((enum constants)mdata) {
1249	case SETGRENT:
1250		stayopen = va_arg(ap, int);
1251		if (st->fp != NULL)
1252			rewind(st->fp);
1253		else if (stayopen)
1254			st->fp = fopen(_PATH_GROUP, "r");
1255		set_setent(dtab, mdata);
1256		(void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgrent",
1257		    compatsrc, 0);
1258		break;
1259	case ENDGRENT:
1260		if (st->fp != NULL) {
1261			fclose(st->fp);
1262			st->fp = NULL;
1263		}
1264		set_setent(dtab, mdata);
1265		(void)_nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "endgrent",
1266		    compatsrc, 0);
1267		break;
1268	default:
1269		break;
1270	}
1271	st->compat = COMPAT_MODE_OFF;
1272	free(st->name);
1273	st->name = NULL;
1274	return (NS_UNAVAIL);
1275#undef set_setent
1276}
1277
1278
1279static int
1280compat_group(void *retval, void *mdata, va_list ap)
1281{
1282	static const ns_src compatsrc[] = {
1283#ifdef YP
1284		{ NSSRC_NIS, NS_SUCCESS },
1285#endif
1286		{ NULL, 0 }
1287	};
1288	ns_dtab dtab[] = {
1289#ifdef YP
1290		{ NSSRC_NIS, nis_group, NULL },
1291#endif
1292#ifdef HESIOD
1293		{ NSSRC_DNS, dns_group, NULL },
1294#endif
1295		{ NULL, NULL, NULL }
1296	};
1297	struct compat_state	*st;
1298	enum nss_lookup_type	 how;
1299	const char		*name, *line;
1300	struct group		*grp;
1301	gid_t			 gid;
1302	char			*buffer, *p;
1303	void			*discard;
1304	size_t			 bufsize, linesize;
1305	off_t			 pos;
1306	int			 rv, stayopen, *errnop;
1307
1308#define set_lookup_type(x, y) do { 				\
1309	int i;							\
1310								\
1311	for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++)	\
1312		x[i].mdata = (void *)y;				\
1313} while (0)
1314
1315	name = NULL;
1316	gid = (gid_t)-1;
1317	how = (enum nss_lookup_type)mdata;
1318	switch (how) {
1319	case nss_lt_name:
1320		name = va_arg(ap, const char *);
1321		break;
1322	case nss_lt_id:
1323		gid = va_arg(ap, gid_t);
1324		break;
1325	case nss_lt_all:
1326		break;
1327	default:
1328		return (NS_NOTFOUND);
1329	}
1330	grp = va_arg(ap, struct group *);
1331	buffer = va_arg(ap, char *);
1332	bufsize = va_arg(ap, size_t);
1333	errnop = va_arg(ap, int *);
1334	*errnop = compat_getstate(&st);
1335	if (*errnop != 0)
1336		return (NS_UNAVAIL);
1337	if (st->fp == NULL &&
1338	    ((st->fp = fopen(_PATH_GROUP, "r")) == NULL)) {
1339		*errnop = errno;
1340		rv = NS_UNAVAIL;
1341		goto fin;
1342	}
1343	if (how == nss_lt_all)
1344		stayopen = 1;
1345	else {
1346		rewind(st->fp);
1347		stayopen = st->stayopen;
1348	}
1349docompat:
1350	switch (st->compat) {
1351	case COMPAT_MODE_ALL:
1352		set_lookup_type(dtab, how);
1353		switch (how) {
1354		case nss_lt_all:
1355			rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
1356			    "getgrent_r", compatsrc, grp, buffer, bufsize,
1357			    errnop);
1358			break;
1359		case nss_lt_id:
1360			rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
1361			    "getgrgid_r", compatsrc, gid, grp, buffer, bufsize,
1362			    errnop);
1363			break;
1364		case nss_lt_name:
1365			rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
1366			    "getgrnam_r", compatsrc, name, grp, buffer,
1367			    bufsize, errnop);
1368			break;
1369		}
1370		if (rv & NS_TERMINATE)
1371			goto fin;
1372		st->compat = COMPAT_MODE_OFF;
1373		break;
1374	case COMPAT_MODE_NAME:
1375		set_lookup_type(dtab, nss_lt_name);
1376		rv = _nsdispatch(&discard, dtab, NSDB_GROUP_COMPAT,
1377		    "getgrnam_r", compatsrc, st->name, grp, buffer, bufsize,
1378		    errnop);
1379		switch (rv) {
1380		case NS_SUCCESS:
1381			switch (how) {
1382			case nss_lt_name:
1383				if (strcmp(name, grp->gr_name) != 0)
1384					rv = NS_NOTFOUND;
1385				break;
1386			case nss_lt_id:
1387				if (gid != grp->gr_gid)
1388					rv = NS_NOTFOUND;
1389				break;
1390			default:
1391				break;
1392			}
1393			break;
1394		case NS_RETURN:
1395			goto fin;
1396		default:
1397			break;
1398		}
1399		free(st->name);
1400		st->name = NULL;
1401		st->compat = COMPAT_MODE_OFF;
1402		if (rv == NS_SUCCESS)
1403			goto fin;
1404		break;
1405	default:
1406		break;
1407	}
1408	rv = NS_NOTFOUND;
1409	pos = ftello(st->fp);
1410	while ((line = fgetln(st->fp, &linesize)) != NULL) {
1411		if (line[linesize-1] == '\n')
1412			linesize--;
1413		if (linesize > 2 && line[0] == '+') {
1414			p = memchr(&line[1], ':', linesize);
1415			if (p == NULL || p == &line[1])
1416				st->compat = COMPAT_MODE_ALL;
1417			else {
1418				st->name = malloc(p - line);
1419				if (st->name == NULL) {
1420					syslog(LOG_ERR,
1421					 "getgrent memory allocation failure");
1422					*errnop = ENOMEM;
1423					rv = NS_UNAVAIL;
1424					break;
1425				}
1426				memcpy(st->name, &line[1], p - line - 1);
1427				st->name[p - line - 1] = '\0';
1428				st->compat = COMPAT_MODE_NAME;
1429			}
1430			goto docompat;
1431		}
1432		rv = __gr_match_entry(line, linesize, how, name, gid);
1433		if (rv != NS_SUCCESS)
1434			continue;
1435		/* We need room at least for the line, a string NUL
1436		 * terminator, alignment padding, and one (char *)
1437		 * pointer for the member list terminator.
1438		 */
1439		if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) {
1440			*errnop = ERANGE;
1441			rv = NS_RETURN;
1442			break;
1443		}
1444		memcpy(buffer, line, linesize);
1445		buffer[linesize] = '\0';
1446		rv = __gr_parse_entry(buffer, linesize, grp,
1447		    &buffer[linesize + 1], bufsize - linesize - 1, errnop);
1448		if (rv & NS_TERMINATE)
1449			break;
1450		pos = ftello(st->fp);
1451	}
1452fin:
1453	if (!stayopen && st->fp != NULL) {
1454		fclose(st->fp);
1455		st->fp = NULL;
1456	}
1457	if (rv == NS_SUCCESS && retval != NULL)
1458		*(struct group **)retval = grp;
1459	else if (rv == NS_RETURN && *errnop == ERANGE && st->fp != NULL)
1460		fseeko(st->fp, pos, SEEK_SET);
1461	return (rv);
1462#undef set_lookup_type
1463}
1464
1465
1466/*
1467 * common group line matching and parsing
1468 */
1469int
1470__gr_match_entry(const char *line, size_t linesize, enum nss_lookup_type how,
1471    const char *name, gid_t gid)
1472{
1473	size_t		 namesize;
1474	const char	*p, *eol;
1475	char		*q;
1476	unsigned long	 n;
1477	int		 i, needed;
1478
1479	if (linesize == 0 || is_comment_line(line, linesize))
1480		return (NS_NOTFOUND);
1481	switch (how) {
1482	case nss_lt_name:	needed = 1; break;
1483	case nss_lt_id:		needed = 2; break;
1484	default:		needed = 2; break;
1485	}
1486	eol = &line[linesize];
1487	for (p = line, i = 0; i < needed && p < eol; p++)
1488		if (*p == ':')
1489			i++;
1490	if (i < needed)
1491		return (NS_NOTFOUND);
1492	switch (how) {
1493	case nss_lt_name:
1494		namesize = strlen(name);
1495		if (namesize + 1 == (size_t)(p - line) &&
1496		    memcmp(line, name, namesize) == 0)
1497			return (NS_SUCCESS);
1498		break;
1499	case nss_lt_id:
1500		n = strtoul(p, &q, 10);
1501		if (q < eol && *q == ':' && gid == (gid_t)n)
1502			return (NS_SUCCESS);
1503		break;
1504	case nss_lt_all:
1505		return (NS_SUCCESS);
1506	default:
1507		break;
1508	}
1509	return (NS_NOTFOUND);
1510}
1511
1512
1513int
1514__gr_parse_entry(char *line, size_t linesize, struct group *grp, char *membuf,
1515    size_t membufsize, int *errnop)
1516{
1517	char	       *s_gid, *s_mem, *p, **members;
1518	unsigned long	n;
1519	int		maxmembers;
1520
1521	memset(grp, 0, sizeof(*grp));
1522	members = (char **)_ALIGN(membuf);
1523	membufsize -= (char *)members - membuf;
1524	maxmembers = membufsize / sizeof(*members);
1525	if (maxmembers <= 0 ||
1526	    (grp->gr_name = strsep(&line, ":")) == NULL ||
1527	    grp->gr_name[0] == '\0' ||
1528	    (grp->gr_passwd = strsep(&line, ":")) == NULL ||
1529	    (s_gid = strsep(&line, ":")) == NULL ||
1530	    s_gid[0] == '\0')
1531		return (NS_NOTFOUND);
1532	s_mem = line;
1533	n = strtoul(s_gid, &s_gid, 10);
1534	if (s_gid[0] != '\0')
1535		return (NS_NOTFOUND);
1536	grp->gr_gid = (gid_t)n;
1537	grp->gr_mem = members;
1538	while (maxmembers > 1 && s_mem != NULL) {
1539		p = strsep(&s_mem, ",");
1540		if (p != NULL && *p != '\0') {
1541			*members++ = p;
1542			maxmembers--;
1543		}
1544	}
1545	*members = NULL;
1546	if (s_mem == NULL)
1547		return (NS_SUCCESS);
1548	else {
1549		*errnop = ERANGE;
1550		return (NS_RETURN);
1551	}
1552}
1553
1554
1555