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