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