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