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