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