|
|
| 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; |
| } | } |