xref: /openbsd-src/usr.bin/doas/env.c (revision 97eb1931544ed8b83f33fa989fe040d1033c1303)
1*97eb1931Stedu /* $OpenBSD: env.c,v 1.10 2019/07/07 19:21:28 tedu Exp $ */
22da8cf98Stedu /*
32da8cf98Stedu  * Copyright (c) 2016 Ted Unangst <tedu@openbsd.org>
42da8cf98Stedu  *
52da8cf98Stedu  * Permission to use, copy, modify, and distribute this software for any
62da8cf98Stedu  * purpose with or without fee is hereby granted, provided that the above
72da8cf98Stedu  * copyright notice and this permission notice appear in all copies.
82da8cf98Stedu  *
92da8cf98Stedu  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
102da8cf98Stedu  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
112da8cf98Stedu  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
122da8cf98Stedu  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
132da8cf98Stedu  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
142da8cf98Stedu  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
152da8cf98Stedu  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
162da8cf98Stedu  */
172da8cf98Stedu 
182da8cf98Stedu #include <sys/types.h>
1987a59335Smartijn #include <sys/tree.h>
202da8cf98Stedu 
212da8cf98Stedu #include <string.h>
222da8cf98Stedu #include <stdio.h>
232da8cf98Stedu #include <stdlib.h>
242da8cf98Stedu #include <err.h>
252da8cf98Stedu #include <unistd.h>
262da8cf98Stedu #include <errno.h>
27e327fa4eStedu #include <pwd.h>
282da8cf98Stedu 
292da8cf98Stedu #include "doas.h"
302da8cf98Stedu 
31b09050b2Stedu const char *formerpath;
32b09050b2Stedu 
3387a59335Smartijn struct envnode {
3487a59335Smartijn 	RB_ENTRY(envnode) node;
3587a59335Smartijn 	const char *key;
3687a59335Smartijn 	const char *value;
3787a59335Smartijn };
3887a59335Smartijn 
3987a59335Smartijn struct env {
4087a59335Smartijn 	RB_HEAD(envtree, envnode) root;
4187a59335Smartijn 	u_int count;
4287a59335Smartijn };
4387a59335Smartijn 
44e327fa4eStedu static void fillenv(struct env *env, const char **envlist);
45e327fa4eStedu 
46ab7e7725Stedu static int
envcmp(struct envnode * a,struct envnode * b)472da8cf98Stedu envcmp(struct envnode *a, struct envnode *b)
482da8cf98Stedu {
492da8cf98Stedu 	return strcmp(a->key, b->key);
502da8cf98Stedu }
RB_GENERATE_STATIC(envtree,envnode,node,envcmp)5187a59335Smartijn RB_GENERATE_STATIC(envtree, envnode, node, envcmp)
5287a59335Smartijn 
53ab7e7725Stedu static struct envnode *
54ab7e7725Stedu createnode(const char *key, const char *value)
55ab7e7725Stedu {
56ab7e7725Stedu 	struct envnode *node;
572da8cf98Stedu 
58ab7e7725Stedu 	node = malloc(sizeof(*node));
59ab7e7725Stedu 	if (!node)
60ab7e7725Stedu 		err(1, NULL);
61ab7e7725Stedu 	node->key = strdup(key);
62ab7e7725Stedu 	node->value = strdup(value);
63ab7e7725Stedu 	if (!node->key || !node->value)
64ab7e7725Stedu 		err(1, NULL);
65ab7e7725Stedu 	return node;
66ab7e7725Stedu }
67ab7e7725Stedu 
68ab7e7725Stedu static void
freenode(struct envnode * node)69ab7e7725Stedu freenode(struct envnode *node)
70ab7e7725Stedu {
71ab7e7725Stedu 	free((char *)node->key);
72ab7e7725Stedu 	free((char *)node->value);
73ab7e7725Stedu 	free(node);
74ab7e7725Stedu }
75ab7e7725Stedu 
76e327fa4eStedu static void
addnode(struct env * env,const char * key,const char * value)77e327fa4eStedu addnode(struct env *env, const char *key, const char *value)
78e327fa4eStedu {
79e327fa4eStedu 	struct envnode *node;
80e327fa4eStedu 
81e327fa4eStedu 	node = createnode(key, value);
82e327fa4eStedu 	RB_INSERT(envtree, &env->root, node);
83e327fa4eStedu 	env->count++;
84e327fa4eStedu }
85e327fa4eStedu 
86ab7e7725Stedu static struct env *
createenv(const struct rule * rule,const struct passwd * mypw,const struct passwd * targpw)87e327fa4eStedu createenv(const struct rule *rule, const struct passwd *mypw,
88e327fa4eStedu     const struct passwd *targpw)
892da8cf98Stedu {
901aab56edStedu 	static const char *copyset[] = {
911aab56edStedu 		"DISPLAY", "TERM",
921aab56edStedu 		NULL
931aab56edStedu 	};
942da8cf98Stedu 	struct env *env;
952da8cf98Stedu 	u_int i;
962da8cf98Stedu 
972da8cf98Stedu 	env = malloc(sizeof(*env));
982da8cf98Stedu 	if (!env)
992da8cf98Stedu 		err(1, NULL);
1002da8cf98Stedu 	RB_INIT(&env->root);
1012da8cf98Stedu 	env->count = 0;
1022da8cf98Stedu 
103e327fa4eStedu 	addnode(env, "DOAS_USER", mypw->pw_name);
1041aab56edStedu 	addnode(env, "HOME", targpw->pw_dir);
1051aab56edStedu 	addnode(env, "LOGNAME", targpw->pw_name);
1061aab56edStedu 	addnode(env, "PATH", getenv("PATH"));
1071aab56edStedu 	addnode(env, "SHELL", targpw->pw_shell);
1081aab56edStedu 	addnode(env, "USER", targpw->pw_name);
1091aab56edStedu 
1101aab56edStedu 	fillenv(env, copyset);
111e327fa4eStedu 
112ab7e7725Stedu 	if (rule->options & KEEPENV) {
113ab7e7725Stedu 		extern const char **environ;
114ab7e7725Stedu 
115ab7e7725Stedu 		for (i = 0; environ[i] != NULL; i++) {
1162da8cf98Stedu 			struct envnode *node;
1172da8cf98Stedu 			const char *e, *eq;
118ab7e7725Stedu 			size_t len;
119f4da8009Stedu 			char name[1024];
1202da8cf98Stedu 
121ab7e7725Stedu 			e = environ[i];
1222da8cf98Stedu 
123ab7e7725Stedu 			/* ignore invalid or overlong names */
1242da8cf98Stedu 			if ((eq = strchr(e, '=')) == NULL || eq == e)
1252da8cf98Stedu 				continue;
126ab7e7725Stedu 			len = eq - e;
127f4da8009Stedu 			if (len > sizeof(name) - 1)
128ab7e7725Stedu 				continue;
129f4da8009Stedu 			memcpy(name, e, len);
130f4da8009Stedu 			name[len] = '\0';
131ab7e7725Stedu 
132f4da8009Stedu 			node = createnode(name, eq + 1);
133ab7e7725Stedu 			if (RB_INSERT(envtree, &env->root, node)) {
134ab7e7725Stedu 				/* ignore any later duplicates */
135ab7e7725Stedu 				freenode(node);
1362da8cf98Stedu 			} else {
1372da8cf98Stedu 				env->count++;
1382da8cf98Stedu 			}
1392da8cf98Stedu 		}
140ab7e7725Stedu 	}
141ab7e7725Stedu 
1422da8cf98Stedu 	return env;
1432da8cf98Stedu }
1442da8cf98Stedu 
145ab7e7725Stedu static char **
flattenenv(struct env * env)1462da8cf98Stedu flattenenv(struct env *env)
1472da8cf98Stedu {
1482da8cf98Stedu 	char **envp;
1492da8cf98Stedu 	struct envnode *node;
1502da8cf98Stedu 	u_int i;
1512da8cf98Stedu 
1522da8cf98Stedu 	envp = reallocarray(NULL, env->count + 1, sizeof(char *));
1532da8cf98Stedu 	if (!envp)
1542da8cf98Stedu 		err(1, NULL);
1552da8cf98Stedu 	i = 0;
1562da8cf98Stedu 	RB_FOREACH(node, envtree, &env->root) {
1572da8cf98Stedu 		if (asprintf(&envp[i], "%s=%s", node->key, node->value) == -1)
1582da8cf98Stedu 			err(1, NULL);
1592da8cf98Stedu 		i++;
1602da8cf98Stedu 	}
1612da8cf98Stedu 	envp[i] = NULL;
1622da8cf98Stedu 	return envp;
1632da8cf98Stedu }
1642da8cf98Stedu 
1652da8cf98Stedu static void
fillenv(struct env * env,const char ** envlist)166ab7e7725Stedu fillenv(struct env *env, const char **envlist)
1672da8cf98Stedu {
1682da8cf98Stedu 	struct envnode *node, key;
169ab7e7725Stedu 	const char *e, *eq;
170ab7e7725Stedu 	const char *val;
171ab7e7725Stedu 	char name[1024];
1722da8cf98Stedu 	u_int i;
173ab7e7725Stedu 	size_t len;
1742da8cf98Stedu 
1752da8cf98Stedu 	for (i = 0; envlist[i]; i++) {
176ab7e7725Stedu 		e = envlist[i];
1772da8cf98Stedu 
178ab7e7725Stedu 		/* parse out env name */
179ab7e7725Stedu 		if ((eq = strchr(e, '=')) == NULL)
180ab7e7725Stedu 			len = strlen(e);
181ab7e7725Stedu 		else
182ab7e7725Stedu 			len = eq - e;
183ab7e7725Stedu 		if (len > sizeof(name) - 1)
184ab7e7725Stedu 			continue;
185ab7e7725Stedu 		memcpy(name, e, len);
186ab7e7725Stedu 		name[len] = '\0';
1872da8cf98Stedu 
188ab7e7725Stedu 		/* delete previous copies */
189ab7e7725Stedu 		key.key = name;
190ab7e7725Stedu 		if (*name == '-')
191ab7e7725Stedu 			key.key = name + 1;
192ab7e7725Stedu 		if ((node = RB_FIND(envtree, &env->root, &key))) {
193ab7e7725Stedu 			RB_REMOVE(envtree, &env->root, node);
194ab7e7725Stedu 			freenode(node);
195ab7e7725Stedu 			env->count--;
196ab7e7725Stedu 		}
197ab7e7725Stedu 		if (*name == '-')
198ab7e7725Stedu 			continue;
199ab7e7725Stedu 
200ab7e7725Stedu 		/* assign value or inherit from environ */
201ab7e7725Stedu 		if (eq) {
202ab7e7725Stedu 			val = eq + 1;
203b09050b2Stedu 			if (*val == '$') {
204b09050b2Stedu 				if (strcmp(val + 1, "PATH") == 0)
205b09050b2Stedu 					val = formerpath;
206b09050b2Stedu 				else
207ab7e7725Stedu 					val = getenv(val + 1);
208b09050b2Stedu 			}
209ab7e7725Stedu 		} else {
210*97eb1931Stedu 			if (strcmp(name, "PATH") == 0)
211*97eb1931Stedu 				val = formerpath;
212*97eb1931Stedu 			else
213ab7e7725Stedu 				val = getenv(name);
214ab7e7725Stedu 		}
215ab7e7725Stedu 		/* at last, we have something to insert */
216ab7e7725Stedu 		if (val) {
217ab7e7725Stedu 			node = createnode(name, val);
218ab7e7725Stedu 			RB_INSERT(envtree, &env->root, node);
219ab7e7725Stedu 			env->count++;
2202da8cf98Stedu 		}
2212da8cf98Stedu 	}
2222da8cf98Stedu }
22387a59335Smartijn 
22487a59335Smartijn char **
prepenv(const struct rule * rule,const struct passwd * mypw,const struct passwd * targpw)225e327fa4eStedu prepenv(const struct rule *rule, const struct passwd *mypw,
226e327fa4eStedu     const struct passwd *targpw)
22787a59335Smartijn {
22887a59335Smartijn 	struct env *env;
22987a59335Smartijn 
230e327fa4eStedu 	env = createenv(rule, mypw, targpw);
231ab7e7725Stedu 	if (rule->envlist)
232ab7e7725Stedu 		fillenv(env, rule->envlist);
233ab7e7725Stedu 
23487a59335Smartijn 	return flattenenv(env);
23587a59335Smartijn }
236