--- embedaddon/sudo/plugins/sudoers/env.c 2012/05/29 12:26:49 1.1.1.2 +++ embedaddon/sudo/plugins/sudoers/env.c 2014/06/15 16:12:54 1.1.1.6 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2005, 2007-2011 + * Copyright (c) 2000-2005, 2007-2013 * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any @@ -22,7 +22,6 @@ #include #include -#include #include #include #ifdef STDC_HEADERS @@ -42,6 +41,9 @@ #ifdef HAVE_UNISTD_H # include #endif /* HAVE_UNISTD_H */ +#ifdef HAVE_INTTYPES_H +# include +#endif #ifdef HAVE_LOGIN_CAP_H # include # ifndef LOGIN_SETENV @@ -50,11 +52,26 @@ #endif /* HAVE_LOGIN_CAP_H */ #include #include +#include #include #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() */ #undef DID_TERM @@ -229,7 +246,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 +280,20 @@ sudo_putenv_nodebug(char *str, bool dupcheck, bool ove 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) { + fatalx_nodebug(U_("internal error, %s overflow"), + "sudo_putenv_nodebug()"); + } + nsize = env.env_size + 128; + if (nsize > SIZE_MAX / sizeof(char *)) { + fatalx_nodebug(U_("internal error, %s overflow"), + "sudo_putenv_nodebug()"); + } + nenvp = realloc(env.envp, nsize * sizeof(char *)); if (nenvp == NULL) { errno = ENOMEM; return -1; @@ -289,22 +315,22 @@ sudo_putenv_nodebug(char *str, bool dupcheck, bool ove 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,13 +358,15 @@ 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) - errorx(1, _("sudo_putenv: corrupted envp, length mismatch")); + fatalx(U_("sudo_putenv: corrupted envp, length mismatch")); #endif - errorx(1, _("unable to allocate memory")); + fatal(NULL); } debug_return_int(rval); } @@ -353,6 +381,7 @@ sudo_setenv2(const char *var, const char *val, bool du { char *estring; size_t esize; + int rval; debug_decl(sudo_setenv2, SUDO_DEBUG_ENV) esize = strlen(var) + 1 + strlen(val) + 1; @@ -363,54 +392,70 @@ sudo_setenv2(const char *var, const char *val, bool du strlcat(estring, "=", esize) >= esize || strlcat(estring, val, esize) >= esize) { - errorx(1, _("internal error, sudo_setenv2() overflow")); + fatalx(U_("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. + */ +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 *estring; + char *ep, *estring = NULL; + const char *cp; size_t esize; + int rval = -1; - esize = strlen(var) + 1 + strlen(val) + 1; - if ((estring = malloc(esize)) == NULL) { - errno = ENOMEM; - return -1; + if (var == NULL || *var == '\0') { + errno = EINVAL; + goto done; } - /* Build environment string and insert it. */ - if (strlcpy(estring, var, esize) >= esize || - strlcat(estring, "=", esize) >= esize || - strlcat(estring, val, esize) >= esize) { - - errno = EINVAL; - return -1; + /* + * 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 "" */ } - return sudo_putenv_nodebug(estring, true, overwrite); -} -/* - * Similar to setenv(3) but operates on a private copy of the environment. - */ -int -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")); + /* Allocate and fill in estring. */ + if ((estring = ep = malloc(esize)) == NULL) { + errno = ENOMEM; + goto done; } - 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; } /* @@ -452,6 +497,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,27 +537,14 @@ 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. */ @@ -524,7 +558,7 @@ matches_env_delete(const char *var) debug_decl(matches_env_delete, SUDO_DEBUG_ENV) /* Skip anything listed in env_delete. */ - for (cur = def_env_delete; cur; cur = cur->next) { + SLIST_FOREACH(cur, &def_env_delete, entries) { len = strlen(cur->value); /* Deal with '*' wildcard */ if (cur->value[len - 1] == '*') { @@ -555,7 +589,7 @@ matches_env_check(const char *var) int keepit = -1; debug_decl(matches_env_check, SUDO_DEBUG_ENV) - for (cur = def_env_check; cur; cur = cur->next) { + SLIST_FOREACH(cur, &def_env_check, entries) { len = strlen(cur->value); /* Deal with '*' wildcard */ if (cur->value[len - 1] == '*') { @@ -590,7 +624,7 @@ matches_env_keep(const char *var) goto done; } - for (cur = def_env_keep; cur; cur = cur->next) { + SLIST_FOREACH(cur, &def_env_keep, entries) { len = strlen(cur->value); /* Deal with '*' wildcard */ if (cur->value[len - 1] == '*') { @@ -621,6 +655,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,9 +675,28 @@ 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); } +/* + * Merge another environment with our private copy. + * Only overwrite an existing variable if it is not + * being preserved from the user's environment. + */ +void +env_merge(char * const envp[]) +{ + char * const *ep; + debug_decl(env_merge, SUDO_DEBUG_ENV) + + for (ep = envp; *ep != NULL; ep++) + sudo_putenv(*ep, true, !env_should_keep(*ep)); + + debug_return; +} + static void env_update_didvar(const char *ep, unsigned int *didvar) { @@ -687,7 +743,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 +844,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 +904,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)) @@ -952,7 +1011,7 @@ validate_env_vars(char * const env_vars[]) if (bad != NULL) { bad[blen - 2] = '\0'; /* remove trailing ", " */ 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 */ efree(bad); } @@ -972,15 +1031,15 @@ void read_env_file(const char *path, int overwrite) { FILE *fp; - char *cp, *var, *val; - size_t var_len, val_len; + char *cp, *var, *val, *line = NULL; + size_t var_len, val_len, linesize = 0; if ((fp = fopen(path, "r")) == NULL) return; - while ((var = sudo_parseln(fp)) != NULL) { + while (sudo_parseln(&line, &linesize, NULL, fp) != -1) { /* Skip blank or comment lines */ - if (*var == '\0') + if (*(var = line) == '\0') continue; /* Skip optional "export " */ @@ -1012,6 +1071,7 @@ read_env_file(const char *path, int overwrite) sudo_putenv(cp, true, overwrite); } + free(line); fclose(fp); } @@ -1025,24 +1085,21 @@ init_envtables(void) for (p = initial_badenv_table; *p; p++) { cur = ecalloc(1, sizeof(struct list_member)); cur->value = estrdup(*p); - cur->next = def_env_delete; - def_env_delete = cur; + SLIST_INSERT_HEAD(&def_env_delete, cur, entries); } /* Fill in the "env_check" list. */ for (p = initial_checkenv_table; *p; p++) { cur = ecalloc(1, sizeof(struct list_member)); cur->value = estrdup(*p); - cur->next = def_env_check; - def_env_check = cur; + SLIST_INSERT_HEAD(&def_env_check, cur, entries); } /* Fill in the "env_keep" list. */ for (p = initial_keepenv_table; *p; p++) { cur = ecalloc(1, sizeof(struct list_member)); cur->value = estrdup(*p); - cur->next = def_env_keep; - def_env_keep = cur; + SLIST_INSERT_HEAD(&def_env_keep, cur, entries); } } @@ -1055,7 +1112,21 @@ sudoers_hook_getenv(const char *name, char **value, vo 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; }