2
0
mirror of https://github.com/sudo-project/sudo.git synced 2025-08-22 01:49:11 +00:00

Catch SIGINT, SIGQUIT and SIGTSTP in the front end before we execute

the command.  If we get SIGINT or SIGQUIT, call the plugin close()
functions as if the command was interrupted.  If we get SIGTSTP,
uninstall the handler and deliver SIGTSTP to ourselves.
This commit is contained in:
Todd C. Miller 2013-01-17 09:20:45 -05:00
parent 541315212e
commit 99704cc101
5 changed files with 137 additions and 47 deletions

View File

@ -214,23 +214,8 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
struct sudo_nss *nss;
int cmnd_status = -1, oldlocale, validated;
volatile int rval = true;
sigaction_t sa, saved_sa_int, saved_sa_quit, saved_sa_tstp;
debug_decl(sudoers_policy_main, SUDO_DEBUG_PLUGIN)
/*
* Signal setup:
* Ignore keyboard-generated signals so the user cannot interrupt
* us at some point and avoid the logging.
* XXX - just block signals for critical sections (logging/auditing)
*/
zero_bytes(&sa, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_IGN;
(void) sigaction(SIGINT, &sa, &saved_sa_int);
(void) sigaction(SIGQUIT, &sa, &saved_sa_quit);
(void) sigaction(SIGTSTP, &sa, &saved_sa_tstp);
/* XXX - would like to move this to policy.c but need the cleanup. */
if (error_setjmp() != 0) {
/* error recovery via error(), errorx() or log_fatal() */
@ -526,11 +511,6 @@ done:
error_disable_setjmp();
rewind_perms();
/* Restore signal handlers before we return. */
(void) sigaction(SIGINT, &saved_sa_int, NULL);
(void) sigaction(SIGQUIT, &saved_sa_quit, NULL);
(void) sigaction(SIGTSTP, &saved_sa_tstp, NULL);
/* Close the password and group files and free up memory. */
sudo_endpwent();
sudo_endgrent();

View File

@ -59,9 +59,6 @@
#include "sudo_plugin.h"
#include "sudo_plugin_int.h"
/* Shared with exec_pty.c for use with handler(). */
int signal_pipe[2];
/* We keep a tailq of signals to forward to child. */
struct sigforward {
struct sigforward *prev, *next;
@ -75,6 +72,7 @@ volatile pid_t cmnd_pid = -1;
static int dispatch_signals(int sv[2], pid_t child, int log_io,
struct command_status *cstat);
static int dispatch_pending_signals(struct command_status *cstat);
static void forward_signals(int fd);
static void schedule_signal(int signo);
#ifdef SA_SIGINFO
@ -181,19 +179,19 @@ static struct signal_state {
int signo;
sigaction_t sa;
} saved_signals[] = {
{ SIGALRM },
{ SIGCHLD },
{ SIGCONT },
{ SIGHUP },
{ SIGINT },
{ SIGPIPE },
{ SIGQUIT },
{ SIGTERM },
{ SIGTSTP },
{ SIGTTIN },
{ SIGTTOU },
{ SIGUSR1 },
{ SIGUSR2 },
{ SIGALRM }, /* SAVED_SIGALRM */
{ SIGCHLD }, /* SAVED_SIGCHLD */
{ SIGCONT }, /* SAVED_SIGCONT */
{ SIGHUP }, /* SAVED_SIGHUP */
{ SIGINT }, /* SAVED_SIGINT */
{ SIGPIPE }, /* SAVED_SIGPIPE */
{ SIGQUIT }, /* SAVED_SIGQUIT */
{ SIGTERM }, /* SAVED_SIGTERM */
{ SIGTSTP }, /* SAVED_SIGTSTP */
{ SIGTTIN }, /* SAVED_SIGTTIN */
{ SIGTTOU }, /* SAVED_SIGTTOU */
{ SIGUSR1 }, /* SAVED_SIGUSR1 */
{ SIGUSR2 }, /* SAVED_SIGUSR2 */
{ -1 }
};
@ -244,6 +242,8 @@ sudo_execute(struct command_details *details, struct command_status *cstat)
pid_t child;
debug_decl(sudo_execute, SUDO_DEBUG_EXEC)
dispatch_pending_signals(cstat);
/* If running in background mode, fork and exit. */
if (ISSET(details->flags, CD_BACKGROUND)) {
switch (sudo_debug_fork()) {
@ -283,13 +283,6 @@ sudo_execute(struct command_details *details, struct command_status *cstat)
if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sv) == -1)
error(1, _("unable to create sockets"));
/*
* We use a pipe to atomically handle signal notification within
* the select() loop.
*/
if (pipe_nonblock(signal_pipe) != 0)
error(1, _("unable to create pipe"));
/*
* Signals to forward to the child process (excluding SIGALRM and SIGCHLD).
* We block all other signals while running the signal handler.
@ -494,7 +487,7 @@ do_tty_io:
}
/*
* Read signals on fd written to by handler().
* Read signals on signal_pipe written by handler().
* Returns -1 on error, 0 on child exit, else 1.
*/
static int
@ -609,6 +602,62 @@ dispatch_signals(int sv[2], pid_t child, int log_io, struct command_status *csta
debug_return_int(1);
}
/*
* Read pending signals on signale_pipe written by sudo_handler().
* Handles the case where the signal was sent to us before
* we have executed the command.
* Returns 1 if we should terminate, else 0.
*/
static int
dispatch_pending_signals(struct command_status *cstat)
{
ssize_t nread;
struct sigaction sa;
unsigned char signo = 0;
int rval = 0;
debug_decl(dispatch_pending_signals, SUDO_DEBUG_EXEC)
for (;;) {
nread = read(signal_pipe[0], &signo, sizeof(signo));
if (nread <= 0) {
/* It should not be possible to get EOF but just in case. */
if (nread == 0)
errno = ECONNRESET;
/* Restart if interrupted by signal so the pipe doesn't fill. */
if (errno == EINTR)
continue;
/* If pipe is empty, we are done. */
if (errno == EAGAIN)
break;
sudo_debug_printf(SUDO_DEBUG_ERROR, "error reading signal pipe %s",
strerror(errno));
cstat->type = CMD_ERRNO;
cstat->val = errno;
rval = 1;
break;
}
/* Take the first terminal signal. */
if (signo == SIGINT || signo == SIGQUIT) {
cstat->type = CMD_WSTATUS;
cstat->val = signo + 128;
rval = 1;
break;
}
}
/* Only stop if we haven't already been terminated. */
if (signo == SIGTSTP)
{
zero_bytes(&sa, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_handler = SIG_DFL;
sigaction(SIGTSTP, &sa, NULL);
if (kill(getpid(), SIGTSTP) != 0)
warning("kill(%d, SIGTSTP)", (int)getpid());
/* No need to reinstall SIGTSTP handler. */
}
debug_return_int(rval);
}
/*
* Forward signals in sigfwd_list to child listening on fd.
*/

View File

@ -95,6 +95,7 @@ struct plugin_container policy_plugin;
struct plugin_container_list io_plugins;
struct user_details user_details;
const char *list_user, *runas_user, *runas_group; /* extern for parse_args.c */
int signal_pipe[2];
static int sudo_mode;
/*
@ -106,6 +107,7 @@ static void sudo_check_suid(const char *path);
static char **get_user_info(struct user_details *);
static void command_info_to_details(char * const info[],
struct command_details *details);
static void setup_signals(void);
/* Policy plugin convenience functions. */
static int policy_open(struct plugin_container *plugin, char * const settings[],
@ -207,6 +209,8 @@ main(int argc, char *argv[], char *envp[])
errorx(1, _("unable to initialize policy plugin"));
}
setup_signals();
switch (sudo_mode & MODE_MASK) {
case MODE_VERSION:
policy_show_version(&policy_plugin, !user_details.uid);
@ -1245,3 +1249,43 @@ iolog_unlink(struct plugin_container *plugin)
debug_return;
}
static void
sudo_handler(int signo)
{
/*
* The pipe is non-blocking, if we overflow the kernel's pipe
* buffer we drop the signal. This is not a problem in practice.
*/
ignore_result(write(signal_pipe[1], &signo, sizeof(signo)));
}
/*
* Trap tty-generated signals so we can't be killed before calling
* the policy close function. The signal pipe will be checked
* in sudo_execute().
*/
static void
setup_signals(void)
{
struct sigaction sa;
debug_decl(setup_signals, SUDO_DEBUG_MAIN)
/*
* We use a pipe to atomically handle signal notification within
* the select() loop without races (we may not have pselect()).
*/
if (pipe_nonblock(signal_pipe) != 0)
error(1, _("unable to create pipe"));
/* XXX - should not install handler if ignored by default. */
memset(&sa, 0, sizeof(sa));
sigfillset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = sudo_handler;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
sigaction(SIGTSTP, &sa, NULL);
debug_return;
}

View File

@ -182,9 +182,10 @@ int tty_present(void);
void zero_bytes(volatile void *, size_t);
/* exec.c */
int pipe_nonblock(int fds[2]);
int sudo_execute(struct command_details *details, struct command_status *cstat);
void save_signals(void);
void restore_signals(void);
void save_signals(void);
/* term.c */
int term_cbreak(int);
@ -217,6 +218,7 @@ int run_command(struct command_details *details);
int os_init_common(int argc, char *argv[], char *envp[]);
extern const char *list_user, *runas_user, *runas_group;
extern struct user_details user_details;
extern int signal_pipe[2];
/* sudo_edit.c */
int sudo_edit(struct command_details *details);

View File

@ -23,13 +23,29 @@
#define SIGCONT_FG -2
#define SIGCONT_BG -3
/*
* Positions in saved_signals[]
*/
#define SAVED_SIGALRM 0
#define SAVED_SIGCHLD 1
#define SAVED_SIGCONT 2
#define SAVED_SIGHUP 3
#define SAVED_SIGINT 4
#define SAVED_SIGPIPE 5
#define SAVED_SIGQUIT 6
#define SAVED_SIGTERM 7
#define SAVED_SIGTSTP 8
#define SAVED_SIGTTIN 9
#define SAVED_SIGTTOU 10
#define SAVED_SIGUSR1 11
#define SAVED_SIGUSR2 12
/*
* Symbols shared between exec.c and exec_pty.c
*/
/* exec.c */
int sudo_execve(const char *path, char *const argv[], char *const envp[], int noexec);
int pipe_nonblock(int fds[2]);
extern volatile pid_t cmnd_pid;
/* exec_pty.c */
@ -47,7 +63,6 @@ void handler(int s);
void pty_close(struct command_status *cstat);
void pty_setup(uid_t uid, const char *tty, const char *utmp_user);
void terminate_command(pid_t pid, bool use_pgrp);
extern int signal_pipe[2];
/* utmp.c */
bool utmp_login(const char *from_line, const char *to_line, int ttyfd,