| version 1.1.1.2, 2012/05/29 12:26:49 | 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 | #ifdef HAVE_LOGIN_CAP_H | 
 | # include <login_cap.h> | # include <login_cap.h> | 
 | # ifndef LOGIN_SETENV | # ifndef LOGIN_SETENV | 
| Line 50 | Line 52 | 
 | #endif /* HAVE_LOGIN_CAP_H */ | #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 229  env_init(char * const envp[]) | Line 246  env_init(char * const envp[]) | 
 | 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. */ | /* Free the old envp we allocated, if any. */ | 
 | if (env.old_envp != NULL) | if (env.old_envp != NULL) | 
| Line 263  sudo_putenv_nodebug(char *str, bool dupcheck, bool ove | Line 280  sudo_putenv_nodebug(char *str, bool dupcheck, bool ove | 
 | bool 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) { | 
 | char **nenvp; | char **nenvp; | 
| size_t nsize = env.env_size + 128; | size_t nsize; | 
| nenvp = env.envp ? realloc(env.envp, nsize * sizeof(char *)) : |  | 
| malloc(nsize * sizeof(char *)); | 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) { | if (nenvp == NULL) { | 
 | errno = ENOMEM; | errno = ENOMEM; | 
 | return -1; | return -1; | 
| Line 289  sudo_putenv_nodebug(char *str, bool dupcheck, bool ove | Line 315  sudo_putenv_nodebug(char *str, bool dupcheck, bool ove | 
 |  |  | 
 | 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 332  sudo_putenv(char *str, bool dupcheck, bool overwrite) | Line 358  sudo_putenv(char *str, bool dupcheck, bool overwrite) | 
 | int rval; | int rval; | 
 | debug_decl(sudo_putenv, SUDO_DEBUG_ENV) | debug_decl(sudo_putenv, SUDO_DEBUG_ENV) | 
 |  |  | 
 |  | sudo_debug_printf(SUDO_DEBUG_INFO, "sudo_putenv: %s", str); | 
 |  |  | 
 | rval = sudo_putenv_nodebug(str, dupcheck, overwrite); | rval = sudo_putenv_nodebug(str, dupcheck, overwrite); | 
 | if (rval == -1) { | if (rval == -1) { | 
 | #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")); | fatalx(_("sudo_putenv: corrupted envp, length mismatch")); | 
 | #endif | #endif | 
| errorx(1, _("unable to allocate memory")); | fatalx(NULL); | 
 | } | } | 
 | debug_return_int(rval); | debug_return_int(rval); | 
 | } | } | 
| Line 353  sudo_setenv2(const char *var, const char *val, bool du | Line 381  sudo_setenv2(const char *var, const char *val, bool du | 
 | { | { | 
 | char *estring; | char *estring; | 
 | size_t esize; | size_t esize; | 
 |  | int rval; | 
 | debug_decl(sudo_setenv2, SUDO_DEBUG_ENV) | debug_decl(sudo_setenv2, SUDO_DEBUG_ENV) | 
 |  |  | 
 | esize = strlen(var) + 1 + strlen(val) + 1; | esize = strlen(var) + 1 + strlen(val) + 1; | 
| Line 363  sudo_setenv2(const char *var, const char *val, bool du | Line 392  sudo_setenv2(const char *var, const char *val, bool du | 
 | strlcat(estring, "=", esize) >= esize || | strlcat(estring, "=", esize) >= esize || | 
 | strlcat(estring, val, esize) >= esize) { | strlcat(estring, val, esize) >= esize) { | 
 |  |  | 
| errorx(1, _("internal error, sudo_setenv2() overflow")); | fatalx(_("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); | 
 | } | } | 
 |  |  | 
 | /* | /* | 
 | * Similar to setenv(3) but operates on a private copy of the environment. | * 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. | * Does not include warnings or debugging to avoid recursive calls. | 
 | */ | */ | 
 | static int | static int | 
 | sudo_setenv_nodebug(const char *var, const char *val, int overwrite) | sudo_setenv_nodebug(const char *var, const char *val, int overwrite) | 
 | { | { | 
| char *estring; | char *ep, *estring = NULL; | 
|  | const char *cp; | 
 | size_t esize; | size_t esize; | 
 |  | int rval = -1; | 
 |  |  | 
| esize = strlen(var) + 1 + strlen(val) + 1; | if (var == NULL || *var == '\0') { | 
| if ((estring = malloc(esize)) == NULL) { | errno = EINVAL; | 
| errno = ENOMEM; | goto done; | 
| return -1; |  | 
 | } | } | 
 |  |  | 
| /* Build environment string and insert it. */ | /* | 
| if (strlcpy(estring, var, esize) >= esize || | * POSIX says a var name with '=' is an error but BSD | 
| strlcat(estring, "=", esize) >= esize || | * just ignores the '=' and anything after it. | 
| strlcat(estring, val, esize) >= esize) { | */ | 
|  | for (cp = var; *cp && *cp != '='; cp++) | 
| errno = EINVAL; | ; | 
| return -1; | esize = (size_t)(cp - var) + 2; | 
|  | if (val) { | 
|  | esize += strlen(val);        /* glibc treats a NULL val as "" */ | 
 | } | } | 
 | return sudo_putenv_nodebug(estring, true, overwrite); |  | 
 | } |  | 
 |  |  | 
| /* | /* Allocate and fill in estring. */ | 
| * Similar to setenv(3) but operates on a private copy of the environment. | if ((estring = ep = malloc(esize)) == NULL) { | 
| */ | errno = ENOMEM; | 
| int | goto done; | 
| sudo_setenv(const char *var, const char *val, int overwrite) |  | 
| { |  | 
| int rval; |  | 
| debug_decl(sudo_setenv, SUDO_DEBUG_ENV) |  | 
|  |  | 
| rval = sudo_setenv_nodebug(var, val, overwrite); |  | 
| if (rval == -1) { |  | 
| if (errno == EINVAL) |  | 
| errorx(1, _("internal error, sudo_setenv() overflow")); |  | 
| errorx(1, _("unable to allocate memory")); |  | 
 | } | } | 
| debug_return_int(rval); | 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; | 
 | } | } | 
 |  |  | 
 | /* | /* | 
| Line 452  sudo_unsetenv(const char *name) | Line 497  sudo_unsetenv(const char *name) | 
 | int rval; | int rval; | 
 | debug_decl(sudo_unsetenv, SUDO_DEBUG_ENV) | debug_decl(sudo_unsetenv, SUDO_DEBUG_ENV) | 
 |  |  | 
 |  | sudo_debug_printf(SUDO_DEBUG_INFO, "sudo_unsetenv: %s", name); | 
 |  |  | 
 | rval = sudo_unsetenv_nodebug(name); | rval = sudo_unsetenv_nodebug(name); | 
 |  |  | 
 | debug_return_int(rval); | debug_return_int(rval); | 
| Line 490  sudo_getenv(const char *name) | Line 537  sudo_getenv(const char *name) | 
 | char *val; | char *val; | 
 | debug_decl(sudo_getenv, SUDO_DEBUG_ENV) | debug_decl(sudo_getenv, SUDO_DEBUG_ENV) | 
 |  |  | 
 |  | sudo_debug_printf(SUDO_DEBUG_INFO, "sudo_getenv: %s", name); | 
 |  |  | 
 | val = sudo_getenv_nodebug(name); | val = sudo_getenv_nodebug(name); | 
 |  |  | 
 | debug_return_str(val); | debug_return_str(val); | 
| Line 621  env_should_delete(const char *var) | Line 670  env_should_delete(const char *var) | 
 | delete_it = matches_env_delete(var); | delete_it = matches_env_delete(var); | 
 | if (!delete_it) | if (!delete_it) | 
 | delete_it = matches_env_check(var) == false; | 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); | debug_return_bool(delete_it); | 
 | } | } | 
 |  |  | 
| Line 638  env_should_keep(const char *var) | Line 690  env_should_keep(const char *var) | 
 | if (keepit == -1) | if (keepit == -1) | 
 | keepit = matches_env_keep(var); | keepit = matches_env_keep(var); | 
 |  |  | 
 |  | sudo_debug_printf(SUDO_DEBUG_INFO, "keep %s: %s", | 
 |  | var, keepit ? "YES" : "NO"); | 
 | debug_return_bool(keepit == true); | debug_return_bool(keepit == true); | 
 | } | } | 
 |  |  | 
| Line 687  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; | 
 | bool reset_home = false; | bool reset_home = false; | 
 |  |  | 
| Line 788  rebuild_env(void) | Line 842  rebuild_env(void) | 
 | } else { | } else { | 
 | if (!ISSET(didvar, DID_SHELL)) | if (!ISSET(didvar, DID_SHELL)) | 
 | sudo_setenv2("SHELL", sudo_user.pw->pw_shell, false, true); | 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_setenv2("LOGNAME", user_name, false, true); | if (!def_set_logname) { | 
| if (!ISSET(didvar, DID_USER)) | if (!ISSET(didvar, DID_LOGNAME)) | 
| sudo_setenv2("USER", user_name, false, true); | sudo_setenv2("LOGNAME", user_name, false, true); | 
| if (!ISSET(didvar, DID_USERNAME)) | if (!ISSET(didvar, DID_USER)) | 
| sudo_setenv2("USERNAME", user_name, false, true); | 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. */ | 
| Line 845  rebuild_env(void) | Line 902  rebuild_env(void) | 
 | /* | /* | 
 | * 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)) | 
| Line 952  validate_env_vars(char * const env_vars[]) | Line 1009  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_fatal(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 972  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 1012  read_env_file(const char *path, int overwrite) | Line 1069  read_env_file(const char *path, int overwrite) | 
 |  |  | 
 | sudo_putenv(cp, true, overwrite); | sudo_putenv(cp, true, overwrite); | 
 | } | } | 
 |  | free(line); | 
 | fclose(fp); | fclose(fp); | 
 | } | } | 
 |  |  | 
| Line 1055  sudoers_hook_getenv(const char *name, char **value, vo | Line 1113  sudoers_hook_getenv(const char *name, char **value, vo | 
 | return SUDO_HOOK_RET_NEXT; | return SUDO_HOOK_RET_NEXT; | 
 |  |  | 
 | in_progress = true; | 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); | *value = sudo_getenv_nodebug(name); | 
 |  | done: | 
 | in_progress = false; | in_progress = false; | 
 | return SUDO_HOOK_RET_STOP; | return SUDO_HOOK_RET_STOP; | 
 | } | } |