Imported Upstream version 1.8.6p8
[debian/sudo] / plugins / sudoers / env.c
index 9b043b282eb756d7a76e9912119a30e03a13da06..d678fa1fbc1601c9129e3b0104bdfc7f2806f252 100644 (file)
@@ -42,6 +42,9 @@
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif /* HAVE_UNISTD_H */
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
 #ifdef HAVE_LOGIN_CAP_H
 # include <login_cap.h>
 # ifndef LOGIN_SETENV
 #endif /* HAVE_LOGIN_CAP_H */
 #include <ctype.h>
 #include <errno.h>
+#include <limits.h>
 #include <pwd.h>
 
 #include "sudoers.h"
 
+/*
+ * If there is no SIZE_MAX or SIZE_T_MAX we have to assume that size_t
+ * could be signed (as it is on SunOS 4.x).  This just means that
+ * emalloc2() and erealloc3() cannot allocate huge amounts on such a
+ * platform but that is OK since sudo doesn't need to do so anyway.
+ */
+#ifndef SIZE_MAX
+# ifdef SIZE_T_MAX
+#  define SIZE_MAX     SIZE_T_MAX
+# else
+#  define SIZE_MAX     INT_MAX
+# endif /* SIZE_T_MAX */
+#endif /* SIZE_MAX */
+       
 /*
  * Flags used in rebuild_env()
  */
@@ -229,7 +247,7 @@ env_init(char * const envp[])
        memset(env.envp, 0, env.env_size * sizeof(char *));
 #endif
        memcpy(env.envp, envp, len * sizeof(char *));
-       env.envp[len] = '\0';
+       env.envp[len] = NULL;
 
        /* Free the old envp we allocated, if any. */
        if (env.old_envp != NULL)
@@ -263,11 +281,20 @@ sudo_putenv_nodebug(char *str, bool dupcheck, bool overwrite)
     bool found = false;
 
     /* Make sure there is room for the new entry plus a NULL. */
-    if (env.env_len + 2 > env.env_size) {
+    if (env.env_size > 2 && env.env_len > env.env_size - 2) {
        char **nenvp;
-       size_t nsize = env.env_size + 128;
-       nenvp = env.envp ? realloc(env.envp, nsize * sizeof(char *)) :
-           malloc(nsize * sizeof(char *));
+       size_t nsize;
+
+       if (env.env_size > SIZE_MAX - 128) {
+           errorx2(1, _("internal error, %s overflow"),
+               "sudo_putenv_nodebug()");
+       }
+       nsize = env.env_size + 128;
+       if (nsize > SIZE_MAX / sizeof(char *)) {
+           errorx2(1, _("internal error, %s overflow"),
+               "sudo_putenv_nodebug()");
+       }
+       nenvp = realloc(env.envp, nsize * sizeof(char *));
        if (nenvp == NULL) {
            errno = ENOMEM;
            return -1;
@@ -289,22 +316,22 @@ sudo_putenv_nodebug(char *str, bool dupcheck, bool overwrite)
 
     if (dupcheck) {
        len = (strchr(str, '=') - str) + 1;
-       for (ep = env.envp; !found && *ep != NULL; ep++) {
+       for (ep = env.envp; *ep != NULL; ep++) {
            if (strncmp(str, *ep, len) == 0) {
                if (overwrite)
                    *ep = str;
                found = true;
+               break;
            }
        }
-       /* Prune out duplicate variables. */
+       /* Prune out extra instances of the variable we just overwrote. */
        if (found && overwrite) {
-           while (*ep != NULL) {
+           while (*++ep != NULL) {
                if (strncmp(str, *ep, len) == 0) {
                    char **cur = ep;
                    while ((*cur = *(cur + 1)) != NULL)
                        cur++;
-               } else {
-                   ep++;
+                   ep--;
                }
            }
            env.env_len = ep - env.envp;
@@ -332,6 +359,8 @@ sudo_putenv(char *str, bool dupcheck, bool overwrite)
     int rval;
     debug_decl(sudo_putenv, SUDO_DEBUG_ENV)
 
+    sudo_debug_printf(SUDO_DEBUG_INFO, "sudo_putenv: %s", str);
+
     rval = sudo_putenv_nodebug(str, dupcheck, overwrite);
     if (rval == -1) {
 #ifdef ENV_DEBUG
@@ -353,6 +382,7 @@ sudo_setenv2(const char *var, const char *val, bool dupcheck, bool overwrite)
 {
     char *estring;
     size_t esize;
+    int rval;
     debug_decl(sudo_setenv2, SUDO_DEBUG_ENV)
 
     esize = strlen(var) + 1 + strlen(val) + 1;
@@ -363,9 +393,12 @@ sudo_setenv2(const char *var, const char *val, bool dupcheck, bool overwrite)
        strlcat(estring, "=", esize) >= esize ||
        strlcat(estring, val, esize) >= esize) {
 
-       errorx(1, _("internal error, sudo_setenv2() overflow"));
+       errorx(1, _("internal error, %s overflow"), "sudo_setenv2()");
     }
-    debug_return_int(sudo_putenv(estring, dupcheck, overwrite));
+    rval = sudo_putenv(estring, dupcheck, overwrite);
+    if (rval == -1)
+       efree(estring);
+    debug_return_int(rval);
 }
 
 /*
@@ -377,11 +410,12 @@ sudo_setenv_nodebug(const char *var, const char *val, int overwrite)
 {
     char *estring;
     size_t esize;
+    int rval = -1;
 
     esize = strlen(var) + 1 + strlen(val) + 1;
     if ((estring = malloc(esize)) == NULL) {
        errno = ENOMEM;
-       return -1;
+       goto done;
     }
 
     /* Build environment string and insert it. */
@@ -390,9 +424,13 @@ sudo_setenv_nodebug(const char *var, const char *val, int overwrite)
        strlcat(estring, val, esize) >= esize) {
 
        errno = EINVAL;
-       return -1;
+       goto done;
     }
-    return sudo_putenv_nodebug(estring, true, overwrite);
+    rval = sudo_putenv_nodebug(estring, true, overwrite);
+done:
+    if (rval == -1)
+       efree(estring);
+    return rval;
 }
 
 /*
@@ -407,7 +445,7 @@ sudo_setenv(const char *var, const char *val, int overwrite)
     rval = sudo_setenv_nodebug(var, val, overwrite);
     if (rval == -1) {
        if (errno == EINVAL)
-           errorx(1, _("internal error, sudo_setenv() overflow"));
+           errorx(1, _("internal error, %s overflow"), "sudo_setenv()");
        errorx(1, _("unable to allocate memory"));
     }
     debug_return_int(rval);
@@ -452,6 +490,8 @@ sudo_unsetenv(const char *name)
     int rval;
     debug_decl(sudo_unsetenv, SUDO_DEBUG_ENV)
 
+    sudo_debug_printf(SUDO_DEBUG_INFO, "sudo_unsetenv: %s", name);
+
     rval = sudo_unsetenv_nodebug(name);
 
     debug_return_int(rval);
@@ -490,6 +530,8 @@ sudo_getenv(const char *name)
     char *val;
     debug_decl(sudo_getenv, SUDO_DEBUG_ENV)
 
+    sudo_debug_printf(SUDO_DEBUG_INFO, "sudo_getenv: %s", name);
+
     val = sudo_getenv_nodebug(name);
 
     debug_return_str(val);
@@ -621,6 +663,9 @@ env_should_delete(const char *var)
     delete_it = matches_env_delete(var);
     if (!delete_it)
        delete_it = matches_env_check(var) == false;
+
+    sudo_debug_printf(SUDO_DEBUG_INFO, "delete %s: %s",
+       var, delete_it ? "YES" : "NO");
     debug_return_bool(delete_it);
 }
 
@@ -638,6 +683,8 @@ env_should_keep(const char *var)
     if (keepit == -1)
        keepit = matches_env_keep(var);
 
+    sudo_debug_printf(SUDO_DEBUG_INFO, "keep %s: %s",
+       var, keepit ? "YES" : "NO");
     debug_return_bool(keepit == true);
 }
 
@@ -687,7 +734,7 @@ void
 rebuild_env(void)
 {
     char **old_envp, **ep, *cp, *ps1;
-    char idbuf[MAX_UID_T_LEN];
+    char idbuf[MAX_UID_T_LEN + 1];
     unsigned int didvar;
     bool reset_home = false;
 
@@ -788,12 +835,15 @@ rebuild_env(void)
        } else {
            if (!ISSET(didvar, DID_SHELL))
                sudo_setenv2("SHELL", sudo_user.pw->pw_shell, false, true);
-           if (!ISSET(didvar, DID_LOGNAME))
-               sudo_setenv2("LOGNAME", user_name, false, true);
-           if (!ISSET(didvar, DID_USER))
-               sudo_setenv2("USER", user_name, false, true);
-           if (!ISSET(didvar, DID_USERNAME))
-               sudo_setenv2("USERNAME", user_name, false, true);
+           /* We will set LOGNAME later in the !def_set_logname case. */
+           if (!def_set_logname) {
+               if (!ISSET(didvar, DID_LOGNAME))
+                   sudo_setenv2("LOGNAME", user_name, false, true);
+               if (!ISSET(didvar, DID_USER))
+                   sudo_setenv2("USER", user_name, false, true);
+               if (!ISSET(didvar, DID_USERNAME))
+                   sudo_setenv2("USERNAME", user_name, false, true);
+           }
        }
 
        /* If we didn't keep HOME, reset it based on target user. */
@@ -845,8 +895,8 @@ rebuild_env(void)
     /*
      * Set $USER, $LOGNAME and $USERNAME to target if "set_logname" is not
      * disabled.  We skip this if we are running a login shell (because
-     * they have already been set them) or sudoedit (because we want the
-     * editor to find the user's startup files).
+     * they have already been set) or sudoedit (because we want the editor
+     * to find the invoking user's startup files).
      */
     if (def_set_logname && !ISSET(sudo_mode, MODE_LOGIN_SHELL|MODE_EDIT)) {
        if (!ISSET(didvar, KEPT_LOGNAME))