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