1/*	$OpenBSD: getnetgrent.c,v 1.32 2024/01/22 17:21:52 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 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
17 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/types.h>
30#include <stdio.h>
31#include <netgroup.h>
32#include <string.h>
33#include <fcntl.h>
34#include <err.h>
35#include <ctype.h>
36#include <stdlib.h>
37#include <db.h>
38#ifdef YP
39#include <rpcsvc/ypclnt.h>
40#endif
41
42#define _NG_STAR(s)	(((s) == NULL || *(s) == '\0') ? _ngstar : s)
43#define _NG_EMPTY(s)	((s) == NULL ? "" : s)
44#define _NG_ISSPACE(p)	(isspace((unsigned char) (p)) || (p) == '\n')
45
46static const char _ngstar[] = "*";
47static struct netgroup *_nghead = (struct netgroup *)NULL;
48static struct netgroup *_nglist = (struct netgroup *)NULL;
49static DB *_ng_db;
50
51/*
52 * Simple string list
53 */
54struct stringlist {
55	char		**sl_str;
56	size_t		  sl_max;
57	size_t		  sl_cur;
58};
59
60static struct stringlist *_ng_sl_init(void);
61static int	_ng_sl_add(struct stringlist *, char *);
62static void	_ng_sl_free(struct stringlist *, int);
63static char    *_ng_sl_find(struct stringlist *, char *);
64static char    *_ng_makekey(const char *, const char *, size_t);
65static int	_ng_parse(char **, char **, struct netgroup **);
66static void	_ng_print(char *, size_t, const struct netgroup *);
67
68static int		getstring(char **, int, char **);
69static struct netgroup	*getnetgroup(char **);
70static int		 lookup(const char *, char *, char **, int);
71static void		 addgroup(char *, struct stringlist *, char *);
72static int		 in_check(const char *, const char *,
73			    const char *, struct netgroup *);
74static int		 in_find(char *, struct stringlist *,
75			    char *, const char *, const char *, const char *);
76static char		*in_lookup1(const char *, const char *,
77			    const char *, int);
78static int		 in_lookup(const char *, const char *,
79			    const char *, const char *, int);
80
81/*
82 * _ng_sl_init(): Initialize a string list
83 */
84static struct stringlist *
85_ng_sl_init(void)
86{
87	struct stringlist *sl = malloc(sizeof(struct stringlist));
88	if (sl == NULL)
89		return NULL;
90
91	sl->sl_cur = 0;
92	sl->sl_max = 20;
93	sl->sl_str = calloc(sl->sl_max, sizeof(char *));
94	if (sl->sl_str == NULL) {
95		free(sl);
96		return NULL;
97	}
98	return sl;
99}
100
101
102/*
103 * _ng_sl_add(): Add an item to the string list
104 */
105static int
106_ng_sl_add(struct stringlist *sl, char *name)
107{
108	if (sl->sl_cur == sl->sl_max - 1) {
109		char **slstr;
110
111		sl->sl_max += 20;
112		slstr = reallocarray(sl->sl_str, sl->sl_max, sizeof(char *));
113		if (slstr == NULL) {
114			free(sl->sl_str);
115			sl->sl_str = NULL;
116			return -1;
117		}
118		sl->sl_str = slstr;
119	}
120	sl->sl_str[sl->sl_cur++] = name;
121	return 0;
122}
123
124
125/*
126 * _ng_sl_free(): Free a stringlist
127 */
128static void
129_ng_sl_free(struct stringlist *sl, int all)
130{
131	size_t	i;
132
133	if (all)
134		for (i = 0; i < sl->sl_cur; i++)
135			free(sl->sl_str[i]);
136	free(sl->sl_str);
137	free(sl);
138}
139
140
141/*
142 * sl_find(): Find a name in the string list
143 */
144static char *
145_ng_sl_find(struct stringlist *sl, char *name)
146{
147	size_t	i;
148
149	for (i = 0; i < sl->sl_cur; i++)
150		if (strcmp(sl->sl_str[i], name) == 0)
151			return sl->sl_str[i];
152
153	return NULL;
154}
155
156
157/*
158 * getstring(): Get a string delimited by the character, skipping leading and
159 * trailing blanks and advancing the pointer
160 */
161static int
162getstring(char **pp, int del, char **str)
163{
164	char *sp, *ep, *dp;
165
166	/* skip leading blanks */
167	for (sp = *pp; *sp && _NG_ISSPACE(*sp); sp++)
168		continue;
169
170	/* accumulate till delimiter or space */
171	for (ep = sp; *ep && *ep != del && !_NG_ISSPACE(*ep); ep++)
172		continue;
173
174	/* hunt for the delimiter */
175	for (dp = ep; *dp && *dp != del && _NG_ISSPACE(*dp); dp++)
176		continue;
177
178	if (*dp != del) {
179		*str = NULL;
180		return 0;
181	}
182
183	*pp = ++dp;
184
185	del = (ep - sp) + 1;
186	if (del > 1) {
187		dp = malloc(del);
188		if (dp == NULL)
189			return 0;
190		memcpy(dp, sp, del);
191		dp[del - 1] = '\0';
192	} else
193		dp = NULL;
194
195	*str = dp;
196	return 1;
197}
198
199
200/*
201 * getnetgroup(): Parse a netgroup, and advance the pointer
202 */
203static struct netgroup *
204getnetgroup(char **pp)
205{
206	struct netgroup *ng = malloc(sizeof(struct netgroup));
207
208	if (ng == NULL)
209		return NULL;
210
211	(*pp)++;	/* skip '(' */
212	if (!getstring(pp, ',', &ng->ng_host))
213		goto badhost;
214
215	if (!getstring(pp, ',', &ng->ng_user))
216		goto baduser;
217
218	if (!getstring(pp, ')', &ng->ng_domain))
219		goto baddomain;
220
221#ifdef DEBUG_NG
222	{
223		char buf[1024];
224		_ng_print(buf, sizeof(buf), ng);
225		fprintf(stderr, "netgroup %s\n", buf);
226	}
227#endif
228	return ng;
229
230baddomain:
231	free(ng->ng_user);
232baduser:
233	free(ng->ng_host);
234badhost:
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 */
244static int
245lookup(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 */
315static 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 */
355static void
356addgroup(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 */
411static int
412in_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 */
434static int
435in_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			free(ng->ng_host);
474			free(ng->ng_user);
475			free(ng->ng_domain);
476			free(ng);
477			if (i) {
478				free(line);
479				return 1;
480			}
481			break;
482
483		case _NG_NAME:
484			/* netgroup name */
485			if (in_find(ypdom, sl, name, host, user, domain)) {
486				free(line);
487				return 1;
488			}
489			break;
490
491		case _NG_ERROR:
492			free(line);
493			return 0;
494		}
495	}
496}
497
498
499/*
500 * _ng_makekey(): Make a key from the two names given. The key is of the form
501 * <name1>.<name2> Names strings are replaced with * if they are empty;
502 */
503static char *
504_ng_makekey(const char *s1, const char *s2, size_t len)
505{
506	char *buf = malloc(len);
507	int ret;
508
509	if (buf == NULL)
510		return NULL;
511	ret = snprintf(buf, len, "%s.%s", _NG_STAR(s1), _NG_STAR(s2));
512	if (ret < 0 || ret >= len) {
513		free(buf);
514		return NULL;
515	}
516
517	return buf;
518}
519
520#ifdef DEBUG_NG
521static 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#endif
528
529
530/*
531 * in_lookup1(): Fast lookup for a key in the appropriate map
532 */
533static char *
534in_lookup1(const char *ypdom, const char *key, const char *domain, int map)
535{
536	char	*line;
537	size_t	 len;
538	char	*ptr;
539	int	 res;
540
541	len = (key ? strlen(key) : 1) + (domain ? strlen(domain) : 1) + 2;
542	ptr = _ng_makekey(key, domain, len);
543	if (ptr == NULL)
544		return NULL;
545	res = lookup(ypdom, ptr, &line, map);
546	free(ptr);
547	return res ? line : NULL;
548}
549
550
551/*
552 * in_lookup(): Fast lookup for a key in the appropriate map
553 */
554static int
555in_lookup(const char *ypdom, const char *group, const char *key,
556    const char *domain, int map)
557{
558	size_t	 len;
559	char	*ptr, *line;
560
561	if (domain != NULL) {
562		/* Domain specified; look in "group.domain" and "*.domain" */
563		if ((line = in_lookup1(ypdom, key, domain, map)) == NULL)
564			line = in_lookup1(ypdom, NULL, domain, map);
565	} else
566		line = NULL;
567
568	if (line == NULL) {
569		/*
570		 * domain not specified or domain lookup failed; look in
571		 * "group.*" and "*.*"
572		 */
573	    if (((line = in_lookup1(ypdom, key, NULL, map)) == NULL) &&
574		((line = in_lookup1(ypdom, NULL, NULL, map)) == NULL))
575		return 0;
576	}
577
578	len = strlen(group);
579
580	for (ptr = line; (ptr = strstr(ptr, group)) != NULL;)
581		/* Make sure we did not find a substring */
582		if ((ptr != line && ptr[-1] != ',') ||
583		    (ptr[len] != '\0' && strchr("\n\t ,", ptr[len]) == NULL))
584			ptr++;
585		else {
586			free(line);
587			return 1;
588		}
589
590	free(line);
591	return 0;
592}
593
594
595void
596endnetgrent(void)
597{
598	for (_nglist = _nghead; _nglist != NULL; _nglist = _nghead) {
599		_nghead = _nglist->ng_next;
600		free(_nglist->ng_host);
601		free(_nglist->ng_user);
602		free(_nglist->ng_domain);
603		free(_nglist);
604	}
605
606	if (_ng_db) {
607		(void) (_ng_db->close) (_ng_db);
608		_ng_db = NULL;
609	}
610}
611DEF_WEAK(endnetgrent);
612
613
614void
615setnetgrent(const char *ng)
616{
617	struct stringlist	*sl;
618#ifdef YP
619	static char		*__ypdomain;
620	char			*line = NULL;
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 = __hash_open(_PATH_NETGROUP_DB, O_RDONLY, 0, NULL, 0);
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		if (!__ypdomain)
642			yp_get_default_domain(&__ypdomain);
643		ypdom = __ypdomain;
644	}
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}
653DEF_WEAK(setnetgrent);
654
655
656int
657getnetgrent(const char **host, const char **user, const char **domain)
658{
659	if (_nglist == NULL)
660		return 0;
661
662	*host   = _nglist->ng_host;
663	*user   = _nglist->ng_user;
664	*domain = _nglist->ng_domain;
665
666	_nglist = _nglist->ng_next;
667
668	return 1;
669}
670DEF_WEAK(getnetgrent);
671
672
673int
674innetgr(const char *grp, const char *host, const char *user, const char *domain)
675{
676	char		*ypdom = NULL, *grpdup;
677#ifdef YP
678	static char	*__ypdomain;
679	char		*line = NULL;
680#endif
681	int	 found;
682	struct stringlist *sl;
683
684	if (_ng_db == NULL)
685		_ng_db = __hash_open(_PATH_NETGROUP_DB, O_RDONLY, 0, NULL, 0);
686
687#ifdef YP
688	/*
689	 * We use yp if there is a "+" in the netgroup file, or if there is
690	 * no netgroup file at all
691	 */
692	if (_ng_db == NULL || lookup(NULL, "+", &line, _NG_KEYBYNAME) == 0) {
693		if (!__ypdomain)
694			yp_get_default_domain(&__ypdomain);
695		ypdom = __ypdomain;
696	}
697
698	free(line);
699#endif
700
701	/* Try the fast lookup first */
702	if (host != NULL && user == NULL) {
703		if (in_lookup(ypdom, grp, host, domain, _NG_KEYBYHOST))
704			return 1;
705	} else if (host == NULL && user != NULL) {
706		if (in_lookup(ypdom, grp, user, domain, _NG_KEYBYUSER))
707			return 1;
708	}
709
710	/* Too bad need the slow recursive way */
711	sl = _ng_sl_init();
712	if (sl == NULL)
713		return 0;
714
715	grpdup = strdup(grp);
716	if (grpdup == NULL) {
717		_ng_sl_free(sl, 1);
718		return 0;
719	}
720
721	found = in_find(ypdom, sl, grpdup, host, user, domain);
722	_ng_sl_free(sl, 1);
723
724	return found;
725}
726DEF_WEAK(innetgr);
727