Fix sudoedit when running with SELinux RBAC mode.
We can't use run_command() to run sesh, that will use the sudo event loop (and might run it in a pty!). There's no need to relabel the tty when copying files. Get the path to sesh from sudo.conf. Currently, for SELinux RBAC, the editor runs with the target user's security context. This defeats the purpose of sudoedit. Fixing that requires passing file descriptors between the main sudo process (running with the invoking user's security context) and sesh (runnning with the target user's security context).
This commit is contained in:
@@ -606,7 +606,7 @@ 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_setup(details->selinux_role, details->selinux_type,
|
||||||
details->tty, io_fds[SFD_SLAVE]) == -1)
|
details->tty, io_fds[SFD_SLAVE], true) == -1)
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -379,7 +379,7 @@ 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_setup(details->selinux_role, details->selinux_type,
|
||||||
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;
|
||||||
|
@@ -387,7 +387,7 @@ bad:
|
|||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
selinux_setup(const char *role, const char *type, const char *ttyn,
|
selinux_setup(const char *role, const char *type, const char *ttyn,
|
||||||
int ptyfd)
|
int ptyfd, bool label_tty)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
debug_decl(selinux_setup, SUDO_DEBUG_SELINUX);
|
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__,
|
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: new context %s", __func__,
|
||||||
se_state.new_context);
|
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);
|
sudo_warn(U_("unable to set tty context to %s"), se_state.new_context);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
@@ -432,6 +432,28 @@ done:
|
|||||||
debug_return_int(ret);
|
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
|
void
|
||||||
selinux_execve(int fd, const char *path, char *const argv[], char *envp[],
|
selinux_execve(int fd, const char *path, char *const argv[], char *envp[],
|
||||||
bool noexec)
|
bool noexec)
|
||||||
@@ -448,19 +470,9 @@ selinux_execve(int fd, const char *path, char *const argv[], char *envp[],
|
|||||||
debug_return;
|
debug_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setexeccon(se_state.new_context)) {
|
/* Set SELinux exec and keycreate contexts. */
|
||||||
sudo_warn(U_("unable to set exec context to %s"), se_state.new_context);
|
if (selinux_setcon() == -1)
|
||||||
if (se_state.enforcing)
|
|
||||||
debug_return;
|
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 */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Build new argv with sesh as argv[0].
|
* Build new argv with sesh as argv[0].
|
||||||
|
@@ -974,10 +974,6 @@ run_command(struct command_details *details)
|
|||||||
case CMD_WSTATUS:
|
case CMD_WSTATUS:
|
||||||
/* Command ran, exited or was killed. */
|
/* Command ran, exited or was killed. */
|
||||||
status = cstat.val;
|
status = cstat.val;
|
||||||
#ifdef HAVE_SELINUX
|
|
||||||
if (ISSET(details->flags, CD_SUDOEDIT_COPY))
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
iolog_close(status, 0);
|
iolog_close(status, 0);
|
||||||
policy_close(status, 0);
|
policy_close(status, 0);
|
||||||
audit_close(SUDO_PLUGIN_WAIT_STATUS, cstat.val);
|
audit_close(SUDO_PLUGIN_WAIT_STATUS, cstat.val);
|
||||||
|
14
src/sudo.h
14
src/sudo.h
@@ -130,12 +130,11 @@ struct user_details {
|
|||||||
#define CD_USE_PTY 0x001000
|
#define CD_USE_PTY 0x001000
|
||||||
#define CD_SET_UTMP 0x002000
|
#define CD_SET_UTMP 0x002000
|
||||||
#define CD_EXEC_BG 0x004000
|
#define CD_EXEC_BG 0x004000
|
||||||
#define CD_SUDOEDIT_COPY 0x008000
|
#define CD_SUDOEDIT_FOLLOW 0x008000
|
||||||
#define CD_SUDOEDIT_FOLLOW 0x010000
|
#define CD_SUDOEDIT_CHECKDIR 0x010000
|
||||||
#define CD_SUDOEDIT_CHECKDIR 0x020000
|
#define CD_SET_GROUPS 0x020000
|
||||||
#define CD_SET_GROUPS 0x040000
|
#define CD_LOGIN_SHELL 0x040000
|
||||||
#define CD_LOGIN_SHELL 0x080000
|
#define CD_OVERRIDE_UMASK 0x080000
|
||||||
#define CD_OVERRIDE_UMASK 0x100000
|
|
||||||
|
|
||||||
struct preserved_fd {
|
struct preserved_fd {
|
||||||
TAILQ_ENTRY(preserved_fd) entries;
|
TAILQ_ENTRY(preserved_fd) entries;
|
||||||
@@ -242,7 +241,8 @@ int os_init_openbsd(int argc, char *argv[], char *envp[]);
|
|||||||
/* selinux.c */
|
/* selinux.c */
|
||||||
int selinux_restore_tty(void);
|
int selinux_restore_tty(void);
|
||||||
int selinux_setup(const char *role, const char *type, const char *ttyn,
|
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[],
|
void selinux_execve(int fd, const char *path, char *const argv[],
|
||||||
char *envp[], bool noexec);
|
char *envp[], bool noexec);
|
||||||
|
|
||||||
|
@@ -718,6 +718,42 @@ bad:
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_SELINUX
|
#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
|
static int
|
||||||
selinux_edit_create_tfiles(struct command_details *command_details,
|
selinux_edit_create_tfiles(struct command_details *command_details,
|
||||||
struct tempfile *tf, char *files[], int nfiles)
|
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;
|
char **sesh_args, **sesh_ap;
|
||||||
int i, rc, sesh_nargs;
|
int i, rc, sesh_nargs;
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
struct command_details saved_command_details;
|
|
||||||
debug_decl(selinux_edit_create_tfiles, SUDO_DEBUG_EDIT);
|
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)
|
if (nfiles < 1)
|
||||||
debug_return_int(0);
|
debug_return_int(0);
|
||||||
|
|
||||||
/* Construct common args for sesh */
|
/* 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_nargs = 4 + (nfiles * 2) + 1;
|
||||||
sesh_args = sesh_ap = reallocarray(NULL, sesh_nargs, sizeof(char *));
|
sesh_args = sesh_ap = reallocarray(NULL, sesh_nargs, sizeof(char *));
|
||||||
if (sesh_args == NULL) {
|
if (sesh_args == NULL) {
|
||||||
@@ -753,6 +779,7 @@ selinux_edit_create_tfiles(struct command_details *command_details,
|
|||||||
*sesh_ap++ = "-h";
|
*sesh_ap++ = "-h";
|
||||||
*sesh_ap++ = "0";
|
*sesh_ap++ = "0";
|
||||||
|
|
||||||
|
/* XXX - temp files should be created with user's context */
|
||||||
for (i = 0; i < nfiles; i++) {
|
for (i = 0; i < nfiles; i++) {
|
||||||
char *tfile, *ofile = files[i];
|
char *tfile, *ofile = files[i];
|
||||||
int tfd;
|
int tfd;
|
||||||
@@ -782,8 +809,7 @@ selinux_edit_create_tfiles(struct command_details *command_details,
|
|||||||
*sesh_ap = NULL;
|
*sesh_ap = NULL;
|
||||||
|
|
||||||
/* Run sesh -e [-h] 0 <o1> <t1> ... <on> <tn> */
|
/* Run sesh -e [-h] 0 <o1> <t1> ... <on> <tn> */
|
||||||
command_details->argv = sesh_args;
|
rc = selinux_run_helper(sesh_args, command_details->envp);
|
||||||
rc = run_command(command_details);
|
|
||||||
switch (rc) {
|
switch (rc) {
|
||||||
case SESH_SUCCESS:
|
case SESH_SUCCESS:
|
||||||
break;
|
break;
|
||||||
@@ -791,15 +817,12 @@ selinux_edit_create_tfiles(struct command_details *command_details,
|
|||||||
sudo_fatalx(U_("sesh: internal error: odd number of paths"));
|
sudo_fatalx(U_("sesh: internal error: odd number of paths"));
|
||||||
case SESH_ERR_NO_FILES:
|
case SESH_ERR_NO_FILES:
|
||||||
sudo_fatalx(U_("sesh: unable to create temporary files"));
|
sudo_fatalx(U_("sesh: unable to create temporary files"));
|
||||||
|
case SESH_ERR_KILLED:
|
||||||
|
sudo_fatalx(U_("sesh: killed by a signal"));
|
||||||
default:
|
default:
|
||||||
sudo_fatalx(U_("sesh: unknown error %d"), rc);
|
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. */
|
/* Chown to user's UID so they can edit the temporary files. */
|
||||||
for (i = 0; i < nfiles; i++) {
|
for (i = 0; i < nfiles; i++) {
|
||||||
if (chown(tf[i].tfile, user_details.uid, user_details.gid) != 0) {
|
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;
|
char **sesh_args, **sesh_ap;
|
||||||
int i, rc, sesh_nargs, ret = 1;
|
int i, rc, sesh_nargs, ret = 1;
|
||||||
struct command_details saved_command_details;
|
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
debug_decl(selinux_edit_copy_tfiles, SUDO_DEBUG_EDIT);
|
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)
|
if (nfiles < 1)
|
||||||
debug_return_int(0);
|
debug_return_int(0);
|
||||||
|
|
||||||
/* Construct common args for sesh */
|
/* 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_nargs = 3 + (nfiles * 2) + 1;
|
||||||
sesh_args = sesh_ap = reallocarray(NULL, sesh_nargs, sizeof(char *));
|
sesh_args = sesh_ap = reallocarray(NULL, sesh_nargs, sizeof(char *));
|
||||||
if (sesh_args == NULL) {
|
if (sesh_args == NULL) {
|
||||||
@@ -875,32 +888,29 @@ selinux_edit_copy_tfiles(struct command_details *command_details,
|
|||||||
|
|
||||||
if (sesh_ap - sesh_args > 3) {
|
if (sesh_ap - sesh_args > 3) {
|
||||||
/* Run sesh -e 1 <t1> <o1> ... <tn> <on> */
|
/* Run sesh -e 1 <t1> <o1> ... <tn> <on> */
|
||||||
command_details->argv = sesh_args;
|
rc = selinux_run_helper(sesh_args, command_details->envp);
|
||||||
rc = run_command(command_details);
|
|
||||||
switch (rc) {
|
switch (rc) {
|
||||||
case SESH_SUCCESS:
|
case SESH_SUCCESS:
|
||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
case SESH_ERR_NO_FILES:
|
case SESH_ERR_NO_FILES:
|
||||||
sudo_warnx(U_("unable to copy temporary files back to their original location"));
|
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;
|
break;
|
||||||
case SESH_ERR_SOME_FILES:
|
case SESH_ERR_SOME_FILES:
|
||||||
sudo_warnx(U_("unable to copy some of the temporary files back to their original location"));
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
sudo_warnx(U_("sesh: unknown error %d"), rc);
|
sudo_warnx(U_("sesh: unknown error %d"), rc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (ret != 0)
|
||||||
|
sudo_warnx(U_("contents of edit session left in %s"), edit_tmpdir);
|
||||||
}
|
}
|
||||||
free(sesh_args);
|
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);
|
debug_return_int(ret);
|
||||||
}
|
}
|
||||||
#endif /* HAVE_SELINUX */
|
#endif /* HAVE_SELINUX */
|
||||||
@@ -952,6 +962,15 @@ 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) {
|
||||||
@@ -987,6 +1006,7 @@ sudo_edit(struct command_details *command_details)
|
|||||||
/*
|
/*
|
||||||
* Run the editor with the invoking user's creds,
|
* Run the editor with the invoking user's creds,
|
||||||
* keeping track of the time spent in the editor.
|
* keeping track of the time spent in the editor.
|
||||||
|
* XXX - should run editor with user's context
|
||||||
*/
|
*/
|
||||||
if (sudo_gettime_real(×[0]) == -1) {
|
if (sudo_gettime_real(×[0]) == -1) {
|
||||||
sudo_warn(U_("unable to read the clock"));
|
sudo_warn(U_("unable to read the clock"));
|
||||||
|
@@ -73,6 +73,7 @@
|
|||||||
*/
|
*/
|
||||||
#define SESH_SUCCESS 0 /* successful operation */
|
#define SESH_SUCCESS 0 /* successful operation */
|
||||||
#define SESH_ERR_FAILURE 1 /* unspecified error */
|
#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_INVALID 30 /* invalid -e arg value */
|
||||||
#define SESH_ERR_BAD_PATHS 31 /* odd number of paths */
|
#define SESH_ERR_BAD_PATHS 31 /* odd number of paths */
|
||||||
#define SESH_ERR_NO_FILES 32 /* copy error, no files copied */
|
#define SESH_ERR_NO_FILES 32 /* copy error, no files copied */
|
||||||
|
Reference in New Issue
Block a user