Annotation of embedaddon/sudo/plugins/sudoers/env.c, revision 1.1.1.3
1.1 misho 1: /*
2: * Copyright (c) 2000-2005, 2007-2011
3: * Todd C. Miller <Todd.Miller@courtesan.com>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: *
17: * Sponsored in part by the Defense Advanced Research Projects
18: * Agency (DARPA) and Air Force Research Laboratory, Air Force
19: * Materiel Command, USAF, under agreement number F39502-99-1-0512.
20: */
21:
22: #include <config.h>
23:
24: #include <sys/types.h>
25: #include <sys/param.h>
26: #include <sys/stat.h>
27: #include <stdio.h>
28: #ifdef STDC_HEADERS
29: # include <stdlib.h>
30: # include <stddef.h>
31: #else
32: # ifdef HAVE_STDLIB_H
33: # include <stdlib.h>
34: # endif
35: #endif /* STDC_HEADERS */
36: #ifdef HAVE_STRING_H
37: # include <string.h>
38: #endif /* HAVE_STRING_H */
39: #ifdef HAVE_STRINGS_H
40: # include <strings.h>
41: #endif /* HAVE_STRINGS_H */
42: #ifdef HAVE_UNISTD_H
43: # include <unistd.h>
44: #endif /* HAVE_UNISTD_H */
1.1.1.3 ! misho 45: #ifdef HAVE_INTTYPES_H
! 46: # include <inttypes.h>
! 47: #endif
1.1.1.2 misho 48: #ifdef HAVE_LOGIN_CAP_H
49: # include <login_cap.h>
50: # ifndef LOGIN_SETENV
51: # define LOGIN_SETENV 0
52: # endif
53: #endif /* HAVE_LOGIN_CAP_H */
1.1 misho 54: #include <ctype.h>
55: #include <errno.h>
1.1.1.3 ! misho 56: #include <limits.h>
1.1 misho 57: #include <pwd.h>
58:
59: #include "sudoers.h"
60:
61: /*
1.1.1.3 ! misho 62: * If there is no SIZE_MAX or SIZE_T_MAX we have to assume that size_t
! 63: * could be signed (as it is on SunOS 4.x). This just means that
! 64: * emalloc2() and erealloc3() cannot allocate huge amounts on such a
! 65: * platform but that is OK since sudo doesn't need to do so anyway.
! 66: */
! 67: #ifndef SIZE_MAX
! 68: # ifdef SIZE_T_MAX
! 69: # define SIZE_MAX SIZE_T_MAX
! 70: # else
! 71: # define SIZE_MAX INT_MAX
! 72: # endif /* SIZE_T_MAX */
! 73: #endif /* SIZE_MAX */
! 74:
! 75: /*
1.1 misho 76: * Flags used in rebuild_env()
77: */
78: #undef DID_TERM
79: #define DID_TERM 0x0001
80: #undef DID_PATH
81: #define DID_PATH 0x0002
82: #undef DID_HOME
83: #define DID_HOME 0x0004
84: #undef DID_SHELL
85: #define DID_SHELL 0x0008
86: #undef DID_LOGNAME
87: #define DID_LOGNAME 0x0010
88: #undef DID_USER
89: #define DID_USER 0x0020
90: #undef DID_USERNAME
91: #define DID_USERNAME 0x0040
92: #undef DID_MAIL
93: #define DID_MAIL 0x0080
94: #undef DID_MAX
95: #define DID_MAX 0x00ff
96:
97: #undef KEPT_TERM
98: #define KEPT_TERM 0x0100
99: #undef KEPT_PATH
100: #define KEPT_PATH 0x0200
101: #undef KEPT_HOME
102: #define KEPT_HOME 0x0400
103: #undef KEPT_SHELL
104: #define KEPT_SHELL 0x0800
105: #undef KEPT_LOGNAME
106: #define KEPT_LOGNAME 0x1000
107: #undef KEPT_USER
108: #define KEPT_USER 0x2000
109: #undef KEPT_USERNAME
110: #define KEPT_USERNAME 0x4000
111: #undef KEPT_MAIL
112: #define KEPT_MAIL 0x8000
113: #undef KEPT_MAX
114: #define KEPT_MAX 0xff00
115:
116: struct environment {
1.1.1.2 misho 117: char * const *old_envp; /* pointer the environment we passed back */
1.1 misho 118: char **envp; /* pointer to the new environment */
119: size_t env_size; /* size of new_environ in char **'s */
120: size_t env_len; /* number of slots used, not counting NULL */
121: };
122:
123: /*
124: * Copy of the sudo-managed environment.
125: */
126: static struct environment env;
127:
128: /*
129: * Default table of "bad" variables to remove from the environment.
130: * XXX - how to omit TERMCAP if it starts with '/'?
131: */
132: static const char *initial_badenv_table[] = {
133: "IFS",
134: "CDPATH",
135: "LOCALDOMAIN",
136: "RES_OPTIONS",
137: "HOSTALIASES",
138: "NLSPATH",
139: "PATH_LOCALE",
140: "LD_*",
141: "_RLD*",
142: #ifdef __hpux
143: "SHLIB_PATH",
144: #endif /* __hpux */
145: #ifdef _AIX
146: "LDR_*",
147: "LIBPATH",
148: "AUTHSTATE",
149: #endif
150: #ifdef __APPLE__
151: "DYLD_*",
152: #endif
153: #ifdef HAVE_KERB5
154: "KRB5_CONFIG*",
155: "KRB5_KTNAME",
156: #endif /* HAVE_KERB5 */
157: #ifdef HAVE_SECURID
158: "VAR_ACE",
159: "USR_ACE",
160: "DLC_ACE",
161: #endif /* HAVE_SECURID */
162: "TERMINFO", /* terminfo, exclusive path to terminfo files */
163: "TERMINFO_DIRS", /* terminfo, path(s) to terminfo files */
164: "TERMPATH", /* termcap, path(s) to termcap files */
165: "TERMCAP", /* XXX - only if it starts with '/' */
166: "ENV", /* ksh, file to source before script runs */
167: "BASH_ENV", /* bash, file to source before script runs */
168: "PS4", /* bash, prefix for lines in xtrace mode */
169: "GLOBIGNORE", /* bash, globbing patterns to ignore */
170: "SHELLOPTS", /* bash, extra command line options */
171: "JAVA_TOOL_OPTIONS", /* java, extra command line options */
172: "PERLIO_DEBUG ", /* perl, debugging output file */
173: "PERLLIB", /* perl, search path for modules/includes */
174: "PERL5LIB", /* perl 5, search path for modules/includes */
175: "PERL5OPT", /* perl 5, extra command line options */
176: "PERL5DB", /* perl 5, command used to load debugger */
177: "FPATH", /* ksh, search path for functions */
178: "NULLCMD", /* zsh, command for null file redirection */
179: "READNULLCMD", /* zsh, command for null file redirection */
180: "ZDOTDIR", /* zsh, search path for dot files */
181: "TMPPREFIX", /* zsh, prefix for temporary files */
182: "PYTHONHOME", /* python, module search path */
183: "PYTHONPATH", /* python, search path */
184: "PYTHONINSPECT", /* python, allow inspection */
185: "PYTHONUSERBASE", /* python, per user site-packages directory */
186: "RUBYLIB", /* ruby, library load path */
187: "RUBYOPT", /* ruby, extra command line options */
188: NULL
189: };
190:
191: /*
192: * Default table of variables to check for '%' and '/' characters.
193: */
194: static const char *initial_checkenv_table[] = {
195: "COLORTERM",
196: "LANG",
197: "LANGUAGE",
198: "LC_*",
199: "LINGUAS",
200: "TERM",
201: NULL
202: };
203:
204: /*
205: * Default table of variables to preserve in the environment.
206: */
207: static const char *initial_keepenv_table[] = {
208: "COLORS",
209: "DISPLAY",
210: "HOSTNAME",
211: "KRB5CCNAME",
212: "LS_COLORS",
213: "PATH",
214: "PS1",
215: "PS2",
216: "TZ",
217: "XAUTHORITY",
218: "XAUTHORIZATION",
219: NULL
220: };
221:
222: /*
223: * Initialize env based on envp.
224: */
225: void
226: env_init(char * const envp[])
227: {
228: char * const *ep;
229: size_t len;
1.1.1.2 misho 230: debug_decl(env_init, SUDO_DEBUG_ENV)
1.1 misho 231:
1.1.1.2 misho 232: if (envp == NULL) {
233: /* Reset to initial state but keep a pointer to what we allocated. */
234: envp = env.envp;
235: memset(&env, 0, sizeof(env));
236: env.old_envp = envp;
237: } else {
238: /* Make private copy of envp. */
239: for (ep = envp; *ep != NULL; ep++)
240: continue;
241: len = (size_t)(ep - envp);
1.1 misho 242:
1.1.1.2 misho 243: env.env_len = len;
244: env.env_size = len + 1 + 128;
245: env.envp = emalloc2(env.env_size, sizeof(char *));
1.1 misho 246: #ifdef ENV_DEBUG
1.1.1.2 misho 247: memset(env.envp, 0, env.env_size * sizeof(char *));
1.1 misho 248: #endif
1.1.1.2 misho 249: memcpy(env.envp, envp, len * sizeof(char *));
1.1.1.3 ! misho 250: env.envp[len] = NULL;
1.1 misho 251:
1.1.1.2 misho 252: /* Free the old envp we allocated, if any. */
253: if (env.old_envp != NULL)
254: efree((void *)env.old_envp);
255: }
256:
257: debug_return;
1.1 misho 258: }
259:
260: /*
1.1.1.2 misho 261: * Getter for private copy of the environment.
1.1 misho 262: */
1.1.1.2 misho 263: char **
264: env_get(void)
1.1 misho 265: {
1.1.1.2 misho 266: return env.envp;
1.1 misho 267: }
268:
269: /*
270: * Similar to putenv(3) but operates on sudo's private copy of the
271: * environment (not environ) and it always overwrites. The dupcheck param
272: * determines whether we need to verify that the variable is not already set.
273: * Will only overwrite an existing variable if overwrite is set.
1.1.1.2 misho 274: * Does not include warnings or debugging to avoid recursive calls.
1.1 misho 275: */
1.1.1.2 misho 276: static int
277: sudo_putenv_nodebug(char *str, bool dupcheck, bool overwrite)
1.1 misho 278: {
279: char **ep;
280: size_t len;
1.1.1.2 misho 281: bool found = false;
1.1 misho 282:
283: /* Make sure there is room for the new entry plus a NULL. */
1.1.1.3 ! misho 284: if (env.env_size > 2 && env.env_len > env.env_size - 2) {
1.1.1.2 misho 285: char **nenvp;
1.1.1.3 ! misho 286: size_t nsize;
! 287:
! 288: if (env.env_size > SIZE_MAX - 128) {
! 289: errorx2(1, _("internal error, %s overflow"),
! 290: "sudo_putenv_nodebug()");
! 291: }
! 292: nsize = env.env_size + 128;
! 293: if (nsize > SIZE_MAX / sizeof(char *)) {
! 294: errorx2(1, _("internal error, %s overflow"),
! 295: "sudo_putenv_nodebug()");
! 296: }
! 297: nenvp = realloc(env.envp, nsize * sizeof(char *));
1.1.1.2 misho 298: if (nenvp == NULL) {
299: errno = ENOMEM;
300: return -1;
301: }
302: env.envp = nenvp;
303: env.env_size = nsize;
1.1 misho 304: #ifdef ENV_DEBUG
305: memset(env.envp + env.env_len, 0,
306: (env.env_size - env.env_len) * sizeof(char *));
307: #endif
308: }
309:
310: #ifdef ENV_DEBUG
1.1.1.2 misho 311: if (env.envp[env.env_len] != NULL) {
312: errno = EINVAL;
313: return -1;
314: }
1.1 misho 315: #endif
316:
317: if (dupcheck) {
318: len = (strchr(str, '=') - str) + 1;
1.1.1.3 ! misho 319: for (ep = env.envp; *ep != NULL; ep++) {
1.1 misho 320: if (strncmp(str, *ep, len) == 0) {
321: if (overwrite)
322: *ep = str;
1.1.1.2 misho 323: found = true;
1.1.1.3 ! misho 324: break;
1.1 misho 325: }
326: }
1.1.1.3 ! misho 327: /* Prune out extra instances of the variable we just overwrote. */
1.1 misho 328: if (found && overwrite) {
1.1.1.3 ! misho 329: while (*++ep != NULL) {
1.1 misho 330: if (strncmp(str, *ep, len) == 0) {
331: char **cur = ep;
332: while ((*cur = *(cur + 1)) != NULL)
333: cur++;
1.1.1.3 ! misho 334: ep--;
1.1 misho 335: }
336: }
337: env.env_len = ep - env.envp;
338: }
339: }
340:
341: if (!found) {
342: ep = env.envp + env.env_len;
343: env.env_len++;
344: *ep++ = str;
345: *ep = NULL;
346: }
1.1.1.2 misho 347: return 0;
1.1 misho 348: }
349:
350: /*
1.1.1.2 misho 351: * Similar to putenv(3) but operates on sudo's private copy of the
352: * environment (not environ) and it always overwrites. The dupcheck param
353: * determines whether we need to verify that the variable is not already set.
354: * Will only overwrite an existing variable if overwrite is set.
1.1 misho 355: */
356: static int
1.1.1.2 misho 357: sudo_putenv(char *str, bool dupcheck, bool overwrite)
358: {
359: int rval;
360: debug_decl(sudo_putenv, SUDO_DEBUG_ENV)
361:
1.1.1.3 ! misho 362: sudo_debug_printf(SUDO_DEBUG_INFO, "sudo_putenv: %s", str);
! 363:
1.1.1.2 misho 364: rval = sudo_putenv_nodebug(str, dupcheck, overwrite);
365: if (rval == -1) {
366: #ifdef ENV_DEBUG
367: if (env.envp[env.env_len] != NULL)
368: errorx(1, _("sudo_putenv: corrupted envp, length mismatch"));
369: #endif
370: errorx(1, _("unable to allocate memory"));
371: }
372: debug_return_int(rval);
373: }
374:
375: /*
376: * Similar to setenv(3) but operates on a private copy of the environment.
377: * The dupcheck param determines whether we need to verify that the variable
378: * is not already set.
379: */
380: static int
381: sudo_setenv2(const char *var, const char *val, bool dupcheck, bool overwrite)
382: {
383: char *estring;
384: size_t esize;
1.1.1.3 ! misho 385: int rval;
1.1.1.2 misho 386: debug_decl(sudo_setenv2, SUDO_DEBUG_ENV)
387:
388: esize = strlen(var) + 1 + strlen(val) + 1;
389: estring = emalloc(esize);
390:
391: /* Build environment string and insert it. */
392: if (strlcpy(estring, var, esize) >= esize ||
393: strlcat(estring, "=", esize) >= esize ||
394: strlcat(estring, val, esize) >= esize) {
395:
1.1.1.3 ! misho 396: errorx(1, _("internal error, %s overflow"), "sudo_setenv2()");
1.1.1.2 misho 397: }
1.1.1.3 ! misho 398: rval = sudo_putenv(estring, dupcheck, overwrite);
! 399: if (rval == -1)
! 400: efree(estring);
! 401: debug_return_int(rval);
1.1.1.2 misho 402: }
403:
404: /*
405: * Similar to setenv(3) but operates on a private copy of the environment.
406: * Does not include warnings or debugging to avoid recursive calls.
407: */
408: static int
409: sudo_setenv_nodebug(const char *var, const char *val, int overwrite)
410: {
411: char *estring;
412: size_t esize;
1.1.1.3 ! misho 413: int rval = -1;
1.1.1.2 misho 414:
415: esize = strlen(var) + 1 + strlen(val) + 1;
416: if ((estring = malloc(esize)) == NULL) {
417: errno = ENOMEM;
1.1.1.3 ! misho 418: goto done;
1.1.1.2 misho 419: }
420:
421: /* Build environment string and insert it. */
422: if (strlcpy(estring, var, esize) >= esize ||
423: strlcat(estring, "=", esize) >= esize ||
424: strlcat(estring, val, esize) >= esize) {
425:
426: errno = EINVAL;
1.1.1.3 ! misho 427: goto done;
1.1.1.2 misho 428: }
1.1.1.3 ! misho 429: rval = sudo_putenv_nodebug(estring, true, overwrite);
! 430: done:
! 431: if (rval == -1)
! 432: efree(estring);
! 433: return rval;
1.1.1.2 misho 434: }
435:
436: /*
437: * Similar to setenv(3) but operates on a private copy of the environment.
438: */
439: int
440: sudo_setenv(const char *var, const char *val, int overwrite)
441: {
442: int rval;
443: debug_decl(sudo_setenv, SUDO_DEBUG_ENV)
444:
445: rval = sudo_setenv_nodebug(var, val, overwrite);
446: if (rval == -1) {
447: if (errno == EINVAL)
1.1.1.3 ! misho 448: errorx(1, _("internal error, %s overflow"), "sudo_setenv()");
1.1.1.2 misho 449: errorx(1, _("unable to allocate memory"));
450: }
451: debug_return_int(rval);
452: }
453:
454: /*
455: * Similar to unsetenv(3) but operates on a private copy of the environment.
456: * Does not include warnings or debugging to avoid recursive calls.
457: */
458: static int
459: sudo_unsetenv_nodebug(const char *var)
460: {
461: char **ep = env.envp;
462: size_t len;
463:
464: if (ep == NULL || var == NULL || *var == '\0' || strchr(var, '=') != NULL) {
465: errno = EINVAL;
466: return -1;
467: }
468:
469: len = strlen(var);
470: while (*ep != NULL) {
471: if (strncmp(var, *ep, len) == 0 && (*ep)[len] == '=') {
472: /* Found it; shift remainder + NULL over by one. */
473: char **cur = ep;
474: while ((*cur = *(cur + 1)) != NULL)
475: cur++;
476: /* Keep going, could be multiple instances of the var. */
477: } else {
478: ep++;
479: }
480: }
481: return 0;
482: }
483:
484: /*
485: * Similar to unsetenv(3) but operates on a private copy of the environment.
486: */
487: int
488: sudo_unsetenv(const char *name)
489: {
490: int rval;
491: debug_decl(sudo_unsetenv, SUDO_DEBUG_ENV)
492:
1.1.1.3 ! misho 493: sudo_debug_printf(SUDO_DEBUG_INFO, "sudo_unsetenv: %s", name);
! 494:
1.1.1.2 misho 495: rval = sudo_unsetenv_nodebug(name);
496:
497: debug_return_int(rval);
498: }
499:
500: /*
501: * Similar to getenv(3) but operates on a private copy of the environment.
502: * Does not include warnings or debugging to avoid recursive calls.
503: */
504: static char *
505: sudo_getenv_nodebug(const char *name)
506: {
507: char **ep, *val = NULL;
508: size_t namelen = 0;
509:
510: if (env.env_len != 0) {
511: /* For BSD compatibility, treat '=' in name like end of string. */
512: while (name[namelen] != '\0' && name[namelen] != '=')
513: namelen++;
514: for (ep = env.envp; *ep != NULL; ep++) {
515: if (strncmp(*ep, name, namelen) == 0 && (*ep)[namelen] == '=') {
516: val = *ep + namelen + 1;
517: break;
518: }
519: }
520: }
521: return val;
522: }
523:
524: /*
525: * Similar to getenv(3) but operates on a private copy of the environment.
526: */
527: char *
528: sudo_getenv(const char *name)
529: {
530: char *val;
531: debug_decl(sudo_getenv, SUDO_DEBUG_ENV)
532:
1.1.1.3 ! misho 533: sudo_debug_printf(SUDO_DEBUG_INFO, "sudo_getenv: %s", name);
! 534:
1.1.1.2 misho 535: val = sudo_getenv_nodebug(name);
536:
537: debug_return_str(val);
538: }
539:
540: /*
541: * Merge another environment with our private copy.
542: */
543: void
544: env_merge(char * const envp[], bool overwrite)
545: {
546: char * const *ep;
547: debug_decl(env_merge, SUDO_DEBUG_ENV)
548:
549: for (ep = envp; *ep != NULL; ep++)
550: sudo_putenv(*ep, true, overwrite);
551:
552: debug_return;
553: }
554:
555: /*
556: * Check the env_delete blacklist.
557: * Returns true if the variable was found, else false.
558: */
559: static bool
1.1 misho 560: matches_env_delete(const char *var)
561: {
562: struct list_member *cur;
563: size_t len;
1.1.1.2 misho 564: bool iswild;
565: bool match = false;
566: debug_decl(matches_env_delete, SUDO_DEBUG_ENV)
1.1 misho 567:
568: /* Skip anything listed in env_delete. */
569: for (cur = def_env_delete; cur; cur = cur->next) {
570: len = strlen(cur->value);
571: /* Deal with '*' wildcard */
572: if (cur->value[len - 1] == '*') {
573: len--;
1.1.1.2 misho 574: iswild = true;
1.1 misho 575: } else
1.1.1.2 misho 576: iswild = false;
1.1 misho 577: if (strncmp(cur->value, var, len) == 0 &&
578: (iswild || var[len] == '=')) {
1.1.1.2 misho 579: match = true;
1.1 misho 580: break;
581: }
582: }
1.1.1.2 misho 583: debug_return_bool(match);
1.1 misho 584: }
585:
586: /*
587: * Apply the env_check list.
1.1.1.2 misho 588: * Returns true if the variable is allowed, false if denied
1.1 misho 589: * or -1 if no match.
590: */
591: static int
592: matches_env_check(const char *var)
593: {
594: struct list_member *cur;
595: size_t len;
1.1.1.2 misho 596: bool iswild;
597: int keepit = -1;
598: debug_decl(matches_env_check, SUDO_DEBUG_ENV)
1.1 misho 599:
600: for (cur = def_env_check; cur; cur = cur->next) {
601: len = strlen(cur->value);
602: /* Deal with '*' wildcard */
603: if (cur->value[len - 1] == '*') {
604: len--;
1.1.1.2 misho 605: iswild = true;
1.1 misho 606: } else
1.1.1.2 misho 607: iswild = false;
1.1 misho 608: if (strncmp(cur->value, var, len) == 0 &&
609: (iswild || var[len] == '=')) {
610: keepit = !strpbrk(var, "/%");
611: break;
612: }
613: }
1.1.1.2 misho 614: debug_return_bool(keepit);
1.1 misho 615: }
616:
617: /*
618: * Check the env_keep list.
1.1.1.2 misho 619: * Returns true if the variable is allowed else false.
1.1 misho 620: */
1.1.1.2 misho 621: static bool
1.1 misho 622: matches_env_keep(const char *var)
623: {
624: struct list_member *cur;
625: size_t len;
1.1.1.2 misho 626: bool iswild, keepit = false;
627: debug_decl(matches_env_keep, SUDO_DEBUG_ENV)
1.1 misho 628:
629: /* Preserve SHELL variable for "sudo -s". */
1.1.1.2 misho 630: if (ISSET(sudo_mode, MODE_SHELL) && strncmp(var, "SHELL=", 6) == 0) {
631: keepit = true;
632: goto done;
633: }
1.1 misho 634:
635: for (cur = def_env_keep; cur; cur = cur->next) {
636: len = strlen(cur->value);
637: /* Deal with '*' wildcard */
638: if (cur->value[len - 1] == '*') {
639: len--;
1.1.1.2 misho 640: iswild = true;
1.1 misho 641: } else
1.1.1.2 misho 642: iswild = false;
1.1 misho 643: if (strncmp(cur->value, var, len) == 0 &&
644: (iswild || var[len] == '=')) {
1.1.1.2 misho 645: keepit = true;
1.1 misho 646: break;
647: }
648: }
1.1.1.2 misho 649: done:
650: debug_return_bool(keepit);
651: }
652:
653: /*
654: * Look up var in the env_delete and env_check.
655: * Returns true if we should delete the variable, else false.
656: */
657: static bool
658: env_should_delete(const char *var)
659: {
660: int delete_it;
661: debug_decl(env_should_delete, SUDO_DEBUG_ENV);
662:
663: delete_it = matches_env_delete(var);
664: if (!delete_it)
665: delete_it = matches_env_check(var) == false;
1.1.1.3 ! misho 666:
! 667: sudo_debug_printf(SUDO_DEBUG_INFO, "delete %s: %s",
! 668: var, delete_it ? "YES" : "NO");
1.1.1.2 misho 669: debug_return_bool(delete_it);
670: }
671:
672: /*
673: * Lookup var in the env_check and env_keep lists.
674: * Returns true if the variable is allowed else false.
675: */
676: static bool
677: env_should_keep(const char *var)
678: {
679: int keepit;
680: debug_decl(env_should_keep, SUDO_DEBUG_ENV)
681:
682: keepit = matches_env_check(var);
683: if (keepit == -1)
684: keepit = matches_env_keep(var);
685:
1.1.1.3 ! misho 686: sudo_debug_printf(SUDO_DEBUG_INFO, "keep %s: %s",
! 687: var, keepit ? "YES" : "NO");
1.1.1.2 misho 688: debug_return_bool(keepit == true);
689: }
690:
691: static void
692: env_update_didvar(const char *ep, unsigned int *didvar)
693: {
694: switch (*ep) {
695: case 'H':
696: if (strncmp(ep, "HOME=", 5) == 0)
697: SET(*didvar, DID_HOME);
698: break;
699: case 'L':
700: if (strncmp(ep, "LOGNAME=", 8) == 0)
701: SET(*didvar, DID_LOGNAME);
702: break;
703: case 'M':
704: if (strncmp(ep, "MAIL=", 5) == 0)
705: SET(*didvar, DID_MAIL);
706: break;
707: case 'P':
708: if (strncmp(ep, "PATH=", 5) == 0)
709: SET(*didvar, DID_PATH);
710: break;
711: case 'S':
712: if (strncmp(ep, "SHELL=", 6) == 0)
713: SET(*didvar, DID_SHELL);
714: break;
715: case 'T':
716: if (strncmp(ep, "TERM=", 5) == 0)
717: SET(*didvar, DID_TERM);
718: break;
719: case 'U':
720: if (strncmp(ep, "USER=", 5) == 0)
721: SET(*didvar, DID_USER);
722: if (strncmp(ep, "USERNAME=", 5) == 0)
723: SET(*didvar, DID_USERNAME);
724: break;
725: }
1.1 misho 726: }
727:
728: /*
729: * Build a new environment and ether clear potentially dangerous
730: * variables from the old one or start with a clean slate.
731: * Also adds sudo-specific variables (SUDO_*).
732: */
733: void
734: rebuild_env(void)
735: {
736: char **old_envp, **ep, *cp, *ps1;
1.1.1.3 ! misho 737: char idbuf[MAX_UID_T_LEN + 1];
1.1 misho 738: unsigned int didvar;
1.1.1.2 misho 739: bool reset_home = false;
1.1 misho 740:
741: /*
742: * Either clean out the environment or reset to a safe default.
743: */
744: ps1 = NULL;
745: didvar = 0;
746: env.env_len = 0;
747: env.env_size = 128;
748: old_envp = env.envp;
749: env.envp = emalloc2(env.env_size, sizeof(char *));
750: #ifdef ENV_DEBUG
751: memset(env.envp, 0, env.env_size * sizeof(char *));
1.1.1.2 misho 752: #else
753: env.envp[0] = NULL;
1.1 misho 754: #endif
755:
756: /* Reset HOME based on target user if configured to. */
757: if (ISSET(sudo_mode, MODE_RUN)) {
758: if (def_always_set_home ||
759: ISSET(sudo_mode, MODE_RESET_HOME | MODE_LOGIN_SHELL) ||
760: (ISSET(sudo_mode, MODE_SHELL) && def_set_home))
1.1.1.2 misho 761: reset_home = true;
1.1 misho 762: }
763:
764: if (def_env_reset || ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
1.1.1.2 misho 765: /*
766: * If starting with a fresh environment, initialize it based on
767: * /etc/environment or login.conf. For "sudo -i" we want those
768: * variables to override the invoking user's environment, so we
769: * defer reading them until later.
770: */
771: if (!ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
772: #ifdef HAVE_LOGIN_CAP_H
773: /* Insert login class environment variables. */
774: if (login_class) {
775: login_cap_t *lc = login_getclass(login_class);
776: if (lc != NULL) {
777: setusercontext(lc, runas_pw, runas_pw->pw_uid,
778: LOGIN_SETPATH|LOGIN_SETENV);
779: login_close(lc);
780: }
781: }
782: #endif /* HAVE_LOGIN_CAP_H */
783: #if defined(_AIX) || (defined(__linux__) && !defined(HAVE_PAM))
784: /* Insert system-wide environment variables. */
785: read_env_file(_PATH_ENVIRONMENT, true);
786: #endif
787: for (ep = env.envp; *ep; ep++)
788: env_update_didvar(*ep, &didvar);
789: }
790:
1.1 misho 791: /* Pull in vars we want to keep from the old environment. */
792: for (ep = old_envp; *ep; ep++) {
1.1.1.2 misho 793: bool keepit;
1.1 misho 794:
795: /* Skip variables with values beginning with () (bash functions) */
796: if ((cp = strchr(*ep, '=')) != NULL) {
797: if (strncmp(cp, "=() ", 3) == 0)
798: continue;
799: }
800:
801: /*
1.1.1.2 misho 802: * Look up the variable in the env_check and env_keep lists.
1.1 misho 803: */
1.1.1.2 misho 804: keepit = env_should_keep(*ep);
1.1 misho 805:
1.1.1.2 misho 806: /*
807: * Do SUDO_PS1 -> PS1 conversion.
808: * This must happen *after* env_should_keep() is called.
809: */
1.1 misho 810: if (strncmp(*ep, "SUDO_PS1=", 8) == 0)
811: ps1 = *ep + 5;
812:
813: if (keepit) {
814: /* Preserve variable. */
1.1.1.2 misho 815: sudo_putenv(*ep, false, false);
816: env_update_didvar(*ep, &didvar);
1.1 misho 817: }
818: }
819: didvar |= didvar << 8; /* convert DID_* to KEPT_* */
820:
821: /*
822: * Add in defaults. In -i mode these come from the runas user,
823: * otherwise they may be from the user's environment (depends
824: * on sudoers options).
825: */
826: if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
1.1.1.2 misho 827: sudo_setenv2("SHELL", runas_pw->pw_shell,
828: ISSET(didvar, DID_SHELL), true);
829: sudo_setenv2("LOGNAME", runas_pw->pw_name,
830: ISSET(didvar, DID_LOGNAME), true);
831: sudo_setenv2("USER", runas_pw->pw_name,
832: ISSET(didvar, DID_USER), true);
833: sudo_setenv2("USERNAME", runas_pw->pw_name,
834: ISSET(didvar, DID_USERNAME), true);
1.1 misho 835: } else {
836: if (!ISSET(didvar, DID_SHELL))
1.1.1.2 misho 837: sudo_setenv2("SHELL", sudo_user.pw->pw_shell, false, true);
1.1.1.3 ! misho 838: /* We will set LOGNAME later in the !def_set_logname case. */
! 839: if (!def_set_logname) {
! 840: if (!ISSET(didvar, DID_LOGNAME))
! 841: sudo_setenv2("LOGNAME", user_name, false, true);
! 842: if (!ISSET(didvar, DID_USER))
! 843: sudo_setenv2("USER", user_name, false, true);
! 844: if (!ISSET(didvar, DID_USERNAME))
! 845: sudo_setenv2("USERNAME", user_name, false, true);
! 846: }
1.1 misho 847: }
848:
849: /* If we didn't keep HOME, reset it based on target user. */
850: if (!ISSET(didvar, KEPT_HOME))
1.1.1.2 misho 851: reset_home = true;
1.1 misho 852:
853: /*
854: * Set MAIL to target user in -i mode or if MAIL is not preserved
855: * from user's environment.
856: */
857: if (ISSET(sudo_mode, MODE_LOGIN_SHELL) || !ISSET(didvar, KEPT_MAIL)) {
858: cp = _PATH_MAILDIR;
859: if (cp[sizeof(_PATH_MAILDIR) - 2] == '/')
860: easprintf(&cp, "MAIL=%s%s", _PATH_MAILDIR, runas_pw->pw_name);
861: else
862: easprintf(&cp, "MAIL=%s/%s", _PATH_MAILDIR, runas_pw->pw_name);
1.1.1.2 misho 863: sudo_putenv(cp, ISSET(didvar, DID_MAIL), true);
1.1 misho 864: }
865: } else {
866: /*
867: * Copy environ entries as long as they don't match env_delete or
868: * env_check.
869: */
870: for (ep = old_envp; *ep; ep++) {
871: /* Skip variables with values beginning with () (bash functions) */
872: if ((cp = strchr(*ep, '=')) != NULL) {
873: if (strncmp(cp, "=() ", 3) == 0)
874: continue;
875: }
876:
1.1.1.2 misho 877: /* Add variable unless it matches a black list. */
878: if (!env_should_delete(*ep)) {
1.1 misho 879: if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
880: ps1 = *ep + 5;
881: else if (strncmp(*ep, "PATH=", 5) == 0)
882: SET(didvar, DID_PATH);
883: else if (strncmp(*ep, "TERM=", 5) == 0)
884: SET(didvar, DID_TERM);
1.1.1.2 misho 885: sudo_putenv(*ep, false, false);
1.1 misho 886: }
887: }
888: }
889: /* Replace the PATH envariable with a secure one? */
890: if (def_secure_path && !user_is_exempt()) {
1.1.1.2 misho 891: sudo_setenv2("PATH", def_secure_path, true, true);
1.1 misho 892: SET(didvar, DID_PATH);
893: }
894:
895: /*
896: * Set $USER, $LOGNAME and $USERNAME to target if "set_logname" is not
897: * disabled. We skip this if we are running a login shell (because
1.1.1.3 ! misho 898: * they have already been set) or sudoedit (because we want the editor
! 899: * to find the invoking user's startup files).
1.1 misho 900: */
901: if (def_set_logname && !ISSET(sudo_mode, MODE_LOGIN_SHELL|MODE_EDIT)) {
902: if (!ISSET(didvar, KEPT_LOGNAME))
1.1.1.2 misho 903: sudo_setenv2("LOGNAME", runas_pw->pw_name, true, true);
1.1 misho 904: if (!ISSET(didvar, KEPT_USER))
1.1.1.2 misho 905: sudo_setenv2("USER", runas_pw->pw_name, true, true);
1.1 misho 906: if (!ISSET(didvar, KEPT_USERNAME))
1.1.1.2 misho 907: sudo_setenv2("USERNAME", runas_pw->pw_name, true, true);
1.1 misho 908: }
909:
910: /* Set $HOME to target user if not preserving user's value. */
911: if (reset_home)
1.1.1.2 misho 912: sudo_setenv2("HOME", runas_pw->pw_dir, true, true);
1.1 misho 913:
914: /* Provide default values for $TERM and $PATH if they are not set. */
915: if (!ISSET(didvar, DID_TERM))
1.1.1.2 misho 916: sudo_putenv("TERM=unknown", false, false);
1.1 misho 917: if (!ISSET(didvar, DID_PATH))
1.1.1.2 misho 918: sudo_setenv2("PATH", _PATH_STDPATH, false, true);
1.1 misho 919:
920: /* Set PS1 if SUDO_PS1 is set. */
921: if (ps1 != NULL)
1.1.1.2 misho 922: sudo_putenv(ps1, true, true);
1.1 misho 923:
924: /* Add the SUDO_COMMAND envariable (cmnd + args). */
925: if (user_args) {
926: easprintf(&cp, "%s %s", user_cmnd, user_args);
1.1.1.2 misho 927: sudo_setenv2("SUDO_COMMAND", cp, true, true);
1.1 misho 928: efree(cp);
929: } else {
1.1.1.2 misho 930: sudo_setenv2("SUDO_COMMAND", user_cmnd, true, true);
1.1 misho 931: }
932:
933: /* Add the SUDO_USER, SUDO_UID, SUDO_GID environment variables. */
1.1.1.2 misho 934: sudo_setenv2("SUDO_USER", user_name, true, true);
1.1 misho 935: snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) user_uid);
1.1.1.2 misho 936: sudo_setenv2("SUDO_UID", idbuf, true, true);
1.1 misho 937: snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) user_gid);
1.1.1.2 misho 938: sudo_setenv2("SUDO_GID", idbuf, true, true);
1.1 misho 939:
940: /* Free old environment. */
941: efree(old_envp);
942: }
943:
944: void
945: insert_env_vars(char * const envp[])
946: {
947: char * const *ep;
948:
949: if (envp == NULL)
950: return;
951:
952: /* Add user-specified environment variables. */
953: for (ep = envp; *ep != NULL; ep++)
1.1.1.2 misho 954: sudo_putenv(*ep, true, true);
1.1 misho 955: }
956:
957: /*
958: * Validate the list of environment variables passed in on the command
959: * line against env_delete, env_check, and env_keep.
1.1.1.2 misho 960: * Calls log_fatal() if any specified variables are not allowed.
1.1 misho 961: */
962: void
963: validate_env_vars(char * const env_vars[])
964: {
965: char * const *ep;
966: char *eq, *bad = NULL;
967: size_t len, blen = 0, bsize = 0;
1.1.1.2 misho 968: bool okvar;
1.1 misho 969:
970: if (env_vars == NULL)
971: return;
972:
973: /* Add user-specified environment variables. */
974: for (ep = env_vars; *ep != NULL; ep++) {
975: if (def_secure_path && !user_is_exempt() &&
976: strncmp(*ep, "PATH=", 5) == 0) {
1.1.1.2 misho 977: okvar = false;
1.1 misho 978: } else if (def_env_reset) {
1.1.1.2 misho 979: okvar = env_should_keep(*ep);
1.1 misho 980: } else {
1.1.1.2 misho 981: okvar = !env_should_delete(*ep);
1.1 misho 982: }
1.1.1.2 misho 983: if (okvar == false) {
1.1 misho 984: /* Not allowed, add to error string, allocating as needed. */
985: if ((eq = strchr(*ep, '=')) != NULL)
986: *eq = '\0';
987: len = strlen(*ep) + 2;
988: if (blen + len >= bsize) {
989: do {
990: bsize += 1024;
991: } while (blen + len >= bsize);
992: bad = erealloc(bad, bsize);
993: bad[blen] = '\0';
994: }
995: strlcat(bad, *ep, bsize);
996: strlcat(bad, ", ", bsize);
997: blen += len;
998: if (eq != NULL)
999: *eq = '=';
1000: }
1001: }
1002: if (bad != NULL) {
1003: bad[blen - 2] = '\0'; /* remove trailing ", " */
1.1.1.2 misho 1004: log_fatal(NO_MAIL,
1.1 misho 1005: _("sorry, you are not allowed to set the following environment variables: %s"), bad);
1006: /* NOTREACHED */
1007: efree(bad);
1008: }
1009: }
1010:
1011: /*
1012: * Read in /etc/environment ala AIX and Linux.
1013: * Lines may be in either of three formats:
1014: * NAME=VALUE
1015: * NAME="VALUE"
1016: * NAME='VALUE'
1017: * with an optional "export" prefix so the shell can source the file.
1018: * Invalid lines, blank lines, or lines consisting solely of a comment
1019: * character are skipped.
1020: */
1021: void
1022: read_env_file(const char *path, int overwrite)
1023: {
1024: FILE *fp;
1025: char *cp, *var, *val;
1026: size_t var_len, val_len;
1027:
1028: if ((fp = fopen(path, "r")) == NULL)
1029: return;
1030:
1031: while ((var = sudo_parseln(fp)) != NULL) {
1032: /* Skip blank or comment lines */
1033: if (*var == '\0')
1034: continue;
1035:
1036: /* Skip optional "export " */
1037: if (strncmp(var, "export", 6) == 0 && isspace((unsigned char) var[6])) {
1038: var += 7;
1039: while (isspace((unsigned char) *var)) {
1040: var++;
1041: }
1042: }
1043:
1044: /* Must be of the form name=["']value['"] */
1045: for (val = var; *val != '\0' && *val != '='; val++)
1046: ;
1047: if (var == val || *val != '=')
1048: continue;
1049: var_len = (size_t)(val - var);
1050: val_len = strlen(++val);
1051:
1052: /* Strip leading and trailing single/double quotes */
1053: if ((val[0] == '\'' || val[0] == '\"') && val[0] == val[val_len - 1]) {
1054: val[val_len - 1] = '\0';
1055: val++;
1056: val_len -= 2;
1057: }
1058:
1059: cp = emalloc(var_len + 1 + val_len + 1);
1060: memcpy(cp, var, var_len + 1); /* includes '=' */
1061: memcpy(cp + var_len + 1, val, val_len + 1); /* includes NUL */
1062:
1.1.1.2 misho 1063: sudo_putenv(cp, true, overwrite);
1.1 misho 1064: }
1065: fclose(fp);
1066: }
1067:
1068: void
1069: init_envtables(void)
1070: {
1071: struct list_member *cur;
1072: const char **p;
1073:
1074: /* Fill in the "env_delete" list. */
1075: for (p = initial_badenv_table; *p; p++) {
1.1.1.2 misho 1076: cur = ecalloc(1, sizeof(struct list_member));
1.1 misho 1077: cur->value = estrdup(*p);
1078: cur->next = def_env_delete;
1079: def_env_delete = cur;
1080: }
1081:
1082: /* Fill in the "env_check" list. */
1083: for (p = initial_checkenv_table; *p; p++) {
1.1.1.2 misho 1084: cur = ecalloc(1, sizeof(struct list_member));
1.1 misho 1085: cur->value = estrdup(*p);
1086: cur->next = def_env_check;
1087: def_env_check = cur;
1088: }
1089:
1090: /* Fill in the "env_keep" list. */
1091: for (p = initial_keepenv_table; *p; p++) {
1.1.1.2 misho 1092: cur = ecalloc(1, sizeof(struct list_member));
1.1 misho 1093: cur->value = estrdup(*p);
1094: cur->next = def_env_keep;
1095: def_env_keep = cur;
1096: }
1097: }
1.1.1.2 misho 1098:
1099: int
1100: sudoers_hook_getenv(const char *name, char **value, void *closure)
1101: {
1102: static bool in_progress = false; /* avoid recursion */
1103:
1104: if (in_progress || env.envp == NULL)
1105: return SUDO_HOOK_RET_NEXT;
1106:
1107: in_progress = true;
1108: *value = sudo_getenv_nodebug(name);
1109: in_progress = false;
1110: return SUDO_HOOK_RET_STOP;
1111: }
1112:
1113: int
1114: sudoers_hook_putenv(char *string, void *closure)
1115: {
1116: static bool in_progress = false; /* avoid recursion */
1117:
1118: if (in_progress || env.envp == NULL)
1119: return SUDO_HOOK_RET_NEXT;
1120:
1121: in_progress = true;
1122: sudo_putenv_nodebug(string, true, true);
1123: in_progress = false;
1124: return SUDO_HOOK_RET_STOP;
1125: }
1126:
1127: int
1128: sudoers_hook_setenv(const char *name, const char *value, int overwrite, void *closure)
1129: {
1130: static bool in_progress = false; /* avoid recursion */
1131:
1132: if (in_progress || env.envp == NULL)
1133: return SUDO_HOOK_RET_NEXT;
1134:
1135: in_progress = true;
1136: sudo_setenv_nodebug(name, value, overwrite);
1137: in_progress = false;
1138: return SUDO_HOOK_RET_STOP;
1139: }
1140:
1141: int
1142: sudoers_hook_unsetenv(const char *name, void *closure)
1143: {
1144: static bool in_progress = false; /* avoid recursion */
1145:
1146: if (in_progress || env.envp == NULL)
1147: return SUDO_HOOK_RET_NEXT;
1148:
1149: in_progress = true;
1150: sudo_unsetenv_nodebug(name);
1151: in_progress = false;
1152: return SUDO_HOOK_RET_STOP;
1153: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>