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