Add set and reset options to control environment variables for scripts.
authorJames Carlson <carlsonj@workingcode.com>
Mon, 23 Aug 2010 13:59:56 +0000 (09:59 -0400)
committerJames Carlson <carlsonj@workingcode.com>
Mon, 23 Aug 2010 13:59:56 +0000 (09:59 -0400)
pppd/main.c
pppd/options.c
pppd/pppd.8
pppd/pppd.h
pppd/utils.c

index 014d614773d1330931f07cd19a3573f5949b11f4..1b513ca3030e1d621625ea6fedf8deb8a4ba0b2a 100644 (file)
@@ -1615,6 +1615,52 @@ safe_fork(int infd, int outfd, int errfd)
        return 0;
 }
 
+static bool
+add_script_env(pos, newstring)
+    int pos;
+    char *newstring;
+{
+    if (pos + 1 >= s_env_nalloc) {
+       int new_n = pos + 17;
+       char **newenv = realloc(script_env, new_n * sizeof(char *));
+       if (newenv == NULL) {
+           free(newstring - 1);
+           return 0;
+       }
+       script_env = newenv;
+       s_env_nalloc = new_n;
+    }
+    script_env[pos] = newstring;
+    script_env[pos + 1] = NULL;
+    return 1;
+}
+
+static void
+remove_script_env(pos)
+    int pos;
+{
+    free(script_env[pos] - 1);
+    while ((script_env[pos] = script_env[pos + 1]) != NULL)
+       pos++;
+}
+
+/*
+ * update_system_environment - process the list of set/unset options
+ * and update the system environment.
+ */
+static void
+update_system_environment()
+{
+    struct userenv *uep;
+
+    for (uep = userenv_list; uep != NULL; uep = uep->ue_next) {
+       if (uep->ue_isset)
+           setenv(uep->ue_name, uep->ue_value, 1);
+       else
+           unsetenv(uep->ue_name);
+    }
+}
+
 /*
  * device_script - run a program to talk to the specified fds
  * (e.g. to run the connector or disconnector script).
@@ -1670,6 +1716,7 @@ device_script(program, in, out, dont_wait)
        fprintf(stderr, "pppd: setuid failed\n");
        exit(1);
     }
+    update_system_environment();
     execl("/bin/sh", "sh", "-c", program, (char *)0);
     perror("pppd: could not exec /bin/sh");
     exit(99);
@@ -1677,6 +1724,44 @@ device_script(program, in, out, dont_wait)
 }
 
 
+/*
+ * update_script_environment - process the list of set/unset options
+ * and update the script environment.  Note that we intentionally do
+ * not update the TDB.  These changes are layered on top right before
+ * exec.  It is not possible to use script_setenv() or
+ * script_unsetenv() safely after this routine is run.
+ */
+static void
+update_script_environment()
+{
+    struct userenv *uep;
+
+    for (uep = userenv_list; uep != NULL; uep = uep->ue_next) {
+       int i;
+       char *p, *newstring;
+       int nlen = strlen(uep->ue_name);
+
+       for (i = 0; (p = script_env[i]) != NULL; i++) {
+           if (strncmp(p, uep->ue_name, nlen) == 0 && p[nlen] == '=')
+               break;
+       }
+       if (uep->ue_isset) {
+           nlen += strlen(uep->ue_value) + 2;
+           newstring = malloc(nlen + 1);
+           if (newstring == NULL)
+               continue;
+           *newstring++ = 0;
+           slprintf(newstring, nlen, "%s=%s", uep->ue_name, uep->ue_value);
+           if (p != NULL)
+               script_env[i] = newstring;
+           else
+               add_script_env(i, newstring);
+       } else {
+           remove_script_env(i);
+       }
+    }
+}
+
 /*
  * run_program - execute a program with given arguments,
  * but don't wait for it unless wait is non-zero.
@@ -1747,6 +1832,7 @@ run_program(prog, args, must_exist, done, arg, wait)
 #endif
 
     /* run the program */
+    update_script_environment();
     execve(prog, args, script_env);
     if (must_exist || errno != ENOENT) {
        /* have to reopen the log, there's nowhere else
@@ -1967,25 +2053,16 @@ script_setenv(var, value, iskey)
     } else {
        /* no space allocated for script env. ptrs. yet */
        i = 0;
-       script_env = (char **) malloc(16 * sizeof(char *));
-       if (script_env == 0)
+       script_env = malloc(16 * sizeof(char *));
+       if (script_env == 0) {
+           free(newstring - 1);
            return;
+       }
        s_env_nalloc = 16;
     }
 
-    /* reallocate script_env with more space if needed */
-    if (i + 1 >= s_env_nalloc) {
-       int new_n = i + 17;
-       char **newenv = (char **) realloc((void *)script_env,
-                                         new_n * sizeof(char *));
-       if (newenv == 0)
-           return;
-       script_env = newenv;
-       s_env_nalloc = new_n;
-    }
-
-    script_env[i] = newstring;
-    script_env[i+1] = 0;
+    if (!add_script_env(i, newstring))
+       return;
 
 #ifdef USE_TDB
     if (pppdb != NULL) {
@@ -2016,9 +2093,7 @@ script_unsetenv(var)
            if (p[-1] && pppdb != NULL)
                delete_db_key(p);
 #endif
-           free(p-1);
-           while ((script_env[i] = script_env[i+1]) != 0)
-               ++i;
+           remove_script_env(i);
            break;
        }
     }
index 482eab99e3274086d7c6b13aac4b7c1254045c27..434ab95748fb34497af2d3fe79e43789638b1736 100644 (file)
@@ -119,6 +119,7 @@ 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 */
@@ -135,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 */
@@ -167,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 *));
@@ -281,6 +288,13 @@ option_t general_options[] = {
       "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 },
@@ -768,6 +782,7 @@ 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) {
@@ -881,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;
@@ -944,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) {
@@ -986,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) {
@@ -1004,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;
@@ -1623,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;
+    }
+}
index b7adc77f28178d87cb45df17c5e767c19e9cf97a..7ad24c51f3a42678697ec8a66723bd6a29e49e92 100644 (file)
@@ -1018,6 +1018,13 @@ Authentication Protocol] authentication.
 Require the peer to authenticate itself using PAP [Password
 Authentication Protocol] authentication.
 .TP
+.B set \fIname\fR=\fIvalue
+Set an environment variable for scripts that are invoked by pppd.
+When set by a privileged source, the variable specified by \fIname\fR
+cannot be changed by options contained in an unprivileged source.  See
+also the \fIunset\fR option and the environment described in
+\fISCRIPTS\fR.
+.TP
 .B show\-password
 When logging the contents of PAP packets, this option causes pppd to
 show the password string in the log message.
@@ -1058,6 +1065,13 @@ under Linux and FreeBSD 2.2.8 and later.
 Sets the ppp unit number (for a ppp0 or ppp1 etc interface name) for outbound
 connections.
 .TP
+.B unset \fIname
+Remove a variable from the environment variable for scripts that are
+invoked by pppd.  When specified by a privileged source, the variable
+\fIname\fR cannot be set by options contained in an unprivileged
+source.  See also the \fIset\fR option and the environment described
+in \fISCRIPTS\fR.
+.TP
 .B updetach
 With this option, pppd will detach from its controlling terminal once
 it has successfully established the ppp connection (to the point where
index cf9840a416bbd9a57098fdeb9d4d0c10bd8882d4..936cc606f318f959dc21c135f5cab30f2a45a537 100644 (file)
@@ -139,7 +139,7 @@ typedef struct {
 #define OPT_INITONLY   0x4000000 /* option can only be set in init phase */
 #define OPT_DEVEQUIV   0x8000000 /* equiv to device name */
 #define OPT_DEVNAM     (OPT_INITONLY | OPT_DEVEQUIV)
-#define OPT_A2PRINTER  0x10000000 /* *addr2 is a fn for printing option */
+#define OPT_A2PRINTER  0x10000000 /* *addr2 printer_func to print option */
 #define OPT_A2STRVAL   0x20000000 /* *addr2 points to current string value */
 #define OPT_NOPRINT    0x40000000 /* don't print this option at all */
 
@@ -199,6 +199,7 @@ struct epdisc {
 #define EPD_PHONENUM   5
 
 typedef void (*notify_func) __P((void *, int));
+typedef void (*printer_func) __P((void *, char *, ...));
 
 struct notifier {
     struct notifier *next;
@@ -406,8 +407,7 @@ struct protent {
     /* Close the protocol */
     void (*close) __P((int unit, char *reason));
     /* Print a packet in readable form */
-    int  (*printpkt) __P((u_char *pkt, int len,
-                         void (*printer) __P((void *, char *, ...)),
+    int  (*printpkt) __P((u_char *pkt, int len, printer_func printer,
                          void *arg));
     /* Process a received data packet */
     void (*datainput) __P((int unit, u_char *pkt, int len));
@@ -460,6 +460,21 @@ struct channel {
 
 extern struct channel *the_channel;
 
+/*
+ * This structure contains environment variables that are set or unset
+ * by the user.
+ */
+struct userenv {
+       struct userenv *ue_next;
+       char *ue_value;         /* value (set only) */
+       bool ue_isset;          /* 1 for set, 0 for unset */
+       bool ue_priv;           /* from privileged source */
+       const char *ue_source;  /* source name */
+       char ue_name[1];        /* variable name */
+};
+
+extern struct userenv *userenv_list;
+
 /*
  * Prototypes.
  */
@@ -504,8 +519,8 @@ void tty_init __P((void));
 /* Procedures exported from utils.c. */
 void log_packet __P((u_char *, int, char *, int));
                                /* Format a packet and log it with syslog */
-void print_string __P((char *, int,  void (*) (void *, char *, ...),
-               void *));       /* Format a string for output */
+void print_string __P((char *, int,  printer_func, void *));
+                               /* Format a string for output */
 int slprintf __P((char *, int, char *, ...));          /* sprintf++ */
 int vslprintf __P((char *, int, char *, va_list));     /* vsprintf++ */
 size_t strlcpy __P((char *, const char *, size_t));    /* safe strcpy */
@@ -691,7 +706,7 @@ void add_options __P((option_t *)); /* Add extra options */
 void check_options __P((void));        /* check values after all options parsed */
 int  override_value __P((const char *, int, const char *));
                                /* override value if permitted by priority */
-void print_options __P((void (*) __P((void *, char *, ...)), void *));
+void print_options __P((printer_func, void *));
                                /* print out values of all options */
 
 int parse_dotted_ip __P((char *, u_int32_t *));
index 062b17e4a8db64cc11e42c43ffe184219f313f51..5c9ba6437757d6d4a679c2e0f642f65020ef8368 100644 (file)
@@ -68,8 +68,7 @@ extern char *strerror();
 static void logit __P((int, char *, va_list));
 static void log_write __P((int, char *));
 static void vslp_printer __P((void *, char *, ...));
-static void format_packet __P((u_char *, int, void (*) (void *, char *, ...),
-                              void *));
+static void format_packet __P((u_char *, int, printer_func, void *));
 
 struct buffer_info {
     char *ptr;
@@ -475,7 +474,7 @@ static void
 format_packet(p, len, printer, arg)
     u_char *p;
     int len;
-    void (*printer) __P((void *, char *, ...));
+    printer_func printer;
     void *arg;
 {
     int i, n;
@@ -613,7 +612,7 @@ void
 print_string(p, len, printer, arg)
     char *p;
     int len;
-    void (*printer) __P((void *, char *, ...));
+    printer_func printer;
     void *arg;
 {
     int c;