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