Wire up SIGALRM handler

Set close on exec flag for child side of the socketpair
Fix signal handling when not doing I/O logging
This commit is contained in:
Todd C. Miller
2010-03-06 14:34:23 -05:00
parent d73f580f5a
commit 4e938c0074
2 changed files with 72 additions and 44 deletions

View File

@@ -111,6 +111,7 @@ static void script_run(const char *path, char *argv[], char *envp[], int);
static void sigchild(int s); static void sigchild(int s);
static void sigwinch(int s); static void sigwinch(int s);
static void sync_winsize(int src, int dst); static void sync_winsize(int src, int dst);
static void deliver_signal(pid_t pid, int signo);
/* sudo.c */ /* sudo.c */
extern struct plugin_container_list io_plugins; extern struct plugin_container_list io_plugins;
@@ -339,6 +340,10 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
sa.sa_handler = sigchild; sa.sa_handler = sigchild;
sigaction(SIGCHLD, &sa, NULL); sigaction(SIGCHLD, &sa, NULL);
/* Catch SIGALRM for command timeout */
sa.sa_handler = handler;
sigaction(SIGALRM, &sa, NULL);
/* Ignore SIGPIPE from other end of socketpair. */ /* Ignore SIGPIPE from other end of socketpair. */
sa.sa_handler = SIG_IGN; sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sa, NULL); sigaction(SIGPIPE, &sa, NULL);
@@ -392,6 +397,10 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
error(1, "Can't set terminal to raw mode"); error(1, "Can't set terminal to raw mode");
} }
/* Set command timeout if specified. */
if (ISSET(details->flags, CD_SET_TIMEOUT))
alarm(details->timeout);
/* /*
* Child will run the command in the pty, parent will pass data * Child will run the command in the pty, parent will pass data
* to and from pty. * to and from pty.
@@ -404,6 +413,7 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
case 0: case 0:
/* child */ /* child */
close(sv[0]); close(sv[0]);
fcntl(sv[1], F_SETFD, FD_CLOEXEC);
if (exec_setup(details) == 0) { if (exec_setup(details) == 0) {
/* headed for execve() */ /* headed for execve() */
if (log_io) if (log_io)
@@ -459,7 +469,7 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
zero_bytes(&input, sizeof(input)); zero_bytes(&input, sizeof(input));
zero_bytes(&output, sizeof(output)); zero_bytes(&output, sizeof(output));
for (;;) { for (;;) {
/* XXX - racey */ /* XXX - racey */
if (!relaysig && recvsig != SIGCHLD) { if (!relaysig && recvsig != SIGCHLD) {
relaysig = recvsig; relaysig = recvsig;
recvsig = 0; recvsig = 0;
@@ -491,8 +501,14 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
FD_SET(script_fds[SFD_MASTER], fdsw); FD_SET(script_fds[SFD_MASTER], fdsw);
} }
FD_SET(sv[0], fdsr); FD_SET(sv[0], fdsr);
if (relaysig) if (relaysig) {
FD_SET(sv[0], fdsw); if (log_io) {
FD_SET(sv[0], fdsw);
} else {
/* nothing listening on sv[0], send directly */
deliver_signal(child, relaysig);
}
}
nready = select(maxfd + 1, fdsr, fdsw, NULL, NULL); nready = select(maxfd + 1, fdsr, fdsw, NULL, NULL);
if (nready == -1) { if (nready == -1) {
@@ -506,7 +522,7 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
if (n == -1) { if (n == -1) {
if (errno == EINTR) if (errno == EINTR)
continue; continue;
if (errno != EAGAIN) { if (log_io && errno != EAGAIN) {
/* Did the other end of the pipe go away? */ /* Did the other end of the pipe go away? */
cstat->type = CMD_ERRNO; cstat->type = CMD_ERRNO;
cstat->val = errno; cstat->val = errno;
@@ -629,14 +645,61 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
return cstat->type == CMD_ERRNO ? -1 : 0; return cstat->type == CMD_ERRNO ? -1 : 0;
} }
static void
deliver_signal(pid_t pid, int signo)
{
int status;
/* Handle signal from parent. */
sudo_debug(8, "signal %d from parent", signo);
switch (signo) {
case SIGKILL:
_exit(1); /* XXX */
/* NOTREACHED */
case SIGHUP:
case SIGTERM:
case SIGINT:
case SIGQUIT:
case SIGTSTP:
/* relay signal to child */
killpg(pid, signo);
break;
case SIGALRM:
/* command time out, kill child with increasing urgency */
killpg(pid, SIGHUP);
sleep(1);
killpg(pid, SIGTERM);
sleep(1);
killpg(pid, SIGKILL);
break;
case SIGUSR1:
/* foreground process, grant it controlling tty. */
do {
status = tcsetpgrp(script_fds[SFD_SLAVE], pid);
} while (status == -1 && errno == EINTR);
killpg(pid, SIGCONT);
break;
case SIGUSR2:
/* background process, I take controlling tty. */
do {
status = tcsetpgrp(script_fds[SFD_SLAVE], getpid());
} while (status == -1 && errno == EINTR);
killpg(pid, SIGCONT);
break;
default:
warningx("unexpected signal from child: %d", signo);
break;
}
}
int int
script_child(const char *path, char *argv[], char *envp[], int backchannel, int rbac) script_child(const char *path, char *argv[], char *envp[], int backchannel, int rbac)
{ {
struct command_status cstat; struct command_status cstat;
fd_set *fdsr; fd_set *fdsr;
sigaction_t sa; sigaction_t sa;
pid_t pid, self = getpid(); pid_t pid;
int n, signo, status; int n, status;
recvsig = 0; recvsig = 0;
@@ -649,8 +712,8 @@ script_child(const char *path, char *argv[], char *envp[], int backchannel, int
sigemptyset(&sa.sa_mask); sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART; sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_DFL; sa.sa_handler = SIG_DFL;
sigaction(SIGCONT, &sa, NULL);
sigaction(SIGWINCH, &sa, NULL); sigaction(SIGWINCH, &sa, NULL);
sigaction(SIGALRM, &sa, NULL);
/* Ignore any SIGTT{IN,OU} or SIGPIPE we get. */ /* Ignore any SIGTT{IN,OU} or SIGPIPE we get. */
sa.sa_handler = SIG_IGN; sa.sa_handler = SIG_IGN;
@@ -774,7 +837,7 @@ script_child(const char *path, char *argv[], char *envp[], int backchannel, int
error(1, "select failed"); error(1, "select failed");
} }
/* read child status */ /* read command from backchannel, should be a signal */
n = recv(backchannel, &cstat, sizeof(cstat), 0); n = recv(backchannel, &cstat, sizeof(cstat), 0);
if (n == -1) { if (n == -1) {
if (errno == EINTR) if (errno == EINTR)
@@ -786,40 +849,7 @@ script_child(const char *path, char *argv[], char *envp[], int backchannel, int
warningx("unexpected reply type on backchannel: %d", cstat.type); warningx("unexpected reply type on backchannel: %d", cstat.type);
continue; continue;
} }
signo = cstat.val; deliver_signal(child, cstat.val);
/* Handle signal from parent. */
sudo_debug(8, "signal %d from parent", signo);
switch (signo) {
case SIGKILL:
_exit(1); /* XXX */
/* NOTREACHED */
case SIGHUP:
case SIGTERM:
case SIGINT:
case SIGQUIT:
case SIGTSTP:
/* relay signal to child */
killpg(child, signo);
break;
case SIGUSR1:
/* foreground process, grant it controlling tty. */
do {
status = tcsetpgrp(script_fds[SFD_SLAVE], child);
} while (status == -1 && errno == EINTR);
killpg(child, SIGCONT);
break;
case SIGUSR2:
/* background process, I take controlling tty. */
do {
status = tcsetpgrp(script_fds[SFD_SLAVE], self);
} while (status == -1 && errno == EINTR);
killpg(child, SIGCONT);
break;
default:
warningx("unexpected signal from child: %d", signo);
break;
}
} }
_exit(1); /* XXX */ _exit(1); /* XXX */

View File

@@ -689,8 +689,6 @@ exec_setup(struct command_details *details)
} }
if (ISSET(details->flags, CD_SET_UMASK)) if (ISSET(details->flags, CD_SET_UMASK))
(void) umask(details->umask); (void) umask(details->umask);
if (ISSET(details->flags, CD_SET_TIMEOUT))
alarm(details->timeout);
if (details->chroot) { if (details->chroot) {
if (chroot(details->chroot) != 0 || chdir("/") != 0) { if (chroot(details->chroot) != 0 || chdir("/") != 0) {
warning("unable to change root to %s", details->chroot); warning("unable to change root to %s", details->chroot);