getnetgrent.c revision 237159
1/*
2 * Copyright (c) 1992, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Rick Macklem at The University of Guelph.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#if defined(LIBC_SCCS) && !defined(lint)
34static char sccsid[] = "@(#)getnetgrent.c	8.2 (Berkeley) 4/27/95";
35#endif /* LIBC_SCCS and not lint */
36#include <sys/cdefs.h>
37__FBSDID("$FreeBSD: head/lib/libc/gen/getnetgrent.c 237159 2012-06-16 13:10:22Z kib $");
38
39#include <ctype.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <unistd.h>
44
45#ifdef YP
46/*
47 * Notes:
48 * We want to be able to use NIS netgroups properly while retaining
49 * the ability to use a local /etc/netgroup file. Unfortunately, you
50 * can't really do both at the same time - at least, not efficiently.
51 * NetBSD deals with this problem by creating a netgroup database
52 * using Berkeley DB (just like the password database) that allows
53 * for lookups using netgroup, netgroup.byuser or netgroup.byhost
54 * searches. This is a neat idea, but I don't have time to implement
55 * something like that now. (I think ultimately it would be nice
56 * if we DB-fied the group and netgroup stuff all in one shot, but
57 * for now I'm satisfied just to have something that works well
58 * without requiring massive code changes.)
59 *
60 * Therefore, to still permit the use of the local file and maintain
61 * optimum NIS performance, we allow for the following conditions:
62 *
63 * - If /etc/netgroup does not exist and NIS is turned on, we use
64 *   NIS netgroups only.
65 *
66 * - If /etc/netgroup exists but is empty, we use NIS netgroups
67 *   only.
68 *
69 * - If /etc/netgroup exists and contains _only_ a '+', we use
70 *   NIS netgroups only.
71 *
72 * - If /etc/netgroup exists, contains locally defined netgroups
73 *   and a '+', we use a mixture of NIS and the local entries.
74 *   This method should return the same NIS data as just using
75 *   NIS alone, but it will be slower if the NIS netgroup database
76 *   is large (innetgr() in particular will suffer since extra
77 *   processing has to be done in order to determine memberships
78 *   using just the raw netgroup data).
79 *
80 * - If /etc/netgroup exists and contains only locally defined
81 *   netgroup entries, we use just those local entries and ignore
82 *   NIS (this is the original, pre-NIS behavior).
83 */
84
85#include <rpc/rpc.h>
86#include <rpcsvc/yp_prot.h>
87#include <rpcsvc/ypclnt.h>
88#include <sys/types.h>
89#include <sys/stat.h>
90#include <sys/param.h>
91#include <sys/errno.h>
92static char *_netgr_yp_domain;
93int _use_only_yp;
94static int _netgr_yp_enabled;
95static int _yp_innetgr;
96#endif
97
98#ifndef _PATH_NETGROUP
99#define _PATH_NETGROUP "/etc/netgroup"
100#endif
101
102/*
103 * Static Variables and functions used by setnetgrent(), getnetgrent() and
104 * endnetgrent().
105 * There are two linked lists:
106 * - linelist is just used by setnetgrent() to parse the net group file via.
107 *   parse_netgrp()
108 * - netgrp is the list of entries for the current netgroup
109 */
110struct linelist {
111	struct linelist	*l_next;	/* Chain ptr. */
112	int		l_parsed;	/* Flag for cycles */
113	char		*l_groupname;	/* Name of netgroup */
114	char		*l_line;	/* Netgroup entrie(s) to be parsed */
115};
116
117struct netgrp {
118	struct netgrp	*ng_next;	/* Chain ptr */
119	char		*ng_str[3];	/* Field pointers, see below */
120};
121#define NG_HOST		0	/* Host name */
122#define NG_USER		1	/* User name */
123#define NG_DOM		2	/* and Domain name */
124
125static struct linelist	*linehead = (struct linelist *)0;
126static struct netgrp	*nextgrp = (struct netgrp *)0;
127static struct {
128	struct netgrp	*gr;
129	char		*grname;
130} grouphead = {
131	(struct netgrp *)0,
132	(char *)0,
133};
134static FILE *netf = (FILE *)0;
135
136static int parse_netgrp(const char *);
137static struct linelist *read_for_group(const char *);
138void setnetgrent(const char *);
139void endnetgrent(void);
140int getnetgrent(char **, char **, char **);
141int innetgr(const char *, const char *, const char *, const char *);
142
143#define	LINSIZ	1024	/* Length of netgroup file line */
144
145/*
146 * setnetgrent()
147 * Parse the netgroup file looking for the netgroup and build the list
148 * of netgrp structures. Let parse_netgrp() and read_for_group() do
149 * most of the work.
150 */
151void
152setnetgrent(const char *group)
153{
154#ifdef YP
155	struct stat _yp_statp;
156	char _yp_plus;
157#endif
158
159	/* Sanity check */
160
161	if (group == NULL || !strlen(group))
162		return;
163
164	if (grouphead.gr == (struct netgrp *)0 ||
165		strcmp(group, grouphead.grname)) {
166		endnetgrent();
167#ifdef YP
168		/* Presumed guilty until proven innocent. */
169		_use_only_yp = 0;
170		/*
171		 * If /etc/netgroup doesn't exist or is empty,
172		 * use NIS exclusively.
173		 */
174		if (((stat(_PATH_NETGROUP, &_yp_statp) < 0) &&
175			errno == ENOENT) || _yp_statp.st_size == 0)
176			_use_only_yp = _netgr_yp_enabled = 1;
177		if ((netf = fopen(_PATH_NETGROUP,"r")) != NULL ||_use_only_yp){
178		/*
179		 * Icky: grab the first character of the netgroup file
180		 * and turn on NIS if it's a '+'. rewind the stream
181		 * afterwards so we don't goof up read_for_group() later.
182		 */
183			if (netf) {
184				fscanf(netf, "%c", &_yp_plus);
185				rewind(netf);
186				if (_yp_plus == '+')
187					_use_only_yp = _netgr_yp_enabled = 1;
188			}
189		/*
190		 * If we were called specifically for an innetgr()
191		 * lookup and we're in NIS-only mode, short-circuit
192		 * parse_netgroup() and cut directly to the chase.
193		 */
194			if (_use_only_yp && _yp_innetgr) {
195				/* dohw! */
196				if (netf != NULL)
197					fclose(netf);
198				return;
199			}
200#else
201		if ((netf = fopen(_PATH_NETGROUP, "r"))) {
202#endif
203			if (parse_netgrp(group))
204				endnetgrent();
205			else {
206				grouphead.grname = strdup(group);
207			}
208			if (netf)
209				fclose(netf);
210		}
211	}
212	nextgrp = grouphead.gr;
213}
214
215/*
216 * Get the next netgroup off the list.
217 */
218int
219getnetgrent(char **hostp, char **userp, char **domp)
220{
221#ifdef YP
222	_yp_innetgr = 0;
223#endif
224
225	if (nextgrp) {
226		*hostp = nextgrp->ng_str[NG_HOST];
227		*userp = nextgrp->ng_str[NG_USER];
228		*domp = nextgrp->ng_str[NG_DOM];
229		nextgrp = nextgrp->ng_next;
230		return (1);
231	}
232	return (0);
233}
234
235/*
236 * endnetgrent() - cleanup
237 */
238void
239endnetgrent(void)
240{
241	struct linelist *lp, *olp;
242	struct netgrp *gp, *ogp;
243
244	lp = linehead;
245	while (lp) {
246		olp = lp;
247		lp = lp->l_next;
248		free(olp->l_groupname);
249		free(olp->l_line);
250		free((char *)olp);
251	}
252	linehead = (struct linelist *)0;
253	if (grouphead.grname) {
254		free(grouphead.grname);
255		grouphead.grname = (char *)0;
256	}
257	gp = grouphead.gr;
258	while (gp) {
259		ogp = gp;
260		gp = gp->ng_next;
261		if (ogp->ng_str[NG_HOST])
262			free(ogp->ng_str[NG_HOST]);
263		if (ogp->ng_str[NG_USER])
264			free(ogp->ng_str[NG_USER]);
265		if (ogp->ng_str[NG_DOM])
266			free(ogp->ng_str[NG_DOM]);
267		free((char *)ogp);
268	}
269	grouphead.gr = (struct netgrp *)0;
270	nextgrp = (struct netgrp *)0;
271#ifdef YP
272	_netgr_yp_enabled = 0;
273#endif
274}
275
276#ifdef YP
277static int
278_listmatch(const char *list, const char *group, int len)
279{
280	const char *ptr = list;
281	const char *cptr;
282	int glen = strlen(group);
283
284	/* skip possible leading whitespace */
285	while(isspace((unsigned char)*ptr))
286		ptr++;
287
288	while (ptr < list + len) {
289		cptr = ptr;
290		while(*ptr != ','  && *ptr != '\0' && !isspace((unsigned char)*ptr))
291			ptr++;
292		if (strncmp(cptr, group, glen) == 0 && glen == (ptr - cptr))
293			return (1);
294		while(*ptr == ','  || isspace((unsigned char)*ptr))
295			ptr++;
296	}
297
298	return (0);
299}
300
301static int
302_revnetgr_lookup(char* lookupdom, char* map, const char* str,
303		 const char* dom, const char* group)
304{
305	int y, rv, rot;
306	char key[MAXHOSTNAMELEN];
307	char *result;
308	int resultlen;
309
310	for (rot = 0; ; rot++) {
311		switch (rot) {
312		case 0:
313			snprintf(key, MAXHOSTNAMELEN, "%s.%s", str,
314			    dom ? dom : lookupdom);
315			break;
316		case 1:
317			snprintf(key, MAXHOSTNAMELEN, "%s.*", str);
318			break;
319		case 2:
320			snprintf(key, MAXHOSTNAMELEN, "*.%s",
321			    dom ? dom : lookupdom);
322			break;
323		case 3:
324			snprintf(key, MAXHOSTNAMELEN, "*.*");
325			break;
326		default:
327			return (0);
328		}
329		y = yp_match(lookupdom, map, key, strlen(key), &result,
330		    &resultlen);
331		if (y == 0) {
332			rv = _listmatch(result, group, resultlen);
333			free(result);
334			if (rv)
335				return (1);
336		} else if (y != YPERR_KEY) {
337			/*
338			 * If we get an error other than 'no
339			 * such key in map' then something is
340			 * wrong and we should stop the search.
341			 */
342			return (-1);
343		}
344	}
345}
346#endif
347
348/*
349 * Search for a match in a netgroup.
350 */
351int
352innetgr(const char *group, const char *host, const char *user, const char *dom)
353{
354	char *hst, *usr, *dm;
355	/* Sanity check */
356
357	if (group == NULL || !strlen(group))
358		return (0);
359
360#ifdef YP
361	_yp_innetgr = 1;
362#endif
363	setnetgrent(group);
364#ifdef YP
365	_yp_innetgr = 0;
366	/*
367	 * If we're in NIS-only mode, do the search using
368	 * NIS 'reverse netgroup' lookups.
369	 *
370	 * What happens with 'reverse netgroup' lookups:
371	 *
372	 * 1) try 'reverse netgroup' lookup
373	 *    1.a) if host is specified and user is null:
374	 *         look in netgroup.byhost
375	 *         (try host.domain, host.*, *.domain or *.*)
376	 *         if found, return yes
377	 *    1.b) if user is specified and host is null:
378	 *         look in netgroup.byuser
379	 *         (try host.domain, host.*, *.domain or *.*)
380	 *         if found, return yes
381	 *    1.c) if both host and user are specified,
382	 *         don't do 'reverse netgroup' lookup.  It won't work.
383	 *    1.d) if neither host ane user are specified (why?!?)
384	 *         don't do 'reverse netgroup' lookup either.
385	 * 2) if domain is specified and 'reverse lookup' is done:
386	 *    'reverse lookup' was authoritative.  bye bye.
387	 * 3) otherwise, too bad, try it the slow way.
388	 */
389	if (_use_only_yp && (host == NULL) != (user == NULL)) {
390		int ret;
391		if(yp_get_default_domain(&_netgr_yp_domain))
392			return (0);
393		ret = _revnetgr_lookup(_netgr_yp_domain,
394				      host?"netgroup.byhost":"netgroup.byuser",
395				      host?host:user, dom, group);
396		if (ret == 1)
397			return (1);
398		else if (ret == 0 && dom != NULL)
399			return (0);
400	}
401
402	setnetgrent(group);
403#endif /* YP */
404
405	while (getnetgrent(&hst, &usr, &dm))
406		if ((host == NULL || hst == NULL || !strcmp(host, hst)) &&
407		    (user == NULL || usr == NULL || !strcmp(user, usr)) &&
408		    ( dom == NULL ||  dm == NULL || !strcmp(dom, dm))) {
409			endnetgrent();
410			return (1);
411		}
412	endnetgrent();
413	return (0);
414}
415
416/*
417 * Parse the netgroup file setting up the linked lists.
418 */
419static int
420parse_netgrp(const char *group)
421{
422	struct netgrp *grp;
423	struct linelist *lp = linehead;
424	char **ng;
425	char *epos, *gpos, *pos, *spos;
426	int freepos, len, strpos;
427#ifdef DEBUG
428	int fields;
429#endif
430
431	/*
432	 * First, see if the line has already been read in.
433	 */
434	while (lp) {
435		if (!strcmp(group, lp->l_groupname))
436			break;
437		lp = lp->l_next;
438	}
439	if (lp == (struct linelist *)0 &&
440	    (lp = read_for_group(group)) == (struct linelist *)0)
441		return (1);
442	if (lp->l_parsed) {
443#ifdef DEBUG
444		/*
445		 * This error message is largely superflous since the
446		 * code handles the error condition sucessfully, and
447		 * spewing it out from inside libc can actually hose
448		 * certain programs.
449		 */
450		fprintf(stderr, "Cycle in netgroup %s\n", lp->l_groupname);
451#endif
452		return (1);
453	} else
454		lp->l_parsed = 1;
455	pos = lp->l_line;
456	/* Watch for null pointer dereferences, dammit! */
457	while (pos != NULL && *pos != '\0') {
458		if (*pos == '(') {
459			grp = malloc(sizeof(*grp));
460			if (grp == NULL)
461				return (1);
462			ng = grp->ng_str;
463			bzero(grp, sizeof(*grp));
464			pos++;
465			gpos = strsep(&pos, ")");
466#ifdef DEBUG
467			fields = 0;
468#endif
469			for (strpos = 0; strpos < 3; strpos++) {
470				if ((spos = strsep(&gpos, ",")) == NULL) {
471					/*
472					 * All other systems I've tested
473					 * return NULL for empty netgroup
474					 * fields. It's up to user programs
475					 * to handle the NULLs appropriately.
476					 */
477					ng[strpos] = NULL;
478					continue;
479				}
480#ifdef DEBUG
481				fields++;
482#endif
483				while (*spos == ' ' || *spos == '\t')
484					spos++;
485				if ((epos = strpbrk(spos, " \t"))) {
486					*epos = '\0';
487					len = epos - spos;
488				} else
489					len = strlen(spos);
490				if (len <= 0)
491					continue;
492				ng[strpos] = malloc(len + 1);
493				if (ng[strpos] == NULL) {
494					for (freepos = 0; freepos < strpos;
495					    freepos++)
496						free(ng[freepos]);
497					free(grp);
498					return (1);
499				}
500				bcopy(spos, ng[strpos], len + 1);
501			}
502			grp->ng_next = grouphead.gr;
503			grouphead.gr = grp;
504#ifdef DEBUG
505			/*
506			 * Note: on other platforms, malformed netgroup
507			 * entries are not normally flagged. While we
508			 * can catch bad entries and report them, we should
509			 * stay silent by default for compatibility's sake.
510			 */
511			if (fields < 3) {
512				fprintf(stderr,
513				"Bad entry (%s%s%s%s%s) in netgroup \"%s\"\n",
514				    ng[NG_HOST] == NULL ? "" : ng[NG_HOST],
515				    ng[NG_USER] == NULL ? "" : ",",
516				    ng[NG_USER] == NULL ? "" : ng[NG_USER],
517				    ng[NG_DOM] == NULL ? "" : ",",
518				    ng[NG_DOM] == NULL ? "" : ng[NG_DOM],
519				    lp->l_groupname);
520#endif
521		} else {
522			spos = strsep(&pos, ", \t");
523			if (parse_netgrp(spos))
524				continue;
525		}
526		if (pos == NULL)
527			break;
528		while (*pos == ' ' || *pos == ',' || *pos == '\t')
529			pos++;
530	}
531	return (0);
532}
533
534/*
535 * Read the netgroup file and save lines until the line for the netgroup
536 * is found. Return 1 if eof is encountered.
537 */
538static struct linelist *
539read_for_group(const char *group)
540{
541	char *linep, *olinep, *pos, *spos;
542	int len, olen;
543	int cont;
544	struct linelist *lp;
545	char line[LINSIZ + 2];
546#ifdef YP
547	char *result;
548	int resultlen;
549	linep = NULL;
550
551	while (_netgr_yp_enabled || fgets(line, LINSIZ, netf) != NULL) {
552		if (_netgr_yp_enabled) {
553			if(!_netgr_yp_domain)
554				if(yp_get_default_domain(&_netgr_yp_domain))
555					continue;
556			if (yp_match(_netgr_yp_domain, "netgroup", group,
557			    strlen(group), &result, &resultlen)) {
558				free(result);
559				if (_use_only_yp)
560					return ((struct linelist *)0);
561				else {
562					_netgr_yp_enabled = 0;
563					continue;
564				}
565			}
566			snprintf(line, LINSIZ, "%s %s", group, result);
567			free(result);
568		}
569#else
570	linep = NULL;
571	while (fgets(line, LINSIZ, netf) != NULL) {
572#endif
573		pos = (char *)&line;
574#ifdef YP
575		if (*pos == '+') {
576			_netgr_yp_enabled = 1;
577			continue;
578		}
579#endif
580		if (*pos == '#')
581			continue;
582		while (*pos == ' ' || *pos == '\t')
583			pos++;
584		spos = pos;
585		while (*pos != ' ' && *pos != '\t' && *pos != '\n' &&
586			*pos != '\0')
587			pos++;
588		len = pos - spos;
589		while (*pos == ' ' || *pos == '\t')
590			pos++;
591		if (*pos != '\n' && *pos != '\0') {
592			lp = (struct linelist *)malloc(sizeof (*lp));
593			if (lp == NULL)
594				return (NULL);
595			lp->l_parsed = 0;
596			lp->l_groupname = (char *)malloc(len + 1);
597			if (lp->l_groupname == NULL) {
598				free(lp);
599				return (NULL);
600			}
601			bcopy(spos, lp->l_groupname, len);
602			*(lp->l_groupname + len) = '\0';
603			len = strlen(pos);
604			olen = 0;
605
606			/*
607			 * Loop around handling line continuations.
608			 */
609			do {
610				if (*(pos + len - 1) == '\n')
611					len--;
612				if (*(pos + len - 1) == '\\') {
613					len--;
614					cont = 1;
615				} else
616					cont = 0;
617				if (len > 0) {
618					linep = malloc(olen + len + 1);
619					if (linep == NULL) {
620						free(lp->l_groupname);
621						free(lp);
622						return (NULL);
623					}
624					if (olen > 0) {
625						bcopy(olinep, linep, olen);
626						free(olinep);
627					}
628					bcopy(pos, linep + olen, len);
629					olen += len;
630					*(linep + olen) = '\0';
631					olinep = linep;
632				}
633				if (cont) {
634					if (fgets(line, LINSIZ, netf)) {
635						pos = line;
636						len = strlen(pos);
637					} else
638						cont = 0;
639				}
640			} while (cont);
641			lp->l_line = linep;
642			lp->l_next = linehead;
643			linehead = lp;
644
645			/*
646			 * If this is the one we wanted, we are done.
647			 */
648			if (!strcmp(lp->l_groupname, group))
649				return (lp);
650		}
651	}
652#ifdef YP
653	/*
654	 * Yucky. The recursive nature of this whole mess might require
655	 * us to make more than one pass through the netgroup file.
656	 * This might be best left outside the #ifdef YP, but YP is
657	 * defined by default anyway, so I'll leave it like this
658	 * until I know better.
659	 */
660	rewind(netf);
661#endif
662	return (NULL);
663}
664