xref: /netbsd-src/lib/libc/gen/getnetgrent.c (revision fe9e9e8f3a8cf5d158e494de016f9d788d5da73e)
1 /*	$NetBSD: getnetgrent.c,v 1.35 2006/07/27 20:54:42 dogcow 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.35 2006/07/27 20:54:42 dogcow 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 
73 static const char _ngstar[] = "*";
74 static struct netgroup *_nghead = NULL;
75 static struct netgroup *_nglist = NULL;
76 static DB *_ng_db;
77 
78 static int getstring(char **, int, __aconst char **);
79 static struct netgroup *getnetgroup(char **);
80 static int lookup(char *, char **, int);
81 static int addgroup(StringList *, char *);
82 static int in_check(const char *, const char *, const char *,
83     struct netgroup *);
84 static int in_find(StringList *, char *, const char *, const char *,
85     const char *);
86 static char *in_lookup1(const char *, const char *, int);
87 static int in_lookup(const char *, const char *, const char *, int);
88 
89 #ifdef NSSRC_FILES
90 static 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 }
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  */
103 static int
104 getstring(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  */
149 static struct netgroup *
150 getnetgroup(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 
181 baddomain:
182 	if (ng->ng_user)
183 		free(ng->ng_user);
184 baduser:
185 	if (ng->ng_host)
186 		free(ng->ng_host);
187 badhost:
188 	free(ng);
189 	return NULL;
190 }
191 
192 void
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 
203 static int _local_lookup(void *, void *, va_list);
204 
205 /*ARGSUSED*/
206 static 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
251 static int _nis_lookup(void *, void *, va_list);
252 
253 /*ARGSUSED*/
254 static 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  */
315 static int
316 lookup(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 		{ 0 }
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
333 static 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 
344 static int
345 lookup(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  */
361 int
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  */
410 static int
411 addgroup(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 printf("%s, %d: add %s\n", __FILE__, __LINE__, grp);
430 	if (sl_add(sl, grp) == -1) {
431 		free(grp);
432 		return 0;
433 	}
434 
435 	/* Lookup this netgroup */
436 	line = NULL;
437 	if (!lookup(grp, &line, _NG_KEYBYNAME)) {
438 		if (line)
439 			free(line);
440 		return 0;
441 	}
442 
443 	p = line;
444 
445 	for (;;) {
446 		switch (_ng_parse(&p, &name, &ng)) {
447 		case _NG_NONE:
448 			/* Done with the line */
449 			free(line);
450 			return 1;
451 
452 		case _NG_GROUP:
453 			/* new netgroup */
454 			/* add to the list */
455 			ng->ng_next = _nglist;
456 			_nglist = ng;
457 			break;
458 
459 		case _NG_NAME:
460 			/* netgroup name */
461 			if (!addgroup(sl, name))
462 				return 0;
463 			break;
464 
465 		case _NG_ERROR:
466 			return 0;
467 
468 		default:
469 			abort();
470 		}
471 	}
472 }
473 
474 
475 /*
476  * in_check(): Compare the spec with the netgroup
477  */
478 static int
479 in_check(const char *host, const char *user, const char *domain,
480     struct netgroup *ng)
481 {
482 
483 	/* host may be NULL */
484 	/* user may be NULL */
485 	/* domain may be NULL */
486 	_DIAGASSERT(ng != NULL);
487 
488 	if ((host != NULL) && (ng->ng_host != NULL)
489 	    && strcmp(ng->ng_host, host) != 0)
490 		return 0;
491 
492 	if ((user != NULL) && (ng->ng_user != NULL)
493 	    && strcmp(ng->ng_user, user) != 0)
494 		return 0;
495 
496 	if ((domain != NULL) && (ng->ng_domain != NULL)
497 	    && strcmp(ng->ng_domain, domain) != 0)
498 		return 0;
499 
500 	return 1;
501 }
502 
503 
504 /*
505  * in_find(): Find a match for the host, user, domain spec.
506  * grp is not a valid pointer after return (either free(3)ed or allocated
507  * to a stringlist). in either case, it shouldn't be used again.
508  */
509 static int
510 in_find(StringList *sl, char *grp, const char *host, const char *user,
511     const char *domain)
512 {
513 	char		*line, *p;
514 	int		 i;
515 	struct netgroup	*ng;
516 	char		*name;
517 
518 	_DIAGASSERT(sl != NULL);
519 	_DIAGASSERT(grp != NULL);
520 	/* host may be NULL */
521 	/* user may be NULL */
522 	/* domain may be NULL */
523 
524 #ifdef DEBUG_NG
525 	(void)fprintf(stderr, "in_find(%s)\n", grp);
526 #endif
527 	/* check for cycles */
528 	if (sl_find(sl, grp) != NULL) {
529 		_ng_cycle(grp, sl);
530 		free(grp);
531 		return 0;
532 	}
533 printf("%s, %d: add %s\n", __FILE__, __LINE__, grp);
534 	if (sl_add(sl, grp) == -1) {
535 		free(grp);
536 		return 0;
537 	}
538 
539 	/* Lookup this netgroup */
540 	line = NULL;
541 	if (!lookup(grp, &line, _NG_KEYBYNAME)) {
542 		if (line)
543 			free(line);
544 		return 0;
545 	}
546 
547 	p = line;
548 
549 	for (;;) {
550 		switch (_ng_parse(&p, &name, &ng)) {
551 		case _NG_NONE:
552 			/* Done with the line */
553 			free(line);
554 			return 0;
555 
556 		case _NG_GROUP:
557 			/* new netgroup */
558 			i = in_check(host, user, domain, ng);
559 			if (ng->ng_host != NULL)
560 				free(ng->ng_host);
561 			if (ng->ng_user != NULL)
562 				free(ng->ng_user);
563 			if (ng->ng_domain != NULL)
564 				free(ng->ng_domain);
565 			free(ng);
566 			if (i) {
567 				free(line);
568 				return 1;
569 			}
570 			break;
571 
572 		case _NG_NAME:
573 			/* netgroup name */
574 			if (in_find(sl, name, host, user, domain)) {
575 				free(line);
576 				return 1;
577 			}
578 			break;
579 
580 		case _NG_ERROR:
581 			free(line);
582 			return 0;
583 
584 		default:
585 			abort();
586 		}
587 	}
588 }
589 
590 /*
591  * _ng_makekey(): Make a key from the two names given. The key is of the form
592  * <name1>.<name2> Names strings are replaced with * if they are empty;
593  * Returns NULL if there's a problem.
594  */
595 char *
596 _ng_makekey(const char *s1, const char *s2, size_t len)
597 {
598 	char *buf;
599 
600 	/* s1 may be NULL */
601 	/* s2 may be NULL */
602 
603 	buf = malloc(len);
604 	if (buf != NULL)
605 		(void)snprintf(buf, len, "%s.%s", _NG_STAR(s1), _NG_STAR(s2));
606 	return buf;
607 }
608 
609 void
610 _ng_print(char *buf, size_t len, const struct netgroup *ng)
611 {
612 	_DIAGASSERT(buf != NULL);
613 	_DIAGASSERT(ng != NULL);
614 
615 	(void)snprintf(buf, len, "(%s,%s,%s)", _NG_EMPTY(ng->ng_host),
616 	    _NG_EMPTY(ng->ng_user), _NG_EMPTY(ng->ng_domain));
617 }
618 
619 
620 /*
621  * in_lookup1(): Fast lookup for a key in the appropriate map
622  */
623 static char *
624 in_lookup1(const char *key, const char *domain, int map)
625 {
626 	char	*line;
627 	size_t	 len;
628 	char	*ptr;
629 	int	 res;
630 
631 	/* key may be NULL */
632 	/* domain may be NULL */
633 
634 	len = (key ? strlen(key) : 1) + (domain ? strlen(domain) : 1) + 2;
635 	ptr = _ng_makekey(key, domain, len);
636 	if (ptr == NULL)
637 		return NULL;
638 	res = lookup(ptr, &line, map);
639 	free(ptr);
640 	return res ? line : NULL;
641 }
642 
643 
644 /*
645  * in_lookup(): Fast lookup for a key in the appropriate map
646  */
647 static int
648 in_lookup(const char *group, const char *key, const char *domain, int map)
649 {
650 	size_t	 len;
651 	char	*ptr, *line;
652 
653 	_DIAGASSERT(group != NULL);
654 	/* key may be NULL */
655 	/* domain may be NULL */
656 
657 	if (domain != NULL) {
658 		/* Domain specified; look in "group.domain" and "*.domain" */
659 		if ((line = in_lookup1(key, domain, map)) == NULL)
660 			line = in_lookup1(NULL, domain, map);
661 	} else
662 		line = NULL;
663 
664 	if (line == NULL) {
665 	    /*
666 	     * domain not specified or domain lookup failed; look in
667 	     * "group.*" and "*.*"
668 	     */
669 	    if (((line = in_lookup1(key, NULL, map)) == NULL) &&
670 		((line = in_lookup1(NULL, NULL, map)) == NULL))
671 		return 0;
672 	}
673 
674 	len = strlen(group);
675 
676 	for (ptr = line; (ptr = strstr(ptr, group)) != NULL;)
677 		/* Make sure we did not find a substring */
678 		if ((ptr != line && ptr[-1] != ',') ||
679 		    (ptr[len] != '\0' && strchr("\n\t ,", ptr[len]) == NULL))
680 			ptr++;
681 		else {
682 			free(line);
683 			return 1;
684 		}
685 
686 	free(line);
687 	return 0;
688 }
689 
690 
691 void
692 endnetgrent(void)
693 {
694 	for (_nglist = _nghead; _nglist != NULL; _nglist = _nghead) {
695 		_nghead = _nglist->ng_next;
696 		if (_nglist->ng_host != NULL)
697 			free(_nglist->ng_host);
698 		if (_nglist->ng_user != NULL)
699 			free(_nglist->ng_user);
700 		if (_nglist->ng_domain != NULL)
701 			free(_nglist->ng_domain);
702 		free(_nglist);
703 	}
704 
705 	if (_ng_db) {
706 		(void)(*_ng_db->close)(_ng_db);
707 		_ng_db = NULL;
708 	}
709 }
710 
711 
712 void
713 setnetgrent(const char *ng)
714 {
715 	StringList	*sl;
716 	char		*ng_copy;
717 
718 	_DIAGASSERT(ng != NULL);
719 
720 	sl = sl_init();
721 	if (sl == NULL)
722 		return;
723 
724 	/* Cleanup any previous storage */
725 	if (_nghead != NULL)
726 		endnetgrent();
727 
728 	if (_ng_db == NULL)
729 		_ng_db = dbopen(_PATH_NETGROUP_DB, O_RDONLY, 0, DB_HASH, NULL);
730 
731 	ng_copy = strdup(ng);
732 	if (ng_copy != NULL)
733 		addgroup(sl, ng_copy);
734 	_nghead = _nglist;
735 	sl_free(sl, 1);
736 }
737 
738 
739 int
740 getnetgrent(const char **host, const char **user, const char **domain)
741 {
742 	_DIAGASSERT(host != NULL);
743 	_DIAGASSERT(user != NULL);
744 	_DIAGASSERT(domain != NULL);
745 
746 	if (_nglist == NULL)
747 		return 0;
748 
749 	*host   = _nglist->ng_host;
750 	*user   = _nglist->ng_user;
751 	*domain = _nglist->ng_domain;
752 
753 	_nglist = _nglist->ng_next;
754 
755 	return 1;
756 }
757 
758 
759 int
760 innetgr(const char *grp, const char *host, const char *user, const char *domain)
761 {
762 	int	 found;
763 	StringList *sl;
764 	char *grcpy;
765 
766 	_DIAGASSERT(grp != NULL);
767 	/* host may be NULL */
768 	/* user may be NULL */
769 	/* domain may be NULL */
770 
771 	if (_ng_db == NULL)
772 		_ng_db = dbopen(_PATH_NETGROUP_DB, O_RDONLY, 0, DB_HASH, NULL);
773 
774 	/* Try the fast lookup first */
775 	if (host != NULL && user == NULL) {
776 		if (in_lookup(grp, host, domain, _NG_KEYBYHOST))
777 			return 1;
778 	} else if (host == NULL && user != NULL) {
779 		if (in_lookup(grp, user, domain, _NG_KEYBYUSER))
780 			return 1;
781 	}
782 	/* If a domainname is given, we would have found a match */
783 	if (domain != NULL)
784 		return 0;
785 
786 	/* Too bad need the slow recursive way */
787 	sl = sl_init();
788 	if (sl == NULL)
789 		return 0;
790 	if ((grcpy = strdup(grp)) == NULL) {
791 		sl_free(sl, 1);
792 		return 0;
793 	}
794 	found = in_find(sl, grcpy, host, user, domain);
795 	sl_free(sl, 1);
796 
797 	return found;
798 }
799