From: Todd C. Miller Date: Sat, 27 Feb 2010 21:53:56 +0000 (-0500) Subject: Refactor script_execve() a bit so that it can be used in non-script X-Git-Tag: SUDO_1_8_0~855 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f145264ee06a23c0b72f0856bb83b87fa9aa5053;p=sudo Refactor script_execve() a bit so that it can be used in non-script mode. Needs more cleanup. --- diff --git a/src/script.c b/src/script.c index a988e1017..04582db66 100644 --- a/src/script.c +++ b/src/script.c @@ -87,7 +87,7 @@ struct script_buf { char buf[16 * 1024]; }; -static int script_fds[3]; +static int script_fds[3] = { -1, -1, -1 }; static int ttyout; /* 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]; fd_set *fdsr, *fdsw; int rbac_enabled = 0; - int maxfd; + int log_io, maxfd; cstat->type = 0; /* XXX */ + log_io = !tq_empty(&io_plugins); + #ifdef HAVE_SELINUX rbac_enabled = is_selinux_enabled() > 0 && user_role != NULL; if (rbac_enabled) { selinux_prefork(user_role, user_type, script_fds[SFD_SLAVE]); - /* Re-open slave fd after it has been relabeled */ - close(script_fds[SFD_SLAVE]); - script_fds[SFD_SLAVE] = open(slavename, O_RDWR|O_NOCTTY, 0); - if (script_fds[SFD_SLAVE] == -1) + if (log_io) { + /* Re-open slave fd after it has been relabeled */ + close(script_fds[SFD_SLAVE]); + script_fds[SFD_SLAVE] = open(slavename, O_RDWR|O_NOCTTY, 0); + if (script_fds[SFD_SLAVE] == -1) error(1, "cannot open %s", slavename); + } } #endif - /* Are we the foreground process? */ 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); - sigaddset(&ttyblock, SIGINT); - sigaddset(&ttyblock, SIGQUIT); - sigaddset(&ttyblock, SIGTSTP); - sigaddset(&ttyblock, SIGTTIN); - sigaddset(&ttyblock, SIGTTOU); + /* + * 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"); - /* Setup signal handlers window size changes and child stop/exit */ zero_bytes(&sa, sizeof(sa)); sigemptyset(&sa.sa_mask); 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) */ sa.sa_handler = sigchild; @@ -322,6 +320,25 @@ script_execve(struct command_details *details, char *argv[], char *envp[], sa.sa_handler = SIG_IGN; 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. */ sa.sa_flags = 0; /* do not restart syscalls for these */ sa.sa_handler = handler; @@ -330,22 +347,12 @@ script_execve(struct command_details *details, char *argv[], char *envp[], sigaction(SIGINT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sigaction(SIGTSTP, &sa, NULL); -#if 0 /* XXX - keep? */ +#if 0 /* XXX - keep these? */ sigaction(SIGTTIN, &sa, NULL); sigaction(SIGTTOU, &sa, NULL); #endif - /* If stdout is not a tty we handle post-processing differently. */ - 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) { + if (log_io && foreground) { /* Copy terminal attrs from user tty -> pty slave. */ if (term_copy(script_fds[SFD_USERTTY], script_fds[SFD_SLAVE], ttyout)) { tty_initialized = 1; @@ -376,7 +383,17 @@ script_execve(struct command_details *details, char *argv[], char *envp[], close(sv[0]); if (exec_setup(details) == 0) { /* 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->val = errno; @@ -385,28 +402,31 @@ script_execve(struct command_details *details, char *argv[], char *envp[], } 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. */ maxfd = sv[0]; - if (maxfd < script_fds[SFD_MASTER]) - maxfd = script_fds[SFD_MASTER]; - if (maxfd < script_fds[SFD_USERTTY]) - maxfd = script_fds[SFD_USERTTY]; + + if (log_io) { + if (maxfd < script_fds[SFD_MASTER]) + 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 @@ -423,22 +443,31 @@ script_execve(struct command_details *details, char *argv[], char *envp[], recvsig = 0; } - if (input.off == input.len) - input.off = input.len = 0; - if (output.off == output.len) - output.off = output.len = 0; + /* If not logging I/O and child has exited we are done. */ + if (!log_io && recvsig == SIGCHLD) { + cstat->type = CMD_WSTATUS; + cstat->val = child_status; + break; + } zero_bytes(fdsw, 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)) - 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); + if (log_io) { + if (input.off == input.len) + input.off = input.len = 0; + if (output.off == output.len) + output.off = output.len = 0; + + if (ttymode == TERM_RAW && input.len != sizeof(input.buf)) + 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); if (relaysig) FD_SET(sv[0], fdsw); @@ -490,6 +519,9 @@ script_execve(struct command_details *details, char *argv[], char *envp[], break; /* should not happen */ } } + if (!log_io) + continue; + if (FD_ISSET(script_fds[SFD_USERTTY], fdsr)) { n = read(script_fds[SFD_USERTTY], 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). */ - n = fcntl(STDOUT_FILENO, F_GETFL, 0); - if (n != -1) { - n &= ~O_NONBLOCK; - (void) fcntl(STDOUT_FILENO, F_SETFL, n); - } - flush_output(&output); + if (log_io) { + /* Flush any remaining output to stdout (plugin already got it) */ + n = fcntl(STDOUT_FILENO, F_GETFL, 0); + if (n != -1) { + n &= ~O_NONBLOCK; + (void) fcntl(STDOUT_FILENO, F_SETFL, n); + } + flush_output(&output); #ifdef HAVE_STRSIGNAL - if (cstat->type == CMD_WSTATUS && WIFSIGNALED(cstat->val)) { - int signo = WTERMSIG(cstat->val); - if (signo && signo != SIGINT && signo != SIGPIPE) { - char *reason = strsignal(signo); - write(STDOUT_FILENO, reason, strlen(reason)); - if (WCOREDUMP(cstat->val)) - write(STDOUT_FILENO, " (core dumped)", 14); - write(STDOUT_FILENO, "\n", 1); + if (cstat->type == CMD_WSTATUS && WIFSIGNALED(cstat->val)) { + int signo = WTERMSIG(cstat->val); + if (signo && signo != SIGINT && signo != SIGPIPE) { + char *reason = strsignal(signo); + write(STDOUT_FILENO, reason, strlen(reason)); + if (WCOREDUMP(cstat->val)) + write(STDOUT_FILENO, " (core dumped)", 14); + write(STDOUT_FILENO, "\n", 1); + } } - } #endif - - do { - n = term_restore(script_fds[SFD_USERTTY], 0); - } while (!n && errno == EINTR); + do { + n = term_restore(script_fds[SFD_USERTTY], 0); + } while (!n && errno == EINTR); + } return cstat->type == CMD_ERRNO ? -1 : 0; } @@ -862,10 +895,15 @@ static void sigchild(int s) { pid_t pid; + int flags = WNOHANG; int serrno = errno; + if (!tq_empty(&io_plugins)) + flags |= WUNTRACED; + else + recvsig = SIGCHLD; do { - pid = waitpid(child, &child_status, WNOHANG | WUNTRACED); + pid = waitpid(child, &child_status, flags); } while (pid == -1 && errno == EINTR); if (pid == child) { if (WIFSTOPPED(child_status)) diff --git a/src/sudo.c b/src/sudo.c index 19054af30..7ff23eb4e 100644 --- a/src/sudo.c +++ b/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 * also no background support - * or selinux... */ /* If there are I/O plugins, allocate a pty and exec */ if (!tq_empty(&io_plugins)) { sudo_debug(8, "script mode"); 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) { case CMD_ERRNO: