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