xref: /freebsd/lib/libc/gen/getgrent.c (revision 729362425c09cf6b362366aabc6fb547eee8035a)
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 		r = copyline(hp[0]);
297 		hesiod_free_list(context, hp);
298 		if (r == 0) {
299 			r = NS_UNAVAIL;
300 			break;
301 		}
302 		if (matchline(search, gid, name)) {
303 			r = NS_SUCCESS;
304 			break;
305 		} else if (search) {
306 			r = NS_NOTFOUND;
307 			break;
308 		}
309 	}
310 	hesiod_end(context);
311 	return (r);
312 }
313 #endif
314 
315 #ifdef YP
316 static int _nis_grscan(void *, void *, va_list);
317 
318 /*ARGSUSED*/
319 static int
320 _nis_grscan(rv, cb_data, ap)
321 	void	*rv;
322 	void	*cb_data;
323 	va_list	 ap;
324 {
325 	int		 search = va_arg(ap, int);
326 	gid_t		 gid = va_arg(ap, gid_t);
327 	const char	*name = va_arg(ap, const char *);
328 
329 	char	*key, *data;
330 	int	 keylen, datalen;
331 	int	 r;
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 		r = copyline(data);
367 		free(data);
368 		if (r == 0)
369 			return NS_UNAVAIL;
370 		if (matchline(search, gid, name))
371 			return NS_SUCCESS;
372 		else
373 			return NS_NOTFOUND;
374 	}
375 
376 						/* ! search */
377 	if (_gr_ypdone)
378 		return NS_NOTFOUND;
379 	for (;;) {
380 		data = NULL;
381 		if(__ypcurrent) {
382 			key = NULL;
383 			r = yp_next(__ypdomain, "group.byname",
384 				__ypcurrent, __ypcurrentlen,
385 				&key, &keylen, &data, &datalen);
386 			free(__ypcurrent);
387 			switch (r) {
388 			case 0:
389 				break;
390 			case YPERR_NOMORE:
391 				__ypcurrent = NULL;
392 				if (key)
393 					free(key);
394 				if (data)
395 					free(data);
396 				_gr_ypdone = 1;
397 				return NS_NOTFOUND;
398 			default:
399 				if (key)
400 					free(key);
401 				if (data)
402 					free(data);
403 				return NS_UNAVAIL;
404 			}
405 			__ypcurrent = key;
406 			__ypcurrentlen = keylen;
407 		} else {
408 			if (yp_first(__ypdomain, "group.byname",
409 					&__ypcurrent, &__ypcurrentlen,
410 					&data, &datalen)) {
411 				if (data)
412 					free(data);
413 				return NS_UNAVAIL;
414 			}
415 		}
416 		data[datalen] = '\0';			/* clear trailing \n */
417 		r = copyline(data);
418 		free(data);
419 		if (r == 0)
420 			return NS_UNAVAIL;
421 		if (matchline(search, gid, name))
422 			return NS_SUCCESS;
423 	}
424 	/* NOTREACHED */
425 }
426 #endif
427 
428 #ifdef _GROUP_COMPAT
429 /*
430  * log an error if "files" or "compat" is specified in group_compat database
431  */
432 static int _bad_grscan(void *, void *, va_list);
433 
434 /*ARGSUSED*/
435 static int
436 _bad_grscan(rv, cb_data, ap)
437 	void	*rv;
438 	void	*cb_data;
439 	va_list	 ap;
440 {
441 	static int warned;
442 
443 	if (!warned) {
444 		syslog(LOG_ERR,
445 			"nsswitch.conf group_compat database can't use '%s'",
446 			(char *)cb_data);
447 	}
448 	warned = 1;
449 	return NS_UNAVAIL;
450 }
451 
452 /*
453  * when a name lookup in compat mode is required, look it up in group_compat
454  * nsswitch database. only Hesiod and NIS is supported - it doesn't make
455  * sense to lookup compat names from 'files' or 'compat'
456  */
457 
458 static int __grscancompat(int, gid_t, const char *);
459 
460 static int
461 __grscancompat(search, gid, name)
462 	int		 search;
463 	gid_t		 gid;
464 	const char	*name;
465 {
466 	static const ns_dtab dtab[] = {
467 		NS_FILES_CB(_bad_grscan, "files")
468 		NS_DNS_CB(_dns_grscan, NULL)
469 		NS_NIS_CB(_nis_grscan, NULL)
470 		NS_COMPAT_CB(_bad_grscan, "compat")
471 		{ 0 }
472 	};
473 	static const ns_src defaultnis[] = {
474 		{ NSSRC_NIS, 	NS_SUCCESS },
475 		{ 0 }
476 	};
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(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 (getline() == NULL)
541 			return NS_NOTFOUND;
542 
543 #ifdef _GROUP_COMPAT
544 		if (line[0] == '+') {
545 			char	*tptr, *bp;
546 
547 			switch(line[1]) {
548 			case ':':
549 			case '\0':
550 			case '\n':
551 				__grmode = GRMODE_FULL;
552 				break;
553 			default:
554 				__grmode = GRMODE_NAME;
555 				bp = line;
556 				tptr = strsep(&bp, ":\n");
557 				grname = strdup(tptr + 1);
558 				break;
559 			}
560 			continue;
561 		}
562 #endif /* _GROUP_COMPAT */
563 		if (matchline(search, gid, name))
564 			return NS_SUCCESS;
565 	}
566 	/* NOTREACHED */
567 }
568 
569 static int
570 grscan(search, gid, name)
571 	int		 search;
572 	gid_t		 gid;
573 	const char	*name;
574 {
575 	int		r;
576 	static const ns_dtab dtab[] = {
577 		NS_FILES_CB(_local_grscan, NULL)
578 		NS_DNS_CB(_dns_grscan, NULL)
579 		NS_NIS_CB(_nis_grscan, NULL)
580 		NS_COMPAT_CB(_compat_grscan, NULL)
581 		{ 0 }
582 	};
583 	static const ns_src compatsrc[] = {
584 		{ NSSRC_COMPAT, NS_SUCCESS },
585 		{ 0 }
586 	};
587 
588 	r = nsdispatch(NULL, dtab, NSDB_GROUP, "grscan", compatsrc,
589 	    search, gid, name);
590 	return (r == NS_SUCCESS) ? 1 : 0;
591 }
592 
593 static int
594 matchline(search, gid, name)
595 	int		 search;
596 	gid_t		 gid;
597 	const char	*name;
598 {
599 	unsigned long	id;
600 	char		**m;
601 	char		*cp, *bp, *ep;
602 
603 	if (line[0] == '+')
604 		return 0;	/* sanity check to prevent recursion */
605 	bp = line;
606 	_gr_group.gr_name = strsep(&bp, ":\n");
607 	if (search && name && strcmp(_gr_group.gr_name, name))
608 		return 0;
609 	_gr_group.gr_passwd = strsep(&bp, ":\n");
610 	if (!(cp = strsep(&bp, ":\n")))
611 		return 0;
612 	id = strtoul(cp, &ep, 10);
613 	if (*ep != '\0')
614 		return 0;
615 	_gr_group.gr_gid = (gid_t)id;
616 	if (search && name == NULL && _gr_group.gr_gid != gid)
617 		return 0;
618 	cp = NULL;
619 	if (bp == NULL)
620 		return 0;
621 	for (_gr_group.gr_mem = m = members;; bp++) {
622 		if (m == &members[maxgrp - 1]) {
623 			members = (char **) reallocf(members, sizeof(char **) *
624 						     (maxgrp + MAXGRP));
625 			if (members == NULL)
626 				return 0;
627 			_gr_group.gr_mem = members;
628 			m = &members[maxgrp - 1];
629 			maxgrp += MAXGRP;
630 		}
631 		if (*bp == ',') {
632 			if (cp) {
633 				*bp = '\0';
634 				*m++ = cp;
635 				cp = NULL;
636 			}
637 		} else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
638 			if (cp) {
639 				*bp = '\0';
640 				*m++ = cp;
641 			}
642 			break;
643 		} else if (cp == NULL)
644 			cp = bp;
645         }
646 	*m = NULL;
647         return 1;
648 }
649 
650 static char *
651 getline(void)
652 {
653 	const char	*cp;
654 
655  tryagain:
656 	if (fgets(line, maxlinelength, _gr_fp) == NULL)
657 		return NULL;
658 	if (index(line, '\n') == NULL) {
659 		do {
660 			if (feof(_gr_fp))
661 				return NULL;
662 			if (MAXLINELENGTHLIMIT > 0 &&
663 			    maxlinelength >= MAXLINELENGTHLIMIT)
664 				return NULL;
665 			line = (char *)reallocf(line, maxlinelength +
666 						MAXLINELENGTH);
667 			if (line == NULL)
668 				return NULL;
669 			if (fgets(line + maxlinelength - 1,
670 				  MAXLINELENGTH + 1, _gr_fp) == NULL)
671 				return NULL;
672 			maxlinelength += MAXLINELENGTH;
673 		} while (index(line + maxlinelength - MAXLINELENGTH - 1,
674 			       '\n') == NULL);
675 	}
676 
677 
678 	/*
679 	 * Ignore comments: ^[ \t]*#
680 	 */
681 	for (cp = line; *cp != '\0'; cp++)
682 		if (*cp != ' ' && *cp != '\t')
683 			break;
684 	if (*cp == '#' || *cp == '\0')
685 		goto tryagain;
686 
687 	if (cp != line) /* skip white space at beginning of line */
688 		bcopy(cp, line, strlen(cp));
689 
690 	return line;
691 }
692 
693 static int
694 copyline(const char *src)
695 {
696 	size_t	sz;
697 
698 	sz = strlen(src);
699 	if (sz > maxlinelength - 1) {
700 		sz = ((sz/MAXLINELENGTH)+1) * MAXLINELENGTH;
701 		if ((line = (char *) reallocf(line, sz)) == NULL)
702 			return 0;
703 		maxlinelength = sz;
704 	}
705 	strlcpy(line, src, maxlinelength);
706 	return 1;
707 }
708 
709