Diff for /embedaddon/sudo/plugins/sudoers/env.c between versions 1.1 and 1.1.1.4

version 1.1, 2012/02/21 16:23:02 version 1.1.1.4, 2013/07/22 10:46:12
Line 1 Line 1
 /*  /*
 * Copyright (c) 2000-2005, 2007-2011 * Copyright (c) 2000-2005, 2007-2013
  *      Todd C. Miller <Todd.Miller@courtesan.com>   *      Todd C. Miller <Todd.Miller@courtesan.com>
  *   *
  * Permission to use, copy, modify, and distribute this software for any   * Permission to use, copy, modify, and distribute this software for any
Line 22 Line 22
 #include <config.h>  #include <config.h>
   
 #include <sys/types.h>  #include <sys/types.h>
 #include <sys/param.h>  
 #include <sys/stat.h>  #include <sys/stat.h>
 #include <stdio.h>  #include <stdio.h>
 #ifdef STDC_HEADERS  #ifdef STDC_HEADERS
Line 42 Line 41
 #ifdef HAVE_UNISTD_H  #ifdef HAVE_UNISTD_H
 # include <unistd.h>  # include <unistd.h>
 #endif /* HAVE_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
   #  define LOGIN_SETENV  0
   # endif
   #endif /* HAVE_LOGIN_CAP_H */
 #include <ctype.h>  #include <ctype.h>
 #include <errno.h>  #include <errno.h>
   #include <limits.h>
 #include <pwd.h>  #include <pwd.h>
   
 #include "sudoers.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()   * Flags used in rebuild_env()
  */   */
 #undef DID_TERM  #undef DID_TERM
Line 90 Line 113
 #define KEPT_MAX        0xff00  #define KEPT_MAX        0xff00
   
 struct environment {  struct environment {
       char * const *old_envp;     /* pointer the environment we passed back */
     char **envp;                /* pointer to the new environment */      char **envp;                /* pointer to the new environment */
     size_t env_size;            /* size of new_environ in char **'s */      size_t env_size;            /* size of new_environ in char **'s */
     size_t env_len;             /* number of slots used, not counting NULL */      size_t env_len;             /* number of slots used, not counting NULL */
 };  };
   
 /*  /*
  * Prototypes  
  */  
 static void sudo_setenv(const char *, const char *, int);  
 static void sudo_putenv(char *, int, int);  
   
 /*  
  * Copy of the sudo-managed environment.   * Copy of the sudo-managed environment.
  */   */
 static struct environment env;  static struct environment env;
Line 131  static const char *initial_badenv_table[] = { Line 149  static const char *initial_badenv_table[] = {
 #ifdef __APPLE__  #ifdef __APPLE__
     "DYLD_*",      "DYLD_*",
 #endif  #endif
 #ifdef HAVE_KERB4  
     "KRB_CONF*",  
     "KRBCONFDIR",  
     "KRBTKFILE",  
 #endif /* HAVE_KERB4 */  
 #ifdef HAVE_KERB5  #ifdef HAVE_KERB5
     "KRB5_CONFIG*",      "KRB5_CONFIG*",
     "KRB5_KTNAME",      "KRB5_KTNAME",
Line 202  static const char *initial_keepenv_table[] = { Line 215  static const char *initial_keepenv_table[] = {
     "TZ",      "TZ",
     "XAUTHORITY",      "XAUTHORITY",
     "XAUTHORIZATION",      "XAUTHORIZATION",
 #ifdef _AIX  
     "ODMDIR",  
 #endif  
     NULL      NULL
 };  };
   
Line 216  env_init(char * const envp[]) Line 226  env_init(char * const envp[])
 {  {
     char * const *ep;      char * const *ep;
     size_t len;      size_t len;
       debug_decl(env_init, SUDO_DEBUG_ENV)
   
    for (ep = envp; *ep != NULL; ep++)    if (envp == NULL) {
        continue;        /* Reset to initial state but keep a pointer to what we allocated. */
    len = (size_t)(ep - envp);        envp = env.envp;
         memset(&env, 0, sizeof(env));
         env.old_envp = envp;
     } else {
         /* Make private copy of envp. */
         for (ep = envp; *ep != NULL; ep++)
             continue;
         len = (size_t)(ep - envp);
   
    env.env_len = len;        env.env_len = len;
    env.env_size = len + 1 + 128;        env.env_size = len + 1 + 128;
    env.envp = emalloc2(env.env_size, sizeof(char *));        env.envp = emalloc2(env.env_size, sizeof(char *));
 #ifdef ENV_DEBUG  #ifdef ENV_DEBUG
    memset(env.envp, 0, env.env_size * sizeof(char *));        memset(env.envp, 0, env.env_size * sizeof(char *));
 #endif  #endif
    memcpy(env.envp, envp, len * sizeof(char *));        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)
             efree((void *)env.old_envp);
     }
 
     debug_return;
 }  }
   
   /*
    * Getter for private copy of the environment.
    */
 char **  char **
 env_get(void)  env_get(void)
 {  {
Line 238  env_get(void) Line 266  env_get(void)
 }  }
   
 /*  /*
  * Similar to setenv(3) but operates on sudo's private copy of the environment  
  * (not environ) and it always overwrites.  The dupcheck param determines  
  * whether we need to verify that the variable is not already set.  
  */  
 static void  
 sudo_setenv(const char *var, const char *val, int dupcheck)  
 {  
     char *estring;  
     size_t esize;  
   
     esize = strlen(var) + 1 + strlen(val) + 1;  
     estring = emalloc(esize);  
   
     /* Build environment string and insert it. */  
     if (strlcpy(estring, var, esize) >= esize ||  
         strlcat(estring, "=", esize) >= esize ||  
         strlcat(estring, val, esize) >= esize) {  
   
         errorx(1, _("internal error, sudo_setenv() overflow"));  
     }  
     sudo_putenv(estring, dupcheck, TRUE);  
 }  
   
 /*  
  * Similar to putenv(3) but operates on sudo's private copy of the   * Similar to putenv(3) but operates on sudo's private copy of the
  * environment (not environ) and it always overwrites.  The dupcheck param   * environment (not environ) and it always overwrites.  The dupcheck param
  * determines whether we need to verify that the variable is not already set.   * determines whether we need to verify that the variable is not already set.
  * Will only overwrite an existing variable if overwrite is set.   * Will only overwrite an existing variable if overwrite is set.
    * Does not include warnings or debugging to avoid recursive calls.
  */   */
static voidstatic int
sudo_putenv(char *str, int dupcheck, int overwrite)sudo_putenv_nodebug(char *str, bool dupcheck, bool overwrite)
 {  {
     char **ep;      char **ep;
     size_t len;      size_t len;
    int found = FALSE;    bool found = false;
   
     /* Make sure there is room for the new entry plus a NULL. */      /* 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) {
        env.env_size += 128;        char **nenvp;
        env.envp = erealloc3(env.envp, env.env_size, sizeof(char *));        size_t nsize;
 
         if (env.env_size > SIZE_MAX - 128) {
             fatalx_nodebug(_("internal error, %s overflow"),
                 "sudo_putenv_nodebug()");
         }
         nsize = env.env_size + 128;
         if (nsize > SIZE_MAX / sizeof(char *)) {
             fatalx_nodebug(_("internal error, %s overflow"),
                 "sudo_putenv_nodebug()");
         }
         nenvp = realloc(env.envp, nsize * sizeof(char *));
         if (nenvp == NULL) {
             errno = ENOMEM;
             return -1;
         }
         env.envp = nenvp;
         env.env_size = nsize;
 #ifdef ENV_DEBUG  #ifdef ENV_DEBUG
         memset(env.envp + env.env_len, 0,          memset(env.envp + env.env_len, 0,
             (env.env_size - env.env_len) * sizeof(char *));              (env.env_size - env.env_len) * sizeof(char *));
Line 285  sudo_putenv(char *str, int dupcheck, int overwrite) Line 307  sudo_putenv(char *str, int dupcheck, int overwrite)
     }      }
   
 #ifdef ENV_DEBUG  #ifdef ENV_DEBUG
    if (env.envp[env.env_len] != NULL)    if (env.envp[env.env_len] != NULL) {
        errorx(1, _("sudo_putenv: corrupted envp, length mismatch"));        errno = EINVAL;
         return -1;
     }
 #endif  #endif
   
     if (dupcheck) {      if (dupcheck) {
         len = (strchr(str, '=') - str) + 1;          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 (strncmp(str, *ep, len) == 0) {
                 if (overwrite)                  if (overwrite)
                     *ep = str;                      *ep = str;
                found = TRUE;                found = true;
                 break;
             }              }
         }          }
        /* Prune out duplicate variables. */        /* Prune out extra instances of the variable we just overwrote. */
         if (found && overwrite) {          if (found && overwrite) {
            while (*ep != NULL) {            while (*++ep != NULL) {
                 if (strncmp(str, *ep, len) == 0) {                  if (strncmp(str, *ep, len) == 0) {
                     char **cur = ep;                      char **cur = ep;
                     while ((*cur = *(cur + 1)) != NULL)                      while ((*cur = *(cur + 1)) != NULL)
                         cur++;                          cur++;
                } else {                    ep--;
                    ep++; 
                 }                  }
             }              }
             env.env_len = ep - env.envp;              env.env_len = ep - env.envp;
Line 319  sudo_putenv(char *str, int dupcheck, int overwrite) Line 343  sudo_putenv(char *str, int dupcheck, int overwrite)
         *ep++ = str;          *ep++ = str;
         *ep = NULL;          *ep = NULL;
     }      }
       return 0;
 }  }
   
 /*  /*
 * Check the env_delete blacklist. * Similar to putenv(3) but operates on sudo's private copy of the
 * Returns TRUE if the variable was found, else false. * environment (not environ) and it always overwrites.  The dupcheck param
  * determines whether we need to verify that the variable is not already set.
  * Will only overwrite an existing variable if overwrite is set.
  */   */
 static int  static int
   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
           if (env.envp[env.env_len] != NULL)
               fatalx(_("sudo_putenv: corrupted envp, length mismatch"));
   #endif
           fatalx(NULL);
       }
       debug_return_int(rval);
   }
   
   /*
    * Similar to setenv(3) but operates on a private copy of the environment.
    * The dupcheck param determines whether we need to verify that the variable
    * is not already set.
    */
   static int
   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;
       estring = emalloc(esize);
   
       /* Build environment string and insert it. */
       if (strlcpy(estring, var, esize) >= esize ||
           strlcat(estring, "=", esize) >= esize ||
           strlcat(estring, val, esize) >= esize) {
   
           fatalx(_("internal error, %s overflow"), "sudo_setenv2()");
       }
       rval = sudo_putenv(estring, dupcheck, overwrite);
       if (rval == -1)
           efree(estring);
       debug_return_int(rval);
   }
   
   /*
    * Similar to setenv(3) but operates on a private copy of the environment.
    */
   int
   sudo_setenv(const char *var, const char *val, int overwrite)
   {
       return sudo_setenv2(var, val, true, (bool)overwrite);
   }
   
   /*
    * Similar to setenv(3) but operates on a private copy of the environment.
    * Does not include warnings or debugging to avoid recursive calls.
    */
   static int
   sudo_setenv_nodebug(const char *var, const char *val, int overwrite)
   {
       char *ep, *estring = NULL;
       const char *cp;
       size_t esize;
       int rval = -1;
   
       if (var == NULL || *var == '\0') {
           errno = EINVAL;
           goto done;
       }
   
       /*
        * POSIX says a var name with '=' is an error but BSD
        * just ignores the '=' and anything after it.
        */
       for (cp = var; *cp && *cp != '='; cp++)
           ;
       esize = (size_t)(cp - var) + 2;
       if (val) {
           esize += strlen(val);   /* glibc treats a NULL val as "" */
       }
   
       /* Allocate and fill in estring. */
       if ((estring = ep = malloc(esize)) == NULL) {
           errno = ENOMEM;
           goto done;
       }
       for (cp = var; *cp && *cp != '='; cp++)
           *ep++ = *cp;
       *ep++ = '=';
       if (val) {
           for (cp = val; *cp; cp++)
               *ep++ = *cp;
       }
       *ep = '\0';
   
       rval = sudo_putenv_nodebug(estring, true, overwrite);
   done:
       if (rval == -1)
           free(estring);
       return rval;
   }
   
   /*
    * Similar to unsetenv(3) but operates on a private copy of the environment.
    * Does not include warnings or debugging to avoid recursive calls.
    */
   static int
   sudo_unsetenv_nodebug(const char *var)
   {
       char **ep = env.envp;
       size_t len;
   
       if (ep == NULL || var == NULL || *var == '\0' || strchr(var, '=') != NULL) {
           errno = EINVAL;
           return -1;
       }
   
       len = strlen(var);
       while (*ep != NULL) {
           if (strncmp(var, *ep, len) == 0 && (*ep)[len] == '=') {
               /* Found it; shift remainder + NULL over by one. */
               char **cur = ep;
               while ((*cur = *(cur + 1)) != NULL)
                   cur++;
               /* Keep going, could be multiple instances of the var. */
           } else {
               ep++;
           }
       }
       return 0;
   }
   
   /*
    * Similar to unsetenv(3) but operates on a private copy of the environment.
    */
   int
   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);
   }
   
   /*
    * Similar to getenv(3) but operates on a private copy of the environment.
    * Does not include warnings or debugging to avoid recursive calls.
    */
   static char *
   sudo_getenv_nodebug(const char *name)
   {
       char **ep, *val = NULL;
       size_t namelen = 0;
   
       if (env.env_len != 0) {
           /* For BSD compatibility, treat '=' in name like end of string. */
           while (name[namelen] != '\0' && name[namelen] != '=')
               namelen++;
           for (ep = env.envp; *ep != NULL; ep++) {
               if (strncmp(*ep, name, namelen) == 0 && (*ep)[namelen] == '=') {
                   val = *ep + namelen + 1;
                   break;
               }
           }
       }
       return val;
   }
   
   /*
    * Similar to getenv(3) but operates on a private copy of the environment.
    */
   char *
   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);
   }
   
   /*
    * Merge another environment with our private copy.
    */
   void
   env_merge(char * const envp[], bool overwrite)
   {
       char * const *ep;
       debug_decl(env_merge, SUDO_DEBUG_ENV)
   
       for (ep = envp; *ep != NULL; ep++)
           sudo_putenv(*ep, true, overwrite);
   
       debug_return;
   }
   
   /*
    * Check the env_delete blacklist.
    * Returns true if the variable was found, else false.
    */
   static bool
 matches_env_delete(const char *var)  matches_env_delete(const char *var)
 {  {
     struct list_member *cur;      struct list_member *cur;
     size_t len;      size_t len;
    int iswild, match = FALSE;    bool iswild;
     bool match = false;
     debug_decl(matches_env_delete, SUDO_DEBUG_ENV)
   
     /* Skip anything listed in env_delete. */      /* Skip anything listed in env_delete. */
     for (cur = def_env_delete; cur; cur = cur->next) {      for (cur = def_env_delete; cur; cur = cur->next) {
Line 338  matches_env_delete(const char *var) Line 578  matches_env_delete(const char *var)
         /* Deal with '*' wildcard */          /* Deal with '*' wildcard */
         if (cur->value[len - 1] == '*') {          if (cur->value[len - 1] == '*') {
             len--;              len--;
            iswild = TRUE;            iswild = true;
         } else          } else
            iswild = FALSE;            iswild = false;
         if (strncmp(cur->value, var, len) == 0 &&          if (strncmp(cur->value, var, len) == 0 &&
             (iswild || var[len] == '=')) {              (iswild || var[len] == '=')) {
            match = TRUE;            match = true;
             break;              break;
         }          }
     }      }
    return match;    debug_return_bool(match);
 }  }
   
 /*  /*
  * Apply the env_check list.   * Apply the env_check list.
 * Returns TRUE if the variable is allowed, FALSE if denied * Returns true if the variable is allowed, false if denied
  * or -1 if no match.   * or -1 if no match.
  */   */
 static int  static int
Line 360  matches_env_check(const char *var) Line 600  matches_env_check(const char *var)
 {  {
     struct list_member *cur;      struct list_member *cur;
     size_t len;      size_t len;
    int iswild, keepit = -1;    bool iswild;
     int keepit = -1;
     debug_decl(matches_env_check, SUDO_DEBUG_ENV)
   
     for (cur = def_env_check; cur; cur = cur->next) {      for (cur = def_env_check; cur; cur = cur->next) {
         len = strlen(cur->value);          len = strlen(cur->value);
         /* Deal with '*' wildcard */          /* Deal with '*' wildcard */
         if (cur->value[len - 1] == '*') {          if (cur->value[len - 1] == '*') {
             len--;              len--;
            iswild = TRUE;            iswild = true;
         } else          } else
            iswild = FALSE;            iswild = false;
         if (strncmp(cur->value, var, len) == 0 &&          if (strncmp(cur->value, var, len) == 0 &&
             (iswild || var[len] == '=')) {              (iswild || var[len] == '=')) {
             keepit = !strpbrk(var, "/%");              keepit = !strpbrk(var, "/%");
             break;              break;
         }          }
     }      }
    return keepit;    debug_return_bool(keepit);
 }  }
   
 /*  /*
  * Check the env_keep list.   * Check the env_keep list.
 * Returns TRUE if the variable is allowed else FALSE. * Returns true if the variable is allowed else false.
  */   */
static intstatic bool
 matches_env_keep(const char *var)  matches_env_keep(const char *var)
 {  {
     struct list_member *cur;      struct list_member *cur;
     size_t len;      size_t len;
    int iswild, keepit = FALSE;    bool iswild, keepit = false;
     debug_decl(matches_env_keep, SUDO_DEBUG_ENV)
   
     /* Preserve SHELL variable for "sudo -s". */      /* Preserve SHELL variable for "sudo -s". */
    if (ISSET(sudo_mode, MODE_SHELL) && strncmp(var, "SHELL=", 6) == 0)    if (ISSET(sudo_mode, MODE_SHELL) && strncmp(var, "SHELL=", 6) == 0) {
        return TRUE;        keepit = true;
         goto done;
     }
   
     for (cur = def_env_keep; cur; cur = cur->next) {      for (cur = def_env_keep; cur; cur = cur->next) {
         len = strlen(cur->value);          len = strlen(cur->value);
         /* Deal with '*' wildcard */          /* Deal with '*' wildcard */
         if (cur->value[len - 1] == '*') {          if (cur->value[len - 1] == '*') {
             len--;              len--;
            iswild = TRUE;            iswild = true;
         } else          } else
            iswild = FALSE;            iswild = false;
         if (strncmp(cur->value, var, len) == 0 &&          if (strncmp(cur->value, var, len) == 0 &&
             (iswild || var[len] == '=')) {              (iswild || var[len] == '=')) {
            keepit = TRUE;            keepit = true;
             break;              break;
         }          }
     }      }
    return keepit;done:
     debug_return_bool(keepit);
 }  }
   
 /*  /*
    * Look up var in the env_delete and env_check.
    * Returns true if we should delete the variable, else false.
    */
   static bool
   env_should_delete(const char *var)
   {
       int delete_it;
       debug_decl(env_should_delete, SUDO_DEBUG_ENV);
   
       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);
   }
   
   /*
    * Lookup var in the env_check and env_keep lists.
    * Returns true if the variable is allowed else false.
    */
   static bool
   env_should_keep(const char *var)
   {
       int keepit;
       debug_decl(env_should_keep, SUDO_DEBUG_ENV)
   
       keepit = matches_env_check(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);
   }
   
   static void
   env_update_didvar(const char *ep, unsigned 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   * Build a new environment and ether clear potentially dangerous
  * variables from the old one or start with a clean slate.   * variables from the old one or start with a clean slate.
  * Also adds sudo-specific variables (SUDO_*).   * Also adds sudo-specific variables (SUDO_*).
Line 420  void Line 741  void
 rebuild_env(void)  rebuild_env(void)
 {  {
     char **old_envp, **ep, *cp, *ps1;      char **old_envp, **ep, *cp, *ps1;
    char idbuf[MAX_UID_T_LEN];    char idbuf[MAX_UID_T_LEN + 1];
     unsigned int didvar;      unsigned int didvar;
    int reset_home = FALSE;    bool reset_home = false;
   
     /*      /*
      * Either clean out the environment or reset to a safe default.       * Either clean out the environment or reset to a safe default.
Line 435  rebuild_env(void) Line 756  rebuild_env(void)
     env.envp = emalloc2(env.env_size, sizeof(char *));      env.envp = emalloc2(env.env_size, sizeof(char *));
 #ifdef ENV_DEBUG  #ifdef ENV_DEBUG
     memset(env.envp, 0, env.env_size * sizeof(char *));      memset(env.envp, 0, env.env_size * sizeof(char *));
   #else
       env.envp[0] = NULL;
 #endif  #endif
   
     /* Reset HOME based on target user if configured to. */      /* Reset HOME based on target user if configured to. */
Line 442  rebuild_env(void) Line 765  rebuild_env(void)
         if (def_always_set_home ||          if (def_always_set_home ||
             ISSET(sudo_mode, MODE_RESET_HOME | MODE_LOGIN_SHELL) ||               ISSET(sudo_mode, MODE_RESET_HOME | MODE_LOGIN_SHELL) || 
             (ISSET(sudo_mode, MODE_SHELL) && def_set_home))              (ISSET(sudo_mode, MODE_SHELL) && def_set_home))
            reset_home = TRUE;            reset_home = true;
     }      }
   
     if (def_env_reset || ISSET(sudo_mode, MODE_LOGIN_SHELL)) {      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. */          /* Pull in vars we want to keep from the old environment. */
         for (ep = old_envp; *ep; ep++) {          for (ep = old_envp; *ep; ep++) {
            int keepit;            bool keepit;
   
             /* Skip variables with values beginning with () (bash functions) */              /* Skip variables with values beginning with () (bash functions) */
             if ((cp = strchr(*ep, '=')) != NULL) {              if ((cp = strchr(*ep, '=')) != NULL) {
Line 457  rebuild_env(void) Line 806  rebuild_env(void)
             }              }
   
             /*              /*
             * First check certain variables for '%' and '/' characters.             * Look up the variable in the env_check and env_keep lists.
             * If no match there, check the keep list. 
             * If nothing matched, we remove it from the environment. 
              */               */
            keepit = matches_env_check(*ep);            keepit = env_should_keep(*ep);
            if (keepit == -1) 
                keepit = matches_env_keep(*ep); 
   
            /* For SUDO_PS1 -> PS1 conversion. */            /*
              * Do SUDO_PS1 -> PS1 conversion.
              * This must happen *after* env_should_keep() is called.
              */
             if (strncmp(*ep, "SUDO_PS1=", 8) == 0)              if (strncmp(*ep, "SUDO_PS1=", 8) == 0)
                 ps1 = *ep + 5;                  ps1 = *ep + 5;
   
             if (keepit) {              if (keepit) {
                 /* Preserve variable. */                  /* Preserve variable. */
                switch (**ep) {                sudo_putenv(*ep, false, false);
                    case 'H':                env_update_didvar(*ep, &didvar);
                        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); 
             }              }
         }          }
         didvar |= didvar << 8;          /* convert DID_* to KEPT_* */          didvar |= didvar << 8;          /* convert DID_* to KEPT_* */
Line 514  rebuild_env(void) Line 831  rebuild_env(void)
          * on sudoers options).           * on sudoers options).
          */           */
         if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {          if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
            sudo_setenv("SHELL", runas_pw->pw_shell, ISSET(didvar, DID_SHELL));            sudo_setenv2("SHELL", runas_pw->pw_shell,
            sudo_setenv("LOGNAME", runas_pw->pw_name,                ISSET(didvar, DID_SHELL), true);
                ISSET(didvar, DID_LOGNAME));            sudo_setenv2("LOGNAME", runas_pw->pw_name,
            sudo_setenv("USER", runas_pw->pw_name, ISSET(didvar, DID_USER));                ISSET(didvar, DID_LOGNAME), true);
            sudo_setenv("USERNAME", runas_pw->pw_name,            sudo_setenv2("USER", runas_pw->pw_name,
                ISSET(didvar, DID_USERNAME));                ISSET(didvar, DID_USER), true);
             sudo_setenv2("USERNAME", runas_pw->pw_name,
                 ISSET(didvar, DID_USERNAME), true);
         } else {          } else {
             if (!ISSET(didvar, DID_SHELL))              if (!ISSET(didvar, DID_SHELL))
                sudo_setenv("SHELL", sudo_user.pw->pw_shell, FALSE);                sudo_setenv2("SHELL", sudo_user.pw->pw_shell, false, true);
            if (!ISSET(didvar, DID_LOGNAME))            /* We will set LOGNAME later in the !def_set_logname case. */
                sudo_setenv("LOGNAME", user_name, FALSE);            if (!def_set_logname) {
            if (!ISSET(didvar, DID_USER))                if (!ISSET(didvar, DID_LOGNAME))
                sudo_setenv("USER", user_name, FALSE);                    sudo_setenv2("LOGNAME", user_name, false, true);
            if (!ISSET(didvar, DID_USERNAME))                if (!ISSET(didvar, DID_USER))
                sudo_setenv("USERNAME", user_name, FALSE);                    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. */          /* If we didn't keep HOME, reset it based on target user. */
         if (!ISSET(didvar, KEPT_HOME))          if (!ISSET(didvar, KEPT_HOME))
            reset_home = TRUE;            reset_home = true;
   
         /*          /*
          * Set MAIL to target user in -i mode or if MAIL is not preserved           * Set MAIL to target user in -i mode or if MAIL is not preserved
Line 545  rebuild_env(void) Line 867  rebuild_env(void)
                 easprintf(&cp, "MAIL=%s%s", _PATH_MAILDIR, runas_pw->pw_name);                  easprintf(&cp, "MAIL=%s%s", _PATH_MAILDIR, runas_pw->pw_name);
             else              else
                 easprintf(&cp, "MAIL=%s/%s", _PATH_MAILDIR, runas_pw->pw_name);                  easprintf(&cp, "MAIL=%s/%s", _PATH_MAILDIR, runas_pw->pw_name);
            sudo_putenv(cp, ISSET(didvar, DID_MAIL), TRUE);            sudo_putenv(cp, ISSET(didvar, DID_MAIL), true);
         }          }
     } else {      } else {
         /*          /*
Line 553  rebuild_env(void) Line 875  rebuild_env(void)
          * env_check.           * env_check.
          */           */
         for (ep = old_envp; *ep; ep++) {          for (ep = old_envp; *ep; ep++) {
             int okvar;  
   
             /* Skip variables with values beginning with () (bash functions) */              /* Skip variables with values beginning with () (bash functions) */
             if ((cp = strchr(*ep, '=')) != NULL) {              if ((cp = strchr(*ep, '=')) != NULL) {
                 if (strncmp(cp, "=() ", 3) == 0)                  if (strncmp(cp, "=() ", 3) == 0)
                     continue;                      continue;
             }              }
   
            /*            /* Add variable unless it matches a black list. */
             * First check variables against the blacklist in env_delete.            if (!env_should_delete(*ep)) {
             * If no match there check for '%' and '/' characters. 
             */ 
            okvar = matches_env_delete(*ep) != TRUE; 
            if (okvar) 
                okvar = matches_env_check(*ep) != FALSE; 
 
            if (okvar) { 
                 if (strncmp(*ep, "SUDO_PS1=", 9) == 0)                  if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
                     ps1 = *ep + 5;                      ps1 = *ep + 5;
                 else if (strncmp(*ep, "PATH=", 5) == 0)                  else if (strncmp(*ep, "PATH=", 5) == 0)
                     SET(didvar, DID_PATH);                      SET(didvar, DID_PATH);
                 else if (strncmp(*ep, "TERM=", 5) == 0)                  else if (strncmp(*ep, "TERM=", 5) == 0)
                     SET(didvar, DID_TERM);                      SET(didvar, DID_TERM);
                sudo_putenv(*ep, FALSE, FALSE);                sudo_putenv(*ep, false, false);
             }              }
         }          }
     }      }
     /* Replace the PATH envariable with a secure one? */      /* Replace the PATH envariable with a secure one? */
     if (def_secure_path && !user_is_exempt()) {      if (def_secure_path && !user_is_exempt()) {
        sudo_setenv("PATH", def_secure_path, TRUE);        sudo_setenv2("PATH", def_secure_path, true, true);
         SET(didvar, DID_PATH);          SET(didvar, DID_PATH);
     }      }
   
     /*      /*
      * Set $USER, $LOGNAME and $USERNAME to target if "set_logname" is not       * Set $USER, $LOGNAME and $USERNAME to target if "set_logname" is not
      * disabled.  We skip this if we are running a login shell (because       * disabled.  We skip this if we are running a login shell (because
     * they have already been set them) or sudoedit (because we want the     * they have already been set) or sudoedit (because we want the editor
     * editor to find the user's startup files).     * to find the invoking user's startup files).
      */       */
     if (def_set_logname && !ISSET(sudo_mode, MODE_LOGIN_SHELL|MODE_EDIT)) {      if (def_set_logname && !ISSET(sudo_mode, MODE_LOGIN_SHELL|MODE_EDIT)) {
         if (!ISSET(didvar, KEPT_LOGNAME))          if (!ISSET(didvar, KEPT_LOGNAME))
            sudo_setenv("LOGNAME", runas_pw->pw_name, TRUE);            sudo_setenv2("LOGNAME", runas_pw->pw_name, true, true);
         if (!ISSET(didvar, KEPT_USER))          if (!ISSET(didvar, KEPT_USER))
            sudo_setenv("USER", runas_pw->pw_name, TRUE);            sudo_setenv2("USER", runas_pw->pw_name, true, true);
         if (!ISSET(didvar, KEPT_USERNAME))          if (!ISSET(didvar, KEPT_USERNAME))
            sudo_setenv("USERNAME", runas_pw->pw_name, TRUE);            sudo_setenv2("USERNAME", runas_pw->pw_name, true, true);
     }      }
   
     /* Set $HOME to target user if not preserving user's value. */      /* Set $HOME to target user if not preserving user's value. */
     if (reset_home)      if (reset_home)
        sudo_setenv("HOME", runas_pw->pw_dir, TRUE);        sudo_setenv2("HOME", runas_pw->pw_dir, true, true);
   
     /* Provide default values for $TERM and $PATH if they are not set. */      /* Provide default values for $TERM and $PATH if they are not set. */
     if (!ISSET(didvar, DID_TERM))      if (!ISSET(didvar, DID_TERM))
        sudo_putenv("TERM=unknown", FALSE, FALSE);        sudo_putenv("TERM=unknown", false, false);
     if (!ISSET(didvar, DID_PATH))      if (!ISSET(didvar, DID_PATH))
        sudo_setenv("PATH", _PATH_STDPATH, FALSE);        sudo_setenv2("PATH", _PATH_STDPATH, false, true);
   
     /* Set PS1 if SUDO_PS1 is set. */      /* Set PS1 if SUDO_PS1 is set. */
     if (ps1 != NULL)      if (ps1 != NULL)
        sudo_putenv(ps1, TRUE, TRUE);        sudo_putenv(ps1, true, true);
   
     /* Add the SUDO_COMMAND envariable (cmnd + args). */      /* Add the SUDO_COMMAND envariable (cmnd + args). */
     if (user_args) {      if (user_args) {
         easprintf(&cp, "%s %s", user_cmnd, user_args);          easprintf(&cp, "%s %s", user_cmnd, user_args);
        sudo_setenv("SUDO_COMMAND", cp, TRUE);        sudo_setenv2("SUDO_COMMAND", cp, true, true);
         efree(cp);          efree(cp);
     } else {      } else {
        sudo_setenv("SUDO_COMMAND", user_cmnd, TRUE);        sudo_setenv2("SUDO_COMMAND", user_cmnd, true, true);
     }      }
   
     /* Add the SUDO_USER, SUDO_UID, SUDO_GID environment variables. */      /* Add the SUDO_USER, SUDO_UID, SUDO_GID environment variables. */
    sudo_setenv("SUDO_USER", user_name, TRUE);    sudo_setenv2("SUDO_USER", user_name, true, true);
     snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) user_uid);      snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) user_uid);
    sudo_setenv("SUDO_UID", idbuf, TRUE);    sudo_setenv2("SUDO_UID", idbuf, true, true);
     snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) user_gid);      snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) user_gid);
    sudo_setenv("SUDO_GID", idbuf, TRUE);    sudo_setenv2("SUDO_GID", idbuf, true, true);
   
     /* Free old environment. */      /* Free old environment. */
     efree(old_envp);      efree(old_envp);
Line 645  insert_env_vars(char * const envp[]) Line 958  insert_env_vars(char * const envp[])
   
     /* Add user-specified environment variables. */      /* Add user-specified environment variables. */
     for (ep = envp; *ep != NULL; ep++)      for (ep = envp; *ep != NULL; ep++)
        sudo_putenv(*ep, TRUE, TRUE);        sudo_putenv(*ep, true, true);
 }  }
   
 /*  /*
  * Validate the list of environment variables passed in on the command   * Validate the list of environment variables passed in on the command
  * line against env_delete, env_check, and env_keep.   * line against env_delete, env_check, and env_keep.
 * Calls log_error() if any specified variables are not allowed. * Calls log_fatal() if any specified variables are not allowed.
  */   */
 void  void
 validate_env_vars(char * const env_vars[])  validate_env_vars(char * const env_vars[])
Line 659  validate_env_vars(char * const env_vars[]) Line 972  validate_env_vars(char * const env_vars[])
     char * const *ep;      char * const *ep;
     char *eq, *bad = NULL;      char *eq, *bad = NULL;
     size_t len, blen = 0, bsize = 0;      size_t len, blen = 0, bsize = 0;
    int okvar;    bool okvar;
   
     if (env_vars == NULL)      if (env_vars == NULL)
         return;          return;
Line 668  validate_env_vars(char * const env_vars[]) Line 981  validate_env_vars(char * const env_vars[])
     for (ep = env_vars; *ep != NULL; ep++) {      for (ep = env_vars; *ep != NULL; ep++) {
         if (def_secure_path && !user_is_exempt() &&          if (def_secure_path && !user_is_exempt() &&
             strncmp(*ep, "PATH=", 5) == 0) {              strncmp(*ep, "PATH=", 5) == 0) {
            okvar = FALSE;            okvar = false;
         } else if (def_env_reset) {          } else if (def_env_reset) {
            okvar = matches_env_check(*ep);            okvar = env_should_keep(*ep);
            if (okvar == -1) 
                okvar = matches_env_keep(*ep); 
         } else {          } else {
            okvar = matches_env_delete(*ep) == FALSE;            okvar = !env_should_delete(*ep);
            if (okvar == FALSE) 
                okvar = matches_env_check(*ep) != FALSE; 
         }          }
        if (okvar == FALSE) {        if (okvar == false) {
             /* Not allowed, add to error string, allocating as needed. */              /* Not allowed, add to error string, allocating as needed. */
             if ((eq = strchr(*ep, '=')) != NULL)              if ((eq = strchr(*ep, '=')) != NULL)
                 *eq = '\0';                  *eq = '\0';
Line 699  validate_env_vars(char * const env_vars[]) Line 1008  validate_env_vars(char * const env_vars[])
     }      }
     if (bad != NULL) {      if (bad != NULL) {
         bad[blen - 2] = '\0';           /* remove trailing ", " */          bad[blen - 2] = '\0';           /* remove trailing ", " */
        log_error(NO_MAIL,        log_fatal(NO_MAIL,
            _("sorry, you are not allowed to set the following environment variables: %s"), bad);            N_("sorry, you are not allowed to set the following environment variables: %s"), bad);
         /* NOTREACHED */          /* NOTREACHED */
         efree(bad);          efree(bad);
     }      }
Line 720  void Line 1029  void
 read_env_file(const char *path, int overwrite)  read_env_file(const char *path, int overwrite)
 {  {
     FILE *fp;      FILE *fp;
    char *cp, *var, *val;    char *cp, *var, *val, *line = NULL;
    size_t var_len, val_len;    size_t var_len, val_len, linesize = 0;
   
     if ((fp = fopen(path, "r")) == NULL)      if ((fp = fopen(path, "r")) == NULL)
         return;          return;
   
    while ((var = sudo_parseln(fp)) != NULL) {    while (sudo_parseln(&line, &linesize, NULL, fp) != -1) {
         /* Skip blank or comment lines */          /* Skip blank or comment lines */
        if (*var == '\0')        if (*(var = line) == '\0')
             continue;              continue;
   
         /* Skip optional "export " */          /* Skip optional "export " */
Line 758  read_env_file(const char *path, int overwrite) Line 1067  read_env_file(const char *path, int overwrite)
         memcpy(cp, var, var_len + 1); /* includes '=' */          memcpy(cp, var, var_len + 1); /* includes '=' */
         memcpy(cp + var_len + 1, val, val_len + 1); /* includes NUL */          memcpy(cp + var_len + 1, val, val_len + 1); /* includes NUL */
   
        sudo_putenv(cp, TRUE, overwrite);        sudo_putenv(cp, true, overwrite);
     }      }
       free(line);
     fclose(fp);      fclose(fp);
 }  }
   
Line 771  init_envtables(void) Line 1081  init_envtables(void)
   
     /* Fill in the "env_delete" list. */      /* Fill in the "env_delete" list. */
     for (p = initial_badenv_table; *p; p++) {      for (p = initial_badenv_table; *p; p++) {
        cur = emalloc(sizeof(struct list_member));        cur = ecalloc(1, sizeof(struct list_member));
         cur->value = estrdup(*p);          cur->value = estrdup(*p);
         cur->next = def_env_delete;          cur->next = def_env_delete;
         def_env_delete = cur;          def_env_delete = cur;
Line 779  init_envtables(void) Line 1089  init_envtables(void)
   
     /* Fill in the "env_check" list. */      /* Fill in the "env_check" list. */
     for (p = initial_checkenv_table; *p; p++) {      for (p = initial_checkenv_table; *p; p++) {
        cur = emalloc(sizeof(struct list_member));        cur = ecalloc(1, sizeof(struct list_member));
         cur->value = estrdup(*p);          cur->value = estrdup(*p);
         cur->next = def_env_check;          cur->next = def_env_check;
         def_env_check = cur;          def_env_check = cur;
Line 787  init_envtables(void) Line 1097  init_envtables(void)
   
     /* Fill in the "env_keep" list. */      /* Fill in the "env_keep" list. */
     for (p = initial_keepenv_table; *p; p++) {      for (p = initial_keepenv_table; *p; p++) {
        cur = emalloc(sizeof(struct list_member));        cur = ecalloc(1, sizeof(struct list_member));
         cur->value = estrdup(*p);          cur->value = estrdup(*p);
         cur->next = def_env_keep;          cur->next = def_env_keep;
         def_env_keep = cur;          def_env_keep = cur;
     }      }
   }
   
   int
   sudoers_hook_getenv(const char *name, char **value, void *closure)
   {
       static bool in_progress = false; /* avoid recursion */
   
       if (in_progress || env.envp == NULL)
           return SUDO_HOOK_RET_NEXT;
   
       in_progress = true;
   
       /* Hack to make GNU gettext() find the sudoers locale when needed. */
       if (*name == 'L' && sudoers_getlocale() == SUDOERS_LOCALE_SUDOERS) {
           if (strcmp(name, "LANGUAGE") == 0 || strcmp(name, "LANG") == 0) {
               *value = NULL;
               goto done;
           }
           if (strcmp(name, "LC_ALL") == 0 || strcmp(name, "LC_MESSAGES") == 0) {
               *value = def_sudoers_locale;
               goto done;
           }
       }
   
       *value = sudo_getenv_nodebug(name);
   done:
       in_progress = false;
       return SUDO_HOOK_RET_STOP;
   }
   
   int
   sudoers_hook_putenv(char *string, void *closure)
   {
       static bool in_progress = false; /* avoid recursion */
   
       if (in_progress || env.envp == NULL)
           return SUDO_HOOK_RET_NEXT;
   
       in_progress = true;
       sudo_putenv_nodebug(string, true, true);
       in_progress = false;
       return SUDO_HOOK_RET_STOP;
   }
   
   int
   sudoers_hook_setenv(const char *name, const char *value, int overwrite, void *closure)
   {
       static bool in_progress = false; /* avoid recursion */
   
       if (in_progress || env.envp == NULL)
           return SUDO_HOOK_RET_NEXT;
   
       in_progress = true;
       sudo_setenv_nodebug(name, value, overwrite);
       in_progress = false;
       return SUDO_HOOK_RET_STOP;
   }
   
   int
   sudoers_hook_unsetenv(const char *name, void *closure)
   {
       static bool in_progress = false; /* avoid recursion */
   
       if (in_progress || env.envp == NULL)
           return SUDO_HOOK_RET_NEXT;
   
       in_progress = true;
       sudo_unsetenv_nodebug(name);
       in_progress = false;
       return SUDO_HOOK_RET_STOP;
 }  }

Removed from v.1.1  
changed lines
  Added in v.1.1.1.4


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