When initializing the environment for env_reset, start out with
authorTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 27 Mar 2012 22:57:11 +0000 (18:57 -0400)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 27 Mar 2012 22:57:11 +0000 (18:57 -0400)
the contents of /etc/environment on AIX and login.conf on BSD.

doc/sudoers.pod
plugins/sudoers/env.c

index 841bf742ae990051ef8ebaee64064296f55714aa..5c5fb1f831ea9c3c4549d2216e79b11f4fdb4347 100644 (file)
@@ -89,11 +89,16 @@ environment are inherited by the command to be run.  There are two
 distinct ways I<sudoers> can deal with environment variables.
 
 By default, the I<env_reset> option is enabled.  This causes commands
-to be executed with a minimal environment containing the C<TERM>,
-C<PATH>, C<HOME>, C<MAIL>, C<SHELL>, C<LOGNAME>, C<USER>, C<USERNAME>
-and C<SUDO_*> variables in addition to variables from the
-invoking process permitted by the I<env_check> and I<env_keep>
-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</etc/environment> file.  On BSD systems, if the
+I<use_loginclass> option is enabled, the environment is initialized
+based on the I<path> and I<setenv> settings in F</etc/login.conf>.
+The new environment contains the C<TERM>, C<PATH>, C<HOME>, C<MAIL>,
+C<SHELL>, C<LOGNAME>, C<USER>, C<USERNAME> and C<SUDO_*> variables
+in addition to variables from the invoking process permitted by the
+I<env_check> and I<env_keep> options.  This is effectively a whitelist
+for environment variables.
 
 If, however, the I<env_reset> option is disabled, any variables not
 explicitly denied by the I<env_check> and I<env_delete> options are
@@ -119,12 +124,15 @@ As a special case, if B<sudo>'s B<-i> option (initial login) is
 specified, I<sudoers> will initialize the environment regardless
 of the value of I<env_reset>.  The I<DISPLAY>, I<PATH> and I<TERM>
 variables remain unchanged; I<HOME>, I<MAIL>, I<SHELL>, I<USER>,
-and I<LOGNAME> are set based on the target user.  On Linux and AIX
-systems the contents of F</etc/environment> are also included.  All
-other environment variables are removed.
+and I<LOGNAME> are set based on the target user.  On AIX (and Linux
+systems without PAM), the contents of F</etc/environment> are also
+included.  On BSD systems, if the I<use_loginclass> option is
+enabled, the I<path> and I<setenv> variables in F</etc/login.conf>
+are also applied.  All other environment variables are removed.
 
-Lastly, if the I<env_file> option is defined, any variables present
-in that file will be set to their specified values.
+Finally, if the I<env_file> 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<sudoers> security policy
 
 =item F</etc/environment>
 
-Initial environment for B<-i> mode on Linux and AIX
+Initial environment for B<-i> mode on AIX and Linux systems
 
 =back
 
index 32d06049d7b0f2751df7e3fc2462e12e4146fded..b2a19590835ebbd86cedc8402d759ceb9a4ad29c 100644 (file)
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif /* HAVE_UNISTD_H */
+#ifdef HAVE_LOGIN_CAP_H
+# include <login_cap.h>
+# ifndef LOGIN_SETENV
+#  define LOGIN_SETENV 0
+# endif
+#endif /* HAVE_LOGIN_CAP_H */
 #include <ctype.h>
 #include <errno.h>
 #include <pwd.h>
@@ -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_* */