diff --git a/src/exec_monitor.c b/src/exec_monitor.c index fd3267c35..bdcd2b349 100644 --- a/src/exec_monitor.c +++ b/src/exec_monitor.c @@ -606,7 +606,7 @@ exec_monitor(struct command_details *details, sigset_t *oset, #ifdef HAVE_SELINUX if (ISSET(details->flags, CD_RBAC_ENABLED)) { if (selinux_setup(details->selinux_role, details->selinux_type, - details->tty, io_fds[SFD_SLAVE]) == -1) + details->tty, io_fds[SFD_SLAVE], true) == -1) goto bad; } #endif diff --git a/src/exec_nopty.c b/src/exec_nopty.c index abea42023..4769846c1 100644 --- a/src/exec_nopty.c +++ b/src/exec_nopty.c @@ -379,7 +379,7 @@ exec_nopty(struct command_details *details, struct command_status *cstat) #ifdef HAVE_SELINUX if (ISSET(details->flags, CD_RBAC_ENABLED)) { if (selinux_setup(details->selinux_role, details->selinux_type, - details->tty, -1) == -1) { + details->tty, -1, true) == -1) { cstat->type = CMD_ERRNO; cstat->val = errno; debug_return; diff --git a/src/selinux.c b/src/selinux.c index 9be57f92c..25e174865 100644 --- a/src/selinux.c +++ b/src/selinux.c @@ -387,7 +387,7 @@ bad: */ int selinux_setup(const char *role, const char *type, const char *ttyn, - int ptyfd) + int ptyfd, bool label_tty) { int ret = -1; debug_decl(selinux_setup, SUDO_DEBUG_SELINUX); @@ -416,7 +416,7 @@ selinux_setup(const char *role, const char *type, const char *ttyn, sudo_debug_printf(SUDO_DEBUG_INFO, "%s: new context %s", __func__, se_state.new_context); - if (relabel_tty(ttyn, ptyfd) == -1) { + if (label_tty && relabel_tty(ttyn, ptyfd) == -1) { sudo_warn(U_("unable to set tty context to %s"), se_state.new_context); goto done; } @@ -432,6 +432,28 @@ done: debug_return_int(ret); } +int +selinux_setcon(void) +{ + debug_decl(selinux_setcon, SUDO_DEBUG_SELINUX); + + if (setexeccon(se_state.new_context)) { + sudo_warn(U_("unable to set exec context to %s"), se_state.new_context); + if (se_state.enforcing) + debug_return_int(-1); + } + +#ifdef HAVE_SETKEYCREATECON + if (setkeycreatecon(se_state.new_context)) { + sudo_warn(U_("unable to set key creation context to %s"), se_state.new_context); + if (se_state.enforcing) + debug_return_int(-1); + } +#endif /* HAVE_SETKEYCREATECON */ + + debug_return_int(0); +} + void selinux_execve(int fd, const char *path, char *const argv[], char *envp[], bool noexec) @@ -448,19 +470,9 @@ selinux_execve(int fd, const char *path, char *const argv[], char *envp[], debug_return; } - if (setexeccon(se_state.new_context)) { - sudo_warn(U_("unable to set exec context to %s"), se_state.new_context); - if (se_state.enforcing) - debug_return; - } - -#ifdef HAVE_SETKEYCREATECON - if (setkeycreatecon(se_state.new_context)) { - sudo_warn(U_("unable to set key creation context to %s"), se_state.new_context); - if (se_state.enforcing) - debug_return; - } -#endif /* HAVE_SETKEYCREATECON */ + /* Set SELinux exec and keycreate contexts. */ + if (selinux_setcon() == -1) + debug_return; /* * Build new argv with sesh as argv[0]. diff --git a/src/sudo.c b/src/sudo.c index 05b2a2acb..4e7ce4632 100644 --- a/src/sudo.c +++ b/src/sudo.c @@ -974,10 +974,6 @@ run_command(struct command_details *details) case CMD_WSTATUS: /* Command ran, exited or was killed. */ status = cstat.val; -#ifdef HAVE_SELINUX - if (ISSET(details->flags, CD_SUDOEDIT_COPY)) - break; -#endif iolog_close(status, 0); policy_close(status, 0); audit_close(SUDO_PLUGIN_WAIT_STATUS, cstat.val); diff --git a/src/sudo.h b/src/sudo.h index 282d9cea5..013209414 100644 --- a/src/sudo.h +++ b/src/sudo.h @@ -130,12 +130,11 @@ struct user_details { #define CD_USE_PTY 0x001000 #define CD_SET_UTMP 0x002000 #define CD_EXEC_BG 0x004000 -#define CD_SUDOEDIT_COPY 0x008000 -#define CD_SUDOEDIT_FOLLOW 0x010000 -#define CD_SUDOEDIT_CHECKDIR 0x020000 -#define CD_SET_GROUPS 0x040000 -#define CD_LOGIN_SHELL 0x080000 -#define CD_OVERRIDE_UMASK 0x100000 +#define CD_SUDOEDIT_FOLLOW 0x008000 +#define CD_SUDOEDIT_CHECKDIR 0x010000 +#define CD_SET_GROUPS 0x020000 +#define CD_LOGIN_SHELL 0x040000 +#define CD_OVERRIDE_UMASK 0x080000 struct preserved_fd { TAILQ_ENTRY(preserved_fd) entries; @@ -242,7 +241,8 @@ int os_init_openbsd(int argc, char *argv[], char *envp[]); /* selinux.c */ int selinux_restore_tty(void); int selinux_setup(const char *role, const char *type, const char *ttyn, - int ttyfd); + int ttyfd, bool label_tty); +int selinux_setcon(void); void selinux_execve(int fd, const char *path, char *const argv[], char *envp[], bool noexec); diff --git a/src/sudo_edit.c b/src/sudo_edit.c index fe08caa00..bff1889a0 100644 --- a/src/sudo_edit.c +++ b/src/sudo_edit.c @@ -718,6 +718,42 @@ bad: } #ifdef HAVE_SELINUX +static int +selinux_run_helper(char *argv[], char *envp[]) +{ + int status, ret = SESH_ERR_FAILURE; + const char *sesh; + pid_t child, pid; + debug_decl(selinux_run_helper, SUDO_DEBUG_EDIT); + + sesh = sudo_conf_sesh_path(); + if (sesh == NULL) { + sudo_warnx("internal error: sesh path not set"); + debug_return_int(-1); + } + + child = sudo_debug_fork(); + switch (child) { + case -1: + sudo_warn(U_("unable to fork")); + break; + case 0: + /* child runs sesh in new context */ + if (selinux_setcon() == 0) + execve(sesh, argv, envp); + _exit(SESH_ERR_FAILURE); + default: + /* parent waits */ + do { + pid = waitpid(child, &status, 0); + } while (pid == -1 && errno == EINTR); + + ret = WIFSIGNALED(status) ? SESH_ERR_KILLED : WEXITSTATUS(status); + } + + debug_return_int(ret); +} + static int selinux_edit_create_tfiles(struct command_details *command_details, struct tempfile *tf, char *files[], int nfiles) @@ -725,22 +761,12 @@ selinux_edit_create_tfiles(struct command_details *command_details, char **sesh_args, **sesh_ap; int i, rc, sesh_nargs; struct stat sb; - struct command_details saved_command_details; debug_decl(selinux_edit_create_tfiles, SUDO_DEBUG_EDIT); - /* Prepare selinux stuff (setexeccon) */ - if (selinux_setup(command_details->selinux_role, - command_details->selinux_type, NULL, -1) != 0) - debug_return_int(-1); - if (nfiles < 1) debug_return_int(0); /* Construct common args for sesh */ - memcpy(&saved_command_details, command_details, sizeof(struct command_details)); - command_details->command = _PATH_SUDO_SESH; - command_details->flags |= CD_SUDOEDIT_COPY; - sesh_nargs = 4 + (nfiles * 2) + 1; sesh_args = sesh_ap = reallocarray(NULL, sesh_nargs, sizeof(char *)); if (sesh_args == NULL) { @@ -753,6 +779,7 @@ selinux_edit_create_tfiles(struct command_details *command_details, *sesh_ap++ = "-h"; *sesh_ap++ = "0"; + /* XXX - temp files should be created with user's context */ for (i = 0; i < nfiles; i++) { char *tfile, *ofile = files[i]; int tfd; @@ -782,8 +809,7 @@ selinux_edit_create_tfiles(struct command_details *command_details, *sesh_ap = NULL; /* Run sesh -e [-h] 0 ... */ - command_details->argv = sesh_args; - rc = run_command(command_details); + rc = selinux_run_helper(sesh_args, command_details->envp); switch (rc) { case SESH_SUCCESS: break; @@ -791,15 +817,12 @@ selinux_edit_create_tfiles(struct command_details *command_details, sudo_fatalx(U_("sesh: internal error: odd number of paths")); case SESH_ERR_NO_FILES: sudo_fatalx(U_("sesh: unable to create temporary files")); + case SESH_ERR_KILLED: + sudo_fatalx(U_("sesh: killed by a signal")); default: sudo_fatalx(U_("sesh: unknown error %d"), rc); } - /* Restore saved command_details. */ - command_details->command = saved_command_details.command; - command_details->flags = saved_command_details.flags; - command_details->argv = saved_command_details.argv; - /* Chown to user's UID so they can edit the temporary files. */ for (i = 0; i < nfiles; i++) { if (chown(tf[i].tfile, user_details.uid, user_details.gid) != 0) { @@ -820,24 +843,14 @@ selinux_edit_copy_tfiles(struct command_details *command_details, { char **sesh_args, **sesh_ap; int i, rc, sesh_nargs, ret = 1; - struct command_details saved_command_details; struct timespec ts; struct stat sb; debug_decl(selinux_edit_copy_tfiles, SUDO_DEBUG_EDIT); - /* Prepare selinux stuff (setexeccon) */ - if (selinux_setup(command_details->selinux_role, - command_details->selinux_type, NULL, -1) != 0) - debug_return_int(1); - if (nfiles < 1) debug_return_int(0); /* Construct common args for sesh */ - memcpy(&saved_command_details, command_details, sizeof(struct command_details)); - command_details->command = _PATH_SUDO_SESH; - command_details->flags |= CD_SUDOEDIT_COPY; - sesh_nargs = 3 + (nfiles * 2) + 1; sesh_args = sesh_ap = reallocarray(NULL, sesh_nargs, sizeof(char *)); if (sesh_args == NULL) { @@ -875,32 +888,29 @@ selinux_edit_copy_tfiles(struct command_details *command_details, if (sesh_ap - sesh_args > 3) { /* Run sesh -e 1 ... */ - command_details->argv = sesh_args; - rc = run_command(command_details); + rc = selinux_run_helper(sesh_args, command_details->envp); switch (rc) { case SESH_SUCCESS: ret = 0; break; case SESH_ERR_NO_FILES: sudo_warnx(U_("unable to copy temporary files back to their original location")); - sudo_warnx(U_("contents of edit session left in %s"), edit_tmpdir); break; case SESH_ERR_SOME_FILES: sudo_warnx(U_("unable to copy some of the temporary files back to their original location")); - sudo_warnx(U_("contents of edit session left in %s"), edit_tmpdir); + break; + case SESH_ERR_KILLED: + sudo_warnx(U_("sesh: killed by a signal")); break; default: sudo_warnx(U_("sesh: unknown error %d"), rc); break; } + if (ret != 0) + sudo_warnx(U_("contents of edit session left in %s"), edit_tmpdir); } free(sesh_args); - /* Restore saved command_details. */ - command_details->command = saved_command_details.command; - command_details->flags = saved_command_details.flags; - command_details->argv = saved_command_details.argv; - debug_return_int(ret); } #endif /* HAVE_SELINUX */ @@ -952,6 +962,15 @@ sudo_edit(struct command_details *command_details) goto cleanup; } +#ifdef HAVE_SELINUX + /* Compute new SELinux security context. */ + if (ISSET(command_details->flags, CD_RBAC_ENABLED)) { + if (selinux_setup(command_details->selinux_role, + command_details->selinux_type, NULL, -1, false) != 0) + goto cleanup; + } +#endif + /* Copy editor files to temporaries. */ tf = calloc(nfiles, sizeof(*tf)); if (tf == NULL) { @@ -987,6 +1006,7 @@ sudo_edit(struct command_details *command_details) /* * Run the editor with the invoking user's creds, * keeping track of the time spent in the editor. + * XXX - should run editor with user's context */ if (sudo_gettime_real(×[0]) == -1) { sudo_warn(U_("unable to read the clock")); diff --git a/src/sudo_exec.h b/src/sudo_exec.h index 4998bf4dc..000a55c34 100644 --- a/src/sudo_exec.h +++ b/src/sudo_exec.h @@ -73,6 +73,7 @@ */ #define SESH_SUCCESS 0 /* successful operation */ #define SESH_ERR_FAILURE 1 /* unspecified error */ +#define SESH_ERR_KILLED 2 /* killed by a signal */ #define SESH_ERR_INVALID 30 /* invalid -e arg value */ #define SESH_ERR_BAD_PATHS 31 /* odd number of paths */ #define SESH_ERR_NO_FILES 32 /* copy error, no files copied */