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