xref: /openbsd-src/usr.bin/doas/env.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /* $OpenBSD: env.c,v 1.4 2016/07/10 03:24:31 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 
28 #include "doas.h"
29 
30 struct envnode {
31 	RB_ENTRY(envnode) node;
32 	const char *key;
33 	const char *value;
34 };
35 
36 struct env {
37 	RB_HEAD(envtree, envnode) root;
38 	u_int count;
39 };
40 
41 static int
42 envcmp(struct envnode *a, struct envnode *b)
43 {
44 	return strcmp(a->key, b->key);
45 }
46 RB_GENERATE_STATIC(envtree, envnode, node, envcmp)
47 
48 static struct envnode *
49 createnode(const char *key, const char *value)
50 {
51 	struct envnode *node;
52 
53 	node = malloc(sizeof(*node));
54 	if (!node)
55 		err(1, NULL);
56 	node->key = strdup(key);
57 	node->value = strdup(value);
58 	if (!node->key || !node->value)
59 		err(1, NULL);
60 	return node;
61 }
62 
63 static void
64 freenode(struct envnode *node)
65 {
66 	free((char *)node->key);
67 	free((char *)node->value);
68 	free(node);
69 }
70 
71 static struct env *
72 createenv(struct rule *rule)
73 {
74 	struct env *env;
75 	u_int i;
76 
77 	env = malloc(sizeof(*env));
78 	if (!env)
79 		err(1, NULL);
80 	RB_INIT(&env->root);
81 	env->count = 0;
82 
83 	if (rule->options & KEEPENV) {
84 		extern const char **environ;
85 
86 		for (i = 0; environ[i] != NULL; i++) {
87 			struct envnode *node;
88 			const char *e, *eq;
89 			size_t len;
90 			char name[1024];
91 
92 			e = environ[i];
93 
94 			/* ignore invalid or overlong names */
95 			if ((eq = strchr(e, '=')) == NULL || eq == e)
96 				continue;
97 			len = eq - e;
98 			if (len > sizeof(name) - 1)
99 				continue;
100 			memcpy(name, e, len);
101 			name[len] = '\0';
102 
103 			node = createnode(name, eq + 1);
104 			if (RB_INSERT(envtree, &env->root, node)) {
105 				/* ignore any later duplicates */
106 				freenode(node);
107 			} else {
108 				env->count++;
109 			}
110 		}
111 	}
112 
113 	return env;
114 }
115 
116 static char **
117 flattenenv(struct env *env)
118 {
119 	char **envp;
120 	struct envnode *node;
121 	u_int i;
122 
123 	envp = reallocarray(NULL, env->count + 1, sizeof(char *));
124 	if (!envp)
125 		err(1, NULL);
126 	i = 0;
127 	RB_FOREACH(node, envtree, &env->root) {
128 		if (asprintf(&envp[i], "%s=%s", node->key, node->value) == -1)
129 			err(1, NULL);
130 		i++;
131 	}
132 	envp[i] = NULL;
133 	return envp;
134 }
135 
136 static void
137 fillenv(struct env *env, const char **envlist)
138 {
139 	struct envnode *node, key;
140 	const char *e, *eq;
141 	const char *val;
142 	char name[1024];
143 	u_int i;
144 	size_t len;
145 
146 	for (i = 0; envlist[i]; i++) {
147 		e = envlist[i];
148 
149 		/* parse out env name */
150 		if ((eq = strchr(e, '=')) == NULL)
151 			len = strlen(e);
152 		else
153 			len = eq - e;
154 		if (len > sizeof(name) - 1)
155 			continue;
156 		memcpy(name, e, len);
157 		name[len] = '\0';
158 
159 		/* delete previous copies */
160 		key.key = name;
161 		if (*name == '-')
162 			key.key = name + 1;
163 		if ((node = RB_FIND(envtree, &env->root, &key))) {
164 			RB_REMOVE(envtree, &env->root, node);
165 			freenode(node);
166 			env->count--;
167 		}
168 		if (*name == '-')
169 			continue;
170 
171 		/* assign value or inherit from environ */
172 		if (eq) {
173 			val = eq + 1;
174 			if (*val == '$')
175 				val = getenv(val + 1);
176 		} else {
177 			val = getenv(name);
178 		}
179 		/* at last, we have something to insert */
180 		if (val) {
181 			node = createnode(name, val);
182 			RB_INSERT(envtree, &env->root, node);
183 			env->count++;
184 		}
185 	}
186 }
187 
188 char **
189 prepenv(struct rule *rule)
190 {
191 	static const char *safeset[] = {
192 		"DISPLAY", "HOME", "LOGNAME", "MAIL",
193 		"PATH", "TERM", "USER", "USERNAME",
194 		NULL
195 	};
196 	struct env *env;
197 
198 	env = createenv(rule);
199 
200 	/* if we started with blank, fill some defaults then apply rules */
201 	if (!(rule->options & KEEPENV))
202 		fillenv(env, safeset);
203 	if (rule->envlist)
204 		fillenv(env, rule->envlist);
205 
206 	return flattenenv(env);
207 }
208