xref: /netbsd-src/lib/libc/gen/getgrent.c (revision da5f4674a3fc214be3572d358b66af40ab9401e7)
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