1/*	$NetBSD: getgrent.c,v 1.66 2012/03/29 13:05:10 christos Exp $	*/
2
3/*-
4 * Copyright (c) 1999-2000, 2004-2005 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Luke Mewburn.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * Copyright (c) 1989, 1993
34 *	The Regents of the University of California.  All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 *    notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 *    notice, this list of conditions and the following disclaimer in the
43 *    documentation and/or other materials provided with the distribution.
44 * 3. Neither the name of the University nor the names of its contributors
45 *    may be used to endorse or promote products derived from this software
46 *    without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 */
60
61/*
62 * Portions Copyright (c) 1994, Jason Downs. All Rights Reserved.
63 *
64 * Redistribution and use in source and binary forms, with or without
65 * modification, are permitted provided that the following conditions
66 * are met:
67 * 1. Redistributions of source code must retain the above copyright
68 *    notice, this list of conditions and the following disclaimer.
69 * 2. Redistributions in binary form must reproduce the above copyright
70 *    notice, this list of conditions and the following disclaimer in the
71 *    documentation and/or other materials provided with the distribution.
72 *
73 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
74 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
75 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
76 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
77 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
78 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
79 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
80 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
81 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
82 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
83 * SUCH DAMAGE.
84 */
85
86#include <sys/cdefs.h>
87#if defined(LIBC_SCCS) && !defined(lint)
88#if 0
89static char sccsid[] = "@(#)getgrent.c	8.2 (Berkeley) 3/21/94";
90#else
91__RCSID("$NetBSD: getgrent.c,v 1.66 2012/03/29 13:05:10 christos Exp $");
92#endif
93#endif /* LIBC_SCCS and not lint */
94
95#include "namespace.h"
96#include "reentrant.h"
97
98#include <sys/param.h>
99
100#include <assert.h>
101#include <errno.h>
102#include <grp.h>
103#include <limits.h>
104#include <nsswitch.h>
105#include <stdarg.h>
106#include <stdio.h>
107#include <stdlib.h>
108#include <string.h>
109#include <syslog.h>
110
111#ifdef HESIOD
112#include <hesiod.h>
113#endif
114
115#ifdef YP
116#include <rpc/rpc.h>
117#include <rpcsvc/yp_prot.h>
118#include <rpcsvc/ypclnt.h>
119#endif
120
121#include "gr_private.h"
122
123#ifdef __weak_alias
124__weak_alias(endgrent,_endgrent)
125__weak_alias(getgrent,_getgrent)
126__weak_alias(getgrent_r,_getgrent_r)
127__weak_alias(getgrgid,_getgrgid)
128__weak_alias(getgrgid_r,_getgrgid_r)
129__weak_alias(getgrnam,_getgrnam)
130__weak_alias(getgrnam_r,_getgrnam_r)
131__weak_alias(setgrent,_setgrent)
132__weak_alias(setgroupent,_setgroupent)
133#endif
134
135#ifdef _REENTRANT
136mutex_t	__grmutex = MUTEX_INITIALIZER;
137#endif
138
139/*
140 * _gr_memfrombuf
141 *	Obtain want bytes from buffer (of size buflen) and return a pointer
142 *	to the available memory after adjusting buffer/buflen.
143 *	Returns NULL if there is insufficient space.
144 */
145static char *
146_gr_memfrombuf(size_t want, char **buffer, size_t *buflen)
147{
148	char	*rv;
149
150	if (want > *buflen) {
151		errno = ERANGE;
152		return NULL;
153	}
154	rv = *buffer;
155	*buffer += want;
156	*buflen -= want;
157	return rv;
158}
159
160/*
161 * _gr_parse
162 *	Parses entry as a line per group(5) (without the trailing \n)
163 *	and fills in grp with corresponding values; memory for strings
164 *	and arrays will be allocated from buf (of size buflen).
165 *	Returns 1 if parsed successfully, 0 on parse failure.
166 */
167static int
168_gr_parse(const char *entry, struct group *grp, char *buf, size_t buflen)
169{
170	unsigned long	id;
171	const char	*bp;
172	char		*ep;
173	size_t		count;
174	int		memc;
175
176	_DIAGASSERT(entry != NULL);
177	_DIAGASSERT(grp != NULL);
178	_DIAGASSERT(buf != NULL);
179
180#define COPYTOBUF(to) \
181	do { \
182		(to) = _gr_memfrombuf(count+1, &buf, &buflen); \
183		if ((to) == NULL) \
184			return 0; \
185		memmove((to), entry, count); \
186		to[count] = '\0'; \
187	} while (0)	/* LINTED */
188
189#if 0
190	if (*entry == '+')			/* fail on compat `+' token */
191		return 0;
192#endif
193
194	count = strcspn(entry, ":");		/* parse gr_name */
195	if (entry[count] == '\0')
196		return 0;
197	COPYTOBUF(grp->gr_name);
198	entry += count + 1;
199
200	count = strcspn(entry, ":");		/* parse gr_passwd */
201	if (entry[count] == '\0')
202		return 0;
203	COPYTOBUF(grp->gr_passwd);
204	entry += count + 1;
205
206	count = strcspn(entry, ":");		/* parse gr_gid */
207	if (entry[count] == '\0')
208		return 0;
209	id = strtoul(entry, &ep, 10);
210	if (id > GID_MAX || *ep != ':')
211		return 0;
212	grp->gr_gid = (gid_t)id;
213	entry += count + 1;
214
215	memc = 1;				/* for final NULL */
216	if (*entry != '\0')
217		memc++;				/* for first item */
218	for (bp = entry; *bp != '\0'; bp++) {
219		if (*bp == ',')
220			memc++;
221	}
222				/* grab ALIGNed char **gr_mem from buf */
223	ep = _gr_memfrombuf(memc * sizeof(char *) + ALIGNBYTES, &buf, &buflen);
224	if (ep == NULL)
225		return 0;
226	grp->gr_mem = (char **)ALIGN(ep);
227
228	for (memc = 0; *entry != '\0'; memc++) {
229		count = strcspn(entry, ",");	/* parse member */
230		COPYTOBUF(grp->gr_mem[memc]);
231		entry += count;
232		if (*entry == ',')
233			entry++;
234	}
235
236#undef COPYTOBUF
237
238	grp->gr_mem[memc] = NULL;
239	return 1;
240}
241
242/*
243 * _gr_copy
244 *	Copy the contents of fromgrp to grp; memory for strings
245 *	and arrays will be allocated from buf (of size buflen).
246 *	Returns 1 if copied successfully, 0 on copy failure.
247 *	NOTE: fromgrp must not use buf for its own pointers.
248 */
249static int
250_gr_copy(struct group *fromgrp, struct group *grp, char *buf, size_t buflen)
251{
252	char	*ep;
253	int	memc;
254
255	_DIAGASSERT(fromgrp != NULL);
256	_DIAGASSERT(grp != NULL);
257	_DIAGASSERT(buf != NULL);
258
259#define COPYSTR(to, from) \
260	do { \
261		size_t count = strlen((from)); \
262		(to) = _gr_memfrombuf(count+1, &buf, &buflen); \
263		if ((to) == NULL) \
264			return 0; \
265		memmove((to), (from), count); \
266		to[count] = '\0'; \
267	} while (0)	/* LINTED */
268
269	COPYSTR(grp->gr_name, fromgrp->gr_name);
270	COPYSTR(grp->gr_passwd, fromgrp->gr_passwd);
271	grp->gr_gid = fromgrp->gr_gid;
272
273	if (fromgrp->gr_mem == NULL)
274		return 0;
275
276	for (memc = 0; fromgrp->gr_mem[memc]; memc++)
277		continue;
278	memc++;					/* for final NULL */
279
280				/* grab ALIGNed char **gr_mem from buf */
281	ep = _gr_memfrombuf(memc * sizeof(char *) + ALIGNBYTES, &buf, &buflen);
282	grp->gr_mem = (char **)ALIGN(ep);
283	if (grp->gr_mem == NULL)
284		return 0;
285
286	for (memc = 0; fromgrp->gr_mem[memc]; memc++) {
287		COPYSTR(grp->gr_mem[memc], fromgrp->gr_mem[memc]);
288	}
289
290#undef COPYSTR
291
292	grp->gr_mem[memc] = NULL;
293	return 1;
294}
295
296		/*
297		 *	files methods
298		 */
299
300int
301__grstart_files(struct __grstate_files *state)
302{
303
304	_DIAGASSERT(state != NULL);
305
306	if (state->fp == NULL) {
307		state->fp = fopen(_PATH_GROUP, "re");
308		if (state->fp == NULL)
309			return NS_UNAVAIL;
310	} else {
311		rewind(state->fp);
312	}
313	return NS_SUCCESS;
314}
315
316int
317__grend_files(struct __grstate_files *state)
318{
319
320	_DIAGASSERT(state != NULL);
321
322	if (state->fp) {
323		(void) fclose(state->fp);
324		state->fp = NULL;
325	}
326	return NS_SUCCESS;
327}
328
329/*
330 * __grscan_files
331 *	Scan state->fp for the next desired entry.
332 *	If search is zero, return the next entry.
333 *	If search is non-zero, look for a specific name (if name != NULL),
334 *	or a specific gid (if name == NULL).
335 *	Sets *retval to the errno if the result is not NS_SUCCESS
336 *	or NS_NOTFOUND.
337 */
338int
339__grscan_files(int *retval, struct group *grp, char *buffer, size_t buflen,
340	struct __grstate_files *state, int search, const char *name, gid_t gid)
341{
342	int	rv;
343	char	filebuf[_GETGR_R_SIZE_MAX], *ep;
344
345	_DIAGASSERT(retval != NULL);
346	_DIAGASSERT(grp != NULL);
347	_DIAGASSERT(buffer != NULL);
348	_DIAGASSERT(state != NULL);
349	/* name is NULL to indicate searching for gid */
350
351	*retval = 0;
352
353	if (state->fp == NULL) {	/* only start if file not open yet */
354		rv = __grstart_files(state);
355		if (rv != NS_SUCCESS)
356			goto filesgrscan_out;
357	}
358
359	rv = NS_NOTFOUND;
360
361							/* scan line by line */
362	while (fgets(filebuf, (int)sizeof(filebuf), state->fp) != NULL) {
363		ep = strchr(filebuf, '\n');
364		if (ep == NULL) {	/* skip lines that are too big */
365			int ch;
366
367			while ((ch = getc(state->fp)) != '\n' && ch != EOF)
368				continue;
369			continue;
370		}
371		*ep = '\0';				/* clear trailing \n */
372
373		if (filebuf[0] == '+')			/* skip compat line */
374			continue;
375
376							/* validate line */
377		if (! _gr_parse(filebuf, grp, buffer, buflen)) {
378			continue;			/* skip bad lines */
379		}
380		if (! search) {				/* just want this one */
381			rv = NS_SUCCESS;
382			break;
383		}
384							/* want specific */
385		if ((name && strcmp(name, grp->gr_name) == 0) ||
386		    (!name && gid == grp->gr_gid)) {
387			rv = NS_SUCCESS;
388			break;
389		}
390	}
391
392 filesgrscan_out:
393	if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
394		*retval = errno;
395	return rv;
396}
397
398
399static struct __grstate_files	_files_state;
400					/* storage for non _r functions */
401static struct group		_files_group;
402static char			_files_groupbuf[_GETGR_R_SIZE_MAX];
403
404/*ARGSUSED*/
405static int
406_files_setgrent(void *nsrv, void *nscb, va_list ap)
407{
408
409	_files_state.stayopen = 0;
410	return __grstart_files(&_files_state);
411}
412
413/*ARGSUSED*/
414static int
415_files_setgroupent(void *nsrv, void *nscb, va_list ap)
416{
417	int	*retval		= va_arg(ap, int *);
418	int	 stayopen	= va_arg(ap, int);
419
420	int	rv;
421
422	_files_state.stayopen = stayopen;
423	rv = __grstart_files(&_files_state);
424	*retval = (rv == NS_SUCCESS);
425	return rv;
426}
427
428/*ARGSUSED*/
429static int
430_files_endgrent(void *nsrv, void *nscb, va_list ap)
431{
432
433	_files_state.stayopen = 0;
434	return __grend_files(&_files_state);
435}
436
437/*ARGSUSED*/
438static int
439_files_getgrent(void *nsrv, void *nscb, va_list ap)
440{
441	struct group	**retval = va_arg(ap, struct group **);
442
443	int	rv, rerror;
444
445	_DIAGASSERT(retval != NULL);
446
447	*retval = NULL;
448	rv = __grscan_files(&rerror, &_files_group,
449	    _files_groupbuf, sizeof(_files_groupbuf),
450	    &_files_state, 0, NULL, 0);
451	if (rv == NS_SUCCESS)
452		*retval = &_files_group;
453	return rv;
454}
455
456/*ARGSUSED*/
457static int
458_files_getgrent_r(void *nsrv, void *nscb, va_list ap)
459{
460	int		*retval	= va_arg(ap, int *);
461	struct group	*grp	= va_arg(ap, struct group *);
462	char		*buffer	= va_arg(ap, char *);
463	size_t		 buflen	= va_arg(ap, size_t);
464	struct group   **result	= va_arg(ap, struct group **);
465
466	int	rv;
467
468	_DIAGASSERT(retval != NULL);
469	_DIAGASSERT(grp != NULL);
470	_DIAGASSERT(buffer != NULL);
471	_DIAGASSERT(result != NULL);
472
473	rv = __grscan_files(retval, grp, buffer, buflen,
474	    &_files_state, 0, NULL, 0);
475	if (rv == NS_SUCCESS)
476		*result = grp;
477	else
478		*result = NULL;
479	return rv;
480}
481
482/*ARGSUSED*/
483static int
484_files_getgrgid(void *nsrv, void *nscb, va_list ap)
485{
486	struct group	**retval = va_arg(ap, struct group **);
487	gid_t		 gid	= va_arg(ap, gid_t);
488
489	int	rv, rerror;
490
491	_DIAGASSERT(retval != NULL);
492
493	*retval = NULL;
494	rv = __grstart_files(&_files_state);
495	if (rv != NS_SUCCESS)
496		return rv;
497	rv = __grscan_files(&rerror, &_files_group,
498	    _files_groupbuf, sizeof(_files_groupbuf),
499	    &_files_state, 1, NULL, gid);
500	if (!_files_state.stayopen)
501		__grend_files(&_files_state);
502	if (rv == NS_SUCCESS)
503		*retval = &_files_group;
504	return rv;
505}
506
507/*ARGSUSED*/
508static int
509_files_getgrgid_r(void *nsrv, void *nscb, va_list ap)
510{
511	int		*retval	= va_arg(ap, int *);
512	gid_t		 gid	= va_arg(ap, gid_t);
513	struct group	*grp	= va_arg(ap, struct group *);
514	char		*buffer	= va_arg(ap, char *);
515	size_t		 buflen	= va_arg(ap, size_t);
516	struct group   **result	= va_arg(ap, struct group **);
517
518	struct __grstate_files state;
519	int	rv;
520
521	_DIAGASSERT(retval != NULL);
522	_DIAGASSERT(grp != NULL);
523	_DIAGASSERT(buffer != NULL);
524	_DIAGASSERT(result != NULL);
525
526	*result = NULL;
527	memset(&state, 0, sizeof(state));
528	rv = __grscan_files(retval, grp, buffer, buflen, &state, 1, NULL, gid);
529	__grend_files(&state);
530	if (rv == NS_SUCCESS)
531		*result = grp;
532	return rv;
533}
534
535/*ARGSUSED*/
536static int
537_files_getgrnam(void *nsrv, void *nscb, va_list ap)
538{
539	struct group	**retval = va_arg(ap, struct group **);
540	const char	*name	= va_arg(ap, const char *);
541
542	int	rv, rerror;
543
544	_DIAGASSERT(retval != NULL);
545
546	*retval = NULL;
547	rv = __grstart_files(&_files_state);
548	if (rv != NS_SUCCESS)
549		return rv;
550	rv = __grscan_files(&rerror, &_files_group,
551	    _files_groupbuf, sizeof(_files_groupbuf),
552	    &_files_state, 1, name, 0);
553	if (!_files_state.stayopen)
554		__grend_files(&_files_state);
555	if (rv == NS_SUCCESS)
556		*retval = &_files_group;
557	return rv;
558}
559
560/*ARGSUSED*/
561static int
562_files_getgrnam_r(void *nsrv, void *nscb, va_list ap)
563{
564	int		*retval	= va_arg(ap, int *);
565	const char	*name	= va_arg(ap, const char *);
566	struct group	*grp	= va_arg(ap, struct group *);
567	char		*buffer	= va_arg(ap, char *);
568	size_t		 buflen	= va_arg(ap, size_t);
569	struct group   **result	= va_arg(ap, struct group **);
570
571	struct __grstate_files state;
572	int	rv;
573
574	_DIAGASSERT(retval != NULL);
575	_DIAGASSERT(grp != NULL);
576	_DIAGASSERT(buffer != NULL);
577	_DIAGASSERT(result != NULL);
578
579	*result = NULL;
580	memset(&state, 0, sizeof(state));
581	rv = __grscan_files(retval, grp, buffer, buflen, &state, 1, name, 0);
582	__grend_files(&state);
583	if (rv == NS_SUCCESS)
584		*result = grp;
585	return rv;
586}
587
588
589#ifdef HESIOD
590		/*
591		 *	dns methods
592		 */
593
594int
595__grstart_dns(struct __grstate_dns *state)
596{
597
598	_DIAGASSERT(state != NULL);
599
600	state->num = 0;
601	if (state->context == NULL) {			/* setup Hesiod */
602		if (hesiod_init(&state->context) == -1)
603			return NS_UNAVAIL;
604	}
605
606	return NS_SUCCESS;
607}
608
609int
610__grend_dns(struct __grstate_dns *state)
611{
612
613	_DIAGASSERT(state != NULL);
614
615	state->num = 0;
616	if (state->context) {
617		hesiod_end(state->context);
618		state->context = NULL;
619	}
620	return NS_SUCCESS;
621}
622
623/*
624 * __grscan_dns
625 *	Search Hesiod for the next desired entry.
626 *	If search is zero, return the next entry.
627 *	If search is non-zero, look for a specific name (if name != NULL),
628 *	or a specific gid (if name == NULL).
629 */
630int
631__grscan_dns(int *retval, struct group *grp, char *buffer, size_t buflen,
632	struct __grstate_dns *state, int search, const char *name, gid_t gid)
633{
634	const char	**curzone;
635	char		**hp, *ep;
636	int		rv;
637
638	static const char *zones_gid_group[] = {
639		"gid",
640		"group",
641		NULL
642	};
643
644	static const char *zones_group[] = {
645		"group",
646		NULL
647	};
648
649	_DIAGASSERT(retval != NULL);
650	_DIAGASSERT(grp != NULL);
651	_DIAGASSERT(buffer != NULL);
652	_DIAGASSERT(state != NULL);
653	/* name is NULL to indicate searching for gid */
654
655	*retval = 0;
656
657	if (state->context == NULL) {	/* only start if Hesiod not setup */
658		rv = __grstart_dns(state);
659		if (rv != NS_SUCCESS)
660			return rv;
661	}
662
663 next_dns_entry:
664	hp = NULL;
665	rv = NS_NOTFOUND;
666
667	if (! search) {			/* find next entry */
668		if (state->num == -1)		/* exhausted search */
669			return NS_NOTFOUND;
670						/* find group-NNN */
671		snprintf(buffer, buflen, "group-%u", state->num);
672		state->num++;
673		curzone = zones_group;
674	} else if (name) {		/* find group name */
675		snprintf(buffer, buflen, "%s", name);
676		curzone = zones_group;
677	} else {			/* find gid */
678		snprintf(buffer, buflen, "%u", (unsigned int)gid);
679		curzone = zones_gid_group;
680	}
681
682	for (; *curzone; curzone++) {		/* search zones */
683		hp = hesiod_resolve(state->context, buffer, *curzone);
684		if (hp != NULL)
685			break;
686		if (errno != ENOENT) {
687			rv = NS_UNAVAIL;
688			goto dnsgrscan_out;
689		}
690	}
691	if (*curzone == NULL) {
692		if (! search)
693			state->num = -1;
694		goto dnsgrscan_out;
695	}
696
697	if ((ep = strchr(hp[0], '\n')) != NULL)
698		*ep = '\0';				/* clear trailing \n */
699	if (_gr_parse(hp[0], grp, buffer, buflen)) {	/* validate line */
700		if (! search) {				/* just want this one */
701			rv = NS_SUCCESS;
702		} else if ((name && strcmp(name, grp->gr_name) == 0) ||
703		    (!name && gid == grp->gr_gid)) {	/* want specific */
704			rv = NS_SUCCESS;
705		}
706	} else {					/* dodgy entry */
707		if (!search) {			/* try again if ! searching */
708			hesiod_free_list(state->context, hp);
709			goto next_dns_entry;
710		}
711	}
712
713 dnsgrscan_out:
714	if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
715		*retval = errno;
716	if (hp)
717		hesiod_free_list(state->context, hp);
718	return rv;
719}
720
721static struct __grstate_dns	_dns_state;
722					/* storage for non _r functions */
723static struct group		_dns_group;
724static char			_dns_groupbuf[_GETGR_R_SIZE_MAX];
725
726/*ARGSUSED*/
727static int
728_dns_setgrent(void *nsrv, void *nscb, va_list ap)
729{
730
731	_dns_state.stayopen = 0;
732	return __grstart_dns(&_dns_state);
733}
734
735/*ARGSUSED*/
736static int
737_dns_setgroupent(void *nsrv, void *nscb, va_list ap)
738{
739	int	*retval		= va_arg(ap, int *);
740	int	 stayopen	= va_arg(ap, int);
741
742	int	rv;
743
744	_dns_state.stayopen = stayopen;
745	rv = __grstart_dns(&_dns_state);
746	*retval = (rv == NS_SUCCESS);
747	return rv;
748}
749
750/*ARGSUSED*/
751static int
752_dns_endgrent(void *nsrv, void *nscb, va_list ap)
753{
754
755	_dns_state.stayopen = 0;
756	return __grend_dns(&_dns_state);
757}
758
759/*ARGSUSED*/
760static int
761_dns_getgrent(void *nsrv, void *nscb, va_list ap)
762{
763	struct group	**retval = va_arg(ap, struct group **);
764
765	int	  rv, rerror;
766
767	_DIAGASSERT(retval != NULL);
768
769	*retval = NULL;
770	rv = __grscan_dns(&rerror, &_dns_group,
771	    _dns_groupbuf, sizeof(_dns_groupbuf), &_dns_state, 0, NULL, 0);
772	if (rv == NS_SUCCESS)
773		*retval = &_dns_group;
774	return rv;
775}
776
777/*ARGSUSED*/
778static int
779_dns_getgrent_r(void *nsrv, void *nscb, va_list ap)
780{
781	int		*retval	= va_arg(ap, int *);
782	struct group	*grp	= va_arg(ap, struct group *);
783	char		*buffer	= va_arg(ap, char *);
784	size_t		 buflen	= va_arg(ap, size_t);
785	struct group   **result	= va_arg(ap, struct group **);
786
787	int	rv;
788
789	_DIAGASSERT(retval != NULL);
790	_DIAGASSERT(grp != NULL);
791	_DIAGASSERT(buffer != NULL);
792	_DIAGASSERT(result != NULL);
793
794	rv = __grscan_dns(retval, grp, buffer, buflen,
795	    &_dns_state, 0, NULL, 0);
796	if (rv == NS_SUCCESS)
797		*result = grp;
798	else
799		*result = NULL;
800	return rv;
801}
802/*ARGSUSED*/
803static int
804_dns_getgrgid(void *nsrv, void *nscb, va_list ap)
805{
806	struct group	**retval = va_arg(ap, struct group **);
807	gid_t		 gid	= va_arg(ap, gid_t);
808
809	int	rv, rerror;
810
811	_DIAGASSERT(retval != NULL);
812
813	*retval = NULL;
814	rv = __grstart_dns(&_dns_state);
815	if (rv != NS_SUCCESS)
816		return rv;
817	rv = __grscan_dns(&rerror, &_dns_group,
818	    _dns_groupbuf, sizeof(_dns_groupbuf), &_dns_state, 1, NULL, gid);
819	if (!_dns_state.stayopen)
820		__grend_dns(&_dns_state);
821	if (rv == NS_SUCCESS)
822		*retval = &_dns_group;
823	return rv;
824}
825
826/*ARGSUSED*/
827static int
828_dns_getgrgid_r(void *nsrv, void *nscb, va_list ap)
829{
830	int		*retval	= va_arg(ap, int *);
831	gid_t		 gid	= va_arg(ap, gid_t);
832	struct group	*grp	= va_arg(ap, struct group *);
833	char		*buffer	= va_arg(ap, char *);
834	size_t		 buflen	= va_arg(ap, size_t);
835	struct group   **result	= va_arg(ap, struct group **);
836
837	struct __grstate_dns state;
838	int	rv;
839
840	_DIAGASSERT(retval != NULL);
841	_DIAGASSERT(grp != NULL);
842	_DIAGASSERT(buffer != NULL);
843	_DIAGASSERT(result != NULL);
844
845	*result = NULL;
846	memset(&state, 0, sizeof(state));
847	rv = __grscan_dns(retval, grp, buffer, buflen, &state, 1, NULL, gid);
848	__grend_dns(&state);
849	if (rv == NS_SUCCESS)
850		*result = grp;
851	return rv;
852}
853
854/*ARGSUSED*/
855static int
856_dns_getgrnam(void *nsrv, void *nscb, va_list ap)
857{
858	struct group	**retval = va_arg(ap, struct group **);
859	const char	*name	= va_arg(ap, const char *);
860
861	int	rv, rerror;
862
863	_DIAGASSERT(retval != NULL);
864
865	*retval = NULL;
866	rv = __grstart_dns(&_dns_state);
867	if (rv != NS_SUCCESS)
868		return rv;
869	rv = __grscan_dns(&rerror, &_dns_group,
870	    _dns_groupbuf, sizeof(_dns_groupbuf), &_dns_state, 1, name, 0);
871	if (!_dns_state.stayopen)
872		__grend_dns(&_dns_state);
873	if (rv == NS_SUCCESS)
874		*retval = &_dns_group;
875	return rv;
876}
877
878/*ARGSUSED*/
879static int
880_dns_getgrnam_r(void *nsrv, void *nscb, va_list ap)
881{
882	int		*retval	= va_arg(ap, int *);
883	const char	*name	= va_arg(ap, const char *);
884	struct group	*grp	= va_arg(ap, struct group *);
885	char		*buffer	= va_arg(ap, char *);
886	size_t		 buflen	= va_arg(ap, size_t);
887	struct group   **result	= va_arg(ap, struct group **);
888
889	struct __grstate_dns state;
890	int	rv;
891
892	_DIAGASSERT(retval != NULL);
893	_DIAGASSERT(grp != NULL);
894	_DIAGASSERT(buffer != NULL);
895	_DIAGASSERT(result != NULL);
896
897	*result = NULL;
898	memset(&state, 0, sizeof(state));
899	rv = __grscan_dns(retval, grp, buffer, buflen, &state, 1, name, 0);
900	__grend_dns(&state);
901	if (rv == NS_SUCCESS)
902		*result = grp;
903	return rv;
904}
905
906#endif /* HESIOD */
907
908
909#ifdef YP
910		/*
911		 *	nis methods
912		 */
913
914int
915__grstart_nis(struct __grstate_nis *state)
916{
917
918	_DIAGASSERT(state != NULL);
919
920	state->done = 0;
921	if (state->current) {
922		free(state->current);
923		state->current = NULL;
924	}
925	if (state->domain == NULL) {			/* setup NIS */
926		switch (yp_get_default_domain(&state->domain)) {
927		case 0:
928			break;
929		case YPERR_RESRC:
930			return NS_TRYAGAIN;
931		default:
932			return NS_UNAVAIL;
933		}
934	}
935	return NS_SUCCESS;
936}
937
938int
939__grend_nis(struct __grstate_nis *state)
940{
941
942	_DIAGASSERT(state != NULL);
943
944	if (state->domain) {
945		state->domain = NULL;
946	}
947	state->done = 0;
948	if (state->current) {
949		free(state->current);
950		state->current = NULL;
951	}
952	return NS_SUCCESS;
953}
954
955/*
956 * __grscan_nis
957 *	Search NIS for the next desired entry.
958 *	If search is zero, return the next entry.
959 *	If search is non-zero, look for a specific name (if name != NULL),
960 *	or a specific gid (if name == NULL).
961 */
962int
963__grscan_nis(int *retval, struct group *grp, char *buffer, size_t buflen,
964	struct __grstate_nis *state, int search, const char *name, gid_t gid)
965{
966	const char *map;
967	char	*key, *data;
968	int	nisr, rv, keylen, datalen;
969
970	_DIAGASSERT(retval != NULL);
971	_DIAGASSERT(grp != NULL);
972	_DIAGASSERT(buffer != NULL);
973	_DIAGASSERT(state != NULL);
974	/* name is NULL to indicate searching for gid */
975
976	*retval = 0;
977
978	if (state->domain == NULL) {	/* only start if NIS not setup */
979		rv = __grstart_nis(state);
980		if (rv != NS_SUCCESS)
981			return rv;
982	}
983
984 next_nis_entry:
985	key = NULL;
986	data = NULL;
987	rv = NS_SUCCESS;
988
989	if (! search) 	{			/* find next entry */
990		if (state->done)			/* exhausted search */
991			return NS_NOTFOUND;
992		map = "group.byname";
993		if (state->current) {			/* already searching */
994			nisr = yp_next(state->domain, map,
995			    state->current, state->currentlen,
996			    &key, &keylen, &data, &datalen);
997			free(state->current);
998			state->current = NULL;
999			switch (nisr) {
1000			case 0:
1001				state->current = key;
1002				state->currentlen = keylen;
1003				key = NULL;
1004				break;
1005			case YPERR_NOMORE:
1006				rv = NS_NOTFOUND;
1007				state->done = 1;
1008				break;
1009			default:
1010				rv = NS_UNAVAIL;
1011				break;
1012			}
1013		} else {				/* new search */
1014			if (yp_first(state->domain, map,
1015			    &state->current, &state->currentlen,
1016			    &data, &datalen)) {
1017				rv = NS_UNAVAIL;
1018			}
1019		}
1020	} else {				/* search for specific item */
1021		if (name) {			/* find group name */
1022			snprintf(buffer, buflen, "%s", name);
1023			map = "group.byname";
1024		} else {			/* find gid */
1025			snprintf(buffer, buflen, "%u", (unsigned int)gid);
1026			map = "group.bygid";
1027		}
1028		nisr = yp_match(state->domain, map, buffer, (int)strlen(buffer),
1029		    &data, &datalen);
1030		switch (nisr) {
1031		case 0:
1032			break;
1033		case YPERR_KEY:
1034			rv = NS_NOTFOUND;
1035			break;
1036		default:
1037			rv = NS_UNAVAIL;
1038			break;
1039		}
1040	}
1041	if (rv == NS_SUCCESS) {				/* validate data */
1042		data[datalen] = '\0';			/* clear trailing \n */
1043		if (_gr_parse(data, grp, buffer, buflen)) {
1044			if (! search) {			/* just want this one */
1045				rv = NS_SUCCESS;
1046			} else if ((name && strcmp(name, grp->gr_name) == 0) ||
1047			    (!name && gid == grp->gr_gid)) {
1048							/* want specific */
1049				rv = NS_SUCCESS;
1050			}
1051		} else {				/* dodgy entry */
1052			if (!search) {		/* try again if ! searching */
1053				free(data);
1054				goto next_nis_entry;
1055			}
1056		}
1057	}
1058
1059	if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
1060		*retval = errno;
1061	if (key)
1062		free(key);
1063	if (data)
1064		free(data);
1065	return rv;
1066}
1067
1068static struct __grstate_nis	_nis_state;
1069					/* storage for non _r functions */
1070static struct group		_nis_group;
1071static char			_nis_groupbuf[_GETGR_R_SIZE_MAX];
1072
1073/*ARGSUSED*/
1074static int
1075_nis_setgrent(void *nsrv, void *nscb, va_list ap)
1076{
1077
1078	_nis_state.stayopen = 0;
1079	return __grstart_nis(&_nis_state);
1080}
1081
1082/*ARGSUSED*/
1083static int
1084_nis_setgroupent(void *nsrv, void *nscb, va_list ap)
1085{
1086	int	*retval		= va_arg(ap, int *);
1087	int	 stayopen	= va_arg(ap, int);
1088
1089	int	rv;
1090
1091	_nis_state.stayopen = stayopen;
1092	rv = __grstart_nis(&_nis_state);
1093	*retval = (rv == NS_SUCCESS);
1094	return rv;
1095}
1096
1097/*ARGSUSED*/
1098static int
1099_nis_endgrent(void *nsrv, void *nscb, va_list ap)
1100{
1101
1102	return __grend_nis(&_nis_state);
1103}
1104
1105/*ARGSUSED*/
1106static int
1107_nis_getgrent(void *nsrv, void *nscb, va_list ap)
1108{
1109	struct group	**retval = va_arg(ap, struct group **);
1110
1111	int	rv, rerror;
1112
1113	_DIAGASSERT(retval != NULL);
1114
1115	*retval = NULL;
1116	rv = __grscan_nis(&rerror, &_nis_group,
1117	    _nis_groupbuf, sizeof(_nis_groupbuf), &_nis_state, 0, NULL, 0);
1118	if (rv == NS_SUCCESS)
1119		*retval = &_nis_group;
1120	return rv;
1121}
1122
1123/*ARGSUSED*/
1124static int
1125_nis_getgrent_r(void *nsrv, void *nscb, va_list ap)
1126{
1127	int		*retval	= va_arg(ap, int *);
1128	struct group	*grp	= va_arg(ap, struct group *);
1129	char		*buffer	= va_arg(ap, char *);
1130	size_t		 buflen	= va_arg(ap, size_t);
1131	struct group   **result	= va_arg(ap, struct group **);
1132
1133	int	rv;
1134
1135	_DIAGASSERT(retval != NULL);
1136	_DIAGASSERT(grp != NULL);
1137	_DIAGASSERT(buffer != NULL);
1138	_DIAGASSERT(result != NULL);
1139
1140	rv = __grscan_nis(retval, grp, buffer, buflen,
1141	    &_nis_state, 0, NULL, 0);
1142	if (rv == NS_SUCCESS)
1143		*result = grp;
1144	else
1145		*result = NULL;
1146	return rv;
1147}
1148
1149/*ARGSUSED*/
1150static int
1151_nis_getgrgid(void *nsrv, void *nscb, va_list ap)
1152{
1153	struct group	**retval = va_arg(ap, struct group **);
1154	gid_t		 gid	= va_arg(ap, gid_t);
1155
1156	int	rv, rerror;
1157
1158	_DIAGASSERT(retval != NULL);
1159
1160	*retval = NULL;
1161	rv = __grstart_nis(&_nis_state);
1162	if (rv != NS_SUCCESS)
1163		return rv;
1164	rv = __grscan_nis(&rerror, &_nis_group,
1165	    _nis_groupbuf, sizeof(_nis_groupbuf), &_nis_state, 1, NULL, gid);
1166	if (!_nis_state.stayopen)
1167		__grend_nis(&_nis_state);
1168	if (rv == NS_SUCCESS)
1169		*retval = &_nis_group;
1170	return rv;
1171}
1172
1173/*ARGSUSED*/
1174static int
1175_nis_getgrgid_r(void *nsrv, void *nscb, va_list ap)
1176{
1177	int		*retval	= va_arg(ap, int *);
1178	gid_t		 gid	= va_arg(ap, gid_t);
1179	struct group	*grp	= va_arg(ap, struct group *);
1180	char		*buffer	= va_arg(ap, char *);
1181	size_t		 buflen	= va_arg(ap, size_t);
1182	struct group   **result	= va_arg(ap, struct group **);
1183
1184	struct __grstate_nis state;
1185	int	rv;
1186
1187	_DIAGASSERT(retval != NULL);
1188	_DIAGASSERT(grp != NULL);
1189	_DIAGASSERT(buffer != NULL);
1190	_DIAGASSERT(result != NULL);
1191
1192	*result = NULL;
1193/* remark: we run under a global mutex inside of this module ... */
1194	if (_nis_state.stayopen)
1195	  { /* use global state only if stayopen is set - otherwiese we would blow up getgrent_r() ... */
1196	     rv = __grscan_nis(retval, grp, buffer, buflen, &_nis_state, 1, NULL, gid);
1197	  }
1198	else
1199	  {
1200	    memset(&state, 0, sizeof(state));
1201	    rv = __grscan_nis(retval, grp, buffer, buflen, &state, 1, NULL, gid);
1202	    __grend_nis(&state);
1203	  }
1204	if (rv == NS_SUCCESS)
1205		*result = grp;
1206	return rv;
1207}
1208
1209/*ARGSUSED*/
1210static int
1211_nis_getgrnam(void *nsrv, void *nscb, va_list ap)
1212{
1213	struct group	**retval = va_arg(ap, struct group **);
1214	const char	*name	= va_arg(ap, const char *);
1215
1216	int	rv, rerror;
1217
1218	_DIAGASSERT(retval != NULL);
1219
1220	*retval = NULL;
1221	rv = __grstart_nis(&_nis_state);
1222	if (rv != NS_SUCCESS)
1223		return rv;
1224	rv = __grscan_nis(&rerror, &_nis_group,
1225	    _nis_groupbuf, sizeof(_nis_groupbuf), &_nis_state, 1, name, 0);
1226	if (!_nis_state.stayopen)
1227		__grend_nis(&_nis_state);
1228	if (rv == NS_SUCCESS)
1229		*retval = &_nis_group;
1230	return rv;
1231}
1232
1233/*ARGSUSED*/
1234static int
1235_nis_getgrnam_r(void *nsrv, void *nscb, va_list ap)
1236{
1237	int		*retval	= va_arg(ap, int *);
1238	const char	*name	= va_arg(ap, const char *);
1239	struct group	*grp	= va_arg(ap, struct group *);
1240	char		*buffer	= va_arg(ap, char *);
1241	size_t		 buflen	= va_arg(ap, size_t);
1242	struct group   **result	= va_arg(ap, struct group **);
1243
1244	struct __grstate_nis state;
1245	int	rv;
1246
1247	_DIAGASSERT(retval != NULL);
1248	_DIAGASSERT(grp != NULL);
1249	_DIAGASSERT(buffer != NULL);
1250	_DIAGASSERT(result != NULL);
1251
1252	*result = NULL;
1253/* remark: we run under a global mutex inside of this module ... */
1254	if (_nis_state.stayopen)
1255	  { /* use global state only if stayopen is set - otherwiese we would blow up getgrent_r() ... */
1256	     rv = __grscan_nis(retval, grp, buffer, buflen, &_nis_state, 1, name, 0);
1257	  }
1258	else
1259	  {
1260	    memset(&state, 0, sizeof(state));
1261	    rv = __grscan_nis(retval, grp, buffer, buflen, &state, 1, name, 0);
1262	    __grend_nis(&state);
1263	  }
1264	if (rv == NS_SUCCESS)
1265		*result = grp;
1266	return rv;
1267}
1268
1269#endif /* YP */
1270
1271
1272#ifdef _GROUP_COMPAT
1273		/*
1274		 *	compat methods
1275		 */
1276
1277int
1278__grstart_compat(struct __grstate_compat *state)
1279{
1280
1281	_DIAGASSERT(state != NULL);
1282
1283	if (state->fp == NULL) {
1284		state->fp = fopen(_PATH_GROUP, "re");
1285		if (state->fp == NULL)
1286			return NS_UNAVAIL;
1287	} else {
1288		rewind(state->fp);
1289	}
1290	return NS_SUCCESS;
1291}
1292
1293int
1294__grend_compat(struct __grstate_compat *state)
1295{
1296
1297	_DIAGASSERT(state != NULL);
1298
1299	if (state->name) {
1300		free(state->name);
1301		state->name = NULL;
1302	}
1303	if (state->fp) {
1304		(void) fclose(state->fp);
1305		state->fp = NULL;
1306	}
1307	return NS_SUCCESS;
1308}
1309
1310
1311/*
1312 * __grbad_compat
1313 *	log an error if "files" or "compat" is specified in
1314 *	group_compat database
1315 */
1316/*ARGSUSED*/
1317int
1318__grbad_compat(void *nsrv, void *nscb, va_list ap)
1319{
1320	static int warned;
1321
1322	_DIAGASSERT(nsrv != NULL);
1323	_DIAGASSERT(nscb != NULL);
1324
1325	if (!warned) {
1326		syslog(LOG_ERR,
1327			"nsswitch.conf group_compat database can't use '%s'",
1328			(const char *)nscb);
1329	}
1330	warned = 1;
1331	return NS_UNAVAIL;
1332}
1333
1334/*
1335 * __grscan_compat
1336 *	Scan state->fp for the next desired entry.
1337 *	If search is zero, return the next entry.
1338 *	If search is non-zero, look for a specific name (if name != NULL),
1339 *	or a specific gid (if name == NULL).
1340 *	Sets *retval to the errno if the result is not NS_SUCCESS or
1341 *	NS_NOTFOUND.
1342 *
1343 *	searchfunc is invoked when a compat "+" lookup is required;
1344 *	searchcookie is passed as the first argument to searchfunc,
1345 *	the second argument is the group result.
1346 *	This should return NS_NOTFOUND when "no more groups" from compat src.
1347 *	If searchfunc is NULL then nsdispatch of getgrent is used.
1348 *	This is primarily intended for getgroupmembership(3)'s compat backend.
1349 */
1350int
1351__grscan_compat(int *retval, struct group *grp, char *buffer, size_t buflen,
1352	struct __grstate_compat *state, int search, const char *name, gid_t gid,
1353	int (*searchfunc)(void *, struct group **), void *searchcookie)
1354{
1355	int		rv;
1356	char		filebuf[_GETGR_R_SIZE_MAX], *ep;
1357
1358	static const ns_dtab compatentdtab[] = {
1359		NS_FILES_CB(__grbad_compat, "files")
1360		NS_DNS_CB(_dns_getgrent_r, NULL)
1361		NS_NIS_CB(_nis_getgrent_r, NULL)
1362		NS_COMPAT_CB(__grbad_compat, "compat")
1363		NS_NULL_CB
1364	};
1365	static const ns_dtab compatgiddtab[] = {
1366		NS_FILES_CB(__grbad_compat, "files")
1367		NS_DNS_CB(_dns_getgrgid_r, NULL)
1368		NS_NIS_CB(_nis_getgrgid_r, NULL)
1369		NS_COMPAT_CB(__grbad_compat, "compat")
1370		NS_NULL_CB
1371	};
1372	static const ns_dtab compatnamdtab[] = {
1373		NS_FILES_CB(__grbad_compat, "files")
1374		NS_DNS_CB(_dns_getgrnam_r, NULL)
1375		NS_NIS_CB(_nis_getgrnam_r, NULL)
1376		NS_COMPAT_CB(__grbad_compat, "compat")
1377		NS_NULL_CB
1378	};
1379
1380	_DIAGASSERT(retval != NULL);
1381	_DIAGASSERT(grp != NULL);
1382	_DIAGASSERT(buffer != NULL);
1383	_DIAGASSERT(state != NULL);
1384	/* name is NULL to indicate searching for gid */
1385
1386	*retval = 0;
1387
1388	if (state->fp == NULL) {	/* only start if file not open yet */
1389		rv = __grstart_compat(state);
1390		if (rv != NS_SUCCESS)
1391			goto compatgrscan_out;
1392	}
1393	rv = NS_NOTFOUND;
1394
1395	for (;;) {					/* loop through file */
1396		if (state->name != NULL) {
1397					/* processing compat entry */
1398			int		crv, cretval;
1399			struct group	cgrp, *cgrpres;
1400
1401			if (state->name[0]) {		/* specific +group: */
1402				crv = nsdispatch(NULL, compatnamdtab,
1403				    NSDB_GROUP_COMPAT, "getgrnam_r",
1404				    __nsdefaultnis,
1405				    &cretval, state->name,
1406				    &cgrp, filebuf, sizeof(filebuf), &cgrpres);
1407				free(state->name);	/* (only check 1 grp) */
1408				state->name = NULL;
1409			} else if (!search) {		/* any group */
1410				if (searchfunc) {
1411					crv = searchfunc(searchcookie,
1412					    &cgrpres);
1413				} else {
1414					crv = nsdispatch(NULL, compatentdtab,
1415					    NSDB_GROUP_COMPAT, "getgrent_r",
1416					    __nsdefaultnis,
1417					    &cretval, &cgrp, filebuf,
1418					    sizeof(filebuf), &cgrpres);
1419				}
1420			} else if (name) {		/* specific group */
1421				crv = nsdispatch(NULL, compatnamdtab,
1422				    NSDB_GROUP_COMPAT, "getgrnam_r",
1423				    __nsdefaultnis,
1424				    &cretval, name,
1425				    &cgrp, filebuf, sizeof(filebuf), &cgrpres);
1426			} else {			/* specific gid */
1427				crv = nsdispatch(NULL, compatgiddtab,
1428				    NSDB_GROUP_COMPAT, "getgrgid_r",
1429				    __nsdefaultnis,
1430				    &cretval, gid,
1431				    &cgrp, filebuf, sizeof(filebuf), &cgrpres);
1432			}
1433			if (crv != NS_SUCCESS) {	/* not found */
1434				free(state->name);
1435				state->name = NULL;
1436				continue;		/* try next line */
1437			}
1438			if (!_gr_copy(cgrpres, grp, buffer, buflen)) {
1439				rv = NS_UNAVAIL;
1440				break;
1441			}
1442			goto compatgrscan_cmpgrp;	/* skip to grp test */
1443		}
1444
1445							/* get next file line */
1446		if (fgets(filebuf, (int)sizeof(filebuf), state->fp) == NULL)
1447			break;
1448
1449		ep = strchr(filebuf, '\n');
1450		if (ep == NULL) {	/* skip lines that are too big */
1451			int ch;
1452
1453			while ((ch = getc(state->fp)) != '\n' && ch != EOF)
1454				continue;
1455			continue;
1456		}
1457		*ep = '\0';				/* clear trailing \n */
1458
1459		if (filebuf[0] == '+') {		/* parse compat line */
1460			if (state->name)
1461				free(state->name);
1462			state->name = NULL;
1463			switch(filebuf[1]) {
1464			case ':':
1465			case '\0':
1466				state->name = strdup("");
1467				break;
1468			default:
1469				ep = strchr(filebuf + 1, ':');
1470				if (ep == NULL)
1471					break;
1472				*ep = '\0';
1473				state->name = strdup(filebuf + 1);
1474				break;
1475			}
1476			if (state->name == NULL) {
1477				rv = NS_UNAVAIL;
1478				break;
1479			}
1480			continue;
1481		}
1482
1483							/* validate line */
1484		if (! _gr_parse(filebuf, grp, buffer, buflen)) {
1485			continue;			/* skip bad lines */
1486		}
1487
1488 compatgrscan_cmpgrp:
1489		if (! search) {				/* just want this one */
1490			rv = NS_SUCCESS;
1491			break;
1492		}
1493							/* want specific */
1494		if ((name && strcmp(name, grp->gr_name) == 0) ||
1495		    (!name && gid == grp->gr_gid)) {
1496			rv = NS_SUCCESS;
1497			break;
1498		}
1499
1500	}
1501
1502 compatgrscan_out:
1503	if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
1504		*retval = errno;
1505	return rv;
1506}
1507
1508static struct __grstate_compat	_compat_state;
1509					/* storage for non _r functions */
1510static struct group		_compat_group;
1511static char			_compat_groupbuf[_GETGR_R_SIZE_MAX];
1512
1513/*ARGSUSED*/
1514static int
1515_compat_setgrent(void *nsrv, void *nscb, va_list ap)
1516{
1517	static const ns_dtab dtab[] = {
1518		NS_FILES_CB(__grbad_compat, "files")
1519		NS_DNS_CB(_dns_setgrent, NULL)
1520		NS_NIS_CB(_nis_setgrent, NULL)
1521		NS_COMPAT_CB(__grbad_compat, "compat")
1522		NS_NULL_CB
1523	};
1524
1525					/* force group_compat setgrent() */
1526	(void) nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgrent",
1527	    __nsdefaultnis_forceall);
1528
1529					/* reset state, keep fp open */
1530	_compat_state.stayopen = 0;
1531	return __grstart_compat(&_compat_state);
1532}
1533
1534/*ARGSUSED*/
1535static int
1536_compat_setgroupent(void *nsrv, void *nscb, va_list ap)
1537{
1538	int	*retval		= va_arg(ap, int *);
1539	int	 stayopen	= va_arg(ap, int);
1540
1541	int	rv;
1542
1543	static const ns_dtab dtab[] = {
1544		NS_FILES_CB(__grbad_compat, "files")
1545		NS_DNS_CB(_dns_setgroupent, NULL)
1546		NS_NIS_CB(_nis_setgroupent, NULL)
1547		NS_COMPAT_CB(__grbad_compat, "compat")
1548		NS_NULL_CB
1549	};
1550
1551					/* force group_compat setgroupent() */
1552	(void) nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgroupent",
1553	    __nsdefaultnis_forceall, &rv, stayopen);
1554
1555	_compat_state.stayopen = stayopen;
1556	rv = __grstart_compat(&_compat_state);
1557	*retval = (rv == NS_SUCCESS);
1558	return rv;
1559}
1560
1561/*ARGSUSED*/
1562static int
1563_compat_endgrent(void *nsrv, void *nscb, va_list ap)
1564{
1565	static const ns_dtab dtab[] = {
1566		NS_FILES_CB(__grbad_compat, "files")
1567		NS_DNS_CB(_dns_endgrent, NULL)
1568		NS_NIS_CB(_nis_endgrent, NULL)
1569		NS_COMPAT_CB(__grbad_compat, "compat")
1570		NS_NULL_CB
1571	};
1572
1573					/* force group_compat endgrent() */
1574	(void) nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "endgrent",
1575	    __nsdefaultnis_forceall);
1576
1577					/* reset state, close fp */
1578	_compat_state.stayopen = 0;
1579	return __grend_compat(&_compat_state);
1580}
1581
1582/*ARGSUSED*/
1583static int
1584_compat_getgrent(void *nsrv, void *nscb, va_list ap)
1585{
1586	struct group	**retval = va_arg(ap, struct group **);
1587
1588	int	rv, rerror;
1589
1590	_DIAGASSERT(retval != NULL);
1591
1592	*retval = NULL;
1593	rv = __grscan_compat(&rerror, &_compat_group,
1594	    _compat_groupbuf, sizeof(_compat_groupbuf),
1595	    &_compat_state, 0, NULL, 0, NULL, NULL);
1596	if (rv == NS_SUCCESS)
1597		*retval = &_compat_group;
1598	return rv;
1599}
1600
1601/*ARGSUSED*/
1602static int
1603_compat_getgrent_r(void *nsrv, void *nscb, va_list ap)
1604{
1605	int		*retval	= va_arg(ap, int *);
1606	struct group	*grp	= va_arg(ap, struct group *);
1607	char		*buffer	= va_arg(ap, char *);
1608	size_t		 buflen	= va_arg(ap, size_t);
1609	struct group   **result	= va_arg(ap, struct group **);
1610
1611	int	rv;
1612
1613	_DIAGASSERT(retval != NULL);
1614	_DIAGASSERT(grp != NULL);
1615	_DIAGASSERT(buffer != NULL);
1616	_DIAGASSERT(result != NULL);
1617
1618	rv = __grscan_compat(retval, grp, buffer, buflen,
1619	    &_compat_state, 0, NULL, 0, NULL, NULL);
1620	if (rv == NS_SUCCESS)
1621		*result = grp;
1622	else
1623		*result = NULL;
1624	return rv;
1625}
1626
1627/*ARGSUSED*/
1628static int
1629_compat_getgrgid(void *nsrv, void *nscb, va_list ap)
1630{
1631	struct group	**retval = va_arg(ap, struct group **);
1632	gid_t		 gid	= va_arg(ap, gid_t);
1633
1634	int	rv, rerror;
1635
1636	_DIAGASSERT(retval != NULL);
1637
1638	*retval = NULL;
1639	rv = __grstart_compat(&_compat_state);
1640	if (rv != NS_SUCCESS)
1641		return rv;
1642	rv = __grscan_compat(&rerror, &_compat_group,
1643	    _compat_groupbuf, sizeof(_compat_groupbuf),
1644	    &_compat_state, 1, NULL, gid, NULL, NULL);
1645	if (!_compat_state.stayopen)
1646		__grend_compat(&_compat_state);
1647	if (rv == NS_SUCCESS)
1648		*retval = &_compat_group;
1649	return rv;
1650}
1651
1652/*ARGSUSED*/
1653static int
1654_compat_getgrgid_r(void *nsrv, void *nscb, va_list ap)
1655{
1656	int		*retval	= va_arg(ap, int *);
1657	gid_t		 gid	= va_arg(ap, gid_t);
1658	struct group	*grp	= va_arg(ap, struct group *);
1659	char		*buffer	= va_arg(ap, char *);
1660	size_t		 buflen	= va_arg(ap, size_t);
1661	struct group   **result	= va_arg(ap, struct group **);
1662
1663	struct __grstate_compat	state;
1664	int		rv;
1665
1666	_DIAGASSERT(retval != NULL);
1667	_DIAGASSERT(grp != NULL);
1668	_DIAGASSERT(buffer != NULL);
1669	_DIAGASSERT(result != NULL);
1670
1671	*result = NULL;
1672	memset(&state, 0, sizeof(state));
1673	rv = __grscan_compat(retval, grp, buffer, buflen, &state,
1674	    1, NULL, gid, NULL, NULL);
1675	__grend_compat(&state);
1676	if (rv == NS_SUCCESS)
1677		*result = grp;
1678	return rv;
1679}
1680
1681/*ARGSUSED*/
1682static int
1683_compat_getgrnam(void *nsrv, void *nscb, va_list ap)
1684{
1685	struct group	**retval = va_arg(ap, struct group **);
1686	const char	*name	= va_arg(ap, const char *);
1687
1688	int	rv, rerror;
1689
1690	_DIAGASSERT(retval != NULL);
1691
1692	*retval = NULL;
1693	rv = __grstart_compat(&_compat_state);
1694	if (rv != NS_SUCCESS)
1695		return rv;
1696	rv = __grscan_compat(&rerror, &_compat_group,
1697	    _compat_groupbuf, sizeof(_compat_groupbuf),
1698	    &_compat_state, 1, name, 0, NULL, NULL);
1699	if (!_compat_state.stayopen)
1700		__grend_compat(&_compat_state);
1701	if (rv == NS_SUCCESS)
1702		*retval = &_compat_group;
1703	return rv;
1704}
1705
1706/*ARGSUSED*/
1707static int
1708_compat_getgrnam_r(void *nsrv, void *nscb, va_list ap)
1709{
1710	int		*retval	= va_arg(ap, int *);
1711	const char	*name	= va_arg(ap, const char *);
1712	struct group	*grp	= va_arg(ap, struct group *);
1713	char		*buffer	= va_arg(ap, char *);
1714	size_t		 buflen	= va_arg(ap, size_t);
1715	struct group   **result	= va_arg(ap, struct group **);
1716
1717	struct __grstate_compat	state;
1718	int		rv;
1719
1720	_DIAGASSERT(retval != NULL);
1721	_DIAGASSERT(grp != NULL);
1722	_DIAGASSERT(buffer != NULL);
1723	_DIAGASSERT(result != NULL);
1724
1725	*result = NULL;
1726	memset(&state, 0, sizeof(state));
1727	rv = __grscan_compat(retval, grp, buffer, buflen, &state,
1728	    1, name, 0, NULL, NULL);
1729	__grend_compat(&state);
1730	if (rv == NS_SUCCESS)
1731		*result = grp;
1732	return rv;
1733}
1734
1735#endif	/* _GROUP_COMPAT */
1736
1737
1738		/*
1739		 *	public functions
1740		 */
1741
1742struct group *
1743getgrent(void)
1744{
1745	int		rv;
1746	struct group	*retval;
1747
1748	static const ns_dtab dtab[] = {
1749		NS_FILES_CB(_files_getgrent, NULL)
1750		NS_DNS_CB(_dns_getgrent, NULL)
1751		NS_NIS_CB(_nis_getgrent, NULL)
1752		NS_COMPAT_CB(_compat_getgrent, NULL)
1753		NS_NULL_CB
1754	};
1755
1756	mutex_lock(&__grmutex);
1757	rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrent", __nsdefaultcompat,
1758	    &retval);
1759	mutex_unlock(&__grmutex);
1760	return (rv == NS_SUCCESS) ? retval : NULL;
1761}
1762
1763int
1764getgrent_r(struct group *grp, char *buffer, size_t buflen,
1765    struct group **result)
1766{
1767	int		rv, retval;
1768
1769	static const ns_dtab dtab[] = {
1770		NS_FILES_CB(_files_getgrent_r, NULL)
1771		NS_DNS_CB(_dns_getgrent_r, NULL)
1772		NS_NIS_CB(_nis_getgrent_r, NULL)
1773		NS_COMPAT_CB(_compat_getgrent_r, NULL)
1774		NS_NULL_CB
1775	};
1776
1777	mutex_lock(&__grmutex);
1778	rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrent_r", __nsdefaultcompat,
1779	    &retval, grp, buffer, buflen, result);
1780	mutex_unlock(&__grmutex);
1781	switch (rv) {
1782	case NS_SUCCESS:
1783	case NS_NOTFOUND:
1784		return 0;
1785	default:
1786		return retval;
1787	}
1788}
1789
1790
1791struct group *
1792getgrgid(gid_t gid)
1793{
1794	int		rv;
1795	struct group	*retval;
1796
1797	static const ns_dtab dtab[] = {
1798		NS_FILES_CB(_files_getgrgid, NULL)
1799		NS_DNS_CB(_dns_getgrgid, NULL)
1800		NS_NIS_CB(_nis_getgrgid, NULL)
1801		NS_COMPAT_CB(_compat_getgrgid, NULL)
1802		NS_NULL_CB
1803	};
1804
1805	mutex_lock(&__grmutex);
1806	rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrgid", __nsdefaultcompat,
1807	    &retval, gid);
1808	mutex_unlock(&__grmutex);
1809	return (rv == NS_SUCCESS) ? retval : NULL;
1810}
1811
1812int
1813getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t buflen,
1814	struct group **result)
1815{
1816	int	rv, retval;
1817
1818	static const ns_dtab dtab[] = {
1819		NS_FILES_CB(_files_getgrgid_r, NULL)
1820		NS_DNS_CB(_dns_getgrgid_r, NULL)
1821		NS_NIS_CB(_nis_getgrgid_r, NULL)
1822		NS_COMPAT_CB(_compat_getgrgid_r, NULL)
1823		NS_NULL_CB
1824	};
1825
1826	_DIAGASSERT(grp != NULL);
1827	_DIAGASSERT(buffer != NULL);
1828	_DIAGASSERT(result != NULL);
1829
1830	*result = NULL;
1831	retval = 0;
1832	mutex_lock(&__grmutex);
1833	rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrgid_r", __nsdefaultcompat,
1834	    &retval, gid, grp, buffer, buflen, result);
1835	mutex_unlock(&__grmutex);
1836	switch (rv) {
1837	case NS_SUCCESS:
1838	case NS_NOTFOUND:
1839		return 0;
1840	default:
1841		return retval;
1842	}
1843}
1844
1845struct group *
1846getgrnam(const char *name)
1847{
1848	int		rv;
1849	struct group	*retval;
1850
1851	static const ns_dtab dtab[] = {
1852		NS_FILES_CB(_files_getgrnam, NULL)
1853		NS_DNS_CB(_dns_getgrnam, NULL)
1854		NS_NIS_CB(_nis_getgrnam, NULL)
1855		NS_COMPAT_CB(_compat_getgrnam, NULL)
1856		NS_NULL_CB
1857	};
1858
1859	mutex_lock(&__grmutex);
1860	rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrnam", __nsdefaultcompat,
1861	    &retval, name);
1862	mutex_unlock(&__grmutex);
1863	return (rv == NS_SUCCESS) ? retval : NULL;
1864}
1865
1866int
1867getgrnam_r(const char *name, struct group *grp, char *buffer, size_t buflen,
1868	struct group **result)
1869{
1870	int	rv, retval;
1871
1872	static const ns_dtab dtab[] = {
1873		NS_FILES_CB(_files_getgrnam_r, NULL)
1874		NS_DNS_CB(_dns_getgrnam_r, NULL)
1875		NS_NIS_CB(_nis_getgrnam_r, NULL)
1876		NS_COMPAT_CB(_compat_getgrnam_r, NULL)
1877		NS_NULL_CB
1878	};
1879
1880	_DIAGASSERT(name != NULL);
1881	_DIAGASSERT(grp != NULL);
1882	_DIAGASSERT(buffer != NULL);
1883	_DIAGASSERT(result != NULL);
1884
1885	*result = NULL;
1886	retval = 0;
1887	mutex_lock(&__grmutex);
1888	rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrnam_r", __nsdefaultcompat,
1889	    &retval, name, grp, buffer, buflen, result);
1890	mutex_unlock(&__grmutex);
1891	switch (rv) {
1892	case NS_SUCCESS:
1893	case NS_NOTFOUND:
1894		return 0;
1895	default:
1896		return retval;
1897	}
1898}
1899
1900void
1901endgrent(void)
1902{
1903	static const ns_dtab dtab[] = {
1904		NS_FILES_CB(_files_endgrent, NULL)
1905		NS_DNS_CB(_dns_endgrent, NULL)
1906		NS_NIS_CB(_nis_endgrent, NULL)
1907		NS_COMPAT_CB(_compat_endgrent, NULL)
1908		NS_NULL_CB
1909	};
1910
1911	mutex_lock(&__grmutex);
1912					/* force all endgrent() methods */
1913	(void) nsdispatch(NULL, dtab, NSDB_GROUP, "endgrent",
1914	    __nsdefaultcompat_forceall);
1915	mutex_unlock(&__grmutex);
1916}
1917
1918int
1919setgroupent(int stayopen)
1920{
1921	static const ns_dtab dtab[] = {
1922		NS_FILES_CB(_files_setgroupent, NULL)
1923		NS_DNS_CB(_dns_setgroupent, NULL)
1924		NS_NIS_CB(_nis_setgroupent, NULL)
1925		NS_COMPAT_CB(_compat_setgroupent, NULL)
1926		NS_NULL_CB
1927	};
1928	int	rv, retval;
1929
1930	mutex_lock(&__grmutex);
1931					/* force all setgroupent() methods */
1932	rv = nsdispatch(NULL, dtab, NSDB_GROUP, "setgroupent",
1933	    __nsdefaultcompat_forceall, &retval, stayopen);
1934	mutex_unlock(&__grmutex);
1935	return (rv == NS_SUCCESS) ? retval : 0;
1936}
1937
1938void
1939setgrent(void)
1940{
1941	static const ns_dtab dtab[] = {
1942		NS_FILES_CB(_files_setgrent, NULL)
1943		NS_DNS_CB(_dns_setgrent, NULL)
1944		NS_NIS_CB(_nis_setgrent, NULL)
1945		NS_COMPAT_CB(_compat_setgrent, NULL)
1946		NS_NULL_CB
1947	};
1948
1949	mutex_lock(&__grmutex);
1950					/* force all setgrent() methods */
1951	(void) nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent",
1952	    __nsdefaultcompat_forceall);
1953	mutex_unlock(&__grmutex);
1954}
1955