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