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