/* * Validate the list of environment variables passed in on the command * line against env_delete, env_check, and env_keep. * Calls log_error() if any specified variables are not allowed. */ void validate_env_vars(char * const env_vars[]) { char * const *ep; char *eq, *bad = NULL; size_t len, blen = 0, bsize = 0; int okvar; if (env_vars == NULL) return; /* Add user-specified environment variables. */ 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 = matches_env_check(*ep); if (okvar == -1) okvar = matches_env_keep(*ep); } else { okvar = matches_env_delete(*ep) == FALSE; if (okvar == FALSE) okvar = matches_env_check(*ep) != FALSE; } if (okvar == FALSE) { /* Not allowed, add to error string, allocating as needed. */ if ((eq = strchr(*ep, '=')) != NULL) *eq = '\0'; len = strlen(*ep) + 2; if (blen + len >= bsize) { do { bsize += 1024; } while (blen + len >= bsize); bad = erealloc(bad, bsize); bad[blen] = '\0'; } strlcat(bad, *ep, bsize); strlcat(bad, ", ", bsize); blen += len; if (eq != NULL) *eq = '='; } } if (bad != NULL) { bad[blen - 2] = '\0'; /* remove trailing ", " */ log_error(NO_MAIL, _("sorry, you are not allowed to set the following environment variables: %s"), bad); /* NOTREACHED */ efree(bad); } }
/* * Returns true if the user successfully authenticates, false if not * or -1 on error. */ int check_user(int validated, int mode) { struct passwd *auth_pw; int rval = -1; debug_decl(check_user, SUDOERS_DEBUG_AUTH) /* * Init authentication system regardless of whether we need a password. * Required for proper PAM session support. */ if ((auth_pw = get_authpw(mode)) == NULL) goto done; if (sudo_auth_init(auth_pw) == -1) goto done; /* * Don't prompt for the root passwd or if the user is exempt. * If the user is not changing uid/gid, no need for a password. */ if (!def_authenticate || user_is_exempt()) { rval = true; goto done; } if (user_uid == 0 || (user_uid == runas_pw->pw_uid && (!runas_gr || user_in_group(sudo_user.pw, runas_gr->gr_name)))) { #ifdef HAVE_SELINUX if (user_role == NULL && user_type == NULL) #endif #ifdef HAVE_PRIV_SET if (runas_privs == NULL && runas_limitprivs == NULL) #endif { rval = true; goto done; } } rval = check_user_interactive(validated, mode, auth_pw); done: sudo_auth_cleanup(auth_pw); sudo_pw_delref(auth_pw); debug_return_bool(rval); }
/* * Build a new environment and ether clear potentially dangerous * variables from the old one or start with a clean slate. * Also adds sudo-specific variables (SUDO_*). */ void rebuild_env(void) { char **old_envp, **ep, *cp, *ps1; char idbuf[MAX_UID_T_LEN]; unsigned int didvar; int reset_home = FALSE; /* * Either clean out the environment or reset to a safe default. */ ps1 = NULL; didvar = 0; env.env_len = 0; env.env_size = 128; old_envp = env.envp; env.envp = emalloc2(env.env_size, sizeof(char *)); #ifdef ENV_DEBUG memset(env.envp, 0, env.env_size * sizeof(char *)); #endif /* Reset HOME based on target user if configured to. */ 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)) { /* Pull in vars we want to keep from the old environment. */ for (ep = old_envp; *ep; ep++) { int keepit; /* Skip variables with values beginning with () (bash functions) */ if ((cp = strchr(*ep, '=')) != NULL) { if (strncmp(cp, "=() ", 3) == 0) continue; } /* * First check certain variables for '%' and '/' characters. * If no match there, check the keep list. * If nothing matched, we remove it from the environment. */ keepit = matches_env_check(*ep); if (keepit == -1) keepit = matches_env_keep(*ep); /* For SUDO_PS1 -> PS1 conversion. */ if (strncmp(*ep, "SUDO_PS1=", 8) == 0) ps1 = *ep + 5; if (keepit) { /* Preserve variable. */ 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="******"USERNAME="******"SHELL", runas_pw->pw_shell, ISSET(didvar, DID_SHELL)); sudo_setenv("LOGNAME", runas_pw->pw_name, ISSET(didvar, DID_LOGNAME)); sudo_setenv("USER", runas_pw->pw_name, ISSET(didvar, DID_USER)); sudo_setenv("USERNAME", runas_pw->pw_name, ISSET(didvar, DID_USERNAME)); } else { if (!ISSET(didvar, DID_SHELL)) sudo_setenv("SHELL", sudo_user.pw->pw_shell, FALSE); if (!ISSET(didvar, DID_LOGNAME)) sudo_setenv("LOGNAME", user_name, FALSE); if (!ISSET(didvar, DID_USER)) sudo_setenv("USER", user_name, FALSE); if (!ISSET(didvar, DID_USERNAME)) sudo_setenv("USERNAME", user_name, FALSE); } /* If we didn't keep HOME, reset it based on target user. */ if (!ISSET(didvar, KEPT_HOME)) reset_home = TRUE; /* * Set MAIL to target user in -i mode or if MAIL is not preserved * from user's environment. */ if (ISSET(sudo_mode, MODE_LOGIN_SHELL) || !ISSET(didvar, KEPT_MAIL)) { cp = _PATH_MAILDIR; if (cp[sizeof(_PATH_MAILDIR) - 2] == '/') easprintf(&cp, "MAIL=%s%s", _PATH_MAILDIR, runas_pw->pw_name); else easprintf(&cp, "MAIL=%s/%s", _PATH_MAILDIR, runas_pw->pw_name); sudo_putenv(cp, ISSET(didvar, DID_MAIL), TRUE); } } else { /* * Copy environ entries as long as they don't match env_delete or * env_check. */ for (ep = old_envp; *ep; ep++) { int okvar; /* Skip variables with values beginning with () (bash functions) */ if ((cp = strchr(*ep, '=')) != NULL) { if (strncmp(cp, "=() ", 3) == 0) continue; } /* * First check variables against the blacklist in env_delete. * 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) ps1 = *ep + 5; else if (strncmp(*ep, "PATH=", 5) == 0) SET(didvar, DID_PATH); else if (strncmp(*ep, "TERM=", 5) == 0) SET(didvar, DID_TERM); sudo_putenv(*ep, FALSE, FALSE); } } } /* Replace the PATH envariable with a secure one? */ if (def_secure_path && !user_is_exempt()) { sudo_setenv("PATH", def_secure_path, TRUE); SET(didvar, DID_PATH); } /* * 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). */ if (def_set_logname && !ISSET(sudo_mode, MODE_LOGIN_SHELL|MODE_EDIT)) { if (!ISSET(didvar, KEPT_LOGNAME)) sudo_setenv("LOGNAME", runas_pw->pw_name, TRUE); if (!ISSET(didvar, KEPT_USER)) sudo_setenv("USER", runas_pw->pw_name, TRUE); if (!ISSET(didvar, KEPT_USERNAME)) sudo_setenv("USERNAME", runas_pw->pw_name, TRUE); } /* Set $HOME to target user if not preserving user's value. */ if (reset_home) sudo_setenv("HOME", runas_pw->pw_dir, TRUE); /* Provide default values for $TERM and $PATH if they are not set. */ if (!ISSET(didvar, DID_TERM)) sudo_putenv("TERM=unknown", FALSE, FALSE); if (!ISSET(didvar, DID_PATH)) sudo_setenv("PATH", _PATH_STDPATH, FALSE); /* Set PS1 if SUDO_PS1 is set. */ if (ps1 != NULL) sudo_putenv(ps1, TRUE, TRUE); /* Add the SUDO_COMMAND envariable (cmnd + args). */ if (user_args) { easprintf(&cp, "%s %s", user_cmnd, user_args); sudo_setenv("SUDO_COMMAND", cp, TRUE); efree(cp); } else { sudo_setenv("SUDO_COMMAND", user_cmnd, TRUE); } /* Add the SUDO_USER, SUDO_UID, SUDO_GID environment variables. */ sudo_setenv("SUDO_USER", user_name, TRUE); snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) user_uid); sudo_setenv("SUDO_UID", idbuf, TRUE); snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) user_gid); sudo_setenv("SUDO_GID", idbuf, TRUE); /* Free old environment. */ efree(old_envp); }
/* * Returns true if the user successfully authenticates, false if not * or -1 on error. */ int check_user(int validated, int mode) { struct passwd *auth_pw; char *timestampdir = NULL; char *timestampfile = NULL; char *prompt; struct stat sb; int status, rval = true; debug_decl(check_user, SUDO_DEBUG_AUTH) /* * Init authentication system regardless of whether we need a password. * Required for proper PAM session support. */ auth_pw = get_authpw(); if (sudo_auth_init(auth_pw) == -1) { rval = -1; goto done; } /* * Don't prompt for the root passwd or if the user is exempt. * If the user is not changing uid/gid, no need for a password. */ if (!def_authenticate || user_uid == 0 || user_is_exempt()) goto done; if (user_uid == runas_pw->pw_uid && (!runas_gr || user_in_group(sudo_user.pw, runas_gr->gr_name))) { #ifdef HAVE_SELINUX if (user_role == NULL && user_type == NULL) #endif #ifdef HAVE_PRIV_SET if (runas_privs == NULL && runas_limitprivs == NULL) #endif goto done; } /* Always need a password when -k was specified with the command. */ if (ISSET(mode, MODE_IGNORE_TICKET)) SET(validated, FLAG_CHECK_USER); /* Stash the tty's ctime for tty ticket comparison. */ if (def_tty_tickets && user_ttypath && stat(user_ttypath, &sb) == 0) { tty_info.dev = sb.st_dev; tty_info.ino = sb.st_ino; tty_info.rdev = sb.st_rdev; if (tty_is_devpts(user_ttypath)) ctim_get(&sb, &tty_info.ctime); } if (build_timestamp(×tampdir, ×tampfile) == -1) { rval = -1; goto done; } status = timestamp_status(timestampdir, timestampfile, user_name, TS_MAKE_DIRS); if (status != TS_CURRENT || ISSET(validated, FLAG_CHECK_USER)) { /* Bail out if we are non-interactive and a password is required */ if (ISSET(mode, MODE_NONINTERACTIVE)) { validated |= FLAG_NON_INTERACTIVE; log_auth_failure(validated, 0); rval = -1; goto done; } /* XXX - should not lecture if askpass helper is being used. */ lecture(status); /* Expand any escapes in the prompt. */ prompt = expand_prompt(user_prompt ? user_prompt : def_passprompt, user_name, user_shost); rval = verify_user(auth_pw, prompt, validated); } /* Only update timestamp if user was validated. */ if (rval == true && ISSET(validated, VALIDATE_OK) && !ISSET(mode, MODE_IGNORE_TICKET) && status != TS_ERROR) update_timestamp(timestampdir, timestampfile); efree(timestampdir); efree(timestampfile); done: sudo_auth_cleanup(auth_pw); sudo_pw_delref(auth_pw); debug_return_bool(rval); }