xref: /freebsd/lib/libc/gen/getgrent.c (revision eacee0ff7ec955b32e09515246bd97b6edcd2b0f)
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)
36 static 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$");
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 
67 static FILE		*_gr_fp;
68 static struct group	_gr_group;
69 static int		_gr_stayopen;
70 static int		_gr_filesdone;
71 
72 static void grcleanup(void);
73 static int grscan(int, gid_t, const char *);
74 static char *getline(void);
75 static int copyline(const char*);
76 static int matchline(int, gid_t, const char *);
77 static 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 
92 static char		**members;       /* list of group members */
93 static int		  maxgrp;        /* current length of **members */
94 static char		 *line;	         /* buffer for group line */
95 static 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
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 	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 
141 struct group *
142 getgrgid(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 
155 void
156 grcleanup()
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 
173 static int
174 start_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 
195 int
196 setgrent(void)
197 {
198 	return setgroupent(0);
199 }
200 
201 int
202 setgroupent(stayopen)
203 	int stayopen;
204 {
205 	if (!start_gr())
206 		return 0;
207 	_gr_stayopen = stayopen;
208 	return 1;
209 }
210 
211 void
212 endgrent()
213 {
214 	grcleanup();
215 	if (_gr_fp) {
216 		(void)fclose(_gr_fp);
217 		_gr_fp = NULL;
218 	}
219 }
220 
221 
222 static int _local_grscan(void *, void *, va_list);
223 
224 /*ARGSUSED*/
225 static 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
250 static int _dns_grscan(void *, void *, va_list);
251 
252 /*ARGSUSED*/
253 static 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
314 static int _nis_grscan(void *, void *, va_list);
315 
316 /*ARGSUSED*/
317 static 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  */
429 static int _bad_grscan(void *, void *, va_list);
430 
431 /*ARGSUSED*/
432 static 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 
455 static int __grscancompat(int, gid_t, const char *);
456 
457 static 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 
481 static int _compat_grscan(void *, void *, va_list);
482 
483 /*ARGSUSED*/
484 static 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 
566 static int
567 grscan(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 
590 static int
591 matchline(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 
647 static char *
648 getline(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 
690 static int
691 copyline(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