2009-08-06 15:51:12 +00:00
|
|
|
/*
|
2017-02-24 15:14:56 -07:00
|
|
|
* Copyright (c) 2009-2017 Todd C. Miller <Todd.Miller@courtesan.com>
|
2009-08-06 15:51:12 +00:00
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <stdio.h>
|
2015-06-19 14:29:27 -06:00
|
|
|
#include <stdlib.h>
|
2009-08-06 15:51:12 +00:00
|
|
|
#ifdef HAVE_STRING_H
|
|
|
|
# include <string.h>
|
|
|
|
#endif /* HAVE_STRING_H */
|
2010-06-29 13:08:05 -04:00
|
|
|
#ifdef HAVE_STRINGS_H
|
|
|
|
# include <strings.h>
|
|
|
|
#endif /* HAVE_STRINGS_H */
|
2015-07-02 09:08:28 -06:00
|
|
|
#include <unistd.h>
|
2009-08-06 15:51:12 +00:00
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <signal.h>
|
|
|
|
|
2010-06-09 13:57:07 -04:00
|
|
|
#include "sudo.h"
|
2010-06-16 16:46:56 -04:00
|
|
|
#include "sudo_exec.h"
|
2013-10-12 05:53:52 -06:00
|
|
|
#include "sudo_event.h"
|
2010-02-20 09:41:49 -05:00
|
|
|
#include "sudo_plugin.h"
|
|
|
|
#include "sudo_plugin_int.h"
|
2009-08-06 15:51:12 +00:00
|
|
|
|
2009-10-11 12:27:11 +00:00
|
|
|
/*
|
2013-03-14 19:59:07 -04:00
|
|
|
* Setup the execution environment and execute the command.
|
|
|
|
* If SELinux is enabled, run the command via sesh, otherwise
|
|
|
|
* execute it directly.
|
|
|
|
* If the exec fails, cstat is filled in with the value of errno.
|
|
|
|
*/
|
|
|
|
void
|
2017-03-03 10:35:11 -07:00
|
|
|
exec_cmnd(struct command_details *details, int errfd)
|
2013-03-14 19:59:07 -04:00
|
|
|
{
|
2015-02-01 08:24:49 -07:00
|
|
|
debug_decl(exec_cmnd, SUDO_DEBUG_EXEC)
|
2013-03-14 19:59:07 -04:00
|
|
|
|
|
|
|
restore_signals();
|
|
|
|
if (exec_setup(details, NULL, -1) == true) {
|
|
|
|
/* headed for execve() */
|
|
|
|
if (details->closefrom >= 0) {
|
2014-10-22 13:23:05 -06:00
|
|
|
int fd, maxfd;
|
2014-10-23 14:37:27 -06:00
|
|
|
unsigned char *debug_fds;
|
2014-10-22 13:23:05 -06:00
|
|
|
|
|
|
|
/* Preserve debug fds and error pipe as needed. */
|
|
|
|
maxfd = sudo_debug_get_fds(&debug_fds);
|
|
|
|
for (fd = 0; fd <= maxfd; fd++) {
|
2014-10-23 14:37:27 -06:00
|
|
|
if (sudo_isset(debug_fds, fd))
|
2014-10-22 13:23:05 -06:00
|
|
|
add_preserved_fd(&details->preserved_fds, fd);
|
|
|
|
}
|
2013-12-20 11:14:32 -07:00
|
|
|
if (errfd != -1)
|
|
|
|
add_preserved_fd(&details->preserved_fds, errfd);
|
|
|
|
|
|
|
|
/* Close all fds except those explicitly preserved. */
|
|
|
|
closefrom_except(details->closefrom, &details->preserved_fds);
|
2013-03-14 19:59:07 -04:00
|
|
|
}
|
|
|
|
#ifdef HAVE_SELINUX
|
|
|
|
if (ISSET(details->flags, CD_RBAC_ENABLED)) {
|
2016-01-04 10:35:18 -07:00
|
|
|
selinux_execve(details->execfd, details->command, details->argv,
|
|
|
|
details->envp, ISSET(details->flags, CD_NOEXEC));
|
2013-03-14 19:59:07 -04:00
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
2016-01-04 10:35:18 -07:00
|
|
|
sudo_execve(details->execfd, details->command, details->argv,
|
|
|
|
details->envp, ISSET(details->flags, CD_NOEXEC));
|
2013-03-14 19:59:07 -04:00
|
|
|
}
|
|
|
|
}
|
2015-09-09 10:50:21 -06:00
|
|
|
sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to exec %s: %s",
|
|
|
|
details->command, strerror(errno));
|
2013-03-14 19:59:07 -04:00
|
|
|
debug_return;
|
|
|
|
}
|
|
|
|
|
2013-01-17 09:20:45 -05:00
|
|
|
/*
|
2017-05-12 10:02:17 -06:00
|
|
|
* Check for caught signals sent to sudo before command execution.
|
|
|
|
* Also suspends the process if SIGTSTP was caught.
|
|
|
|
* Returns true if we should terminate, else false.
|
2013-01-17 09:20:45 -05:00
|
|
|
*/
|
2017-05-12 10:02:17 -06:00
|
|
|
bool
|
|
|
|
sudo_terminated(struct command_status *cstat)
|
2013-01-17 09:20:45 -05:00
|
|
|
{
|
2017-05-12 10:02:17 -06:00
|
|
|
int signo;
|
|
|
|
bool sigtstp = false;
|
|
|
|
debug_decl(sudo_terminated, SUDO_DEBUG_EXEC)
|
|
|
|
|
|
|
|
for (signo = 0; signo < NSIG; signo++) {
|
|
|
|
if (signal_pending(signo)) {
|
|
|
|
switch (signo) {
|
|
|
|
case SIGTSTP:
|
|
|
|
/* Suspend below if not terminated. */
|
|
|
|
sigtstp = true;
|
2013-01-17 09:20:45 -05:00
|
|
|
break;
|
2017-05-12 10:02:17 -06:00
|
|
|
default:
|
|
|
|
/* Terminal signal, do not exec command. */
|
|
|
|
cstat->type = CMD_WSTATUS;
|
|
|
|
cstat->val = signo + 128;
|
|
|
|
debug_return_bool(true);
|
|
|
|
break;
|
|
|
|
}
|
2013-01-17 09:20:45 -05:00
|
|
|
}
|
|
|
|
}
|
2017-05-12 10:02:17 -06:00
|
|
|
if (sigtstp) {
|
|
|
|
struct sigaction sa;
|
|
|
|
sigset_t set, oset;
|
|
|
|
|
|
|
|
/* Send SIGTSTP to ourselves, unblocking it if needed. */
|
2013-01-17 13:29:46 -05:00
|
|
|
memset(&sa, 0, sizeof(sa));
|
2013-01-17 09:20:45 -05:00
|
|
|
sigemptyset(&sa.sa_mask);
|
2013-01-17 13:29:46 -05:00
|
|
|
sa.sa_flags = SA_RESTART;
|
2013-01-17 09:20:45 -05:00
|
|
|
sa.sa_handler = SIG_DFL;
|
2014-09-04 10:13:26 -06:00
|
|
|
if (sudo_sigaction(SIGTSTP, &sa, NULL) != 0)
|
|
|
|
sudo_warn(U_("unable to set handler for signal %d"), SIGTSTP);
|
2017-05-12 10:02:17 -06:00
|
|
|
sigemptyset(&set);
|
|
|
|
sigaddset(&set, SIGTSTP);
|
|
|
|
sigprocmask(SIG_UNBLOCK, &set, &oset);
|
2013-01-17 09:20:45 -05:00
|
|
|
if (kill(getpid(), SIGTSTP) != 0)
|
2014-06-27 09:30:52 -06:00
|
|
|
sudo_warn("kill(%d, SIGTSTP)", (int)getpid());
|
2017-05-12 10:02:17 -06:00
|
|
|
sigprocmask(SIG_SETMASK, &oset, NULL);
|
|
|
|
/* No need to restore old SIGTSTP handler. */
|
2013-01-17 09:20:45 -05:00
|
|
|
}
|
2017-05-12 10:02:17 -06:00
|
|
|
debug_return_bool(false);
|
2013-01-17 09:20:45 -05:00
|
|
|
}
|
|
|
|
|
2010-09-10 11:20:32 -04:00
|
|
|
/*
|
2017-03-03 10:35:11 -07:00
|
|
|
* Execute a command, potentially in a pty with I/O loggging, and
|
|
|
|
* wait for it to finish.
|
|
|
|
* This is a little bit tricky due to how POSIX job control works and
|
|
|
|
* we fact that we have two different controlling terminals to deal with.
|
2009-10-31 17:14:52 +00:00
|
|
|
*/
|
2017-03-03 10:35:11 -07:00
|
|
|
int
|
|
|
|
sudo_execute(struct command_details *details, struct command_status *cstat)
|
2009-10-31 17:14:52 +00:00
|
|
|
{
|
2017-03-03 10:35:11 -07:00
|
|
|
debug_decl(sudo_execute, SUDO_DEBUG_EXEC)
|
2010-09-10 11:20:32 -04:00
|
|
|
|
2017-03-03 10:35:11 -07:00
|
|
|
/* If running in background mode, fork and exit. */
|
|
|
|
if (ISSET(details->flags, CD_BACKGROUND)) {
|
|
|
|
switch (sudo_debug_fork()) {
|
|
|
|
case -1:
|
|
|
|
cstat->type = CMD_ERRNO;
|
|
|
|
cstat->val = errno;
|
|
|
|
debug_return_int(-1);
|
|
|
|
case 0:
|
|
|
|
/* child continues without controlling terminal */
|
|
|
|
(void)setpgid(0, 0);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* parent exits (but does not flush buffers) */
|
|
|
|
sudo_debug_exit_int(__func__, __FILE__, __LINE__,
|
|
|
|
sudo_debug_subsys, 0);
|
|
|
|
_exit(0);
|
2015-08-10 15:13:37 -06:00
|
|
|
}
|
2014-05-28 09:50:14 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-03-03 10:35:11 -07:00
|
|
|
* If we have an I/O plugin or the policy plugin has requested one, we
|
|
|
|
* need to allocate a pty.
|
2014-05-28 09:50:14 -06:00
|
|
|
*/
|
2017-03-03 10:35:11 -07:00
|
|
|
if (!TAILQ_EMPTY(&io_plugins) || ISSET(details->flags, CD_USE_PTY)) {
|
|
|
|
/*
|
|
|
|
* Run the command in a new pty, wait for it to finish and
|
|
|
|
* send the plugin the exit status.
|
|
|
|
*/
|
|
|
|
exec_pty(details, cstat);
|
|
|
|
} else if (!ISSET(details->flags, CD_SET_TIMEOUT|CD_SUDOEDIT) &&
|
|
|
|
policy_plugin.u.policy->close == NULL) {
|
|
|
|
/*
|
|
|
|
* If we are not running the command in a pty, we were not invoked
|
|
|
|
* as sudoedit, there is no command timeout and there is no close
|
|
|
|
* function, just exec directly. Only returns on error.
|
|
|
|
*/
|
2017-05-12 10:02:17 -06:00
|
|
|
if (!sudo_terminated(cstat)) {
|
|
|
|
exec_cmnd(details, -1);
|
|
|
|
cstat->type = CMD_ERRNO;
|
|
|
|
cstat->val = errno;
|
|
|
|
}
|
2017-03-03 10:35:11 -07:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* No pty but we need to wait for the command to finish to
|
|
|
|
* send the plugin the exit status.
|
|
|
|
*/
|
|
|
|
exec_nopty(details, cstat);
|
2012-01-17 10:27:33 -05:00
|
|
|
}
|
2017-03-03 10:35:11 -07:00
|
|
|
debug_return_int(cstat->type == CMD_ERRNO ? -1 : 0);
|
2012-01-17 10:27:33 -05:00
|
|
|
}
|
|
|
|
|
2017-03-03 10:35:11 -07:00
|
|
|
/*
|
|
|
|
* Kill command with increasing urgency.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
terminate_command(pid_t pid, bool use_pgrp)
|
|
|
|
{
|
|
|
|
debug_decl(terminate_command, SUDO_DEBUG_EXEC);
|
|
|
|
|
|
|
|
/* Avoid killing more than a single process or process group. */
|
|
|
|
if (pid <= 0)
|
|
|
|
debug_return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note that SIGCHLD will interrupt the sleep()
|
|
|
|
*/
|
|
|
|
if (use_pgrp) {
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO, "killpg %d SIGHUP", (int)pid);
|
|
|
|
killpg(pid, SIGHUP);
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO, "killpg %d SIGTERM", (int)pid);
|
|
|
|
killpg(pid, SIGTERM);
|
|
|
|
sleep(2);
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO, "killpg %d SIGKILL", (int)pid);
|
|
|
|
killpg(pid, SIGKILL);
|
|
|
|
} else {
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO, "kill %d SIGHUP", (int)pid);
|
|
|
|
kill(pid, SIGHUP);
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO, "kill %d SIGTERM", (int)pid);
|
|
|
|
kill(pid, SIGTERM);
|
|
|
|
sleep(2);
|
|
|
|
sudo_debug_printf(SUDO_DEBUG_INFO, "kill %d SIGKILL", (int)pid);
|
|
|
|
kill(pid, SIGKILL);
|
|
|
|
}
|
|
|
|
|
|
|
|
debug_return;
|
|
|
|
}
|