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