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