Home | History | Annotate | Line # | Download | only in gen
getgrent.c revision 1.49
      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