17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5f48205beScasper  * Common Development and Distribution License (the "License").
6f48205beScasper  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
227c478bd9Sstevel@tonic-gate  * PPPoE Server-mode daemon option parsing.
237c478bd9Sstevel@tonic-gate  *
24f53eecf5SJames Carlson  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
257c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
26*48bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
277c478bd9Sstevel@tonic-gate  */
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate #include <stdio.h>
307c478bd9Sstevel@tonic-gate #include <stdlib.h>
317c478bd9Sstevel@tonic-gate #include <unistd.h>
327c478bd9Sstevel@tonic-gate #include <assert.h>
337c478bd9Sstevel@tonic-gate #include <ctype.h>
347c478bd9Sstevel@tonic-gate #include <string.h>
357c478bd9Sstevel@tonic-gate #include <sys/types.h>
367c478bd9Sstevel@tonic-gate #include <fcntl.h>
377c478bd9Sstevel@tonic-gate #include <pwd.h>
387c478bd9Sstevel@tonic-gate #include <grp.h>
397c478bd9Sstevel@tonic-gate #include <errno.h>
407c478bd9Sstevel@tonic-gate #include <netdb.h>
417c478bd9Sstevel@tonic-gate #include <stropts.h>
427c478bd9Sstevel@tonic-gate #include <sys/stat.h>
437c478bd9Sstevel@tonic-gate #include <sys/socket.h>
447c478bd9Sstevel@tonic-gate #include <net/if.h>
457c478bd9Sstevel@tonic-gate #include <netinet/in.h>
467c478bd9Sstevel@tonic-gate #include <netinet/if_ether.h>
477c478bd9Sstevel@tonic-gate #include <net/sppptun.h>
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate #include "common.h"
507c478bd9Sstevel@tonic-gate #include "logging.h"
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate #define	MAX_KEYWORD	4096	/* Maximum token length */
537c478bd9Sstevel@tonic-gate #define	MAX_NEST	32	/* Maximum ${$sub} nesting */
547c478bd9Sstevel@tonic-gate #define	MAXARGS		256	/* Maximum number of pppd arguments */
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate /*
577c478bd9Sstevel@tonic-gate  * Client filter entry.  These are linked in *reverse* order so that
587c478bd9Sstevel@tonic-gate  * the DAG created by file inclusion nesting works as expected.  Since
597c478bd9Sstevel@tonic-gate  * the administrator who wrote the configuration expects "first
607c478bd9Sstevel@tonic-gate  * match," this means that tests against the filter list must actually
617c478bd9Sstevel@tonic-gate  * use "last match."
627c478bd9Sstevel@tonic-gate  */
637c478bd9Sstevel@tonic-gate struct filter_entry {
647c478bd9Sstevel@tonic-gate 	struct filter_entry *fe_prev;	/* Previous filter in list */
657c478bd9Sstevel@tonic-gate 	struct ether_addr fe_mac;	/* MAC address */
667c478bd9Sstevel@tonic-gate 	struct ether_addr fe_mask;	/* Mask for above address test */
677c478bd9Sstevel@tonic-gate 	uchar_t fe_isexcept;	/* invert sense; exclude matching clients */
687c478bd9Sstevel@tonic-gate 	uchar_t fe_prevcopy;		/* fe_prev points to copied list */
697c478bd9Sstevel@tonic-gate 	uchar_t fe_unused[2];		/* padding */
707c478bd9Sstevel@tonic-gate };
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate /*
737c478bd9Sstevel@tonic-gate  * Note: I would like to make the strings and filters here const, but
747c478bd9Sstevel@tonic-gate  * I can't because they have to be passed to free() during parsing.  I
757c478bd9Sstevel@tonic-gate  * could work around this with offsetof() or data copies, but it's not
767c478bd9Sstevel@tonic-gate  * worth the effort.
777c478bd9Sstevel@tonic-gate  */
787c478bd9Sstevel@tonic-gate struct service_entry {
797c478bd9Sstevel@tonic-gate 	const char *se_name;		/* Name of service */
807c478bd9Sstevel@tonic-gate 	struct filter_entry *se_flist;	/* Pointer to list of client filters */
817c478bd9Sstevel@tonic-gate 	uint_t se_flags;		/* SEF_* flags (below) */
827c478bd9Sstevel@tonic-gate 	int se_debug;			/* Debug level (0=nodebug) */
837c478bd9Sstevel@tonic-gate 	char *se_server;		/* Server (AC) name */
847c478bd9Sstevel@tonic-gate 	char *se_pppd;			/* Options for pppd */
857c478bd9Sstevel@tonic-gate 	char *se_path;			/* Path to pppd executable */
867c478bd9Sstevel@tonic-gate 	char *se_extra;			/* Extra options */
877c478bd9Sstevel@tonic-gate 	char *se_log;			/* Log file */
887c478bd9Sstevel@tonic-gate 	uid_t se_uid;			/* User ID */
897c478bd9Sstevel@tonic-gate 	gid_t se_gid;			/* Group ID */
907c478bd9Sstevel@tonic-gate };
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate #define	SEF_WILD	0x00000001	/* Offer in wildcard reply */
937c478bd9Sstevel@tonic-gate #define	SEF_NOWILD	0x00000002	/* Don't offer in wildcard */
947c478bd9Sstevel@tonic-gate #define	SEF_CFLIST	0x00000004	/* se_flist copied from global */
957c478bd9Sstevel@tonic-gate #define	SEF_CSERVER	0x00000008	/* se_server copied from global */
967c478bd9Sstevel@tonic-gate #define	SEF_CPPPD	0x00000010	/* se_pppd copied from global */
977c478bd9Sstevel@tonic-gate #define	SEF_CPATH	0x00000020	/* se_path copied from global */
987c478bd9Sstevel@tonic-gate #define	SEF_CEXTRA	0x00000040	/* se_extra copied from global */
997c478bd9Sstevel@tonic-gate #define	SEF_CLOG	0x00000080	/* se_log copied from global */
1007c478bd9Sstevel@tonic-gate #define	SEF_UIDSET	0x00000100	/* se_uid has been set */
1017c478bd9Sstevel@tonic-gate #define	SEF_GIDSET	0x00000200	/* se_gid has been set */
1027c478bd9Sstevel@tonic-gate #define	SEF_DEBUGCLR	0x00000400	/* do not add se_debug from global */
1037c478bd9Sstevel@tonic-gate #define	SEF_CDEV	0x00000800	/* copied devs (parse only) */
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate /*
1067c478bd9Sstevel@tonic-gate  * One of these is allocated per lower-level stream (device) that is
1077c478bd9Sstevel@tonic-gate  * referenced by the configuration files.  The queries are received
1087c478bd9Sstevel@tonic-gate  * per device, and this structure allows us to find all of the
1097c478bd9Sstevel@tonic-gate  * services that correspond to that device.
1107c478bd9Sstevel@tonic-gate  */
1117c478bd9Sstevel@tonic-gate struct device_entry {
1127c478bd9Sstevel@tonic-gate 	const char *de_name;
1137c478bd9Sstevel@tonic-gate 	const struct service_entry **de_services;
1147c478bd9Sstevel@tonic-gate 	int de_nservices;
1157c478bd9Sstevel@tonic-gate };
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate /*
1187c478bd9Sstevel@tonic-gate  * This is the parsed configuration.  While a new configuration is
1197c478bd9Sstevel@tonic-gate  * being read, this is kept around until the new configuration is
1207c478bd9Sstevel@tonic-gate  * ready, and then it is discarded in one operation.  It has an array
1217c478bd9Sstevel@tonic-gate  * of device entries (as above) -- one per referenced lower stream --
1227c478bd9Sstevel@tonic-gate  * and a pointer to the allocated parser information.  The latter is
1237c478bd9Sstevel@tonic-gate  * kept around because we reuse pointers rather than reallocating and
1247c478bd9Sstevel@tonic-gate  * copying the data.  There are thus multiple aliases to the dynamic
1257c478bd9Sstevel@tonic-gate  * data, and the "owner" (for purposes of freeing the storage) is
1267c478bd9Sstevel@tonic-gate  * considered to be this 'junk' list.
1277c478bd9Sstevel@tonic-gate  */
1287c478bd9Sstevel@tonic-gate struct option_state {
1297c478bd9Sstevel@tonic-gate 	const struct device_entry *os_devices;
1307c478bd9Sstevel@tonic-gate 	int os_ndevices;
1317c478bd9Sstevel@tonic-gate 	struct per_file *os_pfjunk;	/* Kept for deallocation */
1327c478bd9Sstevel@tonic-gate 	char **os_evjunk;		/* ditto */
1337c478bd9Sstevel@tonic-gate };
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate /*
1367c478bd9Sstevel@tonic-gate  * This is the root pointer to the current parsed options.
1377c478bd9Sstevel@tonic-gate  * This cannot be const because it's passed to free() when reparsing
1387c478bd9Sstevel@tonic-gate  * options.
1397c478bd9Sstevel@tonic-gate  */
1407c478bd9Sstevel@tonic-gate static struct option_state *cur_options;
1417c478bd9Sstevel@tonic-gate 
1427c478bd9Sstevel@tonic-gate /* Global settings for module-wide options. */
1437c478bd9Sstevel@tonic-gate static struct service_entry glob_svc;
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate /*
1467c478bd9Sstevel@tonic-gate  * *******************************************************************
1477c478bd9Sstevel@tonic-gate  * Data structures generated during parsing.
1487c478bd9Sstevel@tonic-gate  */
1497c478bd9Sstevel@tonic-gate 
1507c478bd9Sstevel@tonic-gate /* List of device names attached to one service */
1517c478bd9Sstevel@tonic-gate struct device_list {
1527c478bd9Sstevel@tonic-gate 	struct device_list *dl_next;
1537c478bd9Sstevel@tonic-gate 	const char *dl_name;		/* Name of one device */
1547c478bd9Sstevel@tonic-gate };
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate /* Entry for a single defined service. */
1577c478bd9Sstevel@tonic-gate struct service_list {
1587c478bd9Sstevel@tonic-gate 	struct service_entry sl_entry;	/* Parsed service data */
1597c478bd9Sstevel@tonic-gate 	struct service_list *sl_next;	/* Next service entry */
1607c478bd9Sstevel@tonic-gate 	struct parse_state *sl_parse;	/* Back pointer to state */
1617c478bd9Sstevel@tonic-gate 	struct device_list *sl_dev;	/* List of devices */
1627c478bd9Sstevel@tonic-gate 	int sl_serial;			/* Serial number (conflict resolve) */
1637c478bd9Sstevel@tonic-gate };
1647c478bd9Sstevel@tonic-gate #define	SESERIAL(x)	((struct service_list *)&(x))->sl_serial
1657c478bd9Sstevel@tonic-gate #define	ISGLOBAL(x)	((x) == &(x)->sl_parse->ps_cfile->pf_global)
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate /*
1687c478bd9Sstevel@tonic-gate  * Structure allocated for each file opened.  File nesting is chained
1697c478bd9Sstevel@tonic-gate  * in reverse order so that global option scoping works as expected.
1707c478bd9Sstevel@tonic-gate  */
1717c478bd9Sstevel@tonic-gate struct per_file {
1727c478bd9Sstevel@tonic-gate 	struct per_file *pf_prev;	/* Back chain */
1737c478bd9Sstevel@tonic-gate 	struct service_list pf_global;	/* Global (default) service context */
1747c478bd9Sstevel@tonic-gate 	struct service_list *pf_svc;	/* List of services */
1757c478bd9Sstevel@tonic-gate 	struct service_list *pf_svc_last;
1767c478bd9Sstevel@tonic-gate 	FILE *pf_input;			/* File for input */
1777c478bd9Sstevel@tonic-gate 	const char *pf_name;		/* File name */
1787c478bd9Sstevel@tonic-gate 	int pf_nsvc;			/* Count of services */
1797c478bd9Sstevel@tonic-gate };
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate /* State of parser */
1827c478bd9Sstevel@tonic-gate enum key_state {
1837c478bd9Sstevel@tonic-gate 	ksDefault, ksService, ksDevice, ksClient, ksClientE, ksServer,
1847c478bd9Sstevel@tonic-gate 	ksPppd, ksFile, ksPath, ksExtra, ksLog, ksUser, ksGroup
1857c478bd9Sstevel@tonic-gate };
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate /*
1887c478bd9Sstevel@tonic-gate  * Global parser state.  There is one of these structures, and it
1897c478bd9Sstevel@tonic-gate  * exists only while actively parsing configuration files.
1907c478bd9Sstevel@tonic-gate  */
1917c478bd9Sstevel@tonic-gate struct parse_state {
1927c478bd9Sstevel@tonic-gate 	enum key_state ps_state;	/* Parser state */
1937c478bd9Sstevel@tonic-gate 	int ps_serial;			/* Service serial number */
1947c478bd9Sstevel@tonic-gate 	struct per_file *ps_files;	/* Parsed files */
1957c478bd9Sstevel@tonic-gate 	struct per_file *ps_cfile;	/* Current file */
1967c478bd9Sstevel@tonic-gate 	struct service_list *ps_csvc;	/* Current service */
1977c478bd9Sstevel@tonic-gate 	struct device_list *ps_star;	/* Wildcard device */
1987c478bd9Sstevel@tonic-gate 	int ps_flags;			/* PSF_* below */
1997c478bd9Sstevel@tonic-gate 	char **ps_evlist;		/* allocated environment variables */
2007c478bd9Sstevel@tonic-gate 	int ps_evsize;			/* max length; for realloc */
2017c478bd9Sstevel@tonic-gate };
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate #define	PSF_PERDEV	0x0001		/* In a per-device file */
2047c478bd9Sstevel@tonic-gate #define	PSF_SETLEVEL	0x0002		/* Set log level along the way */
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate /* Should be in a library somewhere. */
2077c478bd9Sstevel@tonic-gate static char *
strsave(const char * str)2087c478bd9Sstevel@tonic-gate strsave(const char *str)
2097c478bd9Sstevel@tonic-gate {
2107c478bd9Sstevel@tonic-gate 	char *newstr;
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate 	if (str == NULL)
2137c478bd9Sstevel@tonic-gate 		return (NULL);
2147c478bd9Sstevel@tonic-gate 	newstr = (char *)malloc(strlen(str) + 1);
2157c478bd9Sstevel@tonic-gate 	if (newstr != NULL)
2167c478bd9Sstevel@tonic-gate 		(void) strcpy(newstr, str);
2177c478bd9Sstevel@tonic-gate 	return (newstr);
2187c478bd9Sstevel@tonic-gate }
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate /*
2217c478bd9Sstevel@tonic-gate  * Stop defining current service and revert to global definition.
2227c478bd9Sstevel@tonic-gate  * This resolves any implicit references to global options by copying
2237c478bd9Sstevel@tonic-gate  * ("inheriting") from the current global state.
2247c478bd9Sstevel@tonic-gate  */
2257c478bd9Sstevel@tonic-gate static void
close_service(struct service_list * slp)2267c478bd9Sstevel@tonic-gate close_service(struct service_list *slp)
2277c478bd9Sstevel@tonic-gate {
2287c478bd9Sstevel@tonic-gate 	struct parse_state *psp;
2297c478bd9Sstevel@tonic-gate 	struct per_file *cfile;
2307c478bd9Sstevel@tonic-gate 	struct service_entry *sep;
2317c478bd9Sstevel@tonic-gate 	struct service_entry *sedefp;
2327c478bd9Sstevel@tonic-gate 	struct filter_entry *fep;
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate 	assert(slp != NULL);
2357c478bd9Sstevel@tonic-gate 	psp = slp->sl_parse;
2367c478bd9Sstevel@tonic-gate 	cfile = psp->ps_cfile;
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate 	/* If no current file, then nothing to close. */
2397c478bd9Sstevel@tonic-gate 	if (cfile == NULL)
2407c478bd9Sstevel@tonic-gate 		return;
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 	sep = &slp->sl_entry;
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate 	/*
2457c478bd9Sstevel@tonic-gate 	 * Fix up filter pointers to make DAG.  First, locate
2467c478bd9Sstevel@tonic-gate 	 * the end of the filter list.
2477c478bd9Sstevel@tonic-gate 	 */
2487c478bd9Sstevel@tonic-gate 	if (sep->se_flags & SEF_CFLIST) {
2497c478bd9Sstevel@tonic-gate 		sep->se_flist = fep = NULL;
2507c478bd9Sstevel@tonic-gate 	} else {
2517c478bd9Sstevel@tonic-gate 		for (fep = sep->se_flist; fep != NULL; fep = fep->fe_prev)
2527c478bd9Sstevel@tonic-gate 			if (fep->fe_prev == NULL || fep->fe_prevcopy) {
2537c478bd9Sstevel@tonic-gate 				fep->fe_prev = NULL;
2547c478bd9Sstevel@tonic-gate 				break;
2557c478bd9Sstevel@tonic-gate 			}
2567c478bd9Sstevel@tonic-gate 	}
2577c478bd9Sstevel@tonic-gate 	if (slp == &cfile->pf_global) {
2587c478bd9Sstevel@tonic-gate 		/*
2597c478bd9Sstevel@tonic-gate 		 * If we're in a global context, then we're about to
2607c478bd9Sstevel@tonic-gate 		 * open a new service, so it's time to fix up the
2617c478bd9Sstevel@tonic-gate 		 * filter list so that it's usable as a reference.
2627c478bd9Sstevel@tonic-gate 		 * Loop through files from which we were included, and
2637c478bd9Sstevel@tonic-gate 		 * link up filters.  Note: closure may occur more than
2647c478bd9Sstevel@tonic-gate 		 * once here.
2657c478bd9Sstevel@tonic-gate 		 */
2667c478bd9Sstevel@tonic-gate 		/* We don't inherit from ourselves. */
2677c478bd9Sstevel@tonic-gate 		cfile = cfile->pf_prev;
2687c478bd9Sstevel@tonic-gate 		while (cfile != NULL) {
2697c478bd9Sstevel@tonic-gate 			if (fep == NULL) {
2707c478bd9Sstevel@tonic-gate 				sep->se_flist = fep =
2717c478bd9Sstevel@tonic-gate 				    cfile->pf_global.sl_entry.se_flist;
2727c478bd9Sstevel@tonic-gate 				sep->se_flags |= SEF_CFLIST;
2737c478bd9Sstevel@tonic-gate 			} else if (fep->fe_prev == NULL) {
2747c478bd9Sstevel@tonic-gate 				fep->fe_prev =
2757c478bd9Sstevel@tonic-gate 				    cfile->pf_global.sl_entry.se_flist;
2767c478bd9Sstevel@tonic-gate 				fep->fe_prevcopy = 1;
2777c478bd9Sstevel@tonic-gate 			}
2787c478bd9Sstevel@tonic-gate 			cfile = cfile->pf_prev;
2797c478bd9Sstevel@tonic-gate 		}
2807c478bd9Sstevel@tonic-gate 	} else {
2817c478bd9Sstevel@tonic-gate 		/*
2827c478bd9Sstevel@tonic-gate 		 * Loop through default options in current and all
2837c478bd9Sstevel@tonic-gate 		 * enclosing include files.  Inherit options.
2847c478bd9Sstevel@tonic-gate 		 */
2857c478bd9Sstevel@tonic-gate 		logdbg("service %s ends", slp->sl_entry.se_name);
2867c478bd9Sstevel@tonic-gate 		while (cfile != NULL) {
2877c478bd9Sstevel@tonic-gate 			/* Inherit from global service options. */
2887c478bd9Sstevel@tonic-gate 			if (slp->sl_dev == NULL) {
2897c478bd9Sstevel@tonic-gate 				slp->sl_dev = cfile->pf_global.sl_dev;
2907c478bd9Sstevel@tonic-gate 				sep->se_flags |= SEF_CDEV;
2917c478bd9Sstevel@tonic-gate 			}
2927c478bd9Sstevel@tonic-gate 			sedefp = &cfile->pf_global.sl_entry;
2937c478bd9Sstevel@tonic-gate 			if (fep == NULL) {
2947c478bd9Sstevel@tonic-gate 				sep->se_flist = fep = sedefp->se_flist;
2957c478bd9Sstevel@tonic-gate 				sep->se_flags |= SEF_CFLIST;
2967c478bd9Sstevel@tonic-gate 			} else if (fep->fe_prev == NULL) {
2977c478bd9Sstevel@tonic-gate 				fep->fe_prev = sedefp->se_flist;
2987c478bd9Sstevel@tonic-gate 				fep->fe_prevcopy = 1;
2997c478bd9Sstevel@tonic-gate 			}
3007c478bd9Sstevel@tonic-gate 			if (sep->se_server == NULL) {
3017c478bd9Sstevel@tonic-gate 				sep->se_server = sedefp->se_server;
3027c478bd9Sstevel@tonic-gate 				sep->se_flags |= SEF_CSERVER;
3037c478bd9Sstevel@tonic-gate 			}
3047c478bd9Sstevel@tonic-gate 			if (sep->se_pppd == NULL) {
3057c478bd9Sstevel@tonic-gate 				sep->se_pppd = sedefp->se_pppd;
3067c478bd9Sstevel@tonic-gate 				sep->se_flags |= SEF_CPPPD;
3077c478bd9Sstevel@tonic-gate 			}
3087c478bd9Sstevel@tonic-gate 			if (sep->se_path == NULL) {
3097c478bd9Sstevel@tonic-gate 				sep->se_path = sedefp->se_path;
3107c478bd9Sstevel@tonic-gate 				sep->se_flags |= SEF_CPATH;
3117c478bd9Sstevel@tonic-gate 			}
3127c478bd9Sstevel@tonic-gate 			if (sep->se_extra == NULL) {
3137c478bd9Sstevel@tonic-gate 				sep->se_extra = sedefp->se_extra;
3147c478bd9Sstevel@tonic-gate 				sep->se_flags |= SEF_CEXTRA;
3157c478bd9Sstevel@tonic-gate 			}
3167c478bd9Sstevel@tonic-gate 			if (sep->se_log == NULL) {
3177c478bd9Sstevel@tonic-gate 				sep->se_log = sedefp->se_log;
3187c478bd9Sstevel@tonic-gate 				sep->se_flags |= SEF_CLOG;
3197c478bd9Sstevel@tonic-gate 			}
3207c478bd9Sstevel@tonic-gate 			if (!(sep->se_flags & SEF_UIDSET) &&
3217c478bd9Sstevel@tonic-gate 			    (sedefp->se_flags & SEF_UIDSET)) {
3227c478bd9Sstevel@tonic-gate 				sep->se_uid = sedefp->se_uid;
3237c478bd9Sstevel@tonic-gate 				sep->se_flags |= SEF_UIDSET;
3247c478bd9Sstevel@tonic-gate 			}
3257c478bd9Sstevel@tonic-gate 			if (!(sep->se_flags & SEF_GIDSET) &&
3267c478bd9Sstevel@tonic-gate 			    (sedefp->se_flags & SEF_GIDSET)) {
3277c478bd9Sstevel@tonic-gate 				sep->se_gid = sedefp->se_gid;
3287c478bd9Sstevel@tonic-gate 				sep->se_flags |= SEF_GIDSET;
3297c478bd9Sstevel@tonic-gate 			}
3307c478bd9Sstevel@tonic-gate 			if (!(sep->se_flags & (SEF_WILD|SEF_NOWILD)))
3317c478bd9Sstevel@tonic-gate 				sep->se_flags |= sedefp->se_flags &
3327c478bd9Sstevel@tonic-gate 				    (SEF_WILD|SEF_NOWILD);
3337c478bd9Sstevel@tonic-gate 			if (!(sep->se_flags & SEF_DEBUGCLR)) {
3347c478bd9Sstevel@tonic-gate 				sep->se_debug += sedefp->se_debug;
3357c478bd9Sstevel@tonic-gate 				sep->se_flags |= sedefp->se_flags &
3367c478bd9Sstevel@tonic-gate 				    SEF_DEBUGCLR;
3377c478bd9Sstevel@tonic-gate 			}
3387c478bd9Sstevel@tonic-gate 			cfile = cfile->pf_prev;
3397c478bd9Sstevel@tonic-gate 		}
3407c478bd9Sstevel@tonic-gate 	}
3417c478bd9Sstevel@tonic-gate 	/* Revert to global definitions. */
3427c478bd9Sstevel@tonic-gate 	psp->ps_csvc = &psp->ps_cfile->pf_global;
3437c478bd9Sstevel@tonic-gate }
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate /* Discard a dynamic device list */
3467c478bd9Sstevel@tonic-gate static void
free_device_list(struct device_list * dlp)3477c478bd9Sstevel@tonic-gate free_device_list(struct device_list *dlp)
3487c478bd9Sstevel@tonic-gate {
3497c478bd9Sstevel@tonic-gate 	struct device_list *dln;
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate 	while (dlp != NULL) {
3527c478bd9Sstevel@tonic-gate 		dln = dlp->dl_next;
3537c478bd9Sstevel@tonic-gate 		free(dlp);
3547c478bd9Sstevel@tonic-gate 		dlp = dln;
3557c478bd9Sstevel@tonic-gate 	}
3567c478bd9Sstevel@tonic-gate }
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate /*
3597c478bd9Sstevel@tonic-gate  * Handle "service <name>" -- finish up previous service definition
3607c478bd9Sstevel@tonic-gate  * (if any) by copying from global state where necessary, and start
3617c478bd9Sstevel@tonic-gate  * defining new service.
3627c478bd9Sstevel@tonic-gate  */
3637c478bd9Sstevel@tonic-gate static int
set_service(struct service_list * slp,const char * str)3647c478bd9Sstevel@tonic-gate set_service(struct service_list *slp, const char *str)
3657c478bd9Sstevel@tonic-gate {
3667c478bd9Sstevel@tonic-gate 	struct parse_state *psp;
3677c478bd9Sstevel@tonic-gate 	struct per_file *cfile;
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 	/* Finish current service */
3707c478bd9Sstevel@tonic-gate 	close_service(slp);
3717c478bd9Sstevel@tonic-gate 
3727c478bd9Sstevel@tonic-gate 	/* Start new service */
3737c478bd9Sstevel@tonic-gate 	psp = slp->sl_parse;
3747c478bd9Sstevel@tonic-gate 	slp = (struct service_list *)calloc(sizeof (*slp) + strlen(str) + 1,
3757c478bd9Sstevel@tonic-gate 	    1);
3767c478bd9Sstevel@tonic-gate 	if (slp == NULL) {
3777c478bd9Sstevel@tonic-gate 		logerr("no memory for service \"%s\"", str);
3787c478bd9Sstevel@tonic-gate 		return (-1);
3797c478bd9Sstevel@tonic-gate 	}
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 	/* Add to end of list */
3827c478bd9Sstevel@tonic-gate 	cfile = psp->ps_cfile;
3837c478bd9Sstevel@tonic-gate 	if (cfile->pf_svc_last == NULL)
3847c478bd9Sstevel@tonic-gate 		cfile->pf_svc = slp;
3857c478bd9Sstevel@tonic-gate 	else
3867c478bd9Sstevel@tonic-gate 		cfile->pf_svc_last->sl_next = slp;
3877c478bd9Sstevel@tonic-gate 	cfile->pf_svc_last = slp;
3887c478bd9Sstevel@tonic-gate 	cfile->pf_nsvc++;
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 	/* Fill in initial service entry */
3917c478bd9Sstevel@tonic-gate 	slp->sl_entry.se_name = (const char *)(slp+1);
3927c478bd9Sstevel@tonic-gate 	(void) strcpy((char *)(slp+1), str);
3937c478bd9Sstevel@tonic-gate 	logdbg("service %s begins", slp->sl_entry.se_name);
3947c478bd9Sstevel@tonic-gate 	slp->sl_serial = psp->ps_serial++;
3957c478bd9Sstevel@tonic-gate 	slp->sl_parse = psp;
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate 	/* This is now the current service that we're defining. */
3987c478bd9Sstevel@tonic-gate 	psp->ps_csvc = slp;
3997c478bd9Sstevel@tonic-gate 	return (0);
4007c478bd9Sstevel@tonic-gate }
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate /*
4037c478bd9Sstevel@tonic-gate  * Handle both "wildcard" and "nowildcard" options.
4047c478bd9Sstevel@tonic-gate  */
4057c478bd9Sstevel@tonic-gate static int
set_wildcard(struct service_list * slp,const char * str)4067c478bd9Sstevel@tonic-gate set_wildcard(struct service_list *slp, const char *str)
4077c478bd9Sstevel@tonic-gate {
4087c478bd9Sstevel@tonic-gate 	/* Allow global context to switch back and forth without error. */
4097c478bd9Sstevel@tonic-gate 	if (!ISGLOBAL(slp) &&
4107c478bd9Sstevel@tonic-gate 	    (slp->sl_entry.se_flags & (SEF_WILD|SEF_NOWILD))) {
4117c478bd9Sstevel@tonic-gate 		logdbg("%s: extra \"%s\" ignored",
4127c478bd9Sstevel@tonic-gate 		    slp->sl_parse->ps_cfile->pf_name, str);
4137c478bd9Sstevel@tonic-gate 		return (0);
4147c478bd9Sstevel@tonic-gate 	}
4157c478bd9Sstevel@tonic-gate 	slp->sl_entry.se_flags =
4167c478bd9Sstevel@tonic-gate 	    (slp->sl_entry.se_flags & ~(SEF_WILD|SEF_NOWILD)) |
4177c478bd9Sstevel@tonic-gate 	    (*str == 'n' ? SEF_NOWILD : SEF_WILD);
4187c478bd9Sstevel@tonic-gate 	return (0);
4197c478bd9Sstevel@tonic-gate }
4207c478bd9Sstevel@tonic-gate 
4217c478bd9Sstevel@tonic-gate /*
4227c478bd9Sstevel@tonic-gate  * Handle "debug" option.
4237c478bd9Sstevel@tonic-gate  */
4247c478bd9Sstevel@tonic-gate /*ARGSUSED*/
4257c478bd9Sstevel@tonic-gate static int
set_debug(struct service_list * slp,const char * str)4267c478bd9Sstevel@tonic-gate set_debug(struct service_list *slp, const char *str)
4277c478bd9Sstevel@tonic-gate {
4287c478bd9Sstevel@tonic-gate 	slp->sl_entry.se_debug++;
4297c478bd9Sstevel@tonic-gate 	if (ISGLOBAL(slp) && (slp->sl_parse->ps_flags & PSF_SETLEVEL)) {
4307c478bd9Sstevel@tonic-gate 		log_level = slp->sl_entry.se_debug;
4317c478bd9Sstevel@tonic-gate 	}
4327c478bd9Sstevel@tonic-gate 	return (0);
4337c478bd9Sstevel@tonic-gate }
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate /*
4367c478bd9Sstevel@tonic-gate  * Handle "nodebug" option.
4377c478bd9Sstevel@tonic-gate  */
4387c478bd9Sstevel@tonic-gate /*ARGSUSED*/
4397c478bd9Sstevel@tonic-gate static int
set_nodebug(struct service_list * slp,const char * str)4407c478bd9Sstevel@tonic-gate set_nodebug(struct service_list *slp, const char *str)
4417c478bd9Sstevel@tonic-gate {
4427c478bd9Sstevel@tonic-gate 	slp->sl_entry.se_flags |= SEF_DEBUGCLR;
4437c478bd9Sstevel@tonic-gate 	slp->sl_entry.se_debug = 0;
4447c478bd9Sstevel@tonic-gate 	if (ISGLOBAL(slp) && (slp->sl_parse->ps_flags & PSF_SETLEVEL)) {
4457c478bd9Sstevel@tonic-gate 		log_level = slp->sl_entry.se_debug;
4467c478bd9Sstevel@tonic-gate 	}
4477c478bd9Sstevel@tonic-gate 	return (0);
4487c478bd9Sstevel@tonic-gate }
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate /*
4517c478bd9Sstevel@tonic-gate  * Handle all plain string options; "server", "pppd", "path", "extra",
4527c478bd9Sstevel@tonic-gate  * and "log".
4537c478bd9Sstevel@tonic-gate  */
4547c478bd9Sstevel@tonic-gate static int
set_string(struct service_list * slp,const char * str)4557c478bd9Sstevel@tonic-gate set_string(struct service_list *slp, const char *str)
4567c478bd9Sstevel@tonic-gate {
4577c478bd9Sstevel@tonic-gate 	char **cpp;
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 	assert(!(slp->sl_entry.se_flags &
4607c478bd9Sstevel@tonic-gate 	    (SEF_CSERVER|SEF_CPPPD|SEF_CPATH|SEF_CEXTRA|SEF_CLOG)));
4617c478bd9Sstevel@tonic-gate 	switch (slp->sl_parse->ps_state) {
4627c478bd9Sstevel@tonic-gate 	case ksServer:
4637c478bd9Sstevel@tonic-gate 		cpp = &slp->sl_entry.se_server;
4647c478bd9Sstevel@tonic-gate 		break;
4657c478bd9Sstevel@tonic-gate 	case ksPppd:
4667c478bd9Sstevel@tonic-gate 		cpp = &slp->sl_entry.se_pppd;
4677c478bd9Sstevel@tonic-gate 		break;
4687c478bd9Sstevel@tonic-gate 	case ksPath:
4697c478bd9Sstevel@tonic-gate 		cpp = &slp->sl_entry.se_path;
4707c478bd9Sstevel@tonic-gate 		break;
4717c478bd9Sstevel@tonic-gate 	case ksExtra:
4727c478bd9Sstevel@tonic-gate 		cpp = &slp->sl_entry.se_extra;
4737c478bd9Sstevel@tonic-gate 		break;
4747c478bd9Sstevel@tonic-gate 	case ksLog:
4757c478bd9Sstevel@tonic-gate 		cpp = &slp->sl_entry.se_log;
4767c478bd9Sstevel@tonic-gate 		break;
4777c478bd9Sstevel@tonic-gate 	default:
4787c478bd9Sstevel@tonic-gate 		assert(0);
4797c478bd9Sstevel@tonic-gate 		return (-1);
4807c478bd9Sstevel@tonic-gate 	}
4817c478bd9Sstevel@tonic-gate 	if (*cpp != NULL)
4827c478bd9Sstevel@tonic-gate 		free(*cpp);
4837c478bd9Sstevel@tonic-gate 	*cpp = strsave(str);
4847c478bd9Sstevel@tonic-gate 	return (0);
4857c478bd9Sstevel@tonic-gate }
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate /*
4887c478bd9Sstevel@tonic-gate  * Handle "file <name>" option.  Close out current service (if any)
4897c478bd9Sstevel@tonic-gate  * and begin parsing from new file.
4907c478bd9Sstevel@tonic-gate  */
4917c478bd9Sstevel@tonic-gate static int
set_file(struct service_list * slp,const char * str)4927c478bd9Sstevel@tonic-gate set_file(struct service_list *slp, const char *str)
4937c478bd9Sstevel@tonic-gate {
4947c478bd9Sstevel@tonic-gate 	FILE *fp;
4957c478bd9Sstevel@tonic-gate 	struct per_file *pfp;
4967c478bd9Sstevel@tonic-gate 	struct parse_state *psp;
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate 	close_service(slp);
4997c478bd9Sstevel@tonic-gate 
5007c478bd9Sstevel@tonic-gate 	if ((fp = fopen(str, "r")) == NULL) {
5017c478bd9Sstevel@tonic-gate 		logwarn("%s: %s: %s", slp->sl_parse->ps_cfile->pf_name, str,
5027c478bd9Sstevel@tonic-gate 		    mystrerror(errno));
5037c478bd9Sstevel@tonic-gate 		return (-1);
5047c478bd9Sstevel@tonic-gate 	}
5057c478bd9Sstevel@tonic-gate 	pfp = (struct per_file *)calloc(sizeof (*pfp) + strlen(str) + 1, 1);
5067c478bd9Sstevel@tonic-gate 	if (pfp == NULL) {
5077c478bd9Sstevel@tonic-gate 		logerr("no memory for parsing file %s", str);
5087c478bd9Sstevel@tonic-gate 		(void) fclose(fp);
5097c478bd9Sstevel@tonic-gate 		return (-1);
5107c478bd9Sstevel@tonic-gate 	}
5117c478bd9Sstevel@tonic-gate 	logdbg("config file %s open", str);
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate 	/* Fill in new file structure. */
5147c478bd9Sstevel@tonic-gate 	pfp->pf_name = (const char *)(pfp+1);
5157c478bd9Sstevel@tonic-gate 	(void) strcpy((char *)(pfp+1), str);
5167c478bd9Sstevel@tonic-gate 	pfp->pf_input = fp;
5177c478bd9Sstevel@tonic-gate 	psp = slp->sl_parse;
5187c478bd9Sstevel@tonic-gate 	pfp->pf_prev = psp->ps_cfile;
5197c478bd9Sstevel@tonic-gate 	psp->ps_cfile = pfp;
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate 	/* Start off in global context for this file. */
5227c478bd9Sstevel@tonic-gate 	psp->ps_csvc = &pfp->pf_global;
5237c478bd9Sstevel@tonic-gate 	pfp->pf_global.sl_parse = psp;
5247c478bd9Sstevel@tonic-gate 	pfp->pf_global.sl_entry.se_name = "<global>";
5257c478bd9Sstevel@tonic-gate 	return (0);
5267c478bd9Sstevel@tonic-gate }
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate /*
5297c478bd9Sstevel@tonic-gate  * Handle "device <list>" option.
5307c478bd9Sstevel@tonic-gate  */
5317c478bd9Sstevel@tonic-gate static int
set_device(struct service_list * slp,const char * str)5327c478bd9Sstevel@tonic-gate set_device(struct service_list *slp, const char *str)
5337c478bd9Sstevel@tonic-gate {
5347c478bd9Sstevel@tonic-gate 	struct parse_state *psp = slp->sl_parse;
5357c478bd9Sstevel@tonic-gate 	struct device_list *dlp;
5367c478bd9Sstevel@tonic-gate 	struct device_list *dln;
5377c478bd9Sstevel@tonic-gate 	struct device_list **dlpp;
5387c478bd9Sstevel@tonic-gate 	const char *cp;
5397c478bd9Sstevel@tonic-gate 	int len;
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate 	/* Can't use this option in the per-device files. */
5427c478bd9Sstevel@tonic-gate 	if (psp->ps_flags & PSF_PERDEV) {
5437c478bd9Sstevel@tonic-gate 		logerr("\"device %s\" ignored in %s", str,
5447c478bd9Sstevel@tonic-gate 		    psp->ps_cfile->pf_name);
5457c478bd9Sstevel@tonic-gate 		return (0);
5467c478bd9Sstevel@tonic-gate 	}
5477c478bd9Sstevel@tonic-gate 
5487c478bd9Sstevel@tonic-gate 	if (strcmp(str, "*") == 0 || strcmp(str, "all") == 0) {
5497c478bd9Sstevel@tonic-gate 		if (!(slp->sl_entry.se_flags & SEF_CDEV))
5507c478bd9Sstevel@tonic-gate 			free_device_list(slp->sl_dev);
5517c478bd9Sstevel@tonic-gate 		slp->sl_dev = psp->ps_star;
5527c478bd9Sstevel@tonic-gate 		slp->sl_entry.se_flags |= SEF_CDEV;
5537c478bd9Sstevel@tonic-gate 	} else {
5547c478bd9Sstevel@tonic-gate 		dlpp = &dlp;
5557c478bd9Sstevel@tonic-gate 		for (;;) {
5567c478bd9Sstevel@tonic-gate 			while (isspace(*str) || *str == ',')
5577c478bd9Sstevel@tonic-gate 				str++;
5587c478bd9Sstevel@tonic-gate 			if (*str == '\0')
5597c478bd9Sstevel@tonic-gate 				break;
5607c478bd9Sstevel@tonic-gate 			cp = str;
5617c478bd9Sstevel@tonic-gate 			while (*str != '\0' && !isspace(*str) && *str != ',')
5627c478bd9Sstevel@tonic-gate 				str++;
5637c478bd9Sstevel@tonic-gate 			len = str - cp;
5647c478bd9Sstevel@tonic-gate 			if ((len == 1 && *cp == '*') ||
5657c478bd9Sstevel@tonic-gate 			    (len == 3 && strncmp(cp, "all", 3) == 0)) {
5667c478bd9Sstevel@tonic-gate 				logerr("%s: cannot use %.*s in device list",
5677c478bd9Sstevel@tonic-gate 				    psp->ps_cfile->pf_name, len, cp);
5687c478bd9Sstevel@tonic-gate 				continue;
5697c478bd9Sstevel@tonic-gate 			}
5707c478bd9Sstevel@tonic-gate 			dln = (struct device_list *)malloc(sizeof (*dln) +
5717c478bd9Sstevel@tonic-gate 			    len + 1);
5727c478bd9Sstevel@tonic-gate 			if (dln == NULL) {
5737c478bd9Sstevel@tonic-gate 				logerr("no memory for device name");
5747c478bd9Sstevel@tonic-gate 				break;
5757c478bd9Sstevel@tonic-gate 			}
5767c478bd9Sstevel@tonic-gate 			dln->dl_name = (const char *)(dln + 1);
5777c478bd9Sstevel@tonic-gate 			/* Cannot use strcpy because cp isn't terminated. */
5787c478bd9Sstevel@tonic-gate 			(void) memcpy(dln + 1, cp, len);
5797c478bd9Sstevel@tonic-gate 			((char *)(dln + 1))[len] = '\0';
5807c478bd9Sstevel@tonic-gate 			logdbg("%s: device %s", psp->ps_cfile->pf_name,
5817c478bd9Sstevel@tonic-gate 			    dln->dl_name);
5827c478bd9Sstevel@tonic-gate 			*dlpp = dln;
5837c478bd9Sstevel@tonic-gate 			dlpp = &dln->dl_next;
5847c478bd9Sstevel@tonic-gate 		}
5857c478bd9Sstevel@tonic-gate 		*dlpp = NULL;
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate 		dlpp = &slp->sl_dev;
5887c478bd9Sstevel@tonic-gate 		if (!(slp->sl_entry.se_flags & SEF_CDEV))
5897c478bd9Sstevel@tonic-gate 			while (*dlpp != NULL)
5907c478bd9Sstevel@tonic-gate 				dlpp = &(*dlpp)->dl_next;
5917c478bd9Sstevel@tonic-gate 		*dlpp = dlp;
5927c478bd9Sstevel@tonic-gate 		slp->sl_entry.se_flags &= ~SEF_CDEV;
5937c478bd9Sstevel@tonic-gate 	}
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate 	return (0);
5967c478bd9Sstevel@tonic-gate }
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate /*
5997c478bd9Sstevel@tonic-gate  * Handle <list> portion of "client [except] <list>" option.  Attach
6007c478bd9Sstevel@tonic-gate  * to list of filters in reverse order.
6017c478bd9Sstevel@tonic-gate  */
6027c478bd9Sstevel@tonic-gate static int
set_client(struct service_list * slp,const char * str)6037c478bd9Sstevel@tonic-gate set_client(struct service_list *slp, const char *str)
6047c478bd9Sstevel@tonic-gate {
6057c478bd9Sstevel@tonic-gate 	struct parse_state *psp = slp->sl_parse;
6067c478bd9Sstevel@tonic-gate 	struct filter_entry *fep;
6077c478bd9Sstevel@tonic-gate 	struct filter_entry *fen;
6087c478bd9Sstevel@tonic-gate 	const char *cp;
6097c478bd9Sstevel@tonic-gate 	int len;
6107c478bd9Sstevel@tonic-gate 	char hbuf[MAXHOSTNAMELEN];
6117c478bd9Sstevel@tonic-gate 	struct ether_addr ea;
6127c478bd9Sstevel@tonic-gate 	struct ether_addr mask;
6137c478bd9Sstevel@tonic-gate 	uchar_t *ucp;
6147c478bd9Sstevel@tonic-gate 	uchar_t *mcp;
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate 	/* Head of list. */
6177c478bd9Sstevel@tonic-gate 	fep = slp->sl_entry.se_flist;
6187c478bd9Sstevel@tonic-gate 	for (;;) {
6197c478bd9Sstevel@tonic-gate 		while (isspace(*str) || *str == ',')
6207c478bd9Sstevel@tonic-gate 			str++;
6217c478bd9Sstevel@tonic-gate 		if (*str == '\0')
6227c478bd9Sstevel@tonic-gate 			break;
6237c478bd9Sstevel@tonic-gate 		cp = str;
6247c478bd9Sstevel@tonic-gate 		while (*str != '\0' && !isspace(*str) && *str != ',')
6257c478bd9Sstevel@tonic-gate 			str++;
6267c478bd9Sstevel@tonic-gate 		len = str - cp;
6277c478bd9Sstevel@tonic-gate 		(void) memcpy(hbuf, cp, len);
6287c478bd9Sstevel@tonic-gate 		hbuf[len] = '\0';
6297c478bd9Sstevel@tonic-gate 		mcp = mask.ether_addr_octet;
6307c478bd9Sstevel@tonic-gate 		mcp[0] = mcp[1] = mcp[2] = mcp[3] = mcp[4] = mcp[5] = 0xFF;
6317c478bd9Sstevel@tonic-gate 		if (ether_hostton(hbuf, &ea) != 0) {
6327c478bd9Sstevel@tonic-gate 			ucp = ea.ether_addr_octet;
6337c478bd9Sstevel@tonic-gate 			while (cp < str) {
6347c478bd9Sstevel@tonic-gate 				if (ucp >= ea.ether_addr_octet + sizeof (ea))
6357c478bd9Sstevel@tonic-gate 					break;
6367c478bd9Sstevel@tonic-gate 				if (*cp == '*') {
6377c478bd9Sstevel@tonic-gate 					*mcp++ = *ucp++ = 0;
6387c478bd9Sstevel@tonic-gate 					cp++;
6397c478bd9Sstevel@tonic-gate 				} else {
6407c478bd9Sstevel@tonic-gate 					if (!isxdigit(*cp))
6417c478bd9Sstevel@tonic-gate 						break;
6427c478bd9Sstevel@tonic-gate 					*ucp = hexdecode(*cp++);
6437c478bd9Sstevel@tonic-gate 					if (cp < str && isxdigit(*cp)) {
6447c478bd9Sstevel@tonic-gate 						*ucp = (*ucp << 4) |
6457c478bd9Sstevel@tonic-gate 						    hexdecode(*cp++);
6467c478bd9Sstevel@tonic-gate 					}
6477c478bd9Sstevel@tonic-gate 					ucp++;
6487c478bd9Sstevel@tonic-gate 					*mcp++ = 0xFF;
6497c478bd9Sstevel@tonic-gate 				}
6507c478bd9Sstevel@tonic-gate 				if (cp < str) {
6517c478bd9Sstevel@tonic-gate 					if (*cp != ':' || cp + 1 == str)
6527c478bd9Sstevel@tonic-gate 						break;
6537c478bd9Sstevel@tonic-gate 					cp++;
6547c478bd9Sstevel@tonic-gate 				}
6557c478bd9Sstevel@tonic-gate 			}
6567c478bd9Sstevel@tonic-gate 			if (cp < str) {
6577c478bd9Sstevel@tonic-gate 				logerr("%s: illegal Ethernet address %.*s",
6587c478bd9Sstevel@tonic-gate 				    psp->ps_cfile->pf_name, len, cp);
6597c478bd9Sstevel@tonic-gate 				continue;
6607c478bd9Sstevel@tonic-gate 			}
6617c478bd9Sstevel@tonic-gate 		}
6627c478bd9Sstevel@tonic-gate 		fen = (struct filter_entry *)malloc(sizeof (*fen));
6637c478bd9Sstevel@tonic-gate 		if (fen == NULL) {
6647c478bd9Sstevel@tonic-gate 			logerr("unable to allocate memory for filter");
6657c478bd9Sstevel@tonic-gate 			break;
6667c478bd9Sstevel@tonic-gate 		}
6677c478bd9Sstevel@tonic-gate 		fen->fe_isexcept = psp->ps_state == ksClientE;
6687c478bd9Sstevel@tonic-gate 		fen->fe_prevcopy = 0;
6697c478bd9Sstevel@tonic-gate 		(void) memcpy(&fen->fe_mac, &ea, sizeof (fen->fe_mac));
6707c478bd9Sstevel@tonic-gate 		(void) memcpy(&fen->fe_mask, &mask, sizeof (fen->fe_mask));
6717c478bd9Sstevel@tonic-gate 		fen->fe_prev = fep;
6727c478bd9Sstevel@tonic-gate 		fep = fen;
6737c478bd9Sstevel@tonic-gate 	}
6747c478bd9Sstevel@tonic-gate 	slp->sl_entry.se_flist = fep;
6757c478bd9Sstevel@tonic-gate 	return (0);
6767c478bd9Sstevel@tonic-gate }
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate /*
6797c478bd9Sstevel@tonic-gate  * Handle "user <name>" option.
6807c478bd9Sstevel@tonic-gate  */
6817c478bd9Sstevel@tonic-gate static int
set_user(struct service_list * slp,const char * str)6827c478bd9Sstevel@tonic-gate set_user(struct service_list *slp, const char *str)
6837c478bd9Sstevel@tonic-gate {
6847c478bd9Sstevel@tonic-gate 	struct passwd *pw;
6857c478bd9Sstevel@tonic-gate 	char *cp;
6867c478bd9Sstevel@tonic-gate 	uid_t myuid, uid;
6877c478bd9Sstevel@tonic-gate 
6887c478bd9Sstevel@tonic-gate 	if ((pw = getpwnam(str)) == NULL) {
6897c478bd9Sstevel@tonic-gate 		uid = (uid_t)strtol(str, &cp, 0);
6907c478bd9Sstevel@tonic-gate 		if (str == cp || *cp != '\0') {
6917c478bd9Sstevel@tonic-gate 			logerr("%s:  bad user name \"%s\"",
6927c478bd9Sstevel@tonic-gate 			    slp->sl_parse->ps_cfile->pf_name, str);
6937c478bd9Sstevel@tonic-gate 			return (0);
6947c478bd9Sstevel@tonic-gate 		}
6957c478bd9Sstevel@tonic-gate 	} else {
6967c478bd9Sstevel@tonic-gate 		uid = pw->pw_uid;
6977c478bd9Sstevel@tonic-gate 	}
6987c478bd9Sstevel@tonic-gate 	slp->sl_entry.se_uid = uid;
6997c478bd9Sstevel@tonic-gate 	myuid = getuid();
7007c478bd9Sstevel@tonic-gate 	if (myuid != 0) {
7017c478bd9Sstevel@tonic-gate 		if (myuid == uid)
7027c478bd9Sstevel@tonic-gate 			return (0);
7037c478bd9Sstevel@tonic-gate 		logdbg("%s:  not root; ignoring attempt to set UID %d (%s)",
7047c478bd9Sstevel@tonic-gate 		    slp->sl_parse->ps_cfile->pf_name, uid, str);
7057c478bd9Sstevel@tonic-gate 		return (0);
7067c478bd9Sstevel@tonic-gate 	}
7077c478bd9Sstevel@tonic-gate 	slp->sl_entry.se_flags |= SEF_UIDSET;
7087c478bd9Sstevel@tonic-gate 	return (0);
7097c478bd9Sstevel@tonic-gate }
7107c478bd9Sstevel@tonic-gate 
7117c478bd9Sstevel@tonic-gate /*
7127c478bd9Sstevel@tonic-gate  * Handle "group <name>" option.
7137c478bd9Sstevel@tonic-gate  */
7147c478bd9Sstevel@tonic-gate static int
set_group(struct service_list * slp,const char * str)7157c478bd9Sstevel@tonic-gate set_group(struct service_list *slp, const char *str)
7167c478bd9Sstevel@tonic-gate {
7177c478bd9Sstevel@tonic-gate 	struct group *gr;
7187c478bd9Sstevel@tonic-gate 	char *cp;
7197c478bd9Sstevel@tonic-gate 	gid_t gid;
7207c478bd9Sstevel@tonic-gate 
7217c478bd9Sstevel@tonic-gate 	if ((gr = getgrnam(str)) == NULL) {
7227c478bd9Sstevel@tonic-gate 		gid = (gid_t)strtol(str, &cp, 0);
7237c478bd9Sstevel@tonic-gate 		if (str == cp || *cp != '\0') {
7247c478bd9Sstevel@tonic-gate 			logerr("%s:  bad group name \"%s\"",
7257c478bd9Sstevel@tonic-gate 			    slp->sl_parse->ps_cfile->pf_name, str);
7267c478bd9Sstevel@tonic-gate 			return (0);
7277c478bd9Sstevel@tonic-gate 		}
7287c478bd9Sstevel@tonic-gate 	} else {
7297c478bd9Sstevel@tonic-gate 		gid = gr->gr_gid;
7307c478bd9Sstevel@tonic-gate 	}
7317c478bd9Sstevel@tonic-gate 	slp->sl_entry.se_gid = gid;
7327c478bd9Sstevel@tonic-gate 	if (getuid() != 0) {
7337c478bd9Sstevel@tonic-gate 		logdbg("%s:  not root; ignoring attempt to set GID %d (%s)",
7347c478bd9Sstevel@tonic-gate 		    slp->sl_parse->ps_cfile->pf_name, gid, str);
7357c478bd9Sstevel@tonic-gate 		return (0);
7367c478bd9Sstevel@tonic-gate 	}
7377c478bd9Sstevel@tonic-gate 	slp->sl_entry.se_flags |= SEF_GIDSET;
7387c478bd9Sstevel@tonic-gate 	return (0);
7397c478bd9Sstevel@tonic-gate }
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate /*
7427c478bd9Sstevel@tonic-gate  * This state machine is used to parse the configuration files.  The
7437c478bd9Sstevel@tonic-gate  * "kwe_in" is the state in which the keyword is recognized.  The
7447c478bd9Sstevel@tonic-gate  * "kwe_out" is the state that the keyword produces.
7457c478bd9Sstevel@tonic-gate  */
7467c478bd9Sstevel@tonic-gate struct kw_entry {
7477c478bd9Sstevel@tonic-gate 	const char *kwe_word;
7487c478bd9Sstevel@tonic-gate 	enum key_state kwe_in;
7497c478bd9Sstevel@tonic-gate 	enum key_state kwe_out;
7507c478bd9Sstevel@tonic-gate 	int (*kwe_func)(struct service_list *slp, const char *str);
7517c478bd9Sstevel@tonic-gate };
7527c478bd9Sstevel@tonic-gate 
7537c478bd9Sstevel@tonic-gate static const struct kw_entry key_list[] = {
7547c478bd9Sstevel@tonic-gate 	{ "service",	ksDefault,	ksService,	NULL },
7557c478bd9Sstevel@tonic-gate 	{ "device",	ksDefault,	ksDevice,	NULL },
7567c478bd9Sstevel@tonic-gate 	{ "client",	ksDefault,	ksClient,	NULL },
7577c478bd9Sstevel@tonic-gate 	{ "except",	ksClient,	ksClientE,	NULL },
7587c478bd9Sstevel@tonic-gate 	{ "wildcard",	ksDefault,	ksDefault,	set_wildcard },
7597c478bd9Sstevel@tonic-gate 	{ "nowildcard",	ksDefault,	ksDefault,	set_wildcard },
7607c478bd9Sstevel@tonic-gate 	{ "server",	ksDefault,	ksServer,	NULL },
7617c478bd9Sstevel@tonic-gate 	{ "pppd",	ksDefault,	ksPppd,		NULL },
7627c478bd9Sstevel@tonic-gate 	{ "debug",	ksDefault,	ksDefault,	set_debug },
7637c478bd9Sstevel@tonic-gate 	{ "nodebug",	ksDefault,	ksDefault,	set_nodebug },
7647c478bd9Sstevel@tonic-gate 	{ "file",	ksDefault,	ksFile,		NULL },
7657c478bd9Sstevel@tonic-gate 	{ "path",	ksDefault,	ksPath,		NULL },
7667c478bd9Sstevel@tonic-gate 	{ "extra",	ksDefault,	ksExtra,	NULL },
7677c478bd9Sstevel@tonic-gate 	{ "log",	ksDefault,	ksLog,		NULL },
7687c478bd9Sstevel@tonic-gate 	{ "user",	ksDefault,	ksUser,		NULL },
7697c478bd9Sstevel@tonic-gate 	{ "group",	ksDefault,	ksGroup,	NULL },
7707c478bd9Sstevel@tonic-gate 	/* Wildcards only past this point. */
7717c478bd9Sstevel@tonic-gate 	{ "",		ksService,	ksDefault,	set_service },
7727c478bd9Sstevel@tonic-gate 	{ "",		ksDevice,	ksDefault,	set_device },
7737c478bd9Sstevel@tonic-gate 	{ "",		ksClient,	ksDefault,	set_client },
7747c478bd9Sstevel@tonic-gate 	{ "",		ksClientE,	ksDefault,	set_client },
7757c478bd9Sstevel@tonic-gate 	{ "",		ksServer,	ksDefault,	set_string },
7767c478bd9Sstevel@tonic-gate 	{ "",		ksPppd,		ksDefault,	set_string },
7777c478bd9Sstevel@tonic-gate 	{ "",		ksFile,		ksDefault,	set_file },
7787c478bd9Sstevel@tonic-gate 	{ "",		ksPath,		ksDefault,	set_string },
7797c478bd9Sstevel@tonic-gate 	{ "",		ksExtra,	ksDefault,	set_string },
7807c478bd9Sstevel@tonic-gate 	{ "",		ksLog,		ksDefault,	set_string },
7817c478bd9Sstevel@tonic-gate 	{ "",		ksUser,		ksDefault,	set_user },
7827c478bd9Sstevel@tonic-gate 	{ "",		ksGroup,	ksDefault,	set_group },
7837c478bd9Sstevel@tonic-gate 	{ NULL, ksDefault, ksDefault, NULL }
7847c478bd9Sstevel@tonic-gate };
7857c478bd9Sstevel@tonic-gate 
7867c478bd9Sstevel@tonic-gate /*
7877c478bd9Sstevel@tonic-gate  * Produce a string for the keyword that would have gotten us into the
7887c478bd9Sstevel@tonic-gate  * current state.
7897c478bd9Sstevel@tonic-gate  */
7907c478bd9Sstevel@tonic-gate static const char *
after_key(enum key_state kstate)7917c478bd9Sstevel@tonic-gate after_key(enum key_state kstate)
7927c478bd9Sstevel@tonic-gate {
7937c478bd9Sstevel@tonic-gate 	const struct kw_entry *kep;
7947c478bd9Sstevel@tonic-gate 
7957c478bd9Sstevel@tonic-gate 	for (kep = key_list; kep->kwe_word != NULL; kep++)
7967c478bd9Sstevel@tonic-gate 		if (kep->kwe_out == kstate)
7977c478bd9Sstevel@tonic-gate 			return (kep->kwe_word);
7987c478bd9Sstevel@tonic-gate 	return ("nothing");
7997c478bd9Sstevel@tonic-gate }
8007c478bd9Sstevel@tonic-gate 
8017c478bd9Sstevel@tonic-gate /*
8027c478bd9Sstevel@tonic-gate  * Handle end-of-file processing -- close service, close file, revert
8037c478bd9Sstevel@tonic-gate  * to global context in previous include file nest level.
8047c478bd9Sstevel@tonic-gate  */
8057c478bd9Sstevel@tonic-gate static void
file_end(struct parse_state * psp)8067c478bd9Sstevel@tonic-gate file_end(struct parse_state *psp)
8077c478bd9Sstevel@tonic-gate {
8087c478bd9Sstevel@tonic-gate 	struct per_file *pfp;
8097c478bd9Sstevel@tonic-gate 
8107c478bd9Sstevel@tonic-gate 	/* Must not be in the middle of parsing a multi-word sequence now. */
8117c478bd9Sstevel@tonic-gate 	if (psp->ps_state != ksDefault) {
8127c478bd9Sstevel@tonic-gate 		logerr("%s ends with \"%s\"", psp->ps_cfile->pf_name,
8137c478bd9Sstevel@tonic-gate 		    after_key(psp->ps_state));
8147c478bd9Sstevel@tonic-gate 		psp->ps_state = ksDefault;
8157c478bd9Sstevel@tonic-gate 	}
8167c478bd9Sstevel@tonic-gate 	close_service(psp->ps_csvc);
8177c478bd9Sstevel@tonic-gate 	if ((pfp = psp->ps_cfile) != NULL) {
8187c478bd9Sstevel@tonic-gate 		/* Put this file on the list of finished files. */
8197c478bd9Sstevel@tonic-gate 		psp->ps_cfile = pfp->pf_prev;
8207c478bd9Sstevel@tonic-gate 		pfp->pf_prev = psp->ps_files;
8217c478bd9Sstevel@tonic-gate 		psp->ps_files = pfp;
8227c478bd9Sstevel@tonic-gate 		if (pfp->pf_input != NULL) {
8237c478bd9Sstevel@tonic-gate 			logdbg("file %s closed", pfp->pf_name);
8247c478bd9Sstevel@tonic-gate 			(void) fclose(pfp->pf_input);
8257c478bd9Sstevel@tonic-gate 			pfp->pf_input = NULL;
8267c478bd9Sstevel@tonic-gate 		}
8277c478bd9Sstevel@tonic-gate 
8287c478bd9Sstevel@tonic-gate 		/* Back up to previous file, if any, and set global context. */
8297c478bd9Sstevel@tonic-gate 		if ((pfp = psp->ps_cfile) != NULL)
8307c478bd9Sstevel@tonic-gate 			psp->ps_csvc = &pfp->pf_global;
8317c478bd9Sstevel@tonic-gate 	}
8327c478bd9Sstevel@tonic-gate }
8337c478bd9Sstevel@tonic-gate 
8347c478bd9Sstevel@tonic-gate /*
8357c478bd9Sstevel@tonic-gate  * Dispatch a single keyword against the parser state machine or
8367c478bd9Sstevel@tonic-gate  * handle an environment variable assignment.  The input is a string
8377c478bd9Sstevel@tonic-gate  * containing the single word to be dispatched.
8387c478bd9Sstevel@tonic-gate  */
8397c478bd9Sstevel@tonic-gate static int
dispatch_keyword(struct parse_state * psp,const char * keybuf)8407c478bd9Sstevel@tonic-gate dispatch_keyword(struct parse_state *psp, const char *keybuf)
8417c478bd9Sstevel@tonic-gate {
8427c478bd9Sstevel@tonic-gate 	const struct kw_entry *kep;
8437c478bd9Sstevel@tonic-gate 	int retv;
8447c478bd9Sstevel@tonic-gate 	char *cp;
8457c478bd9Sstevel@tonic-gate 	char *env;
8467c478bd9Sstevel@tonic-gate 	char **evlist;
8477c478bd9Sstevel@tonic-gate 	int len;
8487c478bd9Sstevel@tonic-gate 
8497c478bd9Sstevel@tonic-gate 	retv = 0;
8507c478bd9Sstevel@tonic-gate 	for (kep = key_list; kep->kwe_word != NULL; kep++) {
8517c478bd9Sstevel@tonic-gate 		if (kep->kwe_in == psp->ps_state &&
8527c478bd9Sstevel@tonic-gate 		    (*kep->kwe_word == '\0' ||
853f53eecf5SJames Carlson 		    strcasecmp(kep->kwe_word, keybuf) == 0)) {
8547c478bd9Sstevel@tonic-gate 			if (kep->kwe_func != NULL)
8557c478bd9Sstevel@tonic-gate 				retv = (*kep->kwe_func)(psp->ps_csvc, keybuf);
8567c478bd9Sstevel@tonic-gate 			psp->ps_state = kep->kwe_out;
8577c478bd9Sstevel@tonic-gate 			return (retv);
8587c478bd9Sstevel@tonic-gate 		}
8597c478bd9Sstevel@tonic-gate 	}
8607c478bd9Sstevel@tonic-gate 	if (strchr(keybuf, '=') != NULL) {
8617c478bd9Sstevel@tonic-gate 		if ((cp = strsave(keybuf)) == NULL) {
8627c478bd9Sstevel@tonic-gate 			logerr("no memory to save %s", keybuf);
8637c478bd9Sstevel@tonic-gate 			return (0);
8647c478bd9Sstevel@tonic-gate 		}
8657c478bd9Sstevel@tonic-gate 		len = (strchr(cp, '=') - cp) + 1;
8667c478bd9Sstevel@tonic-gate 		if ((evlist = psp->ps_evlist) == NULL) {
8677c478bd9Sstevel@tonic-gate 			psp->ps_evlist = evlist =
8687c478bd9Sstevel@tonic-gate 			    (char **)malloc(8 * sizeof (*evlist));
8697c478bd9Sstevel@tonic-gate 			if (evlist == NULL) {
8707c478bd9Sstevel@tonic-gate 				logerr("no memory for evlist");
8717c478bd9Sstevel@tonic-gate 				free(cp);
8727c478bd9Sstevel@tonic-gate 				return (0);
8737c478bd9Sstevel@tonic-gate 			}
8747c478bd9Sstevel@tonic-gate 			psp->ps_evsize = 8;
8757c478bd9Sstevel@tonic-gate 			evlist[0] = evlist[1] = NULL;
8767c478bd9Sstevel@tonic-gate 		} else {
8777c478bd9Sstevel@tonic-gate 			while ((env = *evlist) != NULL) {
8787c478bd9Sstevel@tonic-gate 				if (strncmp(cp, env, len) == 0)
8797c478bd9Sstevel@tonic-gate 					break;
8807c478bd9Sstevel@tonic-gate 				evlist++;
8817c478bd9Sstevel@tonic-gate 			}
8827c478bd9Sstevel@tonic-gate 			if (env == NULL &&
8837c478bd9Sstevel@tonic-gate 			    evlist-psp->ps_evlist >= psp->ps_evsize-1) {
8847c478bd9Sstevel@tonic-gate 				evlist = (char **)realloc(psp->ps_evlist,
8857c478bd9Sstevel@tonic-gate 				    (psp->ps_evsize + 8) * sizeof (*evlist));
8867c478bd9Sstevel@tonic-gate 				if (evlist == NULL) {
8877c478bd9Sstevel@tonic-gate 					logerr("cannot realloc evlist to %d",
8887c478bd9Sstevel@tonic-gate 					    psp->ps_evsize + 8);
8897c478bd9Sstevel@tonic-gate 					free(cp);
8907c478bd9Sstevel@tonic-gate 					return (0);
8917c478bd9Sstevel@tonic-gate 				}
8927c478bd9Sstevel@tonic-gate 				psp->ps_evlist = evlist;
8937c478bd9Sstevel@tonic-gate 				evlist += psp->ps_evsize - 1;
8947c478bd9Sstevel@tonic-gate 				psp->ps_evsize += 8;
8957c478bd9Sstevel@tonic-gate 				evlist[1] = NULL;
8967c478bd9Sstevel@tonic-gate 			}
8977c478bd9Sstevel@tonic-gate 		}
8987c478bd9Sstevel@tonic-gate 		logdbg("setenv \"%s\"", cp);
8997c478bd9Sstevel@tonic-gate 		if (*evlist != NULL)
9007c478bd9Sstevel@tonic-gate 			free(*evlist);
9017c478bd9Sstevel@tonic-gate 		*evlist = cp;
9027c478bd9Sstevel@tonic-gate 		return (0);
9037c478bd9Sstevel@tonic-gate 	}
9047c478bd9Sstevel@tonic-gate 	logerr("%s: unknown keyword '%s'", psp->ps_cfile->pf_name, keybuf);
9057c478bd9Sstevel@tonic-gate 	return (-1);
9067c478bd9Sstevel@tonic-gate }
9077c478bd9Sstevel@tonic-gate 
9087c478bd9Sstevel@tonic-gate /*
9097c478bd9Sstevel@tonic-gate  * Modified version of standard getenv; looks in locally-stored
9107c478bd9Sstevel@tonic-gate  * environment first.  This function exists because we need to be able
9117c478bd9Sstevel@tonic-gate  * to revert to the original environment during a reread (SIGHUP), and
9127c478bd9Sstevel@tonic-gate  * the putenv() function overwrites that environment.
9137c478bd9Sstevel@tonic-gate  */
9147c478bd9Sstevel@tonic-gate static char *
my_getenv(struct parse_state * psp,char * estr)9157c478bd9Sstevel@tonic-gate my_getenv(struct parse_state *psp, char *estr)
9167c478bd9Sstevel@tonic-gate {
9177c478bd9Sstevel@tonic-gate 	char **evlist, *ent;
9187c478bd9Sstevel@tonic-gate 	int elen;
9197c478bd9Sstevel@tonic-gate 
9207c478bd9Sstevel@tonic-gate 	if (psp != NULL && (evlist = psp->ps_evlist) != NULL) {
9217c478bd9Sstevel@tonic-gate 		elen = strlen(estr);
9227c478bd9Sstevel@tonic-gate 		while ((ent = *evlist++) != NULL) {
9237c478bd9Sstevel@tonic-gate 			if (strncmp(ent, estr, elen) == 0 &&
9247c478bd9Sstevel@tonic-gate 			    ent[elen] == '=')
9257c478bd9Sstevel@tonic-gate 				return (ent + elen + 1);
9267c478bd9Sstevel@tonic-gate 		}
9277c478bd9Sstevel@tonic-gate 	}
9287c478bd9Sstevel@tonic-gate 	return (getenv(estr));
9297c478bd9Sstevel@tonic-gate }
9307c478bd9Sstevel@tonic-gate 
9317c478bd9Sstevel@tonic-gate /*
9327c478bd9Sstevel@tonic-gate  * Expand an environment variable at the end of current buffer and
9337c478bd9Sstevel@tonic-gate  * return pointer to next spot in buffer for character append.  psp
9347c478bd9Sstevel@tonic-gate  * context may be null.
9357c478bd9Sstevel@tonic-gate  */
9367c478bd9Sstevel@tonic-gate static char *
env_replace(struct parse_state * psp,char * keybuf,char kwstate)9377c478bd9Sstevel@tonic-gate env_replace(struct parse_state *psp, char *keybuf, char kwstate)
9387c478bd9Sstevel@tonic-gate {
9397c478bd9Sstevel@tonic-gate 	char *cpe;
9407c478bd9Sstevel@tonic-gate 	char *cp;
9417c478bd9Sstevel@tonic-gate 
9427c478bd9Sstevel@tonic-gate 	if ((cp = strrchr(keybuf, kwstate)) != NULL) {
9437c478bd9Sstevel@tonic-gate 		if ((cpe = my_getenv(psp, cp + 1)) != NULL) {
9447c478bd9Sstevel@tonic-gate 			*cp = '\0';
9457c478bd9Sstevel@tonic-gate 			(void) strncat(cp, cpe,
9467c478bd9Sstevel@tonic-gate 			    MAX_KEYWORD - (cp - keybuf) - 1);
9477c478bd9Sstevel@tonic-gate 			keybuf[MAX_KEYWORD - 1] = '\0';
9487c478bd9Sstevel@tonic-gate 			cp += strlen(cp);
9497c478bd9Sstevel@tonic-gate 		} else {
9507c478bd9Sstevel@tonic-gate 			logerr("unknown variable \"%s\"", cp + 1);
9517c478bd9Sstevel@tonic-gate 		}
9527c478bd9Sstevel@tonic-gate 	} else {
9537c478bd9Sstevel@tonic-gate 		/* Should not occur. */
9547c478bd9Sstevel@tonic-gate 		cp = keybuf + strlen(keybuf);
9557c478bd9Sstevel@tonic-gate 	}
9567c478bd9Sstevel@tonic-gate 	return (cp);
9577c478bd9Sstevel@tonic-gate }
9587c478bd9Sstevel@tonic-gate 
9597c478bd9Sstevel@tonic-gate /*
9607c478bd9Sstevel@tonic-gate  * Given a character-at-a-time input function, get a delimited keyword
9617c478bd9Sstevel@tonic-gate  * from the input.  This function handles the usual escape sequences,
9627c478bd9Sstevel@tonic-gate  * quoting, commenting, and environment variable expansion.
9637c478bd9Sstevel@tonic-gate  *
9647c478bd9Sstevel@tonic-gate  * The standard wordexp(3C) function isn't used here because the POSIX
9657c478bd9Sstevel@tonic-gate  * definition is hard to use, and the Solaris implementation is
9667c478bd9Sstevel@tonic-gate  * resource-intensive and insecure.  The "hard-to-use" part is that
9677c478bd9Sstevel@tonic-gate  * wordexp expands only variables from the environment, and can't
9687c478bd9Sstevel@tonic-gate  * handle an environment overlay.  Instead, the caller must use the
9697c478bd9Sstevel@tonic-gate  * feeble putenv/getenv interface, and rewinding to the initial
9707c478bd9Sstevel@tonic-gate  * environment without leaking storage is hard.  The Solaris
9717c478bd9Sstevel@tonic-gate  * implementation invokes an undocumented extensions via
9727c478bd9Sstevel@tonic-gate  * fork/exec("/bin/ksh -\005 %s") for every invocation, and gathers
9737c478bd9Sstevel@tonic-gate  * the expanded result with pipe.  This makes it slow to execute and
9747c478bd9Sstevel@tonic-gate  * exposes the string being expanded to users with access to "ps -f."
9757c478bd9Sstevel@tonic-gate  *
9767c478bd9Sstevel@tonic-gate  * psp may be null; it's used only for environment variable expansion.
9777c478bd9Sstevel@tonic-gate  * Input "flag" is 1 to ignore EOL, '#', and '$'; 0 for normal file parsing.
9787c478bd9Sstevel@tonic-gate  *
9797c478bd9Sstevel@tonic-gate  * Returns:
9807c478bd9Sstevel@tonic-gate  *	0 - keyword parsed.
9817c478bd9Sstevel@tonic-gate  *	1 - end of file; no keyword.
9827c478bd9Sstevel@tonic-gate  *	2 - end of file after this keyword.
9837c478bd9Sstevel@tonic-gate  */
9847c478bd9Sstevel@tonic-gate static int
getkeyword(struct parse_state * psp,char * keybuf,int keymax,int (* nextchr)(void *),void * arg,int flag)9857c478bd9Sstevel@tonic-gate getkeyword(struct parse_state *psp, char *keybuf, int keymax,
9867c478bd9Sstevel@tonic-gate     int (*nextchr)(void *), void *arg, int flag)
9877c478bd9Sstevel@tonic-gate {
9887c478bd9Sstevel@tonic-gate 	char varnest[MAX_NEST];
9897c478bd9Sstevel@tonic-gate 	char *kbp;
9907c478bd9Sstevel@tonic-gate 	char *vnp;
9917c478bd9Sstevel@tonic-gate 	char chr;
9927c478bd9Sstevel@tonic-gate 	int ichr;
9937c478bd9Sstevel@tonic-gate 	char kwstate;
9947c478bd9Sstevel@tonic-gate 	static const char escstr[] = "a\ab\bf\fn\nr\r";
9957c478bd9Sstevel@tonic-gate 	const char *cp;
9967c478bd9Sstevel@tonic-gate 
9977c478bd9Sstevel@tonic-gate 	keymax--;	/* Account for trailing NUL byte */
9987c478bd9Sstevel@tonic-gate 
9997c478bd9Sstevel@tonic-gate 	kwstate = '\0';
10007c478bd9Sstevel@tonic-gate 	kbp = keybuf;
10017c478bd9Sstevel@tonic-gate 	vnp = varnest;
10027c478bd9Sstevel@tonic-gate 	for (;;) {
10037c478bd9Sstevel@tonic-gate 		ichr = (*nextchr)(arg);
10047c478bd9Sstevel@tonic-gate 		chr = (char)ichr;
10057c478bd9Sstevel@tonic-gate 	tryagain:
10067c478bd9Sstevel@tonic-gate 		switch (kwstate) {
10077c478bd9Sstevel@tonic-gate 		case '\\':	/* Start of unquoted escape sequence */
10087c478bd9Sstevel@tonic-gate 		case '|':	/* Start of escape sequence in double quotes */
10097c478bd9Sstevel@tonic-gate 		case '~':	/* Start of escape sequence in single quotes */
10107c478bd9Sstevel@tonic-gate 			/* Convert the character if we can. */
10117c478bd9Sstevel@tonic-gate 			if (chr == '\n')
10127c478bd9Sstevel@tonic-gate 				chr = '\0';
10137c478bd9Sstevel@tonic-gate 			else if (isalpha(chr) &&
10147c478bd9Sstevel@tonic-gate 			    (cp = strchr(escstr, chr)) != NULL)
10157c478bd9Sstevel@tonic-gate 				chr = cp[1];
10167c478bd9Sstevel@tonic-gate 			/* Revert to previous state */
10177c478bd9Sstevel@tonic-gate 			switch (kwstate) {
10187c478bd9Sstevel@tonic-gate 			case '\\':
10197c478bd9Sstevel@tonic-gate 				kwstate = 'A';
10207c478bd9Sstevel@tonic-gate 				break;
10217c478bd9Sstevel@tonic-gate 			case '|':
10227c478bd9Sstevel@tonic-gate 				kwstate = '"';
10237c478bd9Sstevel@tonic-gate 				break;
10247c478bd9Sstevel@tonic-gate 			case '~':
10257c478bd9Sstevel@tonic-gate 				kwstate = '\'';
10267c478bd9Sstevel@tonic-gate 				break;
10277c478bd9Sstevel@tonic-gate 			}
10287c478bd9Sstevel@tonic-gate 			break;
10297c478bd9Sstevel@tonic-gate 		case '"':	/* In double-quote string */
10307c478bd9Sstevel@tonic-gate 			if (!flag && chr == '$') {
10317c478bd9Sstevel@tonic-gate 				/* Handle variable expansion. */
10327c478bd9Sstevel@tonic-gate 				kwstate = '%';
10337c478bd9Sstevel@tonic-gate 				chr = '\0';
10347c478bd9Sstevel@tonic-gate 				break;
10357c478bd9Sstevel@tonic-gate 			}
10367c478bd9Sstevel@tonic-gate 				/* FALLTHROUGH */
10377c478bd9Sstevel@tonic-gate 		case '\'':	/* In single-quote string */
10387c478bd9Sstevel@tonic-gate 			if (chr == '\\') {
10397c478bd9Sstevel@tonic-gate 				/* Handle start of escape sequence */
10407c478bd9Sstevel@tonic-gate 				kwstate = kwstate == '"' ? '|' : '~';
10417c478bd9Sstevel@tonic-gate 				chr = '\0';
10427c478bd9Sstevel@tonic-gate 				break;
10437c478bd9Sstevel@tonic-gate 			}
10447c478bd9Sstevel@tonic-gate 			if (chr == kwstate) {
10457c478bd9Sstevel@tonic-gate 				/* End of quoted string; revert to normal */
10467c478bd9Sstevel@tonic-gate 				kwstate = 'A';
10477c478bd9Sstevel@tonic-gate 				chr = '\0';
10487c478bd9Sstevel@tonic-gate 			}
10497c478bd9Sstevel@tonic-gate 			break;
10507c478bd9Sstevel@tonic-gate 		case '$':	/* Start of unquoted variable name */
10517c478bd9Sstevel@tonic-gate 		case '%':	/* Start of variable name in quoted string */
10527c478bd9Sstevel@tonic-gate 			if (chr == '{') {
10537c478bd9Sstevel@tonic-gate 				/* Variable name is bracketed. */
10547c478bd9Sstevel@tonic-gate 				kwstate = chr =
10557c478bd9Sstevel@tonic-gate 				    kwstate == '$' ? '{' : '[';
10567c478bd9Sstevel@tonic-gate 				break;
10577c478bd9Sstevel@tonic-gate 			}
10587c478bd9Sstevel@tonic-gate 			*kbp++ = kwstate = kwstate == '$' ? '+' : '*';
10597c478bd9Sstevel@tonic-gate 				/* FALLTHROUGH */
10607c478bd9Sstevel@tonic-gate 		case '+':	/* Gathering unquoted variable name */
10617c478bd9Sstevel@tonic-gate 		case '*':	/* Gathering variable name in quoted string */
10627c478bd9Sstevel@tonic-gate 			if (chr == '$' &&
10637c478bd9Sstevel@tonic-gate 			    vnp < varnest + sizeof (varnest)) {
10647c478bd9Sstevel@tonic-gate 				*vnp++ = kwstate;
10657c478bd9Sstevel@tonic-gate 				kwstate = '$';
10667c478bd9Sstevel@tonic-gate 				chr = '\0';
10677c478bd9Sstevel@tonic-gate 				break;
10687c478bd9Sstevel@tonic-gate 			}
10697c478bd9Sstevel@tonic-gate 			if (!isalnum(chr) && chr != '_' &&
10707c478bd9Sstevel@tonic-gate 			    chr != '.' && chr != '-') {
10717c478bd9Sstevel@tonic-gate 				*kbp = '\0';
10727c478bd9Sstevel@tonic-gate 				kbp = env_replace(psp, keybuf, kwstate);
10737c478bd9Sstevel@tonic-gate 				if (vnp > varnest)
10747c478bd9Sstevel@tonic-gate 					kwstate = *--vnp;
10757c478bd9Sstevel@tonic-gate 				else
10767c478bd9Sstevel@tonic-gate 					kwstate = kwstate == '+' ?
10777c478bd9Sstevel@tonic-gate 					    'A' : '"';
10787c478bd9Sstevel@tonic-gate 				/* Go reinterpret in new context */
10797c478bd9Sstevel@tonic-gate 				goto tryagain;
10807c478bd9Sstevel@tonic-gate 			}
10817c478bd9Sstevel@tonic-gate 			break;
10827c478bd9Sstevel@tonic-gate 		case '{':	/* Gathering bracketed, unquoted var name */
10837c478bd9Sstevel@tonic-gate 		case '[':	/* Gathering bracketed, quoted var name */
10847c478bd9Sstevel@tonic-gate 			if (chr == '}') {
10857c478bd9Sstevel@tonic-gate 				*kbp = '\0';
10867c478bd9Sstevel@tonic-gate 				kbp = env_replace(psp, keybuf, kwstate);
10877c478bd9Sstevel@tonic-gate 				kwstate = kwstate == '{' ? 'A' : '"';
10887c478bd9Sstevel@tonic-gate 				chr = '\0';
10897c478bd9Sstevel@tonic-gate 			}
10907c478bd9Sstevel@tonic-gate 			break;
10917c478bd9Sstevel@tonic-gate 		case '#':	/* Comment before word state */
10927c478bd9Sstevel@tonic-gate 		case '@':	/* Comment after word state */
10937c478bd9Sstevel@tonic-gate 			if (chr == '\n' || chr == '\r' || ichr == EOF) {
10947c478bd9Sstevel@tonic-gate 				/* At end of line, revert to previous state */
10957c478bd9Sstevel@tonic-gate 				kwstate = kwstate == '#' ? '\0' : ' ';
10967c478bd9Sstevel@tonic-gate 				chr = '\0';
10977c478bd9Sstevel@tonic-gate 				break;
10987c478bd9Sstevel@tonic-gate 			}
10997c478bd9Sstevel@tonic-gate 			chr = '\0';
11007c478bd9Sstevel@tonic-gate 			break;
11017c478bd9Sstevel@tonic-gate 		case '\0':	/* Initial state; no word seen yet. */
11027c478bd9Sstevel@tonic-gate 			if (ichr == EOF || isspace(chr)) {
11037c478bd9Sstevel@tonic-gate 				chr = '\0';	/* Skip over leading spaces */
11047c478bd9Sstevel@tonic-gate 				break;
11057c478bd9Sstevel@tonic-gate 			}
11067c478bd9Sstevel@tonic-gate 			if (chr == '#') {
11077c478bd9Sstevel@tonic-gate 				kwstate = '#';
11087c478bd9Sstevel@tonic-gate 				chr = '\0';	/* Skip over comments */
11097c478bd9Sstevel@tonic-gate 				break;
11107c478bd9Sstevel@tonic-gate 			}
11117c478bd9Sstevel@tonic-gate 			/* Start of keyword seen. */
11127c478bd9Sstevel@tonic-gate 			kwstate = 'A';
11137c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
11147c478bd9Sstevel@tonic-gate 		default:	/* Middle of keyword parsing. */
11157c478bd9Sstevel@tonic-gate 			if (ichr == EOF)
11167c478bd9Sstevel@tonic-gate 				break;
11177c478bd9Sstevel@tonic-gate 			if (isspace(chr)) {	/* Space terminates word */
11187c478bd9Sstevel@tonic-gate 				kwstate = ' ';
11197c478bd9Sstevel@tonic-gate 				break;
11207c478bd9Sstevel@tonic-gate 			}
11217c478bd9Sstevel@tonic-gate 			if (chr == '"' || chr == '\'' || chr == '\\') {
11227c478bd9Sstevel@tonic-gate 				kwstate = chr;	/* Begin quote or escape */
11237c478bd9Sstevel@tonic-gate 				chr = '\0';
11247c478bd9Sstevel@tonic-gate 				break;
11257c478bd9Sstevel@tonic-gate 			}
11267c478bd9Sstevel@tonic-gate 			if (flag)	/* Allow ignore; for string reparse */
11277c478bd9Sstevel@tonic-gate 				break;
11287c478bd9Sstevel@tonic-gate 			if (chr == '#') {	/* Comment terminates word */
11297c478bd9Sstevel@tonic-gate 				kwstate = '@';	/* Must consume comment also */
11307c478bd9Sstevel@tonic-gate 				chr = '\0';
11317c478bd9Sstevel@tonic-gate 				break;
11327c478bd9Sstevel@tonic-gate 			}
11337c478bd9Sstevel@tonic-gate 			if (chr == '$') {
11347c478bd9Sstevel@tonic-gate 				kwstate = '$';	/* Begin variable expansion */
11357c478bd9Sstevel@tonic-gate 				chr = '\0';
11367c478bd9Sstevel@tonic-gate 			}
11377c478bd9Sstevel@tonic-gate 			break;
11387c478bd9Sstevel@tonic-gate 		}
11397c478bd9Sstevel@tonic-gate 		/*
11407c478bd9Sstevel@tonic-gate 		 * If we've reached a space at the end of the word,
11417c478bd9Sstevel@tonic-gate 		 * then we're done.
11427c478bd9Sstevel@tonic-gate 		 */
11437c478bd9Sstevel@tonic-gate 		if (ichr == EOF || kwstate == ' ')
11447c478bd9Sstevel@tonic-gate 			break;
11457c478bd9Sstevel@tonic-gate 		/*
11467c478bd9Sstevel@tonic-gate 		 * If there's a character to store and space
11477c478bd9Sstevel@tonic-gate 		 * available, then add it to the string
11487c478bd9Sstevel@tonic-gate 		 */
11497c478bd9Sstevel@tonic-gate 		if (chr != '\0' && kbp < keybuf + keymax)
11507c478bd9Sstevel@tonic-gate 			*kbp++ = (char)chr;
11517c478bd9Sstevel@tonic-gate 	}
11527c478bd9Sstevel@tonic-gate 
11537c478bd9Sstevel@tonic-gate 	*kbp = '\0';
11547c478bd9Sstevel@tonic-gate 
11557c478bd9Sstevel@tonic-gate 	if (ichr == EOF) {
11567c478bd9Sstevel@tonic-gate 		return (kwstate == '\0' ? 1 : 2);
11577c478bd9Sstevel@tonic-gate 	}
11587c478bd9Sstevel@tonic-gate 	return (0);
11597c478bd9Sstevel@tonic-gate }
11607c478bd9Sstevel@tonic-gate 
11617c478bd9Sstevel@tonic-gate /*
11627c478bd9Sstevel@tonic-gate  * Fetch words from current file until all files are closed.  Handles
11637c478bd9Sstevel@tonic-gate  * include files.
11647c478bd9Sstevel@tonic-gate  */
11657c478bd9Sstevel@tonic-gate static void
parse_from_file(struct parse_state * psp)11667c478bd9Sstevel@tonic-gate parse_from_file(struct parse_state *psp)
11677c478bd9Sstevel@tonic-gate {
11687c478bd9Sstevel@tonic-gate 	char keybuf[MAX_KEYWORD];
11697c478bd9Sstevel@tonic-gate 	int retv;
11707c478bd9Sstevel@tonic-gate 
11717c478bd9Sstevel@tonic-gate 	while (psp->ps_cfile != NULL && psp->ps_cfile->pf_input != NULL) {
11727c478bd9Sstevel@tonic-gate 		retv = getkeyword(psp, keybuf, sizeof (keybuf),
11737c478bd9Sstevel@tonic-gate 		    (int (*)(void *))fgetc, (void *)psp->ps_cfile->pf_input,
11747c478bd9Sstevel@tonic-gate 		    0);
11757c478bd9Sstevel@tonic-gate 
11767c478bd9Sstevel@tonic-gate 		if (retv != 1)
11777c478bd9Sstevel@tonic-gate 			(void)