]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/options.c
Fix quote handling in configuration files to be more like shell quoting.
[ppp.git] / pppd / options.c
index f9f10b74ca4859a1d9181f1629ace04d69691eb1..008b48217eeef2f2c3a722566c3cfea1ad78d5d1 100644 (file)
@@ -40,7 +40,7 @@
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID  "$Id: options.c,v 1.93 2004/10/28 00:15:08 paulus Exp $"
+#define RCSID  "$Id: options.c,v 1.102 2008/06/15 06:53:06 paulus Exp $"
 
 #include <ctype.h>
 #include <stdio.h>
 #ifdef PPP_FILTER
 #include <pcap.h>
 /*
- * DLT_PPP_WITH_DIRECTION is in current libpcap cvs, and should be in
- * libpcap-0.8.4.  Until that is released, use DLT_PPP - but that means
+ * There have been 3 or 4 different names for this in libpcap CVS, but
+ * this seems to be what they have settled on...
+ * For older versions of libpcap, use DLT_PPP - but that means
  * we lose the inbound and outbound qualifiers.
  */
-#ifndef DLT_PPP_WITH_DIRECTION
-#define DLT_PPP_WITH_DIRECTION DLT_PPP
+#ifndef DLT_PPP_PPPD
+#ifdef DLT_PPP_WITHDIRECTION
+#define DLT_PPP_PPPD   DLT_PPP_WITHDIRECTION
+#else
+#define DLT_PPP_PPPD   DLT_PPP
 #endif
 #endif
+#endif /* PPP_FILTER */
 
 #include "pppd.h"
 #include "pathnames.h"
@@ -113,6 +118,8 @@ char        *bundle_name = NULL;    /* bundle name for multilink */
 bool   dump_options;           /* print out option values */
 bool   dryrun;                 /* print out option values and exit */
 char   *domain;                /* domain name set by domain option */
+int    child_wait = 5;         /* # seconds to wait for children at exit */
+struct userenv *userenv_list;  /* user environment variables */
 
 #ifdef MAXOCTETS
 unsigned int  maxoctets = 0;    /* default - no limit */
@@ -129,6 +136,7 @@ struct      bpf_program pass_filter;/* Filter program for packets to pass */
 struct bpf_program active_filter; /* Filter program for link-active pkts */
 #endif
 
+static option_t *curopt;       /* pointer to option being processed */
 char *current_option;          /* the name of the option being parsed */
 int  privileged_option;                /* set iff the current option came from root */
 char *option_source;           /* string saying where the option came from */
@@ -161,6 +169,11 @@ static int setactivefilter __P((char **));
 static int setmodir __P((char **));
 #endif
 
+static int user_setenv __P((char **));
+static void user_setprint __P((option_t *, printer_func, void *));
+static int user_unsetenv __P((char **));
+static void user_unsetprint __P((option_t *, printer_func, void *));
+
 static option_t *find_option __P((const char *name));
 static int process_option __P((option_t *, char *, char **));
 static int n_arguments __P((option_t *));
@@ -198,7 +211,8 @@ option_t general_options[] = {
       OPT_PRIOSUB | OPT_A2CLR | 1, &nodetach },
 
     { "holdoff", o_int, &holdoff,
-      "Set time in seconds before retrying connection", OPT_PRIO },
+      "Set time in seconds before retrying connection",
+      OPT_PRIO, &holdoff_specified },
 
     { "idle", o_int, &idle_time_limit,
       "Set time in seconds before disconnecting idle link", OPT_PRIO },
@@ -270,6 +284,17 @@ option_t general_options[] = {
     { "dryrun", o_bool, &dryrun,
       "Stop after parsing, printing, and checking options", 1 },
 
+    { "child-timeout", o_int, &child_wait,
+      "Number of seconds to wait for child processes at exit",
+      OPT_PRIO },
+
+    { "set", o_special, (void *)user_setenv,
+      "Set user environment variable",
+      OPT_A2PRINTER | OPT_NOPRINT, (void *)user_setprint },
+    { "unset", o_special, (void *)user_unsetenv,
+      "Unset user environment variable",
+      OPT_A2PRINTER | OPT_NOPRINT, (void *)user_unsetprint },
+
 #ifdef HAVE_MULTILINK
     { "multilink", o_bool, &multilink,
       "Enable multilink operation", OPT_PRIO | 1 },
@@ -290,10 +315,10 @@ option_t general_options[] = {
 #endif
 
 #ifdef PPP_FILTER
-    { "pass-filter", 1, setpassfilter,
+    { "pass-filter", o_special, setpassfilter,
       "set filter for packets to pass", OPT_PRIO },
 
-    { "active-filter", 1, setactivefilter,
+    { "active-filter", o_special, setactivefilter,
       "set filter for active pkts", OPT_PRIO },
 #endif
 
@@ -388,16 +413,20 @@ options_from_file(filename, must_exist, check_prot, priv)
     option_t *opt;
     int oldpriv, n;
     char *oldsource;
+    uid_t euid;
     char *argv[MAXARGS];
     char args[MAXARGS][MAXWORDLEN];
     char cmd[MAXWORDLEN];
 
-    if (check_prot)
-       seteuid(getuid());
+    euid = geteuid();
+    if (check_prot && seteuid(getuid()) == -1) {
+       option_error("unable to drop privileges to open %s: %m", filename);
+       return 0;
+    }
     f = fopen(filename, "r");
     err = errno;
-    if (check_prot)
-       seteuid(0);
+    if (check_prot && seteuid(euid) == -1)
+       fatal("unable to regain privileges");
     if (f == NULL) {
        errno = err;
        if (!must_exist) {
@@ -753,20 +782,24 @@ process_option(opt, cmd, argv)
     case o_special_noarg:
     case o_special:
        parser = (int (*) __P((char **))) opt->addr;
+       curopt = opt;
        if (!(*parser)(argv))
            return 0;
        if (opt->flags & OPT_A2LIST) {
-           struct option_value *ovp, **pp;
+           struct option_value *ovp, *pp;
 
            ovp = malloc(sizeof(*ovp) + strlen(*argv));
            if (ovp != 0) {
                strcpy(ovp->value, *argv);
                ovp->source = option_source;
                ovp->next = NULL;
-               pp = (struct option_value **) &opt->addr2;
-               while (*pp != 0)
-                   pp = &(*pp)->next;
-               *pp = ovp;
+               if (opt->addr2 == NULL) {
+                   opt->addr2 = ovp;
+               } else {
+                   for (pp = opt->addr2; pp->next != NULL; pp = pp->next)
+                       ;
+                   pp->next = ovp;
+               }
            }
        }
        break;
@@ -778,6 +811,10 @@ process_option(opt, cmd, argv)
        break;
     }
 
+    /*
+     * If addr2 wasn't used by any flag (OPT_A2COPY, etc.) but is set,
+     * treat it as a bool and set/clear it based on the OPT_A2CLR bit.
+     */
     if (opt->addr2 && (opt->flags & (OPT_A2COPY|OPT_ENABLE
                |OPT_A2PRINTER|OPT_A2STRVAL|OPT_A2LIST|OPT_A2OR)) == 0)
        *(bool *)(opt->addr2) = !(opt->flags & OPT_A2CLR);
@@ -859,7 +896,7 @@ check_options()
 static void
 print_option(opt, mainopt, printer, arg)
     option_t *opt, *mainopt;
-    void (*printer) __P((void *, char *, ...));
+    printer_func printer;
     void *arg;
 {
        int i, v;
@@ -922,11 +959,8 @@ print_option(opt, mainopt, printer, arg)
                        printer(arg, " ");
                }
                if (opt->flags & OPT_A2PRINTER) {
-                       void (*oprt) __P((option_t *,
-                                         void ((*)__P((void *, char *, ...))),
-                                         void *));
-                       oprt = (void (*) __P((option_t *,
-                                        void ((*)__P((void *, char *, ...))),
+                       void (*oprt) __P((option_t *, printer_func, void *));
+                       oprt = (void (*) __P((option_t *, printer_func,
                                         void *)))opt->addr2;
                        (*oprt)(opt, printer, arg);
                } else if (opt->flags & OPT_A2STRVAL) {
@@ -964,7 +998,7 @@ print_option(opt, mainopt, printer, arg)
 static void
 print_option_list(opt, printer, arg)
     option_t *opt;
-    void (*printer) __P((void *, char *, ...));
+    printer_func printer;
     void *arg;
 {
        while (opt->name != NULL) {
@@ -982,7 +1016,7 @@ print_option_list(opt, printer, arg)
  */
 void
 print_options(printer, arg)
-    void (*printer) __P((void *, char *, ...));
+    printer_func printer;
     void *arg;
 {
        struct option_list *list;
@@ -1112,6 +1146,7 @@ getword(f, word, newlinep, filename)
     len = 0;
     escape = 0;
     comment = 0;
+    quoted = 0;
 
     /*
      * First skip white-space and comments.
@@ -1169,15 +1204,6 @@ getword(f, word, newlinep, filename)
            break;
     }
 
-    /*
-     * Save the delimiter for quoted strings.
-     */
-    if (!escape && (c == '"' || c == '\'')) {
-        quoted = c;
-       c = getc(f);
-    } else
-        quoted = 0;
-
     /*
      * Process characters until the end of the word.
      */
@@ -1266,29 +1292,34 @@ getword(f, word, newlinep, filename)
            if (!got)
                c = getc(f);
            continue;
-
        }
 
        /*
-        * Not escaped: see if we've reached the end of the word.
+        * Backslash starts a new escape sequence.
         */
-       if (quoted) {
-           if (c == quoted)
-               break;
-       } else {
-           if (isspace(c) || c == '#') {
-               ungetc (c, f);
-               break;
-           }
+       if (c == '\\') {
+           escape = 1;
+           c = getc(f);
+           continue;
        }
 
        /*
-        * Backslash starts an escape sequence.
+        * Not escaped: check for the start or end of a quoted
+        * section and see if we've reached the end of the word.
         */
-       if (c == '\\') {
-           escape = 1;
+       if (quoted) {
+           if (c == quoted) {
+               quoted = 0;
+               c = getc(f);
+               continue;
+           }
+       } else if (c == '"' || c == '\'') {
+           quoted = c;
            c = getc(f);
            continue;
+       } else if (isspace(c) || c == '#') {
+           ungetc (c, f);
+           break;
        }
 
        /*
@@ -1317,6 +1348,9 @@ getword(f, word, newlinep, filename)
         */
        if (len == 0)
            return 0;
+       if (quoted)
+           option_error("warning: quoted word runs to end of file (%.20s...)",
+                        filename, word);
     }
 
     /*
@@ -1441,13 +1475,13 @@ setpassfilter(argv)
     char **argv;
 {
     pcap_t *pc;
-    int ret = 0;
+    int ret = 1;
 
-    pc = pcap_open_dead(DLT_PPP_WITH_DIRECTION, 65535);
+    pc = pcap_open_dead(DLT_PPP_PPPD, 65535);
     if (pcap_compile(pc, &pass_filter, *argv, 1, netmask) == -1) {
        option_error("error in pass-filter expression: %s\n",
                     pcap_geterr(pc));
-       ret = 1;
+       ret = 0;
     }
     pcap_close(pc);
 
@@ -1462,13 +1496,13 @@ setactivefilter(argv)
     char **argv;
 {
     pcap_t *pc;
-    int ret = 0;
+    int ret = 1;
 
-    pc = pcap_open_dead(DLT_PPP_WITH_DIRECTION, 65535);
+    pc = pcap_open_dead(DLT_PPP_PPPD, 65535);
     if (pcap_compile(pc, &active_filter, *argv, 1, netmask) == -1) {
        option_error("error in active-filter expression: %s\n",
                     pcap_geterr(pc));
-       ret = 1;
+       ret = 0;
     }
     pcap_close(pc);
 
@@ -1499,15 +1533,19 @@ setlogfile(argv)
     char **argv;
 {
     int fd, err;
+    uid_t euid;
 
-    if (!privileged_option)
-       seteuid(getuid());
+    euid = geteuid();
+    if (!privileged_option && seteuid(getuid()) == -1) {
+       option_error("unable to drop permissions to open %s: %m", *argv);
+       return 0;
+    }
     fd = open(*argv, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0644);
     if (fd < 0 && errno == EEXIST)
        fd = open(*argv, O_WRONLY | O_APPEND);
     err = errno;
-    if (!privileged_option)
-       seteuid(0);
+    if (!privileged_option && seteuid(euid) == -1)
+       fatal("unable to regain privileges: %m");
     if (fd < 0) {
        errno = err;
        option_error("Can't open log file %s: %m", *argv);
@@ -1597,3 +1635,154 @@ loadplugin(argv)
     return 0;
 }
 #endif /* PLUGIN */
+
+/*
+ * Set an environment variable specified by the user.
+ */
+static int
+user_setenv(argv)
+    char **argv;
+{
+    char *arg = argv[0];
+    char *eqp;
+    struct userenv *uep, **insp;
+
+    if ((eqp = strchr(arg, '=')) == NULL) {
+       option_error("missing = in name=value: %s", arg);
+       return 0;
+    }
+    if (eqp == arg) {
+       option_error("missing variable name: %s", arg);
+       return 0;
+    }
+    for (uep = userenv_list; uep != NULL; uep = uep->ue_next) {
+       int nlen = strlen(uep->ue_name);
+       if (nlen == (eqp - arg) &&
+           strncmp(arg, uep->ue_name, nlen) == 0)
+           break;
+    }
+    /* Ignore attempts by unprivileged users to override privileged sources */
+    if (uep != NULL && !privileged_option && uep->ue_priv)
+       return 1;
+    /* The name never changes, so allocate it with the structure */
+    if (uep == NULL) {
+       uep = malloc(sizeof (*uep) + (eqp-arg));
+       strncpy(uep->ue_name, arg, eqp-arg);
+       uep->ue_name[eqp-arg] = '\0';
+       uep->ue_next = NULL;
+       insp = &userenv_list;
+       while (*insp != NULL)
+           insp = &(*insp)->ue_next;
+       *insp = uep;
+    } else {
+       struct userenv *uep2;
+       for (uep2 = userenv_list; uep2 != NULL; uep2 = uep2->ue_next) {
+           if (uep2 != uep && !uep2->ue_isset)
+               break;
+       }
+       if (uep2 == NULL && !uep->ue_isset)
+           find_option("unset")->flags |= OPT_NOPRINT;
+       free(uep->ue_value);
+    }
+    uep->ue_isset = 1;
+    uep->ue_priv = privileged_option;
+    uep->ue_source = option_source;
+    uep->ue_value = strdup(eqp + 1);
+    curopt->flags &= ~OPT_NOPRINT;
+    return 1;
+}
+
+static void
+user_setprint(opt, printer, arg)
+    option_t *opt;
+    printer_func printer;
+    void *arg;
+{
+    struct userenv *uep, *uepnext;
+
+    uepnext = userenv_list;
+    while (uepnext != NULL && !uepnext->ue_isset)
+       uepnext = uepnext->ue_next;
+    while ((uep = uepnext) != NULL) {
+       uepnext = uep->ue_next;
+       while (uepnext != NULL && !uepnext->ue_isset)
+           uepnext = uepnext->ue_next;
+       (*printer)(arg, "%s=%s", uep->ue_name, uep->ue_value);
+       if (uepnext != NULL)
+           (*printer)(arg, "\t\t# (from %s)\n%s ", uep->ue_source, opt->name);
+       else
+           opt->source = uep->ue_source;
+    }
+}
+
+static int
+user_unsetenv(argv)
+    char **argv;
+{
+    struct userenv *uep, **insp;
+    char *arg = argv[0];
+
+    if (strchr(arg, '=') != NULL) {
+       option_error("unexpected = in name: %s", arg);
+       return 0;
+    }
+    if (arg == '\0') {
+       option_error("missing variable name for unset");
+       return 0;
+    }
+    for (uep = userenv_list; uep != NULL; uep = uep->ue_next) {
+       if (strcmp(arg, uep->ue_name) == 0)
+           break;
+    }
+    /* Ignore attempts by unprivileged users to override privileged sources */
+    if (uep != NULL && !privileged_option && uep->ue_priv)
+       return 1;
+    /* The name never changes, so allocate it with the structure */
+    if (uep == NULL) {
+       uep = malloc(sizeof (*uep) + strlen(arg));
+       strcpy(uep->ue_name, arg);
+       uep->ue_next = NULL;
+       insp = &userenv_list;
+       while (*insp != NULL)
+           insp = &(*insp)->ue_next;
+       *insp = uep;
+    } else {
+       struct userenv *uep2;
+       for (uep2 = userenv_list; uep2 != NULL; uep2 = uep2->ue_next) {
+           if (uep2 != uep && uep2->ue_isset)
+               break;
+       }
+       if (uep2 == NULL && uep->ue_isset)
+           find_option("set")->flags |= OPT_NOPRINT;
+       free(uep->ue_value);
+    }
+    uep->ue_isset = 0;
+    uep->ue_priv = privileged_option;
+    uep->ue_source = option_source;
+    uep->ue_value = NULL;
+    curopt->flags &= ~OPT_NOPRINT;
+    return 1;
+}
+
+static void
+user_unsetprint(opt, printer, arg)
+    option_t *opt;
+    printer_func printer;
+    void *arg;
+{
+    struct userenv *uep, *uepnext;
+
+    uepnext = userenv_list;
+    while (uepnext != NULL && uepnext->ue_isset)
+       uepnext = uepnext->ue_next;
+    while ((uep = uepnext) != NULL) {
+       uepnext = uep->ue_next;
+       while (uepnext != NULL && uepnext->ue_isset)
+           uepnext = uepnext->ue_next;
+       (*printer)(arg, "%s", uep->ue_name);
+       if (uepnext != NULL)
+           (*printer)(arg, "\t\t# (from %s)\n%s ", uep->ue_source, opt->name);
+       else
+           opt->source = uep->ue_source;
+    }
+}