1 /* $OpenBSD: parse_netgroup.c,v 1.11 2007/02/20 01:44:16 ray Exp $ */
2 /*
3  * Copyright (c) 1992, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * Rick Macklem at The University of Guelph.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY 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  *	$FreeBSD: parse_netgroup.c,v 1.5 1997/02/22 14:22:02 peter Exp $
34  */
35 
36 /*
37  * This is a specially hacked-up version of getnetgrent.c used to parse
38  * data from the stored hash table of netgroup info rather than from a
39  * file. It's used mainly for the parse_netgroup() function. All the YP
40  * stuff and file support has been stripped out since it isn't needed.
41  */
42 
43 #include <stdio.h>
44 #include <string.h>
45 #include <stdlib.h>
46 #include <unistd.h>
47 #include "hash.h"
48 
49 #ifndef lint
50 static const char rcsid[] = "$OpenBSD: parse_netgroup.c,v 1.11 2007/02/20 01:44:16 ray Exp $";
51 #endif
52 
53 /*
54  * Static Variables and functions used by setnetgrent(), getnetgrent() and
55  * __endnetgrent().
56  * There are two linked lists:
57  * - linelist is just used by setnetgrent() to parse the net group file via.
58  *   parse_netgrp()
59  * - netgrp is the list of entries for the current netgroup
60  */
61 struct linelist {
62 	struct linelist	*l_next;	/* Chain ptr. */
63 	int		l_parsed;	/* Flag for cycles */
64 	char		*l_groupname;	/* Name of netgroup */
65 	char		*l_line;	/* Netgroup entrie(s) to be parsed */
66 };
67 
68 struct netgrp {
69 	struct netgrp	*ng_next;	/* Chain ptr */
70 	char		*ng_str[3];	/* Field pointers, see below */
71 };
72 #define NG_HOST		0	/* Host name */
73 #define NG_USER		1	/* User name */
74 #define NG_DOM		2	/* and Domain name */
75 
76 static struct linelist	*linehead = NULL;
77 static struct netgrp	*nextgrp = NULL;
78 static struct {
79 	struct netgrp	*gr;
80 	char		*grname;
81 } grouphead = {
82 	NULL,
83 	NULL,
84 };
85 
86 static int parse_netgrp(char *);
87 static struct linelist *read_for_group(char *);
88 void __setnetgrent(char *), __endnetgrent(void);
89 int __getnetgrent(char **, char **, char **);
90 extern struct group_entry *gtable[];
91 
92 /*
93  * setnetgrent()
94  * Parse the netgroup file looking for the netgroup and build the list
95  * of netgrp structures. Let parse_netgrp() and read_for_group() do
96  * most of the work.
97  */
98 void
99 __setnetgrent(char *group)
100 {
101 	/* Sanity check */
102 
103 	if (group == NULL || !strlen(group))
104 		return;
105 
106 	if (grouphead.gr == NULL || strcmp(group, grouphead.grname)) {
107 		__endnetgrent();
108 		if (parse_netgrp(group))
109 			__endnetgrent();
110 		else
111 			grouphead.grname = strdup(group);
112 	}
113 	nextgrp = grouphead.gr;
114 }
115 
116 /*
117  * Get the next netgroup off the list.
118  */
119 int
120 __getnetgrent(char **hostp, char **userp, char **domp)
121 {
122 	if (nextgrp) {
123 		*hostp = nextgrp->ng_str[NG_HOST];
124 		*userp = nextgrp->ng_str[NG_USER];
125 		*domp = nextgrp->ng_str[NG_DOM];
126 		nextgrp = nextgrp->ng_next;
127 		return (1);
128 	}
129 	return (0);
130 }
131 
132 /*
133  * __endnetgrent() - cleanup
134  */
135 void
136 __endnetgrent(void)
137 {
138 	struct linelist *lp, *olp;
139 	struct netgrp *gp, *ogp;
140 
141 	lp = linehead;
142 	while (lp) {
143 		olp = lp;
144 		lp = lp->l_next;
145 		free(olp->l_groupname);
146 		free(olp->l_line);
147 		free(olp);
148 	}
149 	linehead = NULL;
150 	if (grouphead.grname) {
151 		free(grouphead.grname);
152 		grouphead.grname = NULL;
153 	}
154 	gp = grouphead.gr;
155 	while (gp) {
156 		ogp = gp;
157 		gp = gp->ng_next;
158 		if (ogp->ng_str[NG_HOST])
159 			free(ogp->ng_str[NG_HOST]);
160 		if (ogp->ng_str[NG_USER])
161 			free(ogp->ng_str[NG_USER]);
162 		if (ogp->ng_str[NG_DOM])
163 			free(ogp->ng_str[NG_DOM]);
164 		free(ogp);
165 	}
166 	grouphead.gr = NULL;
167 }
168 
169 /*
170  * Parse the netgroup file setting up the linked lists.
171  */
172 static int
173 parse_netgrp(char *group)
174 {
175 	char *spos, *epos;
176 	int len, strpos;
177 #ifdef DEBUG
178 	int fields;
179 #endif
180 	char *pos, *gpos;
181 	struct netgrp *grp;
182 	struct linelist *lp = linehead;
183 
184 	/*
185 	 * First, see if the line has already been read in.
186 	 */
187 	while (lp) {
188 		if (!strcmp(group, lp->l_groupname))
189 			break;
190 		lp = lp->l_next;
191 	}
192 	if (lp == NULL && (lp = read_for_group(group)) == NULL)
193 		return (1);
194 	if (lp->l_parsed) {
195 #ifdef DEBUG
196 		/*
197 		 * This error message is largely superflous since the
198 		 * code handles the error condition successfully, and
199 		 * spewing it out from inside libc can actually hose
200 		 * certain programs.
201 		 */
202 		fprintf(stderr, "Cycle in netgroup %s\n", lp->l_groupname);
203 #endif
204 		return (1);
205 	} else
206 		lp->l_parsed = 1;
207 	pos = lp->l_line;
208 	/* Watch for null pointer dereferences, dammit! */
209 	while (pos != NULL && *pos != '\0') {
210 		if (*pos == '(') {
211 			grp = malloc(sizeof(struct netgrp));
212 			bzero(grp, sizeof(struct netgrp));
213 			grp->ng_next = grouphead.gr;
214 			grouphead.gr = grp;
215 			pos++;
216 			gpos = strsep(&pos, ")");
217 #ifdef DEBUG
218 			fields = 0;
219 #endif
220 			for (strpos = 0; strpos < 3; strpos++) {
221 				if ((spos = strsep(&gpos, ","))) {
222 #ifdef DEBUG
223 					fields++;
224 #endif
225 					while (*spos == ' ' || *spos == '\t')
226 						spos++;
227 					if ((epos = strpbrk(spos, " \t"))) {
228 						*epos = '\0';
229 						len = epos - spos;
230 					} else
231 						len = strlen(spos);
232 					if (len > 0) {
233 						grp->ng_str[strpos] = malloc(len + 1);
234 						bcopy(spos, grp->ng_str[strpos],
235 						    len + 1);
236 					}
237 				} else {
238 					/*
239 					 * All other systems I've tested
240 					 * return NULL for empty netgroup
241 					 * fields. It's up to user programs
242 					 * to handle the NULLs appropriately.
243 					 */
244 					grp->ng_str[strpos] = NULL;
245 				}
246 			}
247 #ifdef DEBUG
248 			/*
249 			 * Note: on other platforms, malformed netgroup
250 			 * entries are not normally flagged. While we
251 			 * can catch bad entries and report them, we should
252 			 * stay silent by default for compatibility's sake.
253 			 */
254 			if (fields < 3)
255 					fprintf(stderr, "Bad entry (%s%s%s%s%s) in netgroup \"%s\"\n",
256 						grp->ng_str[NG_HOST] == NULL ? "" : grp->ng_str[NG_HOST],
257 						grp->ng_str[NG_USER] == NULL ? "" : ",",
258 						grp->ng_str[NG_USER] == NULL ? "" : grp->ng_str[NG_USER],
259 						grp->ng_str[NG_DOM] == NULL ? "" : ",",
260 						grp->ng_str[NG_DOM] == NULL ? "" : grp->ng_str[NG_DOM],
261 						lp->l_groupname);
262 #endif
263 		} else {
264 			spos = strsep(&pos, ", \t");
265 			if (parse_netgrp(spos))
266 				continue;
267 		}
268 		/* Watch for null pointer dereferences, dammit! */
269 		if (pos != NULL)
270 			while (*pos == ' ' || *pos == ',' || *pos == '\t')
271 				pos++;
272 	}
273 	return (0);
274 }
275 
276 /*
277  * Read the netgroup file and save lines until the line for the netgroup
278  * is found. Return 1 if eof is encountered.
279  */
280 static struct linelist *
281 read_for_group(char *group)
282 {
283 	char *pos, *spos, *linep = NULL, *olinep = NULL;
284 	int len, olen;
285 	int cont;
286 	struct linelist *lp;
287 	char line[LINSIZ + 1];
288 	char *data = NULL;
289 
290 	data = lookup (gtable, group);
291 	snprintf(line, sizeof line, "%s %s", group, data);
292 	pos = (char *)&line;
293 #ifdef CANT_HAPPEN
294 	if (*pos == '#')
295 		continue;
296 #endif
297 	while (*pos == ' ' || *pos == '\t')
298 		pos++;
299 	spos = pos;
300 	while (*pos != ' ' && *pos != '\t' && *pos != '\n' &&
301 		*pos != '\0')
302 		pos++;
303 	len = pos - spos;
304 	while (*pos == ' ' || *pos == '\t')
305 		pos++;
306 	if (*pos != '\n' && *pos != '\0') {
307 		lp = malloc(sizeof(*lp));
308 		lp->l_parsed = 0;
309 		lp->l_groupname = malloc(len + 1);
310 		bcopy(spos, lp->l_groupname, len);
311 		*(lp->l_groupname + len) = '\0';
312 		len = strlen(pos);
313 		olen = 0;
314 			/*
315 			 * Loop around handling line continuations.
316 			 */
317 			do {
318 				if (*(pos + len - 1) == '\n')
319 					len--;
320 				if (*(pos + len - 1) == '\\') {
321 					len--;
322 					cont = 1;
323 				} else
324 					cont = 0;
325 				if (len > 0) {
326 					linep = malloc(olen + len + 1);
327 					if (olen > 0) {
328 						bcopy(olinep, linep, olen);
329 						free(olinep);
330 					}
331 					bcopy(pos, linep + olen, len);
332 					olen += len;
333 					*(linep + olen) = '\0';
334 					olinep = linep;
335 				}
336 #ifdef CANT_HAPPEN
337 				if (cont) {
338 					if (fgets(line, sizeof(line), netf)) {
339 						pos = line;
340 						len = strlen(pos);
341 					} else
342 						cont = 0;
343 				}
344 #endif
345 			} while (cont);
346 		lp->l_line = linep;
347 		lp->l_next = linehead;
348 		linehead = lp;
349 #ifdef CANT_HAPPEN
350 		/*
351 		 * If this is the one we wanted, we are done.
352 		 */
353 		if (!strcmp(lp->l_groupname, group))
354 #endif
355 			return (lp);
356 	}
357 	return (NULL);
358 }
359