2017-01-23 16:12:56 +00:00
|
|
|
/* $OpenBSD: env.c,v 1.5 2016/09/15 00:58:23 deraadt Exp $ */
|
2016-06-22 15:17:53 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2016 Ted Unangst <tedu@openbsd.org>
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/tree.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2019-03-10 22:11:40 +00:00
|
|
|
#include <string.h>
|
2019-08-03 20:39:15 +00:00
|
|
|
#include <pwd.h>
|
2016-06-22 15:17:53 +00:00
|
|
|
#include <err.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include "doas.h"
|
|
|
|
|
|
|
|
struct envnode {
|
|
|
|
RB_ENTRY(envnode) node;
|
|
|
|
const char *key;
|
|
|
|
const char *value;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct env {
|
|
|
|
RB_HEAD(envtree, envnode) root;
|
|
|
|
u_int count;
|
|
|
|
};
|
|
|
|
|
|
|
|
int
|
|
|
|
envcmp(struct envnode *a, struct envnode *b)
|
|
|
|
{
|
|
|
|
return strcmp(a->key, b->key);
|
|
|
|
}
|
2017-02-15 10:33:34 +00:00
|
|
|
#ifdef __DragonFly__
|
|
|
|
RB_PROTOTYPE_STATIC(envtree, envnode, node, envcmp);
|
|
|
|
#endif
|
2016-06-22 15:17:53 +00:00
|
|
|
RB_GENERATE_STATIC(envtree, envnode, node, envcmp)
|
|
|
|
|
2017-01-23 16:12:56 +00:00
|
|
|
static struct envnode *
|
|
|
|
createnode(const char *key, const char *value)
|
|
|
|
{
|
|
|
|
struct envnode *node;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2016-06-22 15:17:53 +00:00
|
|
|
|
2017-01-23 16:12:56 +00:00
|
|
|
static void
|
|
|
|
freenode(struct envnode *node)
|
|
|
|
{
|
|
|
|
free((char *)node->key);
|
|
|
|
free((char *)node->value);
|
|
|
|
free(node);
|
|
|
|
}
|
|
|
|
|
2019-08-03 20:39:15 +00:00
|
|
|
static void
|
|
|
|
addnode(struct env *env, const char *key, const char *value)
|
|
|
|
{
|
|
|
|
struct envnode *node;
|
|
|
|
|
|
|
|
node = createnode(key, value);
|
|
|
|
RB_INSERT(envtree, &env->root, node);
|
|
|
|
env->count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-28 02:10:57 +00:00
|
|
|
/* Copy an original (possibly static in memory) password structure.
|
|
|
|
Make a deep copy of it so that our original andtarget do not overlap
|
|
|
|
on future calls.
|
|
|
|
*/
|
|
|
|
struct passwd *
|
|
|
|
copyenvpw(struct passwd *my_static)
|
|
|
|
{
|
|
|
|
struct passwd *new_pw;
|
|
|
|
|
|
|
|
if (! my_static)
|
|
|
|
return NULL;
|
|
|
|
new_pw = (struct passwd *) calloc(1, sizeof(struct passwd));
|
|
|
|
if (! new_pw)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
new_pw->pw_name = strdup(my_static->pw_name);
|
|
|
|
new_pw->pw_passwd = strdup(my_static->pw_passwd);
|
|
|
|
new_pw->pw_uid = my_static->pw_uid;
|
|
|
|
new_pw->pw_gid = my_static->pw_gid;
|
2021-05-30 15:27:44 +00:00
|
|
|
#if defined(__FreeBSD__)
|
|
|
|
new_pw->pw_class = strdup(my_static->pw_class);
|
|
|
|
#endif
|
2019-12-28 02:10:57 +00:00
|
|
|
new_pw->pw_gecos = strdup(my_static->pw_gecos);
|
|
|
|
new_pw->pw_dir = strdup(my_static->pw_dir);
|
|
|
|
new_pw->pw_shell = strdup(my_static->pw_shell);
|
|
|
|
return new_pw;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-01-23 16:12:56 +00:00
|
|
|
static struct env *
|
2019-08-03 20:39:15 +00:00
|
|
|
createenv(struct rule *rule, struct passwd *original, struct passwd *target)
|
2016-06-22 15:17:53 +00:00
|
|
|
{
|
|
|
|
struct env *env;
|
|
|
|
u_int i;
|
|
|
|
|
|
|
|
env = malloc(sizeof(*env));
|
|
|
|
if (!env)
|
|
|
|
err(1, NULL);
|
|
|
|
RB_INIT(&env->root);
|
|
|
|
env->count = 0;
|
|
|
|
|
2019-08-03 20:39:15 +00:00
|
|
|
addnode(env, "DOAS_USER", original->pw_name);
|
2019-12-28 02:10:57 +00:00
|
|
|
if (rule->options & KEEPENV)
|
|
|
|
addnode(env, "HOME", original->pw_dir);
|
|
|
|
else
|
|
|
|
addnode(env, "HOME", target->pw_dir);
|
2019-08-03 20:39:15 +00:00
|
|
|
addnode(env, "LOGNAME", target->pw_name);
|
|
|
|
addnode(env, "PATH", GLOBAL_PATH);
|
|
|
|
addnode(env, "SHELL", target->pw_shell);
|
|
|
|
addnode(env, "USER", target->pw_name);
|
|
|
|
|
2017-01-23 16:12:56 +00:00
|
|
|
if (rule->options & KEEPENV) {
|
2019-03-10 22:22:09 +00:00
|
|
|
#ifndef linux
|
|
|
|
extern const char **environ;
|
|
|
|
#endif
|
2017-01-23 16:12:56 +00:00
|
|
|
|
|
|
|
for (i = 0; environ[i] != NULL; i++) {
|
|
|
|
struct envnode *node;
|
|
|
|
const char *e, *eq;
|
|
|
|
size_t len;
|
2021-06-01 17:09:28 +00:00
|
|
|
char name[MAX_ENV_LENGTH];
|
2017-01-23 16:12:56 +00:00
|
|
|
|
|
|
|
e = environ[i];
|
|
|
|
|
|
|
|
/* ignore invalid or overlong names */
|
|
|
|
if ((eq = strchr(e, '=')) == NULL || eq == e)
|
|
|
|
continue;
|
|
|
|
len = eq - e;
|
|
|
|
if (len > sizeof(name) - 1)
|
|
|
|
continue;
|
|
|
|
memcpy(name, e, len);
|
|
|
|
name[len] = '\0';
|
|
|
|
|
|
|
|
node = createnode(name, eq + 1);
|
|
|
|
if (RB_INSERT(envtree, &env->root, node)) {
|
|
|
|
/* ignore any later duplicates */
|
|
|
|
freenode(node);
|
|
|
|
} else {
|
|
|
|
env->count++;
|
|
|
|
}
|
2016-06-22 15:17:53 +00:00
|
|
|
}
|
|
|
|
}
|
2017-01-23 16:12:56 +00:00
|
|
|
|
2016-06-22 15:17:53 +00:00
|
|
|
return env;
|
|
|
|
}
|
|
|
|
|
2017-01-23 16:12:56 +00:00
|
|
|
static char **
|
2016-06-22 15:17:53 +00:00
|
|
|
flattenenv(struct env *env)
|
|
|
|
{
|
|
|
|
char **envp;
|
|
|
|
struct envnode *node;
|
|
|
|
u_int i;
|
|
|
|
|
2017-01-23 16:12:56 +00:00
|
|
|
envp = reallocarray(NULL, env->count + 1, sizeof(char *));
|
2016-06-22 15:17:53 +00:00
|
|
|
if (!envp)
|
|
|
|
err(1, NULL);
|
|
|
|
i = 0;
|
|
|
|
RB_FOREACH(node, envtree, &env->root) {
|
|
|
|
if (asprintf(&envp[i], "%s=%s", node->key, node->value) == -1)
|
|
|
|
err(1, NULL);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
envp[i] = NULL;
|
|
|
|
return envp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-01-23 16:12:56 +00:00
|
|
|
fillenv(struct env *env, const char **envlist)
|
2016-06-22 15:17:53 +00:00
|
|
|
{
|
|
|
|
struct envnode *node, key;
|
2017-01-23 16:12:56 +00:00
|
|
|
const char *e, *eq;
|
|
|
|
const char *val;
|
2021-06-03 14:47:42 +00:00
|
|
|
char name[MAX_ENV_LENGTH];
|
2016-06-22 15:17:53 +00:00
|
|
|
u_int i;
|
2017-01-23 16:12:56 +00:00
|
|
|
size_t len;
|
2016-06-22 15:17:53 +00:00
|
|
|
|
|
|
|
for (i = 0; envlist[i]; i++) {
|
2017-01-23 16:12:56 +00:00
|
|
|
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++;
|
2016-06-22 15:17:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-23 16:12:56 +00:00
|
|
|
char **
|
2019-08-03 20:39:15 +00:00
|
|
|
prepenv(struct rule *rule, struct passwd *original, struct passwd *target)
|
2016-06-22 15:17:53 +00:00
|
|
|
{
|
2019-08-03 20:39:15 +00:00
|
|
|
static const char *safeset[] = {
|
|
|
|
"DISPLAY", "TERM", NULL
|
|
|
|
};
|
|
|
|
|
2017-01-23 16:12:56 +00:00
|
|
|
struct env *env;
|
2016-06-22 15:17:53 +00:00
|
|
|
|
2019-08-03 20:39:15 +00:00
|
|
|
env = createenv(rule, original, target);
|
2016-06-22 15:17:53 +00:00
|
|
|
|
2017-01-23 16:12:56 +00:00
|
|
|
/* if we started with blank, fill some defaults then apply rules */
|
|
|
|
if (!(rule->options & KEEPENV))
|
|
|
|
fillenv(env, safeset);
|
2016-06-22 15:17:53 +00:00
|
|
|
if (rule->envlist)
|
2017-01-23 16:12:56 +00:00
|
|
|
fillenv(env, rule->envlist);
|
2016-06-22 15:17:53 +00:00
|
|
|
|
|
|
|
return flattenenv(env);
|
2016-06-22 15:21:34 +00:00
|
|
|
}
|