Make transcript mode work as long as we can figure out our tty, even if

it is not stdin.  We'd like to use /dev/tty but that won't be valid after
the setsid().
This commit is contained in:
Todd C. Miller
2009-10-18 14:46:01 +00:00
parent e5087b7269
commit 1a5afcbb15

View File

@@ -67,6 +67,7 @@ __unused static const char rcsid[] = "$Sudo$";
#define SFD_LOG 2 #define SFD_LOG 2
#define SFD_OUTPUT 3 #define SFD_OUTPUT 3
#define SFD_TIMING 4 #define SFD_TIMING 4
#define SFD_USERTTY 5
struct script_buf { struct script_buf {
int len; /* buffer length (how much read in) */ int len; /* buffer length (how much read in) */
@@ -74,7 +75,7 @@ struct script_buf {
char buf[16 * 1024]; char buf[16 * 1024];
}; };
static int script_fds[5]; static int script_fds[6];
static sig_atomic_t alive = 1; static sig_atomic_t alive = 1;
static sig_atomic_t suspended = 0; static sig_atomic_t suspended = 0;
@@ -222,19 +223,21 @@ script_setup()
char pathbuf[PATH_MAX]; char pathbuf[PATH_MAX];
int len; int len;
if (!isatty(STDIN_FILENO)) /* XXX - can't use _PATH_TTY here since it won't be valid in new session */
log_error(0, "Standard input is not a tty"); if (!user_ttypath ||
(script_fds[SFD_USERTTY] = open(user_ttypath, O_RDWR|O_NOCTTY, 0) == -1))
log_error(0, "tty required for transcript support"); /* XXX */
if (!get_pty(&script_fds[SFD_MASTER], &script_fds[SFD_SLAVE], if (!get_pty(&script_fds[SFD_MASTER], &script_fds[SFD_SLAVE],
slavename, sizeof(slavename))) slavename, sizeof(slavename)))
log_error(USE_ERRNO, "Can't get pty"); log_error(USE_ERRNO, "Can't get pty");
/* Copy terminal attrs from stdin -> pty slave. */ /* Copy terminal attrs from user tty -> pty slave. */
if (!term_copy(STDIN_FILENO, script_fds[SFD_SLAVE], 0)) if (!term_copy(script_fds[SFD_USERTTY], script_fds[SFD_SLAVE], 0))
log_error(USE_ERRNO, "Can't copy terminal attributes"); log_error(USE_ERRNO, "Can't copy terminal attributes");
sync_winsize(STDIN_FILENO, script_fds[SFD_SLAVE]); sync_winsize(script_fds[SFD_USERTTY], script_fds[SFD_SLAVE]);
if (!term_raw(STDIN_FILENO, 1)) if (!term_raw(script_fds[SFD_USERTTY], 1))
log_error(USE_ERRNO, "Can't set terminal to raw mode"); log_error(USE_ERRNO, "Can't set terminal to raw mode");
/* /*
@@ -268,15 +271,15 @@ int
script_duplow(fd) script_duplow(fd)
int fd; int fd;
{ {
int i, j, indices[5]; int i, j, indices[6];
/* sort fds so we can dup them safely */ /* sort fds so we can dup them safely */
for (i = 0; i < 5; i++) for (i = 0; i < 6; i++)
indices[i] = i; indices[i] = i;
qsort(indices, 5, sizeof(int), fdcompar); qsort(indices, 6, sizeof(int), fdcompar);
/* Move pty master/slave and session fds to low numbered fds. */ /* Move pty master/slave and session fds to low numbered fds. */
for (i = 0; i < 5; i++) { for (i = 0; i < 6; i++) {
j = indices[i]; j = indices[i];
if (script_fds[j] != fd) { if (script_fds[j] != fd) {
#ifdef HAVE_DUP2 #ifdef HAVE_DUP2
@@ -395,7 +398,7 @@ script_execv(path, argv)
} }
/* Restore old tty settings and signal handler. */ /* Restore old tty settings and signal handler. */
term_restore(STDIN_FILENO); term_restore(script_fds[SFD_USERTTY]);
check_sig: check_sig:
switch (signo) { switch (signo) {
case SIGINT: case SIGINT:
@@ -426,12 +429,12 @@ script_execv(path, argv)
kill(parent, signo); /* re-send signal with handler disabled */ kill(parent, signo); /* re-send signal with handler disabled */
/* Reinstall signal handler, reset raw mode and continue child */ /* Reinstall signal handler, reset raw mode and continue child */
(void) sigaction(signo, &sa, NULL); (void) sigaction(signo, &sa, NULL);
if (!term_raw(STDIN_FILENO, 1) && errno == EINTR) if (!term_raw(script_fds[SFD_USERTTY], 1) && errno == EINTR)
goto check_sig; goto check_sig;
kill(child, SIGCONT); kill(child, SIGCONT);
} }
term_restore(STDIN_FILENO); term_restore(script_fds[SFD_USERTTY]);
exit(exitcode); exit(exitcode);
} }
@@ -534,10 +537,10 @@ script_child(path, argv)
n |= O_NONBLOCK; n |= O_NONBLOCK;
(void) fcntl(script_fds[SFD_MASTER], F_SETFL, n); (void) fcntl(script_fds[SFD_MASTER], F_SETFL, n);
} }
n = fcntl(STDIN_FILENO, F_GETFL, 0); n = fcntl(script_fds[SFD_USERTTY], F_GETFL, 0);
if (n != -1) { if (n != -1) {
n |= O_NONBLOCK; n |= O_NONBLOCK;
(void) fcntl(STDIN_FILENO, F_SETFL, n); (void) fcntl(script_fds[SFD_USERTTY], F_SETFL, n);
} }
n = fcntl(STDOUT_FILENO, F_GETFL, 0); n = fcntl(STDOUT_FILENO, F_GETFL, 0);
if (n != -1) { if (n != -1) {
@@ -546,10 +549,9 @@ script_child(path, argv)
} }
/* /*
* In the event loop we pass input from stdin to master * In the event loop we pass input from user tty to master
* and pass output from master to stdout and ofile. * and pass output from master to stdout and ofile. Note that
* Note that we've set things up such that master is above * we've set things up such that master is > 3 (see sudo.c).
* stdin and stdout (see sudo.c).
*/ */
fdsr = (fd_set *)emalloc2(howmany(script_fds[SFD_MASTER] + 1, NFDBITS), fdsr = (fd_set *)emalloc2(howmany(script_fds[SFD_MASTER] + 1, NFDBITS),
sizeof(fd_mask)); sizeof(fd_mask));
@@ -566,7 +568,7 @@ script_child(path, argv)
zero_bytes(fdsw, howmany(script_fds[SFD_MASTER] + 1, NFDBITS) * sizeof(fd_mask)); zero_bytes(fdsw, howmany(script_fds[SFD_MASTER] + 1, NFDBITS) * sizeof(fd_mask));
zero_bytes(fdsr, howmany(script_fds[SFD_MASTER] + 1, NFDBITS) * sizeof(fd_mask)); zero_bytes(fdsr, howmany(script_fds[SFD_MASTER] + 1, NFDBITS) * sizeof(fd_mask));
if (input.len != sizeof(input.buf)) if (input.len != sizeof(input.buf))
FD_SET(STDIN_FILENO, fdsr); FD_SET(script_fds[SFD_USERTTY], fdsr);
if (output.len != sizeof(output.buf)) if (output.len != sizeof(output.buf))
FD_SET(script_fds[SFD_MASTER], fdsr); FD_SET(script_fds[SFD_MASTER], fdsr);
if (output.len > output.off) if (output.len > output.off)
@@ -590,8 +592,8 @@ script_child(path, argv)
continue; continue;
log_error(USE_ERRNO, "select failed"); log_error(USE_ERRNO, "select failed");
} }
if (FD_ISSET(STDIN_FILENO, fdsr)) { if (FD_ISSET(script_fds[SFD_USERTTY], fdsr)) {
n = read(STDIN_FILENO, input.buf + input.len, n = read(script_fds[SFD_USERTTY], input.buf + input.len,
sizeof(input.buf) - input.len); sizeof(input.buf) - input.len);
if (n == -1) { if (n == -1) {
if (errno == EINTR) if (errno == EINTR)
@@ -717,6 +719,10 @@ script_grandchild(path, argv, rbac_enabled)
{ {
pid_t self = getpid(); pid_t self = getpid();
/*
* We have guaranteed that the slave fd > 3
*/
if (isatty(STDIN_FILENO))
dup2(script_fds[SFD_SLAVE], STDIN_FILENO); dup2(script_fds[SFD_SLAVE], STDIN_FILENO);
dup2(script_fds[SFD_SLAVE], STDOUT_FILENO); dup2(script_fds[SFD_SLAVE], STDOUT_FILENO);
dup2(script_fds[SFD_SLAVE], STDERR_FILENO); dup2(script_fds[SFD_SLAVE], STDERR_FILENO);
@@ -729,9 +735,10 @@ script_grandchild(path, argv, rbac_enabled)
close(script_fds[SFD_LOG]); close(script_fds[SFD_LOG]);
close(script_fds[SFD_OUTPUT]); close(script_fds[SFD_OUTPUT]);
close(script_fds[SFD_TIMING]); close(script_fds[SFD_TIMING]);
close(script_fds[SFD_USERTTY]);
/* Spin until parent grants us the controlling pty */ /* Spin until parent grants us the controlling pty */
while (tcgetpgrp(STDIN_FILENO) != self) while (tcgetpgrp(STDOUT_FILENO) != self)
continue; continue;
#ifdef HAVE_SELINUX #ifdef HAVE_SELINUX
@@ -808,6 +815,6 @@ sigwinch(s)
{ {
int serrno = errno; int serrno = errno;
sync_winsize(STDIN_FILENO, script_fds[SFD_SLAVE]); sync_winsize(script_fds[SFD_USERTTY], script_fds[SFD_SLAVE]);
errno = serrno; errno = serrno;
} }