]> git.armaanb.net Git - opendoas.git/commitdiff
revise environment handling. Add a setenv keyword for manipulating the environment...
authortedu <tedu>
Mon, 27 Jun 2016 15:41:17 +0000 (15:41 +0000)
committerDuncaen <mail@duncano.de>
Mon, 27 Jun 2016 19:46:31 +0000 (21:46 +0200)
doas.conf.5
env.c
parse.y

index 1dfeaeec62f62c858b29ac28814f6a72df0fff36..6273e726be843a06b69054183b292396d29513cb 100644 (file)
@@ -57,9 +57,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.
@@ -110,21 +117,24 @@ it isn't 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 ,
-and additionally permits tedu to run procmap as root without a password.
+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
 .Ed
 .Sh SEE ALSO
diff --git a/env.c b/env.c
index f25d21f7abc9f0d8b73ca1f2f4ccde926fcfcc2a..42a0d2e1856f8c766c87338ef95b5bd33d74bf3f 100644 (file)
--- a/env.c
+++ b/env.c
@@ -39,19 +39,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;
@@ -62,34 +81,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;
@@ -110,72 +135,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);
 }
diff --git a/parse.y b/parse.y
index 0307b0f39be9d909c10351f894396a3046919546..506eb686bf2fcffc284755185ac548a09db9113d 100644 (file)
--- a/parse.y
+++ b/parse.y
@@ -51,6 +51,7 @@ FILE *yyfp;
 struct rule **rules;
 int nrules, maxrules;
 int parse_errors = 0;
+int obsolete_warned = 0;
 
 void yyerror(const char *, ...);
 int yylex(void);
@@ -59,7 +60,7 @@ int yyparse(void);
 %}
 
 %token TPERMIT TDENY TAS TCMD TARGS
-%token TNOPASS TKEEPENV
+%token TNOPASS TKEEPENV TSETENV
 %token TSTRING
 
 %%
@@ -100,6 +101,8 @@ action:             TPERMIT options {
                        $$.envlist = $2.envlist;
                } | TDENY {
                        $$.action = DENY;
+                       $$.options = 0;
+                       $$.envlist = NULL;
                } ;
 
 options:       /* none */ {
@@ -110,7 +113,7 @@ options:    /* none */ {
                        $$.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;
@@ -123,7 +126,14 @@ option:            TNOPASS {
                        $$.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;
                } ;
 
@@ -199,6 +209,7 @@ struct keyword {
        { "args", TARGS },
        { "nopass", TNOPASS },
        { "keepenv", TKEEPENV },
+       { "setenv", TSETENV },
 };
 
 int