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