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