Refactor script_execve() a bit so that it can be used in non-script
mode. Needs more cleanup.
This commit is contained in:
206
src/script.c
206
src/script.c
@@ -87,7 +87,7 @@ struct script_buf {
|
|||||||
char buf[16 * 1024];
|
char buf[16 * 1024];
|
||||||
};
|
};
|
||||||
|
|
||||||
static int script_fds[3];
|
static int script_fds[3] = { -1, -1, -1 };
|
||||||
static int ttyout;
|
static int ttyout;
|
||||||
|
|
||||||
/* XXX - use an array of signals instead of just a single variable */
|
/* XXX - use an array of signals instead of just a single variable */
|
||||||
@@ -279,40 +279,38 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
|
|||||||
int relaysig = 0, sv[2];
|
int relaysig = 0, sv[2];
|
||||||
fd_set *fdsr, *fdsw;
|
fd_set *fdsr, *fdsw;
|
||||||
int rbac_enabled = 0;
|
int rbac_enabled = 0;
|
||||||
int maxfd;
|
int log_io, maxfd;
|
||||||
|
|
||||||
cstat->type = 0; /* XXX */
|
cstat->type = 0; /* XXX */
|
||||||
|
|
||||||
|
log_io = !tq_empty(&io_plugins);
|
||||||
|
|
||||||
#ifdef HAVE_SELINUX
|
#ifdef HAVE_SELINUX
|
||||||
rbac_enabled = is_selinux_enabled() > 0 && user_role != NULL;
|
rbac_enabled = is_selinux_enabled() > 0 && user_role != NULL;
|
||||||
if (rbac_enabled) {
|
if (rbac_enabled) {
|
||||||
selinux_prefork(user_role, user_type, script_fds[SFD_SLAVE]);
|
selinux_prefork(user_role, user_type, script_fds[SFD_SLAVE]);
|
||||||
/* Re-open slave fd after it has been relabeled */
|
if (log_io) {
|
||||||
close(script_fds[SFD_SLAVE]);
|
/* Re-open slave fd after it has been relabeled */
|
||||||
script_fds[SFD_SLAVE] = open(slavename, O_RDWR|O_NOCTTY, 0);
|
close(script_fds[SFD_SLAVE]);
|
||||||
if (script_fds[SFD_SLAVE] == -1)
|
script_fds[SFD_SLAVE] = open(slavename, O_RDWR|O_NOCTTY, 0);
|
||||||
|
if (script_fds[SFD_SLAVE] == -1)
|
||||||
error(1, "cannot open %s", slavename);
|
error(1, "cannot open %s", slavename);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Are we the foreground process? */
|
|
||||||
parent = getpid(); /* so child can pass signals back to us */
|
parent = getpid(); /* so child can pass signals back to us */
|
||||||
foreground = tcgetpgrp(script_fds[SFD_USERTTY]) == parent;
|
|
||||||
|
|
||||||
/* So we can block tty-generated signals */
|
/*
|
||||||
sigemptyset(&ttyblock);
|
* We communicate with the child over a bi-directional pipe.
|
||||||
sigaddset(&ttyblock, SIGINT);
|
* Parent sends signal info to child and child sends back wait status.
|
||||||
sigaddset(&ttyblock, SIGQUIT);
|
*/
|
||||||
sigaddset(&ttyblock, SIGTSTP);
|
if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sv) != 0)
|
||||||
sigaddset(&ttyblock, SIGTTIN);
|
error(1, "cannot create sockets");
|
||||||
sigaddset(&ttyblock, SIGTTOU);
|
|
||||||
|
|
||||||
/* Setup signal handlers window size changes and child stop/exit */
|
|
||||||
zero_bytes(&sa, sizeof(sa));
|
zero_bytes(&sa, sizeof(sa));
|
||||||
sigemptyset(&sa.sa_mask);
|
sigemptyset(&sa.sa_mask);
|
||||||
sa.sa_flags = SA_RESTART;
|
sa.sa_flags = SA_RESTART;
|
||||||
sa.sa_handler = sigwinch;
|
|
||||||
sigaction(SIGWINCH, &sa, NULL);
|
|
||||||
|
|
||||||
/* XXX - now get command status via sv (still need to detect child death) */
|
/* XXX - now get command status via sv (still need to detect child death) */
|
||||||
sa.sa_handler = sigchild;
|
sa.sa_handler = sigchild;
|
||||||
@@ -322,6 +320,25 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
|
|||||||
sa.sa_handler = SIG_IGN;
|
sa.sa_handler = SIG_IGN;
|
||||||
sigaction(SIGPIPE, &sa, NULL);
|
sigaction(SIGPIPE, &sa, NULL);
|
||||||
|
|
||||||
|
if (log_io) {
|
||||||
|
sa.sa_handler = sigwinch;
|
||||||
|
sigaction(SIGWINCH, &sa, NULL);
|
||||||
|
|
||||||
|
/* So we can block tty-generated signals */
|
||||||
|
sigemptyset(&ttyblock);
|
||||||
|
sigaddset(&ttyblock, SIGINT);
|
||||||
|
sigaddset(&ttyblock, SIGQUIT);
|
||||||
|
sigaddset(&ttyblock, SIGTSTP);
|
||||||
|
sigaddset(&ttyblock, SIGTTIN);
|
||||||
|
sigaddset(&ttyblock, SIGTTOU);
|
||||||
|
|
||||||
|
/* Are we the foreground process? */
|
||||||
|
foreground = tcgetpgrp(script_fds[SFD_USERTTY]) == parent;
|
||||||
|
|
||||||
|
/* If stdout is not a tty we handle post-processing differently. */
|
||||||
|
ttyout = isatty(STDOUT_FILENO);
|
||||||
|
}
|
||||||
|
|
||||||
/* Signals to relay from parent to child. */
|
/* Signals to relay from parent to child. */
|
||||||
sa.sa_flags = 0; /* do not restart syscalls for these */
|
sa.sa_flags = 0; /* do not restart syscalls for these */
|
||||||
sa.sa_handler = handler;
|
sa.sa_handler = handler;
|
||||||
@@ -330,22 +347,12 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
|
|||||||
sigaction(SIGINT, &sa, NULL);
|
sigaction(SIGINT, &sa, NULL);
|
||||||
sigaction(SIGQUIT, &sa, NULL);
|
sigaction(SIGQUIT, &sa, NULL);
|
||||||
sigaction(SIGTSTP, &sa, NULL);
|
sigaction(SIGTSTP, &sa, NULL);
|
||||||
#if 0 /* XXX - keep? */
|
#if 0 /* XXX - keep these? */
|
||||||
sigaction(SIGTTIN, &sa, NULL);
|
sigaction(SIGTTIN, &sa, NULL);
|
||||||
sigaction(SIGTTOU, &sa, NULL);
|
sigaction(SIGTTOU, &sa, NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* If stdout is not a tty we handle post-processing differently. */
|
if (log_io && foreground) {
|
||||||
ttyout = isatty(STDOUT_FILENO);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We communicate with the child over a bi-directional pipe.
|
|
||||||
* Parent sends signal info to child and child sends back wait status.
|
|
||||||
*/
|
|
||||||
if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sv) != 0)
|
|
||||||
error(1, "cannot create sockets");
|
|
||||||
|
|
||||||
if (foreground) {
|
|
||||||
/* Copy terminal attrs from user tty -> pty slave. */
|
/* Copy terminal attrs from user tty -> pty slave. */
|
||||||
if (term_copy(script_fds[SFD_USERTTY], script_fds[SFD_SLAVE], ttyout)) {
|
if (term_copy(script_fds[SFD_USERTTY], script_fds[SFD_SLAVE], ttyout)) {
|
||||||
tty_initialized = 1;
|
tty_initialized = 1;
|
||||||
@@ -376,7 +383,17 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
|
|||||||
close(sv[0]);
|
close(sv[0]);
|
||||||
if (exec_setup(details) == 0) {
|
if (exec_setup(details) == 0) {
|
||||||
/* headed for execve() */
|
/* headed for execve() */
|
||||||
script_child(details->command, argv, envp, sv[1], rbac_enabled);
|
if (log_io)
|
||||||
|
script_child(details->command, argv, envp, sv[1], rbac_enabled);
|
||||||
|
else {
|
||||||
|
#ifdef HAVE_SELINUX
|
||||||
|
if (rbac_enabled)
|
||||||
|
selinux_execve(details->command, argv, envp);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
execve(details->command, argv, envp);
|
||||||
|
}
|
||||||
|
/* XXX - fallback to sh */
|
||||||
}
|
}
|
||||||
cstat->type = CMD_ERRNO;
|
cstat->type = CMD_ERRNO;
|
||||||
cstat->val = errno;
|
cstat->val = errno;
|
||||||
@@ -385,28 +402,31 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
|
|||||||
}
|
}
|
||||||
close(sv[1]);
|
close(sv[1]);
|
||||||
|
|
||||||
n = fcntl(script_fds[SFD_MASTER], F_GETFL, 0);
|
|
||||||
if (n != -1) {
|
|
||||||
n |= O_NONBLOCK;
|
|
||||||
(void) fcntl(script_fds[SFD_MASTER], F_SETFL, n);
|
|
||||||
}
|
|
||||||
n = fcntl(script_fds[SFD_USERTTY], F_GETFL, 0);
|
|
||||||
if (n != -1) {
|
|
||||||
n |= O_NONBLOCK;
|
|
||||||
(void) fcntl(script_fds[SFD_USERTTY], F_SETFL, n);
|
|
||||||
}
|
|
||||||
n = fcntl(STDOUT_FILENO, F_GETFL, 0);
|
|
||||||
if (n != -1) {
|
|
||||||
n |= O_NONBLOCK;
|
|
||||||
(void) fcntl(STDOUT_FILENO, F_SETFL, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Max fd we will be selecting on. */
|
/* Max fd we will be selecting on. */
|
||||||
maxfd = sv[0];
|
maxfd = sv[0];
|
||||||
if (maxfd < script_fds[SFD_MASTER])
|
|
||||||
maxfd = script_fds[SFD_MASTER];
|
if (log_io) {
|
||||||
if (maxfd < script_fds[SFD_USERTTY])
|
if (maxfd < script_fds[SFD_MASTER])
|
||||||
maxfd = script_fds[SFD_USERTTY];
|
maxfd = script_fds[SFD_MASTER];
|
||||||
|
if (maxfd < script_fds[SFD_USERTTY])
|
||||||
|
maxfd = script_fds[SFD_USERTTY];
|
||||||
|
|
||||||
|
n = fcntl(script_fds[SFD_MASTER], F_GETFL, 0);
|
||||||
|
if (n != -1) {
|
||||||
|
n |= O_NONBLOCK;
|
||||||
|
(void) fcntl(script_fds[SFD_MASTER], F_SETFL, n);
|
||||||
|
}
|
||||||
|
n = fcntl(script_fds[SFD_USERTTY], F_GETFL, 0);
|
||||||
|
if (n != -1) {
|
||||||
|
n |= O_NONBLOCK;
|
||||||
|
(void) fcntl(script_fds[SFD_USERTTY], F_SETFL, n);
|
||||||
|
}
|
||||||
|
n = fcntl(STDOUT_FILENO, F_GETFL, 0);
|
||||||
|
if (n != -1) {
|
||||||
|
n |= O_NONBLOCK;
|
||||||
|
(void) fcntl(STDOUT_FILENO, F_SETFL, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In the event loop we pass input from user tty to master
|
* In the event loop we pass input from user tty to master
|
||||||
@@ -423,22 +443,31 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
|
|||||||
recvsig = 0;
|
recvsig = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.off == input.len)
|
/* If not logging I/O and child has exited we are done. */
|
||||||
input.off = input.len = 0;
|
if (!log_io && recvsig == SIGCHLD) {
|
||||||
if (output.off == output.len)
|
cstat->type = CMD_WSTATUS;
|
||||||
output.off = output.len = 0;
|
cstat->val = child_status;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
zero_bytes(fdsw, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
|
zero_bytes(fdsw, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
|
||||||
zero_bytes(fdsr, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
|
zero_bytes(fdsr, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
|
||||||
|
|
||||||
if (ttymode == TERM_RAW && input.len != sizeof(input.buf))
|
if (log_io) {
|
||||||
FD_SET(script_fds[SFD_USERTTY], fdsr);
|
if (input.off == input.len)
|
||||||
if (output.len != sizeof(output.buf))
|
input.off = input.len = 0;
|
||||||
FD_SET(script_fds[SFD_MASTER], fdsr);
|
if (output.off == output.len)
|
||||||
if (output.len > output.off)
|
output.off = output.len = 0;
|
||||||
FD_SET(STDOUT_FILENO, fdsw);
|
|
||||||
if (input.len > input.off)
|
if (ttymode == TERM_RAW && input.len != sizeof(input.buf))
|
||||||
FD_SET(script_fds[SFD_MASTER], fdsw);
|
FD_SET(script_fds[SFD_USERTTY], fdsr);
|
||||||
|
if (output.len != sizeof(output.buf))
|
||||||
|
FD_SET(script_fds[SFD_MASTER], fdsr);
|
||||||
|
if (output.len > output.off)
|
||||||
|
FD_SET(STDOUT_FILENO, fdsw);
|
||||||
|
if (input.len > input.off)
|
||||||
|
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);
|
FD_SET(sv[0], fdsw);
|
||||||
@@ -490,6 +519,9 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
|
|||||||
break; /* should not happen */
|
break; /* should not happen */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!log_io)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (FD_ISSET(script_fds[SFD_USERTTY], fdsr)) {
|
if (FD_ISSET(script_fds[SFD_USERTTY], fdsr)) {
|
||||||
n = read(script_fds[SFD_USERTTY], input.buf + input.len,
|
n = read(script_fds[SFD_USERTTY], input.buf + input.len,
|
||||||
sizeof(input.buf) - input.len);
|
sizeof(input.buf) - input.len);
|
||||||
@@ -546,30 +578,31 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Flush any remaining output to stdout (already sent to I/O modules). */
|
if (log_io) {
|
||||||
n = fcntl(STDOUT_FILENO, F_GETFL, 0);
|
/* Flush any remaining output to stdout (plugin already got it) */
|
||||||
if (n != -1) {
|
n = fcntl(STDOUT_FILENO, F_GETFL, 0);
|
||||||
n &= ~O_NONBLOCK;
|
if (n != -1) {
|
||||||
(void) fcntl(STDOUT_FILENO, F_SETFL, n);
|
n &= ~O_NONBLOCK;
|
||||||
}
|
(void) fcntl(STDOUT_FILENO, F_SETFL, n);
|
||||||
flush_output(&output);
|
}
|
||||||
|
flush_output(&output);
|
||||||
|
|
||||||
#ifdef HAVE_STRSIGNAL
|
#ifdef HAVE_STRSIGNAL
|
||||||
if (cstat->type == CMD_WSTATUS && WIFSIGNALED(cstat->val)) {
|
if (cstat->type == CMD_WSTATUS && WIFSIGNALED(cstat->val)) {
|
||||||
int signo = WTERMSIG(cstat->val);
|
int signo = WTERMSIG(cstat->val);
|
||||||
if (signo && signo != SIGINT && signo != SIGPIPE) {
|
if (signo && signo != SIGINT && signo != SIGPIPE) {
|
||||||
char *reason = strsignal(signo);
|
char *reason = strsignal(signo);
|
||||||
write(STDOUT_FILENO, reason, strlen(reason));
|
write(STDOUT_FILENO, reason, strlen(reason));
|
||||||
if (WCOREDUMP(cstat->val))
|
if (WCOREDUMP(cstat->val))
|
||||||
write(STDOUT_FILENO, " (core dumped)", 14);
|
write(STDOUT_FILENO, " (core dumped)", 14);
|
||||||
write(STDOUT_FILENO, "\n", 1);
|
write(STDOUT_FILENO, "\n", 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
do {
|
||||||
do {
|
n = term_restore(script_fds[SFD_USERTTY], 0);
|
||||||
n = term_restore(script_fds[SFD_USERTTY], 0);
|
} while (!n && errno == EINTR);
|
||||||
} while (!n && errno == EINTR);
|
}
|
||||||
|
|
||||||
return cstat->type == CMD_ERRNO ? -1 : 0;
|
return cstat->type == CMD_ERRNO ? -1 : 0;
|
||||||
}
|
}
|
||||||
@@ -862,10 +895,15 @@ static void
|
|||||||
sigchild(int s)
|
sigchild(int s)
|
||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
int flags = WNOHANG;
|
||||||
int serrno = errno;
|
int serrno = errno;
|
||||||
|
|
||||||
|
if (!tq_empty(&io_plugins))
|
||||||
|
flags |= WUNTRACED;
|
||||||
|
else
|
||||||
|
recvsig = SIGCHLD;
|
||||||
do {
|
do {
|
||||||
pid = waitpid(child, &child_status, WNOHANG | WUNTRACED);
|
pid = waitpid(child, &child_status, flags);
|
||||||
} while (pid == -1 && errno == EINTR);
|
} while (pid == -1 && errno == EINTR);
|
||||||
if (pid == child) {
|
if (pid == child) {
|
||||||
if (WIFSTOPPED(child_status))
|
if (WIFSTOPPED(child_status))
|
||||||
|
82
src/sudo.c
82
src/sudo.c
@@ -746,94 +746,14 @@ run_command(struct command_details *details, char *argv[], char *envp[])
|
|||||||
/*
|
/*
|
||||||
* XXX - missing closefrom(), may not be possible in new scheme
|
* XXX - missing closefrom(), may not be possible in new scheme
|
||||||
* also no background support
|
* also no background support
|
||||||
* or selinux...
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* If there are I/O plugins, allocate a pty and exec */
|
/* If there are I/O plugins, allocate a pty and exec */
|
||||||
if (!tq_empty(&io_plugins)) {
|
if (!tq_empty(&io_plugins)) {
|
||||||
sudo_debug(8, "script mode");
|
sudo_debug(8, "script mode");
|
||||||
script_setup(details->euid);
|
script_setup(details->euid);
|
||||||
script_execve(details, argv, envp, &cstat);
|
|
||||||
} else {
|
|
||||||
pid_t child, pid;
|
|
||||||
int nready, sv[2];
|
|
||||||
ssize_t nread;
|
|
||||||
sigaction_t sa;
|
|
||||||
fd_set *fdsr;
|
|
||||||
|
|
||||||
zero_bytes(&sa, sizeof(sa));
|
|
||||||
sigemptyset(&sa.sa_mask);
|
|
||||||
sa.sa_flags = SA_RESTART;
|
|
||||||
|
|
||||||
/* Want select() to be interrupted when child dies. */
|
|
||||||
sa.sa_handler = sigchild;
|
|
||||||
sigaction(SIGCHLD, &sa, NULL);
|
|
||||||
|
|
||||||
/* Ignore SIGPIPE from other end of socketpair. */
|
|
||||||
sa.sa_handler = SIG_IGN;
|
|
||||||
sigaction(SIGPIPE, &sa, NULL);
|
|
||||||
|
|
||||||
if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sv) != 0)
|
|
||||||
error(1, "cannot create sockets");
|
|
||||||
|
|
||||||
child = fork();
|
|
||||||
if (child == -1)
|
|
||||||
error(1, "unable to fork");
|
|
||||||
|
|
||||||
if (child == 0) {
|
|
||||||
/* child */
|
|
||||||
close(sv[0]);
|
|
||||||
if (exec_setup(details) == 0) {
|
|
||||||
/* XXX - fallback via /bin/sh */
|
|
||||||
execve(details->command, argv, envp);
|
|
||||||
}
|
|
||||||
cstat.type = CMD_ERRNO;
|
|
||||||
cstat.val = errno;
|
|
||||||
write(sv[1], &cstat, sizeof(cstat));
|
|
||||||
_exit(1);
|
|
||||||
}
|
|
||||||
close(sv[1]);
|
|
||||||
sudo_debug(9, "waiting for child");
|
|
||||||
|
|
||||||
/* wait for child to complete or for data on sv[0] */
|
|
||||||
fdsr = (fd_set *)emalloc2(howmany(sv[0] + 1, NFDBITS), sizeof(fd_mask));
|
|
||||||
zero_bytes(fdsr, howmany(sv[0] + 1, NFDBITS) * sizeof(fd_mask));
|
|
||||||
FD_SET(sv[0], fdsr);
|
|
||||||
for (;;) {
|
|
||||||
/* XXX - we may get SIGCILD before the wait status from the child */
|
|
||||||
if (sigchld) {
|
|
||||||
/* Note: this is the child, not the command we are waiting on */
|
|
||||||
sigchld = 0;
|
|
||||||
do {
|
|
||||||
pid = waitpid(child, &cstat.val, WNOHANG);
|
|
||||||
if (pid == child)
|
|
||||||
cstat.type = CMD_WSTATUS;
|
|
||||||
} while (pid == -1 && errno == EINTR);
|
|
||||||
if (cstat.type == CMD_WSTATUS) {
|
|
||||||
/* command terminated, we're done */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nready = select(sv[0] + 1, fdsr, NULL, NULL, NULL);
|
|
||||||
if (nready == -1) {
|
|
||||||
if (errno == EINTR)
|
|
||||||
continue;
|
|
||||||
error(1, "select failed");
|
|
||||||
}
|
|
||||||
if (FD_ISSET(sv[0], fdsr)) {
|
|
||||||
/* read child status */
|
|
||||||
nread = recv(sv[0], &cstat, sizeof(cstat), 0);
|
|
||||||
if (nread == -1) {
|
|
||||||
/* XXX - could be interrupted by SIGCHLD */
|
|
||||||
if (errno == EINTR)
|
|
||||||
continue;
|
|
||||||
/* XXX - init cstat for failure case */
|
|
||||||
}
|
|
||||||
sudo_debug(9, "cmdtype %d, val %d", cstat.type, cstat.val);
|
|
||||||
break; /* XXX */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
script_execve(details, argv, envp, &cstat);
|
||||||
|
|
||||||
switch (cstat.type) {
|
switch (cstat.type) {
|
||||||
case CMD_ERRNO:
|
case CMD_ERRNO:
|
||||||
|
Reference in New Issue
Block a user