xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.lib/pppoe/options.c (revision 9751:8e29565352fc)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
54321Scasper  * Common Development and Distribution License (the "License").
64321Scasper  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
220Sstevel@tonic-gate  * PPPoE Server-mode daemon option parsing.
230Sstevel@tonic-gate  *
24*9751Sjames.d.carlson@sun.com  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
250Sstevel@tonic-gate  * Use is subject to license terms.
260Sstevel@tonic-gate  */
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <stdio.h>
290Sstevel@tonic-gate #include <stdlib.h>
300Sstevel@tonic-gate #include <unistd.h>
310Sstevel@tonic-gate #include <assert.h>
320Sstevel@tonic-gate #include <ctype.h>
330Sstevel@tonic-gate #include <string.h>
340Sstevel@tonic-gate #include <sys/types.h>
350Sstevel@tonic-gate #include <fcntl.h>
360Sstevel@tonic-gate #include <pwd.h>
370Sstevel@tonic-gate #include <grp.h>
380Sstevel@tonic-gate #include <errno.h>
390Sstevel@tonic-gate #include <netdb.h>
400Sstevel@tonic-gate #include <stropts.h>
410Sstevel@tonic-gate #include <sys/stat.h>
420Sstevel@tonic-gate #include <sys/socket.h>
430Sstevel@tonic-gate #include <net/if.h>
440Sstevel@tonic-gate #include <netinet/in.h>
450Sstevel@tonic-gate #include <netinet/if_ether.h>
460Sstevel@tonic-gate #include <net/sppptun.h>
470Sstevel@tonic-gate 
480Sstevel@tonic-gate #include "common.h"
490Sstevel@tonic-gate #include "logging.h"
500Sstevel@tonic-gate 
510Sstevel@tonic-gate #define	MAX_KEYWORD	4096	/* Maximum token length */
520Sstevel@tonic-gate #define	MAX_NEST	32	/* Maximum ${$sub} nesting */
530Sstevel@tonic-gate #define	MAXARGS		256	/* Maximum number of pppd arguments */
540Sstevel@tonic-gate 
550Sstevel@tonic-gate /*
560Sstevel@tonic-gate  * Client filter entry.  These are linked in *reverse* order so that
570Sstevel@tonic-gate  * the DAG created by file inclusion nesting works as expected.  Since
580Sstevel@tonic-gate  * the administrator who wrote the configuration expects "first
590Sstevel@tonic-gate  * match," this means that tests against the filter list must actually
600Sstevel@tonic-gate  * use "last match."
610Sstevel@tonic-gate  */
620Sstevel@tonic-gate struct filter_entry {
630Sstevel@tonic-gate 	struct filter_entry *fe_prev;	/* Previous filter in list */
640Sstevel@tonic-gate 	struct ether_addr fe_mac;	/* MAC address */
650Sstevel@tonic-gate 	struct ether_addr fe_mask;	/* Mask for above address test */
660Sstevel@tonic-gate 	uchar_t fe_isexcept;	/* invert sense; exclude matching clients */
670Sstevel@tonic-gate 	uchar_t fe_prevcopy;		/* fe_prev points to copied list */
680Sstevel@tonic-gate 	uchar_t fe_unused[2];		/* padding */
690Sstevel@tonic-gate };
700Sstevel@tonic-gate 
710Sstevel@tonic-gate /*
720Sstevel@tonic-gate  * Note: I would like to make the strings and filters here const, but
730Sstevel@tonic-gate  * I can't because they have to be passed to free() during parsing.  I
740Sstevel@tonic-gate  * could work around this with offsetof() or data copies, but it's not
750Sstevel@tonic-gate  * worth the effort.
760Sstevel@tonic-gate  */
770Sstevel@tonic-gate struct service_entry {
780Sstevel@tonic-gate 	const char *se_name;		/* Name of service */
790Sstevel@tonic-gate 	struct filter_entry *se_flist;	/* Pointer to list of client filters */
800Sstevel@tonic-gate 	uint_t se_flags;		/* SEF_* flags (below) */
810Sstevel@tonic-gate 	int se_debug;			/* Debug level (0=nodebug) */
820Sstevel@tonic-gate 	char *se_server;		/* Server (AC) name */
830Sstevel@tonic-gate 	char *se_pppd;			/* Options for pppd */
840Sstevel@tonic-gate 	char *se_path;			/* Path to pppd executable */
850Sstevel@tonic-gate 	char *se_extra;			/* Extra options */
860Sstevel@tonic-gate 	char *se_log;			/* Log file */
870Sstevel@tonic-gate 	uid_t se_uid;			/* User ID */
880Sstevel@tonic-gate 	gid_t se_gid;			/* Group ID */
890Sstevel@tonic-gate };
900Sstevel@tonic-gate 
910Sstevel@tonic-gate #define	SEF_WILD	0x00000001	/* Offer in wildcard reply */
920Sstevel@tonic-gate #define	SEF_NOWILD	0x00000002	/* Don't offer in wildcard */
930Sstevel@tonic-gate #define	SEF_CFLIST	0x00000004	/* se_flist copied from global */
940Sstevel@tonic-gate #define	SEF_CSERVER	0x00000008	/* se_server copied from global */
950Sstevel@tonic-gate #define	SEF_CPPPD	0x00000010	/* se_pppd copied from global */
960Sstevel@tonic-gate #define	SEF_CPATH	0x00000020	/* se_path copied from global */
970Sstevel@tonic-gate #define	SEF_CEXTRA	0x00000040	/* se_extra copied from global */
980Sstevel@tonic-gate #define	SEF_CLOG	0x00000080	/* se_log copied from global */
990Sstevel@tonic-gate #define	SEF_UIDSET	0x00000100	/* se_uid has been set */
1000Sstevel@tonic-gate #define	SEF_GIDSET	0x00000200	/* se_gid has been set */
1010Sstevel@tonic-gate #define	SEF_DEBUGCLR	0x00000400	/* do not add se_debug from global */
1020Sstevel@tonic-gate #define	SEF_CDEV	0x00000800	/* copied devs (parse only) */
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate /*
1050Sstevel@tonic-gate  * One of these is allocated per lower-level stream (device) that is
1060Sstevel@tonic-gate  * referenced by the configuration files.  The queries are received
1070Sstevel@tonic-gate  * per device, and this structure allows us to find all of the
1080Sstevel@tonic-gate  * services that correspond to that device.
1090Sstevel@tonic-gate  */
1100Sstevel@tonic-gate struct device_entry {
1110Sstevel@tonic-gate 	const char *de_name;
1120Sstevel@tonic-gate 	const struct service_entry **de_services;
1130Sstevel@tonic-gate 	int de_nservices;
1140Sstevel@tonic-gate };
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate /*
1170Sstevel@tonic-gate  * This is the parsed configuration.  While a new configuration is
1180Sstevel@tonic-gate  * being read, this is kept around until the new configuration is
1190Sstevel@tonic-gate  * ready, and then it is discarded in one operation.  It has an array
1200Sstevel@tonic-gate  * of device entries (as above) -- one per referenced lower stream --
1210Sstevel@tonic-gate  * and a pointer to the allocated parser information.  The latter is
1220Sstevel@tonic-gate  * kept around because we reuse pointers rather than reallocating and
1230Sstevel@tonic-gate  * copying the data.  There are thus multiple aliases to the dynamic
1240Sstevel@tonic-gate  * data, and the "owner" (for purposes of freeing the storage) is
1250Sstevel@tonic-gate  * considered to be this 'junk' list.
1260Sstevel@tonic-gate  */
1270Sstevel@tonic-gate struct option_state {
1280Sstevel@tonic-gate 	const struct device_entry *os_devices;
1290Sstevel@tonic-gate 	int os_ndevices;
1300Sstevel@tonic-gate 	struct per_file *os_pfjunk;	/* Kept for deallocation */
1310Sstevel@tonic-gate 	char **os_evjunk;		/* ditto */
1320Sstevel@tonic-gate };
1330Sstevel@tonic-gate 
1340Sstevel@tonic-gate /*
1350Sstevel@tonic-gate  * This is the root pointer to the current parsed options.
1360Sstevel@tonic-gate  * This cannot be const because it's passed to free() when reparsing
1370Sstevel@tonic-gate  * options.
1380Sstevel@tonic-gate  */
1390Sstevel@tonic-gate static struct option_state *cur_options;
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate /* Global settings for module-wide options. */
1420Sstevel@tonic-gate static struct service_entry glob_svc;
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate /*
1450Sstevel@tonic-gate  * *******************************************************************
1460Sstevel@tonic-gate  * Data structures generated during parsing.
1470Sstevel@tonic-gate  */
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate /* List of device names attached to one service */
1500Sstevel@tonic-gate struct device_list {
1510Sstevel@tonic-gate 	struct device_list *dl_next;
1520Sstevel@tonic-gate 	const char *dl_name;		/* Name of one device */
1530Sstevel@tonic-gate };
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate /* Entry for a single defined service. */
1560Sstevel@tonic-gate struct service_list {
1570Sstevel@tonic-gate 	struct service_entry sl_entry;	/* Parsed service data */
1580Sstevel@tonic-gate 	struct service_list *sl_next;	/* Next service entry */
1590Sstevel@tonic-gate 	struct parse_state *sl_parse;	/* Back pointer to state */
1600Sstevel@tonic-gate 	struct device_list *sl_dev;	/* List of devices */
1610Sstevel@tonic-gate 	int sl_serial;			/* Serial number (conflict resolve) */
1620Sstevel@tonic-gate };
1630Sstevel@tonic-gate #define	SESERIAL(x)	((struct service_list *)&(x))->sl_serial
1640Sstevel@tonic-gate #define	ISGLOBAL(x)	((x) == &(x)->sl_parse->ps_cfile->pf_global)
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate /*
1670Sstevel@tonic-gate  * Structure allocated for each file opened.  File nesting is chained
1680Sstevel@tonic-gate  * in reverse order so that global option scoping works as expected.
1690Sstevel@tonic-gate  */
1700Sstevel@tonic-gate struct per_file {
1710Sstevel@tonic-gate 	struct per_file *pf_prev;	/* Back chain */
1720Sstevel@tonic-gate 	struct service_list pf_global;	/* Global (default) service context */
1730Sstevel@tonic-gate 	struct service_list *pf_svc;	/* List of services */
1740Sstevel@tonic-gate 	struct service_list *pf_svc_last;
1750Sstevel@tonic-gate 	FILE *pf_input;			/* File for input */
1760Sstevel@tonic-gate 	const char *pf_name;		/* File name */
1770Sstevel@tonic-gate 	int pf_nsvc;			/* Count of services */
1780Sstevel@tonic-gate };
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate /* State of parser */
1810Sstevel@tonic-gate enum key_state {
1820Sstevel@tonic-gate 	ksDefault, ksService, ksDevice, ksClient, ksClientE, ksServer,
1830Sstevel@tonic-gate 	ksPppd, ksFile, ksPath, ksExtra, ksLog, ksUser, ksGroup
1840Sstevel@tonic-gate };
1850Sstevel@tonic-gate 
1860Sstevel@tonic-gate /*
1870Sstevel@tonic-gate  * Global parser state.  There is one of these structures, and it
1880Sstevel@tonic-gate  * exists only while actively parsing configuration files.
1890Sstevel@tonic-gate  */
1900Sstevel@tonic-gate struct parse_state {
1910Sstevel@tonic-gate 	enum key_state ps_state;	/* Parser state */
1920Sstevel@tonic-gate 	int ps_serial;			/* Service serial number */
1930Sstevel@tonic-gate 	struct per_file *ps_files;	/* Parsed files */
1940Sstevel@tonic-gate 	struct per_file *ps_cfile;	/* Current file */
1950Sstevel@tonic-gate 	struct service_list *ps_csvc;	/* Current service */
1960Sstevel@tonic-gate 	struct device_list *ps_star;	/* Wildcard device */
1970Sstevel@tonic-gate 	int ps_flags;			/* PSF_* below */
1980Sstevel@tonic-gate 	char **ps_evlist;		/* allocated environment variables */
1990Sstevel@tonic-gate 	int ps_evsize;			/* max length; for realloc */
2000Sstevel@tonic-gate };
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate #define	PSF_PERDEV	0x0001		/* In a per-device file */
2030Sstevel@tonic-gate #define	PSF_SETLEVEL	0x0002		/* Set log level along the way */
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate /* Should be in a library somewhere. */
2060Sstevel@tonic-gate static char *
strsave(const char * str)2070Sstevel@tonic-gate strsave(const char *str)
2080Sstevel@tonic-gate {
2090Sstevel@tonic-gate 	char *newstr;
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 	if (str == NULL)
2120Sstevel@tonic-gate 		return (NULL);
2130Sstevel@tonic-gate 	newstr = (char *)malloc(strlen(str) + 1);
2140Sstevel@tonic-gate 	if (newstr != NULL)
2150Sstevel@tonic-gate 		(void) strcpy(newstr, str);
2160Sstevel@tonic-gate 	return (newstr);
2170Sstevel@tonic-gate }
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate /*
2200Sstevel@tonic-gate  * Stop defining current service and revert to global definition.
2210Sstevel@tonic-gate  * This resolves any implicit references to global options by copying
2220Sstevel@tonic-gate  * ("inheriting") from the current global state.
2230Sstevel@tonic-gate  */
2240Sstevel@tonic-gate static void
close_service(struct service_list * slp)2250Sstevel@tonic-gate close_service(struct service_list *slp)
2260Sstevel@tonic-gate {
2270Sstevel@tonic-gate 	struct parse_state *psp;
2280Sstevel@tonic-gate 	struct per_file *cfile;
2290Sstevel@tonic-gate 	struct service_entry *sep;
2300Sstevel@tonic-gate 	struct service_entry *sedefp;
2310Sstevel@tonic-gate 	struct filter_entry *fep;
2320Sstevel@tonic-gate 
2330Sstevel@tonic-gate 	assert(slp != NULL);
2340Sstevel@tonic-gate 	psp = slp->sl_parse;
2350Sstevel@tonic-gate 	cfile = psp->ps_cfile;
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 	/* If no current file, then nothing to close. */
2380Sstevel@tonic-gate 	if (cfile == NULL)
2390Sstevel@tonic-gate 		return;
2400Sstevel@tonic-gate 
2410Sstevel@tonic-gate 	sep = &slp->sl_entry;
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate 	/*
2440Sstevel@tonic-gate 	 * Fix up filter pointers to make DAG.  First, locate
2450Sstevel@tonic-gate 	 * the end of the filter list.
2460Sstevel@tonic-gate 	 */
2470Sstevel@tonic-gate 	if (sep->se_flags & SEF_CFLIST) {
2480Sstevel@tonic-gate 		sep->se_flist = fep = NULL;
2490Sstevel@tonic-gate 	} else {
2500Sstevel@tonic-gate 		for (fep = sep->se_flist; fep != NULL; fep = fep->fe_prev)
2510Sstevel@tonic-gate 			if (fep->fe_prev == NULL || fep->fe_prevcopy) {
2520Sstevel@tonic-gate 				fep->fe_prev = NULL;
2530Sstevel@tonic-gate 				break;
2540Sstevel@tonic-gate 			}
2550Sstevel@tonic-gate 	}
2560Sstevel@tonic-gate 	if (slp == &cfile->pf_global) {
2570Sstevel@tonic-gate 		/*
2580Sstevel@tonic-gate 		 * If we're in a global context, then we're about to
2590Sstevel@tonic-gate 		 * open a new service, so it's time to fix up the
2600Sstevel@tonic-gate 		 * filter list so that it's usable as a reference.
2610Sstevel@tonic-gate 		 * Loop through files from which we were included, and
2620Sstevel@tonic-gate 		 * link up filters.  Note: closure may occur more than
2630Sstevel@tonic-gate 		 * once here.
2640Sstevel@tonic-gate 		 */
2650Sstevel@tonic-gate 		/* We don't inherit from ourselves. */
2660Sstevel@tonic-gate 		cfile = cfile->pf_prev;
2670Sstevel@tonic-gate 		while (cfile != NULL) {
2680Sstevel@tonic-gate 			if (fep == NULL) {
2690Sstevel@tonic-gate 				sep->se_flist = fep =
2700Sstevel@tonic-gate 				    cfile->pf_global.sl_entry.se_flist;
2710Sstevel@tonic-gate 				sep->se_flags |= SEF_CFLIST;
2720Sstevel@tonic-gate 			} else if (fep->fe_prev == NULL) {
2730Sstevel@tonic-gate 				fep->fe_prev =
2740Sstevel@tonic-gate 				    cfile->pf_global.sl_entry.se_flist;
2750Sstevel@tonic-gate 				fep->fe_prevcopy = 1;
2760Sstevel@tonic-gate 			}
2770Sstevel@tonic-gate 			cfile = cfile->pf_prev;
2780Sstevel@tonic-gate 		}
2790Sstevel@tonic-gate 	} else {
2800Sstevel@tonic-gate 		/*
2810Sstevel@tonic-gate 		 * Loop through default options in current and all
2820Sstevel@tonic-gate 		 * enclosing include files.  Inherit options.
2830Sstevel@tonic-gate 		 */
2840Sstevel@tonic-gate 		logdbg("service %s ends", slp->sl_entry.se_name);
2850Sstevel@tonic-gate 		while (cfile != NULL) {
2860Sstevel@tonic-gate 			/* Inherit from global service options. */
2870Sstevel@tonic-gate 			if (slp->sl_dev == NULL) {
2880Sstevel@tonic-gate 				slp->sl_dev = cfile->pf_global.sl_dev;
2890Sstevel@tonic-gate 				sep->se_flags |= SEF_CDEV;
2900Sstevel@tonic-gate 			}
2910Sstevel@tonic-gate 			sedefp = &cfile->pf_global.sl_entry;
2920Sstevel@tonic-gate 			if (fep == NULL) {
2930Sstevel@tonic-gate 				sep->se_flist = fep = sedefp->se_flist;
2940Sstevel@tonic-gate 				sep->se_flags |= SEF_CFLIST;
2950Sstevel@tonic-gate 			} else if (fep->fe_prev == NULL) {
2960Sstevel@tonic-gate 				fep->fe_prev = sedefp->se_flist;
2970Sstevel@tonic-gate 				fep->fe_prevcopy = 1;
2980Sstevel@tonic-gate 			}
2990Sstevel@tonic-gate 			if (sep->se_server == NULL) {
3000Sstevel@tonic-gate 				sep->se_server = sedefp->se_server;
3010Sstevel@tonic-gate 				sep->se_flags |= SEF_CSERVER;
3020Sstevel@tonic-gate 			}
3030Sstevel@tonic-gate 			if (sep->se_pppd == NULL) {
3040Sstevel@tonic-gate 				sep->se_pppd = sedefp->se_pppd;
3050Sstevel@tonic-gate 				sep->se_flags |= SEF_CPPPD;
3060Sstevel@tonic-gate 			}
3070Sstevel@tonic-gate 			if (sep->se_path == NULL) {
3080Sstevel@tonic-gate 				sep->se_path = sedefp->se_path;
3090Sstevel@tonic-gate 				sep->se_flags |= SEF_CPATH;
3100Sstevel@tonic-gate 			}
3110Sstevel@tonic-gate 			if (sep->se_extra == NULL) {
3120Sstevel@tonic-gate 				sep->se_extra = sedefp->se_extra;
3130Sstevel@tonic-gate 				sep->se_flags |= SEF_CEXTRA;
3140Sstevel@tonic-gate 			}
3150Sstevel@tonic-gate 			if (sep->se_log == NULL) {
3160Sstevel@tonic-gate 				sep->se_log = sedefp->se_log;
3170Sstevel@tonic-gate 				sep->se_flags |= SEF_CLOG;
3180Sstevel@tonic-gate 			}
3190Sstevel@tonic-gate 			if (!(sep->se_flags & SEF_UIDSET) &&
3200Sstevel@tonic-gate 			    (sedefp->se_flags & SEF_UIDSET)) {
3210Sstevel@tonic-gate 				sep->se_uid = sedefp->se_uid;
3220Sstevel@tonic-gate 				sep->se_flags |= SEF_UIDSET;
3230Sstevel@tonic-gate 			}
3240Sstevel@tonic-gate 			if (!(sep->se_flags & SEF_GIDSET) &&
3250Sstevel@tonic-gate 			    (sedefp->se_flags & SEF_GIDSET)) {
3260Sstevel@tonic-gate 				sep->se_gid = sedefp->se_gid;
3270Sstevel@tonic-gate 				sep->se_flags |= SEF_GIDSET;
3280Sstevel@tonic-gate 			}
3290Sstevel@tonic-gate 			if (!(sep->se_flags & (SEF_WILD|SEF_NOWILD)))
3300Sstevel@tonic-gate 				sep->se_flags |= sedefp->se_flags &
3310Sstevel@tonic-gate 				    (SEF_WILD|SEF_NOWILD);
3320Sstevel@tonic-gate 			if (!(sep->se_flags & SEF_DEBUGCLR)) {
3330Sstevel@tonic-gate 				sep->se_debug += sedefp->se_debug;
3340Sstevel@tonic-gate 				sep->se_flags |= sedefp->se_flags &
3350Sstevel@tonic-gate 				    SEF_DEBUGCLR;
3360Sstevel@tonic-gate 			}
3370Sstevel@tonic-gate 			cfile = cfile->pf_prev;
3380Sstevel@tonic-gate 		}
3390Sstevel@tonic-gate 	}
3400Sstevel@tonic-gate 	/* Revert to global definitions. */
3410Sstevel@tonic-gate 	psp->ps_csvc = &psp->ps_cfile->pf_global;
3420Sstevel@tonic-gate }
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate /* Discard a dynamic device list */
3450Sstevel@tonic-gate static void
free_device_list(struct device_list * dlp)3460Sstevel@tonic-gate free_device_list(struct device_list *dlp)
3470Sstevel@tonic-gate {
3480Sstevel@tonic-gate 	struct device_list *dln;
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate 	while (dlp != NULL) {
3510Sstevel@tonic-gate 		dln = dlp->dl_next;
3520Sstevel@tonic-gate 		free(dlp);
3530Sstevel@tonic-gate 		dlp = dln;
3540Sstevel@tonic-gate 	}
3550Sstevel@tonic-gate }
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate /*
3580Sstevel@tonic-gate  * Handle "service <name>" -- finish up previous service definition
3590Sstevel@tonic-gate  * (if any) by copying from global state where necessary, and start
3600Sstevel@tonic-gate  * defining new service.
3610Sstevel@tonic-gate  */
3620Sstevel@tonic-gate static int
set_service(struct service_list * slp,const char * str)3630Sstevel@tonic-gate set_service(struct service_list *slp, const char *str)
3640Sstevel@tonic-gate {
3650Sstevel@tonic-gate 	struct parse_state *psp;
3660Sstevel@tonic-gate 	struct per_file *cfile;
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate 	/* Finish current service */
3690Sstevel@tonic-gate 	close_service(slp);
3700Sstevel@tonic-gate 
3710Sstevel@tonic-gate 	/* Start new service */
3720Sstevel@tonic-gate 	psp = slp->sl_parse;
3730Sstevel@tonic-gate 	slp = (struct service_list *)calloc(sizeof (*slp) + strlen(str) + 1,
3740Sstevel@tonic-gate 	    1);
3750Sstevel@tonic-gate 	if (slp == NULL) {
3760Sstevel@tonic-gate 		logerr("no memory for service \"%s\"", str);
3770Sstevel@tonic-gate 		return (-1);
3780Sstevel@tonic-gate 	}
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 	/* Add to end of list */
3810Sstevel@tonic-gate 	cfile = psp->ps_cfile;
3820Sstevel@tonic-gate 	if (cfile->pf_svc_last == NULL)
3830Sstevel@tonic-gate 		cfile->pf_svc = slp;
3840Sstevel@tonic-gate 	else
3850Sstevel@tonic-gate 		cfile->pf_svc_last->sl_next = slp;
3860Sstevel@tonic-gate 	cfile->pf_svc_last = slp;
3870Sstevel@tonic-gate 	cfile->pf_nsvc++;
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 	/* Fill in initial service entry */
3900Sstevel@tonic-gate 	slp->sl_entry.se_name = (const char *)(slp+1);
3910Sstevel@tonic-gate 	(void) strcpy((char *)(slp+1), str);
3920Sstevel@tonic-gate 	logdbg("service %s begins", slp->sl_entry.se_name);
3930Sstevel@tonic-gate 	slp->sl_serial = psp->ps_serial++;
3940Sstevel@tonic-gate 	slp->sl_parse = psp;
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate 	/* This is now the current service that we're defining. */
3970Sstevel@tonic-gate 	psp->ps_csvc = slp;
3980Sstevel@tonic-gate 	return (0);
3990Sstevel@tonic-gate }
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate /*
4020Sstevel@tonic-gate  * Handle both "wildcard" and "nowildcard" options.
4030Sstevel@tonic-gate  */
4040Sstevel@tonic-gate static int
set_wildcard(struct service_list * slp,const char * str)4050Sstevel@tonic-gate set_wildcard(struct service_list *slp, const char *str)
4060Sstevel@tonic-gate {
4070Sstevel@tonic-gate 	/* Allow global context to switch back and forth without error. */
4080Sstevel@tonic-gate 	if (!ISGLOBAL(slp) &&
4090Sstevel@tonic-gate 	    (slp->sl_entry.se_flags & (SEF_WILD|SEF_NOWILD))) {
4100Sstevel@tonic-gate 		logdbg("%s: extra \"%s\" ignored",
4110Sstevel@tonic-gate 		    slp->sl_parse->ps_cfile->pf_name, str);
4120Sstevel@tonic-gate 		return (0);
4130Sstevel@tonic-gate 	}
4140Sstevel@tonic-gate 	slp->sl_entry.se_flags =
4150Sstevel@tonic-gate 	    (slp->sl_entry.se_flags & ~(SEF_WILD|SEF_NOWILD)) |
4160Sstevel@tonic-gate 	    (*str == 'n' ? SEF_NOWILD : SEF_WILD);
4170Sstevel@tonic-gate 	return (0);
4180Sstevel@tonic-gate }
4190Sstevel@tonic-gate 
4200Sstevel@tonic-gate /*
4210Sstevel@tonic-gate  * Handle "debug" option.
4220Sstevel@tonic-gate  */
4230Sstevel@tonic-gate /*ARGSUSED*/
4240Sstevel@tonic-gate static int
set_debug(struct service_list * slp,const char * str)4250Sstevel@tonic-gate set_debug(struct service_list *slp, const char *str)
4260Sstevel@tonic-gate {
4270Sstevel@tonic-gate 	slp->sl_entry.se_debug++;
4280Sstevel@tonic-gate 	if (ISGLOBAL(slp) && (slp->sl_parse->ps_flags & PSF_SETLEVEL)) {
4290Sstevel@tonic-gate 		log_level = slp->sl_entry.se_debug;
4300Sstevel@tonic-gate 	}
4310Sstevel@tonic-gate 	return (0);
4320Sstevel@tonic-gate }
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate /*
4350Sstevel@tonic-gate  * Handle "nodebug" option.
4360Sstevel@tonic-gate  */
4370Sstevel@tonic-gate /*ARGSUSED*/
4380Sstevel@tonic-gate static int
set_nodebug(struct service_list * slp,const char * str)4390Sstevel@tonic-gate set_nodebug(struct service_list *slp, const char *str)
4400Sstevel@tonic-gate {
4410Sstevel@tonic-gate 	slp->sl_entry.se_flags |= SEF_DEBUGCLR;
4420Sstevel@tonic-gate 	slp->sl_entry.se_debug = 0;
4430Sstevel@tonic-gate 	if (ISGLOBAL(slp) && (slp->sl_parse->ps_flags & PSF_SETLEVEL)) {
4440Sstevel@tonic-gate 		log_level = slp->sl_entry.se_debug;
4450Sstevel@tonic-gate 	}
4460Sstevel@tonic-gate 	return (0);
4470Sstevel@tonic-gate }
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate /*
4500Sstevel@tonic-gate  * Handle all plain string options; "server", "pppd", "path", "extra",
4510Sstevel@tonic-gate  * and "log".
4520Sstevel@tonic-gate  */
4530Sstevel@tonic-gate static int
set_string(struct service_list * slp,const char * str)4540Sstevel@tonic-gate set_string(struct service_list *slp, const char *str)
4550Sstevel@tonic-gate {
4560Sstevel@tonic-gate 	char **cpp;
4570Sstevel@tonic-gate 
4580Sstevel@tonic-gate 	assert(!(slp->sl_entry.se_flags &
4590Sstevel@tonic-gate 	    (SEF_CSERVER|SEF_CPPPD|SEF_CPATH|SEF_CEXTRA|SEF_CLOG)));
4600Sstevel@tonic-gate 	switch (slp->sl_parse->ps_state) {
4610Sstevel@tonic-gate 	case ksServer:
4620Sstevel@tonic-gate 		cpp = &slp->sl_entry.se_server;
4630Sstevel@tonic-gate 		break;
4640Sstevel@tonic-gate 	case ksPppd:
4650Sstevel@tonic-gate 		cpp = &slp->sl_entry.se_pppd;
4660Sstevel@tonic-gate 		break;
4670Sstevel@tonic-gate 	case ksPath:
4680Sstevel@tonic-gate 		cpp = &slp->sl_entry.se_path;
4690Sstevel@tonic-gate 		break;
4700Sstevel@tonic-gate 	case ksExtra:
4710Sstevel@tonic-gate 		cpp = &slp->sl_entry.se_extra;
4720Sstevel@tonic-gate 		break;
4730Sstevel@tonic-gate 	case ksLog:
4740Sstevel@tonic-gate 		cpp = &slp->sl_entry.se_log;
4750Sstevel@tonic-gate 		break;
4760Sstevel@tonic-gate 	default:
4770Sstevel@tonic-gate 		assert(0);
4780Sstevel@tonic-gate 		return (-1);
4790Sstevel@tonic-gate 	}
4800Sstevel@tonic-gate 	if (*cpp != NULL)
4810Sstevel@tonic-gate 		free(*cpp);
4820Sstevel@tonic-gate 	*cpp = strsave(str);
4830Sstevel@tonic-gate 	return (0);
4840Sstevel@tonic-gate }
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate /*
4870Sstevel@tonic-gate  * Handle "file <name>" option.  Close out current service (if any)
4880Sstevel@tonic-gate  * and begin parsing from new file.
4890Sstevel@tonic-gate  */
4900Sstevel@tonic-gate static int
set_file(struct service_list * slp,const char * str)4910Sstevel@tonic-gate set_file(struct service_list *slp, const char *str)
4920Sstevel@tonic-gate {
4930Sstevel@tonic-gate 	FILE *fp;
4940Sstevel@tonic-gate 	struct per_file *pfp;
4950Sstevel@tonic-gate 	struct parse_state *psp;
4960Sstevel@tonic-gate 
4970Sstevel@tonic-gate 	close_service(slp);
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate 	if ((fp = fopen(str, "r")) == NULL) {
5000Sstevel@tonic-gate 		logwarn("%s: %s: %s", slp->sl_parse->ps_cfile->pf_name, str,
5010Sstevel@tonic-gate 		    mystrerror(errno));
5020Sstevel@tonic-gate 		return (-1);
5030Sstevel@tonic-gate 	}
5040Sstevel@tonic-gate 	pfp = (struct per_file *)calloc(sizeof (*pfp) + strlen(str) + 1, 1);
5050Sstevel@tonic-gate 	if (pfp == NULL) {
5060Sstevel@tonic-gate 		logerr("no memory for parsing file %s", str);
5070Sstevel@tonic-gate 		(void) fclose(fp);
5080Sstevel@tonic-gate 		return (-1);
5090Sstevel@tonic-gate 	}
5100Sstevel@tonic-gate 	logdbg("config file %s open", str);
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate 	/* Fill in new file structure. */
5130Sstevel@tonic-gate 	pfp->pf_name = (const char *)(pfp+1);
5140Sstevel@tonic-gate 	(void) strcpy((char *)(pfp+1), str);
5150Sstevel@tonic-gate 	pfp->pf_input = fp;
5160Sstevel@tonic-gate 	psp = slp->sl_parse;
5170Sstevel@tonic-gate 	pfp->pf_prev = psp->ps_cfile;
5180Sstevel@tonic-gate 	psp->ps_cfile = pfp;
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate 	/* Start off in global context for this file. */
5210Sstevel@tonic-gate 	psp->ps_csvc = &pfp->pf_global;
5220Sstevel@tonic-gate 	pfp->pf_global.sl_parse = psp;
5230Sstevel@tonic-gate 	pfp->pf_global.sl_entry.se_name = "<global>";
5240Sstevel@tonic-gate 	return (0);
5250Sstevel@tonic-gate }
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate /*
5280Sstevel@tonic-gate  * Handle "device <list>" option.
5290Sstevel@tonic-gate  */
5300Sstevel@tonic-gate static int
set_device(struct service_list * slp,const char * str)5310Sstevel@tonic-gate set_device(struct service_list *slp, const char *str)
5320Sstevel@tonic-gate {
5330Sstevel@tonic-gate 	struct parse_state *psp = slp->sl_parse;
5340Sstevel@tonic-gate 	struct device_list *dlp;
5350Sstevel@tonic-gate 	struct device_list *dln;
5360Sstevel@tonic-gate 	struct device_list **dlpp;
5370Sstevel@tonic-gate 	const char *cp;
5380Sstevel@tonic-gate 	int len;
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 	/* Can't use this option in the per-device files. */
5410Sstevel@tonic-gate 	if (psp->ps_flags & PSF_PERDEV) {
5420Sstevel@tonic-gate 		logerr("\"device %s\" ignored in %s", str,
5430Sstevel@tonic-gate 		    psp->ps_cfile->pf_name);
5440Sstevel@tonic-gate 		return (0);
5450Sstevel@tonic-gate 	}
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate 	if (strcmp(str, "*") == 0 || strcmp(str, "all") == 0) {
5480Sstevel@tonic-gate 		if (!(slp->sl_entry.se_flags & SEF_CDEV))
5490Sstevel@tonic-gate 			free_device_list(slp->sl_dev);
5500Sstevel@tonic-gate 		slp->sl_dev = psp->ps_star;
5510Sstevel@tonic-gate 		slp->sl_entry.se_flags |= SEF_CDEV;
5520Sstevel@tonic-gate 	} else {
5530Sstevel@tonic-gate 		dlpp = &dlp;
5540Sstevel@tonic-gate 		for (;;) {
5550Sstevel@tonic-gate 			while (isspace(*str) || *str == ',')
5560Sstevel@tonic-gate 				str++;
5570Sstevel@tonic-gate 			if (*str == '\0')
5580Sstevel@tonic-gate 				break;
5590Sstevel@tonic-gate 			cp = str;
5600Sstevel@tonic-gate 			while (*str != '\0' && !isspace(*str) && *str != ',')
5610Sstevel@tonic-gate 				str++;
5620Sstevel@tonic-gate 			len = str - cp;
5630Sstevel@tonic-gate 			if ((len == 1 && *cp == '*') ||
5640Sstevel@tonic-gate 			    (len == 3 && strncmp(cp, "all", 3) == 0)) {
5650Sstevel@tonic-gate 				logerr("%s: cannot use %.*s in device list",
5660Sstevel@tonic-gate 				    psp->ps_cfile->pf_name, len, cp);
5670Sstevel@tonic-gate 				continue;
5680Sstevel@tonic-gate 			}
5690Sstevel@tonic-gate 			dln = (struct device_list *)malloc(sizeof (*dln) +
5700Sstevel@tonic-gate 			    len + 1);
5710Sstevel@tonic-gate 			if (dln == NULL) {
5720Sstevel@tonic-gate 				logerr("no memory for device name");
5730Sstevel@tonic-gate 				break;
5740Sstevel@tonic-gate 			}
5750Sstevel@tonic-gate 			dln->dl_name = (const char *)(dln + 1);
5760Sstevel@tonic-gate 			/* Cannot use strcpy because cp isn't terminated. */
5770Sstevel@tonic-gate 			(void) memcpy(dln + 1, cp, len);
5780Sstevel@tonic-gate 			((char *)(dln + 1))[len] = '\0';
5790Sstevel@tonic-gate 			logdbg("%s: device %s", psp->ps_cfile->pf_name,
5800Sstevel@tonic-gate 			    dln->dl_name);
5810Sstevel@tonic-gate 			*dlpp = dln;
5820Sstevel@tonic-gate 			dlpp = &dln->dl_next;
5830Sstevel@tonic-gate 		}
5840Sstevel@tonic-gate 		*dlpp = NULL;
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate 		dlpp = &slp->sl_dev;
5870Sstevel@tonic-gate 		if (!(slp->sl_entry.se_flags & SEF_CDEV))
5880Sstevel@tonic-gate 			while (*dlpp != NULL)
5890Sstevel@tonic-gate 				dlpp = &(*dlpp)->dl_next;
5900Sstevel@tonic-gate 		*dlpp = dlp;
5910Sstevel@tonic-gate 		slp->sl_entry.se_flags &= ~SEF_CDEV;
5920Sstevel@tonic-gate 	}
5930Sstevel@tonic-gate 
5940Sstevel@tonic-gate 	return (0);
5950Sstevel@tonic-gate }
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate /*
5980Sstevel@tonic-gate  * Handle <list> portion of "client [except] <list>" option.  Attach
5990Sstevel@tonic-gate  * to list of filters in reverse order.
6000Sstevel@tonic-gate  */
6010Sstevel@tonic-gate static int
set_client(struct service_list * slp,const char * str)6020Sstevel@tonic-gate set_client(struct service_list *slp, const char *str)
6030Sstevel@tonic-gate {
6040Sstevel@tonic-gate 	struct parse_state *psp = slp->sl_parse;
6050Sstevel@tonic-gate 	struct filter_entry *fep;
6060Sstevel@tonic-gate 	struct filter_entry *fen;
6070Sstevel@tonic-gate 	const char *cp;
6080Sstevel@tonic-gate 	int len;
6090Sstevel@tonic-gate 	char hbuf[MAXHOSTNAMELEN];
6100Sstevel@tonic-gate 	struct ether_addr ea;
6110Sstevel@tonic-gate 	struct ether_addr mask;
6120Sstevel@tonic-gate 	uchar_t *ucp;
6130Sstevel@tonic-gate 	uchar_t *mcp;
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate 	/* Head of list. */
6160Sstevel@tonic-gate 	fep = slp->sl_entry.se_flist;
6170Sstevel@tonic-gate 	for (;;) {
6180Sstevel@tonic-gate 		while (isspace(*str) || *str == ',')
6190Sstevel@tonic-gate 			str++;
6200Sstevel@tonic-gate 		if (*str == '\0')
6210Sstevel@tonic-gate 			break;
6220Sstevel@tonic-gate 		cp = str;
6230Sstevel@tonic-gate 		while (*str != '\0' && !isspace(*str) && *str != ',')
6240Sstevel@tonic-gate 			str++;
6250Sstevel@tonic-gate 		len = str - cp;
6260Sstevel@tonic-gate 		(void) memcpy(hbuf, cp, len);
6270Sstevel@tonic-gate 		hbuf[len] = '\0';
6280Sstevel@tonic-gate 		mcp = mask.ether_addr_octet;
6290Sstevel@tonic-gate 		mcp[0] = mcp[1] = mcp[2] = mcp[3] = mcp[4] = mcp[5] = 0xFF;
6300Sstevel@tonic-gate 		if (ether_hostton(hbuf, &ea) != 0) {
6310Sstevel@tonic-gate 			ucp = ea.ether_addr_octet;
6320Sstevel@tonic-gate 			while (cp < str) {
6330Sstevel@tonic-gate 				if (ucp >= ea.ether_addr_octet + sizeof (ea))
6340Sstevel@tonic-gate 					break;
6350Sstevel@tonic-gate 				if (*cp == '*') {
6360Sstevel@tonic-gate 					*mcp++ = *ucp++ = 0;
6370Sstevel@tonic-gate 					cp++;
6380Sstevel@tonic-gate 				} else {
6390Sstevel@tonic-gate 					if (!isxdigit(*cp))
6400Sstevel@tonic-gate 						break;
6410Sstevel@tonic-gate 					*ucp = hexdecode(*cp++);
6420Sstevel@tonic-gate 					if (cp < str && isxdigit(*cp)) {
6430Sstevel@tonic-gate 						*ucp = (*ucp << 4) |
6440Sstevel@tonic-gate 						    hexdecode(*cp++);
6450Sstevel@tonic-gate 					}
6460Sstevel@tonic-gate 					ucp++;
6470Sstevel@tonic-gate 					*mcp++ = 0xFF;
6480Sstevel@tonic-gate 				}
6490Sstevel@tonic-gate 				if (cp < str) {
6500Sstevel@tonic-gate 					if (*cp != ':' || cp + 1 == str)
6510Sstevel@tonic-gate 						break;
6520Sstevel@tonic-gate 					cp++;
6530Sstevel@tonic-gate 				}
6540Sstevel@tonic-gate 			}
6550Sstevel@tonic-gate 			if (cp < str) {
6560Sstevel@tonic-gate 				logerr("%s: illegal Ethernet address %.*s",
6570Sstevel@tonic-gate 				    psp->ps_cfile->pf_name, len, cp);
6580Sstevel@tonic-gate 				continue;
6590Sstevel@tonic-gate 			}
6600Sstevel@tonic-gate 		}
6610Sstevel@tonic-gate 		fen = (struct filter_entry *)malloc(sizeof (*fen));
6620Sstevel@tonic-gate 		if (fen == NULL) {
6630Sstevel@tonic-gate 			logerr("unable to allocate memory for filter");
6640Sstevel@tonic-gate 			break;
6650Sstevel@tonic-gate 		}
6660Sstevel@tonic-gate 		fen->fe_isexcept = psp->ps_state == ksClientE;
6670Sstevel@tonic-gate 		fen->fe_prevcopy = 0;
6680Sstevel@tonic-gate 		(void) memcpy(&fen->fe_mac, &ea, sizeof (fen->fe_mac));
6690Sstevel@tonic-gate 		(void) memcpy(&fen->fe_mask, &mask, sizeof (fen->fe_mask));
6700Sstevel@tonic-gate 		fen->fe_prev = fep;
6710Sstevel@tonic-gate 		fep = fen;
6720Sstevel@tonic-gate 	}
6730Sstevel@tonic-gate 	slp->sl_entry.se_flist = fep;
6740Sstevel@tonic-gate 	return (0);
6750Sstevel@tonic-gate }
6760Sstevel@tonic-gate 
6770Sstevel@tonic-gate /*
6780Sstevel@tonic-gate  * Handle "user <name>" option.
6790Sstevel@tonic-gate  */
6800Sstevel@tonic-gate static int
set_user(struct service_list * slp,const char * str)6810Sstevel@tonic-gate set_user(struct service_list *slp, const char *str)
6820Sstevel@tonic-gate {
6830Sstevel@tonic-gate 	struct passwd *pw;
6840Sstevel@tonic-gate 	char *cp;
6850Sstevel@tonic-gate 	uid_t myuid, uid;
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate 	if ((pw = getpwnam(str)) == NULL) {
6880Sstevel@tonic-gate 		uid = (uid_t)strtol(str, &cp, 0);
6890Sstevel@tonic-gate 		if (str == cp || *cp != '\0') {
6900Sstevel@tonic-gate 			logerr("%s:  bad user name \"%s\"",
6910Sstevel@tonic-gate 			    slp->sl_parse->ps_cfile->pf_name, str);
6920Sstevel@tonic-gate 			return (0);
6930Sstevel@tonic-gate 		}
6940Sstevel@tonic-gate 	} else {
6950Sstevel@tonic-gate 		uid = pw->pw_uid;
6960Sstevel@tonic-gate 	}
6970Sstevel@tonic-gate 	slp->sl_entry.se_uid = uid;
6980Sstevel@tonic-gate 	myuid = getuid();
6990Sstevel@tonic-gate 	if (myuid != 0) {
7000Sstevel@tonic-gate 		if (myuid == uid)
7010Sstevel@tonic-gate 			return (0);
7020Sstevel@tonic-gate 		logdbg("%s:  not root; ignoring attempt to set UID %d (%s)",
7030Sstevel@tonic-gate 		    slp->sl_parse->ps_cfile->pf_name, uid, str);
7040Sstevel@tonic-gate 		return (0);
7050Sstevel@tonic-gate 	}
7060Sstevel@tonic-gate 	slp->sl_entry.se_flags |= SEF_UIDSET;
7070Sstevel@tonic-gate 	return (0);
7080Sstevel@tonic-gate }
7090Sstevel@tonic-gate 
7100Sstevel@tonic-gate /*
7110Sstevel@tonic-gate  * Handle "group <name>" option.
7120Sstevel@tonic-gate  */
7130Sstevel@tonic-gate static int
set_group(struct service_list * slp,const char * str)7140Sstevel@tonic-gate set_group(struct service_list *slp, const char *str)
7150Sstevel@tonic-gate {
7160Sstevel@tonic-gate 	struct group *gr;
7170Sstevel@tonic-gate 	char *cp;
7180Sstevel@tonic-gate 	gid_t gid;
7190Sstevel@tonic-gate 
7200Sstevel@tonic-gate 	if ((gr = getgrnam(str)) == NULL) {
7210Sstevel@tonic-gate 		gid = (gid_t)strtol(str, &cp, 0);
7220Sstevel@tonic-gate 		if (str == cp || *cp != '\0') {
7230Sstevel@tonic-gate 			logerr("%s:  bad group name \"%s\"",
7240Sstevel@tonic-gate 			    slp->sl_parse->ps_cfile->pf_name, str);
7250Sstevel@tonic-gate 			return (0);
7260Sstevel@tonic-gate 		}
7270Sstevel@tonic-gate 	} else {
7280Sstevel@tonic-gate 		gid = gr->gr_gid;
7290Sstevel@tonic-gate 	}
7300Sstevel@tonic-gate 	slp->sl_entry.se_gid = gid;
7310Sstevel@tonic-gate 	if (getuid() != 0) {
7320Sstevel@tonic-gate 		logdbg("%s:  not root; ignoring attempt to set GID %d (%s)",
7330Sstevel@tonic-gate 		    slp->sl_parse->ps_cfile->pf_name, gid, str);
7340Sstevel@tonic-gate 		return (0);
7350Sstevel@tonic-gate 	}
7360Sstevel@tonic-gate 	slp->sl_entry.se_flags |= SEF_GIDSET;
7370Sstevel@tonic-gate 	return (0);
7380Sstevel@tonic-gate }
7390Sstevel@tonic-gate 
7400Sstevel@tonic-gate /*
7410Sstevel@tonic-gate  * This state machine is used to parse the configuration files.  The
7420Sstevel@tonic-gate  * "kwe_in" is the state in which the keyword is recognized.  The
7430Sstevel@tonic-gate  * "kwe_out" is the state that the keyword produces.
7440Sstevel@tonic-gate  */
7450Sstevel@tonic-gate struct kw_entry {
7460Sstevel@tonic-gate 	const char *kwe_word;
7470Sstevel@tonic-gate 	enum key_state kwe_in;
7480Sstevel@tonic-gate 	enum key_state kwe_out;
7490Sstevel@tonic-gate 	int (*kwe_func)(struct service_list *slp, const char *str);
7500Sstevel@tonic-gate };
7510Sstevel@tonic-gate 
7520Sstevel@tonic-gate static const struct kw_entry key_list[] = {
7530Sstevel@tonic-gate 	{ "service",	ksDefault,	ksService,	NULL },
7540Sstevel@tonic-gate 	{ "device",	ksDefault,	ksDevice,	NULL },
7550Sstevel@tonic-gate 	{ "client",	ksDefault,	ksClient,	NULL },
7560Sstevel@tonic-gate 	{ "except",	ksClient,	ksClientE,	NULL },
7570Sstevel@tonic-gate 	{ "wildcard",	ksDefault,	ksDefault,	set_wildcard },
7580Sstevel@tonic-gate 	{ "nowildcard",	ksDefault,	ksDefault,	set_wildcard },
7590Sstevel@tonic-gate 	{ "server",	ksDefault,	ksServer,	NULL },
7600Sstevel@tonic-gate 	{ "pppd",	ksDefault,	ksPppd,		NULL },
7610Sstevel@tonic-gate 	{ "debug",	ksDefault,	ksDefault,	set_debug },
7620Sstevel@tonic-gate 	{ "nodebug",	ksDefault,	ksDefault,	set_nodebug },
7630Sstevel@tonic-gate 	{ "file",	ksDefault,	ksFile,		NULL },
7640Sstevel@tonic-gate 	{ "path",	ksDefault,	ksPath,		NULL },
7650Sstevel@tonic-gate 	{ "extra",	ksDefault,	ksExtra,	NULL },
7660Sstevel@tonic-gate 	{ "log",	ksDefault,	ksLog,		NULL },
7670Sstevel@tonic-gate 	{ "user",	ksDefault,	ksUser,		NULL },
7680Sstevel@tonic-gate 	{ "group",	ksDefault,	ksGroup,	NULL },
7690Sstevel@tonic-gate 	/* Wildcards only past this point. */
7700Sstevel@tonic-gate 	{ "",		ksService,	ksDefault,	set_service },
7710Sstevel@tonic-gate 	{ "",		ksDevice,	ksDefault,	set_device },
7720Sstevel@tonic-gate 	{ "",		ksClient,	ksDefault,	set_client },
7730Sstevel@tonic-gate 	{ "",		ksClientE,	ksDefault,	set_client },
7740Sstevel@tonic-gate 	{ "",		ksServer,	ksDefault,	set_string },
7750Sstevel@tonic-gate 	{ "",		ksPppd,		ksDefault,	set_string },
7760Sstevel@tonic-gate 	{ "",		ksFile,		ksDefault,	set_file },
7770Sstevel@tonic-gate 	{ "",		ksPath,		ksDefault,	set_string },
7780Sstevel@tonic-gate 	{ "",		ksExtra,	ksDefault,	set_string },
7790Sstevel@tonic-gate 	{ "",		ksLog,		ksDefault,	set_string },
7800Sstevel@tonic-gate 	{ "",		ksUser,		ksDefault,	set_user },
7810Sstevel@tonic-gate 	{ "",		ksGroup,	ksDefault,	set_group },
7820Sstevel@tonic-gate 	{ NULL, ksDefault, ksDefault, NULL }
7830Sstevel@tonic-gate };
7840Sstevel@tonic-gate 
7850Sstevel@tonic-gate /*
7860Sstevel@tonic-gate  * Produce a string for the keyword that would have gotten us into the
7870Sstevel@tonic-gate  * current state.
7880Sstevel@tonic-gate  */
7890Sstevel@tonic-gate static const char *
after_key(enum key_state kstate)7900Sstevel@tonic-gate after_key(enum key_state kstate)
7910Sstevel@tonic-gate {
7920Sstevel@tonic-gate 	const struct kw_entry *kep;
7930Sstevel@tonic-gate 
7940Sstevel@tonic-gate 	for (kep = key_list; kep->kwe_word != NULL; kep++)
7950Sstevel@tonic-gate 		if (kep->kwe_out == kstate)
7960Sstevel@tonic-gate 			return (kep->kwe_word);
7970Sstevel@tonic-gate 	return ("nothing");
7980Sstevel@tonic-gate }
7990Sstevel@tonic-gate 
8000Sstevel@tonic-gate /*
8010Sstevel@tonic-gate  * Handle end-of-file processing -- close service, close file, revert
8020Sstevel@tonic-gate  * to global context in previous include file nest level.
8030Sstevel@tonic-gate  */
8040Sstevel@tonic-gate static void
file_end(struct parse_state * psp)8050Sstevel@tonic-gate file_end(struct parse_state *psp)
8060Sstevel@tonic-gate {
8070Sstevel@tonic-gate 	struct per_file *pfp;
8080Sstevel@tonic-gate 
8090Sstevel@tonic-gate 	/* Must not be in the middle of parsing a multi-word sequence now. */
8100Sstevel@tonic-gate 	if (psp->ps_state != ksDefault) {
8110Sstevel@tonic-gate 		logerr("%s ends with \"%s\"", psp->ps_cfile->pf_name,
8120Sstevel@tonic-gate 		    after_key(psp->ps_state));
8130Sstevel@tonic-gate 		psp->ps_state = ksDefault;
8140Sstevel@tonic-gate 	}
8150Sstevel@tonic-gate 	close_service(psp->ps_csvc);
8160Sstevel@tonic-gate 	if ((pfp = psp->ps_cfile) != NULL) {
8170Sstevel@tonic-gate 		/* Put this file on the list of finished files. */
8180Sstevel@tonic-gate 		psp->ps_cfile = pfp->pf_prev;
8190Sstevel@tonic-gate 		pfp->pf_prev = psp->ps_files;
8200Sstevel@tonic-gate 		psp->ps_files = pfp;
8210Sstevel@tonic-gate 		if (pfp->pf_input != NULL) {
8220Sstevel@tonic-gate 			logdbg("file %s closed", pfp->pf_name);
8230Sstevel@tonic-gate 			(void) fclose(pfp->pf_input);
8240Sstevel@tonic-gate 			pfp->pf_input = NULL;
8250Sstevel@tonic-gate 		}
8260Sstevel@tonic-gate 
8270Sstevel@tonic-gate 		/* Back up to previous file, if any, and set global context. */
8280Sstevel@tonic-gate 		if ((pfp = psp->ps_cfile) != NULL)
8290Sstevel@tonic-gate 			psp->ps_csvc = &pfp->pf_global;
8300Sstevel@tonic-gate 	}
8310Sstevel@tonic-gate }
8320Sstevel@tonic-gate 
8330Sstevel@tonic-gate /*
8340Sstevel@tonic-gate  * Dispatch a single keyword against the parser state machine or
8350Sstevel@tonic-gate  * handle an environment variable assignment.  The input is a string
8360Sstevel@tonic-gate  * containing the single word to be dispatched.
8370Sstevel@tonic-gate  */
8380Sstevel@tonic-gate static int
dispatch_keyword(struct parse_state * psp,const char * keybuf)8390Sstevel@tonic-gate dispatch_keyword(struct parse_state *psp, const char *keybuf)
8400Sstevel@tonic-gate {
8410Sstevel@tonic-gate 	const struct kw_entry *kep;
8420Sstevel@tonic-gate 	int retv;
8430Sstevel@tonic-gate 	char *cp;
8440Sstevel@tonic-gate 	char *env;
8450Sstevel@tonic-gate 	char **evlist;
8460Sstevel@tonic-gate 	int len;
8470Sstevel@tonic-gate 
8480Sstevel@tonic-gate 	retv = 0;
8490Sstevel@tonic-gate 	for (kep = key_list; kep->kwe_word != NULL; kep++) {
8500Sstevel@tonic-gate 		if (kep->kwe_in == psp->ps_state &&
8510Sstevel@tonic-gate 		    (*kep->kwe_word == '\0' ||
852*9751Sjames.d.carlson@sun.com 		    strcasecmp(kep->kwe_word, keybuf) == 0)) {
8530Sstevel@tonic-gate 			if (kep->kwe_func != NULL)
8540Sstevel@tonic-gate 				retv = (*kep->kwe_func)(psp->ps_csvc, keybuf);
8550Sstevel@tonic-gate 			psp->ps_state = kep->kwe_out;
8560Sstevel@tonic-gate 			return (retv);
8570Sstevel@tonic-gate 		}
8580Sstevel@tonic-gate 	}
8590Sstevel@tonic-gate 	if (strchr(keybuf, '=') != NULL) {
8600Sstevel@tonic-gate 		if ((cp = strsave(keybuf)) == NULL) {
8610Sstevel@tonic-gate 			logerr("no memory to save %s", keybuf);
8620Sstevel@tonic-gate 			return (0);
8630Sstevel@tonic-gate 		}
8640Sstevel@tonic-gate 		len = (strchr(cp, '=') - cp) + 1;
8650Sstevel@tonic-gate 		if ((evlist = psp->ps_evlist) == NULL) {
8660Sstevel@tonic-gate 			psp->ps_evlist = evlist =
8670Sstevel@tonic-gate 			    (char **)malloc(8 * sizeof (*evlist));
8680Sstevel@tonic-gate 			if (evlist == NULL) {
8690Sstevel@tonic-gate 				logerr("no memory for evlist");
8700Sstevel@tonic-gate 				free(cp);
8710Sstevel@tonic-gate 				return (0);
8720Sstevel@tonic-gate 			}
8730Sstevel@tonic-gate 			psp->ps_evsize = 8;
8740Sstevel@tonic-gate 			evlist[0] = evlist[1] = NULL;
8750Sstevel@tonic-gate 		} else {
8760Sstevel@tonic-gate 			while ((env = *evlist) != NULL) {
8770Sstevel@tonic-gate 				if (strncmp(cp, env, len) == 0)
8780Sstevel@tonic-gate 					break;
8790Sstevel@tonic-gate 				evlist++;
8800Sstevel@tonic-gate 			}
8810Sstevel@tonic-gate 			if (env == NULL &&
8820Sstevel@tonic-gate 			    evlist-psp->ps_evlist >= psp->ps_evsize-1) {
8830Sstevel@tonic-gate 				evlist = (char **)realloc(psp->ps_evlist,
8840Sstevel@tonic-gate 				    (psp->ps_evsize + 8) * sizeof (*evlist));
8850Sstevel@tonic-gate 				if (evlist == NULL) {
8860Sstevel@tonic-gate 					logerr("cannot realloc evlist to %d",
8870Sstevel@tonic-gate 					    psp->ps_evsize + 8);
8880Sstevel@tonic-gate 					free(cp);
8890Sstevel@tonic-gate 					return (0);
8900Sstevel@tonic-gate 				}
8910Sstevel@tonic-gate 				psp->ps_evlist = evlist;
8920Sstevel@tonic-gate 				evlist += psp->ps_evsize - 1;
8930Sstevel@tonic-gate 				psp->ps_evsize += 8;
8940Sstevel@tonic-gate 				evlist[1] = NULL;
8950Sstevel@tonic-gate 			}
8960Sstevel@tonic-gate 		}
8970Sstevel@tonic-gate 		logdbg("setenv \"%s\"", cp);
8980Sstevel@tonic-gate 		if (*evlist != NULL)
8990Sstevel@tonic-gate 			free(*evlist);
9000Sstevel@tonic-gate 		*evlist = cp;
9010Sstevel@tonic-gate 		return (0);
9020Sstevel@tonic-gate 	}
9030Sstevel@tonic-gate 	logerr("%s: unknown keyword '%s'", psp->ps_cfile->pf_name, keybuf);
9040Sstevel@tonic-gate 	return (-1);
9050Sstevel@tonic-gate }
9060Sstevel@tonic-gate 
9070Sstevel@tonic-gate /*
9080Sstevel@tonic-gate  * Modified version of standard getenv; looks in locally-stored
9090Sstevel@tonic-gate  * environment first.  This function exists because we need to be able
9100Sstevel@tonic-gate  * to revert to the original environment during a reread (SIGHUP), and
9110Sstevel@tonic-gate  * the putenv() function overwrites that environment.
9120Sstevel@tonic-gate  */
9130Sstevel@tonic-gate static char *
my_getenv(struct parse_state * psp,char * estr)9140Sstevel@tonic-gate my_getenv(struct parse_state *psp, char *estr)
9150Sstevel@tonic-gate {
9160Sstevel@tonic-gate 	char **evlist, *ent;
9170Sstevel@tonic-gate 	int elen;
9180Sstevel@tonic-gate 
9190Sstevel@tonic-gate 	if (psp != NULL && (evlist = psp->ps_evlist) != NULL) {
9200Sstevel@tonic-gate 		elen = strlen(estr);
9210Sstevel@tonic-gate 		while ((ent = *evlist++) != NULL) {
9220Sstevel@tonic-gate 			if (strncmp(ent, estr, elen) == 0 &&
9230Sstevel@tonic-gate 			    ent[elen] == '=')
9240Sstevel@tonic-gate 				return (ent + elen + 1);
9250Sstevel@tonic-gate 		}
9260Sstevel@tonic-gate 	}
9270Sstevel@tonic-gate 	return (getenv(estr));
9280Sstevel@tonic-gate }
9290Sstevel@tonic-gate 
9300Sstevel@tonic-gate /*
9310Sstevel@tonic-gate  * Expand an environment variable at the end of current buffer and
9320Sstevel@tonic-gate  * return pointer to next spot in buffer for character append.  psp
9330Sstevel@tonic-gate  * context may be null.
9340Sstevel@tonic-gate  */
9350Sstevel@tonic-gate static char *
env_replace(struct parse_state * psp,char * keybuf,char kwstate)9360Sstevel@tonic-gate env_replace(struct parse_state *psp, char *keybuf, char kwstate)
9370Sstevel@tonic-gate {
9380Sstevel@tonic-gate 	char *cpe;
9390Sstevel@tonic-gate 	char *cp;
9400Sstevel@tonic-gate 
9410Sstevel@tonic-gate 	if ((cp = strrchr(keybuf, kwstate)) != NULL) {
9420Sstevel@tonic-gate 		if ((cpe = my_getenv(psp, cp + 1)) != NULL) {
9430Sstevel@tonic-gate 			*cp = '\0';
9440Sstevel@tonic-gate 			(void) strncat(cp, cpe,
9450Sstevel@tonic-gate 			    MAX_KEYWORD - (cp - keybuf) - 1);
9460Sstevel@tonic-gate 			keybuf[MAX_KEYWORD - 1] = '\0';
9470Sstevel@tonic-gate 			cp += strlen(cp);
9480Sstevel@tonic-gate 		} else {
9490Sstevel@tonic-gate 			logerr("unknown variable \"%s\"", cp + 1);
9500Sstevel@tonic-gate 		}
9510Sstevel@tonic-gate 	} else {
9520Sstevel@tonic-gate 		/* Should not occur. */
9530Sstevel@tonic-gate 		cp = keybuf + strlen(keybuf);
9540Sstevel@tonic-gate 	}
9550Sstevel@tonic-gate 	return (cp);
9560Sstevel@tonic-gate }
9570Sstevel@tonic-gate 
9580Sstevel@tonic-gate /*
9590Sstevel@tonic-gate  * Given a character-at-a-time input function, get a delimited keyword
9600Sstevel@tonic-gate  * from the input.  This function handles the usual escape sequences,
9610Sstevel@tonic-gate  * quoting, commenting, and environment variable expansion.
9620Sstevel@tonic-gate  *
9630Sstevel@tonic-gate  * The standard wordexp(3C) function isn't used here because the POSIX
9640Sstevel@tonic-gate  * definition is hard to use, and the Solaris implementation is
9650Sstevel@tonic-gate  * resource-intensive and insecure.  The "hard-to-use" part is that
9660Sstevel@tonic-gate  * wordexp expands only variables from the environment, and can't
9670Sstevel@tonic-gate  * handle an environment overlay.  Instead, the caller must use the
9680Sstevel@tonic-gate  * feeble putenv/getenv interface, and rewinding to the initial
9690Sstevel@tonic-gate  * environment without leaking storage is hard.  The Solaris
9700Sstevel@tonic-gate  * implementation invokes an undocumented extensions via
9710Sstevel@tonic-gate  * fork/exec("/bin/ksh -\005 %s") for every invocation, and gathers
9720Sstevel@tonic-gate  * the expanded result with pipe.  This makes it slow to execute and
9730Sstevel@tonic-gate  * exposes the string being expanded to users with access to "ps -f."
9740Sstevel@tonic-gate  *
9750Sstevel@tonic-gate  * psp may be null; it's used only for environment variable expansion.
9760Sstevel@tonic-gate  * Input "flag" is 1 to ignore EOL, '#', and '$'; 0 for normal file parsing.
9770Sstevel@tonic-gate  *
9780Sstevel@tonic-gate  * Returns:
9790Sstevel@tonic-gate  *	0 - keyword parsed.
9800Sstevel@tonic-gate  *	1 - end of file; no keyword.
9810Sstevel@tonic-gate  *	2 - end of file after this keyword.
9820Sstevel@tonic-gate  */
9830Sstevel@tonic-gate static int
getkeyword(struct parse_state * psp,char * keybuf,int keymax,int (* nextchr)(void *),void * arg,int flag)9840Sstevel@tonic-gate getkeyword(struct parse_state *psp, char *keybuf, int keymax,
9850Sstevel@tonic-gate     int (*nextchr)(void *), void *arg, int flag)
9860Sstevel@tonic-gate {
9870Sstevel@tonic-gate 	char varnest[MAX_NEST];
9880Sstevel@tonic-gate 	char *kbp;
9890Sstevel@tonic-gate 	char *vnp;
9900Sstevel@tonic-gate 	char chr;
9910Sstevel@tonic-gate 	int ichr;
9920Sstevel@tonic-gate 	char kwstate;
9930Sstevel@tonic-gate 	static const char escstr[] = "a\ab\bf\fn\nr\r";
9940Sstevel@tonic-gate 	const char *cp;
9950Sstevel@tonic-gate 
9960Sstevel@tonic-gate 	keymax--;	/* Account for trailing NUL byte */
9970Sstevel@tonic-gate 
9980Sstevel@tonic-gate 	kwstate = '\0';
9990Sstevel@tonic-gate 	kbp = keybuf;
10000Sstevel@tonic-gate 	vnp = varnest;
10010Sstevel@tonic-gate 	for (;;) {
10020Sstevel@tonic-gate 		ichr = (*nextchr)(arg);
10030Sstevel@tonic-gate 		chr = (char)ichr;
10040Sstevel@tonic-gate 	tryagain:
10050Sstevel@tonic-gate 		switch (kwstate) {
10060Sstevel@tonic-gate 		case '\\':	/* Start of unquoted escape sequence */
10070Sstevel@tonic-gate 		case '|':	/* Start of escape sequence in double quotes */
10080Sstevel@tonic-gate 		case '~':	/* Start of escape sequence in single quotes */
10090Sstevel@tonic-gate 			/* Convert the character if we can. */
10100Sstevel@tonic-gate 			if (chr == '\n')
10110Sstevel@tonic-gate 				chr = '\0';
10120Sstevel@tonic-gate 			else if (isalpha(chr) &&
10130Sstevel@tonic-gate 			    (cp = strchr(escstr, chr)) != NULL)
10140Sstevel@tonic-gate 				chr = cp[1];
10150Sstevel@tonic-gate 			/* Revert to previous state */
10160Sstevel@tonic-gate 			switch (kwstate) {
10170Sstevel@tonic-gate 			case '\\':
10180Sstevel@tonic-gate 				kwstate = 'A';
10190Sstevel@tonic-gate 				break;
10200Sstevel@tonic-gate 			case '|':
10210Sstevel@tonic-gate 				kwstate = '"';
10220Sstevel@tonic-gate 				break;
10230Sstevel@tonic-gate 			case '~':
10240Sstevel@tonic-gate 				kwstate = '\'';
10250Sstevel@tonic-gate 				break;
10260Sstevel@tonic-gate 			}
10270Sstevel@tonic-gate 			break;
10280Sstevel@tonic-gate 		case '"':	/* In double-quote string */
10290Sstevel@tonic-gate 			if (!flag && chr == '$') {
10300Sstevel@tonic-gate 				/* Handle variable expansion. */
10310Sstevel@tonic-gate 				kwstate = '%';
10320Sstevel@tonic-gate 				chr = '\0';
10330Sstevel@tonic-gate 				break;
10340Sstevel@tonic-gate 			}
10350Sstevel@tonic-gate 				/* FALLTHROUGH */
10360Sstevel@tonic-gate 		case '\'':	/* In single-quote string */
10370Sstevel@tonic-gate 			if (chr == '\\') {
10380Sstevel@tonic-gate 				/* Handle start of escape sequence */
10390Sstevel@tonic-gate 				kwstate = kwstate == '"' ? '|' : '~';
10400Sstevel@tonic-gate 				chr = '\0';
10410Sstevel@tonic-gate 				break;
10420Sstevel@tonic-gate 			}
10430Sstevel@tonic-gate 			if (chr == kwstate) {
10440Sstevel@tonic-gate 				/* End of quoted string; revert to normal */
10450Sstevel@tonic-gate 				kwstate = 'A';
10460Sstevel@tonic-gate 				chr = '\0';
10470Sstevel@tonic-gate 			}
10480Sstevel@tonic-gate 			break;
10490Sstevel@tonic-gate 		case '$':	/* Start of unquoted variable name */
10500Sstevel@tonic-gate 		case '%':	/* Start of variable name in quoted string */
10510Sstevel@tonic-gate 			if (chr == '{') {
10520Sstevel@tonic-gate 				/* Variable name is bracketed. */
10530Sstevel@tonic-gate 				kwstate = chr =
10540Sstevel@tonic-gate 				    kwstate == '$' ? '{' : '[';
10550Sstevel@tonic-gate 				break;
10560Sstevel@tonic-gate 			}
10570Sstevel@tonic-gate 			*kbp++ = kwstate = kwstate == '$' ? '+' : '*';
10580Sstevel@tonic-gate 				/* FALLTHROUGH */
10590Sstevel@tonic-gate 		case '+':	/* Gathering unquoted variable name */
10600Sstevel@tonic-gate 		case '*':	/* Gathering variable name in quoted string */
10610Sstevel@tonic-gate 			if (chr == '$' &&
10620Sstevel@tonic-gate 			    vnp < varnest + sizeof (varnest)) {
10630Sstevel@tonic-gate 				*vnp++ = kwstate;
10640Sstevel@tonic-gate 				kwstate = '$';
10650Sstevel@tonic-gate 				chr = '\0';
10660Sstevel@tonic-gate 				break;
10670Sstevel@tonic-gate 			}
10680Sstevel@tonic-gate 			if (!isalnum(chr) && chr != '_' &&
10690Sstevel@tonic-gate 			    chr != '.' && chr != '-') {
10700Sstevel@tonic-gate 				*kbp = '\0';
10710Sstevel@tonic-gate 				kbp = env_replace(psp, keybuf, kwstate);
10720Sstevel@tonic-gate 				if (vnp > varnest)
10730Sstevel@tonic-gate 					kwstate = *--vnp;
10740Sstevel@tonic-gate 				else
10750Sstevel@tonic-gate 					kwstate = kwstate == '+' ?
10760Sstevel@tonic-gate 					    'A' : '"';
10770Sstevel@tonic-gate 				/* Go reinterpret in new context */
10780Sstevel@tonic-gate 				goto tryagain;
10790Sstevel@tonic-gate 			}
10800Sstevel@tonic-gate 			break;
10810Sstevel@tonic-gate 		case '{':	/* Gathering bracketed, unquoted var name */
10820Sstevel@tonic-gate 		case '[':	/* Gathering bracketed, quoted var name */
10830Sstevel@tonic-gate 			if (chr == '}') {
10840Sstevel@tonic-gate 				*kbp = '\0';
10850Sstevel@tonic-gate 				kbp = env_replace(psp, keybuf, kwstate);
10860Sstevel@tonic-gate 				kwstate = kwstate == '{' ? 'A' : '"';
10870Sstevel@tonic-gate 				chr = '\0';
10880Sstevel@tonic-gate 			}
10890Sstevel@tonic-gate 			break;
10900Sstevel@tonic-gate 		case '#':	/* Comment before word state */
10910Sstevel@tonic-gate 		case '@':	/* Comment after word state */
10920Sstevel@tonic-gate 			if (chr == '\n' || chr == '\r' || ichr == EOF) {
10930Sstevel@tonic-gate 				/* At end of line, revert to previous state */
10940Sstevel@tonic-gate 				kwstate = kwstate == '#' ? '\0' : ' ';
10950Sstevel@tonic-gate 				chr = '\0';
10960Sstevel@tonic-gate 				break;
10970Sstevel@tonic-gate 			}
10980Sstevel@tonic-gate 			chr = '\0';
10990Sstevel@tonic-gate 			break;
11000Sstevel@tonic-gate 		case '\0':	/* Initial state; no word seen yet. */
11010Sstevel@tonic-gate 			if (ichr == EOF || isspace(chr)) {
11020Sstevel@tonic-gate 				chr = '\0';	/* Skip over leading spaces */
11030Sstevel@tonic-gate 				break;
11040Sstevel@tonic-gate 			}
11050Sstevel@tonic-gate 			if (chr == '#') {
11060Sstevel@tonic-gate 				kwstate = '#';
11070Sstevel@tonic-gate 				chr = '\0';	/* Skip over comments */
11080Sstevel@tonic-gate 				break;
11090Sstevel@tonic-gate 			}
11100Sstevel@tonic-gate 			/* Start of keyword seen. */
11110Sstevel@tonic-gate 			kwstate = 'A';
11120Sstevel@tonic-gate 			/* FALLTHROUGH */
11130Sstevel@tonic-gate 		default:	/* Middle of keyword parsing. */
11140Sstevel@tonic-gate 			if (ichr == EOF)
11150Sstevel@tonic-gate 				break;
11160Sstevel@tonic-gate 			if (isspace(chr)) {	/* Space terminates word */
11170Sstevel@tonic-gate 				kwstate = ' ';
11180Sstevel@tonic-gate 				break;
11190Sstevel@tonic-gate 			}
11200Sstevel@tonic-gate 			if (chr == '"' || chr == '\'' || chr == '\\') {
11210Sstevel@tonic-gate 				kwstate = chr;	/* Begin quote or escape */
11220Sstevel@tonic-gate 				chr = '\0';
11230Sstevel@tonic-gate 				break;
11240Sstevel@tonic-gate 			}
11250Sstevel@tonic-gate 			if (flag)	/* Allow ignore; for string reparse */
11260Sstevel@tonic-gate 				break;
11270Sstevel@tonic-gate 			if (chr == '#') {	/* Comment terminates word */
11280Sstevel@tonic-gate 				kwstate = '@';	/* Must consume comment also */
11290Sstevel@tonic-gate 				chr = '\0';
11300Sstevel@tonic-gate 				break;
11310Sstevel@tonic-gate 			}
11320Sstevel@tonic-gate 			if (chr == '$') {
11330Sstevel@tonic-gate 				kwstate = '$';	/* Begin variable expansion */
11340Sstevel@tonic-gate 				chr = '\0';
11350Sstevel@tonic-gate 			}
11360Sstevel@tonic-gate 			break;
11370Sstevel@tonic-gate 		}
11380Sstevel@tonic-gate 		/*
11390Sstevel@tonic-gate 		 * If we've reached a space at the end of the word,
11400Sstevel@tonic-gate 		 * then we're done.
11410Sstevel@tonic-gate 		 */
11420Sstevel@tonic-gate 		if (ichr == EOF || kwstate == ' ')
11430Sstevel@tonic-gate 			break;
11440Sstevel@tonic-gate 		/*
11450Sstevel@tonic-gate 		 * If there's a character to store and space
11460Sstevel@tonic-gate 		 * available, then add it to the string
11470Sstevel@tonic-gate 		 */
11480Sstevel@tonic-gate 		if (chr != '\0' && kbp < keybuf + keymax)
11490Sstevel@tonic-gate 			*kbp++ = (char)chr;
11500Sstevel@tonic-gate 	}
11510Sstevel@tonic-gate 
11520Sstevel@tonic-gate 	*kbp = '\0';
11530Sstevel@tonic-gate 
11540Sstevel@tonic-gate 	if (ichr == EOF) {
11550Sstevel@tonic-gate 		return (kwstate == '\0' ? 1 : 2);
11560Sstevel@tonic-gate 	}
11570Sstevel@tonic-gate 	return (0);
11580Sstevel@tonic-gate }
11590Sstevel@tonic-gate 
11600Sstevel@tonic-gate /*
11610Sstevel@tonic-gate  * Fetch words from current file until all files are closed.  Handles
11620Sstevel@tonic-gate  * include files.
11630Sstevel@tonic-gate  */
11640Sstevel@tonic-gate static void
parse_from_file(struct parse_state * psp)11650Sstevel@tonic-gate parse_from_file(struct parse_state *psp)
11660Sstevel@tonic-gate {
11670Sstevel@tonic-gate 	char keybuf[MAX_KEYWORD];
11680Sstevel@tonic-gate 	int retv;
11690Sstevel@tonic-gate 
11700Sstevel@tonic-gate 	while (psp->ps_cfile != NULL && psp->ps_cfile->pf_input != NULL) {
11710Sstevel@tonic-gate 		retv = getkeyword(psp, keybuf, sizeof (keybuf),
11720Sstevel@tonic-gate 		    (int (*)(void *))fgetc, (void *)psp->ps_cfile->pf_input,
11730Sstevel@tonic-gate 		    0);
11740Sstevel@tonic-gate 
11750Sstevel@tonic-gate 		if (retv != 1)
11760Sstevel@tonic-gate 			(void) dispatch_keyword(psp, keybuf);
11770Sstevel@tonic-gate 
11780Sstevel@tonic-gate 		if (retv != 0)
11790Sstevel@tonic-gate 			file_end(psp);
11800Sstevel@tonic-gate 	}
11810Sstevel@tonic-gate }
11820Sstevel@tonic-gate 
11830Sstevel@tonic-gate /*
11840Sstevel@tonic-gate  * Open and parse named file.  This is for the predefined
11850Sstevel@tonic-gate  * configuration files in /etc/ppp -- it's not an error if any of
11860Sstevel@tonic-gate  * these are missing.
11870Sstevel@tonic-gate  */
11880Sstevel@tonic-gate static void
parse_file(struct parse_state * psp,const char * fname)11890Sstevel@tonic-gate parse_file(struct parse_state *psp, const char *fname)
11900Sstevel@tonic-gate {
11910Sstevel@tonic-gate 	struct stat sb;
11920Sstevel@tonic-gate 
11930Sstevel@tonic-gate 	/* It's ok if any of these files are missing. */
11940Sstevel@tonic-gate 	if (stat(fname, &sb) == -1 && errno == ENOENT)
11950Sstevel@tonic-gate 		return;
11960Sstevel@tonic-gate 	if (set_file(psp->ps_csvc, fname) == 0)
11970Sstevel@tonic-gate 		parse_from_file(psp);
11980Sstevel@tonic-gate }
11990Sstevel@tonic-gate 
12000Sstevel@tonic-gate /*
12010Sstevel@tonic-gate  * Dispatch keywords from command line.  Handles any files included
12020Sstevel@tonic-gate  * from there.
12030Sstevel@tonic-gate  */
12040Sstevel@tonic-gate static void
parse_arg_list(struct parse_state * psp,int argc,char ** argv)12050Sstevel@tonic-gate parse_arg_list(struct parse_state *psp, int argc, char **argv)
12060Sstevel@tonic-gate {
12070Sstevel@tonic-gate 	/* The first argument (program name) can be null. */
12080Sstevel@tonic-gate 	if (--argc <= 0)
12090Sstevel@tonic-gate 		return;
12100Sstevel@tonic-gate 	while (--argc >= 0) {
12110Sstevel@tonic-gate 		(void) dispatch_keyword(psp, *++argv);
12120Sstevel@tonic-gate 		if (psp->ps_cfile->pf_input != NULL)
12130Sstevel@tonic-gate 			parse_from_file(psp);
12140Sstevel@tonic-gate 	}
12150Sstevel@tonic-gate }
12160Sstevel@tonic-gate 
12170Sstevel@tonic-gate /* Count length of dynamic device list */
12180Sstevel@tonic-gate static int
count_devs(struct device_list * dlp)12190Sstevel@tonic-gate count_devs(struct device_list *dlp)
12200Sstevel@tonic-gate {
12210Sstevel@tonic-gate 	int ndevs;
12220Sstevel@tonic-gate 
12230Sstevel@tonic-gate 	ndevs = 0;
12240Sstevel@tonic-gate 	for (; dlp != NULL; dlp = dlp->dl_next)
12250Sstevel@tonic-gate 		ndevs++;
12260Sstevel@tonic-gate 	return (ndevs);
12270Sstevel@tonic-gate }
12280Sstevel@tonic-gate 
12290Sstevel@tonic-gate /* Count number of devices named in entire file. */
12300Sstevel@tonic-gate static int
count_per_file(struct per_file * pfp)12310Sstevel@tonic-gate count_per_file(struct per_file *pfp)
12320Sstevel@tonic-gate {
12330Sstevel@tonic-gate 	struct service_list *slp;
12340Sstevel@tonic-gate 	int ndevs = 0;
12350Sstevel@tonic-gate 
12360Sstevel@tonic-gate 	for (; pfp != NULL; pfp = pfp->pf_prev) {
12370Sstevel@tonic-gate 		ndevs += count_devs(pfp->pf_global.sl_dev);
12380Sstevel@tonic-gate 		for (slp = pfp->pf_svc; slp != NULL; slp = slp->sl_next)
12390Sstevel@tonic-gate 			if (!(slp->sl_entry.se_flags & SEF_CDEV))
12400Sstevel@tonic-gate 				ndevs += count_devs(slp->sl_dev);
12410Sstevel@tonic-gate 	}
12420Sstevel@tonic-gate 	return (ndevs);
12430Sstevel@tonic-gate }
12440Sstevel@tonic-gate 
12450Sstevel@tonic-gate /* Write device names into linear array. */
12460Sstevel@tonic-gate static const char **
devs_to_list(struct device_list * dlp,const char ** dnames)12470Sstevel@tonic-gate devs_to_list(struct device_list *dlp, const char **dnames)
12480Sstevel@tonic-gate {
12490Sstevel@tonic-gate 	for (; dlp != NULL; dlp = dlp->dl_next)
12500Sstevel@tonic-gate 		*dnames++ = dlp->dl_name;
12510Sstevel@tonic-gate 	return (dnames);
12520Sstevel@tonic-gate }
12530Sstevel@tonic-gate 
12540Sstevel@tonic-gate /* Write all device names from file into a linear array. */
12550Sstevel@tonic-gate static const char **
per_file_to_list(struct per_file * pfp,const char ** dnames)12560Sstevel@tonic-gate per_file_to_list(struct per_file *pfp, const char **dnames)
12570Sstevel@tonic-gate {
12580Sstevel@tonic-gate 	struct service_list *slp;
12590Sstevel@tonic-gate 
12600Sstevel@tonic-gate 	for (; pfp != NULL; pfp = pfp->pf_prev) {
12610Sstevel@tonic-gate 		dnames = devs_to_list(pfp->pf_global.sl_dev, dnames);
12620Sstevel@tonic-gate 		for (slp = pfp->pf_svc; slp != NULL; slp = slp->sl_next)
12630Sstevel@tonic-gate 			if (!(slp->sl_entry.se_flags & SEF_CDEV))
12640Sstevel@tonic-gate 				dnames = devs_to_list(slp->sl_dev, dnames);
12650Sstevel@tonic-gate 	}
12660Sstevel@tonic-gate 	return (dnames);
12670Sstevel@tonic-gate }
12680Sstevel@tonic-gate 
12690Sstevel@tonic-gate /* Compare device names; used with qsort */
12700Sstevel@tonic-gate static int
devcmp(const void * d1,const void * d2)12710Sstevel@tonic-gate devcmp(const void *d1, const void *d2)
12720Sstevel@tonic-gate {
12730Sstevel@tonic-gate 	return (strcmp(*(const char **)d1, *(const char **)d2));
12740Sstevel@tonic-gate }
12750Sstevel@tonic-gate 
12760Sstevel@tonic-gate /*
12770Sstevel@tonic-gate  * Get sorted list of unique device names among all defined and
12780Sstevel@tonic-gate  * partially defined services in all files.
12790Sstevel@tonic-gate  */
12800Sstevel@tonic-gate static const char **
get_unique_devs(struct parse_state * psp)12810Sstevel@tonic-gate get_unique_devs(struct parse_state *psp)
12820Sstevel@tonic-gate {
12830Sstevel@tonic-gate 	int ndevs;
12840Sstevel@tonic-gate 	const char **dnames;
12850Sstevel@tonic-gate 	const char **dnp;
12860Sstevel@tonic-gate 	const char **dnf;
12870Sstevel@tonic-gate 
12880Sstevel@tonic-gate 	/*
12890Sstevel@tonic-gate 	 * Count number of explicitly referenced devices among all
12900Sstevel@tonic-gate 	 * services (including duplicates).
12910Sstevel@tonic-gate 	 */
12920Sstevel@tonic-gate 	ndevs = count_per_file(psp->ps_files);
12930Sstevel@tonic-gate 	ndevs += count_per_file(psp->ps_cfile);
12940Sstevel@tonic-gate 	if (ndevs <= 0) {
12950Sstevel@tonic-gate 		return (NULL);
12960Sstevel@tonic-gate 	}
12970Sstevel@tonic-gate 
12980Sstevel@tonic-gate 	/* Sort and trim out duplicate devices. */
12990Sstevel@tonic-gate 	dnames = (const char **)malloc((ndevs+1) * sizeof (const char *));
13000Sstevel@tonic-gate 	if (dnames == NULL) {
13010Sstevel@tonic-gate 		logerr("unable to allocate space for %d devices", ndevs + 1);
13020Sstevel@tonic-gate 		return (NULL);
13030Sstevel@tonic-gate 	}
13040Sstevel@tonic-gate 	dnp = per_file_to_list(psp->ps_files, dnames);
13050Sstevel@tonic-gate 	(void) per_file_to_list(psp->ps_cfile, dnp);
13060Sstevel@tonic-gate 	qsort(dnames, ndevs, sizeof (const char *), devcmp);
13070Sstevel@tonic-gate 	for (dnf = (dnp = dnames) + 1; dnf < dnames+ndevs; dnf++)
13080Sstevel@tonic-gate 		if (strcmp(*dnf, *dnp) != 0)
13090Sstevel@tonic-gate 			*++dnp = *dnf;
13100Sstevel@tonic-gate 	*++dnp = NULL;
13110Sstevel@tonic-gate 
13120Sstevel@tonic-gate 	/* Return array of pointers to names. */
13130Sstevel@tonic-gate 	return (dnames);
13140Sstevel@tonic-gate }
13150Sstevel@tonic-gate 
13160Sstevel@tonic-gate /*
13170Sstevel@tonic-gate  * Convert data structures created by parsing process into data
13180Sstevel@tonic-gate  * structures used by service dispatch.  This gathers the unique
13190Sstevel@tonic-gate  * device (lower stream) names and attaches the services available on
13200Sstevel@tonic-gate  * each device to a list while triming duplicate services.
13210Sstevel@tonic-gate  */
13220Sstevel@tonic-gate static struct option_state *
organize_state(struct parse_state * psp)13230Sstevel@tonic-gate organize_state(struct parse_state *psp)
13240Sstevel@tonic-gate {
13250Sstevel@tonic-gate 	struct per_file *pfp;
13260Sstevel@tonic-gate 	struct per_file *pftopp;
13270Sstevel@tonic-gate 	struct service_list *slp;
13280Sstevel@tonic-gate 	struct device_list *dlp;
13290Sstevel@tonic-gate 	int ndevs;
13300Sstevel@tonic-gate 	int nsvcs;
13310Sstevel@tonic-gate 	const char **dnames;
13320Sstevel@tonic-gate 	const char **dnp;
13330Sstevel@tonic-gate 	struct device_entry *dep;
13340Sstevel@tonic-gate 	struct option_state *osp;
13350Sstevel@tonic-gate 	struct service_entry **sepp;
13360Sstevel@tonic-gate 	struct service_entry **sebpp;
13370Sstevel@tonic-gate 	struct service_entry **se2pp;
13380Sstevel@tonic-gate 
13390Sstevel@tonic-gate 	/*
13400Sstevel@tonic-gate 	 * Parsing is now done.
13410Sstevel@tonic-gate 	 */
13420Sstevel@tonic-gate 	close_service(psp->ps_csvc);
13430Sstevel@tonic-gate 	psp->ps_csvc = NULL;
13440Sstevel@tonic-gate 	if ((pfp = psp->ps_cfile) != NULL) {
13450Sstevel@tonic-gate 		pfp->pf_prev = psp->ps_files;
13460Sstevel@tonic-gate 		psp->ps_files = pfp;
13470Sstevel@tonic-gate 		psp->ps_cfile = NULL;
13480Sstevel@tonic-gate 	}
13490Sstevel@tonic-gate 
13500Sstevel@tonic-gate 	/* Link the services from all files together for easy referencing. */
13510Sstevel@tonic-gate 	pftopp = psp->ps_files;
13520Sstevel@tonic-gate 	for (pfp = pftopp->pf_prev; pfp != NULL; pfp = pfp->pf_prev)
13530Sstevel@tonic-gate 		if (pfp->pf_svc != NULL) {
13540Sstevel@tonic-gate 			if (pftopp->pf_svc_last == NULL)
13550Sstevel@tonic-gate 				pftopp->pf_svc = pfp->pf_svc;
13560Sstevel@tonic-gate 			else
13570Sstevel@tonic-gate 				pftopp->pf_svc_last->sl_next = pfp->pf_svc;
13580Sstevel@tonic-gate 			pftopp->pf_svc_last = pfp->pf_svc_last;
13590Sstevel@tonic-gate 			pfp->pf_svc = pfp->pf_svc_last = NULL;
13600Sstevel@tonic-gate 		}
13610Sstevel@tonic-gate 
13620Sstevel@tonic-gate 	/*
13630Sstevel@tonic-gate 	 * Count up number of services per device, including
13640Sstevel@tonic-gate 	 * duplicates but not including defaults.
13650Sstevel@tonic-gate 	 */
13660Sstevel@tonic-gate 	nsvcs = 0;
13670Sstevel@tonic-gate 	for (slp = psp->ps_files->pf_svc; slp != NULL; slp = slp->sl_next)
13680Sstevel@tonic-gate 		for (dlp = slp->sl_dev; dlp != NULL; dlp = dlp->dl_next)
13690Sstevel@tonic-gate 			nsvcs++;
13700Sstevel@tonic-gate 
13710Sstevel@tonic-gate 	/*
13720Sstevel@tonic-gate 	 * Get the unique devices referenced by all services.
13730Sstevel@tonic-gate 	 */
13740Sstevel@tonic-gate 	dnames = get_unique_devs(psp);
13750Sstevel@tonic-gate 	if (dnames == NULL) {
13760Sstevel@tonic-gate 		logdbg("no devices referenced by any service");
13770Sstevel@tonic-gate 		return (NULL);
13780Sstevel@tonic-gate 	}
13790Sstevel@tonic-gate 	ndevs = 0;
13800Sstevel@tonic-gate 	for (dnp = dnames; *dnp != NULL; dnp++)
13810Sstevel@tonic-gate 		ndevs++;
13820Sstevel@tonic-gate 
13830Sstevel@tonic-gate 	/*
13840Sstevel@tonic-gate 	 * Allocate room for main structure, device records, and
13850Sstevel@tonic-gate 	 * per-device lists.  Worst case is all devices having all
13860Sstevel@tonic-gate 	 * services; that's why we allocate for nsvcs * ndevs.
13870Sstevel@tonic-gate 	 */
13880Sstevel@tonic-gate 	osp = (struct option_state *)malloc(sizeof (*osp) +
13890Sstevel@tonic-gate 	    ndevs * sizeof (*dep) + nsvcs * ndevs * sizeof (*sepp));
13900Sstevel@tonic-gate 	if (osp == NULL) {
13910Sstevel@tonic-gate 		logerr("unable to allocate option state structure");
13920Sstevel@tonic-gate 		free(dnames);
13930Sstevel@tonic-gate 		return (NULL);
13940Sstevel@tonic-gate 	}
13950Sstevel@tonic-gate 
13960Sstevel@tonic-gate 	/* We're going to succeed now, so steal these over. */
13970Sstevel@tonic-gate 	osp->os_devices = dep = (struct device_entry *)(osp+1);
13980Sstevel@tonic-gate 	osp->os_pfjunk = psp->ps_files;
13990Sstevel@tonic-gate 	psp->ps_files = NULL;
14000Sstevel@tonic-gate 	osp->os_evjunk = psp->ps_evlist;
14010Sstevel@tonic-gate 	psp->ps_evlist = NULL;
14020Sstevel@tonic-gate 
14030Sstevel@tonic-gate 	/* Loop over devices, install services, remove duplicates. */
14040Sstevel@tonic-gate 	sepp = (struct service_entry **)(dep + ndevs);
14050Sstevel@tonic-gate 	for (dnp = dnames; *dnp != NULL; dnp++) {
14060Sstevel@tonic-gate 		dep->de_name = *dnp;
14070Sstevel@tonic-gate 		dep->de_services = (const struct service_entry **)sepp;
14080Sstevel@tonic-gate 		sebpp = sepp;
14090Sstevel@tonic-gate 		for (slp = osp->os_pfjunk->pf_svc; slp != NULL;
14100Sstevel@tonic-gate 		    slp = slp->sl_next)
14110Sstevel@tonic-gate 			for (dlp = slp->sl_dev; dlp != NULL;
14120Sstevel@tonic-gate 			    dlp = dlp->dl_next) {
14130Sstevel@tonic-gate 				if (dlp->dl_name == *dnp ||
14140Sstevel@tonic-gate 				    strcmp(dlp->dl_name, *dnp) == 0) {
14150Sstevel@tonic-gate 					for (se2pp = sebpp; se2pp < sepp;
14160Sstevel@tonic-gate 					    se2pp++)
14170Sstevel@tonic-gate 						if ((*se2pp)->se_name ==
14180Sstevel@tonic-gate 						    slp->sl_entry.se_name ||
14190Sstevel@tonic-gate 						    strcmp((*se2pp)->
1420*9751Sjames.d.carlson@sun.com 						    se_name, slp->sl_entry.
1421*9751Sjames.d.carlson@sun.com 						    se_name) == 0)
14220Sstevel@tonic-gate 							break;
14230Sstevel@tonic-gate 					/*
14240Sstevel@tonic-gate 					 * We retain a service if it's
14250Sstevel@tonic-gate 					 * unique or if its serial
14260Sstevel@tonic-gate 					 * number (position in the
14270Sstevel@tonic-gate 					 * file) is greater than than
14280Sstevel@tonic-gate 					 * any other.
14290Sstevel@tonic-gate 					 */
14300Sstevel@tonic-gate 					if (se2pp >= sepp)
14310Sstevel@tonic-gate 						*sepp++ = &slp->sl_entry;
14320Sstevel@tonic-gate 					else if (SESERIAL(**se2pp) <
14330Sstevel@tonic-gate 					    SESERIAL(slp->sl_entry))
14340Sstevel@tonic-gate 						*se2pp = &slp->sl_entry;
14350Sstevel@tonic-gate 				}
14360Sstevel@tonic-gate 			}
14370Sstevel@tonic-gate 		/* Count up the services on this device. */
14380Sstevel@tonic-gate 		dep->de_nservices = (const struct service_entry **)sepp -
14390Sstevel@tonic-gate 		    dep->de_services;
14400Sstevel@tonic-gate 		/* Ignore devices having no services at all. */
14410Sstevel@tonic-gate 		if (dep->de_nservices > 0)
14420Sstevel@tonic-gate 			dep++;
14430Sstevel@tonic-gate 	}
14440Sstevel@tonic-gate 	/* Count up the devices. */
14450Sstevel@tonic-gate 	osp->os_ndevices = dep - osp->os_devices;
14460Sstevel@tonic-gate 	/* Free the list of device names */
14470Sstevel@tonic-gate 	free(dnames);
14480Sstevel@tonic-gate 	return (osp);
14490Sstevel@tonic-gate }
14500Sstevel@tonic-gate 
14510Sstevel@tonic-gate /*
14520Sstevel@tonic-gate  * Free storage unique to a given service.  Pointers copied from other
14530Sstevel@tonic-gate  * services are ignored.
14540Sstevel@tonic-gate  */
14550Sstevel@tonic-gate static void
free_service(struct service_list * slp)14560Sstevel@tonic-gate free_service(struct service_list *slp)
14570Sstevel@tonic-gate {
14580Sstevel@tonic-gate 	struct filter_entry *fep;
14590Sstevel@tonic-gate 	struct filter_entry *fen;
14600Sstevel@tonic-gate 
14610Sstevel@tonic-gate 	if (!(slp->sl_entry.se_flags & SEF_CDEV))
14620Sstevel@tonic-gate 		free_device_list(slp->sl_dev);
14630Sstevel@tonic-gate 	if (!(slp->sl_entry.se_flags & SEF_CFLIST)) {
14640Sstevel@tonic-gate 		fep = slp->sl_entry.se_flist;
14650Sstevel@tonic-gate 		while (fep != NULL) {
14660Sstevel@tonic-gate 			fen = fep->fe_prevcopy ? NULL : fep->fe_prev;
14670Sstevel@tonic-gate 			free(fep);
14680Sstevel@tonic-gate 			fep = fen;
14690Sstevel@tonic-gate 		}
14700Sstevel@tonic-gate 	}
14710Sstevel@tonic-gate 	if (!(slp->sl_entry.se_flags & SEF_CPPPD) &&
14720Sstevel@tonic-gate 	    slp->sl_entry.se_pppd != NULL)
14730Sstevel@tonic-gate 		free(slp->sl_entry.se_pppd);
14740Sstevel@tonic-gate 	if (!(slp->sl_entry.se_flags & SEF_CSERVER) &&
14750Sstevel@tonic-gate 	    slp->sl_entry.se_server != NULL)
14760Sstevel@tonic-gate 		free(slp->sl_entry.se_server);
14770Sstevel@tonic-gate 	if (!(slp->sl_entry.se_flags & SEF_CPATH) &&
14780Sstevel@tonic-gate 	    slp->sl_entry.se_path != NULL)
14790Sstevel@tonic-gate 		free(slp->sl_entry.se_path);
14800Sstevel@tonic-gate 	if (!(slp->sl_entry.se_flags & SEF_CEXTRA) &&
14810Sstevel@tonic-gate 	    slp->sl_entry.se_extra != NULL)
14820Sstevel@tonic-gate 		free(slp->sl_entry.se_extra);
14830Sstevel@tonic-gate 	if (!(slp->sl_entry.se_flags & SEF_CLOG) &&
14840Sstevel@tonic-gate 	    slp->sl_entry.se_log != NULL)
14850Sstevel@tonic-gate 		free(slp->sl_entry.se_log);
14860Sstevel@tonic-gate }
14870Sstevel@tonic-gate 
14880Sstevel@tonic-gate /*
14890Sstevel@tonic-gate  * Free a linked list of services.
14900Sstevel@tonic-gate  */
14910Sstevel@tonic-gate static void
free_service_list(struct service_list * slp)14920Sstevel@tonic-gate free_service_list(struct service_list *slp)
14930Sstevel@tonic-gate {
14940Sstevel@tonic-gate 	struct service_list *sln;
14950Sstevel@tonic-gate 
14960Sstevel@tonic-gate 	while (slp != NULL) {
14970Sstevel@tonic-gate 		free_service(slp);
14980Sstevel@tonic-gate 		sln = slp->sl_next;
14990Sstevel@tonic-gate 		free(slp);
15000Sstevel@tonic-gate 		slp = sln;
15010Sstevel@tonic-gate 	}
15020Sstevel@tonic-gate }
15030Sstevel@tonic-gate 
15040Sstevel@tonic-gate /*
15050Sstevel@tonic-gate  * Free a linked list of files and all services in those files.
15060Sstevel@tonic-gate  */
15070Sstevel@tonic-gate static void
free_file_list(struct per_file * pfp)15080Sstevel@tonic-gate free_file_list(struct per_file *pfp)
15090Sstevel@tonic-gate {
15100Sstevel@tonic-gate 	struct per_file *pfn;
15110Sstevel@tonic-gate 
15120Sstevel@tonic-gate 	while (pfp != NULL) {
15130Sstevel@tonic-gate 		free_service(&pfp->pf_global);
15140Sstevel@tonic-gate 		free_service_list(pfp->pf_svc);
15150Sstevel@tonic-gate 		pfn = pfp->pf_prev;
15160Sstevel@tonic-gate 		free(pfp);
15170Sstevel@tonic-gate 		pfp = pfn;
15180Sstevel@tonic-gate 	}
15190Sstevel@tonic-gate }
15200Sstevel@tonic-gate 
15210Sstevel@tonic-gate /*
15220Sstevel@tonic-gate  * Free an array of local environment variables.
15230Sstevel@tonic-gate  */
15240Sstevel@tonic-gate static void
free_env_list(char ** evlist)15250Sstevel@tonic-gate free_env_list(char **evlist)
15260Sstevel@tonic-gate {
15270Sstevel@tonic-gate 	char **evp;
15280Sstevel@tonic-gate 	char *env;
15290Sstevel@tonic-gate 
15300Sstevel@tonic-gate 	if ((evp = evlist) != NULL) {
15310Sstevel@tonic-gate 		while ((env = *evp++) != NULL)
15320Sstevel@tonic-gate 			free(env);
15330Sstevel@tonic-gate 		free(evlist);
15340Sstevel@tonic-gate 	}
15350Sstevel@tonic-gate }
15360Sstevel@tonic-gate 
15370Sstevel@tonic-gate /*
15380Sstevel@tonic-gate  * Add a new device (lower stream) to the list for which we're the
15390Sstevel@tonic-gate  * PPPoE server.
15400Sstevel@tonic-gate  */
15410Sstevel@tonic-gate static void
add_new_dev(int tunfd,const char * dname)15420Sstevel@tonic-gate add_new_dev(int tunfd, const char *dname)
15430Sstevel@tonic-gate {
15440Sstevel@tonic-gate 	union ppptun_name ptn;
15450Sstevel@tonic-gate 
15460Sstevel@tonic-gate 	(void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%s:pppoed",
15470Sstevel@tonic-gate 	    dname);
15480Sstevel@tonic-gate 	if (strioctl(tunfd, PPPTUN_SCTL, &ptn, sizeof (ptn), 0) < 0) {
15490Sstevel@tonic-gate 		logerr("PPPTUN_SCTL %s: %s", ptn.ptn_name, mystrerror(errno));
15500Sstevel@tonic-gate 	} else {
15510Sstevel@tonic-gate 		logdbg("added %s", ptn.ptn_name);
15520Sstevel@tonic-gate 	}
15530Sstevel@tonic-gate }
15540Sstevel@tonic-gate 
15550Sstevel@tonic-gate /*
15560Sstevel@tonic-gate  * Remove an existing device (lower stream) from the list for which we
15570Sstevel@tonic-gate  * were the PPPoE server.
15580Sstevel@tonic-gate  */
15590Sstevel@tonic-gate static void
rem_old_dev(int tunfd,const char * dname)15600Sstevel@tonic-gate rem_old_dev(int tunfd, const char *dname)
15610Sstevel@tonic-gate {
15620Sstevel@tonic-gate 	union ppptun_name ptn;
15630Sstevel@tonic-gate 
15640Sstevel@tonic-gate 	(void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%s:pppoed",
15650Sstevel@tonic-gate 	    dname);
15660Sstevel@tonic-gate 	if (strioctl(tunfd, PPPTUN_DCTL, &ptn, sizeof (ptn), 0) < 0) {
15670Sstevel@tonic-gate 		logerr("PPPTUN_DCTL %s: %s", ptn.ptn_name, mystrerror(errno));
15680Sstevel@tonic-gate 	} else {
15690Sstevel@tonic-gate 		logdbg("removed %s", ptn.ptn_name);
15700Sstevel@tonic-gate 	}
15710Sstevel@tonic-gate }
15720Sstevel@tonic-gate 
15730Sstevel@tonic-gate /*
15740Sstevel@tonic-gate  * Get a list of all of the devices currently plumbed for PPPoE.  This
15750Sstevel@tonic-gate  * is used for supporting the "*" and "all" device aliases.
15760Sstevel@tonic-gate  */
15770Sstevel@tonic-gate static void
get_device_list(struct parse_state * psp,int tunfd)15780Sstevel@tonic-gate get_device_list(struct parse_state *psp, int tunfd)
15790Sstevel@tonic-gate {
15800Sstevel@tonic-gate 	struct device_list *dlp;
15810Sstevel@tonic-gate 	struct device_list **dlpp;
15820Sstevel@tonic-gate 	struct device_list *dlalt;
15830Sstevel@tonic-gate 	struct device_list **dl2pp;
15840Sstevel@tonic-gate 	struct device_list *dla;
15850Sstevel@tonic-gate 	int i;
15860Sstevel@tonic-gate 	union ppptun_name ptn;
15870Sstevel@tonic-gate 	char *cp;
15880Sstevel@tonic-gate 
15890Sstevel@tonic-gate 	/* First pass; just allocate space for all *:pppoe* devices */
15900Sstevel@tonic-gate 	dlpp = &psp->ps_star;
15910Sstevel@tonic-gate 	dl2pp = &dlalt;
15920Sstevel@tonic-gate 	for (i = 0; ; i++) {
15930Sstevel@tonic-gate 		ptn.ptn_index = i;
15940Sstevel@tonic-gate 		if (strioctl(tunfd, PPPTUN_GNNAME, &ptn, sizeof (ptn),
15950Sstevel@tonic-gate 		    sizeof (ptn)) < 0) {
15960Sstevel@tonic-gate 			logerr("PPPTUN_GNNAME %d: %s", i, mystrerror(errno));
15970Sstevel@tonic-gate 			break;
15980Sstevel@tonic-gate 		}
15990Sstevel@tonic-gate 		if (ptn.ptn_name[0] == '\0')
16000Sstevel@tonic-gate 			break;
16010Sstevel@tonic-gate 		if ((cp = strchr(ptn.ptn_name, ':')) == NULL ||
16020Sstevel@tonic-gate 		    strncmp(cp, ":pppoe", 6) != 0 ||
16030Sstevel@tonic-gate 		    (cp[6] != '\0' && strcmp(cp+6, "d") != 0))
16040Sstevel@tonic-gate 			continue;
16050Sstevel@tonic-gate 		*cp = '\0';
16060Sstevel@tonic-gate 		dlp = (struct device_list *)malloc(sizeof (*dlp) +
16070Sstevel@tonic-gate 		    strlen(ptn.ptn_name) + 1);
16080Sstevel@tonic-gate 		if (dlp == NULL)
16090Sstevel@tonic-gate 			break;
16100Sstevel@tonic-gate 		dlp->dl_name = (const char *)(dlp + 1);
16110Sstevel@tonic-gate 		(void) strcpy((char *)(dlp + 1), ptn.ptn_name);
16120Sstevel@tonic-gate 		if (cp[6] == '\0') {
16130Sstevel@tonic-gate 			*dlpp = dlp;
16140Sstevel@tonic-gate 			dlpp = &dlp->dl_next;
16150Sstevel@tonic-gate 		} else {
16160Sstevel@tonic-gate 			*dl2pp = dlp;
16170Sstevel@tonic-gate 			dl2pp = &dlp->dl_next;
16180Sstevel@tonic-gate 		}
16190Sstevel@tonic-gate 	}
16200Sstevel@tonic-gate 	*dlpp = NULL;
16210Sstevel@tonic-gate 	*dl2pp = NULL;
16220Sstevel@tonic-gate 
16230Sstevel@tonic-gate 	/* Second pass; eliminate improperly plumbed devices */
16240Sstevel@tonic-gate 	for (dlpp = &psp->ps_star; (dlp = *dlpp) != NULL; ) {
16250Sstevel@tonic-gate 		for (dla = dlalt; dla != NULL; dla = dla->dl_next)
16260Sstevel@tonic-gate 			if (strcmp(dla->dl_name, dlp->dl_name) == 0)
1627*9751Sjames.d.carlson@sun.com 				break;
16280Sstevel@tonic-gate 		if (dla == NULL) {
16290Sstevel@tonic-gate 			*dlpp = dlp->dl_next;
16300Sstevel@tonic-gate 			free(dlp);
16310Sstevel@tonic-gate 		} else {
16320Sstevel@tonic-gate 			dlpp = &dlp->dl_next;
16330Sstevel@tonic-gate 		}
16340Sstevel@tonic-gate 	}
16350Sstevel@tonic-gate 	free_device_list(dlalt);
16360Sstevel@tonic-gate 
16370Sstevel@tonic-gate 	/* Add in "*" so we can always handle dynamic plumbing. */
16380Sstevel@tonic-gate 	dlp = (struct device_list *)malloc(sizeof (*dlp) + 2);
16390Sstevel@tonic-gate 	if (dlp != NULL) {
16400Sstevel@tonic-gate 		dlp->dl_name = (const char *)(dlp + 1);
16410Sstevel@tonic-gate 		(void) strcpy((char *)(dlp + 1), "*");
16420Sstevel@tonic-gate 		dlp->dl_next = psp->ps_star;
16430Sstevel@tonic-gate 		psp->ps_star = dlp;
16440Sstevel@tonic-gate 	}
16450Sstevel@tonic-gate }
16460Sstevel@tonic-gate 
16470Sstevel@tonic-gate /*
16480Sstevel@tonic-gate  * Set logging subsystem back to configured global default values.
16490Sstevel@tonic-gate  */
16500Sstevel@tonic-gate void
global_logging(void)16510Sstevel@tonic-gate global_logging(void)
16520Sstevel@tonic-gate {
16530Sstevel@tonic-gate 	log_for_service(glob_svc.se_log, glob_svc.se_debug);
16540Sstevel@tonic-gate }
16550Sstevel@tonic-gate 
16560Sstevel@tonic-gate /*
16570Sstevel@tonic-gate  * Handle SIGHUP -- reparse command line and all configuration files.
16580Sstevel@tonic-gate  * When reparsing is complete, free old parsed data and replace with
16590Sstevel@tonic-gate  * new.
16600Sstevel@tonic-gate  */
16610Sstevel@tonic-gate void
parse_options(int tunfd,int argc,char ** argv)16620Sstevel@tonic-gate parse_options(int tunfd, int argc, char **argv)
16630Sstevel@tonic-gate {
16640Sstevel@tonic-gate 	struct parse_state pstate;
16650Sstevel@tonic-gate 	struct per_file *argpf;
16660Sstevel@tonic-gate 	struct option_state *newopt;
16670Sstevel@tonic-gate 	const char **dnames;
16680Sstevel@tonic-gate 	const char **dnp;
16690Sstevel@tonic-gate 	const struct device_entry *newdep, *newmax;
16700Sstevel@tonic-gate 	const struct device_entry *olddep, *oldmax;
16710Sstevel@tonic-gate 	int cmpval;
16720Sstevel@tonic-gate 	struct service_entry newglobsvc, *mainsvc;
16730Sstevel@tonic-gate 
16740Sstevel@tonic-gate 	/* Note that all per_file structures must be freeable */
16750Sstevel@tonic-gate 	argpf = (struct per_file *)calloc(sizeof (*argpf), 1);
16760Sstevel@tonic-gate 	if (argpf == NULL) {
16770Sstevel@tonic-gate 		return;
16780Sstevel@tonic-gate 	}
16790Sstevel@tonic-gate 	(void) memset(&pstate, '\0', sizeof (pstate));
16800Sstevel@tonic-gate 	pstate.ps_state = ksDefault;
16810Sstevel@tonic-gate 	pstate.ps_cfile = argpf;
16820Sstevel@tonic-gate 	pstate.ps_csvc = &argpf->pf_global;
16830Sstevel@tonic-gate 	argpf->pf_global.sl_parse = &pstate;
16840Sstevel@tonic-gate 	argpf->pf_name = "command line";
16850Sstevel@tonic-gate 
16860Sstevel@tonic-gate 	/* Default is 1 -- errors only */
16870Sstevel@tonic-gate 	argpf->pf_global.sl_entry.se_debug++;
16880Sstevel@tonic-gate 	argpf->pf_global.sl_entry.se_name = "<global>";
16890Sstevel@tonic-gate 
16900Sstevel@tonic-gate 	/* Get list of all devices */
16910Sstevel@tonic-gate 	get_device_list(&pstate, tunfd);
16920Sstevel@tonic-gate 
16930Sstevel@tonic-gate 	/* Parse options from command line and main configuration file. */
16940Sstevel@tonic-gate 	pstate.ps_flags |= PSF_SETLEVEL;
16950Sstevel@tonic-gate 	parse_arg_list(&pstate, argc, argv);
16960Sstevel@tonic-gate 	parse_file(&pstate, "/etc/ppp/pppoe");
16970Sstevel@tonic-gate 	pstate.ps_flags &= ~PSF_SETLEVEL;
16980Sstevel@tonic-gate 
16990Sstevel@tonic-gate 	/*
17000Sstevel@tonic-gate 	 * At this point, global options from the main configuration
17010Sstevel@tonic-gate 	 * file are pointed to by ps_files, and options from command
17020Sstevel@tonic-gate 	 * line are in argpf.  We need to pull three special options
17030Sstevel@tonic-gate 	 * from these -- wildcard, debug, and log.  Note that the main
17040Sstevel@tonic-gate 	 * options file overrides the command line.  This is
17050Sstevel@tonic-gate 	 * intentional.  The semantics are such that the system
17060Sstevel@tonic-gate 	 * behaves as though the main configuration file were
17070Sstevel@tonic-gate 	 * "included" from the command line, and thus options there
17080Sstevel@tonic-gate 	 * override the command line.  This may seem odd, but at least
17090Sstevel@tonic-gate 	 * it's self-consistent.
17100Sstevel@tonic-gate 	 */
17110Sstevel@tonic-gate 	newglobsvc = argpf->pf_global.sl_entry;
17120Sstevel@tonic-gate 	if (pstate.ps_files != NULL) {
17130Sstevel@tonic-gate 		mainsvc = &pstate.ps_files->pf_global.sl_entry;
17140Sstevel@tonic-gate 		if (mainsvc->se_log != NULL)
17150Sstevel@tonic-gate 			newglobsvc.se_log = mainsvc->se_log;
17160Sstevel@tonic-gate 		if (mainsvc->se_flags & (SEF_WILD|SEF_NOWILD))
17170Sstevel@tonic-gate 			newglobsvc.se_flags =
17180Sstevel@tonic-gate 			    (newglobsvc.se_flags & ~(SEF_WILD|SEF_NOWILD)) |
17190Sstevel@tonic-gate 			    (mainsvc->se_flags & (SEF_WILD|SEF_NOWILD));
17200Sstevel@tonic-gate 		if (mainsvc->se_flags & SEF_DEBUGCLR)
17210Sstevel@tonic-gate 			newglobsvc.se_debug = 0;
17220Sstevel@tonic-gate 		newglobsvc.se_debug += mainsvc->se_debug;
17230Sstevel@tonic-gate 	}
17240Sstevel@tonic-gate 	glob_svc = newglobsvc;
17250Sstevel@tonic-gate 	global_logging();
17260Sstevel@tonic-gate 
17270Sstevel@tonic-gate 	/* Get the list of devices referenced by configuration above. */
17280Sstevel@tonic-gate 	dnames = get_unique_devs(&pstate);
17290Sstevel@tonic-gate 	if (dnames != NULL) {
17300Sstevel@tonic-gate 		/* Read per-device configuration files. */
17310Sstevel@tonic-gate 		pstate.ps_flags |= PSF_PERDEV;
17320Sstevel@tonic-gate 		for (dnp = dnames; *dnp != NULL; dnp++)
17330Sstevel@tonic-gate 			parse_file(&pstate, *dnp);
17340Sstevel@tonic-gate 		pstate.ps_flags &= ~PSF_PERDEV;
17350Sstevel@tonic-gate 		free(dnames);
17360Sstevel@tonic-gate 	}
17370Sstevel@tonic-gate 	file_end(&pstate);
17380Sstevel@tonic-gate 
17390Sstevel@tonic-gate 	/*
17400Sstevel@tonic-gate 	 * Convert parsed data structures into per-device structures.
17410Sstevel@tonic-gate 	 * (Invert the table.)
17420Sstevel@tonic-gate 	 */
17430Sstevel@tonic-gate 	newopt = organize_state(&pstate);
17440Sstevel@tonic-gate 
17450Sstevel@tonic-gate 	/* If we're going to free the file name, then stop logging there. */
17460Sstevel@tonic-gate 	if (newopt == NULL && glob_svc.se_log != NULL) {
17470Sstevel@tonic-gate 		glob_svc.se_log = NULL;
17480Sstevel@tonic-gate 		global_logging();
17490Sstevel@tonic-gate 	}
17500Sstevel@tonic-gate 
17510Sstevel@tonic-gate 	/*
17520Sstevel@tonic-gate 	 * Unless an error has occurred, these pointers are normally
17530Sstevel@tonic-gate 	 * all NULL.  Nothing is freed until the file is re-read.
17540Sstevel@tonic-gate 	 */
17550Sstevel@tonic-gate 	free_file_list(pstate.ps_files);
17560Sstevel@tonic-gate 	free_file_list(pstate.ps_cfile);
17570Sstevel@tonic-gate 	free_device_list(pstate.ps_star);
17580Sstevel@tonic-gate 	free_env_list(pstate.ps_evlist);
17590Sstevel@tonic-gate 
17600Sstevel@tonic-gate 	/*
17610Sstevel@tonic-gate 	 * Match up entries on device list.  Detach devices no longer
17620Sstevel@tonic-gate 	 * referenced.  Attach ones now referenced.  (The use of null
17630Sstevel@tonic-gate 	 * pointers here may look fishy, but it actually works.
17640Sstevel@tonic-gate 	 * NULL>=NULL is always true.)
17650Sstevel@tonic-gate 	 */
17660Sstevel@tonic-gate 	if (newopt != NULL) {
17670Sstevel@tonic-gate 		newdep = newopt->os_devices;
17680Sstevel@tonic-gate 		newmax = newdep + newopt->os_ndevices;
17690Sstevel@tonic-gate 	} else {
17700Sstevel@tonic-gate 		newdep = newmax = NULL;
17710Sstevel@tonic-gate 	}
17720Sstevel@tonic-gate 	if (cur_options != NULL) {
17730Sstevel@tonic-gate 		olddep = cur_options->os_devices;
17740Sstevel@tonic-gate 		oldmax = olddep + cur_options->os_ndevices;
17750Sstevel@tonic-gate 	} else {
17760Sstevel@tonic-gate 		olddep = oldmax = NULL;
17770Sstevel@tonic-gate 	}
17780Sstevel@tonic-gate 	while ((newdep != NULL && newdep < newmax) ||
17790Sstevel@tonic-gate 	    (olddep != NULL && olddep < oldmax)) {
17800Sstevel@tonic-gate 		if (newdep < newmax) {
17810Sstevel@tonic-gate 			if (olddep >= oldmax) {
17820Sstevel@tonic-gate 				add_new_dev(tunfd, newdep->de_name);
17830Sstevel@tonic-gate 				newdep++;
17840Sstevel@tonic-gate 			} else {
17850Sstevel@tonic-gate 				cmpval = strcmp(newdep->de_name,
17860Sstevel@tonic-gate 				    olddep->de_name);
17870Sstevel@tonic-gate 				if (cmpval < 0) {
17880Sstevel@tonic-gate 					/* Brand new device seen. */
17890Sstevel@tonic-gate 					add_new_dev(tunfd, newdep->de_name);
17900Sstevel@tonic-gate 					newdep++;
17910Sstevel@tonic-gate 				} else if (cmpval == 0) {
17920Sstevel@tonic-gate 					/* Existing device; skip it. */
17930Sstevel@tonic-gate 					newdep++;
17940Sstevel@tonic-gate 					olddep++;
17950Sstevel@tonic-gate 				}
17960Sstevel@tonic-gate 				/* No else clause -- removal is below */
17970Sstevel@tonic-gate 			}
17980Sstevel@tonic-gate 		}
17990Sstevel@tonic-gate 		if (olddep < oldmax) {
18000Sstevel@tonic-gate 			if (newdep >= newmax) {
18010Sstevel@tonic-gate 				rem_old_dev(tunfd, olddep->de_name);
18020Sstevel@tonic-gate 				olddep++;
18030Sstevel@tonic-gate 			} else {
18040Sstevel@tonic-gate 				cmpval = strcmp(newdep->de_name,
18050Sstevel@tonic-gate 				    olddep->de_name);
18060Sstevel@tonic-gate 				if (cmpval > 0) {
18070Sstevel@tonic-gate 					/* Old device is gone */
18080Sstevel@tonic-gate 					rem_old_dev(tunfd, olddep->de_name);
18090Sstevel@tonic-gate 					olddep++;
18100Sstevel@tonic-gate 				} else if (cmpval == 0) {
18110Sstevel@tonic-gate 					/* Existing device; skip it. */
18120Sstevel@tonic-gate 					newdep++;
18130Sstevel@tonic-gate 					olddep++;
18140Sstevel@tonic-gate 				}
18150Sstevel@tonic-gate 				/* No else clause -- insert handled above */
18160Sstevel@tonic-gate 			}
18170Sstevel@tonic-gate 		}
18180Sstevel@tonic-gate 	}
18190Sstevel@tonic-gate 
18200Sstevel@tonic-gate 	/* Discard existing parsed data storage. */
18210Sstevel@tonic-gate 	if (cur_options != NULL) {
18220Sstevel@tonic-gate 		free_file_list(cur_options->os_pfjunk);
18230Sstevel@tonic-gate 		free_env_list(cur_options->os_evjunk);
18240Sstevel@tonic-gate 		free(cur_options);
18250Sstevel@tonic-gate 	}
18260Sstevel@tonic-gate 	/* Install new. */
18270Sstevel@tonic-gate 	cur_options = newopt;
18280Sstevel@tonic-gate }
18290Sstevel@tonic-gate 
18300Sstevel@tonic-gate /*
18310Sstevel@tonic-gate  * Check if configured filters permit requesting client to use a given
18320Sstevel@tonic-gate  * service.  Note -- filters are stored in reverse order in order to
18330Sstevel@tonic-gate  * make file-inclusion work as expected.  Thus, the "first match"
18340Sstevel@tonic-gate  * filter rule becomes "last match" here.
18350Sstevel@tonic-gate  */
18360Sstevel@tonic-gate static boolean_t
allow_service(const struct service_entry * sep,const ppptun_atype * pap)18370Sstevel@tonic-gate allow_service(const struct service_entry *sep, const ppptun_atype *pap)
18380Sstevel@tonic-gate {
18390Sstevel@tonic-gate 	const struct filter_entry *fep;
18400Sstevel@tonic-gate 	const struct filter_entry *lmatch;
18410Sstevel@tonic-gate 	boolean_t anynonexcept = B_FALSE;
18420Sstevel@tonic-gate 	const uchar_t *upt;
18430Sstevel@tonic-gate 	const uchar_t *macp;
18440Sstevel@tonic-gate 	const uchar_t *maskp;
18450Sstevel@tonic-gate 	int i;
18460Sstevel@tonic-gate 
18470Sstevel@tonic-gate 	lmatch = NULL;
18480Sstevel@tonic-gate 	for (fep = sep->se_flist; fep != NULL; fep = fep->fe_prev) {
18490Sstevel@tonic-gate 		anynonexcept |= !fep->fe_isexcept;
18500Sstevel@tonic-gate 		upt = pap->pta_pppoe.ptma_mac;
18510Sstevel@tonic-gate 		macp = fep->fe_mac.ether_addr_octet;
18520Sstevel@tonic-gate 		maskp = fep->fe_mask.ether_addr_octet;
18530Sstevel@tonic-gate 		for (i = sizeof (pap->pta_pppoe.ptma_mac); i > 0; i--)
18540Sstevel@tonic-gate 			if (((*macp++ ^ *upt++) & *maskp++) != 0)
18550Sstevel@tonic-gate 				break;
18560Sstevel@tonic-gate 		if (i <= 0)
18570Sstevel@tonic-gate 			lmatch = fep;
18580Sstevel@tonic-gate 	}
18590Sstevel@tonic-gate 
18600Sstevel@tonic-gate 	if (lmatch == NULL) {
18610Sstevel@tonic-gate 		/*
18620Sstevel@tonic-gate 		 * Assume reject by default if any positive-match
18630Sstevel@tonic-gate 		 * (non-except) filters are given.  Otherwise, if
18640Sstevel@tonic-gate 		 * there are no positive-match filters, then
18650Sstevel@tonic-gate 		 * non-matching means accept by default.
18660Sstevel@tonic-gate 		 */
18670Sstevel@tonic-gate 		return (!anynonexcept);
18680Sstevel@tonic-gate 	}
18690Sstevel@tonic-gate 	return (!lmatch->fe_isexcept);
18700Sstevel@tonic-gate }
18710Sstevel@tonic-gate 
18720Sstevel@tonic-gate /*
18730Sstevel@tonic-gate  * Locate available service(s) based on client request.  Assumes that
18740Sstevel@tonic-gate  * outp points to a buffer of at least size PPPOE_MSGMAX.  Creates a
18750Sstevel@tonic-gate  * PPPoE response message in outp.  Returns count of matched services
18760Sstevel@tonic-gate  * and (through *srvp) a pointer to the last (or only) service.  If
18770Sstevel@tonic-gate  * some error is found in the request, an error string is added and -1
18780Sstevel@tonic-gate  * is returned; the caller should just send the message without
18790Sstevel@tonic-gate  * alteration.
18800Sstevel@tonic-gate  */
18810Sstevel@tonic-gate int
locate_service(poep_t * poep,int plen,const char * iname,ppptun_atype * pap,uint32_t * outp,void ** srvp)18820Sstevel@tonic-gate locate_service(poep_t *poep, int plen, const char *iname, ppptun_atype *pap,
18830Sstevel@tonic-gate     uint32_t *outp, void **srvp)
18840Sstevel@tonic-gate {
18850Sstevel@tonic-gate 	poep_t *opoe;
18860Sstevel@tonic-gate 	const uint8_t *tagp;
18870Sstevel@tonic-gate 	const char *cp;
18880Sstevel@tonic-gate 	int ttyp;
18890Sstevel@tonic-gate 	int tlen;
18900Sstevel@tonic-gate 	int nsvcs;
18910Sstevel@tonic-gate 	const struct device_entry *dep, *depe;
18920Sstevel@tonic-gate 	const struct device_entry *wdep;
18930Sstevel@tonic-gate 	const struct service_entry **sepp, **seppe;
18940Sstevel@tonic-gate 	const struct service_entry *sep;
18950Sstevel@tonic-gate 	char *str;
18960Sstevel@tonic-gate 	boolean_t ispadi;
18970Sstevel@tonic-gate 
18980Sstevel@tonic-gate 	ispadi = poep->poep_code == POECODE_PADI;
18990Sstevel@tonic-gate 	opoe = poe_mkheader(outp, ispadi ? POECODE_PADO : POECODE_PADS, 0);
19000Sstevel@tonic-gate 
19010Sstevel@tonic-gate 	*srvp = NULL;
19020Sstevel@tonic-gate 	if (cur_options == NULL)
19030Sstevel@tonic-gate 		return (0);
19040Sstevel@tonic-gate 
19050Sstevel@tonic-gate 	/* Search for named device (lower stream) in tables. */
19060Sstevel@tonic-gate 	dep = cur_options->os_devices;
19070Sstevel@tonic-gate 	depe = dep + cur_options->os_ndevices;
19080Sstevel@tonic-gate 	wdep = NULL;
19090Sstevel@tonic-gate 	if ((cp = strchr(iname, ':')) != NULL)
19100Sstevel@tonic-gate 		tlen = cp - iname;
19110Sstevel@tonic-gate 	else
19120Sstevel@tonic-gate 		tlen = strlen(iname);
19130Sstevel@tonic-gate 	for (; dep < depe; dep++)
19140Sstevel@tonic-gate 		if (strncmp(iname, dep->de_name, tlen) == 0 &&
19150Sstevel@tonic-gate 		    dep->de_name[tlen] == '\0')
19160Sstevel@tonic-gate 			break;
19170Sstevel@tonic-gate 		else if (dep->de_name[0] == '*' && dep->de_name[1] == '\0')
19180Sstevel@tonic-gate 			wdep = dep;
19190Sstevel@tonic-gate 	if (dep >= depe)
19200Sstevel@tonic-gate 		dep = wdep;
19210Sstevel@tonic-gate 	/*
19220Sstevel@tonic-gate 	 * Return if interface not found.  Zero-service case can't
19230Sstevel@tonic-gate 	 * occur, since devices with no services aren't included in
19240Sstevel@tonic-gate 	 * the list, but the code is just being safe here.
19250Sstevel@tonic-gate 	 */
19260Sstevel@tonic-gate 	if (dep == NULL || dep->de_services == NULL || dep->de_nservices <= 0)
19270Sstevel@tonic-gate 		return (0);
19280Sstevel@tonic-gate 
19290Sstevel@tonic-gate 	/*
19300Sstevel@tonic-gate 	 * Loop over tags in client message and process them.
19310Sstevel@tonic-gate 	 * Services must be matched against our list.  Host-Uniq and
19320Sstevel@tonic-gate 	 * Relay-Session-Id must be copied to the reply.  All others
19330Sstevel@tonic-gate 	 * must be discarded.
19340Sstevel@tonic-gate 	 */
19350Sstevel@tonic-gate 	nsvcs = 0;
19360Sstevel@tonic-gate 	sepp = dep->de_services;
19370Sstevel@tonic-gate 	tagp = (const uint8_t *)(poep + 1);
19380Sstevel@tonic-gate 	while (poe_tagcheck(poep, plen, tagp)) {
19390Sstevel@tonic-gate 		ttyp = POET_GET_TYPE(tagp);
19400Sstevel@tonic-gate 		if (ttyp == POETT_END)
19410Sstevel@tonic-gate 			break;
19420Sstevel@tonic-gate 		tlen = POET_GET_LENG(tagp);
19430Sstevel@tonic-gate 		switch (ttyp) {
19440Sstevel@tonic-gate 		case POETT_SERVICE:	/* Service-Name */
19450Sstevel@tonic-gate 			/*
19460Sstevel@tonic-gate 			 * Allow only one.  (Note that this test works
19470Sstevel@tonic-gate 			 * because there's always at least one service
19480Sstevel@tonic-gate 			 * per device; otherwise, the device is
19490Sstevel@tonic-gate 			 * removed from the list.)
19500Sstevel@tonic-gate 			 */
19510Sstevel@tonic-gate 			if (sepp != dep->de_services) {
19520Sstevel@tonic-gate 				if (nsvcs != -1)
19530Sstevel@tonic-gate 					(void) poe_add_str(opoe, POETT_NAMERR,
19540Sstevel@tonic-gate 					    "Too many Service-Name tags");
19550Sstevel@tonic-gate 				nsvcs = -1;
19560Sstevel@tonic-gate 				break;
19570Sstevel@tonic-gate 			}
19580Sstevel@tonic-gate 			seppe = sepp + dep->de_nservices;
19590Sstevel@tonic-gate 			if (tlen == 0) {
19600Sstevel@tonic-gate 				/*
19610Sstevel@tonic-gate 				 * If config specifies "nowild" in a
19620Sstevel@tonic-gate 				 * global context, then we don't
19630Sstevel@tonic-gate 				 * respond to wildcard PADRs.  The
19640Sstevel@tonic-gate 				 * client must know the exact service
19650Sstevel@tonic-gate 				 * name to get access.
19660Sstevel@tonic-gate 				 */
19670Sstevel@tonic-gate 
19680Sstevel@tonic-gate 				if (!ispadi && (glob_svc.se_flags & SEF_NOWILD))
19690Sstevel@tonic-gate 					sepp = seppe;
19700Sstevel@tonic-gate 				while (sepp < seppe) {
19710Sstevel@tonic-gate 					sep = *sepp++;
1972*9751Sjames.d.carlson@sun.com 					if (sep->se_name[0] == '\0' ||
1973*9751Sjames.d.carlson@sun.com 					    (sep->se_flags & SEF_NOWILD) ||
1974*9751Sjames.d.carlson@sun.com 					    !allow_service(sep, pap))
1975*9751Sjames.d.carlson@sun.com 						continue;
1976*9751Sjames.d.carlson@sun.com 					*srvp = (void *)sep;
1977*9751Sjames.d.carlson@sun.com 					/*
1978*9751Sjames.d.carlson@sun.com 					 * RFC requires that PADO includes the
1979*9751Sjames.d.carlson@sun.com 					 * wildcard service request in response
1980*9751Sjames.d.carlson@sun.com 					 * to PADI.
1981*9751Sjames.d.carlson@sun.com 					 */
1982*9751Sjames.d.carlson@sun.com 					if (ispadi && nsvcs == 0 &&
1983*9751Sjames.d.carlson@sun.com 					    !(glob_svc.se_flags & SEF_NOWILD))
1984*9751Sjames.d.carlson@sun.com 						(void) poe_tag_copy(opoe, tagp);
1985*9751Sjames.d.carlson@sun.com 					nsvcs++;
1986*9751Sjames.d.carlson@sun.com 					(void) poe_add_str(opoe, POETT_SERVICE,
1987*9751Sjames.d.carlson@sun.com 					    sep->se_name);
1988*9751Sjames.d.carlson@sun.com 					/* If PADR, then one is enough */
1989*9751Sjames.d.carlson@sun.com 					if (!ispadi)
1990*9751Sjames.d.carlson@sun.com 						break;
19910Sstevel@tonic-gate 				}
1992*9751Sjames.d.carlson@sun.com 				/* Just for generating error messages */
1993*9751Sjames.d.carlson@sun.com 				if (nsvcs == 0)
1994*9751Sjames.d.carlson@sun.com 					(void) poe_tag_copy(opoe, tagp);
19950Sstevel@tonic-gate 			} else {
1996*9751Sjames.d.carlson@sun.com 				/*
1997*9751Sjames.d.carlson@sun.com 				 * Clients's requested service must appear in
1998*9751Sjames.d.carlson@sun.com 				 * reply.
1999*9751Sjames.d.carlson@sun.com 				 */
2000*9751Sjames.d.carlson@sun.com 				(void) poe_tag_copy(opoe, tagp);
2001*9751Sjames.d.carlson@sun.com 
20020Sstevel@tonic-gate 				/* Requested specific service; find it. */
20030Sstevel@tonic-gate 				cp = (char *)POET_DATA(tagp);
20040Sstevel@tonic-gate 				while (sepp < seppe) {
20050Sstevel@tonic-gate 					sep = *sepp++;
20060Sstevel@tonic-gate 					if (strlen(sep->se_name) == tlen &&
20070Sstevel@tonic-gate 					    strncasecmp(sep->se_name, cp,
2008*9751Sjames.d.carlson@sun.com 					    tlen) == 0) {
20090Sstevel@tonic-gate 						if (allow_service(sep, pap)) {
20100Sstevel@tonic-gate 							nsvcs++;
20110Sstevel@tonic-gate 							*srvp = (void *)sep;
20120Sstevel@tonic-gate 						}
20130Sstevel@tonic-gate 						break;
20140Sstevel@tonic-gate 					}
20150Sstevel@tonic-gate 				}
20160Sstevel@tonic-gate 			}
20170Sstevel@tonic-gate 			/*
20180Sstevel@tonic-gate 			 * Allow service definition to override
20190Sstevel@tonic-gate 			 * AC-Name (concentrator [server] name) field.
20200Sstevel@tonic-gate 			 */
20210Sstevel@tonic-gate 			if (*srvp != NULL) {
20220Sstevel@tonic-gate 				sep = (const struct service_entry *)*srvp;
20230Sstevel@tonic-gate 				log_for_service(sep->se_log, sep->se_debug);
20240Sstevel@tonic-gate 				str = "Solaris PPPoE";
20250Sstevel@tonic-gate 				if (sep->se_server != NULL)
20260Sstevel@tonic-gate 					str = sep->se_server;
20270Sstevel@tonic-gate 				(void) poe_add_str(opoe, POETT_ACCESS, str);
20280Sstevel@tonic-gate 			}
20290Sstevel@tonic-gate 			break;
20300Sstevel@tonic-gate 		/* Ones we should discard */
20310Sstevel@tonic-gate 		case POETT_ACCESS:	/* AC-Name */
20320Sstevel@tonic-gate 		case POETT_COOKIE:	/* AC-Cookie */
20330Sstevel@tonic-gate 		case POETT_NAMERR:	/* Service-Name-Error */
20340Sstevel@tonic-gate 		case POETT_SYSERR:	/* AC-System-Error */
20350Sstevel@tonic-gate 		case POETT_GENERR:	/* Generic-Error */
20360Sstevel@tonic-gate 		case POETT_HURL:	/* Host-URL */
20370Sstevel@tonic-gate 		case POETT_MOTM:	/* Message-Of-The-Minute */
20380Sstevel@tonic-gate 		case POETT_RTEADD:	/* IP-Route-Add */
20390Sstevel@tonic-gate 		case POETT_VENDOR:	/* Vendor-Specific */
20400Sstevel@tonic-gate 		case POETT_MULTI:	/* Multicast-Capable */
20410Sstevel@tonic-gate 		default:
20420Sstevel@tonic-gate 			break;
20430Sstevel@tonic-gate 		/* Ones we should copy */
20440Sstevel@tonic-gate 		case POETT_UNIQ:	/* Host-Uniq */
20450Sstevel@tonic-gate 		case POETT_RELAY:	/* Relay-Session-Id */
20460Sstevel@tonic-gate 			(void) poe_tag_copy(opoe, tagp);
20470Sstevel@tonic-gate 			break;
20480Sstevel@tonic-gate 		}
20490Sstevel@tonic-gate 		tagp = POET_NEXT(tagp);
20500Sstevel@tonic-gate 	}
20510Sstevel@tonic-gate 	return (nsvcs);
20520Sstevel@tonic-gate }
20530Sstevel@tonic-gate 
20540Sstevel@tonic-gate /*
20550Sstevel@tonic-gate  * Like fgetc, but reads from a string.
20560Sstevel@tonic-gate  */
20570Sstevel@tonic-gate static int
sgetc(void * arg)20580Sstevel@tonic-gate sgetc(void *arg)
20590Sstevel@tonic-gate {
20600Sstevel@tonic-gate 	char **cpp = (char **)arg;
20610Sstevel@tonic-gate 	if (**cpp == '\0')
20620Sstevel@tonic-gate 		return (EOF);
20630Sstevel@tonic-gate 	return (*(*cpp)++);
20640Sstevel@tonic-gate }
20650Sstevel@tonic-gate 
20660Sstevel@tonic-gate /*
20670Sstevel@tonic-gate  * Given a service structure, launch pppd.  Called by handle_input()
20680Sstevel@tonic-gate  * in pppoed.c if locate_service() [above] finds exactly one service
20690Sstevel@tonic-gate  * matching a PADR.
20700Sstevel@tonic-gate  */
20710Sstevel@tonic-gate int
launch_service(int tunfd,poep_t * poep,void * srvp,struct ppptun_control * ptc)20720Sstevel@tonic-gate launch_service(int tunfd, poep_t *poep, void *srvp, struct ppptun_control *ptc)
20730Sstevel@tonic-gate {
20740Sstevel@tonic-gate 	const struct service_entry *sep = (const struct service_entry *)srvp;
20750Sstevel@tonic-gate 	const char *path;
20760Sstevel@tonic-gate 	const char *extra;
20770Sstevel@tonic-gate 	const char *pppd;
20780Sstevel@tonic-gate 	const char *cp;
20790Sstevel@tonic-gate 	pid_t pidv;
20800Sstevel@tonic-gate 	int newtun;
20810Sstevel@tonic-gate 	struct ppptun_peer ptp;
20820Sstevel@tonic-gate 	union ppptun_name ptn;
20830Sstevel@tonic-gate 	const char *args[MAXARGS];
20840Sstevel@tonic-gate 	struct strbuf ctrl;
20850Sstevel@tonic-gate 	struct strbuf data;
20860Sstevel@tonic-gate 	const char **cpp;
20870Sstevel@tonic-gate 	char *sptr;
20880Sstevel@tonic-gate 	char *spv;
20890Sstevel@tonic-gate 	int slen;
20900Sstevel@tonic-gate 	int retv;
20910Sstevel@tonic-gate 	char keybuf[MAX_KEYWORD];
20920Sstevel@tonic-gate 
20930Sstevel@tonic-gate 	assert(sep != NULL);
20940Sstevel@tonic-gate 
20950Sstevel@tonic-gate 	/* Get tunnel driver connection for new PPP session. */
20960Sstevel@tonic-gate 	newtun = open(tunnam, O_RDWR);
20970Sstevel@tonic-gate 	if (newtun == -1)
20980Sstevel@tonic-gate 		goto syserr;
20990Sstevel@tonic-gate 
21000Sstevel@tonic-gate 	/* Set this session up for standard PPP and client's address. */
21010Sstevel@tonic-gate 	(void) memset(&ptp, '\0', sizeof (ptp));
21020Sstevel@tonic-gate 	ptp.ptp_style = PTS_PPPOE;
21030Sstevel@tonic-gate 	ptp.ptp_address = ptc->ptc_address;
21040Sstevel@tonic-gate 	if (strioctl(newtun, PPPTUN_SPEER, &ptp, sizeof (ptp), sizeof (ptp)) <
21050Sstevel@tonic-gate 	    0)
21060Sstevel@tonic-gate 		goto syserr;
21070Sstevel@tonic-gate 	ptp.ptp_rsessid = ptp.ptp_lsessid;
21080Sstevel@tonic-gate 	if (strioctl(newtun, PPPTUN_SPEER, &ptp, sizeof (ptp), sizeof (ptp)) <
21090Sstevel@tonic-gate 	    0)
21100Sstevel@tonic-gate 		goto syserr;
21110Sstevel@tonic-gate 
21120Sstevel@tonic-gate 	/* Attach the requested lower stream. */
21130Sstevel@tonic-gate 	cp = strchr(ptc->ptc_name, ':');
21140Sstevel@tonic-gate 	if (cp == NULL)
21150Sstevel@tonic-gate 		cp = ptc->ptc_name + strlen(ptc->ptc_name);
21160Sstevel@tonic-gate 	(void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%.*s:pppoe",
21170Sstevel@tonic-gate 	    cp-ptc->ptc_name, ptc->ptc_name);
21180Sstevel@tonic-gate 	if (strioctl(newtun, PPPTUN_SDATA, &ptn, sizeof (ptn), 0) < 0)
21190Sstevel@tonic-gate 		goto syserr;
21200Sstevel@tonic-gate 	(void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%.*s:pppoed",
21210Sstevel@tonic-gate 	    cp-ptc->ptc_name, ptc->ptc_name);
21220Sstevel@tonic-gate 	if (strioctl(newtun, PPPTUN_SCTL, &ptn, sizeof (ptn), 0) < 0)
21230Sstevel@tonic-gate 		goto syserr;
21240Sstevel@tonic-gate 
21250Sstevel@tonic-gate 	pidv = fork();
21260Sstevel@tonic-gate 	if (pidv == (pid_t)-1)
21270Sstevel@tonic-gate 		goto syserr;
21280Sstevel@tonic-gate 
21290Sstevel@tonic-gate 	if (pidv == (pid_t)0) {
21300Sstevel@tonic-gate 		/*
21310Sstevel@tonic-gate 		 * Use syslog only in order to avoid mixing log messages
21320Sstevel@tonic-gate 		 * in regular files.
21330Sstevel@tonic-gate 		 */
21340Sstevel@tonic-gate 		close_log_files();
21350Sstevel@tonic-gate 
21360Sstevel@tonic-gate 		if ((path = sep->se_path) == NULL)
21370Sstevel@tonic-gate 			path = "/usr/bin/pppd";
21380Sstevel@tonic-gate 		if ((extra = sep->se_extra) == NULL)
21390Sstevel@tonic-gate 			extra = "plugin pppoe.so directtty";
21400Sstevel@tonic-gate 		if ((pppd = sep->se_pppd) == NULL)
21410Sstevel@tonic-gate 			pppd = "";
21420Sstevel@tonic-gate 
21430Sstevel@tonic-gate 		/* Concatenate these. */
21440Sstevel@tonic-gate 		slen = strlen(path) + strlen(extra) + strlen(pppd) + 3;
21450Sstevel@tonic-gate 		if ((sptr = (char *)malloc(slen)) == NULL)
21460Sstevel@tonic-gate 			goto bail_out;
21470Sstevel@tonic-gate 		(void) strcpy(sptr, path);
21480Sstevel@tonic-gate 		(void) strcat(sptr, " ");
21490Sstevel@tonic-gate 		(void) strcat(sptr, extra);
21500Sstevel@tonic-gate 		(void) strcat(sptr, " ");
21510Sstevel@tonic-gate 		(void) strcat(sptr, pppd);
21520Sstevel@tonic-gate 
21530Sstevel@tonic-gate 		/* Parse out into arguments */
21540Sstevel@tonic-gate 		cpp = args;
21550Sstevel@tonic-gate 		spv = sptr;
21560Sstevel@tonic-gate 		while (cpp < args + MAXARGS - 1) {
21570Sstevel@tonic-gate 			retv = getkeyword(NULL, keybuf, sizeof (keybuf), sgetc,
21580Sstevel@tonic-gate 			    (void *)&spv, 1);
21590Sstevel@tonic-gate 			if (retv != 1)
21600Sstevel@tonic-gate 				*cpp++ = strsave(keybuf);
21610Sstevel@tonic-gate 			if (retv != 0)
21620Sstevel@tonic-gate 				break;
21630Sstevel@tonic-gate 		}
21640Sstevel@tonic-gate 		*cpp = NULL;
21650Sstevel@tonic-gate 		if (cpp == args)
21660Sstevel@tonic-gate 			goto bail_out;
21670Sstevel@tonic-gate 
21680Sstevel@tonic-gate 		/*
21690Sstevel@tonic-gate 		 * Fix tunnel device on stdin/stdout and error file on
21700Sstevel@tonic-gate 		 * stderr.
21710Sstevel@tonic-gate 		 */
21720Sstevel@tonic-gate 		if (newtun != 0 && dup2(newtun, 0) < 0)
21730Sstevel@tonic-gate 			goto bail_out;
21740Sstevel@tonic-gate 		if (newtun != 1 && dup2(newtun, 1) < 0)
21750Sstevel@tonic-gate 			goto bail_out;
21760Sstevel@tonic-gate 		if (newtun > 1)
21770Sstevel@tonic-gate 			(void) close(newtun);
21780Sstevel@tonic-gate 		if (tunfd > 1)
21790Sstevel@tonic-gate 			(void) close(tunfd);
21800Sstevel@tonic-gate 		(void) close(2);
21810Sstevel@tonic-gate 		(void) open("/etc/ppp/pppoe-errors", O_WRONLY | O_APPEND |
21820Sstevel@tonic-gate 		    O_CREAT, 0600);
21830Sstevel@tonic-gate 
21840Sstevel@tonic-gate 		/*
21850Sstevel@tonic-gate 		 * Change GID first, for obvious reasons.  Note that
21860Sstevel@tonic-gate 		 * we log any problems to syslog, not the errors file.
21870Sstevel@tonic-gate 		 * The errors file is intended for problems in the
21880Sstevel@tonic-gate 		 * exec'd program.
21890Sstevel@tonic-gate 		 */
21900Sstevel@tonic-gate 		if ((sep->se_flags & SEF_GIDSET) &&
21910Sstevel@tonic-gate 		    setgid(sep->se_gid) == -1) {
21920Sstevel@tonic-gate 			cp = mystrerror(errno);
21930Sstevel@tonic-gate 			reopen_log();
21940Sstevel@tonic-gate 			logerr("setgid(%d): %s", sep->se_gid, cp);
21950Sstevel@tonic-gate 			goto logged;
21960Sstevel@tonic-gate 		}
21970Sstevel@tonic-gate 		if ((sep->se_flags & SEF_UIDSET) &&
21980Sstevel@tonic-gate 		    setuid(sep->se_uid) == -1) {
21990Sstevel@tonic-gate 			cp = mystrerror(errno);
22000Sstevel@tonic-gate 			reopen_log();
22010Sstevel@tonic-gate 			logerr("setuid(%d): %s", sep->se_uid, cp);
22020Sstevel@tonic-gate 			goto logged;
22030Sstevel@tonic-gate 		}
22040Sstevel@tonic-gate 
22050Sstevel@tonic-gate 		/* Run pppd */
22060Sstevel@tonic-gate 		path = args[0];
22070Sstevel@tonic-gate 		cp = strrchr(args[0], '/');
22080Sstevel@tonic-gate 		if (cp != NULL && cp[1] != '\0')
22090Sstevel@tonic-gate 			args[0] = cp+1;
22100Sstevel@tonic-gate 		errno = 0;
22110Sstevel@tonic-gate 		(void) execv(path, (char * const *)args);
22120Sstevel@tonic-gate 		newtun = 0;
22130Sstevel@tonic-gate 
22140Sstevel@tonic-gate 		/*
22150Sstevel@tonic-gate 		 * Exec failure; attempt to log the problem and send a
22160Sstevel@tonic-gate 		 * PADT to the client so that he knows the session
22170Sstevel@tonic-gate 		 * went south.
22180Sstevel@tonic-gate 		 */
22190Sstevel@tonic-gate 	bail_out:
22200Sstevel@tonic-gate 		cp = mystrerror(errno);
22210Sstevel@tonic-gate 		reopen_log();
22220Sstevel@tonic-gate 		logerr("\"%s\": %s", (sptr == NULL ? path : sptr), cp);
22230Sstevel@tonic-gate 	logged:
22240Sstevel@tonic-gate 		poep = poe_mkheader(pkt_output, POECODE_PADT, ptp.ptp_lsessid);
22250Sstevel@tonic-gate 		poep->poep_session_id = htons(ptp.ptp_lsessid);
22260Sstevel@tonic-gate 		(void) poe_add_str(poep, POETT_SYSERR, cp);
22270Sstevel@tonic-gate 		(void) sleep(1);
22280Sstevel@tonic-gate 		ctrl.len = sizeof (*ptc);
22290Sstevel@tonic-gate 		ctrl.buf = (caddr_t)ptc;
22300Sstevel@tonic-gate 		data.len = poe_length(poep) + sizeof (*poep);
22310Sstevel@tonic-gate 		data.buf = (caddr_t)poep;
22320Sstevel@tonic-gate 		if (putmsg(newtun, &ctrl, &data, 0) < 0) {
22330Sstevel@tonic-gate 			logerr("putmsg %s: %s", ptc->ptc_name,
22340Sstevel@tonic-gate 			    mystrerror(errno));
22350Sstevel@tonic-gate 		}
22360Sstevel@tonic-gate 		exit(1);
22370Sstevel@tonic-gate 	}
22380Sstevel@tonic-gate 
22390Sstevel@tonic-gate 	(void) close(newtun);
22400Sstevel@tonic-gate 
22410Sstevel@tonic-gate 	/* Give session ID to client in reply. */
22420Sstevel@tonic-gate 	poep->poep_session_id = htons(ptp.ptp_lsessid);
22430Sstevel@tonic-gate 	return (1);
22440Sstevel@tonic-gate 
22450Sstevel@tonic-gate syserr:
22460Sstevel@tonic-gate 	/* Peer doesn't know session ID yet; hope for the best. */
22470Sstevel@tonic-gate 	retv = errno;
22480Sstevel@tonic-gate 	if (newtun >= 0)
22490Sstevel@tonic-gate 		(void) close(newtun);
22500Sstevel@tonic-gate 	(void) poe_add_str(poep, POETT_SYSERR, mystrerror(retv));
22510Sstevel@tonic-gate 	return (0);
22520Sstevel@tonic-gate }
22530Sstevel@tonic-gate 
22540Sstevel@tonic-gate /*
22550Sstevel@tonic-gate  * This is pretty awful -- it uses recursion to print a simple list.
22560Sstevel@tonic-gate  * It's just for debug, though, and does a reasonable job of printing
22570Sstevel@tonic-gate  * the filters in the right order.
22580Sstevel@tonic-gate  */
22590Sstevel@tonic-gate static void
print_filter_list(FILE * fp,struct filter_entry * fep)22600Sstevel@tonic-gate print_filter_list(FILE *fp, struct filter_entry *fep)
22610Sstevel@tonic-gate {
22620Sstevel@tonic-gate 	if (fep->fe_prev != NULL)
22630Sstevel@tonic-gate 		print_filter_list(fp, fep->fe_prev);
22640Sstevel@tonic-gate 	(void) fprintf(fp, "\t\t    MAC %s", ehost2(&fep->fe_mac));
22650Sstevel@tonic-gate 	(void) fprintf(fp, ", mask %s%s\n", ehost2(&fep->fe_mask),
22660Sstevel@tonic-gate 	    (fep->fe_isexcept ? ", except" : ""));
22670Sstevel@tonic-gate }
22680Sstevel@tonic-gate 
22690Sstevel@tonic-gate /*
22700Sstevel@tonic-gate  * Write summary of parsed configuration data to given file.
22710Sstevel@tonic-gate  */
22720Sstevel@tonic-gate void
dump_configuration(FILE * fp)22730Sstevel@tonic-gate dump_configuration(FILE *fp)
22740Sstevel@tonic-gate {
22750Sstevel@tonic-gate 	const struct device_entry *dep;
22760Sstevel@tonic-gate 	const struct service_entry *sep, **sepp;
22770Sstevel@tonic-gate 	struct per_file *pfp;
22780Sstevel@tonic-gate 	int i, j;
22790Sstevel@tonic-gate 
22800Sstevel@tonic-gate 	(void) fprintf(fp, "Will%s respond to wildcard queries.\n",
22810Sstevel@tonic-gate 	    (glob_svc.se_flags & SEF_NOWILD) ? " not" : "");
22820Sstevel@tonic-gate 	(void) fprintf(fp,
22830Sstevel@tonic-gate 	    "Global debug level %d, log to %s; current level %d\n",
22840Sstevel@tonic-gate 	    glob_svc.se_debug,
22850Sstevel@tonic-gate 	    ((glob_svc.se_log == NULL || *glob_svc.se_log == '\0') ?
2286*9751Sjames.d.carlson@sun.com 	    "syslog" : glob_svc.se_log),
22870Sstevel@tonic-gate 	    log_level);
22880Sstevel@tonic-gate 	if (cur_options == NULL) {
22890Sstevel@tonic-gate 		(void) fprintf(fp, "No current configuration.\n");
22900Sstevel@tonic-gate 		return;
22910Sstevel@tonic-gate 	}
22920Sstevel@tonic-gate 	(void) fprintf(fp, "Current configuration:\n");
22930Sstevel@tonic-gate 	(void) fprintf(fp, "    %d device(s):\n", cur_options->os_ndevices);
22940Sstevel@tonic-gate 	dep = cur_options->os_devices;
22950Sstevel@tonic-gate 	for (i = 0; i < cur_options->os_ndevices; i++, dep++) {
22960Sstevel@tonic-gate 		(void) fprintf(fp, "\t%s: %d service(s):\n",
22970Sstevel@tonic-gate 		    dep->de_name, dep->de_nservices);
22980Sstevel@tonic-gate 		sepp = dep->de_services;
22990Sstevel@tonic-gate 		for (j = 0; j < dep->de_nservices; j++, sepp++) {
23000Sstevel@tonic-gate 			sep = *sepp;
23010Sstevel@tonic-gate 			(void) fprintf(fp, "\t    %s: debug level %d",
23020Sstevel@tonic-gate 			    sep->se_name, sep->se_debug);
23030Sstevel@tonic-gate 			if (sep->se_flags & SEF_UIDSET)
23044321Scasper 				(void) fprintf(fp, ", UID %u", sep->se_uid);
23050Sstevel@tonic-gate 			if (sep->se_flags & SEF_GIDSET)
23064321Scasper 				(void) fprintf(fp, ", GID %u", sep->se_gid);
23070Sstevel@tonic-gate 			if (sep->se_flags & SEF_WILD)
23080Sstevel@tonic-gate 				(void) fprintf(fp, ", wildcard");
23090Sstevel@tonic-gate 			else if (sep->se_flags & SEF_NOWILD)
23100Sstevel@tonic-gate 				(void) fprintf(fp, ", nowildcard");
23110Sstevel@tonic-gate 			else
23120Sstevel@tonic-gate 				(void) fprintf(fp, ", wildcard (default)");
23130Sstevel@tonic-gate 			(void) putc('\n', fp);
23140Sstevel@tonic-gate 			if (sep->se_server != NULL)
23150Sstevel@tonic-gate 				(void) fprintf(fp, "\t\tserver \"%s\"\n",
23160Sstevel@tonic-gate 				    sep->se_server);
23170Sstevel@tonic-gate 			if (sep->se_pppd != NULL)
23180Sstevel@tonic-gate 				(void) fprintf(fp, "\t\tpppd \"%s\"\n",
23190Sstevel@tonic-gate 				    sep->se_pppd);
23200Sstevel@tonic-gate 			if (sep->se_path != NULL)
23210Sstevel@tonic-gate 				(void) fprintf(fp, "\t\tpath \"%s\"\n",
23220Sstevel@tonic-gate 				    sep->se_path);
23230Sstevel@tonic-gate 			if (sep->se_extra != NULL)
23240Sstevel@tonic-gate 				(void) fprintf(fp, "\t\textra \"%s\"\n",
23250Sstevel@tonic-gate 				    sep->se_extra);
23260Sstevel@tonic-gate 			if (sep->se_log != NULL)
23270Sstevel@tonic-gate 				(void) fprintf(fp, "\t\tlog \"%s\"\n",
23280Sstevel@tonic-gate 				    sep->se_log);
23290Sstevel@tonic-gate 			if (sep->se_flist != NULL) {
23300Sstevel@tonic-gate 				(void) fprintf(fp, "\t\tfilter list:\n");
23310Sstevel@tonic-gate 				print_filter_list(fp, sep->se_flist);
23320Sstevel@tonic-gate 			}
23330Sstevel@tonic-gate 		}
23340Sstevel@tonic-gate 	}
23350Sstevel@tonic-gate 	(void) fprintf(fp, "\nConfiguration read from:\n");
23360Sstevel@tonic-gate 	for (pfp = cur_options->os_pfjunk; pfp != NULL; pfp = pfp->pf_prev) {
23370Sstevel@tonic-gate 		(void) fprintf(fp, "    %s: %d service(s)\n", pfp->pf_name,
23380Sstevel@tonic-gate 		    pfp->pf_nsvc);
23390Sstevel@tonic-gate 	}
23400Sstevel@tonic-gate }
2341