diff -up ./src/exec_monitor.c.symbolic-link-attack-3 ./src/exec_monitor.c --- ./src/exec_monitor.c.symbolic-link-attack-3 2019-10-28 13:27:39.000000000 +0100 +++ ./src/exec_monitor.c 2021-02-02 17:11:32.382020407 +0100 @@ -613,7 +613,7 @@ exec_monitor(struct command_details *det #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 -up ./src/exec_nopty.c.symbolic-link-attack-3 ./src/exec_nopty.c --- ./src/exec_nopty.c.symbolic-link-attack-3 2019-10-28 13:27:39.000000000 +0100 +++ ./src/exec_nopty.c 2021-02-02 17:11:32.382020407 +0100 @@ -381,7 +381,7 @@ exec_nopty(struct command_details *detai #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 -up ./src/selinux.c.symbolic-link-attack-3 ./src/selinux.c --- ./src/selinux.c.symbolic-link-attack-3 2019-10-28 13:27:39.000000000 +0100 +++ ./src/selinux.c 2021-02-02 17:11:32.382020407 +0100 @@ -363,7 +363,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) @@ -392,7 +392,7 @@ selinux_setup(const char *role, const ch 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; } @@ -408,6 +408,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) @@ -424,19 +446,9 @@ selinux_execve(int fd, const char *path, 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 -up ./src/sudo.c.symbolic-link-attack-3 ./src/sudo.c --- ./src/sudo.c.symbolic-link-attack-3 2021-02-02 17:12:32.773182386 +0100 +++ ./src/sudo.c 2021-02-02 17:12:48.510964009 +0100 @@ -971,10 +971,6 @@ run_command(struct command_details *deta case CMD_WSTATUS: /* Command ran, exited or was killed. */ status = cstat.val; -#ifdef HAVE_SELINUX - if (ISSET(details->flags, CD_SUDOEDIT_COPY)) - break; -#endif sudo_debug_printf(SUDO_DEBUG_DEBUG, "calling policy close with wait status %d", status); policy_close(&policy_plugin, status, 0); diff -up ./src/sudo_edit.c.symbolic-link-attack-3 ./src/sudo_edit.c --- ./src/sudo_edit.c.symbolic-link-attack-3 2021-02-02 17:11:32.380020435 +0100 +++ ./src/sudo_edit.c 2021-02-02 17:11:32.382020407 +0100 @@ -757,28 +757,54 @@ 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) { 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) { @@ -791,6 +817,7 @@ selinux_edit_create_tfiles(struct comman *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; @@ -820,8 +847,7 @@ selinux_edit_create_tfiles(struct comman *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; @@ -829,15 +855,12 @@ selinux_edit_create_tfiles(struct comman 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) { @@ -858,24 +881,14 @@ selinux_edit_copy_tfiles(struct command_ { 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) { @@ -913,32 +926,29 @@ selinux_edit_copy_tfiles(struct command_ 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 */ @@ -990,6 +1000,15 @@ sudo_edit(struct command_details *comman 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) { @@ -1025,6 +1044,7 @@ sudo_edit(struct command_details *comman /* * 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 -up ./src/sudo_exec.h.symbolic-link-attack-3 ./src/sudo_exec.h --- ./src/sudo_exec.h.symbolic-link-attack-3 2021-02-02 17:11:32.380020435 +0100 +++ ./src/sudo_exec.h 2021-02-02 17:11:32.382020407 +0100 @@ -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 */ diff -up ./src/sudo.h.symbolic-link-attack-3 ./src/sudo.h --- ./src/sudo.h.symbolic-link-attack-3 2019-10-28 13:28:52.000000000 +0100 +++ ./src/sudo.h 2021-02-02 17:11:32.382020407 +0100 @@ -135,12 +135,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; @@ -240,7 +239,8 @@ int os_init_openbsd(int argc, char *argv /* 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);