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