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