1/*	$NetBSD: lcl_ng.c,v 1.1.1.2 2012/09/09 16:07:54 christos Exp $	*/
2
3/*
4 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (c) 1996-1999 by Internet Software Consortium.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#if !defined(LINT) && !defined(CODECENTER)
21static const char rcsid[] = "Id: lcl_ng.c,v 1.3 2005/04/27 04:56:31 sra Exp ";
22#endif
23
24/* Imports */
25
26#include "port_before.h"
27
28#include <sys/types.h>
29#include <netinet/in.h>
30#include <arpa/nameser.h>
31#include <resolv.h>
32#include <errno.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <unistd.h>
37
38#include <irs.h>
39#include <isc/memcluster.h>
40
41#include "port_after.h"
42
43#include "irs_p.h"
44#include "lcl_p.h"
45
46/* Definitions */
47
48#define NG_HOST         0       /*%< Host name */
49#define NG_USER         1       /*%< User name */
50#define NG_DOM          2       /*%< and Domain name */
51#define LINSIZ		1024    /*%< Length of netgroup file line */
52/*
53 * XXX Warning XXX
54 * This code is a hack-and-slash special.  It realy needs to be
55 * rewritten with things like strdup, and realloc in mind.
56 * More reasonable data structures would not be a bad thing.
57 */
58
59/*%
60 * Static Variables and functions used by setnetgrent(), getnetgrent() and
61 * endnetgrent().
62 *
63 * There are two linked lists:
64 * \li linelist is just used by setnetgrent() to parse the net group file via.
65 *   parse_netgrp()
66 * \li netgrp is the list of entries for the current netgroup
67 */
68struct linelist {
69	struct linelist *l_next;	/*%< Chain ptr. */
70	int		l_parsed;	/*%< Flag for cycles */
71	char *		l_groupname;	/*%< Name of netgroup */
72	char *		l_line;		/*%< Netgroup entrie(s) to be parsed */
73};
74
75struct ng_old_struct {
76	struct ng_old_struct *ng_next;	/*%< Chain ptr */
77	char *		ng_str[3];	/*%< Field pointers, see below */
78};
79
80struct pvt {
81	FILE			*fp;
82	struct linelist		*linehead;
83	struct ng_old_struct    *nextgrp;
84	struct {
85		struct ng_old_struct	*gr;
86		char			*grname;
87	} grouphead;
88};
89
90/* Forward */
91
92static void 		ng_rewind(struct irs_ng *, const char*);
93static void 		ng_close(struct irs_ng *);
94static int		ng_next(struct irs_ng *, const char **,
95				const char **, const char **);
96static int 		ng_test(struct irs_ng *, const char *,
97				const char *, const char *,
98				const char *);
99static void		ng_minimize(struct irs_ng *);
100
101static int 		parse_netgrp(struct irs_ng *, const char*);
102static struct linelist *read_for_group(struct irs_ng *, const char *);
103static void		freelists(struct irs_ng *);
104
105/* Public */
106
107struct irs_ng *
108irs_lcl_ng(struct irs_acc *this) {
109	struct irs_ng *ng;
110	struct pvt *pvt;
111
112	UNUSED(this);
113
114	if (!(ng = memget(sizeof *ng))) {
115		errno = ENOMEM;
116		return (NULL);
117	}
118	memset(ng, 0x5e, sizeof *ng);
119	if (!(pvt = memget(sizeof *pvt))) {
120		memput(ng, sizeof *ng);
121		errno = ENOMEM;
122		return (NULL);
123	}
124	memset(pvt, 0, sizeof *pvt);
125	ng->private = pvt;
126	ng->close = ng_close;
127	ng->next = ng_next;
128	ng->test = ng_test;
129	ng->rewind = ng_rewind;
130	ng->minimize = ng_minimize;
131	return (ng);
132}
133
134/* Methods */
135
136static void
137ng_close(struct irs_ng *this) {
138	struct pvt *pvt = (struct pvt *)this->private;
139
140	if (pvt->fp != NULL)
141		fclose(pvt->fp);
142	freelists(this);
143	memput(pvt, sizeof *pvt);
144	memput(this, sizeof *this);
145}
146
147/*%
148 * Parse the netgroup file looking for the netgroup and build the list
149 * of netgrp structures. Let parse_netgrp() and read_for_group() do
150 * most of the work.
151 */
152static void
153ng_rewind(struct irs_ng *this, const char *group) {
154	struct pvt *pvt = (struct pvt *)this->private;
155
156	if (pvt->fp != NULL && fseek(pvt->fp, SEEK_CUR, 0L) == -1) {
157		fclose(pvt->fp);
158		pvt->fp = NULL;
159	}
160
161	if (pvt->fp == NULL || pvt->grouphead.gr == NULL ||
162	    strcmp(group, pvt->grouphead.grname)) {
163		freelists(this);
164		if (pvt->fp != NULL)
165			fclose(pvt->fp);
166		pvt->fp = fopen(_PATH_NETGROUP, "r");
167		if (pvt->fp != NULL) {
168			if (parse_netgrp(this, group))
169				freelists(this);
170			if (!(pvt->grouphead.grname = strdup(group)))
171				freelists(this);
172			fclose(pvt->fp);
173			pvt->fp = NULL;
174		}
175	}
176	pvt->nextgrp = pvt->grouphead.gr;
177}
178
179/*%
180 * Get the next netgroup off the list.
181 */
182static int
183ng_next(struct irs_ng *this, const char **host, const char **user,
184	const char **domain)
185{
186	struct pvt *pvt = (struct pvt *)this->private;
187
188	if (pvt->nextgrp) {
189		*host = pvt->nextgrp->ng_str[NG_HOST];
190		*user = pvt->nextgrp->ng_str[NG_USER];
191		*domain = pvt->nextgrp->ng_str[NG_DOM];
192		pvt->nextgrp = pvt->nextgrp->ng_next;
193		return (1);
194	}
195	return (0);
196}
197
198/*%
199 * Search for a match in a netgroup.
200 */
201static int
202ng_test(struct irs_ng *this, const char *name,
203	const char *host, const char *user, const char *domain)
204{
205	const char *ng_host, *ng_user, *ng_domain;
206
207	ng_rewind(this, name);
208	while (ng_next(this, &ng_host, &ng_user, &ng_domain))
209		if ((host == NULL || ng_host == NULL ||
210		     !strcmp(host, ng_host)) &&
211		    (user ==  NULL || ng_user == NULL ||
212		     !strcmp(user, ng_user)) &&
213		    (domain == NULL || ng_domain == NULL ||
214		     !strcmp(domain, ng_domain))) {
215			freelists(this);
216			return (1);
217		}
218	freelists(this);
219	return (0);
220}
221
222static void
223ng_minimize(struct irs_ng *this) {
224	struct pvt *pvt = (struct pvt *)this->private;
225
226	if (pvt->fp != NULL) {
227		(void)fclose(pvt->fp);
228		pvt->fp = NULL;
229	}
230}
231
232/* Private */
233
234/*%
235 * endnetgrent() - cleanup
236 */
237static void
238freelists(struct irs_ng *this) {
239	struct pvt *pvt = (struct pvt *)this->private;
240	struct linelist *lp, *olp;
241	struct ng_old_struct *gp, *ogp;
242
243	lp = pvt->linehead;
244	while (lp) {
245		olp = lp;
246		lp = lp->l_next;
247		free(olp->l_groupname);
248		free(olp->l_line);
249		free((char *)olp);
250	}
251	pvt->linehead = NULL;
252	if (pvt->grouphead.grname) {
253		free(pvt->grouphead.grname);
254		pvt->grouphead.grname = NULL;
255	}
256	gp = pvt->grouphead.gr;
257	while (gp) {
258		ogp = gp;
259		gp = gp->ng_next;
260		if (ogp->ng_str[NG_HOST])
261			free(ogp->ng_str[NG_HOST]);
262		if (ogp->ng_str[NG_USER])
263			free(ogp->ng_str[NG_USER]);
264		if (ogp->ng_str[NG_DOM])
265			free(ogp->ng_str[NG_DOM]);
266		free((char *)ogp);
267	}
268	pvt->grouphead.gr = NULL;
269}
270
271/*%
272 * Parse the netgroup file setting up the linked lists.
273 */
274static int
275parse_netgrp(struct irs_ng *this, const char *group) {
276	struct pvt *pvt = (struct pvt *)this->private;
277	char *spos, *epos;
278	int len, strpos;
279	char *pos, *gpos;
280	struct ng_old_struct *grp;
281	struct linelist *lp = pvt->linehead;
282
283        /*
284         * First, see if the line has already been read in.
285         */
286	while (lp) {
287		if (!strcmp(group, lp->l_groupname))
288			break;
289		lp = lp->l_next;
290	}
291	if (lp == NULL &&
292	    (lp = read_for_group(this, group)) == NULL)
293		return (1);
294	if (lp->l_parsed) {
295		/*fprintf(stderr, "Cycle in netgroup %s\n", lp->l_groupname);*/
296		return (1);
297	} else
298		lp->l_parsed = 1;
299	pos = lp->l_line;
300	while (*pos != '\0') {
301		if (*pos == '(') {
302			if (!(grp = malloc(sizeof (struct ng_old_struct)))) {
303				freelists(this);
304				errno = ENOMEM;
305				return (1);
306			}
307			memset(grp, 0, sizeof (struct ng_old_struct));
308			grp->ng_next = pvt->grouphead.gr;
309			pvt->grouphead.gr = grp;
310			pos++;
311			gpos = strsep(&pos, ")");
312			for (strpos = 0; strpos < 3; strpos++) {
313				if ((spos = strsep(&gpos, ","))) {
314					while (*spos == ' ' || *spos == '\t')
315						spos++;
316					if ((epos = strpbrk(spos, " \t"))) {
317						*epos = '\0';
318						len = epos - spos;
319					} else
320						len = strlen(spos);
321					if (len > 0) {
322						if(!(grp->ng_str[strpos]
323						   =  (char *)
324						   malloc(len + 1))) {
325							freelists(this);
326							return (1);
327						}
328						memcpy(grp->ng_str[strpos],
329						       spos,
330						       len + 1);
331					}
332				} else
333					goto errout;
334			}
335		} else {
336			spos = strsep(&pos, ", \t");
337			if (spos != NULL && parse_netgrp(this, spos)) {
338				freelists(this);
339				return (1);
340			}
341		}
342		if (pos == NULL)
343			break;
344		while (*pos == ' ' || *pos == ',' || *pos == '\t')
345			pos++;
346	}
347	return (0);
348 errout:
349	/*fprintf(stderr, "Bad netgroup %s at ..%s\n", lp->l_groupname,
350		  spos);*/
351	return (1);
352}
353
354/*%
355 * Read the netgroup file and save lines until the line for the netgroup
356 * is found. Return 1 if eof is encountered.
357 */
358static struct linelist *
359read_for_group(struct irs_ng *this, const char *group) {
360	struct pvt *pvt = (struct pvt *)this->private;
361	char *pos, *spos, *linep = NULL, *olinep;
362	int len, olen, cont;
363	struct linelist *lp;
364	char line[LINSIZ + 1];
365
366	while (fgets(line, LINSIZ, pvt->fp) != NULL) {
367		pos = line;
368		if (*pos == '#')
369			continue;
370		while (*pos == ' ' || *pos == '\t')
371			pos++;
372		spos = pos;
373		while (*pos != ' ' && *pos != '\t' && *pos != '\n' &&
374			*pos != '\0')
375			pos++;
376		len = pos - spos;
377		while (*pos == ' ' || *pos == '\t')
378			pos++;
379		if (*pos != '\n' && *pos != '\0') {
380			if (!(lp = malloc(sizeof (*lp)))) {
381				freelists(this);
382				return (NULL);
383			}
384			lp->l_parsed = 0;
385			if (!(lp->l_groupname = malloc(len + 1))) {
386				free(lp);
387				freelists(this);
388				return (NULL);
389			}
390			memcpy(lp->l_groupname, spos,  len);
391			*(lp->l_groupname + len) = '\0';
392			len = strlen(pos);
393			olen = 0;
394			olinep = NULL;
395
396			/*
397			 * Loop around handling line continuations.
398			 */
399			do {
400				if (*(pos + len - 1) == '\n')
401					len--;
402				if (*(pos + len - 1) == '\\') {
403					len--;
404					cont = 1;
405				} else
406					cont = 0;
407				if (len > 0) {
408					if (!(linep = malloc(olen + len + 1))){
409						if (olen > 0)
410							free(olinep);
411						free(lp->l_groupname);
412						free(lp);
413						freelists(this);
414						errno = ENOMEM;
415						return (NULL);
416					}
417					if (olen > 0) {
418						memcpy(linep, olinep, olen);
419						free(olinep);
420					}
421					memcpy(linep + olen, pos, len);
422					olen += len;
423					*(linep + olen) = '\0';
424					olinep = linep;
425				}
426				if (cont) {
427					if (fgets(line, LINSIZ, pvt->fp)) {
428						pos = line;
429						len = strlen(pos);
430					} else
431						cont = 0;
432				}
433			} while (cont);
434			lp->l_line = linep;
435			lp->l_next = pvt->linehead;
436			pvt->linehead = lp;
437
438			/*
439			 * If this is the one we wanted, we are done.
440			 */
441			if (!strcmp(lp->l_groupname, group))
442				return (lp);
443		}
444	}
445	return (NULL);
446}
447
448/*! \file */
449