Annotation of src/usr.bin/sudo/env.c, Revision 1.5
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 */
59: #include <pwd.h>
60: #include <errno.h>
61:
62: #include "sudo.h"
63:
64: #ifndef lint
1.5 ! millert 65: static const char rcsid[] = "$Sudo: env.c,v 1.25 2003/03/15 20:31:01 millert Exp $";
1.1 millert 66: #endif /* lint */
67:
68: /*
1.5 ! millert 69: * Flags used in rebuild_env()
1.1 millert 70: */
71: #undef DID_TERM
72: #define DID_TERM 0x01
73: #undef DID_PATH
74: #define DID_PATH 0x02
75: #undef DID_HOME
76: #define DID_HOME 0x04
77: #undef DID_SHELL
78: #define DID_SHELL 0x08
79: #undef DID_LOGNAME
80: #define DID_LOGNAME 0x10
81: #undef DID_USER
82: #define DID_USER 0x12
83:
84: /*
85: * Prototypes
86: */
87: char **rebuild_env __P((int, char **));
88: char **zero_env __P((char **));
1.5 ! millert 89: static void insert_env __P((char *, int));
1.1 millert 90: static char *format_env __P((char *, char *));
91:
92: /*
93: * Default table of "bad" variables to remove from the environment.
94: * XXX - how to omit TERMCAP if it starts with '/'?
95: */
1.5 ! millert 96: static const char *initial_badenv_table[] = {
1.1 millert 97: "IFS",
98: "LOCALDOMAIN",
99: "RES_OPTIONS",
100: "HOSTALIASES",
101: "NLSPATH",
102: "PATH_LOCALE",
103: "LD_*",
104: "_RLD*",
105: #ifdef __hpux
106: "SHLIB_PATH",
107: #endif /* __hpux */
108: #ifdef _AIX
109: "LIBPATH",
110: #endif /* _AIX */
1.5 ! millert 111: #ifdef __APPLE__
! 112: "DYLD_*",
! 113: #endif
1.1 millert 114: #ifdef HAVE_KERB4
115: "KRB_CONF*",
116: "KRBCONFDIR"
117: "KRBTKFILE",
118: #endif /* HAVE_KERB4 */
119: #ifdef HAVE_KERB5
120: "KRB5_CONFIG*",
121: #endif /* HAVE_KERB5 */
122: #ifdef HAVE_SECURID
123: "VAR_ACE",
124: "USR_ACE",
125: "DLC_ACE",
126: #endif /* HAVE_SECURID */
127: "TERMINFO",
128: "TERMINFO_DIRS",
129: "TERMPATH",
130: "TERMCAP", /* XXX - only if it starts with '/' */
131: "ENV",
132: "BASH_ENV",
133: NULL
134: };
135:
136: /*
137: * Default table of variables to check for '%' and '/' characters.
138: */
1.5 ! millert 139: static const char *initial_checkenv_table[] = {
1.1 millert 140: "LC_*",
141: "LANG",
142: "LANGUAGE",
143: NULL
144: };
145:
1.5 ! millert 146: static char **new_environ; /* Modified copy of the environment */
! 147: static size_t env_size; /* size of new_environ in char **'s */
! 148: static size_t env_len; /* number of slots used, not counting NULL */
! 149:
1.1 millert 150: /*
151: * Zero out environment and replace with a minimal set of
152: * USER, LOGNAME, HOME, TZ, PATH (XXX - should just set path to default)
153: * May set user_path, user_shell, and/or user_prompt as side effects.
154: */
155: char **
156: zero_env(envp)
157: char **envp;
158: {
159: char **ep, **nep;
160: static char *newenv[7];
161:
162: for (ep = envp; *ep; ep++) {
163: switch (**ep) {
164: case 'H':
165: if (strncmp("HOME=", *ep, 5) == 0)
166: break;
1.2 millert 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.2 millert 182: else if (!user_prompt && !strncmp("SUDO_PROMPT=", *ep, 12))
1.1 millert 183: user_prompt = *ep + 12;
1.2 millert 184: continue;
1.1 millert 185: case 'T':
186: if (strncmp("TZ=", *ep, 3) == 0)
187: break;
1.2 millert 188: continue;
1.1 millert 189: case 'U':
190: if (strncmp("USER=", *ep, 5) == 0)
191: break;
1.2 millert 192: continue;
1.1 millert 193: default:
194: continue;
195: }
196:
197: /* Deal with multiply defined variables (take first instance) */
198: for (nep = newenv; *nep; nep++) {
199: if (**nep == **ep)
200: break;
201: }
202: if (*nep == NULL)
203: *nep++ = *ep;
204: }
205: return(&newenv[0]);
206: }
207:
208: /*
209: * Given a variable and value, allocate and format an environment string.
210: */
211: static char *
212: format_env(var, val)
213: char *var;
214: char *val;
215: {
1.5 ! millert 216: char *estring;
! 217: size_t esize;
1.1 millert 218:
1.5 ! millert 219: esize = strlen(var) + 1 + strlen(val) + 1;
! 220: estring = (char *) emalloc(esize);
! 221:
! 222: /* We pre-allocate enough space, so this should never overflow. */
! 223: if (strlcpy(estring, var, esize) >= esize ||
! 224: strlcat(estring, "=", esize) >= esize ||
! 225: strlcat(estring, val, esize) >= esize) {
! 226: (void) fprintf(stderr, "%s: internal error, format_env() overflow\n",
! 227: Argv[0]);
! 228: exit(1);
! 229: }
1.1 millert 230:
231: return(estring);
232: }
233:
234: /*
1.5 ! millert 235: * Insert str into new_environ, assumes str has an '=' in it.
! 236: * NOTE: no other routines may modify new_environ, env_size, or env_len.
1.1 millert 237: */
238: static void
1.5 ! millert 239: insert_env(str, dupcheck)
1.1 millert 240: char *str;
1.5 ! millert 241: int dupcheck;
1.1 millert 242: {
1.5 ! millert 243: char **nep;
1.1 millert 244: size_t varlen;
245:
1.5 ! millert 246: /* Make sure there is room for the new entry. */
! 247: if (env_len + 1 > env_size) {
! 248: env_size += 128;
! 249: new_environ = erealloc3(new_environ, env_size, sizeof(char *));
! 250: }
! 251:
! 252: if (dupcheck) {
! 253: varlen = (strchr(str, '=') - str) + 1;
1.1 millert 254:
1.5 ! millert 255: for (nep = new_environ; *nep; nep++) {
! 256: if (strncmp(str, *nep, varlen) == 0) {
! 257: *nep = str;
! 258: return;
! 259: }
! 260: }
! 261: } else
! 262: nep = &new_environ[env_len];
! 263:
! 264: env_len++;
! 265: *nep++ = str;
! 266: *nep = NULL;
1.1 millert 267: }
268:
269: /*
270: * Build a new environment and ether clear potentially dangerous
271: * variables from the old one or start with a clean slate.
272: * Also adds sudo-specific variables (SUDO_*).
273: */
274: char **
275: rebuild_env(sudo_mode, envp)
276: int sudo_mode;
277: char **envp;
278: {
1.5 ! millert 279: char **ep, *cp, *ps1;
1.1 millert 280: int okvar, iswild, didvar;
1.5 ! millert 281: size_t len;
1.1 millert 282: struct list_member *cur;
283:
284: /*
285: * Either clean out the environment or reset to a safe default.
286: */
287: ps1 = NULL;
288: didvar = 0;
289: if (def_flag(I_ENV_RESET)) {
290: int keepit;
291:
292: /* Pull in vars we want to keep from the old environment. */
293: for (ep = envp; *ep; ep++) {
294: keepit = 0;
295: for (cur = def_list(I_ENV_KEEP); cur; cur = cur->next) {
296: len = strlen(cur->value);
297: /* Deal with '*' wildcard */
298: if (cur->value[len - 1] == '*') {
299: len--;
300: iswild = 1;
301: } else
302: iswild = 0;
303: if (strncmp(cur->value, *ep, len) == 0 &&
304: (iswild || (*ep)[len] == '=')) {
305: /* We always preserve TERM, no special treatment needed. */
306: if (strncmp(*ep, "TERM=", 5) != 0)
307: keepit = 1;
308: break;
309: }
310: }
311:
312: /* For SUDO_PS1 -> PS1 conversion. */
313: if (strncmp(*ep, "SUDO_PS1=", 8) == 0)
314: ps1 = *ep + 5;
315:
316: if (keepit) {
317: /* Preserve variable. */
318: switch (**ep) {
319: case 'H':
320: if (strncmp(*ep, "HOME=", 5) == 0)
321: didvar |= DID_HOME;
1.5 ! millert 322: break;
1.1 millert 323: case 'S':
324: if (strncmp(*ep, "SHELL=", 6) == 0)
325: didvar |= DID_SHELL;
1.5 ! millert 326: break;
1.1 millert 327: case 'L':
328: if (strncmp(*ep, "LOGNAME=", 8) == 0)
329: didvar |= DID_LOGNAME;
1.5 ! millert 330: break;
1.1 millert 331: case 'U':
332: if (strncmp(*ep, "USER=", 5) == 0)
333: didvar |= DID_USER;
1.5 ! millert 334: break;
1.1 millert 335: }
1.5 ! millert 336: insert_env(*ep, 0);
1.1 millert 337: } else {
338: /* Preserve TERM and PATH, ignore anything else. */
339: if (!(didvar & DID_TERM) && !strncmp(*ep, "TERM=", 5)) {
1.5 ! millert 340: insert_env(*ep, 0);
1.1 millert 341: didvar |= DID_TERM;
342: } else if (!(didvar & DID_PATH) && !strncmp(*ep, "PATH=", 5)) {
1.5 ! millert 343: insert_env(*ep, 0);
1.1 millert 344: didvar |= DID_PATH;
345: }
346: }
347: }
348:
349: /*
350: * Add in defaults unless they were preserved from the
351: * user's environment.
352: */
353: if (!(didvar & DID_HOME))
1.5 ! millert 354: insert_env(format_env("HOME", user_dir), 0);
1.1 millert 355: if (!(didvar & DID_SHELL))
1.5 ! millert 356: insert_env(format_env("SHELL", sudo_user.pw->pw_shell), 0);
1.1 millert 357: if (!(didvar & DID_LOGNAME))
1.5 ! millert 358: insert_env(format_env("LOGNAME", user_name), 0);
1.1 millert 359: if (!(didvar & DID_USER))
1.5 ! millert 360: insert_env(format_env("USER", user_name), 0);
1.1 millert 361: } else {
362: /*
363: * Copy envp entries as long as they don't match env_delete or
364: * env_check.
365: */
366: for (ep = envp; *ep; ep++) {
367: okvar = 1;
368:
369: /* Skip anything listed in env_delete. */
370: for (cur = def_list(I_ENV_DELETE); cur && okvar; cur = cur->next) {
371: len = strlen(cur->value);
372: /* Deal with '*' wildcard */
373: if (cur->value[len - 1] == '*') {
374: len--;
375: iswild = 1;
376: } else
377: iswild = 0;
378: if (strncmp(cur->value, *ep, len) == 0 &&
379: (iswild || (*ep)[len] == '=')) {
380: okvar = 0;
381: }
382: }
383:
384: /* Check certain variables for '%' and '/' characters. */
385: for (cur = def_list(I_ENV_CHECK); cur && okvar; cur = cur->next) {
386: len = strlen(cur->value);
387: /* Deal with '*' wildcard */
388: if (cur->value[len - 1] == '*') {
389: len--;
390: iswild = 1;
391: } else
392: iswild = 0;
393: if (strncmp(cur->value, *ep, len) == 0 &&
394: (iswild || (*ep)[len] == '=') &&
395: strpbrk(*ep, "/%")) {
396: okvar = 0;
397: }
398: }
399:
400: if (okvar) {
401: if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
402: ps1 = *ep + 5;
403: else if (strncmp(*ep, "PATH=", 5) == 0)
404: didvar |= DID_PATH;
405: else if (strncmp(*ep, "TERM=", 5) == 0)
406: didvar |= DID_TERM;
1.5 ! millert 407: insert_env(*ep, 0);
1.1 millert 408: }
409: }
410: }
411: /* Provide default values for $TERM and $PATH if they are not set. */
412: if (!(didvar & DID_TERM))
1.5 ! millert 413: insert_env("TERM=unknown", 0);
1.1 millert 414: if (!(didvar & DID_PATH))
1.5 ! millert 415: insert_env(format_env("PATH", _PATH_DEFPATH), 0);
1.1 millert 416:
417: #ifdef SECURE_PATH
418: /* Replace the PATH envariable with a secure one. */
1.5 ! millert 419: insert_env(format_env("PATH", SECURE_PATH), 1);
1.1 millert 420: #endif
421:
422: /* Set $USER and $LOGNAME to target if "set_logname" is true. */
423: if (def_flag(I_SET_LOGNAME) && runas_pw->pw_name) {
1.5 ! millert 424: insert_env(format_env("LOGNAME", runas_pw->pw_name), 1);
! 425: insert_env(format_env("USER", runas_pw->pw_name), 1);
1.1 millert 426: }
427:
428: /* Set $HOME for `sudo -H'. Only valid at PERM_RUNAS. */
429: if ((sudo_mode & MODE_RESET_HOME) && runas_pw->pw_dir)
1.5 ! millert 430: insert_env(format_env("HOME", runas_pw->pw_dir), 1);
1.1 millert 431:
432: /* Set PS1 if SUDO_PS1 is set. */
433: if (ps1)
1.5 ! millert 434: insert_env(ps1, 1);
1.1 millert 435:
436: /* Add the SUDO_COMMAND envariable (cmnd + args). */
437: if (user_args) {
1.4 millert 438: easprintf(&cp, "SUDO_COMMAND=%s %s", user_cmnd, user_args);
1.5 ! millert 439: insert_env(cp, 1);
1.1 millert 440: } else
1.5 ! millert 441: insert_env(format_env("SUDO_COMMAND", user_cmnd), 1);
1.1 millert 442:
443: /* Add the SUDO_USER, SUDO_UID, SUDO_GID environment variables. */
1.5 ! millert 444: insert_env(format_env("SUDO_USER", user_name), 1);
! 445: easprintf(&cp, "SUDO_UID=%lu", (unsigned long) user_uid);
! 446: insert_env(cp, 1);
! 447: easprintf(&cp, "SUDO_GID=%lu", (unsigned long) user_gid);
! 448: insert_env(cp, 1);
1.1 millert 449:
1.5 ! millert 450: return(new_environ);
1.1 millert 451: }
452:
453: void
454: init_envtables()
455: {
456: struct list_member *cur;
1.5 ! millert 457: const char **p;
1.1 millert 458:
459: /* Fill in "env_delete" variable. */
460: for (p = initial_badenv_table; *p; p++) {
461: cur = emalloc(sizeof(struct list_member));
462: cur->value = estrdup(*p);
463: cur->next = def_list(I_ENV_DELETE);
464: def_list(I_ENV_DELETE) = cur;
465: }
466:
467: /* Fill in "env_check" variable. */
468: for (p = initial_checkenv_table; *p; p++) {
469: cur = emalloc(sizeof(struct list_member));
470: cur->value = estrdup(*p);
471: cur->next = def_list(I_ENV_CHECK);
472: def_list(I_ENV_CHECK) = cur;
473: }
474: }