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