Separate out the code to compute the context from selinux_setup().

This makes it possible to determine whether we really need to execute
the command via the sesh helper.  What was left of selinux_setup()
is now selinux_relabel_tty() and selinux_audit_role_change().
This commit is contained in:
Todd C. Miller
2021-11-05 12:33:20 -06:00
parent a336a8422f
commit e97fb5fd0b
6 changed files with 61 additions and 69 deletions

View File

@@ -605,9 +605,9 @@ exec_monitor(struct command_details *details, sigset_t *oset,
#ifdef HAVE_SELINUX #ifdef HAVE_SELINUX
if (ISSET(details->flags, CD_RBAC_ENABLED)) { if (ISSET(details->flags, CD_RBAC_ENABLED)) {
if (selinux_setup(details->selinux_role, details->selinux_type, if (selinux_relabel_tty(details->tty, io_fds[SFD_FOLLOWER]) == -1)
details->tty, io_fds[SFD_FOLLOWER], true) == -1)
goto bad; goto bad;
selinux_audit_role_change();
} }
#endif #endif

View File

@@ -392,12 +392,12 @@ exec_nopty(struct command_details *details, struct command_status *cstat)
#ifdef HAVE_SELINUX #ifdef HAVE_SELINUX
if (ISSET(details->flags, CD_RBAC_ENABLED)) { if (ISSET(details->flags, CD_RBAC_ENABLED)) {
if (selinux_setup(details->selinux_role, details->selinux_type, if (selinux_relabel_tty(details->tty, -1) == -1) {
details->tty, -1, true) == -1) {
cstat->type = CMD_ERRNO; cstat->type = CMD_ERRNO;
cstat->val = errno; cstat->val = errno;
debug_return; debug_return;
} }
selinux_audit_role_change();
} }
#endif #endif

View File

@@ -67,14 +67,13 @@ static struct selinux_state {
int enforcing; int enforcing;
} se_state; } se_state;
#ifdef HAVE_LINUX_AUDIT int
static int selinux_audit_role_change(void)
audit_role_change(const char * old_context,
const char * new_context, const char *ttyn, int result)
{ {
#ifdef HAVE_LINUX_AUDIT
int au_fd, rc = -1; int au_fd, rc = -1;
char *message; char *message;
debug_decl(audit_role_change, SUDO_DEBUG_SELINUX); debug_decl(selinux_audit_role_change, SUDO_DEBUG_SELINUX);
au_fd = audit_open(); au_fd = audit_open();
if (au_fd == -1) { if (au_fd == -1) {
@@ -85,11 +84,11 @@ audit_role_change(const char * old_context,
} else { } else {
/* audit role change using the same format as newrole(1) */ /* audit role change using the same format as newrole(1) */
rc = asprintf(&message, "newrole: old-context=%s new-context=%s", 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) if (rc == -1)
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
rc = audit_log_user_message(au_fd, AUDIT_USER_ROLE_CHANGE, 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) if (rc <= 0)
sudo_warn("%s", U_("unable to send audit message")); sudo_warn("%s", U_("unable to send audit message"));
free(message); free(message);
@@ -97,8 +96,10 @@ audit_role_change(const char * old_context,
} }
debug_return_int(rc); debug_return_int(rc);
#else
return 0;
#endif /* HAVE_LINUX_AUDIT */
} }
#endif
/* /*
* This function attempts to revert the relabeling done to the tty. * 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 * This function will not fail if it can not relabel the tty when selinux is
* in permissive mode. * in permissive mode.
*/ */
static int int
relabel_tty(const char *ttyn, int ptyfd) selinux_relabel_tty(const char *ttyn, int ptyfd)
{ {
char * tty_con = NULL; char * tty_con = NULL;
char * new_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. * specified role and type.
* Returns 0 on success, and -1 on failure.
*/ */
static char * static int
get_exec_context(char *old_context, const char *role, const char *type) get_exec_context(const char *role, const char *type)
{ {
char *rolebuf = NULL, *typebuf = NULL; char *rolebuf = NULL, *typebuf = NULL;
char *new_context = NULL; char *new_context = NULL;
context_t context = NULL; context_t context = NULL;
int ret = -1;
debug_decl(get_exec_context, SUDO_DEBUG_SELINUX); debug_decl(get_exec_context, SUDO_DEBUG_SELINUX);
/* /*
* Expand old_context into a context_t so that we can extract and modify * Expand old_context into a context_t so that we can extract and modify
* its components easily. * 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")); sudo_warn("%s", U_("failed to get new context"));
goto bad; goto done;
} }
if (role == NULL) { if (role == NULL) {
/* TODO: if role is unconfined_r and type is NULL, disable SELinux */
rolebuf = strdup(context_role_get(context)); rolebuf = strdup(context_role_get(context));
if (rolebuf == NULL) if (rolebuf == NULL)
goto bad; goto done;
role = rolebuf; role = rolebuf;
} }
if (type == NULL) { if (type == NULL) {
if (get_default_type(role, &typebuf)) { if (get_default_type(role, &typebuf)) {
sudo_warnx(U_("unable to get default type for role %s"), role); sudo_warnx(U_("unable to get default type for role %s"), role);
errno = EINVAL; errno = EINVAL;
goto bad; goto done;
} }
type = typebuf; type = typebuf;
} }
@@ -347,11 +349,11 @@ get_exec_context(char *old_context, const char *role, const char *type)
*/ */
if (context_role_set(context, role)) { if (context_role_set(context, role)) {
sudo_warn(U_("failed to set new role %s"), role); sudo_warn(U_("failed to set new role %s"), role);
goto bad; goto done;
} }
if (context_type_set(context, type)) { if (context_type_set(context, type)) {
sudo_warn(U_("failed to set new type %s"), 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) { if ((new_context = strdup(context_str(context))) == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
goto bad; goto done;
} }
if (security_check_context(new_context) == -1) { if (security_check_context(new_context) == -1) {
sudo_warnx(U_("%s is not a valid context"), new_context); sudo_warnx(U_("%s is not a valid context"), new_context);
errno = EINVAL; errno = EINVAL;
goto bad; goto done;
} }
context_free(context); se_state.new_context = new_context;
debug_return_str(new_context); new_context = NULL;
ret = 0;
bad: done:
free(rolebuf); free(rolebuf);
free(typebuf); free(typebuf);
context_free(context); context_free(context);
freecon(new_context); freecon(new_context);
debug_return_str(NULL); debug_return_int(ret);
} }
/* /*
* Determine the exec and tty contexts in preparation for fork/exec. * Determine the exec and tty contexts the command will run in.
* Must run as root, before forking the child process. * Returns 0 on success, 1 if SELinux is not needed, and -1 on failure.
* 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.
*/ */
int int
selinux_setup(const char *role, const char *type, const char *ttyn, selinux_getexeccon(const char *role, const char *type)
int ptyfd, bool label_tty)
{ {
int ret = -1; 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. */ /* Store the caller's SID in old_context. */
if (getprevcon(&se_state.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__, sudo_debug_printf(SUDO_DEBUG_INFO, "%s: old context %s", __func__,
se_state.old_context); se_state.old_context);
se_state.new_context = get_exec_context(se_state.old_context, role, type); ret = get_exec_context(role, type);
if (se_state.new_context == NULL) { if (ret == -1) {
#ifdef HAVE_LINUX_AUDIT /* Audit role change failure (success is logged later). */
audit_role_change(se_state.old_context, "?", se_state.ttyn, 0); selinux_audit_role_change();
#endif
goto done; goto done;
} }
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: new context %s", __func__, sudo_debug_printf(SUDO_DEBUG_INFO, "%s: new context %s", __func__,
se_state.new_context); 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: done:
debug_return_int(ret); debug_return_int(ret);
} }

View File

@@ -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. */ /* Newer sudoers plugin sets selinux_rbac, older only sets role/type. */
if (selinux_rbac == -1) if (selinux_rbac == -1)
selinux_rbac = details->selinux_role || details->selinux_type; selinux_rbac = details->selinux_role || details->selinux_type;
if (selinux_rbac && is_selinux_enabled() > 0) 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); SET(details->flags, CD_RBAC_ENABLED);
break;
case 1:
/* No role change needed. */
break;
default:
exit(EXIT_FAILURE);
}
}
#endif #endif
debug_return; debug_return;
} }

View File

@@ -246,9 +246,10 @@ void usage(void) __attribute__((__noreturn__));
int os_init_openbsd(int argc, char *argv[], char *envp[]); int os_init_openbsd(int argc, char *argv[], char *envp[]);
/* selinux.c */ /* 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_restore_tty(void);
int selinux_setup(const char *role, const char *type, const char *ttyn,
int ttyfd, bool label_tty);
int selinux_setcon(void); int selinux_setcon(void);
void selinux_execve(int fd, const char *path, char *const argv[], void selinux_execve(int fd, const char *path, char *const argv[],
char *envp[], bool noexec); char *envp[], bool noexec);

View File

@@ -671,15 +671,6 @@ sudo_edit(struct command_details *command_details)
goto cleanup; 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. */ /* Copy editor files to temporaries. */
tf = calloc(nfiles, sizeof(*tf)); tf = calloc(nfiles, sizeof(*tf));
if (tf == NULL) { if (tf == NULL) {
@@ -722,6 +713,10 @@ sudo_edit(struct command_details *command_details)
sudo_warn("%s", U_("unable to read the clock")); sudo_warn("%s", U_("unable to read the clock"));
goto cleanup; 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)); memcpy(&saved_command_details, command_details, sizeof(struct command_details));
command_details->cred = user_details.cred; command_details->cred = user_details.cred;
command_details->cred.euid = user_details.cred.uid; command_details->cred.euid = user_details.cred.uid;