xref: /openbsd/usr.bin/doas/env.c (revision 97eb1931)
1 /* $OpenBSD: env.c,v 1.10 2019/07/07 19:21:28 tedu Exp $ */
2 /*
3  * Copyright (c) 2016 Ted Unangst <tedu@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 #include <sys/tree.h>
20 
21 #include <string.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <err.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <pwd.h>
28 
29 #include "doas.h"
30 
31 const char *formerpath;
32 
33 struct envnode {
34 	RB_ENTRY(envnode) node;
35 	const char *key;
36 	const char *value;
37 };
38 
39 struct env {
40 	RB_HEAD(envtree, envnode) root;
41 	u_int count;
42 };
43 
44 static void fillenv(struct env *env, const char **envlist);
45 
46 static int
envcmp(struct envnode * a,struct envnode * b)47 envcmp(struct envnode *a, struct envnode *b)
48 {
49 	return strcmp(a->key, b->key);
50 }
RB_GENERATE_STATIC(envtree,envnode,node,envcmp)51 RB_GENERATE_STATIC(envtree, envnode, node, envcmp)
52 
53 static struct envnode *
54 createnode(const char *key, const char *value)
55 {
56 	struct envnode *node;
57 
58 	node = malloc(sizeof(*node));
59 	if (!node)
60 		err(1, NULL);
61 	node->key = strdup(key);
62 	node->value = strdup(value);
63 	if (!node->key || !node->value)
64 		err(1, NULL);
65 	return node;
66 }
67 
68 static void
freenode(struct envnode * node)69 freenode(struct envnode *node)
70 {
71 	free((char *)node->key);
72 	free((char *)node->value);
73 	free(node);
74 }
75 
76 static void
addnode(struct env * env,const char * key,const char * value)77 addnode(struct env *env, const char *key, const char *value)
78 {
79 	struct envnode *node;
80 
81 	node = createnode(key, value);
82 	RB_INSERT(envtree, &env->root, node);
83 	env->count++;
84 }
85 
86 static struct env *
createenv(const struct rule * rule,const struct passwd * mypw,const struct passwd * targpw)87 createenv(const struct rule *rule, const struct passwd *mypw,
88     const struct passwd *targpw)
89 {
90 	static const char *copyset[] = {
91 		"DISPLAY", "TERM",
92 		NULL
93 	};
94 	struct env *env;
95 	u_int i;
96 
97 	env = malloc(sizeof(*env));
98 	if (!env)
99 		err(1, NULL);
100 	RB_INIT(&env->root);
101 	env->count = 0;
102 
103 	addnode(env, "DOAS_USER", mypw->pw_name);
104 	addnode(env, "HOME", targpw->pw_dir);
105 	addnode(env, "LOGNAME", targpw->pw_name);
106 	addnode(env, "PATH", getenv("PATH"));
107 	addnode(env, "SHELL", targpw->pw_shell);
108 	addnode(env, "USER", targpw->pw_name);
109 
110 	fillenv(env, copyset);
111 
112 	if (rule->options & KEEPENV) {
113 		extern const char **environ;
114 
115 		for (i = 0; environ[i] != NULL; i++) {
116 			struct envnode *node;
117 			const char *e, *eq;
118 			size_t len;
119 			char name[1024];
120 
121 			e = environ[i];
122 
123 			/* ignore invalid or overlong names */
124 			if ((eq = strchr(e, '=')) == NULL || eq == e)
125 				continue;
126 			len = eq - e;
127 			if (len > sizeof(name) - 1)
128 				continue;
129 			memcpy(name, e, len);
130 			name[len] = '\0';
131 
132 			node = createnode(name, eq + 1);
133 			if (RB_INSERT(envtree, &env->root, node)) {
134 				/* ignore any later duplicates */
135 				freenode(node);
136 			} else {
137 				env->count++;
138 			}
139 		}
140 	}
141 
142 	return env;
143 }
144 
145 static char **
flattenenv(struct env * env)146 flattenenv(struct env *env)
147 {
148 	char **envp;
149 	struct envnode *node;
150 	u_int i;
151 
152 	envp = reallocarray(NULL, env->count + 1, sizeof(char *));
153 	if (!envp)
154 		err(1, NULL);
155 	i = 0;
156 	RB_FOREACH(node, envtree, &env->root) {
157 		if (asprintf(&envp[i], "%s=%s", node->key, node->value) == -1)
158 			err(1, NULL);
159 		i++;
160 	}
161 	envp[i] = NULL;
162 	return envp;
163 }
164 
165 static void
fillenv(struct env * env,const char ** envlist)166 fillenv(struct env *env, const char **envlist)
167 {
168 	struct envnode *node, key;
169 	const char *e, *eq;
170 	const char *val;
171 	char name[1024];
172 	u_int i;
173 	size_t len;
174 
175 	for (i = 0; envlist[i]; i++) {
176 		e = envlist[i];
177 
178 		/* parse out env name */
179 		if ((eq = strchr(e, '=')) == NULL)
180 			len = strlen(e);
181 		else
182 			len = eq - e;
183 		if (len > sizeof(name) - 1)
184 			continue;
185 		memcpy(name, e, len);
186 		name[len] = '\0';
187 
188 		/* delete previous copies */
189 		key.key = name;
190 		if (*name == '-')
191 			key.key = name + 1;
192 		if ((node = RB_FIND(envtree, &env->root, &key))) {
193 			RB_REMOVE(envtree, &env->root, node);
194 			freenode(node);
195 			env->count--;
196 		}
197 		if (*name == '-')
198 			continue;
199 
200 		/* assign value or inherit from environ */
201 		if (eq) {
202 			val = eq + 1;
203 			if (*val == '$') {
204 				if (strcmp(val + 1, "PATH") == 0)
205 					val = formerpath;
206 				else
207 					val = getenv(val + 1);
208 			}
209 		} else {
210 			if (strcmp(name, "PATH") == 0)
211 				val = formerpath;
212 			else
213 				val = getenv(name);
214 		}
215 		/* at last, we have something to insert */
216 		if (val) {
217 			node = createnode(name, val);
218 			RB_INSERT(envtree, &env->root, node);
219 			env->count++;
220 		}
221 	}
222 }
223 
224 char **
prepenv(const struct rule * rule,const struct passwd * mypw,const struct passwd * targpw)225 prepenv(const struct rule *rule, const struct passwd *mypw,
226     const struct passwd *targpw)
227 {
228 	struct env *env;
229 
230 	env = createenv(rule, mypw, targpw);
231 	if (rule->envlist)
232 		fillenv(env, rule->envlist);
233 
234 	return flattenenv(env);
235 }
236