From ab7e77250893f592e8edd3dba0fbfb743837d073 Mon Sep 17 00:00:00 2001 From: tedu Date: Mon, 27 Jun 2016 15:41:17 +0000 Subject: [PATCH] revise environment handling. 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 | 25 ++++-- usr.bin/doas/env.c | 187 ++++++++++++++++++++++----------------- usr.bin/doas/parse.y | 27 ++++-- 3 files changed, 145 insertions(+), 94 deletions(-) diff --git a/usr.bin/doas/doas.conf.5 b/usr.bin/doas/doas.conf.5 index e246942cd7f..0e8da080ce7 100644 --- a/usr.bin/doas/doas.conf.5 +++ b/usr.bin/doas/doas.conf.5 @@ -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 .\" @@ -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 diff --git a/usr.bin/doas/env.c b/usr.bin/doas/env.c index 2f67602391b..3938e5c77c6 100644 --- a/usr.bin/doas/env.c +++ b/usr.bin/doas/env.c @@ -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 * @@ -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, ©->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(©->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/usr.bin/doas/parse.y b/usr.bin/doas/parse.y index 702b45729d3..7e68cb7c5b3 100644 --- a/usr.bin/doas/parse.y +++ b/usr.bin/doas/parse.y @@ -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 * @@ -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 -- 2.20.1