diff --git a/doc/sudoers.pod b/doc/sudoers.pod index 841bf742a..5c5fb1f83 100644 --- a/doc/sudoers.pod +++ b/doc/sudoers.pod @@ -89,11 +89,16 @@ environment are inherited by the command to be run. There are two distinct ways I can deal with environment variables. By default, the I option is enabled. This causes commands -to be executed with a minimal environment containing the C, -C, C, C, C, C, C, C -and C variables in addition to variables from the -invoking process permitted by the I and I -options. This is effectively a whitelist for environment variables. +to be executed with a new, minimal environment. On AIX (and Linux +systems without PAM), the environment is initialized with the +contents of the F file. On BSD systems, if the +I option is enabled, the environment is initialized +based on the I and I settings in F. +The new environment contains the C, C, C, C, +C, C, C, C and C variables +in addition to variables from the invoking process permitted by the +I and I options. This is effectively a whitelist +for environment variables. If, however, the I option is disabled, any variables not explicitly denied by the I and I options are @@ -119,12 +124,15 @@ As a special case, if B's B<-i> option (initial login) is specified, I will initialize the environment regardless of the value of I. The I, I and I variables remain unchanged; I, I, I, I, -and I are set based on the target user. On Linux and AIX -systems the contents of F are also included. All -other environment variables are removed. +and I are set based on the target user. On AIX (and Linux +systems without PAM), the contents of F are also +included. On BSD systems, if the I option is +enabled, the I and I variables in F +are also applied. All other environment variables are removed. -Lastly, if the I option is defined, any variables present -in that file will be set to their specified values. +Finally, if the I option is defined, any variables present +in that file will be set to their specified values as long as they +would not conflict with an existing environment variable. =head1 SUDOERS FILE FORMAT @@ -1776,7 +1784,7 @@ Directory containing time stamps for the I security policy =item F -Initial environment for B<-i> mode on Linux and AIX +Initial environment for B<-i> mode on AIX and Linux systems =back diff --git a/plugins/sudoers/env.c b/plugins/sudoers/env.c index 32d06049d..b2a195908 100644 --- a/plugins/sudoers/env.c +++ b/plugins/sudoers/env.c @@ -42,6 +42,12 @@ #ifdef HAVE_UNISTD_H # include #endif /* HAVE_UNISTD_H */ +#ifdef HAVE_LOGIN_CAP_H +# include +# ifndef LOGIN_SETENV +# define LOGIN_SETENV 0 +# endif +#endif /* HAVE_LOGIN_CAP_H */ #include #include #include @@ -638,6 +644,43 @@ env_should_keep(const char *var) debug_return_bool(keepit == true); } +static void +env_update_didvar(const char *ep, int *didvar) +{ + switch (*ep) { + case 'H': + if (strncmp(ep, "HOME=", 5) == 0) + SET(*didvar, DID_HOME); + break; + case 'L': + if (strncmp(ep, "LOGNAME=", 8) == 0) + SET(*didvar, DID_LOGNAME); + break; + case 'M': + if (strncmp(ep, "MAIL=", 5) == 0) + SET(*didvar, DID_MAIL); + break; + case 'P': + if (strncmp(ep, "PATH=", 5) == 0) + SET(*didvar, DID_PATH); + break; + case 'S': + if (strncmp(ep, "SHELL=", 6) == 0) + SET(*didvar, DID_SHELL); + break; + case 'T': + if (strncmp(ep, "TERM=", 5) == 0) + SET(*didvar, DID_TERM); + break; + case 'U': + if (strncmp(ep, "USER=", 5) == 0) + SET(*didvar, DID_USER); + if (strncmp(ep, "USERNAME=", 5) == 0) + SET(*didvar, DID_USERNAME); + break; + } +} + /* * Build a new environment and ether clear potentially dangerous * variables from the old one or start with a clean slate. @@ -662,6 +705,8 @@ rebuild_env(void) env.envp = emalloc2(env.env_size, sizeof(char *)); #ifdef ENV_DEBUG memset(env.envp, 0, env.env_size * sizeof(char *)); +#else + env.envp[0] = NULL; #endif /* Reset HOME based on target user if configured to. */ @@ -673,6 +718,32 @@ rebuild_env(void) } if (def_env_reset || ISSET(sudo_mode, MODE_LOGIN_SHELL)) { + /* + * If starting with a fresh environment, initialize it based on + * /etc/environment or login.conf. For "sudo -i" we want those + * variables to override the invoking user's environment, so we + * defer reading them until later. + */ + if (!ISSET(sudo_mode, MODE_LOGIN_SHELL)) { +#ifdef HAVE_LOGIN_CAP_H + /* Insert login class environment variables. */ + if (login_class) { + login_cap_t *lc = login_getclass(login_class); + if (lc != NULL) { + setusercontext(lc, runas_pw, runas_pw->pw_uid, + LOGIN_SETPATH|LOGIN_SETENV); + login_close(lc); + } + } +#endif /* HAVE_LOGIN_CAP_H */ +#if defined(_AIX) || (defined(__linux__) && !defined(HAVE_PAM)) + /* Insert system-wide environment variables. */ + read_env_file(_PATH_ENVIRONMENT, true); +#endif + for (ep = env.envp; *ep; ep++) + env_update_didvar(*ep, &didvar); + } + /* Pull in vars we want to keep from the old environment. */ for (ep = old_envp; *ep; ep++) { bool keepit; @@ -697,39 +768,8 @@ rebuild_env(void) if (keepit) { /* Preserve variable. */ - switch (**ep) { - case 'H': - if (strncmp(*ep, "HOME=", 5) == 0) - SET(didvar, DID_HOME); - break; - case 'L': - if (strncmp(*ep, "LOGNAME=", 8) == 0) - SET(didvar, DID_LOGNAME); - break; - case 'M': - if (strncmp(*ep, "MAIL=", 5) == 0) - SET(didvar, DID_MAIL); - break; - case 'P': - if (strncmp(*ep, "PATH=", 5) == 0) - SET(didvar, DID_PATH); - break; - case 'S': - if (strncmp(*ep, "SHELL=", 6) == 0) - SET(didvar, DID_SHELL); - break; - case 'T': - if (strncmp(*ep, "TERM=", 5) == 0) - SET(didvar, DID_TERM); - break; - case 'U': - if (strncmp(*ep, "USER=", 5) == 0) - SET(didvar, DID_USER); - if (strncmp(*ep, "USERNAME=", 5) == 0) - SET(didvar, DID_USERNAME); - break; - } sudo_putenv(*ep, false, false); + env_update_didvar(*ep, &didvar); } } didvar |= didvar << 8; /* convert DID_* to KEPT_* */