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