]> git.armaanb.net Git - opendoas.git/blob - env.c
more precisely describe what happens to the environment without keepenv; OK tedu@
[opendoas.git] / env.c
1 /* $OpenBSD$ */
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 #include "includes.h"
31
32 const char *formerpath;
33
34 struct envnode {
35         RB_ENTRY(envnode) node;
36         const char *key;
37         const char *value;
38 };
39
40 struct env {
41         RB_HEAD(envtree, envnode) root;
42         u_int count;
43 };
44
45 static void fillenv(struct env *env, const char **envlist);
46
47 static int
48 envcmp(struct envnode *a, struct envnode *b)
49 {
50         return strcmp(a->key, b->key);
51 }
52 RB_GENERATE_STATIC(envtree, envnode, node, envcmp)
53
54 static struct envnode *
55 createnode(const char *key, const char *value)
56 {
57         struct envnode *node;
58
59         node = malloc(sizeof(*node));
60         if (!node)
61                 err(1, NULL);
62         node->key = strdup(key);
63         node->value = strdup(value);
64         if (!node->key || !node->value)
65                 err(1, NULL);
66         return node;
67 }
68
69 static void
70 freenode(struct envnode *node)
71 {
72         free((char *)node->key);
73         free((char *)node->value);
74         free(node);
75 }
76
77 static void
78 addnode(struct env *env, const char *key, const char *value)
79 {
80         struct envnode *node;
81
82         node = createnode(key, value);
83         RB_INSERT(envtree, &env->root, node);
84         env->count++;
85 }
86
87 static struct env *
88 createenv(const struct rule *rule, const struct passwd *mypw,
89     const struct passwd *targpw)
90 {
91         static const char *copyset[] = {
92                 "DISPLAY", "TERM",
93                 NULL
94         };
95         struct env *env;
96         u_int i;
97
98         env = malloc(sizeof(*env));
99         if (!env)
100                 err(1, NULL);
101         RB_INIT(&env->root);
102         env->count = 0;
103
104         addnode(env, "DOAS_USER", mypw->pw_name);
105         addnode(env, "HOME", targpw->pw_dir);
106         addnode(env, "LOGNAME", targpw->pw_name);
107         addnode(env, "PATH", getenv("PATH"));
108         addnode(env, "SHELL", targpw->pw_shell);
109         addnode(env, "USER", targpw->pw_name);
110
111         fillenv(env, copyset);
112
113         if (rule->options & KEEPENV) {
114                 extern char **environ;
115
116                 for (i = 0; environ[i] != NULL; i++) {
117                         struct envnode *node;
118                         const char *e, *eq;
119                         size_t len;
120                         char keybuf[1024];
121
122                         e = environ[i];
123
124                         /* ignore invalid or overlong names */
125                         if ((eq = strchr(e, '=')) == NULL || eq == e)
126                                 continue;
127                         len = eq - e;
128                         if (len > sizeof(keybuf) - 1)
129                                 continue;
130                         memcpy(keybuf, e, len);
131                         keybuf[len] = '\0';
132
133                         node = createnode(keybuf, eq + 1);
134                         if (RB_INSERT(envtree, &env->root, node)) {
135                                 /* ignore any later duplicates */
136                                 freenode(node);
137                         } else {
138                                 env->count++;
139                         }
140                 }
141         }
142
143         return env;
144 }
145
146 static char **
147 flattenenv(struct env *env)
148 {
149         char **envp;
150         struct envnode *node;
151         u_int i;
152
153         envp = reallocarray(NULL, env->count + 1, sizeof(char *));
154         if (!envp)
155                 err(1, NULL);
156         i = 0;
157         RB_FOREACH(node, envtree, &env->root) {
158                 if (asprintf(&envp[i], "%s=%s", node->key, node->value) == -1)
159                         err(1, NULL);
160                 i++;
161         }
162         envp[i] = NULL;
163         return envp;
164 }
165
166 static void
167 fillenv(struct env *env, const char **envlist)
168 {
169         struct envnode *node, key;
170         const char *e, *eq;
171         const char *val;
172         char name[1024];
173         u_int i;
174         size_t len;
175
176         for (i = 0; envlist[i]; i++) {
177                 e = envlist[i];
178
179                 /* parse out env name */
180                 if ((eq = strchr(e, '=')) == NULL)
181                         len = strlen(e);
182                 else
183                         len = eq - e;
184                 if (len > sizeof(name) - 1)
185                         continue;
186                 memcpy(name, e, len);
187                 name[len] = '\0';
188
189                 /* delete previous copies */
190                 key.key = name;
191                 if (*name == '-')
192                         key.key = name + 1;
193                 if ((node = RB_FIND(envtree, &env->root, &key))) {
194                         RB_REMOVE(envtree, &env->root, node);
195                         freenode(node);
196                         env->count--;
197                 }
198                 if (*name == '-')
199                         continue;
200
201                 /* assign value or inherit from environ */
202                 if (eq) {
203                         val = eq + 1;
204                         if (*val == '$') {
205                                 if (strcmp(val + 1, "PATH") == 0)
206                                         val = formerpath;
207                                 else
208                                         val = getenv(val + 1);
209                         }
210                 } else {
211                         val = getenv(name);
212                 }
213                 /* at last, we have something to insert */
214                 if (val) {
215                         node = createnode(name, val);
216                         RB_INSERT(envtree, &env->root, node);
217                         env->count++;
218                 }
219         }
220 }
221
222 char **
223 prepenv(const struct rule *rule, const struct passwd *mypw,
224     const struct passwd *targpw)
225 {
226         struct env *env;
227
228         env = createenv(rule, mypw, targpw);
229         if (rule->envlist)
230                 fillenv(env, rule->envlist);
231
232         return flattenenv(env);
233 }