xref: /openbsd-src/lib/libc/gen/getnetgrent.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: getnetgrent.c,v 1.10 2000/12/09 23:04:16 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1994 Christos Zoulas
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by Christos Zoulas.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
22  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #if defined(LIBC_SCCS) && !defined(lint)
35 static char *rcsid = "$OpenBSD: getnetgrent.c,v 1.10 2000/12/09 23:04:16 deraadt Exp $";
36 #endif /* LIBC_SCCS and not lint */
37 
38 #include <sys/types.h>
39 #include <stdio.h>
40 #define _NETGROUP_PRIVATE
41 #include <netgroup.h>
42 #include <string.h>
43 #include <fcntl.h>
44 #include <err.h>
45 #include <ctype.h>
46 #include <stdlib.h>
47 #include <db.h>
48 #ifdef YP
49 #include <rpcsvc/ypclnt.h>
50 #endif
51 
52 #define _NG_STAR(s)	(((s) == NULL || *(s) == '\0') ? _ngstar : s)
53 #define _NG_EMPTY(s)	((s) == NULL ? "" : s)
54 #define _NG_ISSPACE(p)	(isspace((unsigned char) (p)) || (p) == '\n')
55 
56 static const char _ngstar[] = "*";
57 static const char _ngoomem[] = "netgroup: %m";
58 static struct netgroup *_nghead = (struct netgroup *)NULL;
59 static struct netgroup *_nglist = (struct netgroup *)NULL;
60 static DB *_ng_db;
61 
62 /*
63  * Simple string list
64  */
65 struct stringlist {
66 	char		**sl_str;
67 	size_t		  sl_max;
68 	size_t		  sl_cur;
69 };
70 
71 static int		getstring __P((char **, int, char **));
72 static struct netgroup	*getnetgroup __P((char **));
73 static int		 lookup __P((const char *, char *, char **, int));
74 static void		 addgroup __P((char *, struct stringlist *, char *));
75 static int		 in_check __P((const char *, const char *,
76 				       const char *, struct netgroup *));
77 static int		 in_find __P((char *, struct stringlist *,
78 				      char *, const char *,
79 				      const char *, const char *));
80 static char		*in_lookup1 __P((const char *, const char *,
81 					 const char *, int));
82 static int		 in_lookup __P((const char *, const char *,
83 					const char *, const char *, int));
84 
85 /*
86  * _ng_sl_init(): Initialize a string list
87  */
88 struct stringlist *
89 _ng_sl_init()
90 {
91 	struct stringlist *sl = malloc(sizeof(struct stringlist));
92 	if (sl == NULL)
93 		_err(1, _ngoomem);
94 
95 	sl->sl_cur = 0;
96 	sl->sl_max = 20;
97 	sl->sl_str = malloc(sl->sl_max * sizeof(char *));
98 	if (sl->sl_str == NULL)
99 		_err(1, _ngoomem);
100 	return sl;
101 }
102 
103 
104 /*
105  * _ng_sl_add(): Add an item to the string list
106  */
107 void
108 _ng_sl_add(sl, name)
109 	struct stringlist	*sl;
110 	char			*name;
111 {
112 	if (sl->sl_cur == sl->sl_max - 1) {
113 		char **slstr;
114 
115 		sl->sl_max += 20;
116 		slstr = realloc(sl->sl_str, sl->sl_max * sizeof(char *));
117 		if (slstr == NULL) {
118 			if (sl->sl_str)
119 				free(sl->sl_str);
120 			sl->sl_str = NULL;
121 			_err(1, _ngoomem);
122 		}
123 		sl->sl_str = slstr;
124 	}
125 	sl->sl_str[sl->sl_cur++] = name;
126 }
127 
128 
129 /*
130  * _ng_sl_free(): Free a stringlist
131  */
132 void
133 _ng_sl_free(sl, all)
134 	struct stringlist	*sl;
135 	int			 all;
136 {
137 	size_t	i;
138 
139 	if (all)
140 		for (i = 0; i < sl->sl_cur; i++)
141 			free(sl->sl_str[i]);
142 	free(sl->sl_str);
143 	free(sl);
144 }
145 
146 
147 /*
148  * sl_find(): Find a name in the string list
149  */
150 char *
151 _ng_sl_find(sl, name)
152 	struct stringlist	*sl;
153 	char			*name;
154 {
155 	size_t	i;
156 
157 	for (i = 0; i < sl->sl_cur; i++)
158 		if (strcmp(sl->sl_str[i], name) == 0)
159 			return sl->sl_str[i];
160 
161 	return NULL;
162 }
163 
164 
165 /*
166  * getstring(): Get a string delimited by the character, skipping leading and
167  * trailing blanks and advancing the pointer
168  */
169 static int
170 getstring(pp, del, str)
171 	char	**pp;
172 	int	  del;
173 	char	**str;
174 {
175 	char *sp, *ep, *dp;
176 
177 	/* skip leading blanks */
178 	for (sp = *pp; *sp && _NG_ISSPACE(*sp); sp++)
179 		continue;
180 
181 	/* accumulate till delimiter or space */
182 	for (ep = sp; *ep && *ep != del && !_NG_ISSPACE(*ep); ep++)
183 		continue;
184 
185 	/* hunt for the delimiter */
186 	for (dp = ep; *dp && *dp != del && _NG_ISSPACE(*dp); dp++)
187 		continue;
188 
189 	if (*dp != del) {
190 		*str = NULL;
191 		return 0;
192 	}
193 
194 	*pp = ++dp;
195 
196 	del = (ep - sp) + 1;
197 	if (del > 1) {
198 		dp = malloc(del);
199 		if (dp == NULL)
200 			_err(1, _ngoomem);
201 		memcpy(dp, sp, del);
202 		dp[del - 1] = '\0';
203 	} else
204 		dp = NULL;
205 
206 	*str = dp;
207 	return 1;
208 }
209 
210 
211 /*
212  * getnetgroup(): Parse a netgroup, and advance the pointer
213  */
214 static struct netgroup *
215 getnetgroup(pp)
216 	char	**pp;
217 {
218 	struct netgroup *ng = malloc(sizeof(struct netgroup));
219 
220 	if (ng == NULL)
221 		_err(1, _ngoomem);
222 
223 	(*pp)++;	/* skip '(' */
224 	if (!getstring(pp, ',', &ng->ng_host))
225 		goto badhost;
226 
227 	if (!getstring(pp, ',', &ng->ng_user))
228 		goto baduser;
229 
230 	if (!getstring(pp, ')', &ng->ng_domain))
231 		goto baddomain;
232 
233 #ifdef DEBUG_NG
234 	{
235 		char buf[1024];
236 		(void) fprintf(stderr, "netgroup %s\n",
237 		    _ng_print(buf, sizeof(buf), ng));
238 	}
239 #endif
240 	return ng;
241 
242 baddomain:
243 	if (ng->ng_user)
244 		free(ng->ng_user);
245 baduser:
246 	if (ng->ng_host)
247 		free(ng->ng_host);
248 badhost:
249 	free(ng);
250 	return NULL;
251 }
252 
253 
254 /*
255  * lookup(): Find the given key in the database or yp, and return its value
256  * in *line; returns 1 if key was found, 0 otherwise
257  */
258 static int
259 lookup(ypdom, name, line, bywhat)
260 	const char	 *ypdom;
261 	char		 *name;
262 	char		**line;
263 	int		  bywhat;
264 {
265 #ifdef YP
266 	int		i;
267 	char	       *map = NULL;
268 #endif
269 
270 	if (_ng_db) {
271 		DBT	 key, data;
272 		size_t	 len = strlen(name) + 2;
273 		char	*ks = malloc(len);
274 
275 		ks[0] = bywhat;
276 		memcpy(&ks[1], name, len - 1);
277 
278 		key.data = (u_char *) ks;
279 		key.size = len;
280 
281 		switch ((_ng_db->get) (_ng_db, &key, &data, 0)) {
282 		case 0:
283 			free(ks);
284 			*line = strdup(data.data);
285 			if (*line == NULL)
286 				_err(1, _ngoomem);
287 			return 1;
288 
289 		case 1:
290 			break;
291 
292 		case -1:
293 			_warn("netgroup: db get");
294 			break;
295 		}
296 		free(ks);
297 	}
298 #ifdef YP
299 	if (ypdom) {
300 		switch (bywhat) {
301 		case _NG_KEYBYNAME:
302 			map = "netgroup";
303 			break;
304 
305 		case _NG_KEYBYUSER:
306 			map = "netgroup.byuser";
307 			break;
308 
309 		case _NG_KEYBYHOST:
310 			map = "netgroup.byhost";
311 			break;
312 		}
313 
314 
315 		if (yp_match(ypdom, map, name, strlen(name), line, &i) == 0)
316 			return 1;
317 	}
318 #endif
319 
320 	return 0;
321 }
322 
323 
324 /*
325  * _ng_parse(): Parse a line and return: _NG_ERROR: Syntax Error _NG_NONE:
326  * line was empty or a comment _NG_GROUP: line had a netgroup definition,
327  * returned in ng _NG_NAME:  line had a netgroup name, returned in name
328  *
329  * Public since used by netgroup_mkdb
330  */
331 int
332 _ng_parse(p, name, ng)
333 	char		**p;
334 	char		**name;
335 	struct netgroup	**ng;
336 {
337 	while (**p) {
338 		if (**p == '#')
339 			/* comment */
340 			return _NG_NONE;
341 
342 		while (**p && _NG_ISSPACE(**p))
343 			/* skipblank */
344 			(*p)++;
345 
346 		if (**p == '(') {
347 			if ((*ng = getnetgroup(p)) == NULL) {
348 				_warnx("netgroup: Syntax error `%s'", *p);
349 				return _NG_ERROR;
350 			}
351 			return _NG_GROUP;
352 		} else {
353 			char	       *np;
354 			int		i;
355 
356 			for (np = *p; **p && !_NG_ISSPACE(**p); (*p)++)
357 				continue;
358 			if (np != *p) {
359 				i = (*p - np) + 1;
360 				*name = malloc(i);
361 				if (*name == NULL)
362 					_err(1, _ngoomem);
363 				memcpy(*name, np, i);
364 				(*name)[i - 1] = '\0';
365 				return _NG_NAME;
366 			}
367 		}
368 	}
369 	return _NG_NONE;
370 }
371 
372 
373 /*
374  * addgroup(): Recursively add all the members of the netgroup to this group
375  */
376 static void
377 addgroup(ypdom, sl, grp)
378 	char			*ypdom;
379 	struct stringlist	*sl;
380 	char			*grp;
381 {
382 	char		*line, *p;
383 	struct netgroup	*ng;
384 	char		*name;
385 
386 #ifdef DEBUG_NG
387 	(void) fprintf(stderr, "addgroup(%s)\n", grp);
388 #endif
389 	/* check for cycles */
390 	if (_ng_sl_find(sl, grp) != NULL) {
391 		free(grp);
392 		_warnx("netgroup: Cycle in group `%s'", grp);
393 		return;
394 	}
395 	_ng_sl_add(sl, grp);
396 
397 	/* Lookup this netgroup */
398 	if (!lookup(ypdom, grp, &line, _NG_KEYBYNAME))
399 		return;
400 
401 	p = line;
402 
403 	for (;;) {
404 		switch (_ng_parse(&p, &name, &ng)) {
405 		case _NG_NONE:
406 			/* Done with the line */
407 			free(line);
408 			return;
409 
410 		case _NG_GROUP:
411 			/* new netgroup */
412 			/* add to the list */
413 			ng->ng_next = _nglist;
414 			_nglist = ng;
415 			break;
416 
417 		case _NG_NAME:
418 			/* netgroup name */
419 			addgroup(ypdom, sl, name);
420 			break;
421 
422 		case _NG_ERROR:
423 			return;
424 		}
425 	}
426 }
427 
428 
429 /*
430  * in_check(): Compare the spec with the netgroup
431  */
432 static int
433 in_check(host, user, domain, ng)
434 	const char	*host;
435 	const char	*user;
436 	const char	*domain;
437 	struct netgroup	*ng;
438 {
439 	if ((host != NULL) && (ng->ng_host != NULL)
440 	    && strcmp(ng->ng_host, host) != 0)
441 		return 0;
442 
443 	if ((user != NULL) && (ng->ng_user != NULL)
444 	    && strcmp(ng->ng_user, user) != 0)
445 		return 0;
446 
447 	if ((domain != NULL) && (ng->ng_domain != NULL)
448 	    && strcmp(ng->ng_domain, domain) != 0)
449 		return 0;
450 
451 	return 1;
452 }
453 
454 
455 /*
456  * in_find(): Find a match for the host, user, domain spec
457  */
458 static int
459 in_find(ypdom, sl, grp, host, user, domain)
460 	char			*ypdom;
461 	struct stringlist	*sl;
462 	char			*grp;
463 	const char		*host;
464 	const char		*user;
465 	const char		*domain;
466 {
467 	char		*line, *p;
468 	int		 i;
469 	struct netgroup	*ng;
470 	char		*name;
471 
472 #ifdef DEBUG_NG
473 	(void) fprintf(stderr, "in_find(%s)\n", grp);
474 #endif
475 	/* check for cycles */
476 	if (_ng_sl_find(sl, grp) != NULL) {
477 		free(grp);
478 		_warnx("netgroup: Cycle in group `%s'", grp);
479 		return 0;
480 	}
481 	_ng_sl_add(sl, grp);
482 
483 	/* Lookup this netgroup */
484 	if (!lookup(ypdom, grp, &line, _NG_KEYBYNAME))
485 		return 0;
486 
487 	p = line;
488 
489 	for (;;) {
490 		switch (_ng_parse(&p, &name, &ng)) {
491 		case _NG_NONE:
492 			/* Done with the line */
493 			free(line);
494 			return 0;
495 
496 		case _NG_GROUP:
497 			/* new netgroup */
498 			i = in_check(host, user, domain, ng);
499 			if (ng->ng_host != NULL)
500 				free(ng->ng_host);
501 			if (ng->ng_user != NULL)
502 				free(ng->ng_user);
503 			if (ng->ng_domain != NULL)
504 				free(ng->ng_domain);
505 			free(ng);
506 			if (i) {
507 				free(line);
508 				return 1;
509 			}
510 			break;
511 
512 		case _NG_NAME:
513 			/* netgroup name */
514 			if (in_find(ypdom, sl, name, host, user, domain)) {
515 				free(line);
516 				return 1;
517 			}
518 			break;
519 
520 		case _NG_ERROR:
521 			free(line);
522 			return 0;
523 		}
524 	}
525 }
526 
527 
528 /*
529  * _ng_makekey(): Make a key from the two names given. The key is of the form
530  * <name1>.<name2> Names strings are replaced with * if they are empty;
531  */
532 char *
533 _ng_makekey(s1, s2, len)
534 	const char	*s1, *s2;
535 	size_t		 len;
536 {
537 	char *buf = malloc(len);
538 	if (buf == NULL)
539 		_err(1, _ngoomem);
540 	(void) snprintf(buf, len, "%s.%s", _NG_STAR(s1), _NG_STAR(s2));
541 	return buf;
542 }
543 
544 void
545 _ng_print(buf, len, ng)
546 	char *buf;
547 	size_t len;
548 	const struct netgroup *ng;
549 {
550 	(void) snprintf(buf, len, "(%s,%s,%s)", _NG_EMPTY(ng->ng_host),
551 	    _NG_EMPTY(ng->ng_user), _NG_EMPTY(ng->ng_domain));
552 }
553 
554 
555 /*
556  * in_lookup1(): Fast lookup for a key in the appropriate map
557  */
558 static char *
559 in_lookup1(ypdom, key, domain, map)
560 	const char	*ypdom;
561 	const char	*key;
562 	const char	*domain;
563 	int		 map;
564 {
565 	char	*line;
566 	size_t	 len;
567 	char	*ptr;
568 	int	 res;
569 
570 	len = (key ? strlen(key) : 1) + (domain ? strlen(domain) : 1) + 2;
571 	ptr = _ng_makekey(key, domain, len);
572 	res = lookup(ypdom, ptr, &line, map);
573 	free(ptr);
574 	return res ? line : NULL;
575 }
576 
577 
578 /*
579  * in_lookup(): Fast lookup for a key in the appropriate map
580  */
581 static int
582 in_lookup(ypdom, group, key, domain, map)
583 	const char	*ypdom;
584 	const char	*group;
585 	const char	*key;
586 	const char	*domain;
587 	int		 map;
588 {
589 	size_t	 len;
590 	char	*ptr, *line;
591 
592 	if (domain != NULL) {
593 		/* Domain specified; look in "group.domain" and "*.domain" */
594 		if ((line = in_lookup1(ypdom, key, domain, map)) == NULL)
595 			line = in_lookup1(ypdom, NULL, domain, map);
596 	} else
597 		line = NULL;
598 
599 	if (line == NULL) {
600 		/*
601 		 * domain not specified or domain lookup failed; look in
602 		 * "group.*" and "*.*"
603 		 */
604 	    if (((line = in_lookup1(ypdom, key, NULL, map)) == NULL) &&
605 		((line = in_lookup1(ypdom, NULL, NULL, map)) == NULL))
606 		return 0;
607 	}
608 
609 	len = strlen(group);
610 
611 	for (ptr = line; (ptr = strstr(ptr, group)) != NULL;)
612 		/* Make sure we did not find a substring */
613 		if ((ptr != line && ptr[-1] != ',') ||
614 		    (ptr[len] != '\0' && strchr("\n\t ,", ptr[len]) == NULL))
615 			ptr++;
616 		else {
617 			free(line);
618 			return 1;
619 		}
620 
621 	free(line);
622 	return 0;
623 }
624 
625 
626 void
627 endnetgrent()
628 {
629 	for (_nglist = _nghead; _nglist != NULL; _nglist = _nghead) {
630 		_nghead = _nglist->ng_next;
631 		if (_nglist->ng_host != NULL)
632 			free(_nglist->ng_host);
633 		if (_nglist->ng_user != NULL)
634 			free(_nglist->ng_user);
635 		if (_nglist->ng_domain != NULL)
636 			free(_nglist->ng_domain);
637 		free(_nglist);
638 	}
639 
640 	if (_ng_db) {
641 		(void) (_ng_db->close) (_ng_db);
642 		_ng_db = NULL;
643 	}
644 }
645 
646 
647 void
648 setnetgrent(ng)
649 	const char	*ng;
650 {
651 	struct stringlist	*sl = _ng_sl_init();
652 #ifdef YP
653 	char			*line;
654 #endif
655 	char			*ng_copy, *ypdom = NULL;
656 
657 	/* Cleanup any previous storage */
658 	if (_nghead != NULL)
659 		endnetgrent();
660 
661 	if (_ng_db == NULL)
662 		_ng_db = dbopen(_PATH_NETGROUP_DB, O_RDONLY, 0, DB_HASH, NULL);
663 
664 #ifdef YP
665 	/*
666 	 * We use yp if there is a "+" in the netgroup file, or if there is
667 	 * no netgroup file at all
668 	 */
669 	if (_ng_db == NULL || lookup(NULL, "+", &line, _NG_KEYBYNAME) == 0)
670 		yp_get_default_domain(&ypdom);
671 	else
672 		free(line);
673 #endif
674 	ng_copy = strdup(ng);
675 	if (ng_copy == NULL)
676 		_err(1, _ngoomem);
677 	addgroup(ypdom, sl, ng_copy);
678 	_nghead = _nglist;
679 	_ng_sl_free(sl, 1);
680 }
681 
682 
683 int
684 getnetgrent(host, user, domain)
685 	const char	**host;
686 	const char	**user;
687 	const char	**domain;
688 {
689 	if (_nglist == NULL)
690 		return 0;
691 
692 	*host   = _nglist->ng_host;
693 	*user   = _nglist->ng_user;
694 	*domain = _nglist->ng_domain;
695 
696 	_nglist = _nglist->ng_next;
697 
698 	return 1;
699 }
700 
701 
702 int
703 innetgr(grp, host, user, domain)
704 	const char	*grp, *host, *user, *domain;
705 {
706 	char	*ypdom = NULL;
707 #ifdef YP
708 	char	*line = NULL;
709 #endif
710 	int	 found;
711 	struct stringlist *sl;
712 
713 	if (_ng_db == NULL)
714 		_ng_db = dbopen(_PATH_NETGROUP_DB, O_RDONLY, 0, DB_HASH, NULL);
715 
716 #ifdef YP
717 	/*
718 	 * We use yp if there is a "+" in the netgroup file, or if there is
719 	 * no netgroup file at all
720 	 */
721 	if (_ng_db == NULL)
722 		yp_get_default_domain(&ypdom);
723 	else if (lookup(NULL, "+", &line, _NG_KEYBYNAME) == 0)
724 		yp_get_default_domain(&ypdom);
725 
726 	if (line)
727 		free(line);
728 #endif
729 
730 	/* Try the fast lookup first */
731 	if (host != NULL && user == NULL) {
732 		if (in_lookup(ypdom, grp, host, domain, _NG_KEYBYHOST))
733 			return 1;
734 	} else if (host == NULL && user != NULL) {
735 		if (in_lookup(ypdom, grp, user, domain, _NG_KEYBYUSER))
736 			return 1;
737 	}
738 	/* If a domainname is given, we would have found a match */
739 	if (domain != NULL)
740 		return 0;
741 
742 	/* Too bad need the slow recursive way */
743 	sl = _ng_sl_init();
744 	found = in_find(ypdom, sl, strdup(grp), host, user, domain);
745 	_ng_sl_free(sl, 1);
746 
747 	return found;
748 }
749