xref: /freebsd/lib/libc/gen/getgrent.c (revision 4b2eaea43fec8e8792be611dea204071a10b655a)
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 
267 	r = NS_UNAVAIL;
268 	if (!search && _gr_hesnum == -1)
269 		return NS_NOTFOUND;
270 	if (hesiod_init(&context) == -1)
271 		return (r);
272 
273 	for (;;) {
274 		if (search) {
275 			if (name)
276 				strlcpy(line, name, maxlinelength);
277 			else
278 				snprintf(line, maxlinelength, "%u",
279 				    (unsigned int)gid);
280 		} else {
281 			snprintf(line, maxlinelength, "group-%u", _gr_hesnum);
282 			_gr_hesnum++;
283 		}
284 
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 		if (copyline(hp[0]) == 0)
297 			return NS_UNAVAIL;
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(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 			strlcpy(line, name, maxlinelength);
344 		else
345 			snprintf(line, maxlinelength, "%u", (unsigned int)gid);
346 		data = NULL;
347 		r = yp_match(__ypdomain,
348 				(name) ? "group.byname" : "group.bygid",
349 				line, (int)strlen(line), &data, &datalen);
350 		switch (r) {
351 		case 0:
352 			break;
353 		case YPERR_KEY:
354 			if (data)
355 				free(data);
356 			return NS_NOTFOUND;
357 		default:
358 			if (data)
359 				free(data);
360 			return NS_UNAVAIL;
361 		}
362 		data[datalen] = '\0';			/* clear trailing \n */
363 		if (copyline(data) == 0)
364 			return NS_UNAVAIL;
365 		free(data);
366 		if (matchline(search, gid, name))
367 			return NS_SUCCESS;
368 		else
369 			return NS_NOTFOUND;
370 	}
371 
372 						/* ! search */
373 	if (_gr_ypdone)
374 		return NS_NOTFOUND;
375 	for (;;) {
376 		data = NULL;
377 		if(__ypcurrent) {
378 			key = NULL;
379 			r = yp_next(__ypdomain, "group.byname",
380 				__ypcurrent, __ypcurrentlen,
381 				&key, &keylen, &data, &datalen);
382 			free(__ypcurrent);
383 			switch (r) {
384 			case 0:
385 				break;
386 			case YPERR_NOMORE:
387 				__ypcurrent = NULL;
388 				if (key)
389 					free(key);
390 				if (data)
391 					free(data);
392 				_gr_ypdone = 1;
393 				return NS_NOTFOUND;
394 			default:
395 				if (key)
396 					free(key);
397 				if (data)
398 					free(data);
399 				return NS_UNAVAIL;
400 			}
401 			__ypcurrent = key;
402 			__ypcurrentlen = keylen;
403 		} else {
404 			if (yp_first(__ypdomain, "group.byname",
405 					&__ypcurrent, &__ypcurrentlen,
406 					&data, &datalen)) {
407 				if (data)
408 					free(data);
409 				return NS_UNAVAIL;
410 			}
411 		}
412 		data[datalen] = '\0';			/* clear trailing \n */
413 		if (copyline(data) == 0)
414 			return NS_UNAVAIL;
415 		free(data);
416 		if (matchline(search, gid, name))
417 			return NS_SUCCESS;
418 	}
419 	/* NOTREACHED */
420 }
421 #endif
422 
423 #ifdef _GROUP_COMPAT
424 /*
425  * log an error if "files" or "compat" is specified in group_compat database
426  */
427 static int _bad_grscan(void *, void *, va_list);
428 
429 /*ARGSUSED*/
430 static int
431 _bad_grscan(rv, cb_data, ap)
432 	void	*rv;
433 	void	*cb_data;
434 	va_list	 ap;
435 {
436 	static int warned;
437 
438 	if (!warned) {
439 		syslog(LOG_ERR,
440 			"nsswitch.conf group_compat database can't use '%s'",
441 			(char *)cb_data);
442 	}
443 	warned = 1;
444 	return NS_UNAVAIL;
445 }
446 
447 /*
448  * when a name lookup in compat mode is required, look it up in group_compat
449  * nsswitch database. only Hesiod and NIS is supported - it doesn't make
450  * sense to lookup compat names from 'files' or 'compat'
451  */
452 
453 static int __grscancompat(int, gid_t, const char *);
454 
455 static int
456 __grscancompat(search, gid, name)
457 	int		 search;
458 	gid_t		 gid;
459 	const char	*name;
460 {
461 	static const ns_dtab dtab[] = {
462 		NS_FILES_CB(_bad_grscan, "files")
463 		NS_DNS_CB(_dns_grscan, NULL)
464 		NS_NIS_CB(_nis_grscan, NULL)
465 		NS_COMPAT_CB(_bad_grscan, "compat")
466 		{ 0 }
467 	};
468 	static const ns_src defaultnis[] = {
469 		{ NSSRC_NIS, 	NS_SUCCESS },
470 		{ 0 }
471 	};
472 
473 	return (nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "grscancompat",
474 	    defaultnis, search, gid, name));
475 }
476 #endif
477 
478 
479 static int _compat_grscan(void *, void *, va_list);
480 
481 /*ARGSUSED*/
482 static int
483 _compat_grscan(rv, cb_data, ap)
484 	void	*rv;
485 	void	*cb_data;
486 	va_list	 ap;
487 {
488 	int		 search = va_arg(ap, int);
489 	gid_t		 gid = va_arg(ap, gid_t);
490 	const char	*name = va_arg(ap, const char *);
491 
492 #ifdef _GROUP_COMPAT
493 	static char	*grname = NULL;
494 #endif
495 
496 	for (;;) {
497 #ifdef _GROUP_COMPAT
498 		if(__grmode != GRMODE_NONE) {
499 			int	 r;
500 
501 			switch(__grmode) {
502 			case GRMODE_FULL:
503 				r = __grscancompat(search, gid, name);
504 				if (r == NS_SUCCESS)
505 					return r;
506 				__grmode = GRMODE_NONE;
507 				break;
508 			case GRMODE_NAME:
509 				if(grname == (char *)NULL) {
510 					__grmode = GRMODE_NONE;
511 					break;
512 				}
513 				r = __grscancompat(1, 0, grname);
514 				free(grname);
515 				grname = (char *)NULL;
516 				if (r != NS_SUCCESS)
517 					break;
518 				if (!search)
519 					return NS_SUCCESS;
520 				if (name) {
521 					if (! strcmp(_gr_group.gr_name, name))
522 						return NS_SUCCESS;
523 				} else {
524 					if (_gr_group.gr_gid == gid)
525 						return NS_SUCCESS;
526 				}
527 				break;
528 			case GRMODE_NONE:
529 				abort();
530 			}
531 			continue;
532 		}
533 #endif /* _GROUP_COMPAT */
534 
535 		if (getline() == NULL)
536 			return NS_NOTFOUND;
537 
538 #ifdef _GROUP_COMPAT
539 		if (line[0] == '+') {
540 			char	*tptr, *bp;
541 
542 			switch(line[1]) {
543 			case ':':
544 			case '\0':
545 			case '\n':
546 				__grmode = GRMODE_FULL;
547 				break;
548 			default:
549 				__grmode = GRMODE_NAME;
550 				bp = line;
551 				tptr = strsep(&bp, ":\n");
552 				grname = strdup(tptr + 1);
553 				break;
554 			}
555 			continue;
556 		}
557 #endif /* _GROUP_COMPAT */
558 		if (matchline(search, gid, name))
559 			return NS_SUCCESS;
560 	}
561 	/* NOTREACHED */
562 }
563 
564 static int
565 grscan(search, gid, name)
566 	int		 search;
567 	gid_t		 gid;
568 	const char	*name;
569 {
570 	int		r;
571 	static const ns_dtab dtab[] = {
572 		NS_FILES_CB(_local_grscan, NULL)
573 		NS_DNS_CB(_dns_grscan, NULL)
574 		NS_NIS_CB(_nis_grscan, NULL)
575 		NS_COMPAT_CB(_compat_grscan, NULL)
576 		{ 0 }
577 	};
578 	static const ns_src compatsrc[] = {
579 		{ NSSRC_COMPAT, NS_SUCCESS },
580 		{ 0 }
581 	};
582 
583 	r = nsdispatch(NULL, dtab, NSDB_GROUP, "grscan", compatsrc,
584 	    search, gid, name);
585 	return (r == NS_SUCCESS) ? 1 : 0;
586 }
587 
588 static int
589 matchline(search, gid, name)
590 	int		 search;
591 	gid_t		 gid;
592 	const char	*name;
593 {
594 	unsigned long	id;
595 	char		**m;
596 	char		*cp, *bp, *ep;
597 
598 	if (line[0] == '+')
599 		return 0;	/* sanity check to prevent recursion */
600 	bp = line;
601 	_gr_group.gr_name = strsep(&bp, ":\n");
602 	if (search && name && strcmp(_gr_group.gr_name, name))
603 		return 0;
604 	_gr_group.gr_passwd = strsep(&bp, ":\n");
605 	if (!(cp = strsep(&bp, ":\n")))
606 		return 0;
607 	id = strtoul(cp, &ep, 10);
608 	if (*ep != '\0')
609 		return 0;
610 	_gr_group.gr_gid = (gid_t)id;
611 	if (search && name == NULL && _gr_group.gr_gid != gid)
612 		return 0;
613 	cp = NULL;
614 	if (bp == NULL)
615 		return 0;
616 	for (_gr_group.gr_mem = m = members;; bp++) {
617 		if (m == &members[maxgrp - 1]) {
618 			members = (char **) reallocf(members, sizeof(char **) *
619 						     (maxgrp + MAXGRP));
620 			if (members == NULL)
621 				return 0;
622 			_gr_group.gr_mem = members;
623 			m = &members[maxgrp - 1];
624 			maxgrp += MAXGRP;
625 		}
626 		if (*bp == ',') {
627 			if (cp) {
628 				*bp = '\0';
629 				*m++ = cp;
630 				cp = NULL;
631 			}
632 		} else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
633 			if (cp) {
634 				*bp = '\0';
635 				*m++ = cp;
636 			}
637 			break;
638 		} else if (cp == NULL)
639 			cp = bp;
640         }
641 	*m = NULL;
642         return 1;
643 }
644 
645 static char *
646 getline(void)
647 {
648 	const char	*cp;
649 
650  tryagain:
651 	if (fgets(line, maxlinelength, _gr_fp) == NULL)
652 		return NULL;
653 	if (index(line, '\n') == NULL) {
654 		do {
655 			if (feof(_gr_fp))
656 				return NULL;
657 			if (MAXLINELENGTHLIMIT > 0 &&
658 			    maxlinelength >= MAXLINELENGTHLIMIT)
659 				return NULL;
660 			line = (char *)reallocf(line, maxlinelength +
661 						MAXLINELENGTH);
662 			if (line == NULL)
663 				return NULL;
664 			if (fgets(line + maxlinelength - 1,
665 				  MAXLINELENGTH + 1, _gr_fp) == NULL)
666 				return NULL;
667 			maxlinelength += MAXLINELENGTH;
668 		} while (index(line + maxlinelength - MAXLINELENGTH - 1,
669 			       '\n') == NULL);
670 	}
671 
672 
673 	/*
674 	 * Ignore comments: ^[ \t]*#
675 	 */
676 	for (cp = line; *cp != '\0'; cp++)
677 		if (*cp != ' ' && *cp != '\t')
678 			break;
679 	if (*cp == '#' || *cp == '\0')
680 		goto tryagain;
681 
682 	if (cp != line) /* skip white space at beginning of line */
683 		bcopy(cp, line, strlen(cp));
684 
685 	return line;
686 }
687 
688 static int
689 copyline(const char *src)
690 {
691 	size_t	sz;
692 
693 	sz = strlen(src);
694 	if (sz > maxlinelength - 1) {
695 		sz = ((sz/MAXLINELENGTH)+1) * MAXLINELENGTH;
696 		if ((line = (char *) reallocf(line, sz)) == NULL)
697 			return 0;
698 		maxlinelength = sz;
699 	}
700 	strlcpy(line, src, maxlinelength);
701 	return 1;
702 }
703 
704