Home | History | Annotate | Line # | Download | only in gen
getgrent.c revision 1.47
      1 /*	$NetBSD: getgrent.c,v 1.47 2003/08/07 16:42:49 agc Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1989, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. Neither the name of the University nor the names of its contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 /*
     33  * Portions Copyright (c) 1994, Jason Downs. All Rights Reserved.
     34  *
     35  * Redistribution and use in source and binary forms, with or without
     36  * modification, are permitted provided that the following conditions
     37  * are met:
     38  * 1. Redistributions of source code must retain the above copyright
     39  *    notice, this list of conditions and the following disclaimer.
     40  * 2. Redistributions in binary form must reproduce the above copyright
     41  *    notice, this list of conditions and the following disclaimer in the
     42  *    documentation and/or other materials provided with the distribution.
     43  * 3. All advertising materials mentioning features or use of this software
     44  *    must display the following acknowledgement:
     45  *	This product includes software developed by the University of
     46  *	California, Berkeley and its contributors.
     47  * 4. Neither the name of the University nor the names of its contributors
     48  *    may be used to endorse or promote products derived from this software
     49  *    without specific prior written permission.
     50  *
     51  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     52  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     53  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     54  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     55  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     56  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     57  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     58  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     59  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     60  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     61  * SUCH DAMAGE.
     62  */
     63 
     64 #include <sys/cdefs.h>
     65 #if defined(LIBC_SCCS) && !defined(lint)
     66 #if 0
     67 static char sccsid[] = "@(#)getgrent.c	8.2 (Berkeley) 3/21/94";
     68 #else
     69 __RCSID("$NetBSD: getgrent.c,v 1.47 2003/08/07 16:42:49 agc Exp $");
     70 #endif
     71 #endif /* LIBC_SCCS and not lint */
     72 
     73 #include "namespace.h"
     74 
     75 #include <sys/types.h>
     76 
     77 #include <assert.h>
     78 #include <errno.h>
     79 #include <grp.h>
     80 #include <limits.h>
     81 #include <nsswitch.h>
     82 #include <stdio.h>
     83 #include <stdlib.h>
     84 #include <string.h>
     85 #include <syslog.h>
     86 
     87 #include <stdarg.h>
     88 
     89 #ifdef HESIOD
     90 #include <hesiod.h>
     91 #endif
     92 #ifdef YP
     93 #include <rpc/rpc.h>
     94 #include <rpcsvc/yp_prot.h>
     95 #include <rpcsvc/ypclnt.h>
     96 #endif
     97 
     98 #if defined(YP) || defined(HESIOD)
     99 #define _GROUP_COMPAT
    100 #endif
    101 
    102 struct group *_getgrent_user(const char *);
    103 
    104 #ifdef __weak_alias
    105 __weak_alias(endgrent,_endgrent)
    106 __weak_alias(getgrent,_getgrent)
    107 __weak_alias(getgrgid,_getgrgid)
    108 __weak_alias(getgrnam,_getgrnam)
    109 __weak_alias(setgrent,_setgrent)
    110 __weak_alias(setgroupent,_setgroupent)
    111 #endif
    112 
    113 static FILE		*_gr_fp;
    114 static struct group	_gr_group;
    115 static int		_gr_stayopen;
    116 static int		_gr_filesdone;
    117 
    118 static void grcleanup(void);
    119 static int grscan(int, gid_t, const char *, const char *);
    120 static int grstart(void);
    121 static int grmatchline(int, gid_t, const char *, const char *);
    122 
    123 #define	MAXGRP		200
    124 #define	MAXLINELENGTH	1024
    125 
    126 static __aconst char	*members[MAXGRP];
    127 static char		line[MAXLINELENGTH];
    128 
    129 #ifdef YP
    130 static char	*__ypcurrent, *__ypdomain;
    131 static int	 __ypcurrentlen;
    132 static int	 _gr_ypdone;
    133 #endif
    134 
    135 #ifdef HESIOD
    136 static int		 _gr_hesnum;
    137 static struct group	*_gr_hesgrplist = NULL;
    138 static int		 _gr_hesgrplistnum;
    139 static int		 _gr_hesgrplistmax;
    140 #endif
    141 
    142 #ifdef _GROUP_COMPAT
    143 enum _grmode { GRMODE_NONE, GRMODE_FULL, GRMODE_NAME };
    144 static enum _grmode	 __grmode;
    145 #endif
    146 
    147 struct group *
    148 getgrent(void)
    149 {
    150 
    151 	if ((!_gr_fp && !grstart()) || !grscan(0, 0, NULL, NULL))
    152  		return (NULL);
    153 	return &_gr_group;
    154 }
    155 
    156 /*
    157  * _getgrent_user() is designed only to be called by getgrouplist(3) and
    158  * hence makes no guarantees about filling the entire structure that it
    159  * returns.  It may only fill in the group name and gid fields.
    160  */
    161 
    162 struct group *
    163 _getgrent_user(const char *user)
    164 {
    165 
    166 	if ((!_gr_fp && !grstart()) || !grscan(0, 0, NULL, user))
    167  		return (NULL);
    168 	return &_gr_group;
    169 }
    170 
    171 struct group *
    172 getgrnam(const char *name)
    173 {
    174 	int rval;
    175 
    176 	_DIAGASSERT(name != NULL);
    177 
    178 	if (!grstart())
    179 		return NULL;
    180 	rval = grscan(1, 0, name, NULL);
    181 	if (!_gr_stayopen)
    182 		endgrent();
    183 	return (rval) ? &_gr_group : NULL;
    184 }
    185 
    186 struct group *
    187 getgrgid(gid_t gid)
    188 {
    189 	int rval;
    190 
    191 	if (!grstart())
    192 		return NULL;
    193 	rval = grscan(1, gid, NULL, NULL);
    194 	if (!_gr_stayopen)
    195 		endgrent();
    196 	return (rval) ? &_gr_group : NULL;
    197 }
    198 
    199 void
    200 grcleanup(void)
    201 {
    202 
    203 	_gr_filesdone = 0;
    204 #ifdef YP
    205 	if (__ypcurrent)
    206 		free(__ypcurrent);
    207 	__ypcurrent = NULL;
    208 	_gr_ypdone = 0;
    209 #endif
    210 #ifdef HESIOD
    211 	_gr_hesnum = 0;
    212 	if (!_gr_hesgrplist)
    213 		free(_gr_hesgrplist);
    214 	_gr_hesgrplist = NULL;
    215 	_gr_hesgrplistnum = -1;
    216 	_gr_hesgrplistmax = 0;
    217 #endif
    218 #ifdef _GROUP_COMPAT
    219 	__grmode = GRMODE_NONE;
    220 #endif
    221 }
    222 
    223 static int
    224 grstart(void)
    225 {
    226 
    227 	grcleanup();
    228 	if (_gr_fp) {
    229 		rewind(_gr_fp);
    230 		return 1;
    231 	}
    232 	return (_gr_fp = fopen(_PATH_GROUP, "r")) ? 1 : 0;
    233 }
    234 
    235 void
    236 setgrent(void)
    237 {
    238 
    239 	(void) setgroupent(0);
    240 }
    241 
    242 int
    243 setgroupent(int stayopen)
    244 {
    245 
    246 	if (!grstart())
    247 		return 0;
    248 	_gr_stayopen = stayopen;
    249 	return 1;
    250 }
    251 
    252 void
    253 endgrent(void)
    254 {
    255 
    256 	grcleanup();
    257 	if (_gr_fp) {
    258 		(void)fclose(_gr_fp);
    259 		_gr_fp = NULL;
    260 	}
    261 }
    262 
    263 
    264 static int _local_grscan(void *, void *, va_list);
    265 
    266 /*ARGSUSED*/
    267 static int
    268 _local_grscan(void *rv, void *cb_data, va_list ap)
    269 {
    270 	int		 search = va_arg(ap, int);
    271 	gid_t		 gid = va_arg(ap, gid_t);
    272 	const char	*name = va_arg(ap, const char *);
    273 	const char	*user = va_arg(ap, const char *);
    274 
    275 	if (_gr_filesdone)
    276 		return NS_NOTFOUND;
    277 	for (;;) {
    278 		if (!fgets(line, sizeof(line), _gr_fp)) {
    279 			if (!search)
    280 				_gr_filesdone = 1;
    281 			return NS_NOTFOUND;
    282 		}
    283 		/* skip lines that are too big */
    284 		if (!strchr(line, '\n')) {
    285 			int ch;
    286 
    287 			while ((ch = getc(_gr_fp)) != '\n' && ch != EOF)
    288 				;
    289 			continue;
    290 		}
    291 		if (grmatchline(search, gid, name, user))
    292 			return NS_SUCCESS;
    293 	}
    294 	/* NOTREACHED */
    295 }
    296 
    297 #ifdef HESIOD
    298 static int _dns_grscan(void *, void *, va_list);
    299 static int _dns_grplist(const char *);
    300 
    301 /*ARGSUSED*/
    302 static int
    303 _dns_grscan(void *rv, void *cb_data, va_list ap)
    304 {
    305 	int		 search = va_arg(ap, int);
    306 	gid_t		 gid = va_arg(ap, gid_t);
    307 	const char	*name = va_arg(ap, const char *);
    308 	const char	*user = va_arg(ap, const char *);
    309 
    310 	char		**hp;
    311 	void		 *context;
    312 	int		  r;
    313 
    314 	r = NS_UNAVAIL;
    315 	if (!search && user && _gr_hesgrplistmax != -1) {
    316 		r = _dns_grplist(user);
    317 		/* if we did not find user.grplist, just iterate */
    318 		if (!_gr_hesgrplist) {
    319 			_gr_hesgrplistmax = -1;
    320 			if (r != NS_NOTFOUND)
    321 				return r;
    322 		} else
    323 			return r;
    324 	}
    325 	if (!search && _gr_hesnum == -1)
    326 		return NS_NOTFOUND;
    327 	if (hesiod_init(&context) == -1)
    328 		return (r);
    329 
    330 	for (;;) {
    331 		if (search) {
    332 			if (name)
    333 				strlcpy(line, name, sizeof(line));
    334 			else
    335 				snprintf(line, sizeof(line), "%u",
    336 				    (unsigned int)gid);
    337 		} else {
    338 			snprintf(line, sizeof(line), "group-%u", _gr_hesnum);
    339 			_gr_hesnum++;
    340 		}
    341 
    342 		hp = NULL;
    343 		if (search && !name) {
    344 			hp = hesiod_resolve(context, line, "gid");
    345 			if (hp == NULL && errno != ENOENT)
    346 				break;
    347 		}
    348 		if (hp == NULL)
    349 			hp = hesiod_resolve(context, line, "group");
    350 		if (hp == NULL) {
    351 			if (errno == ENOENT) {
    352 				if (!search)
    353 					_gr_hesnum = -1;
    354 				r = NS_NOTFOUND;
    355 			}
    356 			break;
    357 		}
    358 
    359 						/* only check first elem */
    360 		strlcpy(line, hp[0], sizeof(line));
    361 		hesiod_free_list(context, hp);
    362 		if (grmatchline(search, gid, name, user)) {
    363 			r = NS_SUCCESS;
    364 			break;
    365 		} else if (search) {
    366 			r = NS_NOTFOUND;
    367 			break;
    368 		}
    369 	}
    370 	hesiod_end(context);
    371 	return (r);
    372 }
    373 
    374 static int
    375 _dns_grplist(const char *user)
    376 {
    377 	void	 *context;
    378 	int	  r;
    379 	char	**hp;
    380 	char	 *cp;
    381 
    382 	r = NS_UNAVAIL;
    383 	if (!_gr_hesgrplist) {
    384 		if (hesiod_init(&context) == -1)
    385 			return r;
    386 
    387 		_gr_hesgrplistnum = -1;
    388 		hp = hesiod_resolve(context, user, "grplist");
    389 		if (!hp) {
    390 			if (errno == ENOENT)
    391 				r = NS_NOTFOUND;
    392 			hesiod_end(context);
    393 			return r;
    394 		}
    395 
    396 		strlcpy(line, hp[0], sizeof(line));
    397 		hesiod_free_list(context, hp);
    398 
    399 		_gr_hesgrplistmax = 0;
    400 		for (cp=line; *cp; cp++)
    401 			if (*cp == ':')
    402 				_gr_hesgrplistmax++;
    403 		_gr_hesgrplistmax /= 2;
    404 		_gr_hesgrplistmax++;
    405 
    406 		_gr_hesgrplist = malloc(_gr_hesgrplistmax *
    407 		    sizeof(*_gr_hesgrplist));
    408 		if (!_gr_hesgrplist) {
    409 			hesiod_end(context);
    410 			return NS_UNAVAIL;
    411 		}
    412 
    413 		cp = line;
    414 		_gr_hesgrplistmax = 0;
    415 		for (;;) {
    416 			char	*name;
    417 			char	*num;
    418 			gid_t	 gid;
    419 			char	*ep;
    420 
    421 			/* XXXrcd: error handling */
    422 			if (!(name = strsep(&cp, ":")))
    423 				break;
    424 			if (!(num = strsep(&cp, ":")))
    425 				break;
    426 			gid = (gid_t) strtoul(num, &ep, 10);
    427 			if (gid > GID_MAX || *ep != '\0')
    428 				break;
    429 
    430 			_gr_hesgrplist[_gr_hesgrplistmax].gr_name = name;
    431 			_gr_hesgrplist[_gr_hesgrplistmax].gr_gid  = gid;
    432 			_gr_hesgrplistmax++;
    433 		}
    434 
    435 		hesiod_end(context);
    436 	}
    437 
    438 	/* we assume that _gr_hesgrplist is now defined */
    439 	if (++_gr_hesgrplistnum >= _gr_hesgrplistmax)
    440 		return NS_NOTFOUND;
    441 
    442 	/*
    443 	 * Now we copy the relevant information into _gr_group, so that
    444 	 * it can be returned.  Note that we only fill in the bare necessities
    445 	 * as this will be used exclusively by getgrouplist(3) and we do
    446 	 * not want to have to look up all of the information.
    447 	 */
    448 	_gr_group.gr_name   = _gr_hesgrplist[_gr_hesgrplistnum].gr_name;
    449 	_gr_group.gr_passwd = NULL;
    450 	_gr_group.gr_gid    = _gr_hesgrplist[_gr_hesgrplistnum].gr_gid;
    451 	_gr_group.gr_mem    = NULL;
    452 
    453 	return NS_SUCCESS;
    454 }
    455 #endif	/* HESIOD */
    456 
    457 #ifdef YP
    458 static int _nis_grscan(void *, void *, va_list);
    459 
    460 /*ARGSUSED*/
    461 static int
    462 _nis_grscan(void *rv, void *cb_data, va_list ap)
    463 {
    464 	int		 search = va_arg(ap, int);
    465 	gid_t		 gid = va_arg(ap, gid_t);
    466 	const char	*name = va_arg(ap, const char *);
    467 	const char	*user = va_arg(ap, const char *);
    468 
    469 	char	*key, *data;
    470 	int	 keylen, datalen;
    471 	int	 r;
    472 
    473 	if(__ypdomain == NULL) {
    474 		switch (yp_get_default_domain(&__ypdomain)) {
    475 		case 0:
    476 			break;
    477 		case YPERR_RESRC:
    478 			return NS_TRYAGAIN;
    479 		default:
    480 			return NS_UNAVAIL;
    481 		}
    482 	}
    483 
    484 	if (search) {			/* specific group or gid */
    485 		if (name)
    486 			strlcpy(line, name, sizeof(line));
    487 		else
    488 			snprintf(line, sizeof(line), "%u", (unsigned int)gid);
    489 		data = NULL;
    490 		r = yp_match(__ypdomain,
    491 				(name) ? "group.byname" : "group.bygid",
    492 				line, (int)strlen(line), &data, &datalen);
    493 		switch (r) {
    494 		case 0:
    495 			break;
    496 		case YPERR_KEY:
    497 			if (data)
    498 				free(data);
    499 			return NS_NOTFOUND;
    500 		default:
    501 			if (data)
    502 				free(data);
    503 			return NS_UNAVAIL;
    504 		}
    505 		data[datalen] = '\0';			/* clear trailing \n */
    506 		strlcpy(line, data, sizeof(line));
    507 		free(data);
    508 		if (grmatchline(search, gid, name, user))
    509 			return NS_SUCCESS;
    510 		else
    511 			return NS_NOTFOUND;
    512 	}
    513 
    514 						/* ! search */
    515 	if (_gr_ypdone)
    516 		return NS_NOTFOUND;
    517 	for (;;) {
    518 		data = NULL;
    519 		if(__ypcurrent) {
    520 			key = NULL;
    521 			r = yp_next(__ypdomain, "group.byname",
    522 				__ypcurrent, __ypcurrentlen,
    523 				&key, &keylen, &data, &datalen);
    524 			free(__ypcurrent);
    525 			switch (r) {
    526 			case 0:
    527 				break;
    528 			case YPERR_NOMORE:
    529 				__ypcurrent = NULL;
    530 				if (key)
    531 					free(key);
    532 				if (data)
    533 					free(data);
    534 				_gr_ypdone = 1;
    535 				return NS_NOTFOUND;
    536 			default:
    537 				if (key)
    538 					free(key);
    539 				if (data)
    540 					free(data);
    541 				return NS_UNAVAIL;
    542 			}
    543 			__ypcurrent = key;
    544 			__ypcurrentlen = keylen;
    545 		} else {
    546 			if (yp_first(__ypdomain, "group.byname",
    547 					&__ypcurrent, &__ypcurrentlen,
    548 					&data, &datalen)) {
    549 				if (data)
    550 					free(data);
    551 				return NS_UNAVAIL;
    552 			}
    553 		}
    554 		data[datalen] = '\0';			/* clear trailing \n */
    555 		strlcpy(line, data, sizeof(line));
    556 		free(data);
    557 		if (grmatchline(search, gid, name, user))
    558 			return NS_SUCCESS;
    559 	}
    560 	/* NOTREACHED */
    561 }
    562 #endif	/* YP */
    563 
    564 #ifdef _GROUP_COMPAT
    565 /*
    566  * log an error if "files" or "compat" is specified in group_compat database
    567  */
    568 static int _bad_grscan(void *, void *, va_list);
    569 
    570 /*ARGSUSED*/
    571 static int
    572 _bad_grscan(void *rv, void *cb_data, va_list ap)
    573 {
    574 	static int warned;
    575 
    576 	_DIAGASSERT(cb_data != NULL);
    577 
    578 	if (!warned) {
    579 		syslog(LOG_ERR,
    580 			"nsswitch.conf group_compat database can't use '%s'",
    581 			(char *)cb_data);
    582 	}
    583 	warned = 1;
    584 	return NS_UNAVAIL;
    585 }
    586 
    587 /*
    588  * when a name lookup in compat mode is required, look it up in group_compat
    589  * nsswitch database. only Hesiod and NIS is supported - it doesn't make
    590  * sense to lookup compat names from 'files' or 'compat'
    591  */
    592 
    593 static int __grscancompat(int, gid_t, const char *, const char *);
    594 
    595 static int
    596 __grscancompat(int search, gid_t gid, const char *name, const char *user)
    597 {
    598 	static const ns_dtab dtab[] = {
    599 		NS_FILES_CB(_bad_grscan, "files")
    600 		NS_DNS_CB(_dns_grscan, NULL)
    601 		NS_NIS_CB(_nis_grscan, NULL)
    602 		NS_COMPAT_CB(_bad_grscan, "compat")
    603 		{ 0 }
    604 	};
    605 	static const ns_src defaultnis[] = {
    606 		{ NSSRC_NIS, 	NS_SUCCESS },
    607 		{ 0 }
    608 	};
    609 
    610 	_DIAGASSERT(name != NULL);
    611 
    612 	return (nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "grscancompat",
    613 	    defaultnis, search, gid, name, user));
    614 }
    615 #endif	/* GROUP_COMPAT */
    616 
    617 
    618 static int _compat_grscan(void *, void *, va_list);
    619 
    620 /*ARGSUSED*/
    621 static int
    622 _compat_grscan(void *rv, void *cb_data, va_list ap)
    623 {
    624 	int		 search = va_arg(ap, int);
    625 	gid_t		 gid = va_arg(ap, gid_t);
    626 	const char	*name = va_arg(ap, const char *);
    627 	const char	*user = va_arg(ap, const char *);
    628 
    629 #ifdef _GROUP_COMPAT
    630 	static char	*grname = NULL;
    631 #endif
    632 
    633 	for (;;) {
    634 #ifdef _GROUP_COMPAT
    635 		if(__grmode != GRMODE_NONE) {
    636 			int	 r;
    637 
    638 			switch(__grmode) {
    639 			case GRMODE_FULL:
    640 				r = __grscancompat(search, gid, name, user);
    641 				if (r == NS_SUCCESS)
    642 					return r;
    643 				__grmode = GRMODE_NONE;
    644 				break;
    645 			case GRMODE_NAME:
    646 				if(grname == (char *)NULL) {
    647 					__grmode = GRMODE_NONE;
    648 					break;
    649 				}
    650 				r = __grscancompat(1, 0, grname, user);
    651 				free(grname);
    652 				grname = (char *)NULL;
    653 				if (r != NS_SUCCESS)
    654 					break;
    655 				if (!search)
    656 					return NS_SUCCESS;
    657 				if (name) {
    658 					if (! strcmp(_gr_group.gr_name, name))
    659 						return NS_SUCCESS;
    660 				} else {
    661 					if (_gr_group.gr_gid == gid)
    662 						return NS_SUCCESS;
    663 				}
    664 				break;
    665 			case GRMODE_NONE:
    666 				abort();
    667 			}
    668 			continue;
    669 		}
    670 #endif	/* _GROUP_COMPAT */
    671 
    672 		if (!fgets(line, sizeof(line), _gr_fp))
    673 			return NS_NOTFOUND;
    674 		/* skip lines that are too big */
    675 		if (!strchr(line, '\n')) {
    676 			int ch;
    677 
    678 			while ((ch = getc(_gr_fp)) != '\n' && ch != EOF)
    679 				;
    680 			continue;
    681 		}
    682 
    683 #ifdef _GROUP_COMPAT
    684 		if (line[0] == '+') {
    685 			char	*tptr, *bp;
    686 
    687 			switch(line[1]) {
    688 			case ':':
    689 			case '\0':
    690 			case '\n':
    691 				__grmode = GRMODE_FULL;
    692 				break;
    693 			default:
    694 				__grmode = GRMODE_NAME;
    695 				bp = line;
    696 				tptr = strsep(&bp, ":\n");
    697 				grname = strdup(tptr + 1);
    698 				break;
    699 			}
    700 			continue;
    701 		}
    702 #endif	/* _GROUP_COMPAT */
    703 		if (grmatchline(search, gid, name, user))
    704 			return NS_SUCCESS;
    705 	}
    706 	/* NOTREACHED */
    707 }
    708 
    709 static int
    710 grscan(int search, gid_t gid, const char *name, const char *user)
    711 {
    712 	int		r;
    713 	static const ns_dtab dtab[] = {
    714 		NS_FILES_CB(_local_grscan, NULL)
    715 		NS_DNS_CB(_dns_grscan, NULL)
    716 		NS_NIS_CB(_nis_grscan, NULL)
    717 		NS_COMPAT_CB(_compat_grscan, NULL)
    718 		{ 0 }
    719 	};
    720 	static const ns_src compatsrc[] = {
    721 		{ NSSRC_COMPAT, NS_SUCCESS },
    722 		{ 0 }
    723 	};
    724 
    725 	/* name may be NULL if search is nonzero */
    726 
    727 	r = nsdispatch(NULL, dtab, NSDB_GROUP, "grscan", compatsrc,
    728 	    search, gid, name, user);
    729 	return (r == NS_SUCCESS) ? 1 : 0;
    730 }
    731 
    732 static int
    733 grmatchline(int search, gid_t gid, const char *name, const char *user)
    734 {
    735 	unsigned long	id;
    736 	__aconst char	**m;
    737 	char		*cp, *bp, *ep;
    738 
    739 	/* name may be NULL if search is nonzero */
    740 
    741 	if (line[0] == '+')
    742 		return 0;	/* sanity check to prevent recursion */
    743 	bp = line;
    744 	_gr_group.gr_name = strsep(&bp, ":\n");
    745 	if (search && name && strcmp(_gr_group.gr_name, name))
    746 		return 0;
    747 	_gr_group.gr_passwd = strsep(&bp, ":\n");
    748 	if (!(cp = strsep(&bp, ":\n")))
    749 		return 0;
    750 	id = strtoul(cp, &ep, 10);
    751 	if (id > GID_MAX || *ep != '\0')
    752 		return 0;
    753 	_gr_group.gr_gid = (gid_t)id;
    754 	if (search && name == NULL && _gr_group.gr_gid != gid)
    755 		return 0;
    756 	cp = NULL;
    757 	if (bp == NULL)
    758 		return 0;
    759 	for (_gr_group.gr_mem = m = members;; bp++) {
    760 		if (m == &members[MAXGRP - 1])
    761 			break;
    762 		if (*bp == ',') {
    763 			if (cp) {
    764 				*bp = '\0';
    765 				*m++ = cp;
    766 				cp = NULL;
    767 			}
    768 		} else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
    769 			if (cp) {
    770 				*bp = '\0';
    771 				*m++ = cp;
    772 			}
    773 			break;
    774 		} else if (cp == NULL)
    775 			cp = bp;
    776 	}
    777 	*m = NULL;
    778 	if (user) {
    779 		for (m = members; *m; m++)
    780 			if (!strcmp(user, *m))
    781 				return 1;
    782 		return 0;
    783 	}
    784 	return 1;
    785 }
    786