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