Annotation of src/usr.bin/sudo/env.c, Revision 1.10
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.1 millert 72: #define DID_USER 0x12
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",
91: "LOCALDOMAIN",
92: "RES_OPTIONS",
93: "HOSTALIASES",
94: "NLSPATH",
95: "PATH_LOCALE",
96: "LD_*",
97: "_RLD*",
98: #ifdef __hpux
99: "SHLIB_PATH",
100: #endif /* __hpux */
101: #ifdef _AIX
102: "LIBPATH",
103: #endif /* _AIX */
1.5 millert 104: #ifdef __APPLE__
105: "DYLD_*",
106: #endif
1.1 millert 107: #ifdef HAVE_KERB4
108: "KRB_CONF*",
1.6 millert 109: "KRBCONFDIR",
1.1 millert 110: "KRBTKFILE",
111: #endif /* HAVE_KERB4 */
112: #ifdef HAVE_KERB5
113: "KRB5_CONFIG*",
114: #endif /* HAVE_KERB5 */
115: #ifdef HAVE_SECURID
116: "VAR_ACE",
117: "USR_ACE",
118: "DLC_ACE",
119: #endif /* HAVE_SECURID */
120: "TERMINFO",
121: "TERMINFO_DIRS",
122: "TERMPATH",
123: "TERMCAP", /* XXX - only if it starts with '/' */
124: "ENV",
125: "BASH_ENV",
126: NULL
127: };
128:
129: /*
130: * Default table of variables to check for '%' and '/' characters.
131: */
1.5 millert 132: static const char *initial_checkenv_table[] = {
1.1 millert 133: "LC_*",
134: "LANG",
135: "LANGUAGE",
136: NULL
137: };
138:
1.5 millert 139: static char **new_environ; /* Modified copy of the environment */
140: static size_t env_size; /* size of new_environ in char **'s */
141: static size_t env_len; /* number of slots used, not counting NULL */
142:
1.1 millert 143: /*
144: * Zero out environment and replace with a minimal set of
145: * USER, LOGNAME, HOME, TZ, PATH (XXX - should just set path to default)
146: * May set user_path, user_shell, and/or user_prompt as side effects.
147: */
148: char **
149: zero_env(envp)
150: char **envp;
151: {
1.9 millert 152: static char *newenv[8];
153: char **ep, **nep = newenv;
154: extern char *prev_user;
1.1 millert 155:
156: for (ep = envp; *ep; ep++) {
157: switch (**ep) {
158: case 'H':
159: if (strncmp("HOME=", *ep, 5) == 0)
160: break;
1.2 millert 161: continue;
1.1 millert 162: case 'L':
163: if (strncmp("LOGNAME=", *ep, 8) == 0)
164: break;
1.2 millert 165: continue;
1.1 millert 166: case 'P':
167: if (strncmp("PATH=", *ep, 5) == 0) {
168: user_path = *ep + 5;
169: /* XXX - set to sane default instead of user's? */
170: break;
171: }
1.2 millert 172: continue;
1.1 millert 173: case 'S':
1.2 millert 174: if (strncmp("SHELL=", *ep, 6) == 0)
1.1 millert 175: user_shell = *ep + 6;
1.9 millert 176: else if (!user_prompt && strncmp("SUDO_PROMPT=", *ep, 12) == 0)
1.1 millert 177: user_prompt = *ep + 12;
1.9 millert 178: else if (strncmp("SUDO_USER=", *ep, 10) == 0)
179: prev_user = *ep + 10;
1.2 millert 180: continue;
1.1 millert 181: case 'T':
182: if (strncmp("TZ=", *ep, 3) == 0)
183: break;
1.2 millert 184: continue;
1.1 millert 185: case 'U':
186: if (strncmp("USER=", *ep, 5) == 0)
187: break;
1.2 millert 188: continue;
1.1 millert 189: default:
190: continue;
191: }
192:
193: /* Deal with multiply defined variables (take first instance) */
194: for (nep = newenv; *nep; nep++) {
195: if (**nep == **ep)
196: break;
197: }
198: if (*nep == NULL)
199: *nep++ = *ep;
200: }
1.9 millert 201:
202: #ifdef HAVE_LDAP
203: /*
204: * Prevent OpenLDAP from reading any user dotfiles
205: * or files in the current directory.
206: *
207: */
208: *nep++ = "LDAPNOINIT=1";
209: #endif
210:
1.1 millert 211: return(&newenv[0]);
212: }
213:
214: /*
215: * Given a variable and value, allocate and format an environment string.
216: */
217: static char *
1.9 millert 218: #ifdef __STDC__
219: format_env(char *var, ...)
220: #else
221: format_env(var, va_alist)
1.1 millert 222: char *var;
1.9 millert 223: va_dcl
224: #endif
1.1 millert 225: {
1.5 millert 226: char *estring;
1.9 millert 227: char *val;
1.5 millert 228: size_t esize;
1.9 millert 229: va_list ap;
1.1 millert 230:
1.9 millert 231: #ifdef __STDC__
232: va_start(ap, var);
233: #else
234: va_start(ap);
235: #endif
236: esize = strlen(var) + 2;
237: while ((val = va_arg(ap, char *)) != NULL)
238: esize += strlen(val);
239: va_end(ap);
1.5 millert 240: estring = (char *) emalloc(esize);
241:
1.9 millert 242: /* Store variable name and the '=' separator. */
1.5 millert 243: if (strlcpy(estring, var, esize) >= esize ||
1.9 millert 244: strlcat(estring, "=", esize) >= esize) {
1.6 millert 245:
246: errx(1, "internal error, format_env() overflow");
1.5 millert 247: }
1.1 millert 248:
1.9 millert 249: /* Now store the variable's value (if any) */
250: #ifdef __STDC__
251: va_start(ap, var);
252: #else
253: va_start(ap);
254: #endif
255: while ((val = va_arg(ap, char *)) != NULL) {
256: if (strlcat(estring, val, esize) >= esize)
257: errx(1, "internal error, format_env() overflow");
258: }
259: va_end(ap);
260:
1.1 millert 261: return(estring);
262: }
263:
264: /*
1.5 millert 265: * Insert str into new_environ, assumes str has an '=' in it.
266: * NOTE: no other routines may modify new_environ, env_size, or env_len.
1.1 millert 267: */
268: static void
1.5 millert 269: insert_env(str, dupcheck)
1.1 millert 270: char *str;
1.5 millert 271: int dupcheck;
1.1 millert 272: {
1.5 millert 273: char **nep;
1.1 millert 274: size_t varlen;
275:
1.8 millert 276: /* Make sure there is room for the new entry plus a NULL. */
277: if (env_len + 2 > env_size) {
1.5 millert 278: env_size += 128;
279: new_environ = erealloc3(new_environ, env_size, sizeof(char *));
280: }
281:
282: if (dupcheck) {
283: varlen = (strchr(str, '=') - str) + 1;
1.1 millert 284:
1.5 millert 285: for (nep = new_environ; *nep; nep++) {
286: if (strncmp(str, *nep, varlen) == 0) {
287: *nep = str;
288: return;
289: }
290: }
291: } else
292: nep = &new_environ[env_len];
293:
294: env_len++;
295: *nep++ = str;
296: *nep = NULL;
1.1 millert 297: }
298:
299: /*
300: * Build a new environment and ether clear potentially dangerous
301: * variables from the old one or start with a clean slate.
302: * Also adds sudo-specific variables (SUDO_*).
303: */
304: char **
1.9 millert 305: rebuild_env(envp, sudo_mode, noexec)
306: char **envp;
1.1 millert 307: int sudo_mode;
1.9 millert 308: int noexec;
1.1 millert 309: {
1.5 millert 310: char **ep, *cp, *ps1;
1.1 millert 311: int okvar, iswild, didvar;
1.5 millert 312: size_t len;
1.1 millert 313: struct list_member *cur;
314:
315: /*
316: * Either clean out the environment or reset to a safe default.
317: */
318: ps1 = NULL;
319: didvar = 0;
1.9 millert 320: if (def_env_reset) {
1.1 millert 321: int keepit;
322:
323: /* Pull in vars we want to keep from the old environment. */
324: for (ep = envp; *ep; ep++) {
325: keepit = 0;
1.10 ! millert 326:
! 327: /* Skip variables with values beginning with () (bash functions) */
! 328: if ((cp = strchr(*ep, '=')) != NULL) {
! 329: if (strncmp(cp, "=() ", 3) == 0)
! 330: continue;
! 331: }
! 332:
1.9 millert 333: for (cur = def_env_keep; cur; cur = cur->next) {
1.1 millert 334: len = strlen(cur->value);
335: /* Deal with '*' wildcard */
336: if (cur->value[len - 1] == '*') {
337: len--;
338: iswild = 1;
339: } else
340: iswild = 0;
341: if (strncmp(cur->value, *ep, len) == 0 &&
342: (iswild || (*ep)[len] == '=')) {
343: /* We always preserve TERM, no special treatment needed. */
344: if (strncmp(*ep, "TERM=", 5) != 0)
345: keepit = 1;
346: break;
347: }
348: }
349:
350: /* For SUDO_PS1 -> PS1 conversion. */
351: if (strncmp(*ep, "SUDO_PS1=", 8) == 0)
352: ps1 = *ep + 5;
353:
354: if (keepit) {
355: /* Preserve variable. */
356: switch (**ep) {
357: case 'H':
358: if (strncmp(*ep, "HOME=", 5) == 0)
1.9 millert 359: SET(didvar, DID_HOME);
1.5 millert 360: break;
1.1 millert 361: case 'S':
362: if (strncmp(*ep, "SHELL=", 6) == 0)
1.9 millert 363: SET(didvar, DID_SHELL);
1.5 millert 364: break;
1.1 millert 365: case 'L':
366: if (strncmp(*ep, "LOGNAME=", 8) == 0)
1.9 millert 367: SET(didvar, DID_LOGNAME);
1.5 millert 368: break;
1.1 millert 369: case 'U':
370: if (strncmp(*ep, "USER=", 5) == 0)
1.9 millert 371: SET(didvar, DID_USER);
1.5 millert 372: break;
1.1 millert 373: }
1.5 millert 374: insert_env(*ep, 0);
1.1 millert 375: } else {
376: /* Preserve TERM and PATH, ignore anything else. */
1.9 millert 377: if (!ISSET(didvar, DID_TERM) && strncmp(*ep, "TERM=", 5) == 0) {
1.5 millert 378: insert_env(*ep, 0);
1.9 millert 379: SET(didvar, DID_TERM);
380: } else if (!ISSET(didvar, DID_PATH) && strncmp(*ep, "PATH=", 5) == 0) {
1.5 millert 381: insert_env(*ep, 0);
1.9 millert 382: SET(didvar, DID_PATH);
1.1 millert 383: }
384: }
385: }
386:
387: /*
1.9 millert 388: * Add in defaults. In -i mode these come from the runas user,
389: * otherwise they may be from the user's environment (depends
390: * on sudoers options).
1.1 millert 391: */
1.9 millert 392: if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
393: insert_env(format_env("HOME", runas_pw->pw_dir, VNULL), 0);
394: insert_env(format_env("SHELL", runas_pw->pw_shell, VNULL), 0);
395: insert_env(format_env("LOGNAME", runas_pw->pw_name, VNULL), 0);
396: insert_env(format_env("USER", runas_pw->pw_name, VNULL), 0);
397: } else {
398: if (!ISSET(didvar, DID_HOME))
399: insert_env(format_env("HOME", user_dir, VNULL), 0);
400: if (!ISSET(didvar, DID_SHELL))
401: insert_env(format_env("SHELL", sudo_user.pw->pw_shell, VNULL), 0);
402: if (!ISSET(didvar, DID_LOGNAME))
403: insert_env(format_env("LOGNAME", user_name, VNULL), 0);
404: if (!ISSET(didvar, DID_USER))
405: insert_env(format_env("USER", user_name, VNULL), 0);
406: }
1.1 millert 407: } else {
408: /*
409: * Copy envp entries as long as they don't match env_delete or
410: * env_check.
411: */
412: for (ep = envp; *ep; ep++) {
413: okvar = 1;
1.10 ! millert 414:
! 415: /* Skip variables with values beginning with () (bash functions) */
! 416: if ((cp = strchr(*ep, '=')) != NULL) {
! 417: if (strncmp(cp, "=() ", 3) == 0)
! 418: continue;
! 419: }
1.1 millert 420:
421: /* Skip anything listed in env_delete. */
1.9 millert 422: for (cur = def_env_delete; cur && okvar; cur = cur->next) {
1.1 millert 423: len = strlen(cur->value);
424: /* Deal with '*' wildcard */
425: if (cur->value[len - 1] == '*') {
426: len--;
427: iswild = 1;
428: } else
429: iswild = 0;
430: if (strncmp(cur->value, *ep, len) == 0 &&
431: (iswild || (*ep)[len] == '=')) {
432: okvar = 0;
433: }
434: }
435:
436: /* Check certain variables for '%' and '/' characters. */
1.9 millert 437: for (cur = def_env_check; cur && okvar; cur = cur->next) {
1.1 millert 438: len = strlen(cur->value);
439: /* Deal with '*' wildcard */
440: if (cur->value[len - 1] == '*') {
441: len--;
442: iswild = 1;
443: } else
444: iswild = 0;
445: if (strncmp(cur->value, *ep, len) == 0 &&
446: (iswild || (*ep)[len] == '=') &&
447: strpbrk(*ep, "/%")) {
448: okvar = 0;
449: }
450: }
451:
452: if (okvar) {
453: if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
454: ps1 = *ep + 5;
455: else if (strncmp(*ep, "PATH=", 5) == 0)
1.9 millert 456: SET(didvar, DID_PATH);
1.1 millert 457: else if (strncmp(*ep, "TERM=", 5) == 0)
1.9 millert 458: SET(didvar, DID_TERM);
1.5 millert 459: insert_env(*ep, 0);
1.1 millert 460: }
461: }
462: }
463: /* Provide default values for $TERM and $PATH if they are not set. */
1.9 millert 464: if (!ISSET(didvar, DID_TERM))
1.5 millert 465: insert_env("TERM=unknown", 0);
1.9 millert 466: if (!ISSET(didvar, DID_PATH))
467: insert_env(format_env("PATH", _PATH_DEFPATH, VNULL), 0);
1.1 millert 468:
469: #ifdef SECURE_PATH
470: /* Replace the PATH envariable with a secure one. */
1.9 millert 471: insert_env(format_env("PATH", SECURE_PATH, VNULL), 1);
1.1 millert 472: #endif
473:
474: /* Set $USER and $LOGNAME to target if "set_logname" is true. */
1.9 millert 475: if (def_set_logname && runas_pw->pw_name) {
476: insert_env(format_env("LOGNAME", runas_pw->pw_name, VNULL), 1);
477: insert_env(format_env("USER", runas_pw->pw_name, VNULL), 1);
1.1 millert 478: }
479:
1.9 millert 480: /* Set $HOME for `sudo -H'. Only valid at PERM_FULL_RUNAS. */
481: if (ISSET(sudo_mode, MODE_RESET_HOME) && runas_pw->pw_dir)
482: insert_env(format_env("HOME", runas_pw->pw_dir, VNULL), 1);
483:
484: /*
485: * Preload a noexec file? For a list of LD_PRELOAD-alikes, see
486: * http://www.fortran-2000.com/ArnaudRecipes/sharedlib.html
487: * XXX - should prepend to original value, if any
488: */
489: if (noexec && def_noexec_file != NULL)
490: #if defined(__darwin__) || defined(__APPLE__)
491: insert_env(format_env("DYLD_INSERT_LIBRARIES", def_noexec_file, VNULL), 1);
492: insert_env(format_env("DYLD_FORCE_FLAT_NAMESPACE", VNULL), 1);
493: #else
494: # if defined(__osf__) || defined(__sgi)
495: insert_env(format_env("_RLD_LIST", def_noexec_file, ":DEFAULT", VNULL), 1);
496: # else
497: insert_env(format_env("LD_PRELOAD", def_noexec_file, VNULL), 1);
498: # endif
499: #endif
1.1 millert 500:
501: /* Set PS1 if SUDO_PS1 is set. */
502: if (ps1)
1.5 millert 503: insert_env(ps1, 1);
1.1 millert 504:
505: /* Add the SUDO_COMMAND envariable (cmnd + args). */
1.9 millert 506: if (user_args)
507: insert_env(format_env("SUDO_COMMAND", user_cmnd, " ", user_args, VNULL), 1);
508: else
509: insert_env(format_env("SUDO_COMMAND", user_cmnd, VNULL), 1);
1.1 millert 510:
511: /* Add the SUDO_USER, SUDO_UID, SUDO_GID environment variables. */
1.9 millert 512: insert_env(format_env("SUDO_USER", user_name, VNULL), 1);
1.5 millert 513: easprintf(&cp, "SUDO_UID=%lu", (unsigned long) user_uid);
514: insert_env(cp, 1);
515: easprintf(&cp, "SUDO_GID=%lu", (unsigned long) user_gid);
516: insert_env(cp, 1);
1.1 millert 517:
1.5 millert 518: return(new_environ);
1.1 millert 519: }
520:
521: void
522: init_envtables()
523: {
524: struct list_member *cur;
1.5 millert 525: const char **p;
1.1 millert 526:
527: /* Fill in "env_delete" variable. */
528: for (p = initial_badenv_table; *p; p++) {
529: cur = emalloc(sizeof(struct list_member));
530: cur->value = estrdup(*p);
1.9 millert 531: cur->next = def_env_delete;
532: def_env_delete = cur;
1.1 millert 533: }
534:
535: /* Fill in "env_check" variable. */
536: for (p = initial_checkenv_table; *p; p++) {
537: cur = emalloc(sizeof(struct list_member));
538: cur->value = estrdup(*p);
1.9 millert 539: cur->next = def_env_check;
540: def_env_check = cur;
1.1 millert 541: }
542: }