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