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