diff --git a/src/edit_open.c b/src/edit_open.c index 3bbcc2cd8..8795218a9 100644 --- a/src/edit_open.c +++ b/src/edit_open.c @@ -74,7 +74,7 @@ switch_user(uid_t euid, gid_t egid, int ngroups, GETGROUPS_T *groups) * Returns true if the open directory fd is owned or writable by the user. */ int -dir_is_writable(int dfd, struct sudo_cred *user_cred, struct sudo_cred *run_cred) +dir_is_writable(int dfd, struct sudo_cred *user_cred, struct sudo_cred *cur_cred) { struct stat sb; int rc; @@ -92,7 +92,7 @@ dir_is_writable(int dfd, struct sudo_cred *user_cred, struct sudo_cred *run_cred } /* Change uid/gid/groups to invoking user, usually needs root perms. */ - if (run_cred->euid != ROOT_UID) { + if (cur_cred->euid != ROOT_UID) { if (seteuid(ROOT_UID) != 0) sudo_fatal("seteuid(ROOT_UID)"); } @@ -102,13 +102,13 @@ dir_is_writable(int dfd, struct sudo_cred *user_cred, struct sudo_cred *run_cred /* Access checks are done using the euid/egid and group vector. */ rc = faccessat(dfd, ".", W_OK, AT_EACCESS); - /* Change uid/gid/groups back to target user, may need root perms. */ + /* Restore uid/gid/groups, may need root perms. */ if (user_cred->uid != ROOT_UID) { if (seteuid(ROOT_UID) != 0) sudo_fatal("seteuid(ROOT_UID)"); } - switch_user(run_cred->euid, run_cred->egid, run_cred->ngroups, - run_cred->groups); + switch_user(cur_cred->euid, cur_cred->egid, cur_cred->ngroups, + cur_cred->groups); if (rc == 0) debug_return_int(true); @@ -144,7 +144,7 @@ group_matches(gid_t target, struct sudo_cred *cred) * Returns true if the open directory fd is owned or writable by the user. */ int -dir_is_writable(int dfd, struct sudo_cred *user_cred, struct sudo_cred *run_cred) +dir_is_writable(int dfd, struct sudo_cred *user_cred, struct sudo_cred *cur_cred) { struct stat sb; debug_decl(dir_is_writable, SUDO_DEBUG_EDIT); @@ -272,7 +272,7 @@ done: static int sudo_edit_open_nonwritable(char *path, int oflags, mode_t mode, - struct sudo_cred *user_cred, struct sudo_cred *run_cred) + struct sudo_cred *user_cred, struct sudo_cred *cur_cred) { const int dflags = DIR_OPEN_FLAGS; int dfd, fd, is_writable; @@ -297,7 +297,7 @@ sudo_edit_open_nonwritable(char *path, int oflags, mode_t mode, * Look up one component at a time, avoiding symbolic links in * writable directories. */ - is_writable = dir_is_writable(dfd, user_cred, run_cred); + is_writable = dir_is_writable(dfd, user_cred, cur_cred); if (is_writable == -1) { close(dfd); debug_return_int(-1); @@ -339,7 +339,7 @@ sudo_edit_open_nonwritable(char *path, int oflags, mode_t mode, #ifdef O_NOFOLLOW int sudo_edit_open(char *path, int oflags, mode_t mode, int sflags, - struct sudo_cred *user_cred, struct sudo_cred *run_cred) + struct sudo_cred *user_cred, struct sudo_cred *cur_cred) { int fd; debug_decl(sudo_edit_open, SUDO_DEBUG_EDIT); @@ -348,7 +348,7 @@ sudo_edit_open(char *path, int oflags, mode_t mode, int sflags, oflags |= O_NOFOLLOW; if (ISSET(sflags, CD_SUDOEDIT_CHECKDIR) && user_cred->uid != ROOT_UID) { fd = sudo_edit_open_nonwritable(path, oflags|O_NONBLOCK, mode, - user_cred, run_cred); + user_cred, cur_cred); } else { fd = open(path, oflags|O_NONBLOCK, mode); } @@ -359,7 +359,7 @@ sudo_edit_open(char *path, int oflags, mode_t mode, int sflags, #else int sudo_edit_open(char *path, int oflags, mode_t mode, int sflags, - struct sudo_cred *user_cred, struct sudo_cred *run_cred) + struct sudo_cred *user_cred, struct sudo_cred *cur_cred) { struct stat sb; int fd; @@ -380,7 +380,7 @@ sudo_edit_open(char *path, int oflags, mode_t mode, int sflags, if (ISSET(sflags, CD_SUDOEDIT_CHECKDIR) && user_cred->uid != ROOT_UID) { fd = sudo_edit_open_nonwritable(path, oflags|O_NONBLOCK, mode, - user_cred, run_cred); + user_cred, cur_cred); } else { fd = open(path, oflags|O_NONBLOCK, mode); } @@ -412,7 +412,7 @@ sudo_edit_open(char *path, int oflags, mode_t mode, int sflags, */ bool sudo_edit_parent_valid(char *path, int sflags, struct sudo_cred *user_cred, - struct sudo_cred *run_cred) + struct sudo_cred *cur_cred) { const int serrno = errno; struct stat sb; @@ -438,7 +438,7 @@ sudo_edit_parent_valid(char *path, int sflags, struct sudo_cred *user_cred, * *its* parent is writable and CD_SUDOEDIT_CHECK is set. */ dfd = sudo_edit_open(path, DIR_OPEN_FLAGS, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, - sflags|CD_SUDOEDIT_FOLLOW, user_cred, run_cred); + sflags|CD_SUDOEDIT_FOLLOW, user_cred, cur_cred); if (dfd != -1) { if (fstat(dfd, &sb) == 0 && S_ISDIR(sb.st_mode)) ret = true; diff --git a/src/sesh.c b/src/sesh.c index 4efe06556..338e40145 100644 --- a/src/sesh.c +++ b/src/sesh.c @@ -251,6 +251,9 @@ sesh_sudoedit(int argc, char *argv[]) sudo_warn("%s", U_("unable to get group list")); debug_return_int(SESH_ERR_FAILURE); } + } else { + run_cred.ngroups = 0; + run_cred.groups = NULL; } for (i = 0; i < argc - 1; i += 2) { diff --git a/src/sudo_edit.c b/src/sudo_edit.c index 27262762a..396f1c6b1 100644 --- a/src/sudo_edit.c +++ b/src/sudo_edit.c @@ -60,7 +60,7 @@ static char edit_tmpdir[MAX(sizeof(_PATH_VARTMP), sizeof(_PATH_TMP))]; * Returns true on success, else false; */ static bool -set_tmpdir(struct command_details *command_details) +set_tmpdir(struct sudo_cred *user_cred) { const char *tdir = NULL; const char *tmpdirs[] = { @@ -70,21 +70,49 @@ set_tmpdir(struct command_details *command_details) #endif _PATH_TMP }; + struct sudo_cred saved_cred; unsigned int i; size_t len; int dfd; debug_decl(set_tmpdir, SUDO_DEBUG_EDIT); + /* Stash old credentials. */ + saved_cred.uid = getuid(); + saved_cred.euid = geteuid(); + saved_cred.gid = getgid(); + saved_cred.egid = getegid(); + saved_cred.ngroups = getgroups(0, NULL); + if (saved_cred.ngroups > 0) { + saved_cred.groups = + reallocarray(NULL, saved_cred.ngroups, sizeof(GETGROUPS_T)); + if (saved_cred.groups == NULL) { + sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + debug_return_bool(false); + } + if (getgroups(saved_cred.ngroups, saved_cred.groups) < 0) { + sudo_warn("%s", U_("unable to get group list")); + free(saved_cred.groups); + debug_return_bool(false); + } + } else { + saved_cred.ngroups = 0; + saved_cred.groups = NULL; + } + for (i = 0; tdir == NULL && i < nitems(tmpdirs); i++) { if ((dfd = open(tmpdirs[i], O_RDONLY)) != -1) { - if (dir_is_writable(dfd, &user_details.cred, &command_details->cred) == true) + if (dir_is_writable(dfd, user_cred, &saved_cred) == true) tdir = tmpdirs[i]; close(dfd); } } - if (tdir == NULL) - sudo_fatalx("%s", U_("no writable temporary directory found")); - + free(saved_cred.groups); + + if (tdir == NULL) { + sudo_warnx("%s", U_("no writable temporary directory found")); + debug_return_bool(false); + } + len = strlcpy(edit_tmpdir, tdir, sizeof(edit_tmpdir)); if (len >= sizeof(edit_tmpdir)) { errno = ENAMETOOLONG; @@ -609,9 +637,6 @@ sudo_edit(struct command_details *command_details) struct tempfile *tf = NULL; debug_decl(sudo_edit, SUDO_DEBUG_EDIT); - if (!set_tmpdir(command_details)) - goto cleanup; - /* * Set real, effective and saved uids to root. * We will change the euid as needed below. @@ -623,6 +648,10 @@ sudo_edit(struct command_details *command_details) goto cleanup; } + /* Find a temporary directory writable by the user. */ + if (!set_tmpdir(&user_details.cred)) + goto cleanup; + /* * The user's editor must be separated from the files to be * edited by a "--" option. diff --git a/src/sudo_edit.h b/src/sudo_edit.h index 0714642b0..87e6e9f6a 100644 --- a/src/sudo_edit.h +++ b/src/sudo_edit.h @@ -48,8 +48,8 @@ bool sudo_check_temp_file(int tfd, const char *tname, uid_t uid, struct stat *sb /* edit_open.c */ struct sudo_cred; void switch_user(uid_t euid, gid_t egid, int ngroups, GETGROUPS_T *groups); -int sudo_edit_open(char *path, int oflags, mode_t mode, int sflags, struct sudo_cred *user_cred, struct sudo_cred *run_cred); -int dir_is_writable(int dfd, struct sudo_cred *user_cred, struct sudo_cred *run_cred); -bool sudo_edit_parent_valid(char *path, int sflags, struct sudo_cred *user_cred, struct sudo_cred *run_cred); +int sudo_edit_open(char *path, int oflags, mode_t mode, int sflags, struct sudo_cred *user_cred, struct sudo_cred *cur_cred); +int dir_is_writable(int dfd, struct sudo_cred *user_cred, struct sudo_cred *cur_cred); +bool sudo_edit_parent_valid(char *path, int sflags, struct sudo_cred *user_cred, struct sudo_cred *cur_cred); #endif /* SUDO_EDIT_H */