#include <config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <unistd.h>
#if defined(HAVE_STDINT_H)
# include <stdint.h>
#elif defined(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
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <pwd.h>
#include "sudoers.h"
#undef DID_TERM
#define DID_TERM 0x00000001
#undef DID_PATH
#define DID_PATH 0x00000002
#undef DID_HOME
#define DID_HOME 0x00000004
#undef DID_SHELL
#define DID_SHELL 0x00000008
#undef DID_LOGNAME
#define DID_LOGNAME 0x00000010
#undef DID_USER
#define DID_USER 0x00000020
#undef DID_LOGIN
#define DID_LOGIN 0x00000040
#undef DID_MAIL
#define DID_MAIL 0x00000080
#undef DID_MAX
#define DID_MAX 0x0000ffff
#undef KEPT_TERM
#define KEPT_TERM 0x00010000
#undef KEPT_PATH
#define KEPT_PATH 0x00020000
#undef KEPT_HOME
#define KEPT_HOME 0x00040000
#undef KEPT_SHELL
#define KEPT_SHELL 0x00080000
#undef KEPT_LOGNAME
#define KEPT_LOGNAME 0x00100000
#undef KEPT_USER
#define KEPT_USER 0x00200000
#undef KEPT_LOGIN
#define KEPT_LOGIN 0x00400000
#undef KEPT_MAIL
#define KEPT_MAIL 0x00800000
#undef KEPT_MAX
#define KEPT_MAX 0xffff0000
#ifdef _AIX
# define KEPT_USER_VARIABLES (KEPT_LOGIN|KEPT_LOGNAME|KEPT_USER)
#else
# define KEPT_USER_VARIABLES (KEPT_LOGNAME|KEPT_USER)
#endif
struct sudoers_env_file {
void * (*open)(const char *);
void (*close)(void *);
char * (*next)(void *, int *);
};
struct env_file_local {
FILE *fp;
char *line;
size_t linesize;
};
struct environment {
char **envp;
char **old_envp;
size_t env_size;
size_t env_len;
};
static struct environment env;
static const char *initial_badenv_table[] = {
"IFS",
"CDPATH",
"LOCALDOMAIN",
"RES_OPTIONS",
"HOSTALIASES",
"NLSPATH",
"PATH_LOCALE",
"LD_*",
"_RLD*",
#ifdef __hpux
"SHLIB_PATH",
#endif
#ifdef _AIX
"LDR_*",
"LIBPATH",
"AUTHSTATE",
#endif
#ifdef __APPLE__
"DYLD_*",
#endif
#ifdef HAVE_KERB5
"KRB5_CONFIG*",
"KRB5_KTNAME",
#endif
#ifdef HAVE_SECURID
"VAR_ACE",
"USR_ACE",
"DLC_ACE",
#endif
"TERMINFO",
"TERMINFO_DIRS",
"TERMPATH",
"TERMCAP",
"ENV",
"BASH_ENV",
"PS4",
"GLOBIGNORE",
"BASHOPTS",
"SHELLOPTS",
"JAVA_TOOL_OPTIONS",
"PERLIO_DEBUG ",
"PERLLIB",
"PERL5LIB",
"PERL5OPT",
"PERL5DB",
"FPATH",
"NULLCMD",
"READNULLCMD",
"ZDOTDIR",
"TMPPREFIX",
"PYTHONHOME",
"PYTHONPATH",
"PYTHONINSPECT",
"PYTHONUSERBASE",
"RUBYLIB",
"RUBYOPT",
"*=()*",
NULL
};
static const char *initial_checkenv_table[] = {
"COLORTERM",
"LANG",
"LANGUAGE",
"LC_*",
"LINGUAS",
"TERM",
"TZ",
NULL
};
static const char *initial_keepenv_table[] = {
"COLORS",
"DISPLAY",
"HOSTNAME",
"KRB5CCNAME",
"LS_COLORS",
"PATH",
"PS1",
"PS2",
"XAUTHORITY",
"XAUTHORIZATION",
NULL
};
bool
env_init(char * const envp[])
{
char * const *ep;
size_t len;
debug_decl(env_init, SUDOERS_DEBUG_ENV)
if (envp == NULL) {
free(env.old_envp);
env.old_envp = env.envp;
env.envp = NULL;
env.env_size = 0;
env.env_len = 0;
} else {
for (ep = envp; *ep != NULL; ep++)
continue;
len = (size_t)(ep - envp);
env.env_len = len;
env.env_size = len + 1 + 128;
env.envp = reallocarray(NULL, env.env_size, sizeof(char *));
if (env.envp == NULL) {
env.env_size = 0;
env.env_len = 0;
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
debug_return_bool(false);
}
#ifdef ENV_DEBUG
memset(env.envp, 0, env.env_size * sizeof(char *));
#endif
memcpy(env.envp, envp, len * sizeof(char *));
env.envp[len] = NULL;
free(env.old_envp);
env.old_envp = NULL;
}
debug_return_bool(true);
}
char **
env_get(void)
{
return env.envp;
}
bool
env_swap_old(void)
{
char **old_envp;
if (env.old_envp == NULL)
return false;
old_envp = env.old_envp;
env.old_envp = env.envp;
env.envp = old_envp;
return true;
}
static int
sudo_putenv_nodebug(char *str, bool dupcheck, bool overwrite)
{
char **ep;
size_t len;
bool found = false;
if (env.env_size > 2 && env.env_len > env.env_size - 2) {
char **nenvp;
size_t nsize;
if (env.env_size > SIZE_MAX - 128) {
sudo_warnx_nodebug(U_("internal error, %s overflow"),
"sudo_putenv_nodebug");
errno = EOVERFLOW;
return -1;
}
nsize = env.env_size + 128;
if (nsize > SIZE_MAX / sizeof(char *)) {
sudo_warnx_nodebug(U_("internal error, %s overflow"),
"sudo_putenv_nodebug");
errno = EOVERFLOW;
return -1;
}
nenvp = reallocarray(env.envp, nsize, sizeof(char *));
if (nenvp == NULL)
return -1;
env.envp = nenvp;
env.env_size = nsize;
#ifdef ENV_DEBUG
memset(env.envp + env.env_len, 0,
(env.env_size - env.env_len) * sizeof(char *));
#endif
}
#ifdef ENV_DEBUG
if (env.envp[env.env_len] != NULL) {
errno = EINVAL;
return -1;
}
#endif
if (dupcheck) {
len = (strchr(str, '=') - str) + 1;
for (ep = env.envp; *ep != NULL; ep++) {
if (strncmp(str, *ep, len) == 0) {
if (overwrite)
*ep = str;
found = true;
break;
}
}
if (found && overwrite) {
while (*++ep != NULL) {
if (strncmp(str, *ep, len) == 0) {
char **cur = ep;
while ((*cur = *(cur + 1)) != NULL)
cur++;
ep--;
}
}
env.env_len = ep - env.envp;
}
}
if (!found) {
ep = env.envp + env.env_len;
env.env_len++;
*ep++ = str;
*ep = NULL;
}
return 0;
}
static int
sudo_putenv(char *str, bool dupcheck, bool overwrite)
{
int ret;
debug_decl(sudo_putenv, SUDOERS_DEBUG_ENV)
sudo_debug_printf(SUDO_DEBUG_INFO, "sudo_putenv: %s", str);
ret = sudo_putenv_nodebug(str, dupcheck, overwrite);
if (ret == -1) {
#ifdef ENV_DEBUG
if (env.envp[env.env_len] != NULL)
sudo_warnx(U_("sudo_putenv: corrupted envp, length mismatch"));
#endif
}
debug_return_int(ret);
}
static int
sudo_setenv2(const char *var, const char *val, bool dupcheck, bool overwrite)
{
char *estring;
size_t esize;
int ret = -1;
debug_decl(sudo_setenv2, SUDOERS_DEBUG_ENV)
esize = strlen(var) + 1 + strlen(val) + 1;
if ((estring = malloc(esize)) == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to allocate memory");
debug_return_int(-1);
}
if (strlcpy(estring, var, esize) >= esize ||
strlcat(estring, "=", esize) >= esize ||
strlcat(estring, val, esize) >= esize) {
sudo_warnx(U_("internal error, %s overflow"), __func__);
errno = EOVERFLOW;
} else {
ret = sudo_putenv(estring, dupcheck, overwrite);
}
if (ret == -1)
free(estring);
else
sudoers_gc_add(GC_PTR, estring);
debug_return_int(ret);
}
int
sudo_setenv(const char *var, const char *val, int overwrite)
{
return sudo_setenv2(var, val, true, (bool)overwrite);
}
static int
sudo_setenv_nodebug(const char *var, const char *val, int overwrite)
{
char *ep, *estring = NULL;
const char *cp;
size_t esize;
int ret = -1;
if (var == NULL || *var == '\0') {
errno = EINVAL;
goto done;
}
for (cp = var; *cp && *cp != '='; cp++)
continue;
esize = (size_t)(cp - var) + 2;
if (val) {
esize += strlen(val);
}
if ((estring = ep = malloc(esize)) == NULL)
goto done;
for (cp = var; *cp && *cp != '='; cp++)
*ep++ = *cp;
*ep++ = '=';
if (val) {
for (cp = val; *cp; cp++)
*ep++ = *cp;
}
*ep = '\0';
ret = sudo_putenv_nodebug(estring, true, overwrite);
done:
if (ret == -1)
free(estring);
else
sudoers_gc_add(GC_PTR, estring);
return ret;
}
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] == '=') {
char **cur = ep;
while ((*cur = *(cur + 1)) != NULL)
cur++;
env.env_len--;
} else {
ep++;
}
}
return 0;
}
int
sudo_unsetenv(const char *name)
{
int ret;
debug_decl(sudo_unsetenv, SUDOERS_DEBUG_ENV)
sudo_debug_printf(SUDO_DEBUG_INFO, "sudo_unsetenv: %s", name);
ret = sudo_unsetenv_nodebug(name);
debug_return_int(ret);
}
static char *
sudo_getenv_nodebug(const char *name)
{
char **ep, *val = NULL;
size_t namelen = 0;
if (env.env_len != 0) {
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;
}
char *
sudo_getenv(const char *name)
{
char *val;
debug_decl(sudo_getenv, SUDOERS_DEBUG_ENV)
sudo_debug_printf(SUDO_DEBUG_INFO, "sudo_getenv: %s", name);
val = sudo_getenv_nodebug(name);
debug_return_str(val);
}
static bool
matches_env_list(const char *var, struct list_members *list, bool *full_match)
{
struct list_member *cur;
bool is_logname = false;
debug_decl(matches_env_list, SUDOERS_DEBUG_ENV)
switch (*var) {
case 'L':
if (strncmp(var, "LOGNAME=", 8) == 0)
is_logname = true;
#ifdef _AIX
else if (strncmp(var, "LOGIN=", 6) == 0)
is_logname = true;
#endif
break;
case 'U':
if (strncmp(var, "USER=", 5) == 0)
is_logname = true;
break;
}
if (is_logname) {
SLIST_FOREACH(cur, list, entries) {
if (matches_env_pattern(cur->value, "LOGNAME", full_match) ||
#ifdef _AIX
matches_env_pattern(cur->value, "LOGIN", full_match) ||
#endif
matches_env_pattern(cur->value, "USER", full_match))
debug_return_bool(true);
}
} else {
SLIST_FOREACH(cur, list, entries) {
if (matches_env_pattern(cur->value, var, full_match))
debug_return_bool(true);
}
}
debug_return_bool(false);
}
static bool
matches_env_delete(const char *var)
{
bool full_match;
debug_decl(matches_env_delete, SUDOERS_DEBUG_ENV)
debug_return_bool(matches_env_list(var, &def_env_delete, &full_match));
}
static bool
tz_is_sane(const char *tzval)
{
const char *cp;
char lastch;
debug_decl(tz_is_sane, SUDOERS_DEBUG_ENV)
if (tzval[0] == ':')
tzval++;
if (tzval[0] == '/') {
#ifdef _PATH_ZONEINFO
if (strncmp(tzval, _PATH_ZONEINFO, sizeof(_PATH_ZONEINFO) - 1) != 0 ||
tzval[sizeof(_PATH_ZONEINFO) - 1] != '/')
debug_return_bool(false);
#else
debug_return_bool(false);
#endif
}
lastch = '/';
for (cp = tzval; *cp != '\0'; cp++) {
if (isspace((unsigned char)*cp) || !isprint((unsigned char)*cp))
debug_return_bool(false);
if (lastch == '/' && cp[0] == '.' && cp[1] == '.' &&
(cp[2] == '/' || cp[2] == '\0'))
debug_return_bool(false);
lastch = *cp;
}
if ((size_t)(cp - tzval) >= PATH_MAX)
debug_return_bool(false);
debug_return_bool(true);
}
static int
matches_env_check(const char *var, bool *full_match)
{
int keepit = -1;
debug_decl(matches_env_check, SUDOERS_DEBUG_ENV)
if (matches_env_list(var, &def_env_check, full_match)) {
if (strncmp(var, "TZ=", 3) == 0) {
keepit = tz_is_sane(var + 3);
} else {
const char *val = strchr(var, '=');
if (val != NULL)
keepit = !strpbrk(++val, "/%");
}
}
debug_return_int(keepit);
}
static bool
matches_env_keep(const char *var, bool *full_match)
{
bool keepit = false;
debug_decl(matches_env_keep, SUDOERS_DEBUG_ENV)
if (ISSET(sudo_mode, MODE_SHELL) && strncmp(var, "SHELL=", 6) == 0) {
keepit = true;
} else if (matches_env_list(var, &def_env_keep, full_match)) {
keepit = true;
}
debug_return_bool(keepit);
}
static bool
env_should_delete(const char *var)
{
int delete_it;
bool full_match = false;
debug_decl(env_should_delete, SUDOERS_DEBUG_ENV);
delete_it = matches_env_delete(var);
if (!delete_it)
delete_it = matches_env_check(var, &full_match) == false;
sudo_debug_printf(SUDO_DEBUG_INFO, "delete %s: %s",
var, delete_it ? "YES" : "NO");
debug_return_bool(delete_it);
}
static bool
env_should_keep(const char *var)
{
int keepit;
bool full_match = false;
const char *cp;
debug_decl(env_should_keep, SUDOERS_DEBUG_ENV)
keepit = matches_env_check(var, &full_match);
if (keepit == -1)
keepit = matches_env_keep(var, &full_match);
if (keepit && !full_match) {
if ((cp = strchr(var, '=')) != NULL) {
if (strncmp(cp, "=() ", 4) == 0)
keepit = false;
}
}
sudo_debug_printf(SUDO_DEBUG_INFO, "keep %s: %s",
var, keepit == true ? "YES" : "NO");
debug_return_bool(keepit == true);
}
#ifdef HAVE_PAM
bool
env_merge(char * const envp[])
{
char * const *ep;
bool ret = true;
debug_decl(env_merge, SUDOERS_DEBUG_ENV)
for (ep = envp; *ep != NULL; ep++) {
bool overwrite = def_env_reset ? !env_should_keep(*ep) : env_should_delete(*ep);
if (sudo_putenv(*ep, true, overwrite) == -1) {
ret = false;
break;
}
}
debug_return_bool(ret);
}
#endif
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':
#ifdef _AIX
if (strncmp(ep, "LOGIN=", 8) == 0)
SET(*didvar, DID_LOGIN);
#endif
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);
break;
}
}
#define CHECK_PUTENV(a, b, c) do { \
if (sudo_putenv((a), (b), (c)) == -1) \
goto bad; \
} while (0)
#define CHECK_SETENV2(a, b, c, d) do { \
if (sudo_setenv2((a), (b), (c), (d)) == -1) \
goto bad; \
} while (0)
bool
rebuild_env(void)
{
char **ep, *cp, *ps1;
char idbuf[MAX_UID_T_LEN + 1];
unsigned int didvar;
bool reset_home = false;
debug_decl(rebuild_env, SUDOERS_DEBUG_ENV)
ps1 = NULL;
didvar = 0;
env.env_len = 0;
env.env_size = 128;
free(env.old_envp);
env.old_envp = env.envp;
env.envp = reallocarray(NULL, env.env_size, sizeof(char *));
if (env.envp == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to allocate memory");
env.env_size = 0;
goto bad;
}
#ifdef ENV_DEBUG
memset(env.envp, 0, env.env_size * sizeof(char *));
#else
env.envp[0] = NULL;
#endif
if (ISSET(sudo_mode, MODE_RUN)) {
if (def_always_set_home ||
ISSET(sudo_mode, MODE_RESET_HOME | MODE_LOGIN_SHELL) ||
(ISSET(sudo_mode, MODE_SHELL) && def_set_home))
reset_home = true;
}
if (def_env_reset || ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
if (!ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
#ifdef HAVE_LOGIN_CAP_H
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
#if defined(_AIX) || (defined(__linux__) && !defined(HAVE_PAM))
read_env_file(_PATH_ENVIRONMENT, true, false);
#endif
for (ep = env.envp; *ep; ep++)
env_update_didvar(*ep, &didvar);
}
for (ep = env.old_envp; *ep; ep++) {
bool keepit;
keepit = env_should_keep(*ep);
if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
ps1 = *ep + 5;
if (keepit) {
CHECK_PUTENV(*ep, true, false);
env_update_didvar(*ep, &didvar);
}
}
didvar |= didvar << 16;
if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
CHECK_SETENV2("SHELL", runas_pw->pw_shell,
ISSET(didvar, DID_SHELL), true);
#ifdef _AIX
CHECK_SETENV2("LOGIN", runas_pw->pw_name,
ISSET(didvar, DID_LOGIN), true);
#endif
CHECK_SETENV2("LOGNAME", runas_pw->pw_name,
ISSET(didvar, DID_LOGNAME), true);
CHECK_SETENV2("USER", runas_pw->pw_name,
ISSET(didvar, DID_USER), true);
} else {
if (!def_set_logname) {
#ifdef _AIX
if (!ISSET(didvar, DID_LOGIN))
CHECK_SETENV2("LOGIN", user_name, false, true);
#endif
if (!ISSET(didvar, DID_LOGNAME))
CHECK_SETENV2("LOGNAME", user_name, false, true);
if (!ISSET(didvar, DID_USER))
CHECK_SETENV2("USER", user_name, false, true);
}
}
if (!ISSET(didvar, KEPT_HOME))
reset_home = true;
if (ISSET(sudo_mode, MODE_LOGIN_SHELL) || !ISSET(didvar, KEPT_MAIL)) {
if (_PATH_MAILDIR[sizeof(_PATH_MAILDIR) - 2] == '/') {
if (asprintf(&cp, "MAIL=%s%s", _PATH_MAILDIR, runas_pw->pw_name) == -1)
goto bad;
} else {
if (asprintf(&cp, "MAIL=%s/%s", _PATH_MAILDIR, runas_pw->pw_name) == -1)
goto bad;
}
if (sudo_putenv(cp, ISSET(didvar, DID_MAIL), true) == -1) {
free(cp);
goto bad;
}
sudoers_gc_add(GC_PTR, cp);
}
} else {
for (ep = env.old_envp; *ep; ep++) {
if (!env_should_delete(*ep)) {
if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
ps1 = *ep + 5;
else if (strncmp(*ep, "SHELL=", 6) == 0)
SET(didvar, DID_SHELL);
else if (strncmp(*ep, "PATH=", 5) == 0)
SET(didvar, DID_PATH);
else if (strncmp(*ep, "TERM=", 5) == 0)
SET(didvar, DID_TERM);
CHECK_PUTENV(*ep, true, false);
}
}
}
if (def_secure_path && !user_is_exempt()) {
CHECK_SETENV2("PATH", def_secure_path, true, true);
SET(didvar, DID_PATH);
}
if (def_set_logname && !ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
if ((didvar & KEPT_USER_VARIABLES) == 0) {
#ifdef _AIX
CHECK_SETENV2("LOGIN", runas_pw->pw_name, true, true);
#endif
CHECK_SETENV2("LOGNAME", runas_pw->pw_name, true, true);
CHECK_SETENV2("USER", runas_pw->pw_name, true, true);
} else if ((didvar & KEPT_USER_VARIABLES) != KEPT_USER_VARIABLES) {
if (ISSET(didvar, KEPT_LOGNAME))
cp = sudo_getenv("LOGNAME");
#ifdef _AIX
else if (ISSET(didvar, KEPT_LOGIN))
cp = sudo_getenv("LOGIN");
#endif
else if (ISSET(didvar, KEPT_USER))
cp = sudo_getenv("USER");
else
cp = NULL;
if (cp != NULL) {
#ifdef _AIX
if (!ISSET(didvar, KEPT_LOGIN))
CHECK_SETENV2("LOGIN", cp, true, true);
#endif
if (!ISSET(didvar, KEPT_LOGNAME))
CHECK_SETENV2("LOGNAME", cp, true, true);
if (!ISSET(didvar, KEPT_USER))
CHECK_SETENV2("USER", cp, true, true);
}
}
}
if (reset_home)
CHECK_SETENV2("HOME", runas_pw->pw_dir, true, true);
if (!ISSET(didvar, DID_SHELL))
CHECK_SETENV2("SHELL", runas_pw->pw_shell, false, false);
if (!ISSET(didvar, DID_TERM))
CHECK_PUTENV("TERM=unknown", false, false);
if (!ISSET(didvar, DID_PATH))
CHECK_SETENV2("PATH", _PATH_STDPATH, false, true);
if (ps1 != NULL)
CHECK_PUTENV(ps1, true, true);
if (user_args) {
if (asprintf(&cp, "SUDO_COMMAND=%s %s", user_cmnd, user_args) == -1)
goto bad;
if (sudo_putenv(cp, true, true) == -1) {
free(cp);
goto bad;
}
sudoers_gc_add(GC_PTR, cp);
} else {
CHECK_SETENV2("SUDO_COMMAND", user_cmnd, true, true);
}
CHECK_SETENV2("SUDO_USER", user_name, true, true);
(void)snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) user_uid);
CHECK_SETENV2("SUDO_UID", idbuf, true, true);
(void)snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) user_gid);
CHECK_SETENV2("SUDO_GID", idbuf, true, true);
debug_return_bool(true);
bad:
sudo_warn(U_("unable to rebuild the environment"));
debug_return_bool(false);
}
bool
insert_env_vars(char * const envp[])
{
char * const *ep;
bool ret = true;
debug_decl(insert_env_vars, SUDOERS_DEBUG_ENV)
if (envp != NULL) {
for (ep = envp; *ep != NULL; ep++) {
if (sudo_putenv(*ep, true, true) == -1) {
ret = false;
break;
}
}
}
debug_return_bool(ret);
}
bool
validate_env_vars(char * const env_vars[])
{
char * const *ep;
char *eq, errbuf[4096];
bool okvar, ret = true;
debug_decl(validate_env_vars, SUDOERS_DEBUG_ENV)
if (env_vars == NULL)
debug_return_bool(true);
errbuf[0] = '\0';
for (ep = env_vars; *ep != NULL; ep++) {
if (def_secure_path && !user_is_exempt() &&
strncmp(*ep, "PATH=", 5) == 0) {
okvar = false;
} else if (def_env_reset) {
okvar = env_should_keep(*ep);
} else {
okvar = !env_should_delete(*ep);
}
if (okvar == false) {
if ((eq = strchr(*ep, '=')) != NULL)
*eq = '\0';
if (errbuf[0] != '\0')
(void)strlcat(errbuf, ", ", sizeof(errbuf));
if (strlcat(errbuf, *ep, sizeof(errbuf)) >= sizeof(errbuf)) {
errbuf[sizeof(errbuf) - 4] = '\0';
(void)strlcat(errbuf, "...", sizeof(errbuf));
}
if (eq != NULL)
*eq = '=';
}
}
if (errbuf[0] != '\0') {
log_warningx(0,
N_("sorry, you are not allowed to set the following environment variables: %s"), errbuf);
ret = false;
}
debug_return_bool(ret);
}
static void *
env_file_open_local(const char *path)
{
struct env_file_local *efl;
debug_decl(env_file_open_local, SUDOERS_DEBUG_ENV)
efl = calloc(1, sizeof(*efl));
if (efl != NULL) {
if ((efl->fp = fopen(path, "r")) == NULL) {
free(efl);
efl = NULL;
}
}
debug_return_ptr(efl);
}
static void
env_file_close_local(void *cookie)
{
struct env_file_local *efl = cookie;
debug_decl(env_file_close_local, SUDOERS_DEBUG_ENV)
if (efl != NULL) {
if (efl->fp != NULL)
fclose(efl->fp);
free(efl->line);
free(efl);
}
debug_return;
}
static char *
env_file_next_local(void *cookie, int *errnum)
{
struct env_file_local *efl = cookie;
char *var, *val, *ret = NULL;
size_t var_len, val_len;
debug_decl(env_file_next_local, SUDOERS_DEBUG_ENV)
*errnum = 0;
for (;;) {
if (sudo_parseln(&efl->line, &efl->linesize, NULL, efl->fp, PARSELN_CONT_IGN) == -1) {
if (!feof(efl->fp))
*errnum = errno;
break;
}
if (*(var = efl->line) == '\0')
continue;
if (strncmp(var, "export", 6) == 0 && isspace((unsigned char) var[6])) {
var += 7;
while (isspace((unsigned char) *var)) {
var++;
}
}
for (val = var; *val != '\0' && *val != '='; val++)
continue;
if (var == val || *val != '=')
continue;
var_len = (size_t)(val - var);
val_len = strlen(++val);
if ((val[0] == '\'' || val[0] == '\"') && val[0] == val[val_len - 1]) {
val[val_len - 1] = '\0';
val++;
val_len -= 2;
}
if ((ret = malloc(var_len + 1 + val_len + 1)) == NULL) {
*errnum = errno;
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to allocate memory");
} else {
memcpy(ret, var, var_len + 1);
memcpy(ret + var_len + 1, val, val_len + 1);
sudoers_gc_add(GC_PTR, ret);
}
break;
}
debug_return_str(ret);
}
static struct sudoers_env_file env_file_sudoers = {
env_file_open_local,
env_file_close_local,
env_file_next_local
};
static struct sudoers_env_file env_file_system = {
env_file_open_local,
env_file_close_local,
env_file_next_local
};
void
register_env_file(void * (*ef_open)(const char *), void (*ef_close)(void *),
char * (*ef_next)(void *, int *), bool system)
{
struct sudoers_env_file *ef = system ? &env_file_system : &env_file_sudoers;
ef->open = ef_open;
ef->close = ef_close;
ef->next = ef_next;
}
bool
read_env_file(const char *path, bool overwrite, bool restricted)
{
struct sudoers_env_file *ef;
bool ret = true;
char *envstr;
void *cookie;
int errnum;
debug_decl(read_env_file, SUDOERS_DEBUG_ENV)
if (path == def_env_file || path == def_restricted_env_file)
ef = &env_file_sudoers;
else
ef = &env_file_system;
cookie = ef->open(path);
if (cookie == NULL)
debug_return_bool(false);
for (;;) {
if ((envstr = ef->next(cookie, &errnum)) == NULL) {
if (errnum != 0)
ret = false;
break;
}
if (restricted) {
if (def_env_reset ? !env_should_keep(envstr) : env_should_delete(envstr)) {
free(envstr);
continue;
}
}
if (sudo_putenv(envstr, true, overwrite) == -1) {
ret = false;
break;
}
}
ef->close(cookie);
debug_return_bool(ret);
}
bool
init_envtables(void)
{
struct list_member *cur;
const char **p;
debug_decl(init_envtables, SUDOERS_DEBUG_ENV)
for (p = initial_badenv_table; *p; p++) {
cur = calloc(1, sizeof(struct list_member));
if (cur == NULL || (cur->value = strdup(*p)) == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to allocate memory");
free(cur);
debug_return_bool(false);
}
SLIST_INSERT_HEAD(&def_env_delete, cur, entries);
}
for (p = initial_checkenv_table; *p; p++) {
cur = calloc(1, sizeof(struct list_member));
if (cur == NULL || (cur->value = strdup(*p)) == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to allocate memory");
free(cur);
debug_return_bool(false);
}
SLIST_INSERT_HEAD(&def_env_check, cur, entries);
}
for (p = initial_keepenv_table; *p; p++) {
cur = calloc(1, sizeof(struct list_member));
if (cur == NULL || (cur->value = strdup(*p)) == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to allocate memory");
free(cur);
debug_return_bool(false);
}
SLIST_INSERT_HEAD(&def_env_keep, cur, entries);
}
debug_return_bool(true);
}
int
sudoers_hook_getenv(const char *name, char **value, void *closure)
{
static bool in_progress = false;
if (in_progress || env.envp == NULL)
return SUDO_HOOK_RET_NEXT;
in_progress = true;
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;
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;
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;
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;
}