revise environment handling.
authortedu <tedu@openbsd.org>
Mon, 27 Jun 2016 15:41:17 +0000 (15:41 +0000)
committertedu <tedu@openbsd.org>
Mon, 27 Jun 2016 15:41:17 +0000 (15:41 +0000)
Add a setenv keyword for manipulating the environment. keepenv now means
only retain everything. (for one release, the old use of keepenv will still
work.)
Allow setting variables to new or existing values, and also removing vars
when keepenv is used.
ok djm martijn tb

usr.bin/doas/doas.conf.5
usr.bin/doas/env.c
usr.bin/doas/parse.y

index e246942..0e8da08 100644 (file)
@@ -1,4 +1,4 @@
-.\" $OpenBSD: doas.conf.5,v 1.26 2016/06/11 17:17:10 tedu Exp $
+.\" $OpenBSD: doas.conf.5,v 1.27 2016/06/27 15:41:17 tedu Exp $
 .\"
 .\"Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
 .\"
@@ -13,7 +13,7 @@
 .\"WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 .\"ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\"OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.Dd $Mdocdate: June 11 2016 $
+.Dd $Mdocdate: June 27 2016 $
 .Dt DOAS.CONF 5
 .Os
 .Sh NAME
@@ -59,9 +59,16 @@ The default is to reset the environment, except for the variables
 .Ev USER
 and
 .Ev USERNAME .
-.It Ic keepenv { Oo Ar variable ... Oc Ic }
+.It Ic setenv { Oo Ar variable ... Oc Ic Oo Ar variable=value ... Oc Ic }
 In addition to the variables mentioned above, keep the space-separated
 specified variables.
+Variables may also be removed with a leading - or set using the latter syntax.
+If the first character of
+.Ar value
+is a
+.Ql $
+then the value to be set is taken from the existing environment
+variable of the same name.
 .El
 .It Ar identity
 The username to match.
@@ -112,22 +119,24 @@ it is not considered a keyword.
 The following example permits users in group wsrc to build ports,
 wheel to execute commands as any user while keeping the environment
 variables
-.Ev ENV ,
-.Ev PS1 ,
+.Ev PS1
+and
+.Ev SSH_AUTH_SOCK
 and
-.Ev SSH_AUTH_SOCK ,
+unsetting
+.Ev ENV ,
 permits tedu to run procmap as root without a password,
 and additionally permits root to run unrestricted commands as itself.
 .Bd -literal -offset indent
 # Non-exhaustive list of variables needed to
 # build release(8) and ports(7)
-permit nopass keepenv { \e
+permit nopass setenv { \e
         FTPMODE PKG_CACHE PKG_PATH SM_PATH SSH_AUTH_SOCK \e
         DESTDIR DISTDIR FETCH_CMD FLAVOR GROUP MAKE MAKECONF \e
         MULTI_PACKAGES NOMAN OKAY_FILES OWNER PKG_DBDIR \e
         PKG_DESTDIR PKG_TMPDIR PORTSDIR RELEASEDIR SHARED_ONLY \e
         SUBPACKAGE WRKOBJDIR SUDO_PORT_V1 } :wsrc
-permit nopass keepenv { ENV PS1 SSH_AUTH_SOCK } :wheel
+permit nopass setenv { -ENV PS1=$DOAS_PS1 SSH_AUTH_SOCK } :wheel
 permit nopass tedu as root cmd /usr/sbin/procmap
 permit nopass keepenv root as root
 .Ed
index 2f67602..3938e5c 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: env.c,v 1.2 2016/06/19 19:29:43 martijn Exp $ */
+/* $OpenBSD: env.c,v 1.3 2016/06/27 15:41:17 tedu Exp $ */
 /*
  * Copyright (c) 2016 Ted Unangst <tedu@openbsd.org>
  *
@@ -38,19 +38,38 @@ struct env {
        u_int count;
 };
 
-int
+static int
 envcmp(struct envnode *a, struct envnode *b)
 {
        return strcmp(a->key, b->key);
 }
 RB_GENERATE_STATIC(envtree, envnode, node, envcmp)
 
-struct env *createenv(char **);
-struct env *filterenv(struct env *, struct rule *);
-char **flattenenv(struct env *);
+static struct envnode *
+createnode(const char *key, const char *value)
+{
+       struct envnode *node;
 
-struct env *
-createenv(char **envp)
+       node = malloc(sizeof(*node));
+       if (!node)
+               err(1, NULL);
+       node->key = strdup(key);
+       node->value = strdup(value);
+       if (!node->key || !node->value)
+               err(1, NULL);
+       return node;
+}
+
+static void
+freenode(struct envnode *node)
+{
+       free((char *)node->key);
+       free((char *)node->value);
+       free(node);
+}
+
+static struct env *
+createenv(struct rule *rule)
 {
        struct env *env;
        u_int i;
@@ -61,34 +80,40 @@ createenv(char **envp)
        RB_INIT(&env->root);
        env->count = 0;
 
-       for (i = 0; envp[i] != NULL; i++) {
-               struct envnode *node;
-               const char *e, *eq;
-
-               e = envp[i];
-
-               if ((eq = strchr(e, '=')) == NULL || eq == e)
-                       continue;
-               node = malloc(sizeof(*node));
-               if (!node)
-                       err(1, NULL);
-               node->key = strndup(envp[i], eq - e);
-               node->value = strdup(eq + 1);
-               if (!node->key || !node->value)
-                       err(1, NULL);
-               if (RB_FIND(envtree, &env->root, node)) {
-                       free((char *)node->key);
-                       free((char *)node->value);
-                       free(node);
-               } else {
-                       RB_INSERT(envtree, &env->root, node);
-                       env->count++;
+       if (rule->options & KEEPENV) {
+               extern const char **environ;
+
+               for (i = 0; environ[i] != NULL; i++) {
+                       struct envnode *node;
+                       const char *e, *eq;
+                       size_t len;
+                       char keybuf[1024];
+
+                       e = environ[i];
+
+                       /* ignore invalid or overlong names */
+                       if ((eq = strchr(e, '=')) == NULL || eq == e)
+                               continue;
+                       len = eq - e;
+                       if (len > sizeof(keybuf) - 1)
+                               continue;
+                       memcpy(keybuf, e, len);
+                       keybuf[len] = '\0';
+
+                       node = createnode(keybuf, eq + 1);
+                       if (RB_INSERT(envtree, &env->root, node)) {
+                               /* ignore any later duplicates */
+                               freenode(node);
+                       } else {
+                               env->count++;
+                       }
                }
        }
+
        return env;
 }
 
-char **
+static char **
 flattenenv(struct env *env)
 {
        char **envp;
@@ -109,72 +134,74 @@ flattenenv(struct env *env)
 }
 
 static void
-copyenv(struct env *orig, struct env *copy, const char **envlist)
+fillenv(struct env *env, const char **envlist)
 {
        struct envnode *node, key;
+       const char *e, *eq;
+       const char *val;
+       char name[1024];
        u_int i;
+       size_t len;
 
        for (i = 0; envlist[i]; i++) {
-               key.key = envlist[i];
-               if ((node = RB_FIND(envtree, &orig->root, &key))) {
-                       RB_REMOVE(envtree, &orig->root, node);
-                       orig->count--;
-                       RB_INSERT(envtree, &copy->root, node);
-                       copy->count++;
+               e = envlist[i];
+
+               /* parse out env name */
+               if ((eq = strchr(e, '=')) == NULL)
+                       len = strlen(e);
+               else
+                       len = eq - e;
+               if (len > sizeof(name) - 1)
+                       continue;
+               memcpy(name, e, len);
+               name[len] = '\0';
+
+               /* delete previous copies */
+               key.key = name;
+               if (*name == '-')
+                       key.key = name + 1;
+               if ((node = RB_FIND(envtree, &env->root, &key))) {
+                       RB_REMOVE(envtree, &env->root, node);
+                       freenode(node);
+                       env->count--;
+               }
+               if (*name == '-')
+                       continue;
+
+               /* assign value or inherit from environ */
+               if (eq) {
+                       val = eq + 1;
+                       if (*val == '$')
+                               val = getenv(val + 1);
+               } else {
+                       val = getenv(name);
+               }
+               /* at last, we have something to insert */
+               if (val) {
+                       node = createnode(name, val);
+                       RB_INSERT(envtree, &env->root, node);
+                       env->count++;
                }
        }
 }
 
-struct env *
-filterenv(struct env *orig, struct rule *rule)
+char **
+prepenv(struct rule *rule)
 {
-       const char *safeset[] = {
+       static const char *safeset[] = {
                "DISPLAY", "HOME", "LOGNAME", "MAIL",
                "PATH", "TERM", "USER", "USERNAME",
                NULL
        };
-       const char *badset[] = {
-               "ENV",
-               NULL
-       };
-       struct env *copy;
-       struct envnode *node, key;
-       u_int i;
-
-       if ((rule->options & KEEPENV) && !rule->envlist) {
-               for (i = 0; badset[i]; i++) {
-                       key.key = badset[i];
-                       if ((node = RB_FIND(envtree, &orig->root, &key))) {
-                               RB_REMOVE(envtree, &orig->root, node);
-                               free((char *)node->key);
-                               free((char *)node->value);
-                               free(node);
-                               orig->count--;
-                       }
-               }
-               return orig;
-       }
-
-       copy = malloc(sizeof(*copy));
-       if (!copy)
-               err(1, NULL);
-       RB_INIT(&copy->root);
-       copy->count = 0;
+       struct env *env;
+       
+       env = createenv(rule);
 
+       /* if we started with blank, fill some defaults then apply rules */
+       if (!(rule->options & KEEPENV))
+               fillenv(env, safeset);
        if (rule->envlist)
-               copyenv(orig, copy, rule->envlist);
-       copyenv(orig, copy, safeset);
-
-       return copy;
-}
+               fillenv(env, rule->envlist);
 
-char **
-prepenv(struct rule *rule)
-{
-       extern char **environ;
-       struct env *env;
-       
-       env = createenv(environ);
-       env = filterenv(env, rule);
        return flattenenv(env);
 }
index 702b457..7e68cb7 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.18 2016/06/07 16:49:23 tedu Exp $ */
+/* $OpenBSD: parse.y,v 1.19 2016/06/27 15:41:17 tedu Exp $ */
 /*
  * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
  *
@@ -48,6 +48,7 @@ FILE *yyfp;
 struct rule **rules;
 int nrules, maxrules;
 int parse_errors = 0;
+int obsolete_warned = 0;
 
 void yyerror(const char *, ...);
 int yylex(void);
@@ -56,7 +57,7 @@ int yyparse(void);
 %}
 
 %token TPERMIT TDENY TAS TCMD TARGS
-%token TNOPASS TKEEPENV
+%token TNOPASS TKEEPENV TSETENV
 %token TSTRING
 
 %%
@@ -97,15 +98,19 @@ action:             TPERMIT options {
                        $$.envlist = $2.envlist;
                } | TDENY {
                        $$.action = DENY;
+                       $$.options = 0;
+                       $$.envlist = NULL;
                } ;
 
-options:       /* none */
-               | options option {
+options:       /* none */ {
+                       $$.options = 0;
+                       $$.envlist = NULL;
+               } | options option {
                        $$.options = $1.options | $2.options;
                        $$.envlist = $1.envlist;
                        if ($2.envlist) {
                                if ($$.envlist) {
-                                       yyerror("can't have two keepenv sections");
+                                       yyerror("can't have two setenv sections");
                                        YYERROR;
                                } else
                                        $$.envlist = $2.envlist;
@@ -113,10 +118,19 @@ options:  /* none */
                } ;
 option:                TNOPASS {
                        $$.options = NOPASS;
+                       $$.envlist = NULL;
                } | TKEEPENV {
                        $$.options = KEEPENV;
+                       $$.envlist = NULL;
                } | TKEEPENV '{' envlist '}' {
-                       $$.options = KEEPENV;
+                       $$.options = 0;
+                       if (!obsolete_warned) {
+                               warnx("keepenv with list is obsolete");
+                               obsolete_warned = 1;
+                       }
+                       $$.envlist = $3.envlist;
+               } | TSETENV '{' envlist '}' {
+                       $$.options = 0;
                        $$.envlist = $3.envlist;
                } ;
 
@@ -195,6 +209,7 @@ struct keyword {
        { "args", TARGS },
        { "nopass", TNOPASS },
        { "keepenv", TKEEPENV },
+       { "setenv", TSETENV },
 };
 
 int