Home | History | Annotate | Download | only in pppoe
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * PPPoE Server-mode daemon option parsing.
     24  *
     25  * Copyright 2000-2002 Sun Microsystems, Inc.  All rights reserved.
     26  * Use is subject to license terms.
     27  */
     28 
     29 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     30 
     31 #include <stdio.h>
     32 #include <stdlib.h>
     33 #include <unistd.h>
     34 #include <assert.h>
     35 #include <ctype.h>
     36 #include <string.h>
     37 #include <sys/types.h>
     38 #include <fcntl.h>
     39 #include <pwd.h>
     40 #include <grp.h>
     41 #include <errno.h>
     42 #include <netdb.h>
     43 #include <stropts.h>
     44 #include <sys/stat.h>
     45 #include <sys/socket.h>
     46 #include <net/if.h>
     47 #include <netinet/in.h>
     48 #include <netinet/if_ether.h>
     49 #include <net/sppptun.h>
     50 
     51 #include "common.h"
     52 #include "logging.h"
     53 
     54 #define	MAX_KEYWORD	4096	/* Maximum token length */
     55 #define	MAX_NEST	32	/* Maximum ${$sub} nesting */
     56 #define	MAXARGS		256	/* Maximum number of pppd arguments */
     57 
     58 /*
     59  * Client filter entry.  These are linked in *reverse* order so that
     60  * the DAG created by file inclusion nesting works as expected.  Since
     61  * the administrator who wrote the configuration expects "first
     62  * match," this means that tests against the filter list must actually
     63  * use "last match."
     64  */
     65 struct filter_entry {
     66 	struct filter_entry *fe_prev;	/* Previous filter in list */
     67 	struct ether_addr fe_mac;	/* MAC address */
     68 	struct ether_addr fe_mask;	/* Mask for above address test */
     69 	uchar_t fe_isexcept;	/* invert sense; exclude matching clients */
     70 	uchar_t fe_prevcopy;		/* fe_prev points to copied list */
     71 	uchar_t fe_unused[2];		/* padding */
     72 };
     73 
     74 /*
     75  * Note: I would like to make the strings and filters here const, but
     76  * I can't because they have to be passed to free() during parsing.  I
     77  * could work around this with offsetof() or data copies, but it's not
     78  * worth the effort.
     79  */
     80 struct service_entry {
     81 	const char *se_name;		/* Name of service */
     82 	struct filter_entry *se_flist;	/* Pointer to list of client filters */
     83 	uint_t se_flags;		/* SEF_* flags (below) */
     84 	int se_debug;			/* Debug level (0=nodebug) */
     85 	char *se_server;		/* Server (AC) name */
     86 	char *se_pppd;			/* Options for pppd */
     87 	char *se_path;			/* Path to pppd executable */
     88 	char *se_extra;			/* Extra options */
     89 	char *se_log;			/* Log file */
     90 	uid_t se_uid;			/* User ID */
     91 	gid_t se_gid;			/* Group ID */
     92 };
     93 
     94 #define	SEF_WILD	0x00000001	/* Offer in wildcard reply */
     95 #define	SEF_NOWILD	0x00000002	/* Don't offer in wildcard */
     96 #define	SEF_CFLIST	0x00000004	/* se_flist copied from global */
     97 #define	SEF_CSERVER	0x00000008	/* se_server copied from global */
     98 #define	SEF_CPPPD	0x00000010	/* se_pppd copied from global */
     99 #define	SEF_CPATH	0x00000020	/* se_path copied from global */
    100 #define	SEF_CEXTRA	0x00000040	/* se_extra copied from global */
    101 #define	SEF_CLOG	0x00000080	/* se_log copied from global */
    102 #define	SEF_UIDSET	0x00000100	/* se_uid has been set */
    103 #define	SEF_GIDSET	0x00000200	/* se_gid has been set */
    104 #define	SEF_DEBUGCLR	0x00000400	/* do not add se_debug from global */
    105 #define	SEF_CDEV	0x00000800	/* copied devs (parse only) */
    106 
    107 /*
    108  * One of these is allocated per lower-level stream (device) that is
    109  * referenced by the configuration files.  The queries are received
    110  * per device, and this structure allows us to find all of the
    111  * services that correspond to that device.
    112  */
    113 struct device_entry {
    114 	const char *de_name;
    115 	const struct service_entry **de_services;
    116 	int de_nservices;
    117 };
    118 
    119 /*
    120  * This is the parsed configuration.  While a new configuration is
    121  * being read, this is kept around until the new configuration is
    122  * ready, and then it is discarded in one operation.  It has an array
    123  * of device entries (as above) -- one per referenced lower stream --
    124  * and a pointer to the allocated parser information.  The latter is
    125  * kept around because we reuse pointers rather than reallocating and
    126  * copying the data.  There are thus multiple aliases to the dynamic
    127  * data, and the "owner" (for purposes of freeing the storage) is
    128  * considered to be this 'junk' list.
    129  */
    130 struct option_state {
    131 	const struct device_entry *os_devices;
    132 	int os_ndevices;
    133 	struct per_file *os_pfjunk;	/* Kept for deallocation */
    134 	char **os_evjunk;		/* ditto */
    135 };
    136 
    137 /*
    138  * This is the root pointer to the current parsed options.
    139  * This cannot be const because it's passed to free() when reparsing
    140  * options.
    141  */
    142 static struct option_state *cur_options;
    143 
    144 /* Global settings for module-wide options. */
    145 static struct service_entry glob_svc;
    146 
    147 /*
    148  * *******************************************************************
    149  * Data structures generated during parsing.
    150  */
    151 
    152 /* List of device names attached to one service */
    153 struct device_list {
    154 	struct device_list *dl_next;
    155 	const char *dl_name;		/* Name of one device */
    156 };
    157 
    158 /* Entry for a single defined service. */
    159 struct service_list {
    160 	struct service_entry sl_entry;	/* Parsed service data */
    161 	struct service_list *sl_next;	/* Next service entry */
    162 	struct parse_state *sl_parse;	/* Back pointer to state */
    163 	struct device_list *sl_dev;	/* List of devices */
    164 	int sl_serial;			/* Serial number (conflict resolve) */
    165 };
    166 #define	SESERIAL(x)	((struct service_list *)&(x))->sl_serial
    167 #define	ISGLOBAL(x)	((x) == &(x)->sl_parse->ps_cfile->pf_global)
    168 
    169 /*
    170  * Structure allocated for each file opened.  File nesting is chained
    171  * in reverse order so that global option scoping works as expected.
    172  */
    173 struct per_file {
    174 	struct per_file *pf_prev;	/* Back chain */
    175 	struct service_list pf_global;	/* Global (default) service context */
    176 	struct service_list *pf_svc;	/* List of services */
    177 	struct service_list *pf_svc_last;
    178 	FILE *pf_input;			/* File for input */
    179 	const char *pf_name;		/* File name */
    180 	int pf_nsvc;			/* Count of services */
    181 };
    182 
    183 /* State of parser */
    184 enum key_state {
    185 	ksDefault, ksService, ksDevice, ksClient, ksClientE, ksServer,
    186 	ksPppd, ksFile, ksPath, ksExtra, ksLog, ksUser, ksGroup
    187 };
    188 
    189 /*
    190  * Global parser state.  There is one of these structures, and it
    191  * exists only while actively parsing configuration files.
    192  */
    193 struct parse_state {
    194 	enum key_state ps_state;	/* Parser state */
    195 	int ps_serial;			/* Service serial number */
    196 	struct per_file *ps_files;	/* Parsed files */
    197 	struct per_file *ps_cfile;	/* Current file */
    198 	struct service_list *ps_csvc;	/* Current service */
    199 	struct device_list *ps_star;	/* Wildcard device */
    200 	int ps_flags;			/* PSF_* below */
    201 	char **ps_evlist;		/* allocated environment variables */
    202 	int ps_evsize;			/* max length; for realloc */
    203 };
    204 
    205 #define	PSF_PERDEV	0x0001		/* In a per-device file */
    206 #define	PSF_SETLEVEL	0x0002		/* Set log level along the way */
    207 
    208 /* Should be in a library somewhere. */
    209 static char *
    210 strsave(const char *str)
    211 {
    212 	char *newstr;
    213 
    214 	if (str == NULL)
    215 		return (NULL);
    216 	newstr = (char *)malloc(strlen(str) + 1);
    217 	if (newstr != NULL)
    218 		(void) strcpy(newstr, str);
    219 	return (newstr);
    220 }
    221 
    222 /*
    223  * Stop defining current service and revert to global definition.
    224  * This resolves any implicit references to global options by copying
    225  * ("inheriting") from the current global state.
    226  */
    227 static void
    228 close_service(struct service_list *slp)
    229 {
    230 	struct parse_state *psp;
    231 	struct per_file *cfile;
    232 	struct service_entry *sep;
    233 	struct service_entry *sedefp;
    234 	struct filter_entry *fep;
    235 
    236 	assert(slp != NULL);
    237 	psp = slp->sl_parse;
    238 	cfile = psp->ps_cfile;
    239 
    240 	/* If no current file, then nothing to close. */
    241 	if (cfile == NULL)
    242 		return;
    243 
    244 	sep = &slp->sl_entry;
    245 
    246 	/*
    247 	 * Fix up filter pointers to make DAG.  First, locate
    248 	 * the end of the filter list.
    249 	 */
    250 	if (sep->se_flags & SEF_CFLIST) {
    251 		sep->se_flist = fep = NULL;
    252 	} else {
    253 		for (fep = sep->se_flist; fep != NULL; fep = fep->fe_prev)
    254 			if (fep->fe_prev == NULL || fep->fe_prevcopy) {
    255 				fep->fe_prev = NULL;
    256 				break;
    257 			}
    258 	}
    259 	if (slp == &cfile->pf_global) {
    260 		/*
    261 		 * If we're in a global context, then we're about to
    262 		 * open a new service, so it's time to fix up the
    263 		 * filter list so that it's usable as a reference.
    264 		 * Loop through files from which we were included, and
    265 		 * link up filters.  Note: closure may occur more than
    266 		 * once here.
    267 		 */
    268 		/* We don't inherit from ourselves. */
    269 		cfile = cfile->pf_prev;
    270 		while (cfile != NULL) {
    271 			if (fep == NULL) {
    272 				sep->se_flist = fep =
    273 				    cfile->pf_global.sl_entry.se_flist;
    274 				sep->se_flags |= SEF_CFLIST;
    275 			} else if (fep->fe_prev == NULL) {
    276 				fep->fe_prev =
    277 				    cfile->pf_global.sl_entry.se_flist;
    278 				fep->fe_prevcopy = 1;
    279 			}
    280 			cfile = cfile->pf_prev;
    281 		}
    282 	} else {
    283 		/*
    284 		 * Loop through default options in current and all
    285 		 * enclosing include files.  Inherit options.
    286 		 */
    287 		logdbg("service %s ends", slp->sl_entry.se_name);
    288 		while (cfile != NULL) {
    289 			/* Inherit from global service options. */
    290 			if (slp->sl_dev == NULL) {
    291 				slp->sl_dev = cfile->pf_global.sl_dev;
    292 				sep->se_flags |= SEF_CDEV;
    293 			}
    294 			sedefp = &cfile->pf_global.sl_entry;
    295 			if (fep == NULL) {
    296 				sep->se_flist = fep = sedefp->se_flist;
    297 				sep->se_flags |= SEF_CFLIST;
    298 			} else if (fep->fe_prev == NULL) {
    299 				fep->fe_prev = sedefp->se_flist;
    300 				fep->fe_prevcopy = 1;
    301 			}
    302 			if (sep->se_server == NULL) {
    303 				sep->se_server = sedefp->se_server;
    304 				sep->se_flags |= SEF_CSERVER;
    305 			}
    306 			if (sep->se_pppd == NULL) {
    307 				sep->se_pppd = sedefp->se_pppd;
    308 				sep->se_flags |= SEF_CPPPD;
    309 			}
    310 			if (sep->se_path == NULL) {
    311 				sep->se_path = sedefp->se_path;
    312 				sep->se_flags |= SEF_CPATH;
    313 			}
    314 			if (sep->se_extra == NULL) {
    315 				sep->se_extra = sedefp->se_extra;
    316 				sep->se_flags |= SEF_CEXTRA;
    317 			}
    318 			if (sep->se_log == NULL) {
    319 				sep->se_log = sedefp->se_log;
    320 				sep->se_flags |= SEF_CLOG;
    321 			}
    322 			if (!(sep->se_flags & SEF_UIDSET) &&
    323 			    (sedefp->se_flags & SEF_UIDSET)) {
    324 				sep->se_uid = sedefp->se_uid;
    325 				sep->se_flags |= SEF_UIDSET;
    326 			}
    327 			if (!(sep->se_flags & SEF_GIDSET) &&
    328 			    (sedefp->se_flags & SEF_GIDSET)) {
    329 				sep->se_gid = sedefp->se_gid;
    330 				sep->se_flags |= SEF_GIDSET;
    331 			}
    332 			if (!(sep->se_flags & (SEF_WILD|SEF_NOWILD)))
    333 				sep->se_flags |= sedefp->se_flags &
    334 				    (SEF_WILD|SEF_NOWILD);
    335 			if (!(sep->se_flags & SEF_DEBUGCLR)) {
    336 				sep->se_debug += sedefp->se_debug;
    337 				sep->se_flags |= sedefp->se_flags &
    338 				    SEF_DEBUGCLR;
    339 			}
    340 			cfile = cfile->pf_prev;
    341 		}
    342 	}
    343 	/* Revert to global definitions. */
    344 	psp->ps_csvc = &psp->ps_cfile->pf_global;
    345 }
    346 
    347 /* Discard a dynamic device list */
    348 static void
    349 free_device_list(struct device_list *dlp)
    350 {
    351 	struct device_list *dln;
    352 
    353 	while (dlp != NULL) {
    354 		dln = dlp->dl_next;
    355 		free(dlp);
    356 		dlp = dln;
    357 	}
    358 }
    359 
    360 /*
    361  * Handle "service <name>" -- finish up previous service definition
    362  * (if any) by copying from global state where necessary, and start
    363  * defining new service.
    364  */
    365 static int
    366 set_service(struct service_list *slp, const char *str)
    367 {
    368 	struct parse_state *psp;
    369 	struct per_file *cfile;
    370 
    371 	/* Finish current service */
    372 	close_service(slp);
    373 
    374 	/* Start new service */
    375 	psp = slp->sl_parse;
    376 	slp = (struct service_list *)calloc(sizeof (*slp) + strlen(str) + 1,
    377 	    1);
    378 	if (slp == NULL) {
    379 		logerr("no memory for service \"%s\"", str);
    380 		return (-1);
    381 	}
    382 
    383 	/* Add to end of list */
    384 	cfile = psp->ps_cfile;
    385 	if (cfile->pf_svc_last == NULL)
    386 		cfile->pf_svc = slp;
    387 	else
    388 		cfile->pf_svc_last->sl_next = slp;
    389 	cfile->pf_svc_last = slp;
    390 	cfile->pf_nsvc++;
    391 
    392 	/* Fill in initial service entry */
    393 	slp->sl_entry.se_name = (const char *)(slp+1);
    394 	(void) strcpy((char *)(slp+1), str);
    395 	logdbg("service %s begins", slp->sl_entry.se_name);
    396 	slp->sl_serial = psp->ps_serial++;
    397 	slp->sl_parse = psp;
    398 
    399 	/* This is now the current service that we're defining. */
    400 	psp->ps_csvc = slp;
    401 	return (0);
    402 }
    403 
    404 /*
    405  * Handle both "wildcard" and "nowildcard" options.
    406  */
    407 static int
    408 set_wildcard(struct service_list *slp, const char *str)
    409 {
    410 	/* Allow global context to switch back and forth without error. */
    411 	if (!ISGLOBAL(slp) &&
    412 	    (slp->sl_entry.se_flags & (SEF_WILD|SEF_NOWILD))) {
    413 		logdbg("%s: extra \"%s\" ignored",
    414 		    slp->sl_parse->ps_cfile->pf_name, str);
    415 		return (0);
    416 	}
    417 	slp->sl_entry.se_flags =
    418 	    (slp->sl_entry.se_flags & ~(SEF_WILD|SEF_NOWILD)) |
    419 	    (*str == 'n' ? SEF_NOWILD : SEF_WILD);
    420 	return (0);
    421 }
    422 
    423 /*
    424  * Handle "debug" option.
    425  */
    426 /*ARGSUSED*/
    427 static int
    428 set_debug(struct service_list *slp, const char *str)
    429 {
    430 	slp->sl_entry.se_debug++;
    431 	if (ISGLOBAL(slp) && (slp->sl_parse->ps_flags & PSF_SETLEVEL)) {
    432 		log_level = slp->sl_entry.se_debug;
    433 	}
    434 	return (0);
    435 }
    436 
    437 /*
    438  * Handle "nodebug" option.
    439  */
    440 /*ARGSUSED*/
    441 static int
    442 set_nodebug(struct service_list *slp, const char *str)
    443 {
    444 	slp->sl_entry.se_flags |= SEF_DEBUGCLR;
    445 	slp->sl_entry.se_debug = 0;
    446 	if (ISGLOBAL(slp) && (slp->sl_parse->ps_flags & PSF_SETLEVEL)) {
    447 		log_level = slp->sl_entry.se_debug;
    448 	}
    449 	return (0);
    450 }
    451 
    452 /*
    453  * Handle all plain string options; "server", "pppd", "path", "extra",
    454  * and "log".
    455  */
    456 static int
    457 set_string(struct service_list *slp, const char *str)
    458 {
    459 	char **cpp;
    460 
    461 	assert(!(slp->sl_entry.se_flags &
    462 	    (SEF_CSERVER|SEF_CPPPD|SEF_CPATH|SEF_CEXTRA|SEF_CLOG)));
    463 	switch (slp->sl_parse->ps_state) {
    464 	case ksServer:
    465 		cpp = &slp->sl_entry.se_server;
    466 		break;
    467 	case ksPppd:
    468 		cpp = &slp->sl_entry.se_pppd;
    469 		break;
    470 	case ksPath:
    471 		cpp = &slp->sl_entry.se_path;
    472 		break;
    473 	case ksExtra:
    474 		cpp = &slp->sl_entry.se_extra;
    475 		break;
    476 	case ksLog:
    477 		cpp = &slp->sl_entry.se_log;
    478 		break;
    479 	default:
    480 		assert(0);
    481 		return (-1);
    482 	}
    483 	if (*cpp != NULL)
    484 		free(*cpp);
    485 	*cpp = strsave(str);
    486 	return (0);
    487 }
    488 
    489 /*
    490  * Handle "file <name>" option.  Close out current service (if any)
    491  * and begin parsing from new file.
    492  */
    493 static int
    494 set_file(struct service_list *slp, const char *str)
    495 {
    496 	FILE *fp;
    497 	struct per_file *pfp;
    498 	struct parse_state *psp;
    499 
    500 	close_service(slp);
    501 
    502 	if ((fp = fopen(str, "r")) == NULL) {
    503 		logwarn("%s: %s: %s", slp->sl_parse->ps_cfile->pf_name, str,
    504 		    mystrerror(errno));
    505 		return (-1);
    506 	}
    507 	pfp = (struct per_file *)calloc(sizeof (*pfp) + strlen(str) + 1, 1);
    508 	if (pfp == NULL) {
    509 		logerr("no memory for parsing file %s", str);
    510 		(void) fclose(fp);
    511 		return (-1);
    512 	}
    513 	logdbg("config file %s open", str);
    514 
    515 	/* Fill in new file structure. */
    516 	pfp->pf_name = (const char *)(pfp+1);
    517 	(void) strcpy((char *)(pfp+1), str);
    518 	pfp->pf_input = fp;
    519 	psp = slp->sl_parse;
    520 	pfp->pf_prev = psp->ps_cfile;
    521 	psp->ps_cfile = pfp;
    522 
    523 	/* Start off in global context for this file. */
    524 	psp->ps_csvc = &pfp->pf_global;
    525 	pfp->pf_global.sl_parse = psp;
    526 	pfp->pf_global.sl_entry.se_name = "<global>";
    527 	return (0);
    528 }
    529 
    530 /*
    531  * Handle "device <list>" option.
    532  */
    533 static int
    534 set_device(struct service_list *slp, const char *str)
    535 {
    536 	struct parse_state *psp = slp->sl_parse;
    537 	struct device_list *dlp;
    538 	struct device_list *dln;
    539 	struct device_list **dlpp;
    540 	const char *cp;
    541 	int len;
    542 
    543 	/* Can't use this option in the per-device files. */
    544 	if (psp->ps_flags & PSF_PERDEV) {
    545 		logerr("\"device %s\" ignored in %s", str,
    546 		    psp->ps_cfile->pf_name);
    547 		return (0);
    548 	}
    549 
    550 	if (strcmp(str, "*") == 0 || strcmp(str, "all") == 0) {
    551 		if (!(slp->sl_entry.se_flags & SEF_CDEV))
    552 			free_device_list(slp->sl_dev);
    553 		slp->sl_dev = psp->ps_star;
    554 		slp->sl_entry.se_flags |= SEF_CDEV;
    555 	} else {
    556 		dlpp = &dlp;
    557 		for (;;) {
    558 			while (isspace(*str) || *str == ',')
    559 				str++;
    560 			if (*str == '\0')
    561 				break;
    562 			cp = str;
    563 			while (*str != '\0' && !isspace(*str) && *str != ',')
    564 				str++;
    565 			len = str - cp;
    566 			if ((len == 1 && *cp == '*') ||
    567 			    (len == 3 && strncmp(cp, "all", 3) == 0)) {
    568 				logerr("%s: cannot use %.*s in device list",
    569 				    psp->ps_cfile->pf_name, len, cp);
    570 				continue;
    571 			}
    572 			dln = (struct device_list *)malloc(sizeof (*dln) +
    573 			    len + 1);
    574 			if (dln == NULL) {
    575 				logerr("no memory for device name");
    576 				break;
    577 			}
    578 			dln->dl_name = (const char *)(dln + 1);
    579 			/* Cannot use strcpy because cp isn't terminated. */
    580 			(void) memcpy(dln + 1, cp, len);
    581 			((char *)(dln + 1))[len] = '\0';
    582 			logdbg("%s: device %s", psp->ps_cfile->pf_name,
    583 			    dln->dl_name);
    584 			*dlpp = dln;
    585 			dlpp = &dln->dl_next;
    586 		}
    587 		*dlpp = NULL;
    588 
    589 		dlpp = &slp->sl_dev;
    590 		if (!(slp->sl_entry.se_flags & SEF_CDEV))
    591 			while (*dlpp != NULL)
    592 				dlpp = &(*dlpp)->dl_next;
    593 		*dlpp = dlp;
    594 		slp->sl_entry.se_flags &= ~SEF_CDEV;
    595 	}
    596 
    597 	return (0);
    598 }
    599 
    600 /*
    601  * Handle <list> portion of "client [except] <list>" option.  Attach
    602  * to list of filters in reverse order.
    603  */
    604 static int
    605 set_client(struct service_list *slp, const char *str)
    606 {
    607 	struct parse_state *psp = slp->sl_parse;
    608 	struct filter_entry *fep;
    609 	struct filter_entry *fen;
    610 	const char *cp;
    611 	int len;
    612 	char hbuf[MAXHOSTNAMELEN];
    613 	struct ether_addr ea;
    614 	struct ether_addr mask;
    615 	uchar_t *ucp;
    616 	uchar_t *mcp;
    617 
    618 	/* Head of list. */
    619 	fep = slp->sl_entry.se_flist;
    620 	for (;;) {
    621 		while (isspace(*str) || *str == ',')
    622 			str++;
    623 		if (*str == '\0')
    624 			break;
    625 		cp = str;
    626 		while (*str != '\0' && !isspace(*str) && *str != ',')
    627 			str++;
    628 		len = str - cp;
    629 		(void) memcpy(hbuf, cp, len);
    630 		hbuf[len] = '\0';
    631 		mcp = mask.ether_addr_octet;
    632 		mcp[0] = mcp[1] = mcp[2] = mcp[3] = mcp[4] = mcp[5] = 0xFF;
    633 		if (ether_hostton(hbuf, &ea) != 0) {
    634 			ucp = ea.ether_addr_octet;
    635 			while (cp < str) {
    636 				if (ucp >= ea.ether_addr_octet + sizeof (ea))
    637 					break;
    638 				if (*cp == '*') {
    639 					*mcp++ = *ucp++ = 0;
    640 					cp++;
    641 				} else {
    642 					if (!isxdigit(*cp))
    643 						break;
    644 					*ucp = hexdecode(*cp++);
    645 					if (cp < str && isxdigit(*cp)) {
    646 						*ucp = (*ucp << 4) |
    647 						    hexdecode(*cp++);
    648 					}
    649 					ucp++;
    650 					*mcp++ = 0xFF;
    651 				}
    652 				if (cp < str) {
    653 					if (*cp != ':' || cp + 1 == str)
    654 						break;
    655 					cp++;
    656 				}
    657 			}
    658 			if (cp < str) {
    659 				logerr("%s: illegal Ethernet address %.*s",
    660 				    psp->ps_cfile->pf_name, len, cp);
    661 				continue;
    662 			}
    663 		}
    664 		fen = (struct filter_entry *)malloc(sizeof (*fen));
    665 		if (fen == NULL) {
    666 			logerr("unable to allocate memory for filter");
    667 			break;
    668 		}
    669 		fen->fe_isexcept = psp->ps_state == ksClientE;
    670 		fen->fe_prevcopy = 0;
    671 		(void) memcpy(&fen->fe_mac, &ea, sizeof (fen->fe_mac));
    672 		(void) memcpy(&fen->fe_mask, &mask, sizeof (fen->fe_mask));
    673 		fen->fe_prev = fep;
    674 		fep = fen;
    675 	}
    676 	slp->sl_entry.se_flist = fep;
    677 	return (0);
    678 }
    679 
    680 /*
    681  * Handle "user <name>" option.
    682  */
    683 static int
    684 set_user(struct service_list *slp, const char *str)
    685 {
    686 	struct passwd *pw;
    687 	char *cp;
    688 	uid_t myuid, uid;
    689 
    690 	if ((pw = getpwnam(str)) == NULL) {
    691 		uid = (uid_t)strtol(str, &cp, 0);
    692 		if (str == cp || *cp != '\0') {
    693 			logerr("%s:  bad user name \"%s\"",
    694 			    slp->sl_parse->ps_cfile->pf_name, str);
    695 			return (0);
    696 		}
    697 	} else {
    698 		uid = pw->pw_uid;
    699 	}
    700 	slp->sl_entry.se_uid = uid;
    701 	myuid = getuid();
    702 	if (myuid != 0) {
    703 		if (myuid == uid)
    704 			return (0);
    705 		logdbg("%s:  not root; ignoring attempt to set UID %d (%s)",
    706 		    slp->sl_parse->ps_cfile->pf_name, uid, str);
    707 		return (0);
    708 	}
    709 	slp->sl_entry.se_flags |= SEF_UIDSET;
    710 	return (0);
    711 }
    712 
    713 /*
    714  * Handle "group <name>" option.
    715  */
    716 static int
    717 set_group(struct service_list *slp, const char *str)
    718 {
    719 	struct group *gr;
    720 	char *cp;
    721 	gid_t gid;
    722 
    723 	if ((gr = getgrnam(str)) == NULL) {
    724 		gid = (gid_t)strtol(str, &cp, 0);
    725 		if (str == cp || *cp != '\0') {
    726 			logerr("%s:  bad group name \"%s\"",
    727 			    slp->sl_parse->ps_cfile->pf_name, str);
    728 			return (0);
    729 		}
    730 	} else {
    731 		gid = gr->gr_gid;
    732 	}
    733 	slp->sl_entry.se_gid = gid;
    734 	if (getuid() != 0) {
    735 		logdbg("%s:  not root; ignoring attempt to set GID %d (%s)",
    736 		    slp->sl_parse->ps_cfile->pf_name, gid, str);
    737 		return (0);
    738 	}
    739 	slp->sl_entry.se_flags |= SEF_GIDSET;
    740 	return (0);
    741 }
    742 
    743 /*
    744  * This state machine is used to parse the configuration files.  The
    745  * "kwe_in" is the state in which the keyword is recognized.  The
    746  * "kwe_out" is the state that the keyword produces.
    747  */
    748 struct kw_entry {
    749 	const char *kwe_word;
    750 	enum key_state kwe_in;
    751 	enum key_state kwe_out;
    752 	int (*kwe_func)(struct service_list *slp, const char *str);
    753 };
    754 
    755 static const struct kw_entry key_list[] = {
    756 	{ "service",	ksDefault,	ksService,	NULL },
    757 	{ "device",	ksDefault,	ksDevice,	NULL },
    758 	{ "client",	ksDefault,	ksClient,	NULL },
    759 	{ "except",	ksClient,	ksClientE,	NULL },
    760 	{ "wildcard",	ksDefault,	ksDefault,	set_wildcard },
    761 	{ "nowildcard",	ksDefault,	ksDefault,	set_wildcard },
    762 	{ "server",	ksDefault,	ksServer,	NULL },
    763 	{ "pppd",	ksDefault,	ksPppd,		NULL },
    764 	{ "debug",	ksDefault,	ksDefault,	set_debug },
    765 	{ "nodebug",	ksDefault,	ksDefault,	set_nodebug },
    766 	{ "file",	ksDefault,	ksFile,		NULL },
    767 	{ "path",	ksDefault,	ksPath,		NULL },
    768 	{ "extra",	ksDefault,	ksExtra,	NULL },
    769 	{ "log",	ksDefault,	ksLog,		NULL },
    770 	{ "user",	ksDefault,	ksUser,		NULL },
    771 	{ "group",	ksDefault,	ksGroup,	NULL },
    772 	/* Wildcards only past this point. */
    773 	{ "",		ksService,	ksDefault,	set_service },
    774 	{ "",		ksDevice,	ksDefault,	set_device },
    775 	{ "",		ksClient,	ksDefault,	set_client },
    776 	{ "",		ksClientE,	ksDefault,	set_client },
    777 	{ "",		ksServer,	ksDefault,	set_string },
    778 	{ "",		ksPppd,		ksDefault,	set_string },
    779 	{ "",		ksFile,		ksDefault,	set_file },
    780 	{ "",		ksPath,		ksDefault,	set_string },
    781 	{ "",		ksExtra,	ksDefault,	set_string },
    782 	{ "",		ksLog,		ksDefault,	set_string },
    783 	{ "",		ksUser,		ksDefault,	set_user },
    784 	{ "",		ksGroup,	ksDefault,	set_group },
    785 	{ NULL, ksDefault, ksDefault, NULL }
    786 };
    787 
    788 /*
    789  * Produce a string for the keyword that would have gotten us into the
    790  * current state.
    791  */
    792 static const char *
    793 after_key(enum key_state kstate)
    794 {
    795 	const struct kw_entry *kep;
    796 
    797 	for (kep = key_list; kep->kwe_word != NULL; kep++)
    798 		if (kep->kwe_out == kstate)
    799 			return (kep->kwe_word);
    800 	return ("nothing");
    801 }
    802 
    803 /*
    804  * Handle end-of-file processing -- close service, close file, revert
    805  * to global context in previous include file nest level.
    806  */
    807 static void
    808 file_end(struct parse_state *psp)
    809 {
    810 	struct per_file *pfp;
    811 
    812 	/* Must not be in the middle of parsing a multi-word sequence now. */
    813 	if (psp->ps_state != ksDefault) {
    814 		logerr("%s ends with \"%s\"", psp->ps_cfile->pf_name,
    815 		    after_key(psp->ps_state));
    816 		psp->ps_state = ksDefault;
    817 	}
    818 	close_service(psp->ps_csvc);
    819 	if ((pfp = psp->ps_cfile) != NULL) {
    820 		/* Put this file on the list of finished files. */
    821 		psp->ps_cfile = pfp->pf_prev;
    822 		pfp->pf_prev = psp->ps_files;
    823 		psp->ps_files = pfp;
    824 		if (pfp->pf_input != NULL) {
    825 			logdbg("file %s closed", pfp->pf_name);
    826 			(void) fclose(pfp->pf_input);
    827 			pfp->pf_input = NULL;
    828 		}
    829 
    830 		/* Back up to previous file, if any, and set global context. */
    831 		if ((pfp = psp->ps_cfile) != NULL)
    832 			psp->ps_csvc = &pfp->pf_global;
    833 	}
    834 }
    835 
    836 /*
    837  * Dispatch a single keyword against the parser state machine or
    838  * handle an environment variable assignment.  The input is a string
    839  * containing the single word to be dispatched.
    840  */
    841 static int
    842 dispatch_keyword(struct parse_state *psp, const char *keybuf)
    843 {
    844 	const struct kw_entry *kep;
    845 	int retv;
    846 	char *cp;
    847 	char *env;
    848 	char **evlist;
    849 	int len;
    850 
    851 	retv = 0;
    852 	for (kep = key_list; kep->kwe_word != NULL; kep++) {
    853 		if (kep->kwe_in == psp->ps_state &&
    854 		    (*kep->kwe_word == '\0' ||
    855 			strcasecmp(kep->kwe_word, keybuf) == 0)) {
    856 			if (kep->kwe_func != NULL)
    857 				retv = (*kep->kwe_func)(psp->ps_csvc, keybuf);
    858 			psp->ps_state = kep->kwe_out;
    859 			return (retv);
    860 		}
    861 	}
    862 	if (strchr(keybuf, '=') != NULL) {
    863 		if ((cp = strsave(keybuf)) == NULL) {
    864 			logerr("no memory to save %s", keybuf);
    865 			return (0);
    866 		}
    867 		len = (strchr(cp, '=') - cp) + 1;
    868 		if ((evlist = psp->ps_evlist) == NULL) {
    869 			psp->ps_evlist = evlist =
    870 			    (char **)malloc(8 * sizeof (*evlist));
    871 			if (evlist == NULL) {
    872 				logerr("no memory for evlist");
    873 				free(cp);
    874 				return (0);
    875 			}
    876 			psp->ps_evsize = 8;
    877 			evlist[0] = evlist[1] = NULL;
    878 		} else {
    879 			while ((env = *evlist) != NULL) {
    880 				if (strncmp(cp, env, len) == 0)
    881 					break;
    882 				evlist++;
    883 			}
    884 			if (env == NULL &&
    885 			    evlist-psp->ps_evlist >= psp->ps_evsize-1) {
    886 				evlist = (char **)realloc(psp->ps_evlist,
    887 				    (psp->ps_evsize + 8) * sizeof (*evlist));
    888 				if (evlist == NULL) {
    889 					logerr("cannot realloc evlist to %d",
    890 					    psp->ps_evsize + 8);
    891 					free(cp);
    892 					return (0);
    893 				}
    894 				psp->ps_evlist = evlist;
    895 				evlist += psp->ps_evsize - 1;
    896 				psp->ps_evsize += 8;
    897 				evlist[1] = NULL;
    898 			}
    899 		}
    900 		logdbg("setenv \"%s\"", cp);
    901 		if (*evlist != NULL)
    902 			free(*evlist);
    903 		*evlist = cp;
    904 		return (0);
    905 	}
    906 	logerr("%s: unknown keyword '%s'", psp->ps_cfile->pf_name, keybuf);
    907 	return (-1);
    908 }
    909 
    910 /*
    911  * Modified version of standard getenv; looks in locally-stored
    912  * environment first.  This function exists because we need to be able
    913  * to revert to the original environment during a reread (SIGHUP), and
    914  * the putenv() function overwrites that environment.
    915  */
    916 static char *
    917 my_getenv(struct parse_state *psp, char *estr)
    918 {
    919 	char **evlist, *ent;
    920 	int elen;
    921 
    922 	if (psp != NULL && (evlist = psp->ps_evlist) != NULL) {
    923 		elen = strlen(estr);
    924 		while ((ent = *evlist++) != NULL) {
    925 			if (strncmp(ent, estr, elen) == 0 &&
    926 			    ent[elen] == '=')
    927 				return (ent + elen + 1);
    928 		}
    929 	}
    930 	return (getenv(estr));
    931 }
    932 
    933 /*
    934  * Expand an environment variable at the end of current buffer and
    935  * return pointer to next spot in buffer for character append.  psp
    936  * context may be null.
    937  */
    938 static char *
    939 env_replace(struct parse_state *psp, char *keybuf, char kwstate)
    940 {
    941 	char *cpe;
    942 	char *cp;
    943 
    944 	if ((cp = strrchr(keybuf, kwstate)) != NULL) {
    945 		if ((cpe = my_getenv(psp, cp + 1)) != NULL) {
    946 			*cp = '\0';
    947 			(void) strncat(cp, cpe,
    948 			    MAX_KEYWORD - (cp - keybuf) - 1);
    949 			keybuf[MAX_KEYWORD - 1] = '\0';
    950 			cp += strlen(cp);
    951 		} else {
    952 			logerr("unknown variable \"%s\"", cp + 1);
    953 		}
    954 	} else {
    955 		/* Should not occur. */
    956 		cp = keybuf + strlen(keybuf);
    957 	}
    958 	return (cp);
    959 }
    960 
    961 /*
    962  * Given a character-at-a-time input function, get a delimited keyword
    963  * from the input.  This function handles the usual escape sequences,
    964  * quoting, commenting, and environment variable expansion.
    965  *
    966  * The standard wordexp(3C) function isn't used here because the POSIX
    967  * definition is hard to use, and the Solaris implementation is
    968  * resource-intensive and insecure.  The "hard-to-use" part is that
    969  * wordexp expands only variables from the environment, and can't
    970  * handle an environment overlay.  Instead, the caller must use the
    971  * feeble putenv/getenv interface, and rewinding to the initial
    972  * environment without leaking storage is hard.  The Solaris
    973  * implementation invokes an undocumented extensions via
    974  * fork/exec("/bin/ksh -\005 %s") for every invocation, and gathers
    975  * the expanded result with pipe.  This makes it slow to execute and
    976  * exposes the string being expanded to users with access to "ps -f."
    977  *
    978  * psp may be null; it's used only for environment variable expansion.
    979  * Input "flag" is 1 to ignore EOL, '#', and '$'; 0 for normal file parsing.
    980  *
    981  * Returns:
    982  *	0 - keyword parsed.
    983  *	1 - end of file; no keyword.
    984  *	2 - end of file after this keyword.
    985  */
    986 static int
    987 getkeyword(struct parse_state *psp, char *keybuf, int keymax,
    988     int (*nextchr)(void *), void *arg, int flag)
    989 {
    990 	char varnest[MAX_NEST];
    991 	char *kbp;
    992 	char *vnp;
    993 	char chr;
    994 	int ichr;
    995 	char kwstate;
    996 	static const char escstr[] = "a\ab\bf\fn\nr\r";
    997 	const char *cp;
    998 
    999 	keymax--;	/* Account for trailing NUL byte */
   1000 
   1001 	kwstate = '\0';
   1002 	kbp = keybuf;
   1003 	vnp = varnest;
   1004 	for (;;) {
   1005 		ichr = (*nextchr)(arg);
   1006 		chr = (char)ichr;
   1007 	tryagain:
   1008 		switch (kwstate) {
   1009 		case '\\':	/* Start of unquoted escape sequence */
   1010 		case '|':	/* Start of escape sequence in double quotes */
   1011 		case '~':	/* Start of escape sequence in single quotes */
   1012 			/* Convert the character if we can. */
   1013 			if (chr == '\n')
   1014 				chr = '\0';
   1015 			else if (isalpha(chr) &&
   1016 			    (cp = strchr(escstr, chr)) != NULL)
   1017 				chr = cp[1];
   1018 			/* Revert to previous state */
   1019 			switch (kwstate) {
   1020 			case '\\':
   1021 				kwstate = 'A';
   1022 				break;
   1023 			case '|':
   1024 				kwstate = '"';
   1025 				break;
   1026 			case '~':
   1027 				kwstate = '\'';
   1028 				break;
   1029 			}
   1030 			break;
   1031 		case '"':	/* In double-quote string */
   1032 			if (!flag && chr == '$') {
   1033 				/* Handle variable expansion. */
   1034 				kwstate = '%';
   1035 				chr = '\0';
   1036 				break;
   1037 			}
   1038 				/* FALLTHROUGH */
   1039 		case '\'':	/* In single-quote string */
   1040 			if (chr == '\\') {
   1041 				/* Handle start of escape sequence */
   1042 				kwstate = kwstate == '"' ? '|' : '~';
   1043 				chr = '\0';
   1044 				break;
   1045 			}
   1046 			if (chr == kwstate) {
   1047 				/* End of quoted string; revert to normal */
   1048 				kwstate = 'A';
   1049 				chr = '\0';
   1050 			}
   1051 			break;
   1052 		case '$':	/* Start of unquoted variable name */
   1053 		case '%':	/* Start of variable name in quoted string */
   1054 			if (chr == '{') {
   1055 				/* Variable name is bracketed. */
   1056 				kwstate = chr =
   1057 				    kwstate == '$' ? '{' : '[';
   1058 				break;
   1059 			}
   1060 			*kbp++ = kwstate = kwstate == '$' ? '+' : '*';
   1061 				/* FALLTHROUGH */
   1062 		case '+':	/* Gathering unquoted variable name */
   1063 		case '*':	/* Gathering variable name in quoted string */
   1064 			if (chr == '$' &&
   1065 			    vnp < varnest + sizeof (varnest)) {
   1066 				*vnp++ = kwstate;
   1067 				kwstate = '$';
   1068 				chr = '\0';
   1069 				break;
   1070 			}
   1071 			if (!isalnum(chr) && chr != '_' &&
   1072 			    chr != '.' && chr != '-') {
   1073 				*kbp = '\0';
   1074 				kbp = env_replace(psp, keybuf, kwstate);
   1075 				if (vnp > varnest)
   1076 					kwstate = *--vnp;
   1077 				else
   1078 					kwstate = kwstate == '+' ?
   1079 					    'A' : '"';
   1080 				/* Go reinterpret in new context */
   1081 				goto tryagain;
   1082 			}
   1083 			break;
   1084 		case '{':	/* Gathering bracketed, unquoted var name */
   1085 		case '[':	/* Gathering bracketed, quoted var name */
   1086 			if (chr == '}') {
   1087 				*kbp = '\0';
   1088 				kbp = env_replace(psp, keybuf, kwstate);
   1089 				kwstate = kwstate == '{' ? 'A' : '"';
   1090 				chr = '\0';
   1091 			}
   1092 			break;
   1093 		case '#':	/* Comment before word state */
   1094 		case '@':	/* Comment after word state */
   1095 			if (chr == '\n' || chr == '\r' || ichr == EOF) {
   1096 				/* At end of line, revert to previous state */
   1097 				kwstate = kwstate == '#' ? '\0' : ' ';
   1098 				chr = '\0';
   1099 				break;
   1100 			}
   1101 			chr = '\0';
   1102 			break;
   1103 		case '\0':	/* Initial state; no word seen yet. */
   1104 			if (ichr == EOF || isspace(chr)) {
   1105 				chr = '\0';	/* Skip over leading spaces */
   1106 				break;
   1107 			}
   1108 			if (chr == '#') {
   1109 				kwstate = '#';
   1110 				chr = '\0';	/* Skip over comments */
   1111 				break;
   1112 			}
   1113 			/* Start of keyword seen. */
   1114 			kwstate = 'A';
   1115 			/* FALLTHROUGH */
   1116 		default:	/* Middle of keyword parsing. */
   1117 			if (ichr == EOF)
   1118 				break;
   1119 			if (isspace(chr)) {	/* Space terminates word */
   1120 				kwstate = ' ';
   1121 				break;
   1122 			}
   1123 			if (chr == '"' || chr == '\'' || chr == '\\') {
   1124 				kwstate = chr;	/* Begin quote or escape */
   1125 				chr = '\0';
   1126 				break;
   1127 			}
   1128 			if (flag)	/* Allow ignore; for string reparse */
   1129 				break;
   1130 			if (chr == '#') {	/* Comment terminates word */
   1131 				kwstate = '@';	/* Must consume comment also */
   1132 				chr = '\0';
   1133 				break;
   1134 			}
   1135 			if (chr == '$') {
   1136 				kwstate = '$';	/* Begin variable expansion */
   1137 				chr = '\0';
   1138 			}
   1139 			break;
   1140 		}
   1141 		/*
   1142 		 * If we've reached a space at the end of the word,
   1143 		 * then we're done.
   1144 		 */
   1145 		if (ichr == EOF || kwstate == ' ')
   1146 			break;
   1147 		/*
   1148 		 * If there's a character to store and space
   1149 		 * available, then add it to the string
   1150 		 */
   1151 		if (chr != '\0' && kbp < keybuf + keymax)
   1152 			*kbp++ = (char)chr;
   1153 	}
   1154 
   1155 	*kbp = '\0';
   1156 
   1157 	if (ichr == EOF) {
   1158 		return (kwstate == '\0' ? 1 : 2);
   1159 	}
   1160 	return (0);
   1161 }
   1162 
   1163 /*
   1164  * Fetch words from current file until all files are closed.  Handles
   1165  * include files.
   1166  */
   1167 static void
   1168 parse_from_file(struct parse_state *psp)
   1169 {
   1170 	char keybuf[MAX_KEYWORD];
   1171 	int retv;
   1172 
   1173 	while (psp->ps_cfile != NULL && psp->ps_cfile->pf_input != NULL) {
   1174 		retv = getkeyword(psp, keybuf, sizeof (keybuf),
   1175 		    (int (*)(void *))fgetc, (void *)psp->ps_cfile->pf_input,
   1176 		    0);
   1177 
   1178 		if (retv != 1)
   1179 			(void) dispatch_keyword(psp, keybuf);
   1180 
   1181 		if (retv != 0)
   1182 			file_end(psp);
   1183 	}
   1184 }
   1185 
   1186 /*
   1187  * Open and parse named file.  This is for the predefined
   1188  * configuration files in /etc/ppp -- it's not an error if any of
   1189  * these are missing.
   1190  */
   1191 static void
   1192 parse_file(struct parse_state *psp, const char *fname)
   1193 {
   1194 	struct stat sb;
   1195 
   1196 	/* It's ok if any of these files are missing. */
   1197 	if (stat(fname, &sb) == -1 && errno == ENOENT)
   1198 		return;
   1199 	if (set_file(psp->ps_csvc, fname) == 0)
   1200 		parse_from_file(psp);
   1201 }
   1202 
   1203 /*
   1204  * Dispatch keywords from command line.  Handles any files included
   1205  * from there.
   1206  */
   1207 static void
   1208 parse_arg_list(struct parse_state *psp, int argc, char **argv)
   1209 {
   1210 	/* The first argument (program name) can be null. */
   1211 	if (--argc <= 0)
   1212 		return;
   1213 	while (--argc >= 0) {
   1214 		(void) dispatch_keyword(psp, *++argv);
   1215 		if (psp->ps_cfile->pf_input != NULL)
   1216 			parse_from_file(psp);
   1217 	}
   1218 }
   1219 
   1220 /* Count length of dynamic device list */
   1221 static int
   1222 count_devs(struct device_list *dlp)
   1223 {
   1224 	int ndevs;
   1225 
   1226 	ndevs = 0;
   1227 	for (; dlp != NULL; dlp = dlp->dl_next)
   1228 		ndevs++;
   1229 	return (ndevs);
   1230 }
   1231 
   1232 /* Count number of devices named in entire file. */
   1233 static int
   1234 count_per_file(struct per_file *pfp)
   1235 {
   1236 	struct service_list *slp;
   1237 	int ndevs = 0;
   1238 
   1239 	for (; pfp != NULL; pfp = pfp->pf_prev) {
   1240 		ndevs += count_devs(pfp->pf_global.sl_dev);
   1241 		for (slp = pfp->pf_svc; slp != NULL; slp = slp->sl_next)
   1242 			if (!(slp->sl_entry.se_flags & SEF_CDEV))
   1243 				ndevs += count_devs(slp->sl_dev);
   1244 	}
   1245 	return (ndevs);
   1246 }
   1247 
   1248 /* Write device names into linear array. */
   1249 static const char **
   1250 devs_to_list(struct device_list *dlp, const char **dnames)
   1251 {
   1252 	for (; dlp != NULL; dlp = dlp->dl_next)
   1253 		*dnames++ = dlp->dl_name;
   1254 	return (dnames);
   1255 }
   1256 
   1257 /* Write all device names from file into a linear array. */
   1258 static const char **
   1259 per_file_to_list(struct per_file *pfp, const char **dnames)
   1260 {
   1261 	struct service_list *slp;
   1262 
   1263 	for (; pfp != NULL; pfp = pfp->pf_prev) {
   1264 		dnames = devs_to_list(pfp->pf_global.sl_dev, dnames);
   1265 		for (slp = pfp->pf_svc; slp != NULL; slp = slp->sl_next)
   1266 			if (!(slp->sl_entry.se_flags & SEF_CDEV))
   1267 				dnames = devs_to_list(slp->sl_dev, dnames);
   1268 	}
   1269 	return (dnames);
   1270 }
   1271 
   1272 /* Compare device names; used with qsort */
   1273 static int
   1274 devcmp(const void *d1, const void *d2)
   1275 {
   1276 	return (strcmp(*(const char **)d1, *(const char **)d2));
   1277 }
   1278 
   1279 /*
   1280  * Get sorted list of unique device names among all defined and
   1281  * partially defined services in all files.
   1282  */
   1283 static const char **
   1284 get_unique_devs(struct parse_state *psp)
   1285 {
   1286 	int ndevs;
   1287 	const char **dnames;
   1288 	const char **dnp;
   1289 	const char **dnf;
   1290 
   1291 	/*
   1292 	 * Count number of explicitly referenced devices among all
   1293 	 * services (including duplicates).
   1294 	 */
   1295 	ndevs = count_per_file(psp->ps_files);
   1296 	ndevs += count_per_file(psp->ps_cfile);
   1297 	if (ndevs <= 0) {
   1298 		return (NULL);
   1299 	}
   1300 
   1301 	/* Sort and trim out duplicate devices. */
   1302 	dnames = (const char **)malloc((ndevs+1) * sizeof (const char *));
   1303 	if (dnames == NULL) {
   1304 		logerr("unable to allocate space for %d devices", ndevs + 1);
   1305 		return (NULL);
   1306 	}
   1307 	dnp = per_file_to_list(psp->ps_files, dnames);
   1308 	(void) per_file_to_list(psp->ps_cfile, dnp);
   1309 	qsort(dnames, ndevs, sizeof (const char *), devcmp);
   1310 	for (dnf = (dnp = dnames) + 1; dnf < dnames+ndevs; dnf++)
   1311 		if (strcmp(*dnf, *dnp) != 0)
   1312 			*++dnp = *dnf;
   1313 	*++dnp = NULL;
   1314 
   1315 	/* Return array of pointers to names. */
   1316 	return (dnames);
   1317 }
   1318 
   1319 /*
   1320  * Convert data structures created by parsing process into data
   1321  * structures used by service dispatch.  This gathers the unique
   1322  * device (lower stream) names and attaches the services available on
   1323  * each device to a list while triming duplicate services.
   1324  */
   1325 static struct option_state *
   1326 organize_state(struct parse_state *psp)
   1327 {
   1328 	struct per_file *pfp;
   1329 	struct per_file *pftopp;
   1330 	struct service_list *slp;
   1331 	struct device_list *dlp;
   1332 	int ndevs;
   1333 	int nsvcs;
   1334 	const char **dnames;
   1335 	const char **dnp;
   1336 	struct device_entry *dep;
   1337 	struct option_state *osp;
   1338 	struct service_entry **sepp;
   1339 	struct service_entry **sebpp;
   1340 	struct service_entry **se2pp;
   1341 
   1342 	/*
   1343 	 * Parsing is now done.
   1344 	 */
   1345 	close_service(psp->ps_csvc);
   1346 	psp->ps_csvc = NULL;
   1347 	if ((pfp = psp->ps_cfile) != NULL) {
   1348 		pfp->pf_prev = psp->ps_files;
   1349 		psp->ps_files = pfp;
   1350 		psp->ps_cfile = NULL;
   1351 	}
   1352 
   1353 	/* Link the services from all files together for easy referencing. */
   1354 	pftopp = psp->ps_files;
   1355 	for (pfp = pftopp->pf_prev; pfp != NULL; pfp = pfp->pf_prev)
   1356 		if (pfp->pf_svc != NULL) {
   1357 			if (pftopp->pf_svc_last == NULL)
   1358 				pftopp->pf_svc = pfp->pf_svc;
   1359 			else
   1360 				pftopp->pf_svc_last->sl_next = pfp->pf_svc;
   1361 			pftopp->pf_svc_last = pfp->pf_svc_last;
   1362 			pfp->pf_svc = pfp->pf_svc_last = NULL;
   1363 		}
   1364 
   1365 	/*
   1366 	 * Count up number of services per device, including
   1367 	 * duplicates but not including defaults.
   1368 	 */
   1369 	nsvcs = 0;
   1370 	for (slp = psp->ps_files->pf_svc; slp != NULL; slp = slp->sl_next)
   1371 		for (dlp = slp->sl_dev; dlp != NULL; dlp = dlp->dl_next)
   1372 			nsvcs++;
   1373 
   1374 	/*
   1375 	 * Get the unique devices referenced by all services.
   1376 	 */
   1377 	dnames = get_unique_devs(psp);
   1378 	if (dnames == NULL) {
   1379 		logdbg("no devices referenced by any service");
   1380 		return (NULL);
   1381 	}
   1382 	ndevs = 0;
   1383 	for (dnp = dnames; *dnp != NULL; dnp++)
   1384 		ndevs++;
   1385 
   1386 	/*
   1387 	 * Allocate room for main structure, device records, and
   1388 	 * per-device lists.  Worst case is all devices having all
   1389 	 * services; that's why we allocate for nsvcs * ndevs.
   1390 	 */
   1391 	osp = (struct option_state *)malloc(sizeof (*osp) +
   1392 	    ndevs * sizeof (*dep) + nsvcs * ndevs * sizeof (*sepp));
   1393 	if (osp == NULL) {
   1394 		logerr("unable to allocate option state structure");
   1395 		free(dnames);
   1396 		return (NULL);
   1397 	}
   1398 
   1399 	/* We're going to succeed now, so steal these over. */
   1400 	osp->os_devices = dep = (struct device_entry *)(osp+1);
   1401 	osp->os_pfjunk = psp->ps_files;
   1402 	psp->ps_files = NULL;
   1403 	osp->os_evjunk = psp->ps_evlist;
   1404 	psp->ps_evlist = NULL;
   1405 
   1406 	/* Loop over devices, install services, remove duplicates. */
   1407 	sepp = (struct service_entry **)(dep + ndevs);
   1408 	for (dnp = dnames; *dnp != NULL; dnp++) {
   1409 		dep->de_name = *dnp;
   1410 		dep->de_services = (const struct service_entry **)sepp;
   1411 		sebpp = sepp;
   1412 		for (slp = osp->os_pfjunk->pf_svc; slp != NULL;
   1413 		    slp = slp->sl_next)
   1414 			for (dlp = slp->sl_dev; dlp != NULL;
   1415 			    dlp = dlp->dl_next) {
   1416 				if (dlp->dl_name == *dnp ||
   1417 				    strcmp(dlp->dl_name, *dnp) == 0) {
   1418 					for (se2pp = sebpp; se2pp < sepp;
   1419 					    se2pp++)
   1420 						if ((*se2pp)->se_name ==
   1421 						    slp->sl_entry.se_name ||
   1422 						    strcmp((*se2pp)->
   1423 							se_name,
   1424 							slp->sl_entry.
   1425 							se_name) == 0)
   1426 							break;
   1427 					/*
   1428 					 * We retain a service if it's
   1429 					 * unique or if its serial
   1430 					 * number (position in the
   1431 					 * file) is greater than than
   1432 					 * any other.
   1433 					 */
   1434 					if (se2pp >= sepp)
   1435 						*sepp++ = &slp->sl_entry;
   1436 					else if (SESERIAL(**se2pp) <
   1437 					    SESERIAL(slp->sl_entry))
   1438 						*se2pp = &slp->sl_entry;
   1439 				}
   1440 			}
   1441 		/* Count up the services on this device. */
   1442 		dep->de_nservices = (const struct service_entry **)sepp -
   1443 		    dep->de_services;
   1444 		/* Ignore devices having no services at all. */
   1445 		if (dep->de_nservices > 0)
   1446 			dep++;
   1447 	}
   1448 	/* Count up the devices. */
   1449 	osp->os_ndevices = dep - osp->os_devices;
   1450 	/* Free the list of device names */
   1451 	free(dnames);
   1452 	return (osp);
   1453 }
   1454 
   1455 /*
   1456  * Free storage unique to a given service.  Pointers copied from other
   1457  * services are ignored.
   1458  */
   1459 static void
   1460 free_service(struct service_list *slp)
   1461 {
   1462 	struct filter_entry *fep;
   1463 	struct filter_entry *fen;
   1464 
   1465 	if (!(slp->sl_entry.se_flags & SEF_CDEV))
   1466 		free_device_list(slp->sl_dev);
   1467 	if (!(slp->sl_entry.se_flags & SEF_CFLIST)) {
   1468 		fep = slp->sl_entry.se_flist;
   1469 		while (fep != NULL) {
   1470 			fen = fep->fe_prevcopy ? NULL : fep->fe_prev;
   1471 			free(fep);
   1472 			fep = fen;
   1473 		}
   1474 	}
   1475 	if (!(slp->sl_entry.se_flags & SEF_CPPPD) &&
   1476 	    slp->sl_entry.se_pppd != NULL)
   1477 		free(slp->sl_entry.se_pppd);
   1478 	if (!(slp->sl_entry.se_flags & SEF_CSERVER) &&
   1479 	    slp->sl_entry.se_server != NULL)
   1480 		free(slp->sl_entry.se_server);
   1481 	if (!(slp->sl_entry.se_flags & SEF_CPATH) &&
   1482 	    slp->sl_entry.se_path != NULL)
   1483 		free(slp->sl_entry.se_path);
   1484 	if (!(slp->sl_entry.se_flags & SEF_CEXTRA) &&
   1485 	    slp->sl_entry.se_extra != NULL)
   1486 		free(slp->sl_entry.se_extra);
   1487 	if (!(slp->sl_entry.se_flags & SEF_CLOG) &&
   1488 	    slp->sl_entry.se_log != NULL)
   1489 		free(slp->sl_entry.se_log);
   1490 }
   1491 
   1492 /*
   1493  * Free a linked list of services.
   1494  */
   1495 static void
   1496 free_service_list(struct service_list *slp)
   1497 {
   1498 	struct service_list *sln;
   1499 
   1500 	while (slp != NULL) {
   1501 		free_service(slp);
   1502 		sln = slp->sl_next;
   1503 		free(slp);
   1504 		slp = sln;
   1505 	}
   1506 }
   1507 
   1508 /*
   1509  * Free a linked list of files and all services in those files.
   1510  */
   1511 static void
   1512 free_file_list(struct per_file *pfp)
   1513 {
   1514 	struct per_file *pfn;
   1515 
   1516 	while (pfp != NULL) {
   1517 		free_service(&pfp->pf_global);
   1518 		free_service_list(pfp->pf_svc);
   1519 		pfn = pfp->pf_prev;
   1520 		free(pfp);
   1521 		pfp = pfn;
   1522 	}
   1523 }
   1524 
   1525 /*
   1526  * Free an array of local environment variables.
   1527  */
   1528 static void
   1529 free_env_list(char **evlist)
   1530 {
   1531 	char **evp;
   1532 	char *env;
   1533 
   1534 	if ((evp = evlist) != NULL) {
   1535 		while ((env = *evp++) != NULL)
   1536 			free(env);
   1537 		free(evlist);
   1538 	}
   1539 }
   1540 
   1541 /*
   1542  * Add a new device (lower stream) to the list for which we're the
   1543  * PPPoE server.
   1544  */
   1545 static void
   1546 add_new_dev(int tunfd, const char *dname)
   1547 {
   1548 	union ppptun_name ptn;
   1549 
   1550 	(void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%s:pppoed",
   1551 	    dname);
   1552 	if (strioctl(tunfd, PPPTUN_SCTL, &ptn, sizeof (ptn), 0) < 0) {
   1553 		logerr("PPPTUN_SCTL %s: %s", ptn.ptn_name, mystrerror(errno));
   1554 	} else {
   1555 		logdbg("added %s", ptn.ptn_name);
   1556 	}
   1557 }
   1558 
   1559 /*
   1560  * Remove an existing device (lower stream) from the list for which we
   1561  * were the PPPoE server.
   1562  */
   1563 static void
   1564 rem_old_dev(int tunfd, const char *dname)
   1565 {
   1566 	union ppptun_name ptn;
   1567 
   1568 	(void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%s:pppoed",
   1569 	    dname);
   1570 	if (strioctl(tunfd, PPPTUN_DCTL, &ptn, sizeof (ptn), 0) < 0) {
   1571 		logerr("PPPTUN_DCTL %s: %s", ptn.ptn_name, mystrerror(errno));
   1572 	} else {
   1573 		logdbg("removed %s", ptn.ptn_name);
   1574 	}
   1575 }
   1576 
   1577 /*
   1578  * Get a list of all of the devices currently plumbed for PPPoE.  This
   1579  * is used for supporting the "*" and "all" device aliases.
   1580  */
   1581 static void
   1582 get_device_list(struct parse_state *psp, int tunfd)
   1583 {
   1584 	struct device_list *dlp;
   1585 	struct device_list **dlpp;
   1586 	struct device_list *dlalt;
   1587 	struct device_list **dl2pp;
   1588 	struct device_list *dla;
   1589 	int i;
   1590 	union ppptun_name ptn;
   1591 	char *cp;
   1592 
   1593 	/* First pass; just allocate space for all *:pppoe* devices */
   1594 	dlpp = &psp->ps_star;
   1595 	dl2pp = &dlalt;
   1596 	for (i = 0; ; i++) {
   1597 		ptn.ptn_index = i;
   1598 		if (strioctl(tunfd, PPPTUN_GNNAME, &ptn, sizeof (ptn),
   1599 		    sizeof (ptn)) < 0) {
   1600 			logerr("PPPTUN_GNNAME %d: %s", i, mystrerror(errno));
   1601 			break;
   1602 		}
   1603 		if (ptn.ptn_name[0] == '\0')
   1604 			break;
   1605 		if ((cp = strchr(ptn.ptn_name, ':')) == NULL ||
   1606 		    strncmp(cp, ":pppoe", 6) != 0 ||
   1607 		    (cp[6] != '\0' && strcmp(cp+6, "d") != 0))
   1608 			continue;
   1609 		*cp = '\0';
   1610 		dlp = (struct device_list *)malloc(sizeof (*dlp) +
   1611 		    strlen(ptn.ptn_name) + 1);
   1612 		if (dlp == NULL)
   1613 			break;
   1614 		dlp->dl_name = (const char *)(dlp + 1);
   1615 		(void) strcpy((char *)(dlp + 1), ptn.ptn_name);
   1616 		if (cp[6] == '\0') {
   1617 			*dlpp = dlp;
   1618 			dlpp = &dlp->dl_next;
   1619 		} else {
   1620 			*dl2pp = dlp;
   1621 			dl2pp = &dlp->dl_next;
   1622 		}
   1623 	}
   1624 	*dlpp = NULL;
   1625 	*dl2pp = NULL;
   1626 
   1627 	/* Second pass; eliminate improperly plumbed devices */
   1628 	for (dlpp = &psp->ps_star; (dlp = *dlpp) != NULL; ) {
   1629 		for (dla = dlalt; dla != NULL; dla = dla->dl_next)
   1630 			if (strcmp(dla->dl_name, dlp->dl_name) == 0)
   1631 			    break;
   1632 		if (dla == NULL) {
   1633 			*dlpp = dlp->dl_next;
   1634 			free(dlp);
   1635 		} else {
   1636 			dlpp = &dlp->dl_next;
   1637 		}
   1638 	}
   1639 	free_device_list(dlalt);
   1640 
   1641 	/* Add in "*" so we can always handle dynamic plumbing. */
   1642 	dlp = (struct device_list *)malloc(sizeof (*dlp) + 2);
   1643 	if (dlp != NULL) {
   1644 		dlp->dl_name = (const char *)(dlp + 1);
   1645 		(void) strcpy((char *)(dlp + 1), "*");
   1646 		dlp->dl_next = psp->ps_star;
   1647 		psp->ps_star = dlp;
   1648 	}
   1649 }
   1650 
   1651 /*
   1652  * Set logging subsystem back to configured global default values.
   1653  */
   1654 void
   1655 global_logging(void)
   1656 {
   1657 	log_for_service(glob_svc.se_log, glob_svc.se_debug);
   1658 }
   1659 
   1660 /*
   1661  * Handle SIGHUP -- reparse command line and all configuration files.
   1662  * When reparsing is complete, free old parsed data and replace with
   1663  * new.
   1664  */
   1665 void
   1666 parse_options(int tunfd, int argc, char **argv)
   1667 {
   1668 	struct parse_state pstate;
   1669 	struct per_file *argpf;
   1670 	struct option_state *newopt;
   1671 	const char **dnames;
   1672 	const char **dnp;
   1673 	const struct device_entry *newdep, *newmax;
   1674 	const struct device_entry *olddep, *oldmax;
   1675 	int cmpval;
   1676 	struct service_entry newglobsvc, *mainsvc;
   1677 
   1678 	/* Note that all per_file structures must be freeable */
   1679 	argpf = (struct per_file *)calloc(sizeof (*argpf), 1);
   1680 	if (argpf == NULL) {
   1681 		return;
   1682 	}
   1683 	(void) memset(&pstate, '\0', sizeof (pstate));
   1684 	pstate.ps_state = ksDefault;
   1685 	pstate.ps_cfile = argpf;
   1686 	pstate.ps_csvc = &argpf->pf_global;
   1687 	argpf->pf_global.sl_parse = &pstate;
   1688 	argpf->pf_name = "command line";
   1689 
   1690 	/* Default is 1 -- errors only */
   1691 	argpf->pf_global.sl_entry.se_debug++;
   1692 	argpf->pf_global.sl_entry.se_name = "<global>";
   1693 
   1694 	/* Get list of all devices */
   1695 	get_device_list(&pstate, tunfd);
   1696 
   1697 	/* Parse options from command line and main configuration file. */
   1698 	pstate.ps_flags |= PSF_SETLEVEL;
   1699 	parse_arg_list(&pstate, argc, argv);
   1700 	parse_file(&pstate, "/etc/ppp/pppoe");
   1701 	pstate.ps_flags &= ~PSF_SETLEVEL;
   1702 
   1703 	/*
   1704 	 * At this point, global options from the main configuration
   1705 	 * file are pointed to by ps_files, and options from command
   1706 	 * line are in argpf.  We need to pull three special options
   1707 	 * from these -- wildcard, debug, and log.  Note that the main
   1708 	 * options file overrides the command line.  This is
   1709 	 * intentional.  The semantics are such that the system
   1710 	 * behaves as though the main configuration file were
   1711 	 * "included" from the command line, and thus options there
   1712 	 * override the command line.  This may seem odd, but at least
   1713 	 * it's self-consistent.
   1714 	 */
   1715 	newglobsvc = argpf->pf_global.sl_entry;
   1716 	if (pstate.ps_files != NULL) {
   1717 		mainsvc = &pstate.ps_files->pf_global.sl_entry;
   1718 		if (mainsvc->se_log != NULL)
   1719 			newglobsvc.se_log = mainsvc->se_log;
   1720 		if (mainsvc->se_flags & (SEF_WILD|SEF_NOWILD))
   1721 			newglobsvc.se_flags =
   1722 			    (newglobsvc.se_flags & ~(SEF_WILD|SEF_NOWILD)) |
   1723 			    (mainsvc->se_flags & (SEF_WILD|SEF_NOWILD));
   1724 		if (mainsvc->se_flags & SEF_DEBUGCLR)
   1725 			newglobsvc.se_debug = 0;
   1726 		newglobsvc.se_debug += mainsvc->se_debug;
   1727 	}
   1728 	glob_svc = newglobsvc;
   1729 	global_logging();
   1730 
   1731 	/* Get the list of devices referenced by configuration above. */
   1732 	dnames = get_unique_devs(&pstate);
   1733 	if (dnames != NULL) {
   1734 		/* Read per-device configuration files. */
   1735 		pstate.ps_flags |= PSF_PERDEV;
   1736 		for (dnp = dnames; *dnp != NULL; dnp++)
   1737 			parse_file(&pstate, *dnp);
   1738 		pstate.ps_flags &= ~PSF_PERDEV;
   1739 		free(dnames);
   1740 	}
   1741 	file_end(&pstate);
   1742 
   1743 	/*
   1744 	 * Convert parsed data structures into per-device structures.
   1745 	 * (Invert the table.)
   1746 	 */
   1747 	newopt = organize_state(&pstate);
   1748 
   1749 	/* If we're going to free the file name, then stop logging there. */
   1750 	if (newopt == NULL && glob_svc.se_log != NULL) {
   1751 		glob_svc.se_log = NULL;
   1752 		global_logging();
   1753 	}
   1754 
   1755 	/*
   1756 	 * Unless an error has occurred, these pointers are normally
   1757 	 * all NULL.  Nothing is freed until the file is re-read.
   1758 	 */
   1759 	free_file_list(pstate.ps_files);
   1760 	free_file_list(pstate.ps_cfile);
   1761 	free_device_list(pstate.ps_star);
   1762 	free_env_list(pstate.ps_evlist);
   1763 
   1764 	/*
   1765 	 * Match up entries on device list.  Detach devices no longer
   1766 	 * referenced.  Attach ones now referenced.  (The use of null
   1767 	 * pointers here may look fishy, but it actually works.
   1768 	 * NULL>=NULL is always true.)
   1769 	 */
   1770 	if (newopt != NULL) {
   1771 		newdep = newopt->os_devices;
   1772 		newmax = newdep + newopt->os_ndevices;
   1773 	} else {
   1774 		newdep = newmax = NULL;
   1775 	}
   1776 	if (cur_options != NULL) {
   1777 		olddep = cur_options->os_devices;
   1778 		oldmax = olddep + cur_options->os_ndevices;
   1779 	} else {
   1780 		olddep = oldmax = NULL;
   1781 	}
   1782 	while ((newdep != NULL && newdep < newmax) ||
   1783 	    (olddep != NULL && olddep < oldmax)) {
   1784 		if (newdep < newmax) {
   1785 			if (olddep >= oldmax) {
   1786 				add_new_dev(tunfd, newdep->de_name);
   1787 				newdep++;
   1788 			} else {
   1789 				cmpval = strcmp(newdep->de_name,
   1790 				    olddep->de_name);
   1791 				if (cmpval < 0) {
   1792 					/* Brand new device seen. */
   1793 					add_new_dev(tunfd, newdep->de_name);
   1794 					newdep++;
   1795 				} else if (cmpval == 0) {
   1796 					/* Existing device; skip it. */
   1797 					newdep++;
   1798 					olddep++;
   1799 				}
   1800 				/* No else clause -- removal is below */
   1801 			}
   1802 		}
   1803 		if (olddep < oldmax) {
   1804 			if (newdep >= newmax) {
   1805 				rem_old_dev(tunfd, olddep->de_name);
   1806 				olddep++;
   1807 			} else {
   1808 				cmpval = strcmp(newdep->de_name,
   1809 				    olddep->de_name);
   1810 				if (cmpval > 0) {
   1811 					/* Old device is gone */
   1812 					rem_old_dev(tunfd, olddep->de_name);
   1813 					olddep++;
   1814 				} else if (cmpval == 0) {
   1815 					/* Existing device; skip it. */
   1816 					newdep++;
   1817 					olddep++;
   1818 				}
   1819 				/* No else clause -- insert handled above */
   1820 			}
   1821 		}
   1822 	}
   1823 
   1824 	/* Discard existing parsed data storage. */
   1825 	if (cur_options != NULL) {
   1826 		free_file_list(cur_options->os_pfjunk);
   1827 		free_env_list(cur_options->os_evjunk);
   1828 		free(cur_options);
   1829 	}
   1830 	/* Install new. */
   1831 	cur_options = newopt;
   1832 }
   1833 
   1834 /*
   1835  * Check if configured filters permit requesting client to use a given
   1836  * service.  Note -- filters are stored in reverse order in order to
   1837  * make file-inclusion work as expected.  Thus, the "first match"
   1838  * filter rule becomes "last match" here.
   1839  */
   1840 static boolean_t
   1841 allow_service(const struct service_entry *sep, const ppptun_atype *pap)
   1842 {
   1843 	const struct filter_entry *fep;
   1844 	const struct filter_entry *lmatch;
   1845 	boolean_t anynonexcept = B_FALSE;
   1846 	const uchar_t *upt;
   1847 	const uchar_t *macp;
   1848 	const uchar_t *maskp;
   1849 	int i;
   1850 
   1851 	lmatch = NULL;
   1852 	for (fep = sep->se_flist; fep != NULL; fep = fep->fe_prev) {
   1853 		anynonexcept |= !fep->fe_isexcept;
   1854 		upt = pap->pta_pppoe.ptma_mac;
   1855 		macp = fep->fe_mac.ether_addr_octet;
   1856 		maskp = fep->fe_mask.ether_addr_octet;
   1857 		for (i = sizeof (pap->pta_pppoe.ptma_mac); i > 0; i--)
   1858 			if (((*macp++ ^ *upt++) & *maskp++) != 0)
   1859 				break;
   1860 		if (i <= 0)
   1861 			lmatch = fep;
   1862 	}
   1863 
   1864 	if (lmatch == NULL) {
   1865 		/*
   1866 		 * Assume reject by default if any positive-match
   1867 		 * (non-except) filters are given.  Otherwise, if
   1868 		 * there are no positive-match filters, then
   1869 		 * non-matching means accept by default.
   1870 		 */
   1871 		return (!anynonexcept);
   1872 	}
   1873 	return (!lmatch->fe_isexcept);
   1874 }
   1875 
   1876 /*
   1877  * Locate available service(s) based on client request.  Assumes that
   1878  * outp points to a buffer of at least size PPPOE_MSGMAX.  Creates a
   1879  * PPPoE response message in outp.  Returns count of matched services
   1880  * and (through *srvp) a pointer to the last (or only) service.  If
   1881  * some error is found in the request, an error string is added and -1
   1882  * is returned; the caller should just send the message without
   1883  * alteration.
   1884  */
   1885 int
   1886 locate_service(poep_t *poep, int plen, const char *iname, ppptun_atype *pap,
   1887     uint32_t *outp, void **srvp)
   1888 {
   1889 	poep_t *opoe;
   1890 	const uint8_t *tagp;
   1891 	const char *cp;
   1892 	int ttyp;
   1893 	int tlen;
   1894 	int nsvcs;
   1895 	const struct device_entry *dep, *depe;
   1896 	const struct device_entry *wdep;
   1897 	const struct service_entry **sepp, **seppe;
   1898 	const struct service_entry *sep;
   1899 	char *str;
   1900 	boolean_t ispadi;
   1901 
   1902 	ispadi = poep->poep_code == POECODE_PADI;
   1903 	opoe = poe_mkheader(outp, ispadi ? POECODE_PADO : POECODE_PADS, 0);
   1904 
   1905 	*srvp = NULL;
   1906 	if (cur_options == NULL)
   1907 		return (0);
   1908 
   1909 	/* Search for named device (lower stream) in tables. */
   1910 	dep = cur_options->os_devices;
   1911 	depe = dep + cur_options->os_ndevices;
   1912 	wdep = NULL;
   1913 	if ((cp = strchr(iname, ':')) != NULL)
   1914 		tlen = cp - iname;
   1915 	else
   1916 		tlen = strlen(iname);
   1917 	for (; dep < depe; dep++)
   1918 		if (strncmp(iname, dep->de_name, tlen) == 0 &&
   1919 		    dep->de_name[tlen] == '\0')
   1920 			break;
   1921 		else if (dep->de_name[0] == '*' && dep->de_name[1] == '\0')
   1922 			wdep = dep;
   1923 	if (dep >= depe)
   1924 		dep = wdep;
   1925 	/*
   1926 	 * Return if interface not found.  Zero-service case can't
   1927 	 * occur, since devices with no services aren't included in
   1928 	 * the list, but the code is just being safe here.
   1929 	 */
   1930 	if (dep == NULL || dep->de_services == NULL || dep->de_nservices <= 0)
   1931 		return (0);
   1932 
   1933 	/*
   1934 	 * Loop over tags in client message and process them.
   1935 	 * Services must be matched against our list.  Host-Uniq and
   1936 	 * Relay-Session-Id must be copied to the reply.  All others
   1937 	 * must be discarded.
   1938 	 */
   1939 	nsvcs = 0;
   1940 	sepp = dep->de_services;
   1941 	tagp = (const uint8_t *)(poep + 1);
   1942 	while (poe_tagcheck(poep, plen, tagp)) {
   1943 		ttyp = POET_GET_TYPE(tagp);
   1944 		if (ttyp == POETT_END)
   1945 			break;
   1946 		tlen = POET_GET_LENG(tagp);
   1947 		switch (ttyp) {
   1948 		case POETT_SERVICE:	/* Service-Name */
   1949 			/*
   1950 			 * Allow only one.  (Note that this test works
   1951 			 * because there's always at least one service
   1952 			 * per device; otherwise, the device is
   1953 			 * removed from the list.)
   1954 			 */
   1955 			if (sepp != dep->de_services) {
   1956 				if (nsvcs != -1)
   1957 					(void) poe_add_str(opoe, POETT_NAMERR,
   1958 					    "Too many Service-Name tags");
   1959 				nsvcs = -1;
   1960 				break;
   1961 			}
   1962 			seppe = sepp + dep->de_nservices;
   1963 			/* Clients's requested service must appear in reply. */
   1964 			if (tlen != 0 || (ispadi &&
   1965 				    !(glob_svc.se_flags & SEF_NOWILD)))
   1966 				(void) poe_tag_copy(opoe, tagp);
   1967 			if (tlen == 0) {
   1968 				/*
   1969 				 * If config specifies "nowild" in a
   1970 				 * global context, then we don't
   1971 				 * respond to wildcard PADRs.  The
   1972 				 * client must know the exact service
   1973 				 * name to get access.
   1974 				 */
   1975 
   1976 				if (!ispadi && (glob_svc.se_flags & SEF_NOWILD))
   1977 					sepp = seppe;
   1978 				while (sepp < seppe) {
   1979 					sep = *sepp++;
   1980 					if ((ispadi || !(sep->se_flags &
   1981 						    SEF_NOWILD)) &&
   1982 					    allow_service(sep, pap)) {
   1983 						nsvcs++;
   1984 						*srvp = (void *)sep;
   1985 						if (poep->poep_code ==
   1986 						    POECODE_PADR)
   1987 							break;
   1988 						if (sep->se_name[0] == '\0')
   1989 							continue;
   1990 						(void) poe_add_str(opoe,
   1991 						    POETT_SERVICE,
   1992 						    sep->se_name);
   1993 					}
   1994 				}
   1995 			} else {
   1996 				/* Requested specific service; find it. */
   1997 				cp = (char *)POET_DATA(tagp);
   1998 				while (sepp < seppe) {
   1999 					sep = *sepp++;
   2000 					if (strlen(sep->se_name) == tlen &&
   2001 					    strncasecmp(sep->se_name, cp,
   2002 						tlen) == 0) {
   2003 						if (allow_service(sep, pap)) {
   2004 							nsvcs++;
   2005 							*srvp = (void *)sep;
   2006 						}
   2007 						break;
   2008 					}
   2009 				}
   2010 			}
   2011 			/*
   2012 			 * Allow service definition to override
   2013 			 * AC-Name (concentrator [server] name) field.
   2014 			 */
   2015 			if (*srvp != NULL) {
   2016 				sep = (const struct service_entry *)*srvp;
   2017 				log_for_service(sep->se_log, sep->se_debug);
   2018 				str = "Solaris PPPoE";
   2019 				if (sep->se_server != NULL)
   2020 					str = sep->se_server;
   2021 				(void) poe_add_str(opoe, POETT_ACCESS, str);
   2022 			}
   2023 			break;
   2024 		/* Ones we should discard */
   2025 		case POETT_ACCESS:	/* AC-Name */
   2026 		case POETT_COOKIE:	/* AC-Cookie */
   2027 		case POETT_NAMERR:	/* Service-Name-Error */
   2028 		case POETT_SYSERR:	/* AC-System-Error */
   2029 		case POETT_GENERR:	/* Generic-Error */
   2030 		case POETT_HURL:	/* Host-URL */
   2031 		case POETT_MOTM:	/* Message-Of-The-Minute */
   2032 		case POETT_RTEADD:	/* IP-Route-Add */
   2033 		case POETT_VENDOR:	/* Vendor-Specific */
   2034 		case POETT_MULTI:	/* Multicast-Capable */
   2035 		default:
   2036 			break;
   2037 		/* Ones we should copy */
   2038 		case POETT_UNIQ:	/* Host-Uniq */
   2039 		case POETT_RELAY:	/* Relay-Session-Id */
   2040 			(void) poe_tag_copy(opoe, tagp);
   2041 			break;
   2042 		}
   2043 		tagp = POET_NEXT(tagp);
   2044 	}
   2045 	return (nsvcs);
   2046 }
   2047 
   2048 /*
   2049  * Like fgetc, but reads from a string.
   2050  */
   2051 static int
   2052 sgetc(void *arg)
   2053 {
   2054 	char **cpp = (char **)arg;
   2055 	if (**cpp == '\0')
   2056 		return (EOF);
   2057 	return (*(*cpp)++);
   2058 }
   2059 
   2060 /*
   2061  * Given a service structure, launch pppd.  Called by handle_input()
   2062  * in pppoed.c if locate_service() [above] finds exactly one service
   2063  * matching a PADR.
   2064  */
   2065 int
   2066 launch_service(int tunfd, poep_t *poep, void *srvp, struct ppptun_control *ptc)
   2067 {
   2068 	const struct service_entry *sep = (const struct service_entry *)srvp;
   2069 	const char *path;
   2070 	const char *extra;
   2071 	const char *pppd;
   2072 	const char *cp;
   2073 	pid_t pidv;
   2074 	int newtun;
   2075 	struct ppptun_peer ptp;
   2076 	union ppptun_name ptn;
   2077 	const char *args[MAXARGS];
   2078 	struct strbuf ctrl;
   2079 	struct strbuf data;
   2080 	const char **cpp;
   2081 	char *sptr;
   2082 	char *spv;
   2083 	int slen;
   2084 	int retv;
   2085 	char keybuf[MAX_KEYWORD];
   2086 
   2087 	assert(sep != NULL);
   2088 
   2089 	/* Get tunnel driver connection for new PPP session. */
   2090 	newtun = open(tunnam, O_RDWR);
   2091 	if (newtun == -1)
   2092 		goto syserr;
   2093 
   2094 	/* Set this session up for standard PPP and client's address. */
   2095 	(void) memset(&ptp, '\0', sizeof (ptp));
   2096 	ptp.ptp_style = PTS_PPPOE;
   2097 	ptp.ptp_address = ptc->ptc_address;
   2098 	if (strioctl(newtun, PPPTUN_SPEER, &ptp, sizeof (ptp), sizeof (ptp)) <
   2099 	    0)
   2100 		goto syserr;
   2101 	ptp.ptp_rsessid = ptp.ptp_lsessid;
   2102 	if (strioctl(newtun, PPPTUN_SPEER, &ptp, sizeof (ptp), sizeof (ptp)) <
   2103 	    0)
   2104 		goto syserr;
   2105 
   2106 	/* Attach the requested lower stream. */
   2107 	cp = strchr(ptc->ptc_name, ':');
   2108 	if (cp == NULL)
   2109 		cp = ptc->ptc_name + strlen(ptc->ptc_name);
   2110 	(void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%.*s:pppoe",
   2111 	    cp-ptc->ptc_name, ptc->ptc_name);
   2112 	if (strioctl(newtun, PPPTUN_SDATA, &ptn, sizeof (ptn), 0) < 0)
   2113 		goto syserr;
   2114 	(void) snprintf(ptn.ptn_name, sizeof (ptn.ptn_name), "%.*s:pppoed",
   2115 	    cp-ptc->ptc_name, ptc->ptc_name);
   2116 	if (strioctl(newtun, PPPTUN_SCTL, &ptn, sizeof (ptn), 0) < 0)
   2117 		goto syserr;
   2118 
   2119 	pidv = fork();
   2120 	if (pidv == (pid_t)-1)
   2121 		goto syserr;
   2122 
   2123 	if (pidv == (pid_t)0) {
   2124 		/*
   2125 		 * Use syslog only in order to avoid mixing log messages
   2126 		 * in regular files.
   2127 		 */
   2128 		close_log_files();
   2129 
   2130 		if ((path = sep->se_path) == NULL)
   2131 			path = "/usr/bin/pppd";
   2132 		if ((extra = sep->se_extra) == NULL)
   2133 			extra = "plugin pppoe.so directtty";
   2134 		if ((pppd = sep->se_pppd) == NULL)
   2135 			pppd = "";
   2136 
   2137 		/* Concatenate these. */
   2138 		slen = strlen(path) + strlen(extra) + strlen(pppd) + 3;
   2139 		if ((sptr = (char *)malloc(slen)) == NULL)
   2140 			goto bail_out;
   2141 		(void) strcpy(sptr, path);
   2142 		(void) strcat(sptr, " ");
   2143 		(void) strcat(sptr, extra);
   2144 		(void) strcat(sptr, " ");
   2145 		(void) strcat(sptr, pppd);
   2146 
   2147 		/* Parse out into arguments */
   2148 		cpp = args;
   2149 		spv = sptr;
   2150 		while (cpp < args + MAXARGS - 1) {
   2151 			retv = getkeyword(NULL, keybuf, sizeof (keybuf), sgetc,
   2152 			    (void *)&spv, 1);
   2153 			if (retv != 1)
   2154 				*cpp++ = strsave(keybuf);
   2155 			if (retv != 0)
   2156 				break;
   2157 		}
   2158 		*cpp = NULL;
   2159 		if (cpp == args)
   2160 			goto bail_out;
   2161 
   2162 		/*
   2163 		 * Fix tunnel device on stdin/stdout and error file on
   2164 		 * stderr.
   2165 		 */
   2166 		if (newtun != 0 && dup2(newtun, 0) < 0)
   2167 			goto bail_out;
   2168 		if (newtun != 1 && dup2(newtun, 1) < 0)
   2169 			goto bail_out;
   2170 		if (newtun > 1)
   2171 			(void) close(newtun);
   2172 		if (tunfd > 1)
   2173 			(void) close(tunfd);
   2174 		(void) close(2);
   2175 		(void) open("/etc/ppp/pppoe-errors", O_WRONLY | O_APPEND |
   2176 		    O_CREAT, 0600);
   2177 
   2178 		/*
   2179 		 * Change GID first, for obvious reasons.  Note that
   2180 		 * we log any problems to syslog, not the errors file.
   2181 		 * The errors file is intended for problems in the
   2182 		 * exec'd program.
   2183 		 */
   2184 		if ((sep->se_flags & SEF_GIDSET) &&
   2185 		    setgid(sep->se_gid) == -1) {
   2186 			cp = mystrerror(errno);
   2187 			reopen_log();
   2188 			logerr("setgid(%d): %s", sep->se_gid, cp);
   2189 			goto logged;
   2190 		}
   2191 		if ((sep->se_flags & SEF_UIDSET) &&
   2192 		    setuid(sep->se_uid) == -1) {
   2193 			cp = mystrerror(errno);
   2194 			reopen_log();
   2195 			logerr("setuid(%d): %s", sep->se_uid, cp);
   2196 			goto logged;
   2197 		}
   2198 
   2199 		/* Run pppd */
   2200 		path = args[0];
   2201 		cp = strrchr(args[0], '/');
   2202 		if (cp != NULL && cp[1] != '\0')
   2203 			args[0] = cp+1;
   2204 		errno = 0;
   2205 		(void) execv(path, (char * const *)args);
   2206 		newtun = 0;
   2207 
   2208 		/*
   2209 		 * Exec failure; attempt to log the problem and send a
   2210 		 * PADT to the client so that he knows the session
   2211 		 * went south.
   2212 		 */
   2213 	bail_out:
   2214 		cp = mystrerror(errno);
   2215 		reopen_log();
   2216 		logerr("\"%s\": %s", (sptr == NULL ? path : sptr), cp);
   2217 	logged:
   2218 		poep = poe_mkheader(pkt_output, POECODE_PADT, ptp.ptp_lsessid);
   2219 		poep->poep_session_id = htons(ptp.ptp_lsessid);
   2220 		(void) poe_add_str(poep, POETT_SYSERR, cp);
   2221 		(void) sleep(1);
   2222 		ctrl.len = sizeof (*ptc);
   2223 		ctrl.buf = (caddr_t)ptc;
   2224 		data.len = poe_length(poep) + sizeof (*poep);
   2225 		data.buf = (caddr_t)poep;
   2226 		if (putmsg(newtun, &ctrl, &data, 0) < 0) {
   2227 			logerr("putmsg %s: %s", ptc->ptc_name,
   2228 			    mystrerror(errno));
   2229 		}
   2230 		exit(1);
   2231 	}
   2232 
   2233 	(void) close(newtun);
   2234 
   2235 	/* Give session ID to client in reply. */
   2236 	poep->poep_session_id = htons(ptp.ptp_lsessid);
   2237 	return (1);
   2238 
   2239 syserr:
   2240 	/* Peer doesn't know session ID yet; hope for the best. */
   2241 	retv = errno;
   2242 	if (newtun >= 0)
   2243 		(void) close(newtun);
   2244 	(void) poe_add_str(poep, POETT_SYSERR, mystrerror(retv));
   2245 	return (0);
   2246 }
   2247 
   2248 /*
   2249  * This is pretty awful -- it uses recursion to print a simple list.
   2250  * It's just for debug, though, and does a reasonable job of printing
   2251  * the filters in the right order.
   2252  */
   2253 static void
   2254 print_filter_list(FILE *fp, struct filter_entry *fep)
   2255 {
   2256 	if (fep->fe_prev != NULL)
   2257 		print_filter_list(fp, fep->fe_prev);
   2258 	(void) fprintf(fp, "\t\t    MAC %s", ehost2(&fep->fe_mac));
   2259 	(void) fprintf(fp, ", mask %s%s\n", ehost2(&fep->fe_mask),
   2260 	    (fep->fe_isexcept ? ", except" : ""));
   2261 }
   2262 
   2263 /*
   2264  * Write summary of parsed configuration data to given file.
   2265  */
   2266 void
   2267 dump_configuration(FILE *fp)
   2268 {
   2269 	const struct device_entry *dep;
   2270 	const struct service_entry *sep, **sepp;
   2271 	struct per_file *pfp;
   2272 	int i, j;
   2273 
   2274 	(void) fprintf(fp, "Will%s respond to wildcard queries.\n",
   2275 	    (glob_svc.se_flags & SEF_NOWILD) ? " not" : "");
   2276 	(void) fprintf(fp,
   2277 	    "Global debug level %d, log to %s; current level %d\n",
   2278 	    glob_svc.se_debug,
   2279 	    ((glob_svc.se_log == NULL || *glob_svc.se_log == '\0') ?
   2280 		"syslog" : glob_svc.se_log),
   2281 	    log_level);
   2282 	if (cur_options == NULL) {
   2283 		(void) fprintf(fp, "No current configuration.\n");
   2284 		return;
   2285 	}
   2286 	(void) fprintf(fp, "Current configuration:\n");
   2287 	(void) fprintf(fp, "    %d device(s):\n", cur_options->os_ndevices);
   2288 	dep = cur_options->os_devices;
   2289 	for (i = 0; i < cur_options->os_ndevices; i++, dep++) {
   2290 		(void) fprintf(fp, "\t%s: %d service(s):\n",
   2291 		    dep->de_name, dep->de_nservices);
   2292 		sepp = dep->de_services;
   2293 		for (j = 0; j < dep->de_nservices; j++, sepp++) {
   2294 			sep = *sepp;
   2295 			(void) fprintf(fp, "\t    %s: debug level %d",
   2296 			    sep->se_name, sep->se_debug);
   2297 			if (sep->se_flags & SEF_UIDSET)
   2298 				(void) fprintf(fp, ", UID %ld", sep->se_uid);
   2299 			if (sep->se_flags & SEF_GIDSET)
   2300 				(void) fprintf(fp, ", GID %ld", sep->se_gid);
   2301 			if (sep->se_flags & SEF_WILD)
   2302 				(void) fprintf(fp, ", wildcard");
   2303 			else if (sep->se_flags & SEF_NOWILD)
   2304 				(void) fprintf(fp, ", nowildcard");
   2305 			else
   2306 				(void) fprintf(fp, ", wildcard (default)");
   2307 			(void) putc('\n', fp);
   2308 			if (sep->se_server != NULL)
   2309 				(void) fprintf(fp, "\t\tserver \"%s\"\n",
   2310 				    sep->se_server);
   2311 			if (sep->se_pppd != NULL)
   2312 				(void) fprintf(fp, "\t\tpppd \"%s\"\n",
   2313 				    sep->se_pppd);
   2314 			if (sep->se_path != NULL)
   2315 				(void) fprintf(fp, "\t\tpath \"%s\"\n",
   2316 				    sep->se_path);
   2317 			if (sep->se_extra != NULL)
   2318 				(void) fprintf(fp, "\t\textra \"%s\"\n",
   2319 				    sep->se_extra);
   2320 			if (sep->se_log != NULL)
   2321 				(void) fprintf(fp, "\t\tlog \"%s\"\n",
   2322 				    sep->se_log);
   2323 			if (sep->se_flist != NULL) {
   2324 				(void) fprintf(fp, "\t\tfilter list:\n");
   2325 				print_filter_list(fp, sep->se_flist);
   2326 			}
   2327 		}
   2328 	}
   2329 	(void) fprintf(fp, "\nConfiguration read from:\n");
   2330 	for (pfp = cur_options->os_pfjunk; pfp != NULL; pfp = pfp->pf_prev) {
   2331 		(void) fprintf(fp, "    %s: %d service(s)\n", pfp->pf_name,
   2332 		    pfp->pf_nsvc);
   2333 	}
   2334 }
   2335