xref: /freebsd/lib/libc/gen/getgrent.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
1 /*	$NetBSD: getgrent.c,v 1.34.2.1 1999/04/27 14:10:58 perry 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 static char rcsid[] =
40   "$FreeBSD$";
41 #endif /* LIBC_SCCS and not lint */
42 
43 #include <sys/types.h>
44 
45 #include <errno.h>
46 #include <grp.h>
47 #include <limits.h>
48 #include <nsswitch.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <syslog.h>
53 
54 #ifdef HESIOD
55 #include <hesiod.h>
56 #include <arpa/nameser.h>
57 #endif
58 #ifdef YP
59 #include <rpc/rpc.h>
60 #include <rpcsvc/yp_prot.h>
61 #include <rpcsvc/ypclnt.h>
62 #endif
63 
64 #if defined(YP) || defined(HESIOD)
65 #define _GROUP_COMPAT
66 #endif
67 
68 static FILE		*_gr_fp;
69 static struct group	_gr_group;
70 static int		_gr_stayopen;
71 static int		_gr_filesdone;
72 
73 static void grcleanup	__P((void));
74 static int grscan	__P((int, gid_t, const char *));
75 static char *getline	__P((void));
76 static int copyline     __P((const char*));
77 static int matchline	__P((int, gid_t, const char *));
78 static int start_gr	__P((void));
79 
80 
81 
82 
83 /* initial size for malloc and increase steps for realloc */
84 #define	MAXGRP		64
85 #define	MAXLINELENGTH	256
86 
87 #ifdef HESIOD
88 #if MAXLINELENGTH < NS_MAXLABEL + 1
89 #error "MAXLINELENGTH must be at least NS_MAXLABEL + 1"
90 #endif
91 #endif
92 
93 static char		**members;       /* list of group members */
94 static int		  maxgrp;        /* current length of **members */
95 static char		 *line;	         /* buffer for group line */
96 static int		  maxlinelength; /* current length of *line */
97 
98 /*
99  * Lines longer than MAXLINELENGTHLIMIT will be counted as an error.
100  * <= 0 disable check for maximum line length
101  * 256K is enough for 64,000 uids
102  */
103 #define MAXLINELENGTHLIMIT	(256 * 1024)
104 
105 #ifdef YP
106 static char	*__ypcurrent, *__ypdomain;
107 static int	 __ypcurrentlen;
108 static int	 _gr_ypdone;
109 #endif
110 
111 #ifdef HESIOD
112 static int	_gr_hesnum;
113 #endif
114 
115 #ifdef _GROUP_COMPAT
116 enum _grmode { GRMODE_NONE, GRMODE_FULL, GRMODE_NAME };
117 static enum _grmode	 __grmode;
118 #endif
119 
120 struct group *
121 getgrent()
122 {
123 	if ((!_gr_fp && !start_gr()) || !grscan(0, 0, NULL))
124  		return (NULL);
125 	return &_gr_group;
126 }
127 
128 struct group *
129 getgrnam(name)
130 	const char *name;
131 {
132 	int rval;
133 
134 	if (!start_gr())
135 		return NULL;
136 	rval = grscan(1, 0, name);
137 	if (!_gr_stayopen)
138 		endgrent();
139 	return (rval) ? &_gr_group : NULL;
140 }
141 
142 struct group *
143 getgrgid(gid)
144 	gid_t gid;
145 {
146 	int rval;
147 
148 	if (!start_gr())
149 		return NULL;
150 	rval = grscan(1, gid, NULL);
151 	if (!_gr_stayopen)
152 		endgrent();
153 	return (rval) ? &_gr_group : NULL;
154 }
155 
156 void
157 grcleanup()
158 {
159 	_gr_filesdone = 0;
160 #ifdef YP
161 	if (__ypcurrent)
162 		free(__ypcurrent);
163 	__ypcurrent = NULL;
164 	_gr_ypdone = 0;
165 #endif
166 #ifdef HESIOD
167 	_gr_hesnum = 0;
168 #endif
169 #ifdef _GROUP_COMPAT
170 	__grmode = GRMODE_NONE;
171 #endif
172 }
173 
174 static int
175 start_gr()
176 {
177 	grcleanup();
178 	if (maxlinelength == 0) {
179 		if ((line = (char *)malloc(MAXLINELENGTH)) == NULL)
180 			return 0;
181 		maxlinelength = MAXLINELENGTH;
182 	}
183 	if (maxgrp == 0) {
184 		if ((members = (char **) malloc(sizeof(char**) *
185 					       MAXGRP)) == NULL)
186 			return 0;
187 		maxgrp = MAXGRP;
188 	}
189 	if (_gr_fp) {
190 		rewind(_gr_fp);
191 		return 1;
192 	}
193 	return (_gr_fp = fopen(_PATH_GROUP, "r")) ? 1 : 0;
194 }
195 
196 int
197 setgrent(void)
198 {
199 	return setgroupent(0);
200 }
201 
202 int
203 setgroupent(stayopen)
204 	int stayopen;
205 {
206 	if (!start_gr())
207 		return 0;
208 	_gr_stayopen = stayopen;
209 	return 1;
210 }
211 
212 void
213 endgrent()
214 {
215 	grcleanup();
216 	if (_gr_fp) {
217 		(void)fclose(_gr_fp);
218 		_gr_fp = NULL;
219 	}
220 }
221 
222 
223 static int _local_grscan __P((void *, void *, va_list));
224 
225 /*ARGSUSED*/
226 static int
227 _local_grscan(rv, cb_data, ap)
228 	void	*rv;
229 	void	*cb_data;
230 	va_list	 ap;
231 {
232 	int		 search = va_arg(ap, int);
233 	gid_t		 gid = va_arg(ap, gid_t);
234 	const char	*name = va_arg(ap, const char *);
235 
236 	if (_gr_filesdone)
237 		return NS_NOTFOUND;
238 	for (;;) {
239 		if (getline() == NULL) {
240 			if (!search)
241 				_gr_filesdone = 1;
242 			return NS_NOTFOUND;
243 		}
244 		if (matchline(search, gid, name))
245 			return NS_SUCCESS;
246 	}
247 	/* NOTREACHED */
248 }
249 
250 #ifdef HESIOD
251 static int _dns_grscan __P((void *, void *, va_list));
252 
253 /*ARGSUSED*/
254 static int
255 _dns_grscan(rv, cb_data, ap)
256 	void	*rv;
257 	void	*cb_data;
258 	va_list	 ap;
259 {
260 	int		 search = va_arg(ap, int);
261 	gid_t		 gid = va_arg(ap, gid_t);
262 	const char	*name = va_arg(ap, const char *);
263 
264 	char		**hp;
265 	void		 *context;
266 	int		  r;
267 	size_t		  sz;
268 
269 	r = NS_UNAVAIL;
270 	if (!search && _gr_hesnum == -1)
271 		return NS_NOTFOUND;
272 	if (hesiod_init(&context) == -1)
273 		return (r);
274 
275 	for (;;) {
276 		if (search) {
277 			if (name)
278 				strlcpy(line, name, maxlinelength);
279 			else
280 				snprintf(line, maxlinelength, "%u",
281 				    (unsigned int)gid);
282 		} else {
283 			snprintf(line, maxlinelength, "group-%u", _gr_hesnum);
284 			_gr_hesnum++;
285 		}
286 
287 		hp = hesiod_resolve(context, line, "group");
288 		if (hp == NULL) {
289 			if (errno == ENOENT) {
290 				if (!search)
291 					_gr_hesnum = -1;
292 				r = NS_NOTFOUND;
293 			}
294 			break;
295 		}
296 
297 						/* only check first elem */
298 		if (copyline(hp[0]) == 0)
299 			return NS_UNAVAIL;
300 		hesiod_free_list(context, hp);
301 		if (matchline(search, gid, name)) {
302 			r = NS_SUCCESS;
303 			break;
304 		} else if (search) {
305 			r = NS_NOTFOUND;
306 			break;
307 		}
308 	}
309 	hesiod_end(context);
310 	return (r);
311 }
312 #endif
313 
314 #ifdef YP
315 static int _nis_grscan __P((void *, void *, va_list));
316 
317 /*ARGSUSED*/
318 static int
319 _nis_grscan(rv, cb_data, ap)
320 	void	*rv;
321 	void	*cb_data;
322 	va_list	 ap;
323 {
324 	int		 search = va_arg(ap, int);
325 	gid_t		 gid = va_arg(ap, gid_t);
326 	const char	*name = va_arg(ap, const char *);
327 
328 	char	*key, *data;
329 	int	 keylen, datalen;
330 	int	 r;
331 	size_t   sz;
332 
333 	if(__ypdomain == NULL) {
334 		switch (yp_get_default_domain(&__ypdomain)) {
335 		case 0:
336 			break;
337 		case YPERR_RESRC:
338 			return NS_TRYAGAIN;
339 		default:
340 			return NS_UNAVAIL;
341 		}
342 	}
343 
344 	if (search) {			/* specific group or gid */
345 		if (name)
346 			strlcpy(line, name, maxlinelength);
347 		else
348 			snprintf(line, maxlinelength, "%u", (unsigned int)gid);
349 		data = NULL;
350 		r = yp_match(__ypdomain,
351 				(name) ? "group.byname" : "group.bygid",
352 				line, (int)strlen(line), &data, &datalen);
353 		switch (r) {
354 		case 0:
355 			break;
356 		case YPERR_KEY:
357 			if (data)
358 				free(data);
359 			return NS_NOTFOUND;
360 		default:
361 			if (data)
362 				free(data);
363 			return NS_UNAVAIL;
364 		}
365 		data[datalen] = '\0';			/* clear trailing \n */
366 		if (copyline(data) == 0)
367 			return NS_UNAVAIL;
368 		free(data);
369 		if (matchline(search, gid, name))
370 			return NS_SUCCESS;
371 		else
372 			return NS_NOTFOUND;
373 	}
374 
375 						/* ! search */
376 	if (_gr_ypdone)
377 		return NS_NOTFOUND;
378 	for (;;) {
379 		data = NULL;
380 		if(__ypcurrent) {
381 			key = NULL;
382 			r = yp_next(__ypdomain, "group.byname",
383 				__ypcurrent, __ypcurrentlen,
384 				&key, &keylen, &data, &datalen);
385 			free(__ypcurrent);
386 			switch (r) {
387 			case 0:
388 				break;
389 			case YPERR_NOMORE:
390 				__ypcurrent = NULL;
391 				if (key)
392 					free(key);
393 				if (data)
394 					free(data);
395 				_gr_ypdone = 1;
396 				return NS_NOTFOUND;
397 			default:
398 				if (key)
399 					free(key);
400 				if (data)
401 					free(data);
402 				return NS_UNAVAIL;
403 			}
404 			__ypcurrent = key;
405 			__ypcurrentlen = keylen;
406 		} else {
407 			if (yp_first(__ypdomain, "group.byname",
408 					&__ypcurrent, &__ypcurrentlen,
409 					&data, &datalen)) {
410 				if (data)
411 					free(data);
412 				return NS_UNAVAIL;
413 			}
414 		}
415 		data[datalen] = '\0';			/* clear trailing \n */
416 		if (copyline(data) == 0)
417 			return NS_UNAVAIL;
418 		free(data);
419 		if (matchline(search, gid, name))
420 			return NS_SUCCESS;
421 	}
422 	/* NOTREACHED */
423 }
424 #endif
425 
426 #ifdef _GROUP_COMPAT
427 /*
428  * log an error if "files" or "compat" is specified in group_compat database
429  */
430 static int _bad_grscan __P((void *, void *, va_list));
431 
432 /*ARGSUSED*/
433 static int
434 _bad_grscan(rv, cb_data, ap)
435 	void	*rv;
436 	void	*cb_data;
437 	va_list	 ap;
438 {
439 	static int warned;
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 	return (nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "grscancompat",
477 	    defaultnis, search, gid, name));
478 }
479 #endif
480 
481 
482 static int _compat_grscan __P((void *, void *, va_list));
483 
484 /*ARGSUSED*/
485 static int
486 _compat_grscan(rv, cb_data, ap)
487 	void	*rv;
488 	void	*cb_data;
489 	va_list	 ap;
490 {
491 	int		 search = va_arg(ap, int);
492 	gid_t		 gid = va_arg(ap, gid_t);
493 	const char	*name = va_arg(ap, const char *);
494 
495 #ifdef _GROUP_COMPAT
496 	static char	*grname = NULL;
497 #endif
498 
499 	for (;;) {
500 #ifdef _GROUP_COMPAT
501 		if(__grmode != GRMODE_NONE) {
502 			int	 r;
503 
504 			switch(__grmode) {
505 			case GRMODE_FULL:
506 				r = __grscancompat(search, gid, name);
507 				if (r == NS_SUCCESS)
508 					return r;
509 				__grmode = GRMODE_NONE;
510 				break;
511 			case GRMODE_NAME:
512 				if(grname == (char *)NULL) {
513 					__grmode = GRMODE_NONE;
514 					break;
515 				}
516 				r = __grscancompat(1, 0, grname);
517 				free(grname);
518 				grname = (char *)NULL;
519 				if (r != NS_SUCCESS)
520 					break;
521 				if (!search)
522 					return NS_SUCCESS;
523 				if (name) {
524 					if (! strcmp(_gr_group.gr_name, name))
525 						return NS_SUCCESS;
526 				} else {
527 					if (_gr_group.gr_gid == gid)
528 						return NS_SUCCESS;
529 				}
530 				break;
531 			case GRMODE_NONE:
532 				abort();
533 			}
534 			continue;
535 		}
536 #endif /* _GROUP_COMPAT */
537 
538 		if (getline() == NULL)
539 			return NS_NOTFOUND;
540 
541 #ifdef _GROUP_COMPAT
542 		if (line[0] == '+') {
543 			char	*tptr, *bp;
544 
545 			switch(line[1]) {
546 			case ':':
547 			case '\0':
548 			case '\n':
549 				__grmode = GRMODE_FULL;
550 				break;
551 			default:
552 				__grmode = GRMODE_NAME;
553 				bp = line;
554 				tptr = strsep(&bp, ":\n");
555 				grname = strdup(tptr + 1);
556 				break;
557 			}
558 			continue;
559 		}
560 #endif /* _GROUP_COMPAT */
561 		if (matchline(search, gid, name))
562 			return NS_SUCCESS;
563 	}
564 	/* NOTREACHED */
565 }
566 
567 static int
568 grscan(search, gid, name)
569 	int		 search;
570 	gid_t		 gid;
571 	const char	*name;
572 {
573 	int		r;
574 	static const ns_dtab dtab[] = {
575 		NS_FILES_CB(_local_grscan, NULL)
576 		NS_DNS_CB(_dns_grscan, NULL)
577 		NS_NIS_CB(_nis_grscan, NULL)
578 		NS_COMPAT_CB(_compat_grscan, NULL)
579 		{ 0 }
580 	};
581 	static const ns_src compatsrc[] = {
582 		{ NSSRC_COMPAT, NS_SUCCESS },
583 		{ 0 }
584 	};
585 
586 	r = nsdispatch(NULL, dtab, NSDB_GROUP, "grscan", compatsrc,
587 	    search, gid, name);
588 	return (r == NS_SUCCESS) ? 1 : 0;
589 }
590 
591 static int
592 matchline(search, gid, name)
593 	int		 search;
594 	gid_t		 gid;
595 	const char	*name;
596 {
597 	unsigned long	id;
598 	char		**m;
599 	char		*cp, *bp, *ep;
600 
601 	if (line[0] == '+')
602 		return 0;	/* sanity check to prevent recursion */
603 	bp = line;
604 	_gr_group.gr_name = strsep(&bp, ":\n");
605 	if (search && name && strcmp(_gr_group.gr_name, name))
606 		return 0;
607 	_gr_group.gr_passwd = strsep(&bp, ":\n");
608 	if (!(cp = strsep(&bp, ":\n")))
609 		return 0;
610 	id = strtoul(cp, &ep, 10);
611 	if (*ep != '\0')
612 		return 0;
613 	_gr_group.gr_gid = (gid_t)id;
614 	if (search && name == NULL && _gr_group.gr_gid != gid)
615 		return 0;
616 	cp = NULL;
617 	if (bp == NULL)
618 		return 0;
619 	for (_gr_group.gr_mem = m = members;; bp++) {
620 		if (m == &members[maxgrp - 1]) {
621 			members = (char **) reallocf(members, sizeof(char **) *
622 						     (maxgrp + MAXGRP));
623 			if (members == NULL)
624 				return 0;
625 			_gr_group.gr_mem = members;
626 			m = &members[maxgrp - 1];
627 			maxgrp += MAXGRP;
628 		}
629 		if (*bp == ',') {
630 			if (cp) {
631 				*bp = '\0';
632 				*m++ = cp;
633 				cp = NULL;
634 			}
635 		} else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
636 			if (cp) {
637 				*bp = '\0';
638 				*m++ = cp;
639 			}
640 			break;
641 		} else if (cp == NULL)
642 			cp = bp;
643         }
644 	*m = NULL;
645         return 1;
646 }
647 
648 static char *
649 getline(void)
650 {
651 	const char	*cp;
652 
653  tryagain:
654 	if (fgets(line, maxlinelength, _gr_fp) == NULL)
655 		return NULL;
656 	if (index(line, '\n') == NULL) {
657 		do {
658 			if (feof(_gr_fp))
659 				return NULL;
660 			if (MAXLINELENGTHLIMIT > 0 &&
661 			    maxlinelength >= MAXLINELENGTHLIMIT)
662 				return NULL;
663 			line = (char *)reallocf(line, maxlinelength +
664 						MAXLINELENGTH);
665 			if (line == NULL)
666 				return NULL;
667 			if (fgets(line + maxlinelength - 1,
668 				  MAXLINELENGTH + 1, _gr_fp) == NULL)
669 				return NULL;
670 			maxlinelength += MAXLINELENGTH;
671 		} while (index(line + maxlinelength - MAXLINELENGTH - 1,
672 			       '\n') == NULL);
673 	}
674 
675 
676 	/*
677 	 * Ignore comments: ^[ \t]*#
678 	 */
679 	for (cp = line; *cp != '\0'; cp++)
680 		if (*cp != ' ' && *cp != '\t')
681 			break;
682 	if (*cp == '#' || *cp == '\0')
683 		goto tryagain;
684 
685 	if (cp != line) /* skip white space at beginning of line */
686 		bcopy(cp, line, strlen(cp));
687 
688 	return line;
689 }
690 
691 static int
692 copyline(const char *src)
693 {
694 	size_t	sz;
695 
696 	sz = strlen(src);
697 	if (sz > maxlinelength - 1) {
698 		sz = ((sz/MAXLINELENGTH)+1) * MAXLINELENGTH;
699 		if ((line = (char *) reallocf(line, sz)) == NULL)
700 			return 0;
701 		maxlinelength = sz;
702 	}
703 	strlcpy(line, src, maxlinelength);
704 	return 1;
705 }
706 
707