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