--- embedaddon/sudo/src/exec_pty.c 2012/02/21 16:23:02 1.1 +++ embedaddon/sudo/src/exec_pty.c 2012/05/29 12:26:49 1.1.1.2 @@ -85,15 +85,13 @@ struct io_buffer { int off; /* write position (how much already consumed) */ int rfd; /* reader (producer) */ int wfd; /* writer (consumer) */ - int (*action)(const char *buf, unsigned int len); + bool (*action)(const char *buf, unsigned int len); char buf[16 * 1024]; }; static char slavename[PATH_MAX]; -static int foreground; +static bool foreground, pipeline, tty_initialized; static int io_fds[6] = { -1, -1, -1, -1, -1, -1}; -static int pipeline = FALSE; -static int tty_initialized; static int ttymode = TERM_COOKED; static pid_t ppgrp, child, child_pgrp; static sigset_t ttyblock; @@ -101,11 +99,12 @@ static struct io_buffer *iobufs; static void flush_output(void); static int exec_monitor(struct command_details *details, int backchannel); -static void exec_pty(struct command_details *detail); +static void exec_pty(struct command_details *detail, int *errfd); static void sigwinch(int s); static void sync_ttysize(int src, int dst); -static void deliver_signal(pid_t pid, int signo); +static void deliver_signal(pid_t pid, int signo, bool from_parent); static int safe_close(int fd); +static void check_foreground(void); /* * Cleanup hook for error()/errorx() @@ -113,12 +112,19 @@ static int safe_close(int fd); void cleanup(int gotsignal) { - if (!tq_empty(&io_plugins)) - term_restore(io_fds[SFD_USERTTY], 0); + debug_decl(cleanup, SUDO_DEBUG_EXEC); + + if (!tq_empty(&io_plugins) && io_fds[SFD_USERTTY] != -1) { + check_foreground(); + if (foreground) + term_restore(io_fds[SFD_USERTTY], 0); + } #ifdef HAVE_SELINUX selinux_restore_tty(); #endif utmp_logout(slavename, 0); /* XXX - only if CD_SET_UTMP */ + + debug_return; } /* @@ -129,6 +135,8 @@ cleanup(int gotsignal) void pty_setup(uid_t uid, const char *tty, const char *utmp_user) { + debug_decl(pty_setup, SUDO_DEBUG_EXEC); + io_fds[SFD_USERTTY] = open(_PATH_TTY, O_RDWR|O_NOCTTY, 0); if (io_fds[SFD_USERTTY] != -1) { if (!get_pty(&io_fds[SFD_MASTER], &io_fds[SFD_SLAVE], @@ -138,121 +146,123 @@ pty_setup(uid_t uid, const char *tty, const char *utmp if (utmp_user != NULL) utmp_login(tty, slavename, io_fds[SFD_SLAVE], utmp_user); } + + debug_return; } /* Call I/O plugin tty input log method. */ -static int +static bool log_ttyin(const char *buf, unsigned int n) { struct plugin_container *plugin; sigset_t omask; - int rval = TRUE; + bool rval = true; + debug_decl(log_ttyin, SUDO_DEBUG_EXEC); sigprocmask(SIG_BLOCK, &ttyblock, &omask); - tq_foreach_fwd(&io_plugins, plugin) { if (plugin->u.io->log_ttyin) { if (!plugin->u.io->log_ttyin(buf, n)) { - rval = FALSE; + rval = false; break; } } } - sigprocmask(SIG_SETMASK, &omask, NULL); - return rval; + + debug_return_bool(rval); } /* Call I/O plugin stdin log method. */ -static int +static bool log_stdin(const char *buf, unsigned int n) { struct plugin_container *plugin; sigset_t omask; - int rval = TRUE; + bool rval = true; + debug_decl(log_stdin, SUDO_DEBUG_EXEC); sigprocmask(SIG_BLOCK, &ttyblock, &omask); - tq_foreach_fwd(&io_plugins, plugin) { if (plugin->u.io->log_stdin) { if (!plugin->u.io->log_stdin(buf, n)) { - rval = FALSE; + rval = false; break; } } } - sigprocmask(SIG_SETMASK, &omask, NULL); - return rval; + + debug_return_bool(rval); } /* Call I/O plugin tty output log method. */ -static int +static bool log_ttyout(const char *buf, unsigned int n) { struct plugin_container *plugin; sigset_t omask; - int rval = TRUE; + bool rval = true; + debug_decl(log_ttyout, SUDO_DEBUG_EXEC); sigprocmask(SIG_BLOCK, &ttyblock, &omask); - tq_foreach_fwd(&io_plugins, plugin) { if (plugin->u.io->log_ttyout) { if (!plugin->u.io->log_ttyout(buf, n)) { - rval = FALSE; + rval = false; break; } } } - sigprocmask(SIG_SETMASK, &omask, NULL); - return rval; + + debug_return_bool(rval); } /* Call I/O plugin stdout log method. */ -static int +static bool log_stdout(const char *buf, unsigned int n) { struct plugin_container *plugin; sigset_t omask; - int rval = TRUE; + bool rval = true; + debug_decl(log_stdout, SUDO_DEBUG_EXEC); sigprocmask(SIG_BLOCK, &ttyblock, &omask); - tq_foreach_fwd(&io_plugins, plugin) { if (plugin->u.io->log_stdout) { if (!plugin->u.io->log_stdout(buf, n)) { - rval = FALSE; + rval = false; break; } } } - sigprocmask(SIG_SETMASK, &omask, NULL); - return rval; + + debug_return_bool(rval); } /* Call I/O plugin stderr log method. */ -static int +static bool log_stderr(const char *buf, unsigned int n) { struct plugin_container *plugin; sigset_t omask; - int rval = TRUE; + bool rval = true; + debug_decl(log_stderr, SUDO_DEBUG_EXEC); sigprocmask(SIG_BLOCK, &ttyblock, &omask); - tq_foreach_fwd(&io_plugins, plugin) { if (plugin->u.io->log_stderr) { if (!plugin->u.io->log_stderr(buf, n)) { - rval = FALSE; + rval = false; break; } } } - sigprocmask(SIG_SETMASK, &omask, NULL); - return rval; + + debug_return_bool(rval); } /* @@ -263,15 +273,19 @@ log_stderr(const char *buf, unsigned int n) static void check_foreground(void) { + debug_decl(check_foreground, SUDO_DEBUG_EXEC); + if (io_fds[SFD_USERTTY] != -1) { foreground = tcgetpgrp(io_fds[SFD_USERTTY]) == ppgrp; if (foreground && !tty_initialized) { if (term_copy(io_fds[SFD_USERTTY], io_fds[SFD_SLAVE])) { - tty_initialized = 1; + tty_initialized = true; sync_ttysize(io_fds[SFD_USERTTY], io_fds[SFD_SLAVE]); } } } + + debug_return; } /* @@ -284,6 +298,7 @@ suspend_parent(int signo) { sigaction_t sa, osa; int n, oldmode = ttymode, rval = 0; + debug_decl(suspend_parent, SUDO_DEBUG_EXEC); switch (signo) { case SIGTTOU: @@ -319,9 +334,12 @@ suspend_parent(int signo) } /* Suspend self and continue child when we resume. */ + zero_bytes(&sa, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_INTERRUPT; /* do not restart syscalls */ sa.sa_handler = SIG_DFL; sigaction(signo, &sa, &osa); - sudo_debug(8, "kill parent %d", signo); + sudo_debug_printf(SUDO_DEBUG_INFO, "kill parent %d", signo); if (killpg(ppgrp, signo) != 0) warning("killpg(%d, %d)", (int)ppgrp, signo); @@ -332,7 +350,7 @@ suspend_parent(int signo) * Only modify term if we are foreground process and either * the old tty mode was not cooked or child got SIGTT{IN,OU} */ - sudo_debug(8, "parent is in %s, ttymode %d -> %d", + sudo_debug_printf(SUDO_DEBUG_INFO, "parent is in %s, ttymode %d -> %d", foreground ? "foreground" : "background", oldmode, ttymode); if (ttymode != TERM_COOKED) { @@ -352,44 +370,55 @@ suspend_parent(int signo) break; } - return rval; + debug_return_int(rval); } /* * Kill child with increasing urgency. */ void -terminate_child(pid_t pid, int use_pgrp) +terminate_child(pid_t pid, bool use_pgrp) { + debug_decl(terminate_child, SUDO_DEBUG_EXEC); + /* * 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; } static struct io_buffer * -io_buf_new(int rfd, int wfd, int (*action)(const char *, unsigned int), +io_buf_new(int rfd, int wfd, bool (*action)(const char *, unsigned int), struct io_buffer *head) { struct io_buffer *iob; + debug_decl(io_buf_new, SUDO_DEBUG_EXEC); - iob = emalloc(sizeof(*iob)); - zero_bytes(iob, sizeof(*iob)); + iob = ecalloc(1, sizeof(*iob)); iob->rfd = rfd; iob->wfd = wfd; iob->action = action; iob->next = head; - return iob; + + debug_return_ptr(iob); } /* @@ -401,6 +430,7 @@ perform_io(fd_set *fdsr, fd_set *fdsw, struct command_ { struct io_buffer *iob; int n, errors = 0; + debug_decl(perform_io, SUDO_DEBUG_EXEC); for (iob = iobufs; iob; iob = iob->next) { if (iob->rfd != -1 && FD_ISSET(iob->rfd, fdsr)) { @@ -410,21 +440,27 @@ perform_io(fd_set *fdsr, fd_set *fdsw, struct command_ } while (n == -1 && errno == EINTR); switch (n) { case -1: - if (errno == EAGAIN) - break; - if (errno != ENXIO && errno != EBADF) { - errors++; - break; + if (errno != EAGAIN) { + /* treat read error as fatal and close the fd */ + sudo_debug_printf(SUDO_DEBUG_ERROR, + "error reading fd %d: %s", iob->rfd, + strerror(errno)); + safe_close(iob->rfd); + iob->rfd = -1; } - /* FALLTHROUGH */ + break; case 0: /* got EOF or pty has gone away */ + sudo_debug_printf(SUDO_DEBUG_INFO, + "read EOF from fd %d", iob->rfd); safe_close(iob->rfd); iob->rfd = -1; break; default: + sudo_debug_printf(SUDO_DEBUG_INFO, + "read %d bytes from fd %d", n, iob->rfd); if (!iob->action(iob->buf + iob->len, n)) - terminate_child(child, TRUE); + terminate_child(child, true); iob->len += n; break; } @@ -435,7 +471,10 @@ perform_io(fd_set *fdsr, fd_set *fdsw, struct command_ iob->len - iob->off); } while (n == -1 && errno == EINTR); if (n == -1) { - if (errno == EPIPE || errno == ENXIO || errno == EBADF) { + if (errno == EPIPE || errno == ENXIO || errno == EIO || errno == EBADF) { + sudo_debug_printf(SUDO_DEBUG_INFO, + "unable to write %d bytes to fd %d", + iob->len - iob->off, iob->wfd); /* other end of pipe closed or pty revoked */ if (iob->rfd != -1) { safe_close(iob->rfd); @@ -445,9 +484,14 @@ perform_io(fd_set *fdsr, fd_set *fdsw, struct command_ iob->wfd = -1; continue; } - if (errno != EAGAIN) + if (errno != EAGAIN) { errors++; + sudo_debug_printf(SUDO_DEBUG_ERROR, + "error writing fd %d: %s", iob->wfd, strerror(errno)); + } } else { + sudo_debug_printf(SUDO_DEBUG_INFO, + "wrote %d bytes to fd %d", n, iob->wfd); iob->off += n; } } @@ -456,7 +500,7 @@ perform_io(fd_set *fdsr, fd_set *fdsw, struct command_ cstat->type = CMD_ERRNO; cstat->val = errno; } - return errors; + debug_return_int(errors); } /* @@ -471,6 +515,7 @@ fork_pty(struct command_details *details, int sv[], in struct io_buffer *iob; int io_pipe[3][2], n; sigaction_t sa; + debug_decl(fork_pty, SUDO_DEBUG_EXEC); ppgrp = getpgrp(); /* parent's pgrp, so child can signal us */ @@ -521,7 +566,8 @@ fork_pty(struct command_details *details, int sv[], in */ memset(io_pipe, 0, sizeof(io_pipe)); if (io_fds[SFD_STDIN] == -1 || !isatty(STDIN_FILENO)) { - pipeline = TRUE; + sudo_debug_printf(SUDO_DEBUG_INFO, "stdin not a tty, creating a pipe"); + pipeline = true; if (pipe(io_pipe[STDIN_FILENO]) != 0) error(1, _("unable to create pipe")); iobufs = io_buf_new(STDIN_FILENO, io_pipe[STDIN_FILENO][1], @@ -529,7 +575,8 @@ fork_pty(struct command_details *details, int sv[], in io_fds[SFD_STDIN] = io_pipe[STDIN_FILENO][0]; } if (io_fds[SFD_STDOUT] == -1 || !isatty(STDOUT_FILENO)) { - pipeline = TRUE; + sudo_debug_printf(SUDO_DEBUG_INFO, "stdout not a tty, creating a pipe"); + pipeline = true; if (pipe(io_pipe[STDOUT_FILENO]) != 0) error(1, _("unable to create pipe")); iobufs = io_buf_new(io_pipe[STDOUT_FILENO][0], STDOUT_FILENO, @@ -537,6 +584,7 @@ fork_pty(struct command_details *details, int sv[], in io_fds[SFD_STDOUT] = io_pipe[STDOUT_FILENO][1]; } if (io_fds[SFD_STDERR] == -1 || !isatty(STDERR_FILENO)) { + sudo_debug_printf(SUDO_DEBUG_INFO, "stderr not a tty, creating a pipe"); if (pipe(io_pipe[STDERR_FILENO]) != 0) error(1, _("unable to create pipe")); iobufs = io_buf_new(io_pipe[STDERR_FILENO][0], STDERR_FILENO, @@ -549,10 +597,15 @@ fork_pty(struct command_details *details, int sv[], in sa.sa_handler = handler; sigaction(SIGTSTP, &sa, NULL); + /* We don't want to receive SIGTTIN/SIGTTOU, getting EIO is preferable. */ + sa.sa_handler = SIG_IGN; + sigaction(SIGTTIN, &sa, NULL); + sigaction(SIGTTOU, &sa, NULL); + if (foreground) { /* Copy terminal attrs from user tty -> pty slave. */ if (term_copy(io_fds[SFD_USERTTY], io_fds[SFD_SLAVE])) { - tty_initialized = 1; + tty_initialized = true; sync_ttysize(io_fds[SFD_USERTTY], io_fds[SFD_SLAVE]); } @@ -567,7 +620,14 @@ fork_pty(struct command_details *details, int sv[], in } } - child = fork(); + /* + * The policy plugin's session init must be run before we fork + * or certain pam modules won't be able to track their state. + */ + if (policy_init_session(details) != true) + errorx(1, _("policy plugin failed session initialization")); + + child = sudo_debug_fork(); switch (child) { case -1: error(1, _("unable to fork")); @@ -578,7 +638,7 @@ fork_pty(struct command_details *details, int sv[], in close(signal_pipe[0]); close(signal_pipe[1]); fcntl(sv[1], F_SETFD, FD_CLOEXEC); - if (exec_setup(details, slavename, io_fds[SFD_SLAVE]) == TRUE) { + if (exec_setup(details, slavename, io_fds[SFD_SLAVE]) == true) { /* Close the other end of the stdin/stdout/stderr pipes and exec. */ if (io_pipe[STDIN_FILENO][1]) close(io_pipe[STDIN_FILENO][1]); @@ -618,13 +678,14 @@ fork_pty(struct command_details *details, int sv[], in (void) fcntl(iob->wfd, F_SETFL, n | O_NONBLOCK); } - return child; + debug_return_int(child); } void pty_close(struct command_status *cstat) { int n; + debug_decl(pty_close, SUDO_DEBUG_EXEC); /* Flush any remaining output (the plugin already got it) */ if (io_fds[SFD_USERTTY] != -1) { @@ -637,9 +698,12 @@ pty_close(struct command_status *cstat) flush_output(); if (io_fds[SFD_USERTTY] != -1) { - do { - n = term_restore(io_fds[SFD_USERTTY], 0); - } while (!n && errno == EINTR); + check_foreground(); + if (foreground) { + do { + n = term_restore(io_fds[SFD_USERTTY], 0); + } while (!n && errno == EINTR); + } } /* If child was signalled, write the reason to stdout like the shell. */ @@ -651,15 +715,14 @@ pty_close(struct command_status *cstat) io_fds[SFD_USERTTY] : STDOUT_FILENO; if (write(n, reason, strlen(reason)) != -1) { if (WCOREDUMP(cstat->val)) { - if (write(n, " (core dumped)", 14) == -1) - /* shut up glibc */; + ignore_result(write(n, " (core dumped)", 14)); } - if (write(n, "\n", 1) == -1) - /* shut up glibc */; + ignore_result(write(n, "\n", 1)); } } } utmp_logout(slavename, cstat->type == CMD_WSTATUS ? cstat->val : 0); /* XXX - only if CD_SET_UTMP */ + debug_return; } /* @@ -670,6 +733,7 @@ void fd_set_iobs(fd_set *fdsr, fd_set *fdsw) { struct io_buffer *iob; + debug_decl(fd_set_iobs, SUDO_DEBUG_EXEC); for (iob = iobufs; iob; iob = iob->next) { if (iob->rfd == -1 && iob->wfd == -1) @@ -694,18 +758,21 @@ fd_set_iobs(fd_set *fdsr, fd_set *fdsw) FD_SET(iob->wfd, fdsw); } } + debug_return; } static void -deliver_signal(pid_t pid, int signo) +deliver_signal(pid_t pid, int signo, bool from_parent) { int status; + debug_decl(deliver_signal, SUDO_DEBUG_EXEC); /* Handle signal from parent. */ - sudo_debug(8, "signal %d from parent", signo); + sudo_debug_printf(SUDO_DEBUG_INFO, "received signal %d%s", signo, + from_parent ? " from parent" : ""); switch (signo) { case SIGALRM: - terminate_child(pid, TRUE); + terminate_child(pid, true); break; case SIGCONT_FG: /* Continue in foreground, grant it controlling tty. */ @@ -729,6 +796,7 @@ deliver_signal(pid_t pid, int signo) killpg(pid, signo); break; } + debug_return; } /* @@ -739,33 +807,37 @@ static int send_status(int fd, struct command_status *cstat) { int n = -1; + debug_decl(send_status, SUDO_DEBUG_EXEC); if (cstat->type != CMD_INVALID) { + sudo_debug_printf(SUDO_DEBUG_INFO, + "sending status message to parent: [%d, %d]", + cstat->type, cstat->val); do { n = send(fd, cstat, sizeof(*cstat), 0); } while (n == -1 && errno == EINTR); if (n != sizeof(*cstat)) { - sudo_debug(8, "unable to send status to parent: %s", - strerror(errno)); - } else { - sudo_debug(8, "sent status to parent"); + sudo_debug_printf(SUDO_DEBUG_ERROR, + "unable to send status to parent: %s", strerror(errno)); } cstat->type = CMD_INVALID; /* prevent re-sending */ } - return n; + debug_return_int(n); } /* * Wait for child status after receiving SIGCHLD. * If the child was stopped, the status is send back to the parent. * Otherwise, cstat is filled in but not sent. - * Returns TRUE if child is still alive, else FALSE. + * Returns true if child is still alive, else false. */ -static int +static bool handle_sigchld(int backchannel, struct command_status *cstat) { - int status, alive = TRUE; + bool alive = true; + int status; pid_t pid; + debug_decl(handle_sigchld, SUDO_DEBUG_EXEC); /* read child status */ do { @@ -776,22 +848,25 @@ handle_sigchld(int backchannel, struct command_status cstat->type = CMD_WSTATUS; cstat->val = status; if (WIFSTOPPED(status)) { - sudo_debug(8, "command stopped, signal %d", WSTOPSIG(status)); + sudo_debug_printf(SUDO_DEBUG_INFO, "command stopped, signal %d", + WSTOPSIG(status)); do { child_pgrp = tcgetpgrp(io_fds[SFD_SLAVE]); } while (child_pgrp == -1 && errno == EINTR); if (send_status(backchannel, cstat) == -1) return alive; /* XXX */ } else if (WIFSIGNALED(status)) { - sudo_debug(8, "command killed, signal %d", WTERMSIG(status)); + sudo_debug_printf(SUDO_DEBUG_INFO, "command killed, signal %d", + WTERMSIG(status)); } else { - sudo_debug(8, "command exited: %d", WEXITSTATUS(status)); + sudo_debug_printf(SUDO_DEBUG_INFO, "command exited: %d", + WEXITSTATUS(status)); } } if (!WIFSTOPPED(status)) - alive = FALSE; + alive = false; } - return alive; + debug_return_bool(alive); } /* @@ -809,8 +884,9 @@ exec_monitor(struct command_details *details, int back fd_set *fdsr; sigaction_t sa; int errpipe[2], maxfd, n, status; - int alive = TRUE; + bool alive = true; unsigned char signo; + debug_decl(exec_monitor, SUDO_DEBUG_EXEC); /* Close unused fds. */ if (io_fds[SFD_MASTER] != -1) @@ -843,6 +919,17 @@ exec_monitor(struct command_details *details, int back sa.sa_handler = handler; sigaction(SIGCHLD, &sa, NULL); + /* Catch common signals so we can cleanup properly. */ + sa.sa_flags = SA_RESTART; + sa.sa_handler = handler; + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGTSTP, &sa, NULL); + sigaction(SIGUSR1, &sa, NULL); + sigaction(SIGUSR2, &sa, NULL); + /* * Start a new session with the parent as the session leader * and the slave pty as the controlling terminal. @@ -870,12 +957,12 @@ exec_monitor(struct command_details *details, int back * when it needs access to the controlling tty. */ if (pipeline) - foreground = 0; + foreground = false; /* Start command and wait for it to stop or exit */ if (pipe(errpipe) == -1) error(1, _("unable to create pipe")); - child = fork(); + child = sudo_debug_fork(); if (child == -1) { warning(_("unable to fork")); goto bad; @@ -890,11 +977,10 @@ exec_monitor(struct command_details *details, int back restore_signals(); /* setup tty and exec command */ - exec_pty(details); + exec_pty(details, &errpipe[1]); cstat.type = CMD_ERRNO; cstat.val = errno; - if (write(errpipe[1], &cstat, sizeof(cstat)) == -1) - /* shut up glibc */; + ignore_result(write(errpipe[1], &cstat, sizeof(cstat))); _exit(1); } close(errpipe[1]); @@ -921,9 +1007,8 @@ exec_monitor(struct command_details *details, int back /* Wait for errno on pipe, signal on backchannel or for SIGCHLD */ maxfd = MAX(MAX(errpipe[0], signal_pipe[0]), backchannel); - fdsr = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask)); - zero_bytes(fdsr, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask)); - zero_bytes(&cstat, sizeof(cstat)); + fdsr = ecalloc(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask)); + memset(&cstat, 0, sizeof(cstat)); tv.tv_sec = 0; tv.tv_usec = 0; for (;;) { @@ -939,9 +1024,10 @@ exec_monitor(struct command_details *details, int back if (n <= 0) { if (n == 0) goto done; - if (errno == EINTR) + if (errno == EINTR || errno == ENOMEM) continue; - error(1, _("select failed")); + warning("monitor: %s", _("select failed")); + break; } if (FD_ISSET(signal_pipe[0], fdsr)) { @@ -956,10 +1042,12 @@ exec_monitor(struct command_details *details, int back * Handle SIGCHLD specially and deliver other signals * directly to the child. */ - if (signo == SIGCHLD) - alive = handle_sigchld(backchannel, &cstat); - else - deliver_signal(child, signo); + if (signo == SIGCHLD) { + if (!handle_sigchld(backchannel, &cstat)) + alive = false; + } else { + deliver_signal(child, signo, false); + } continue; } if (errpipe[0] != -1 && FD_ISSET(errpipe[0], fdsr)) { @@ -992,7 +1080,7 @@ exec_monitor(struct command_details *details, int back cstmp.type); continue; } - deliver_signal(child, cstmp.val); + deliver_signal(child, cstmp.val, true); } } @@ -1004,10 +1092,11 @@ done: /* Send parent status. */ send_status(backchannel, &cstat); } + sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, 1); _exit(1); bad: - return errno; + debug_return_int(errno); } /* @@ -1021,6 +1110,7 @@ flush_output(void) struct timeval tv; fd_set *fdsr, *fdsw; int nready, nwriters, maxfd = -1; + debug_decl(flush_output, SUDO_DEBUG_EXEC); /* Determine maxfd */ for (iob = iobufs; iob; iob = iob->next) { @@ -1030,13 +1120,13 @@ flush_output(void) maxfd = iob->wfd; } if (maxfd == -1) - return; + debug_return; - fdsr = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask)); - fdsw = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask)); + fdsr = emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask)); + fdsw = emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask)); for (;;) { - zero_bytes(fdsw, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask)); - zero_bytes(fdsr, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask)); + memset(fdsw, 0, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask)); + memset(fdsr, 0, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask)); nwriters = 0; for (iob = iobufs; iob; iob = iob->next) { @@ -1072,15 +1162,16 @@ flush_output(void) if (nready <= 0) { if (nready == 0) break; /* all I/O flushed */ - if (errno == EINTR) + if (errno == EINTR || errno == ENOMEM) continue; - error(1, _("select failed")); + warning(_("select failed")); } - if (perform_io(fdsr, fdsw, NULL) != 0) + if (perform_io(fdsr, fdsw, NULL) != 0 || nready == -1) break; } efree(fdsr); efree(fdsw); + debug_return; } /* @@ -1088,9 +1179,10 @@ flush_output(void) * Returns only if execve() fails. */ static void -exec_pty(struct command_details *details) +exec_pty(struct command_details *details, int *errfd) { pid_t self = getpid(); + debug_decl(exec_pty, SUDO_DEBUG_EXEC); /* Set child process group here too to avoid a race. */ setpgid(0, self); @@ -1117,14 +1209,31 @@ exec_pty(struct command_details *details) if (io_fds[SFD_STDERR] != io_fds[SFD_SLAVE]) close(io_fds[SFD_STDERR]); - if (details->closefrom >= 0) - closefrom(details->closefrom); + sudo_debug_execve(SUDO_DEBUG_INFO, details->command, + details->argv, details->envp); + + if (details->closefrom >= 0) { + int maxfd = details->closefrom; + dup2(*errfd, maxfd); + (void)fcntl(maxfd, F_SETFD, FD_CLOEXEC); + *errfd = maxfd++; + if (sudo_debug_fd_set(maxfd) != -1) + maxfd++; + closefrom(maxfd); + } #ifdef HAVE_SELINUX - if (ISSET(details->flags, CD_RBAC_ENABLED)) - selinux_execve(details->command, details->argv, details->envp); - else + if (ISSET(details->flags, CD_RBAC_ENABLED)) { + selinux_execve(details->command, details->argv, details->envp, + ISSET(details->flags, CD_NOEXEC)); + } else #endif - my_execve(details->command, details->argv, details->envp); + { + sudo_execve(details->command, details->argv, details->envp, + ISSET(details->flags, CD_NOEXEC)); + } + sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to exec %s: %s", + details->command, strerror(errno)); + debug_return; } /* @@ -1136,12 +1245,15 @@ sync_ttysize(int src, int dst) #ifdef TIOCGWINSZ struct winsize wsize; pid_t pgrp; + debug_decl(sync_ttysize, SUDO_DEBUG_EXEC); if (ioctl(src, TIOCGWINSZ, &wsize) == 0) { ioctl(dst, TIOCSWINSZ, &wsize); if ((pgrp = tcgetpgrp(dst)) != -1) killpg(pgrp, SIGWINCH); } + + debug_return; #endif } @@ -1164,10 +1276,12 @@ sigwinch(int s) static int safe_close(int fd) { + debug_decl(safe_close, SUDO_DEBUG_EXEC); + /* Avoid closing /dev/tty or std{in,out,err}. */ if (fd < 3 || fd == io_fds[SFD_USERTTY]) { errno = EINVAL; - return -1; + debug_return_int(-1); } - return close(fd); + debug_return_int(close(fd)); }