File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / plugins / sudoers / env.c
Revision 1.1.1.6 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 16:12:54 2014 UTC (10 years, 2 months ago) by misho
Branches: sudo, MAIN
CVS tags: v1_8_10p3_0, v1_8_10p3, HEAD
sudo v 1.8.10p3

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>