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