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