diff --git a/src/exec_monitor.c b/src/exec_monitor.c index 3df42d401..5f5fc326e 100644 --- a/src/exec_monitor.c +++ b/src/exec_monitor.c @@ -605,9 +605,9 @@ 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_FOLLOWER], true) == -1) + if (selinux_relabel_tty(details->tty, io_fds[SFD_FOLLOWER]) == -1) goto bad; + selinux_audit_role_change(); } #endif diff --git a/src/exec_nopty.c b/src/exec_nopty.c index b11916602..8891610c6 100644 --- a/src/exec_nopty.c +++ b/src/exec_nopty.c @@ -392,12 +392,12 @@ 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, true) == -1) { + if (selinux_relabel_tty(details->tty, -1) == -1) { cstat->type = CMD_ERRNO; cstat->val = errno; debug_return; } + selinux_audit_role_change(); } #endif diff --git a/src/selinux.c b/src/selinux.c index f2eb4ec18..def379e37 100644 --- a/src/selinux.c +++ b/src/selinux.c @@ -67,14 +67,13 @@ static struct selinux_state { int enforcing; } se_state; -#ifdef HAVE_LINUX_AUDIT -static int -audit_role_change(const char * old_context, - const char * new_context, const char *ttyn, int result) +int +selinux_audit_role_change(void) { +#ifdef HAVE_LINUX_AUDIT int au_fd, rc = -1; char *message; - debug_decl(audit_role_change, SUDO_DEBUG_SELINUX); + debug_decl(selinux_audit_role_change, SUDO_DEBUG_SELINUX); au_fd = audit_open(); if (au_fd == -1) { @@ -85,11 +84,11 @@ audit_role_change(const char * old_context, } else { /* audit role change using the same format as newrole(1) */ rc = asprintf(&message, "newrole: old-context=%s new-context=%s", - old_context, new_context); + se_state.old_context, se_state.new_context ? se_state.new_context : ? "?"); if (rc == -1) sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); rc = audit_log_user_message(au_fd, AUDIT_USER_ROLE_CHANGE, - message, NULL, NULL, ttyn, result); + message, NULL, NULL, se_state.ttyn, se_state.new_context ? 1 : 0); if (rc <= 0) sudo_warn("%s", U_("unable to send audit message")); free(message); @@ -97,8 +96,10 @@ audit_role_change(const char * old_context, } debug_return_int(rc); +#else + return 0; +#endif /* HAVE_LINUX_AUDIT */ } -#endif /* * This function attempts to revert the relabeling done to the tty. @@ -163,8 +164,8 @@ skip_relabel: * This function will not fail if it can not relabel the tty when selinux is * in permissive mode. */ -static int -relabel_tty(const char *ttyn, int ptyfd) +int +selinux_relabel_tty(const char *ttyn, int ptyfd) { char * tty_con = NULL; char * new_tty_con = NULL; @@ -305,38 +306,39 @@ bad: } /* - * Returns a new security context based on the old context and the + * Determine the new security context based on the old context and the * specified role and type. + * Returns 0 on success, and -1 on failure. */ -static char * -get_exec_context(char *old_context, const char *role, const char *type) +static int +get_exec_context(const char *role, const char *type) { char *rolebuf = NULL, *typebuf = NULL; char *new_context = NULL; context_t context = NULL; + int ret = -1; debug_decl(get_exec_context, SUDO_DEBUG_SELINUX); /* * Expand old_context into a context_t so that we can extract and modify * its components easily. */ - if ((context = context_new(old_context)) == NULL) { + if ((context = context_new(se_state.old_context)) == NULL) { sudo_warn("%s", U_("failed to get new context")); - goto bad; + goto done; } if (role == NULL) { - /* TODO: if role is unconfined_r and type is NULL, disable SELinux */ rolebuf = strdup(context_role_get(context)); if (rolebuf == NULL) - goto bad; + goto done; role = rolebuf; } if (type == NULL) { if (get_default_type(role, &typebuf)) { sudo_warnx(U_("unable to get default type for role %s"), role); errno = EINVAL; - goto bad; + goto done; } type = typebuf; } @@ -347,11 +349,11 @@ get_exec_context(char *old_context, const char *role, const char *type) */ if (context_role_set(context, role)) { sudo_warn(U_("failed to set new role %s"), role); - goto bad; + goto done; } if (context_type_set(context, type)) { sudo_warn(U_("failed to set new type %s"), type); - goto bad; + goto done; } /* @@ -359,39 +361,35 @@ get_exec_context(char *old_context, const char *role, const char *type) */ if ((new_context = strdup(context_str(context))) == NULL) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); - goto bad; + goto done; } if (security_check_context(new_context) == -1) { sudo_warnx(U_("%s is not a valid context"), new_context); errno = EINVAL; - goto bad; + goto done; } - context_free(context); - debug_return_str(new_context); + se_state.new_context = new_context; + new_context = NULL; + ret = 0; -bad: +done: free(rolebuf); free(typebuf); context_free(context); freecon(new_context); - debug_return_str(NULL); + debug_return_int(ret); } /* - * Determine the exec and tty contexts in preparation for fork/exec. - * Must run as root, before forking the child process. - * Sets the tty context but not the exec context (which happens later). - * If ptyfd is not -1, it indicates we are running - * in a pty and do not need to reset std{in,out,err}. - * Returns 0 on success and -1 on failure. + * Determine the exec and tty contexts the command will run in. + * Returns 0 on success, 1 if SELinux is not needed, and -1 on failure. */ int -selinux_setup(const char *role, const char *type, const char *ttyn, - int ptyfd, bool label_tty) +selinux_getexeccon(const char *role, const char *type) { int ret = -1; - debug_decl(selinux_setup, SUDO_DEBUG_SELINUX); + debug_decl(selinux_getexeccon, SUDO_DEBUG_SELINUX); /* Store the caller's SID in old_context. */ if (getprevcon(&se_state.old_context)) { @@ -407,28 +405,15 @@ selinux_setup(const char *role, const char *type, const char *ttyn, sudo_debug_printf(SUDO_DEBUG_INFO, "%s: old context %s", __func__, se_state.old_context); - se_state.new_context = get_exec_context(se_state.old_context, role, type); - if (se_state.new_context == NULL) { -#ifdef HAVE_LINUX_AUDIT - audit_role_change(se_state.old_context, "?", se_state.ttyn, 0); -#endif + ret = get_exec_context(role, type); + if (ret == -1) { + /* Audit role change failure (success is logged later). */ + selinux_audit_role_change(); goto done; } sudo_debug_printf(SUDO_DEBUG_INFO, "%s: new context %s", __func__, se_state.new_context); - if (label_tty && relabel_tty(ttyn, ptyfd) == -1) { - sudo_warn(U_("unable to set tty context to %s"), se_state.new_context); - goto done; - } - -#ifdef HAVE_LINUX_AUDIT - audit_role_change(se_state.old_context, se_state.new_context, - se_state.ttyn, 1); -#endif - - ret = 0; - done: debug_return_int(ret); } diff --git a/src/sudo.c b/src/sudo.c index dbe7aab4e..38befb553 100644 --- a/src/sudo.c +++ b/src/sudo.c @@ -887,8 +887,19 @@ command_info_to_details(char * const info[], struct command_details *details) /* Newer sudoers plugin sets selinux_rbac, older only sets role/type. */ if (selinux_rbac == -1) selinux_rbac = details->selinux_role || details->selinux_type; - if (selinux_rbac && is_selinux_enabled() > 0) - SET(details->flags, CD_RBAC_ENABLED); + if (selinux_rbac && is_selinux_enabled() > 0) { + i = selinux_getexeccon(details->selinux_role, details->selinux_type); + switch (i) { + case 0: + SET(details->flags, CD_RBAC_ENABLED); + break; + case 1: + /* No role change needed. */ + break; + default: + exit(EXIT_FAILURE); + } + } #endif debug_return; } diff --git a/src/sudo.h b/src/sudo.h index a7894a409..58d6fa1e0 100644 --- a/src/sudo.h +++ b/src/sudo.h @@ -246,9 +246,10 @@ void usage(void) __attribute__((__noreturn__)); int os_init_openbsd(int argc, char *argv[], char *envp[]); /* selinux.c */ +int selinux_audit_role_change(void); +int selinux_getexeccon(const char *role, const char *type); +int selinux_relabel_tty(const char *ttyn, int ttyfd); int selinux_restore_tty(void); -int selinux_setup(const char *role, const char *type, const char *ttyn, - 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 af552749f..c77e9118f 100644 --- a/src/sudo_edit.c +++ b/src/sudo_edit.c @@ -671,15 +671,6 @@ 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) { @@ -722,6 +713,10 @@ sudo_edit(struct command_details *command_details) sudo_warn("%s", U_("unable to read the clock")); goto cleanup; } +#ifdef HAVE_SELINUX + if (ISSET(command_details->flags, CD_RBAC_ENABLED)) + selinux_audit_role_change(); +#endif memcpy(&saved_command_details, command_details, sizeof(struct command_details)); command_details->cred = user_details.cred; command_details->cred.euid = user_details.cred.uid;