Annotation of src/usr.bin/sudo/env.c, Revision 1.13
1.1 millert 1: /*
1.9 millert 2: * Copyright (c) 2000-2004 Todd C. Miller <Todd.Miller@courtesan.com>
1.1 millert 3: *
1.9 millert 4: * Permission to use, copy, modify, and distribute this software for any
5: * purpose with or without fee is hereby granted, provided that the above
6: * copyright notice and this permission notice appear in all copies.
1.1 millert 7: *
1.9 millert 8: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.7 millert 15: *
16: * Sponsored in part by the Defense Advanced Research Projects
17: * Agency (DARPA) and Air Force Research Laboratory, Air Force
18: * Materiel Command, USAF, under agreement number F39502-99-1-0512.
1.1 millert 19: */
20:
21: #include "config.h"
22:
23: #include <sys/types.h>
24: #include <sys/param.h>
25: #include <sys/stat.h>
26: #include <stdio.h>
27: #ifdef STDC_HEADERS
28: # include <stdlib.h>
29: # include <stddef.h>
30: #else
31: # ifdef HAVE_STDLIB_H
32: # include <stdlib.h>
33: # endif
34: #endif /* STDC_HEADERS */
35: #ifdef HAVE_STRING_H
36: # include <string.h>
37: #else
38: # ifdef HAVE_STRINGS_H
39: # include <strings.h>
40: # endif
41: #endif /* HAVE_STRING_H */
42: #ifdef HAVE_UNISTD_H
43: # include <unistd.h>
44: #endif /* HAVE_UNISTD_H */
1.6 millert 45: #ifdef HAVE_ERR_H
46: # include <err.h>
47: #else
48: # include "emul/err.h"
49: #endif /* HAVE_ERR_H */
1.1 millert 50: #include <pwd.h>
51:
52: #include "sudo.h"
53:
54: #ifndef lint
1.9 millert 55: static const char rcsid[] = "$Sudo: env.c,v 1.42 2004/09/08 15:57:49 millert Exp $";
1.1 millert 56: #endif /* lint */
57:
58: /*
1.5 millert 59: * Flags used in rebuild_env()
1.1 millert 60: */
61: #undef DID_TERM
62: #define DID_TERM 0x01
63: #undef DID_PATH
64: #define DID_PATH 0x02
65: #undef DID_HOME
66: #define DID_HOME 0x04
67: #undef DID_SHELL
68: #define DID_SHELL 0x08
69: #undef DID_LOGNAME
70: #define DID_LOGNAME 0x10
1.9 millert 71: #undef DID_USER
1.13 ! millert 72: #define DID_USER 0x20
1.1 millert 73:
1.9 millert 74: #undef VNULL
75: #define VNULL (VOID *)NULL
76:
1.1 millert 77: /*
78: * Prototypes
79: */
1.9 millert 80: char **rebuild_env __P((char **, int, int));
1.1 millert 81: char **zero_env __P((char **));
1.5 millert 82: static void insert_env __P((char *, int));
1.9 millert 83: static char *format_env __P((char *, ...));
1.1 millert 84:
85: /*
86: * Default table of "bad" variables to remove from the environment.
87: * XXX - how to omit TERMCAP if it starts with '/'?
88: */
1.5 millert 89: static const char *initial_badenv_table[] = {
1.1 millert 90: "IFS",
1.11 millert 91: "CDPATH",
1.1 millert 92: "LOCALDOMAIN",
93: "RES_OPTIONS",
94: "HOSTALIASES",
95: "NLSPATH",
96: "PATH_LOCALE",
97: "LD_*",
98: "_RLD*",
99: #ifdef __hpux
100: "SHLIB_PATH",
101: #endif /* __hpux */
102: #ifdef _AIX
103: "LIBPATH",
104: #endif /* _AIX */
1.5 millert 105: #ifdef __APPLE__
106: "DYLD_*",
107: #endif
1.1 millert 108: #ifdef HAVE_KERB4
109: "KRB_CONF*",
1.6 millert 110: "KRBCONFDIR",
1.1 millert 111: "KRBTKFILE",
112: #endif /* HAVE_KERB4 */
113: #ifdef HAVE_KERB5
114: "KRB5_CONFIG*",
115: #endif /* HAVE_KERB5 */
116: #ifdef HAVE_SECURID
117: "VAR_ACE",
118: "USR_ACE",
119: "DLC_ACE",
120: #endif /* HAVE_SECURID */
121: "TERMINFO",
122: "TERMINFO_DIRS",
123: "TERMPATH",
124: "TERMCAP", /* XXX - only if it starts with '/' */
125: "ENV",
126: "BASH_ENV",
127: NULL
128: };
129:
130: /*
131: * Default table of variables to check for '%' and '/' characters.
132: */
1.5 millert 133: static const char *initial_checkenv_table[] = {
1.1 millert 134: "LC_*",
135: "LANG",
136: "LANGUAGE",
137: NULL
138: };
139:
1.5 millert 140: static char **new_environ; /* Modified copy of the environment */
141: static size_t env_size; /* size of new_environ in char **'s */
142: static size_t env_len; /* number of slots used, not counting NULL */
143:
1.1 millert 144: /*
1.11 millert 145: * Zero out environment and replace with a minimal set of KRB5CCNAME
1.1 millert 146: * USER, LOGNAME, HOME, TZ, PATH (XXX - should just set path to default)
147: * May set user_path, user_shell, and/or user_prompt as side effects.
148: */
149: char **
150: zero_env(envp)
151: char **envp;
152: {
1.11 millert 153: static char *newenv[9];
1.9 millert 154: char **ep, **nep = newenv;
1.11 millert 155: char **ne_last = &newenv[(sizeof(newenv) / sizeof(newenv[0])) - 1];
1.9 millert 156: extern char *prev_user;
1.1 millert 157:
158: for (ep = envp; *ep; ep++) {
159: switch (**ep) {
160: case 'H':
161: if (strncmp("HOME=", *ep, 5) == 0)
162: break;
1.2 millert 163: continue;
1.11 millert 164: case 'K':
165: if (strncmp("KRB5CCNAME=", *ep, 11) == 0)
166: break;
167: continue;
1.1 millert 168: case 'L':
169: if (strncmp("LOGNAME=", *ep, 8) == 0)
170: break;
1.2 millert 171: continue;
1.1 millert 172: case 'P':
173: if (strncmp("PATH=", *ep, 5) == 0) {
174: user_path = *ep + 5;
175: /* XXX - set to sane default instead of user's? */
176: break;
177: }
1.2 millert 178: continue;
1.1 millert 179: case 'S':
1.2 millert 180: if (strncmp("SHELL=", *ep, 6) == 0)
1.1 millert 181: user_shell = *ep + 6;
1.9 millert 182: else if (!user_prompt && strncmp("SUDO_PROMPT=", *ep, 12) == 0)
1.1 millert 183: user_prompt = *ep + 12;
1.9 millert 184: else if (strncmp("SUDO_USER=", *ep, 10) == 0)
185: prev_user = *ep + 10;
1.2 millert 186: continue;
1.1 millert 187: case 'T':
188: if (strncmp("TZ=", *ep, 3) == 0)
189: break;
1.2 millert 190: continue;
1.1 millert 191: case 'U':
192: if (strncmp("USER=", *ep, 5) == 0)
193: break;
1.2 millert 194: continue;
1.1 millert 195: default:
196: continue;
197: }
198:
199: /* Deal with multiply defined variables (take first instance) */
200: for (nep = newenv; *nep; nep++) {
201: if (**nep == **ep)
202: break;
203: }
1.11 millert 204: if (*nep == NULL) {
205: if (nep < ne_last)
206: *nep++ = *ep;
207: else
208: errx(1, "internal error, attempt to write outside newenv");
209: }
1.1 millert 210: }
1.9 millert 211:
212: #ifdef HAVE_LDAP
213: /*
214: * Prevent OpenLDAP from reading any user dotfiles
215: * or files in the current directory.
216: *
217: */
1.11 millert 218: if (nep < ne_last)
219: *nep++ = "LDAPNOINIT=1";
220: else
221: errx(1, "internal error, attempt to write outside newenv");
1.9 millert 222: #endif
223:
1.1 millert 224: return(&newenv[0]);
225: }
226:
227: /*
228: * Given a variable and value, allocate and format an environment string.
229: */
230: static char *
1.9 millert 231: #ifdef __STDC__
232: format_env(char *var, ...)
233: #else
234: format_env(var, va_alist)
1.1 millert 235: char *var;
1.9 millert 236: va_dcl
237: #endif
1.1 millert 238: {
1.5 millert 239: char *estring;
1.9 millert 240: char *val;
1.5 millert 241: size_t esize;
1.9 millert 242: va_list ap;
1.1 millert 243:
1.9 millert 244: #ifdef __STDC__
245: va_start(ap, var);
246: #else
247: va_start(ap);
248: #endif
249: esize = strlen(var) + 2;
250: while ((val = va_arg(ap, char *)) != NULL)
251: esize += strlen(val);
252: va_end(ap);
1.5 millert 253: estring = (char *) emalloc(esize);
254:
1.9 millert 255: /* Store variable name and the '=' separator. */
1.5 millert 256: if (strlcpy(estring, var, esize) >= esize ||
1.9 millert 257: strlcat(estring, "=", esize) >= esize) {
1.6 millert 258:
259: errx(1, "internal error, format_env() overflow");
1.5 millert 260: }
1.1 millert 261:
1.9 millert 262: /* Now store the variable's value (if any) */
263: #ifdef __STDC__
264: va_start(ap, var);
265: #else
266: va_start(ap);
267: #endif
268: while ((val = va_arg(ap, char *)) != NULL) {
269: if (strlcat(estring, val, esize) >= esize)
270: errx(1, "internal error, format_env() overflow");
271: }
272: va_end(ap);
273:
1.1 millert 274: return(estring);
275: }
276:
277: /*
1.5 millert 278: * Insert str into new_environ, assumes str has an '=' in it.
279: * NOTE: no other routines may modify new_environ, env_size, or env_len.
1.1 millert 280: */
281: static void
1.5 millert 282: insert_env(str, dupcheck)
1.1 millert 283: char *str;
1.5 millert 284: int dupcheck;
1.1 millert 285: {
1.5 millert 286: char **nep;
1.1 millert 287: size_t varlen;
288:
1.8 millert 289: /* Make sure there is room for the new entry plus a NULL. */
290: if (env_len + 2 > env_size) {
1.5 millert 291: env_size += 128;
292: new_environ = erealloc3(new_environ, env_size, sizeof(char *));
293: }
294:
295: if (dupcheck) {
296: varlen = (strchr(str, '=') - str) + 1;
1.1 millert 297:
1.5 millert 298: for (nep = new_environ; *nep; nep++) {
299: if (strncmp(str, *nep, varlen) == 0) {
300: *nep = str;
301: return;
302: }
303: }
304: } else
305: nep = &new_environ[env_len];
306:
307: env_len++;
308: *nep++ = str;
309: *nep = NULL;
1.1 millert 310: }
311:
312: /*
313: * Build a new environment and ether clear potentially dangerous
314: * variables from the old one or start with a clean slate.
315: * Also adds sudo-specific variables (SUDO_*).
316: */
317: char **
1.9 millert 318: rebuild_env(envp, sudo_mode, noexec)
319: char **envp;
1.1 millert 320: int sudo_mode;
1.9 millert 321: int noexec;
1.1 millert 322: {
1.5 millert 323: char **ep, *cp, *ps1;
1.1 millert 324: int okvar, iswild, didvar;
1.5 millert 325: size_t len;
1.1 millert 326: struct list_member *cur;
327:
328: /*
329: * Either clean out the environment or reset to a safe default.
330: */
331: ps1 = NULL;
332: didvar = 0;
1.9 millert 333: if (def_env_reset) {
1.1 millert 334: int keepit;
335:
336: /* Pull in vars we want to keep from the old environment. */
337: for (ep = envp; *ep; ep++) {
338: keepit = 0;
1.10 millert 339:
340: /* Skip variables with values beginning with () (bash functions) */
341: if ((cp = strchr(*ep, '=')) != NULL) {
342: if (strncmp(cp, "=() ", 3) == 0)
343: continue;
344: }
345:
1.9 millert 346: for (cur = def_env_keep; cur; cur = cur->next) {
1.1 millert 347: len = strlen(cur->value);
348: /* Deal with '*' wildcard */
349: if (cur->value[len - 1] == '*') {
350: len--;
351: iswild = 1;
352: } else
353: iswild = 0;
354: if (strncmp(cur->value, *ep, len) == 0 &&
355: (iswild || (*ep)[len] == '=')) {
356: /* We always preserve TERM, no special treatment needed. */
357: if (strncmp(*ep, "TERM=", 5) != 0)
358: keepit = 1;
359: break;
360: }
361: }
362:
363: /* For SUDO_PS1 -> PS1 conversion. */
364: if (strncmp(*ep, "SUDO_PS1=", 8) == 0)
365: ps1 = *ep + 5;
366:
367: if (keepit) {
368: /* Preserve variable. */
369: switch (**ep) {
370: case 'H':
371: if (strncmp(*ep, "HOME=", 5) == 0)
1.9 millert 372: SET(didvar, DID_HOME);
1.5 millert 373: break;
1.1 millert 374: case 'S':
375: if (strncmp(*ep, "SHELL=", 6) == 0)
1.9 millert 376: SET(didvar, DID_SHELL);
1.5 millert 377: break;
1.1 millert 378: case 'L':
379: if (strncmp(*ep, "LOGNAME=", 8) == 0)
1.9 millert 380: SET(didvar, DID_LOGNAME);
1.5 millert 381: break;
1.1 millert 382: case 'U':
383: if (strncmp(*ep, "USER=", 5) == 0)
1.9 millert 384: SET(didvar, DID_USER);
1.5 millert 385: break;
1.1 millert 386: }
1.5 millert 387: insert_env(*ep, 0);
1.1 millert 388: } else {
389: /* Preserve TERM and PATH, ignore anything else. */
1.9 millert 390: if (!ISSET(didvar, DID_TERM) && strncmp(*ep, "TERM=", 5) == 0) {
1.5 millert 391: insert_env(*ep, 0);
1.9 millert 392: SET(didvar, DID_TERM);
393: } else if (!ISSET(didvar, DID_PATH) && strncmp(*ep, "PATH=", 5) == 0) {
1.5 millert 394: insert_env(*ep, 0);
1.9 millert 395: SET(didvar, DID_PATH);
1.1 millert 396: }
397: }
398: }
399:
400: /*
1.9 millert 401: * Add in defaults. In -i mode these come from the runas user,
402: * otherwise they may be from the user's environment (depends
403: * on sudoers options).
1.1 millert 404: */
1.9 millert 405: if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
406: insert_env(format_env("HOME", runas_pw->pw_dir, VNULL), 0);
407: insert_env(format_env("SHELL", runas_pw->pw_shell, VNULL), 0);
408: insert_env(format_env("LOGNAME", runas_pw->pw_name, VNULL), 0);
409: insert_env(format_env("USER", runas_pw->pw_name, VNULL), 0);
410: } else {
411: if (!ISSET(didvar, DID_HOME))
412: insert_env(format_env("HOME", user_dir, VNULL), 0);
413: if (!ISSET(didvar, DID_SHELL))
414: insert_env(format_env("SHELL", sudo_user.pw->pw_shell, VNULL), 0);
415: if (!ISSET(didvar, DID_LOGNAME))
416: insert_env(format_env("LOGNAME", user_name, VNULL), 0);
417: if (!ISSET(didvar, DID_USER))
418: insert_env(format_env("USER", user_name, VNULL), 0);
419: }
1.1 millert 420: } else {
421: /*
422: * Copy envp entries as long as they don't match env_delete or
423: * env_check.
424: */
425: for (ep = envp; *ep; ep++) {
426: okvar = 1;
1.10 millert 427:
428: /* Skip variables with values beginning with () (bash functions) */
429: if ((cp = strchr(*ep, '=')) != NULL) {
430: if (strncmp(cp, "=() ", 3) == 0)
431: continue;
432: }
1.1 millert 433:
434: /* Skip anything listed in env_delete. */
1.9 millert 435: for (cur = def_env_delete; cur && okvar; cur = cur->next) {
1.1 millert 436: len = strlen(cur->value);
437: /* Deal with '*' wildcard */
438: if (cur->value[len - 1] == '*') {
439: len--;
440: iswild = 1;
441: } else
442: iswild = 0;
443: if (strncmp(cur->value, *ep, len) == 0 &&
444: (iswild || (*ep)[len] == '=')) {
445: okvar = 0;
446: }
447: }
448:
449: /* Check certain variables for '%' and '/' characters. */
1.9 millert 450: for (cur = def_env_check; cur && okvar; cur = cur->next) {
1.1 millert 451: len = strlen(cur->value);
452: /* Deal with '*' wildcard */
453: if (cur->value[len - 1] == '*') {
454: len--;
455: iswild = 1;
456: } else
457: iswild = 0;
458: if (strncmp(cur->value, *ep, len) == 0 &&
459: (iswild || (*ep)[len] == '=') &&
460: strpbrk(*ep, "/%")) {
461: okvar = 0;
462: }
463: }
464:
465: if (okvar) {
466: if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
467: ps1 = *ep + 5;
468: else if (strncmp(*ep, "PATH=", 5) == 0)
1.9 millert 469: SET(didvar, DID_PATH);
1.1 millert 470: else if (strncmp(*ep, "TERM=", 5) == 0)
1.9 millert 471: SET(didvar, DID_TERM);
1.5 millert 472: insert_env(*ep, 0);
1.1 millert 473: }
474: }
475: }
476: /* Provide default values for $TERM and $PATH if they are not set. */
1.9 millert 477: if (!ISSET(didvar, DID_TERM))
1.5 millert 478: insert_env("TERM=unknown", 0);
1.9 millert 479: if (!ISSET(didvar, DID_PATH))
480: insert_env(format_env("PATH", _PATH_DEFPATH, VNULL), 0);
1.1 millert 481:
482: #ifdef SECURE_PATH
483: /* Replace the PATH envariable with a secure one. */
1.9 millert 484: insert_env(format_env("PATH", SECURE_PATH, VNULL), 1);
1.1 millert 485: #endif
486:
487: /* Set $USER and $LOGNAME to target if "set_logname" is true. */
1.9 millert 488: if (def_set_logname && runas_pw->pw_name) {
489: insert_env(format_env("LOGNAME", runas_pw->pw_name, VNULL), 1);
490: insert_env(format_env("USER", runas_pw->pw_name, VNULL), 1);
1.1 millert 491: }
492:
1.9 millert 493: /* Set $HOME for `sudo -H'. Only valid at PERM_FULL_RUNAS. */
494: if (ISSET(sudo_mode, MODE_RESET_HOME) && runas_pw->pw_dir)
495: insert_env(format_env("HOME", runas_pw->pw_dir, VNULL), 1);
496:
497: /*
498: * Preload a noexec file? For a list of LD_PRELOAD-alikes, see
499: * http://www.fortran-2000.com/ArnaudRecipes/sharedlib.html
500: * XXX - should prepend to original value, if any
501: */
1.12 millert 502: if (noexec && def_noexec_file != NULL) {
1.9 millert 503: #if defined(__darwin__) || defined(__APPLE__)
504: insert_env(format_env("DYLD_INSERT_LIBRARIES", def_noexec_file, VNULL), 1);
505: insert_env(format_env("DYLD_FORCE_FLAT_NAMESPACE", VNULL), 1);
506: #else
507: # if defined(__osf__) || defined(__sgi)
508: insert_env(format_env("_RLD_LIST", def_noexec_file, ":DEFAULT", VNULL), 1);
509: # else
510: insert_env(format_env("LD_PRELOAD", def_noexec_file, VNULL), 1);
511: # endif
512: #endif
1.12 millert 513: }
1.1 millert 514:
515: /* Set PS1 if SUDO_PS1 is set. */
516: if (ps1)
1.5 millert 517: insert_env(ps1, 1);
1.1 millert 518:
519: /* Add the SUDO_COMMAND envariable (cmnd + args). */
1.9 millert 520: if (user_args)
521: insert_env(format_env("SUDO_COMMAND", user_cmnd, " ", user_args, VNULL), 1);
522: else
523: insert_env(format_env("SUDO_COMMAND", user_cmnd, VNULL), 1);
1.1 millert 524:
525: /* Add the SUDO_USER, SUDO_UID, SUDO_GID environment variables. */
1.9 millert 526: insert_env(format_env("SUDO_USER", user_name, VNULL), 1);
1.5 millert 527: easprintf(&cp, "SUDO_UID=%lu", (unsigned long) user_uid);
528: insert_env(cp, 1);
529: easprintf(&cp, "SUDO_GID=%lu", (unsigned long) user_gid);
530: insert_env(cp, 1);
1.1 millert 531:
1.5 millert 532: return(new_environ);
1.1 millert 533: }
534:
535: void
536: init_envtables()
537: {
538: struct list_member *cur;
1.5 millert 539: const char **p;
1.1 millert 540:
541: /* Fill in "env_delete" variable. */
542: for (p = initial_badenv_table; *p; p++) {
543: cur = emalloc(sizeof(struct list_member));
544: cur->value = estrdup(*p);
1.9 millert 545: cur->next = def_env_delete;
546: def_env_delete = cur;
1.1 millert 547: }
548:
549: /* Fill in "env_check" variable. */
550: for (p = initial_checkenv_table; *p; p++) {
551: cur = emalloc(sizeof(struct list_member));
552: cur->value = estrdup(*p);
1.9 millert 553: cur->next = def_env_check;
554: def_env_check = cur;
1.1 millert 555: }
556: }