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