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