Annotation of src/usr.bin/sudo/env.c, Revision 1.6
1.1 millert 1: /*
1.5 millert 2: * Copyright (c) 2000-2003 Todd C. Miller <Todd.Miller@courtesan.com>
1.1 millert 3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: *
9: * 1. Redistributions of source code must retain the above copyright
10: * notice, this list of conditions and the following disclaimer.
11: *
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
15: *
16: * 3. The name of the author may not be used to endorse or promote products
17: * derived from this software without specific prior written permission.
18: *
19: * 4. Products derived from this software may not be called "Sudo" nor
20: * may "Sudo" appear in their names without specific prior written
21: * permission from the author.
22: *
23: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
25: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
26: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33: */
34:
35: #include "config.h"
36:
37: #include <sys/types.h>
38: #include <sys/param.h>
39: #include <sys/stat.h>
40: #include <stdio.h>
41: #ifdef STDC_HEADERS
42: # include <stdlib.h>
43: # include <stddef.h>
44: #else
45: # ifdef HAVE_STDLIB_H
46: # include <stdlib.h>
47: # endif
48: #endif /* STDC_HEADERS */
49: #ifdef HAVE_STRING_H
50: # include <string.h>
51: #else
52: # ifdef HAVE_STRINGS_H
53: # include <strings.h>
54: # endif
55: #endif /* HAVE_STRING_H */
56: #ifdef HAVE_UNISTD_H
57: # include <unistd.h>
58: #endif /* HAVE_UNISTD_H */
1.6 ! millert 59: #ifdef HAVE_ERR_H
! 60: # include <err.h>
! 61: #else
! 62: # include "emul/err.h"
! 63: #endif /* HAVE_ERR_H */
1.1 millert 64: #include <pwd.h>
65:
66: #include "sudo.h"
67:
68: #ifndef lint
1.6 ! millert 69: static const char rcsid[] = "$Sudo: env.c,v 1.26 2003/04/02 18:25:19 millert Exp $";
1.1 millert 70: #endif /* lint */
71:
72: /*
1.5 millert 73: * Flags used in rebuild_env()
1.1 millert 74: */
75: #undef DID_TERM
76: #define DID_TERM 0x01
77: #undef DID_PATH
78: #define DID_PATH 0x02
79: #undef DID_HOME
80: #define DID_HOME 0x04
81: #undef DID_SHELL
82: #define DID_SHELL 0x08
83: #undef DID_LOGNAME
84: #define DID_LOGNAME 0x10
85: #undef DID_USER
86: #define DID_USER 0x12
87:
88: /*
89: * Prototypes
90: */
91: char **rebuild_env __P((int, char **));
92: char **zero_env __P((char **));
1.5 millert 93: static void insert_env __P((char *, int));
1.1 millert 94: static char *format_env __P((char *, char *));
95:
96: /*
97: * Default table of "bad" variables to remove from the environment.
98: * XXX - how to omit TERMCAP if it starts with '/'?
99: */
1.5 millert 100: static const char *initial_badenv_table[] = {
1.1 millert 101: "IFS",
102: "LOCALDOMAIN",
103: "RES_OPTIONS",
104: "HOSTALIASES",
105: "NLSPATH",
106: "PATH_LOCALE",
107: "LD_*",
108: "_RLD*",
109: #ifdef __hpux
110: "SHLIB_PATH",
111: #endif /* __hpux */
112: #ifdef _AIX
113: "LIBPATH",
114: #endif /* _AIX */
1.5 millert 115: #ifdef __APPLE__
116: "DYLD_*",
117: #endif
1.1 millert 118: #ifdef HAVE_KERB4
119: "KRB_CONF*",
1.6 ! millert 120: "KRBCONFDIR",
1.1 millert 121: "KRBTKFILE",
122: #endif /* HAVE_KERB4 */
123: #ifdef HAVE_KERB5
124: "KRB5_CONFIG*",
125: #endif /* HAVE_KERB5 */
126: #ifdef HAVE_SECURID
127: "VAR_ACE",
128: "USR_ACE",
129: "DLC_ACE",
130: #endif /* HAVE_SECURID */
131: "TERMINFO",
132: "TERMINFO_DIRS",
133: "TERMPATH",
134: "TERMCAP", /* XXX - only if it starts with '/' */
135: "ENV",
136: "BASH_ENV",
137: NULL
138: };
139:
140: /*
141: * Default table of variables to check for '%' and '/' characters.
142: */
1.5 millert 143: static const char *initial_checkenv_table[] = {
1.1 millert 144: "LC_*",
145: "LANG",
146: "LANGUAGE",
147: NULL
148: };
149:
1.5 millert 150: static char **new_environ; /* Modified copy of the environment */
151: static size_t env_size; /* size of new_environ in char **'s */
152: static size_t env_len; /* number of slots used, not counting NULL */
153:
1.1 millert 154: /*
155: * Zero out environment and replace with a minimal set of
156: * USER, LOGNAME, HOME, TZ, PATH (XXX - should just set path to default)
157: * May set user_path, user_shell, and/or user_prompt as side effects.
158: */
159: char **
160: zero_env(envp)
161: char **envp;
162: {
163: char **ep, **nep;
164: static char *newenv[7];
165:
166: for (ep = envp; *ep; ep++) {
167: switch (**ep) {
168: case 'H':
169: if (strncmp("HOME=", *ep, 5) == 0)
170: break;
1.2 millert 171: continue;
1.1 millert 172: case 'L':
173: if (strncmp("LOGNAME=", *ep, 8) == 0)
174: break;
1.2 millert 175: continue;
1.1 millert 176: case 'P':
177: if (strncmp("PATH=", *ep, 5) == 0) {
178: user_path = *ep + 5;
179: /* XXX - set to sane default instead of user's? */
180: break;
181: }
1.2 millert 182: continue;
1.1 millert 183: case 'S':
1.2 millert 184: if (strncmp("SHELL=", *ep, 6) == 0)
1.1 millert 185: user_shell = *ep + 6;
1.2 millert 186: else if (!user_prompt && !strncmp("SUDO_PROMPT=", *ep, 12))
1.1 millert 187: user_prompt = *ep + 12;
1.2 millert 188: continue;
1.1 millert 189: case 'T':
190: if (strncmp("TZ=", *ep, 3) == 0)
191: break;
1.2 millert 192: continue;
1.1 millert 193: case 'U':
194: if (strncmp("USER=", *ep, 5) == 0)
195: break;
1.2 millert 196: continue;
1.1 millert 197: default:
198: continue;
199: }
200:
201: /* Deal with multiply defined variables (take first instance) */
202: for (nep = newenv; *nep; nep++) {
203: if (**nep == **ep)
204: break;
205: }
206: if (*nep == NULL)
207: *nep++ = *ep;
208: }
209: return(&newenv[0]);
210: }
211:
212: /*
213: * Given a variable and value, allocate and format an environment string.
214: */
215: static char *
216: format_env(var, val)
217: char *var;
218: char *val;
219: {
1.5 millert 220: char *estring;
221: size_t esize;
1.1 millert 222:
1.5 millert 223: esize = strlen(var) + 1 + strlen(val) + 1;
224: estring = (char *) emalloc(esize);
225:
226: /* We pre-allocate enough space, so this should never overflow. */
227: if (strlcpy(estring, var, esize) >= esize ||
228: strlcat(estring, "=", esize) >= esize ||
229: strlcat(estring, val, esize) >= esize) {
1.6 ! millert 230:
! 231: errx(1, "internal error, format_env() overflow");
1.5 millert 232: }
1.1 millert 233:
234: return(estring);
235: }
236:
237: /*
1.5 millert 238: * Insert str into new_environ, assumes str has an '=' in it.
239: * NOTE: no other routines may modify new_environ, env_size, or env_len.
1.1 millert 240: */
241: static void
1.5 millert 242: insert_env(str, dupcheck)
1.1 millert 243: char *str;
1.5 millert 244: int dupcheck;
1.1 millert 245: {
1.5 millert 246: char **nep;
1.1 millert 247: size_t varlen;
248:
1.5 millert 249: /* Make sure there is room for the new entry. */
250: if (env_len + 1 > env_size) {
251: env_size += 128;
252: new_environ = erealloc3(new_environ, env_size, sizeof(char *));
253: }
254:
255: if (dupcheck) {
256: varlen = (strchr(str, '=') - str) + 1;
1.1 millert 257:
1.5 millert 258: for (nep = new_environ; *nep; nep++) {
259: if (strncmp(str, *nep, varlen) == 0) {
260: *nep = str;
261: return;
262: }
263: }
264: } else
265: nep = &new_environ[env_len];
266:
267: env_len++;
268: *nep++ = str;
269: *nep = NULL;
1.1 millert 270: }
271:
272: /*
273: * Build a new environment and ether clear potentially dangerous
274: * variables from the old one or start with a clean slate.
275: * Also adds sudo-specific variables (SUDO_*).
276: */
277: char **
278: rebuild_env(sudo_mode, envp)
279: int sudo_mode;
280: char **envp;
281: {
1.5 millert 282: char **ep, *cp, *ps1;
1.1 millert 283: int okvar, iswild, didvar;
1.5 millert 284: size_t len;
1.1 millert 285: struct list_member *cur;
286:
287: /*
288: * Either clean out the environment or reset to a safe default.
289: */
290: ps1 = NULL;
291: didvar = 0;
292: if (def_flag(I_ENV_RESET)) {
293: int keepit;
294:
295: /* Pull in vars we want to keep from the old environment. */
296: for (ep = envp; *ep; ep++) {
297: keepit = 0;
298: for (cur = def_list(I_ENV_KEEP); cur; cur = cur->next) {
299: len = strlen(cur->value);
300: /* Deal with '*' wildcard */
301: if (cur->value[len - 1] == '*') {
302: len--;
303: iswild = 1;
304: } else
305: iswild = 0;
306: if (strncmp(cur->value, *ep, len) == 0 &&
307: (iswild || (*ep)[len] == '=')) {
308: /* We always preserve TERM, no special treatment needed. */
309: if (strncmp(*ep, "TERM=", 5) != 0)
310: keepit = 1;
311: break;
312: }
313: }
314:
315: /* For SUDO_PS1 -> PS1 conversion. */
316: if (strncmp(*ep, "SUDO_PS1=", 8) == 0)
317: ps1 = *ep + 5;
318:
319: if (keepit) {
320: /* Preserve variable. */
321: switch (**ep) {
322: case 'H':
323: if (strncmp(*ep, "HOME=", 5) == 0)
324: didvar |= DID_HOME;
1.5 millert 325: break;
1.1 millert 326: case 'S':
327: if (strncmp(*ep, "SHELL=", 6) == 0)
328: didvar |= DID_SHELL;
1.5 millert 329: break;
1.1 millert 330: case 'L':
331: if (strncmp(*ep, "LOGNAME=", 8) == 0)
332: didvar |= DID_LOGNAME;
1.5 millert 333: break;
1.1 millert 334: case 'U':
335: if (strncmp(*ep, "USER=", 5) == 0)
336: didvar |= DID_USER;
1.5 millert 337: break;
1.1 millert 338: }
1.5 millert 339: insert_env(*ep, 0);
1.1 millert 340: } else {
341: /* Preserve TERM and PATH, ignore anything else. */
342: if (!(didvar & DID_TERM) && !strncmp(*ep, "TERM=", 5)) {
1.5 millert 343: insert_env(*ep, 0);
1.1 millert 344: didvar |= DID_TERM;
345: } else if (!(didvar & DID_PATH) && !strncmp(*ep, "PATH=", 5)) {
1.5 millert 346: insert_env(*ep, 0);
1.1 millert 347: didvar |= DID_PATH;
348: }
349: }
350: }
351:
352: /*
353: * Add in defaults unless they were preserved from the
354: * user's environment.
355: */
356: if (!(didvar & DID_HOME))
1.5 millert 357: insert_env(format_env("HOME", user_dir), 0);
1.1 millert 358: if (!(didvar & DID_SHELL))
1.5 millert 359: insert_env(format_env("SHELL", sudo_user.pw->pw_shell), 0);
1.1 millert 360: if (!(didvar & DID_LOGNAME))
1.5 millert 361: insert_env(format_env("LOGNAME", user_name), 0);
1.1 millert 362: if (!(didvar & DID_USER))
1.5 millert 363: insert_env(format_env("USER", user_name), 0);
1.1 millert 364: } else {
365: /*
366: * Copy envp entries as long as they don't match env_delete or
367: * env_check.
368: */
369: for (ep = envp; *ep; ep++) {
370: okvar = 1;
371:
372: /* Skip anything listed in env_delete. */
373: for (cur = def_list(I_ENV_DELETE); cur && okvar; cur = cur->next) {
374: len = strlen(cur->value);
375: /* Deal with '*' wildcard */
376: if (cur->value[len - 1] == '*') {
377: len--;
378: iswild = 1;
379: } else
380: iswild = 0;
381: if (strncmp(cur->value, *ep, len) == 0 &&
382: (iswild || (*ep)[len] == '=')) {
383: okvar = 0;
384: }
385: }
386:
387: /* Check certain variables for '%' and '/' characters. */
388: for (cur = def_list(I_ENV_CHECK); cur && okvar; cur = cur->next) {
389: len = strlen(cur->value);
390: /* Deal with '*' wildcard */
391: if (cur->value[len - 1] == '*') {
392: len--;
393: iswild = 1;
394: } else
395: iswild = 0;
396: if (strncmp(cur->value, *ep, len) == 0 &&
397: (iswild || (*ep)[len] == '=') &&
398: strpbrk(*ep, "/%")) {
399: okvar = 0;
400: }
401: }
402:
403: if (okvar) {
404: if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
405: ps1 = *ep + 5;
406: else if (strncmp(*ep, "PATH=", 5) == 0)
407: didvar |= DID_PATH;
408: else if (strncmp(*ep, "TERM=", 5) == 0)
409: didvar |= DID_TERM;
1.5 millert 410: insert_env(*ep, 0);
1.1 millert 411: }
412: }
413: }
414: /* Provide default values for $TERM and $PATH if they are not set. */
415: if (!(didvar & DID_TERM))
1.5 millert 416: insert_env("TERM=unknown", 0);
1.1 millert 417: if (!(didvar & DID_PATH))
1.5 millert 418: insert_env(format_env("PATH", _PATH_DEFPATH), 0);
1.1 millert 419:
420: #ifdef SECURE_PATH
421: /* Replace the PATH envariable with a secure one. */
1.5 millert 422: insert_env(format_env("PATH", SECURE_PATH), 1);
1.1 millert 423: #endif
424:
425: /* Set $USER and $LOGNAME to target if "set_logname" is true. */
426: if (def_flag(I_SET_LOGNAME) && runas_pw->pw_name) {
1.5 millert 427: insert_env(format_env("LOGNAME", runas_pw->pw_name), 1);
428: insert_env(format_env("USER", runas_pw->pw_name), 1);
1.1 millert 429: }
430:
431: /* Set $HOME for `sudo -H'. Only valid at PERM_RUNAS. */
432: if ((sudo_mode & MODE_RESET_HOME) && runas_pw->pw_dir)
1.5 millert 433: insert_env(format_env("HOME", runas_pw->pw_dir), 1);
1.1 millert 434:
435: /* Set PS1 if SUDO_PS1 is set. */
436: if (ps1)
1.5 millert 437: insert_env(ps1, 1);
1.1 millert 438:
439: /* Add the SUDO_COMMAND envariable (cmnd + args). */
440: if (user_args) {
1.4 millert 441: easprintf(&cp, "SUDO_COMMAND=%s %s", user_cmnd, user_args);
1.5 millert 442: insert_env(cp, 1);
1.1 millert 443: } else
1.5 millert 444: insert_env(format_env("SUDO_COMMAND", user_cmnd), 1);
1.1 millert 445:
446: /* Add the SUDO_USER, SUDO_UID, SUDO_GID environment variables. */
1.5 millert 447: insert_env(format_env("SUDO_USER", user_name), 1);
448: easprintf(&cp, "SUDO_UID=%lu", (unsigned long) user_uid);
449: insert_env(cp, 1);
450: easprintf(&cp, "SUDO_GID=%lu", (unsigned long) user_gid);
451: insert_env(cp, 1);
1.1 millert 452:
1.5 millert 453: return(new_environ);
1.1 millert 454: }
455:
456: void
457: init_envtables()
458: {
459: struct list_member *cur;
1.5 millert 460: const char **p;
1.1 millert 461:
462: /* Fill in "env_delete" variable. */
463: for (p = initial_badenv_table; *p; p++) {
464: cur = emalloc(sizeof(struct list_member));
465: cur->value = estrdup(*p);
466: cur->next = def_list(I_ENV_DELETE);
467: def_list(I_ENV_DELETE) = cur;
468: }
469:
470: /* Fill in "env_check" variable. */
471: for (p = initial_checkenv_table; *p; p++) {
472: cur = emalloc(sizeof(struct list_member));
473: cur->value = estrdup(*p);
474: cur->next = def_list(I_ENV_CHECK);
475: def_list(I_ENV_CHECK) = cur;
476: }
477: }