]> granicus.if.org Git - sudo/commitdiff
Merge I/O logging changes from trunk. Disabling I/O log support
authorTodd C. Miller <Todd.Miller@courtesan.com>
Fri, 4 Jun 2010 13:41:08 +0000 (09:41 -0400)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Fri, 4 Jun 2010 13:41:08 +0000 (09:41 -0400)
at compile time does not currently work.
Sudoedit is not yet hooked up to I/O logging.

--HG--
branch : 1.7

31 files changed:
INSTALL
Makefile.in
WHATSNEW
aclocal.m4
configure
configure.in
def_data.c
def_data.h
def_data.in
defaults.c
exec.c [new file with mode: 0644]
gram.c
gram.h
gram.y
iolog.c [new file with mode: 0644]
parse.c
parse.h
pathnames.h.in
pty.c
script.c [deleted file]
selinux.c
sudo.c
sudo.h
sudoers.cat
sudoers.man.in
sudoers.pod
sudoreplay.c
sudoreplay.cat
sudoreplay.man.in
sudoreplay.pod
term.c

diff --git a/INSTALL b/INSTALL
index 350d4bda0bf18cc3c3770e8369e1844b876e9026..6cb9e59966aab374463229d4faa5065aa65a49ee 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -598,26 +598,26 @@ The following options are also configurable at runtime:
        if the executable is simply not in the user's path, sudo will tell
        the user that they are not allowed to run it, which can be confusing.
 
-  --disable-transcript
-        Disable sudo's transcript support.  This can be used to allow sudo
+  --disable-iologdir
+       Disable sudo's I/O logging support.  This can be used to allow sudo
        to be compiled on systems without pseudo-tty support.
 
-  --enable-transcript[=DIR]
-       By default, sudo stores transcript files in either
-       /var/log/sudo-transcript, /var/adm/sudo-transcript, or
-       /usr/log/sudo-transcript.  If DIR is specified, transcripts
-       will be stored in the indicated directory instead.
+  --enable-iologdir[=DIR]
+       By default, sudo stores I/O log files in either /var/log/sudo-io,
+       /var/adm/sudo-sudo-io or /usr/log/sudo-io.  If DIR is
+       specified, I/O logs will be stored in the indicated directory
+       instead.
+
+  --enable-zlib[=DIR]
+       Enable the use of the zlib compress library when storing
+       I/O log files.  If specified, DIR is the base directory
+       containing the zlib include and lib directories.  By default
+       zlib is used if it is found on the system and I/O logging
+       support is not disabled.
 
   --disable-zlib
         Disable the use of the zlib compress library when storing
-        transcript files.
-
-  --enable-zlib[=DIR]
-        Enable the use of the zlib compress library when storing
-        transcript files.  If specified, DIR is the base directory
-        containing the zlib include and lib directories.  By default
-        zlib is used if it is found on the system and transcript
-        support is not disabled.
+        I/O log files.
 
   --enable-warnings
        Enable compiler warnings when building sudo with gcc.
index a7a4d111f41930418591b95d975d7699d13bcc78..21d386700a4d9f42cde5a06e26956701856e6d6e 100644 (file)
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 1996, 1998-2005, 2007-2009
+# Copyright (c) 1996, 1998-2005, 2007-2010
 #      Todd C. Miller <Todd.Miller@courtesan.com>
 #
 # Permission to use, copy, modify, and distribute this software for any
@@ -33,7 +33,7 @@ top_builddir = .
 CC = @CC@
 FLEX = @FLEX@
 YACC = @YACC@
-NROFF = nroff -Tascii
+NROFF = @NROFFPROG@ -Tascii
 LIBTOOL = @LIBTOOL@
 AR=@AR@
 RANLIB=@RANLIB@
@@ -103,11 +103,11 @@ SHELL = @SHELL@
 PROGS = @PROGS@
 
 SRCS = aix.c alias.c alloc.c audit.c boottime.c bsm_audit.c check.c \
-       closefrom.c def_data.c defaults.c env.c error.c fileops.c find_path.c \
-       fnmatch.c getcwd.c getprogname.c getspwuid.c gettime.c glob.c \
-       goodpath.c gram.c gram.y interfaces.c isblank.c lbuf.c ldap.c list.c \
-       logging.c match.c mkstemp.c memrchr.c nanosleep.c parse.c pwutil.c \
-       pty.c script.c set_perms.c sigaction.c snprintf.c strcasecmp.c \
+       closefrom.c def_data.c defaults.c env.c error.c exec.c fileops.c \
+       find_path.c fnmatch.c getcwd.c getprogname.c getspwuid.c gettime.c \
+       glob.c goodpath.c gram.c gram.y interfaces.c iolog.c isblank.c lbuf.c \
+       ldap.c list.c logging.c match.c mkstemp.c memrchr.c nanosleep.c \
+       parse.c pwutil.c pty.c set_perms.c sigaction.c snprintf.c strcasecmp.c \
        strerror.c strlcat.c strlcpy.c strsignal.c sudo.c sudo_noexec.c \
        sudo_edit.c sudo_nss.c term.c testsudoers.c tgetpass.c toke.c toke.l \
        tsgetgrpw.c utimes.c vasgroups.c visudo.c zero_bytes.c redblack.c \
@@ -131,8 +131,8 @@ COMMON_OBJS = alias.o alloc.o defaults.o error.o getline.o gram.o \
              list.o match.o pwutil.o timestr.o toke.o redblack.o \
              term.o zero_bytes.o @NONUNIX_GROUPS_IMPL@
 
-SUDO_OBJS = $(AUTH_OBJS) @SUDO_OBJS@ audit.o boottime.o check.o \
-           env.o getspwuid.o gettime.o goodpath.o fileops.o find_path.o \
+SUDO_OBJS = $(AUTH_OBJS) @SUDO_OBJS@ audit.o boottime.o check.o env.o \
+           exec.o getspwuid.o gettime.o goodpath.o fileops.o find_path.o \
            interfaces.o lbuf.o logging.o parse.o set_perms.o sudo.o \
            sudo_edit.o sudo_nss.o tgetpass.o
 
@@ -157,7 +157,7 @@ DISTFILES = $(SRCS) $(HDRS) ChangeLog HISTORY INSTALL INSTALL.configure \
             sudoers.man.in sudoers.pod sudoers.ldap.cat sudoers.ldap.man.in \
            sudoers.ldap.pod sudoers2ldif sudoreplay.cat sudoreplay.man.in \
            sudoreplay.pod visudo.cat visudo.man.in visudo.pod auth/API \
-           sudo.man.pl sudoers.man.pl 
+           sudo.man.pl sudoers.man.pl
 
 BINFILES= ChangeLog HISTORY LICENSE README TROUBLESHOOTING \
          UPGRADE install-sh mkinstalldirs sample.syslog.conf sample.sudoers \
@@ -263,6 +263,8 @@ env.o: $(srcdir)/env.c $(SUDODEP)
        $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/env.c
 error.o: $(srcdir)/error.c $(srcdir)/compat.h $(srcdir)/error.h config.h
        $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/error.c
+exec.o: $(srcdir)/exec.c $(SUDODEP)
+       $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/exec.c
 fileops.o: $(srcdir)/fileops.c $(SUDODEP)
        $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/fileops.c
 find_path.o: $(srcdir)/find_path.c $(SUDODEP)
@@ -289,6 +291,8 @@ gram.o: $(devdir)/gram.c $(SUDODEP) $(srcdir)/parse.h $(srcdir)/list.h $(devdir)
        $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(devdir)/gram.c
 interfaces.o: $(srcdir)/interfaces.c $(SUDODEP) $(srcdir)/interfaces.h
        $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/interfaces.c
+iolog.o: $(srcdir)/iolog.c $(SUDODEP)
+       $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/iolog.c
 isblank.o: $(srcdir)/isblank.c $(srcdir)/compat.h config.h
        $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/isblank.c
 lbuf.o: $(srcdir)/lbuf.c $(SUDODEP)
@@ -315,8 +319,6 @@ pty.o: $(srcdir)/pty.c $(SUDODEP)
        $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/pty.c
 redblack.o: $(srcdir)/redblack.c $(SUDODEP) $(srcdir)/redblack.h
        $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/redblack.c
-script.o: $(srcdir)/script.c $(SUDODEP)
-       $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/script.c
 set_perms.o: $(srcdir)/set_perms.c $(SUDODEP)
        $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/set_perms.c
 sigaction.o: $(srcdir)/sigaction.c $(srcdir)/compat.h
index f43049ebf775fb63d3bd0087fb7e0db6997b562f..8c685b50029fce10c980a23bf16b62e4ac849eda 100644 (file)
--- a/WHATSNEW
+++ b/WHATSNEW
@@ -1,8 +1,9 @@
 What's new in Sudo 1.7.3?
 
- * Support for logging a transcript of the command being run.
-   For more information, see the documentation for the "transcript"
-   Defaults option in the sudoers manual and the sudoreplay manual.
+ * Support for logging I/O for the command being run.
+   For more information, see the documentation for the "log_input"
+   and "log_output" Defaults options in the sudoers manual.  Also
+   see the sudoreplay manual for how to replay I/O log sessions.
 
  * The passwd_timeout and timestamp_timeout options may now be
    specified as floating point numbers for more granular timeout
index 841ede86be09a9613fdaa171441d533ae603a12d..17b317be6a2824790359fb146984cb4f85476c4c 100644 (file)
@@ -158,24 +158,22 @@ fi
 ])dnl
 
 dnl
-dnl Where the transcript files go, use /var/log/sudo-transcript if
-dnl /var/log exists, else /{var,usr}/adm/sudo-transcript
-dnl
-AC_DEFUN(SUDO_TRANSCRIPT, [
-    if test "${enable_transcript-yes}" != "no"; then
-       AC_MSG_CHECKING(for transcript dir location)
-       if test "${enable_transcript-yes}" != "yes"; then
-           :
-       elif test -d "/var/log"; then
-           enable_transcript="/var/log/sudo-transcript"
-       elif test -d "/var/adm"; then
-           enable_transcript="/var/adm/sudo-transcript"
-       else
-           enable_transcript="/usr/adm/sudo-transcript"
-       fi
-       SUDO_DEFINE_UNQUOTED(_PATH_SUDO_TRANSCRIPT, "$enable_transcript")
-       AC_MSG_RESULT($enable_transcript)
+dnl Where the I/O log files go, use /var/log/sudo-io if
+dnl /var/log exists, else /{var,usr}/adm/sudo-io
+dnl
+AC_DEFUN(SUDO_IO_LOGDIR, [
+    AC_MSG_CHECKING(for I/O log dir location)
+    if test "${with_iologdir-yes}" != "yes"; then
+       :
+    elif test -d "/var/log"; then
+       with_iologdir="/var/log/sudo-io"
+    elif test -d "/var/adm"; then
+       with_iologdir="/var/adm/sudo-io"
+    else
+       with_iologdir="/usr/adm/sudo-io"
     fi
+    SUDO_DEFINE_UNQUOTED(_PATH_SUDO_IO_LOGDIR, "$with_iologdir")
+    AC_MSG_RESULT($with_iologdir)
 ])dnl
 
 dnl
index 420fd1b842839cc8b2a9031ccc2457e61d0f2cc1..1499df90f43b429e596a2e160093aae46d74a3b9 100755 (executable)
--- a/configure
+++ b/configure
@@ -939,6 +939,7 @@ with_passprompt
 with_badpass_message
 with_fqdn
 with_timedir
+with_iologdir
 with_sendmail
 with_sudoers_mode
 with_sudoers_uid
@@ -980,7 +981,6 @@ enable_noargs_shell
 enable_shell_sets_home
 enable_path_info
 enable_env_debug
-enable_transcript
 enable_warnings
 with_selinux
 enable_gss_krb5_ccache_name
@@ -1634,9 +1634,6 @@ Optional Features:
                           Set $HOME to target user in shell mode
   --disable-path-info     Print 'command not allowed' not 'command not found'
   --enable-env-debug      Whether to enable environment debugging.
-  --enable-transcript[=DIR]
-                          Enable Sudo's transcript support, storing them in
-                          DIR
   --enable-warnings       Whether to enable compiler warnings
   --enable-gss-krb5-ccache-name
                           Use GSS-API to set the Kerberos V cred cache name
@@ -1697,6 +1694,7 @@ Optional Packages:
   --with-badpass-message  message the user sees when the password is wrong
   --with-fqdn             expect fully qualified hosts in sudoers
   --with-timedir          path to the sudo timestamp dir
+  --with-iologdir=DIR     directory to store sudo I/O log files in
   --with-sendmail         set path to sendmail
   --without-sendmail      do not send mail at all
   --with-sudoers-mode     mode of sudoers file (defaults to 0440)
@@ -3656,6 +3654,17 @@ fi
 
 
 
+# Check whether --with-iologdir was given.
+if test "${with_iologdir+set}" = set; then :
+  withval=$with_iologdir; case $with_iologdir in
+    yes)       ;;
+    no)                as_fn_error "\"--without-iologdir not supported.\"" "$LINENO" 5
+               ;;
+esac
+fi
+
+
+
 # Check whether --with-sendmail was given.
 if test "${with_sendmail+set}" = set; then :
   withval=$with_sendmail; case $with_sendmail in
@@ -5344,12 +5353,6 @@ $as_echo "no" >&6; }
 fi
 
 
-# Check whether --enable-transcript was given.
-if test "${enable_transcript+set}" = set; then :
-  enableval=$enable_transcript;
-fi
-
-
 # Check whether --enable-warnings was given.
 if test "${enable_warnings+set}" = set; then :
   enableval=$enable_warnings;  case "$enableval" in
@@ -6521,13 +6524,13 @@ if test "${lt_cv_nm_interface+set}" = set; then :
 else
   lt_cv_nm_interface="BSD nm"
   echo "int some_variable = 0;" > conftest.$ac_ext
-  (eval echo "\"\$as_me:6524: $ac_compile\"" >&5)
+  (eval echo "\"\$as_me:6527: $ac_compile\"" >&5)
   (eval "$ac_compile" 2>conftest.err)
   cat conftest.err >&5
-  (eval echo "\"\$as_me:6527: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+  (eval echo "\"\$as_me:6530: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
   (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
   cat conftest.err >&5
-  (eval echo "\"\$as_me:6530: output\"" >&5)
+  (eval echo "\"\$as_me:6533: output\"" >&5)
   cat conftest.out >&5
   if $GREP 'External.*some_variable' conftest.out > /dev/null; then
     lt_cv_nm_interface="MS dumpbin"
@@ -7732,7 +7735,7 @@ ia64-*-hpux*)
   ;;
 *-*-irix6*)
   # Find out which ABI we are using.
-  echo '#line 7735 "configure"' > conftest.$ac_ext
+  echo '#line 7738 "configure"' > conftest.$ac_ext
   if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
   (eval $ac_compile) 2>&5
   ac_status=$?
@@ -9125,11 +9128,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:9128: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:9131: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:9132: \$? = $ac_status" >&5
+   echo "$as_me:9135: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -9464,11 +9467,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:9467: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:9470: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:9471: \$? = $ac_status" >&5
+   echo "$as_me:9474: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -9569,11 +9572,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:9572: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:9575: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:9576: \$? = $ac_status" >&5
+   echo "$as_me:9579: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -9624,11 +9627,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:9627: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:9630: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:9631: \$? = $ac_status" >&5
+   echo "$as_me:9634: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -11991,7 +11994,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11994 "configure"
+#line 11997 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12087,7 +12090,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12090 "configure"
+#line 12093 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -17918,29 +17921,27 @@ EOF
 fi
 
 
-    if test "${enable_transcript-yes}" != "no"; then
-       { $as_echo "$as_me:${as_lineno-$LINENO}: checking for transcript dir location" >&5
-$as_echo_n "checking for transcript dir location... " >&6; }
-       if test "${enable_transcript-yes}" != "yes"; then
-           :
-       elif test -d "/var/log"; then
-           enable_transcript="/var/log/sudo-transcript"
-       elif test -d "/var/adm"; then
-           enable_transcript="/var/adm/sudo-transcript"
-       else
-           enable_transcript="/usr/adm/sudo-transcript"
-       fi
-       cat >>confdefs.h <<EOF
-#define _PATH_SUDO_TRANSCRIPT "$enable_transcript"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for I/O log dir location" >&5
+$as_echo_n "checking for I/O log dir location... " >&6; }
+    if test "${with_iologdir-yes}" != "yes"; then
+       :
+    elif test -d "/var/log"; then
+       with_iologdir="/var/log/sudo-io"
+    elif test -d "/var/adm"; then
+       with_iologdir="/var/adm/sudo-io"
+    else
+       with_iologdir="/usr/adm/sudo-io"
+    fi
+    cat >>confdefs.h <<EOF
+#define _PATH_SUDO_IO_LOGDIR "$with_iologdir"
 EOF
 
-       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_transcript" >&5
-$as_echo "$enable_transcript" >&6; }
-    fi
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_iologdir" >&5
+$as_echo "$with_iologdir" >&6; }
 
 
-if test "${enable_transcript-yes}" != "no"; then
-    # currently require POSIX job control for transcript support
+if test "${with_iologdir-yes}" != "no"; then
+    # Require POSIX job control for I/O log support
     for ac_func in tcsetpgrp
 do :
   ac_fn_c_check_func "$LINENO" "tcsetpgrp" "ac_cv_func_tcsetpgrp"
@@ -17949,7 +17950,7 @@ if test "x$ac_cv_func_tcsetpgrp" = x""yes; then :
 #define HAVE_TCSETPGRP 1
 _ACEOF
 
-       SUDO_OBJS="${SUDO_OBJS} pty.o script.o"
+       SUDO_OBJS="${SUDO_OBJS} exec.o iolog.o pty.o"
        PROGS="$PROGS sudoreplay"
        REPLAY=""
 
@@ -18034,9 +18035,9 @@ fi
 
 else
 
-       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Disabling transcript support due to lack of tcsetpgrp function" >&5
-$as_echo "$as_me: WARNING: Disabling transcript support due to lack of tcsetpgrp function" >&2;}
-       enable_transcript=no
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Disabling I/O log support due to lack of tcsetpgrp function" >&5
+$as_echo "$as_me: WARNING: Disabling I/O log support due to lack of tcsetpgrp function" >&2;}
+       with_iologdir=no
 
 fi
 done
index 9f1e0c4473f0024642358fe4eb8021d0abd76de1..82151ea72855228219a3e4090f57b0e835c9e128 100644 (file)
@@ -696,6 +696,13 @@ AC_ARG_WITH(timedir, [AS_HELP_STRING([--with-timedir], [path to the sudo timesta
                ;;
 esac])
 
+AC_ARG_WITH(iologdir, [AS_HELP_STRING([--with-iologdir=DIR], [directory to store sudo I/O log files in])],
+[case $with_iologdir in
+    yes)       ;;
+    no)                AC_MSG_ERROR(["--without-iologdir not supported."])
+               ;;
+esac])
+
 AC_ARG_WITH(sendmail, [AS_HELP_STRING([--with-sendmail], [set path to sendmail])
 AS_HELP_STRING([--without-sendmail], [do not send mail at all])],
 [case $with_sendmail in
@@ -1224,10 +1231,6 @@ AC_ARG_ENABLE(env_debug,
   esac
 ], AC_MSG_RESULT(no))
 
-dnl we check enable_transcript later
-AC_ARG_ENABLE(transcript,
-[AS_HELP_STRING([--enable-transcript[[=DIR]]], [Enable Sudo's transcript support, storing them in DIR])])
-
 AC_ARG_ENABLE(warnings,
 [AS_HELP_STRING([--enable-warnings], [Whether to enable compiler warnings])],
 [ case "$enableval" in
@@ -2626,19 +2629,19 @@ if test -n "$blibpath"; then
 fi
 
 dnl
-dnl Check for log file, timestamp and transcript locations
+dnl Check for log file, timestamp and iolog locations
 dnl
 SUDO_LOGFILE
 SUDO_TIMEDIR
-SUDO_TRANSCRIPT
+SUDO_IO_LOGDIR
 
 dnl
-dnl If transcript is enabled, build sudoreplay and add pty.o script.o for sudo
+dnl If I/O logging is enabled, build sudoreplay and add iolog.o pty.o for sudo
 dnl
-if test "${enable_transcript-yes}" != "no"; then
-    # currently require POSIX job control for transcript support
+if test "${with_iologdir-yes}" != "no"; then
+    # Require POSIX job control for I/O log support
     AC_CHECK_FUNCS(tcsetpgrp, [
-       SUDO_OBJS="${SUDO_OBJS} pty.o script.o"
+       SUDO_OBJS="${SUDO_OBJS} iolog.o pty.o"
        PROGS="$PROGS sudoreplay"
        REPLAY=""
 
@@ -2663,8 +2666,8 @@ if test "${enable_transcript-yes}" != "no"; then
            ])
        fi
     ], [
-       AC_MSG_WARN([Disabling transcript support due to lack of tcsetpgrp function])
-       enable_transcript=no
+       AC_MSG_WARN([Disabling I/O log support due to lack of tcsetpgrp function])
+       with_iologdir=no
     ])
 fi
 
index b16d128bb6aaae510b340d01d3dcd20482462192..fe46789862a8ec466edeb6c66e6f388be736e869 100644 (file)
@@ -315,12 +315,16 @@ struct sudo_defs_types sudo_defs_table[] = {
        "The umask specified in sudoers will override the user's, even if it is more permissive",
        NULL,
     }, {
-       "transcript", T_FLAG,
-       "Log a transcript of the command being run",
+       "log_input", T_FLAG,
+       "Log user's input for the command being run",
        NULL,
     }, {
-       "compress_transcript", T_FLAG,
-       "Compress session transcripts with zlib",
+       "log_output", T_FLAG,
+       "Log the output of the command being run",
+       NULL,
+    }, {
+       "compress_io", T_FLAG,
+       "Compress I/O logs using zlib",
        NULL,
     }, {
        NULL, 0, NULL
index 9e9e80d378f3587ad39ee2ab5efcb416a0727989..96652af2c48c46dfdd1bbbe004f5bd9109c07b91 100644 (file)
 #define I_FAST_GLOB             71
 #define def_umask_override      (sudo_defs_table[72].sd_un.flag)
 #define I_UMASK_OVERRIDE        72
-#define def_transcript          (sudo_defs_table[73].sd_un.flag)
-#define I_TRANSCRIPT            73
-#define def_compress_transcript (sudo_defs_table[74].sd_un.flag)
-#define I_COMPRESS_TRANSCRIPT   74
+#define def_log_input           (sudo_defs_table[73].sd_un.flag)
+#define I_LOG_INPUT             73
+#define def_log_output          (sudo_defs_table[74].sd_un.flag)
+#define I_LOG_OUTPUT            74
+#define def_compress_io         (sudo_defs_table[75].sd_un.flag)
+#define I_COMPRESS_IO           75
 
 enum def_tupple {
        never,
index a5b8e9d4117950457a2903707f40931319f18008..56418397dc0cf2cbaec1f80a0906e7c0981710de 100644 (file)
@@ -232,9 +232,12 @@ fast_glob
 umask_override
        T_FLAG
        "The umask specified in sudoers will override the user's, even if it is more permissive"
-transcript
+log_input
        T_FLAG
-       "Log a transcript of the command being run"
-compress_transcript
+       "Log user's input for the command being run"
+log_output
        T_FLAG
-       "Compress session transcripts with zlib"
+       "Log the output of the command being run"
+compress_io
+       T_FLAG
+       "Compress I/O logs using zlib"
index 081cf725f684f4bcd6b0ffc2400154010672454e..ce68f05da799bd5a3ad76233eec63912b952a760 100644 (file)
@@ -470,7 +470,7 @@ init_defaults()
     def_passwd_timeout = PASSWORD_TIMEOUT;
     def_passwd_tries = TRIES_FOR_PASSWORD;
 #ifdef HAVE_ZLIB
-    def_compress_transcript = TRUE;
+    def_compress_io = TRUE;
 #endif
 
     /* Now do the strings */
diff --git a/exec.c b/exec.c
new file mode 100644 (file)
index 0000000..349c224
--- /dev/null
+++ b/exec.c
@@ -0,0 +1,1227 @@
+/*
+ * Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#ifdef HAVE_TERMIOS_H
+# include <termios.h>
+#else
+# include <termio.h>
+#endif /* HAVE_TERMIOS_H */
+#include <sys/ioctl.h>
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif /* HAVE_SYS_SELECT_H */
+#include <stdio.h>
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif /* STDC_HEADERS */
+#ifdef HAVE_STRING_H
+# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
+#  include <memory.h>
+# endif
+# include <string.h>
+#else
+# ifdef HAVE_STRINGS_H
+#  include <strings.h>
+# endif
+#endif /* HAVE_STRING_H */
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#if TIME_WITH_SYS_TIME
+# include <time.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#ifdef HAVE_SELINUX
+# include <selinux/selinux.h>
+#endif
+
+#if !defined(NSIG)
+# if defined(_NSIG)
+#  define NSIG _NSIG
+# elif defined(__NSIG)
+#  define NSIG __NSIG
+# else
+#  define NSIG 64
+# endif
+#endif
+
+#include "sudo.h"
+
+#define SFD_STDIN      0
+#define SFD_STDOUT     1
+#define SFD_STDERR     2
+#define SFD_MASTER     3
+#define SFD_SLAVE      4
+#define SFD_USERTTY    5
+
+#define TERM_COOKED    0
+#define TERM_RAW       1
+
+/* Compatibility with older tty systems. */
+#if !defined(TIOCGSIZE) && defined(TIOCGWINSZ)
+# define TIOCGSIZE     TIOCGWINSZ
+# define TIOCSSIZE     TIOCSWINSZ
+# define ttysize       winsize
+# define ts_cols       ws_col
+#endif
+
+struct io_buffer {
+    struct io_buffer *next;
+    int len; /* buffer length (how much produced) */
+    int off; /* write position (how much already consumed) */
+    int rfd;  /* reader (producer) */
+    int wfd; /* writer (consumer) */
+    int (*action)(const char *buf, unsigned int len);
+    char buf[16 * 1024];
+};
+
+static int io_fds[6] = { -1, -1, -1, -1, -1, -1};
+static int pipeline = FALSE;
+
+static sig_atomic_t recvsig[NSIG];
+
+static pid_t ppgrp, child;
+static int foreground;
+static int ttymode = TERM_COOKED;
+static int tty_initialized;
+
+static char slavename[PATH_MAX];
+
+static int suspend_parent(int signo, struct io_buffer *iobufs);
+static void flush_output(struct io_buffer *iobufs);
+static int perform_io(struct io_buffer *iobufs, fd_set *fdsr, fd_set *fdsw);
+static void handler(int s);
+static int exec_monitor(const char *path, char *argv[],
+    char *envp[], int, int);
+static void exec_pty(const char *path, char *argv[],
+    char *envp[], int);
+static void sigwinch(int s);
+static void sync_ttysize(int src, int dst);
+static void deliver_signal(pid_t pid, int signo);
+static int safe_close(int fd);
+
+static void
+pty_setup()
+{
+    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],
+           slavename, sizeof(slavename)))
+           error(1, "Can't get pty");
+    }
+}
+
+static void
+check_foreground(void)
+{
+    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;
+               sync_ttysize(io_fds[SFD_USERTTY], io_fds[SFD_SLAVE]);
+           }
+       }
+    }
+}
+
+/*
+ * Suspend sudo if the underlying command is suspended.
+ * Returns SIGUSR1 if the child should be resume in foreground else SIGUSR2.
+ */
+static int
+suspend_parent(int signo, struct io_buffer *iobufs)
+{
+    sigaction_t sa, osa;
+    int n, oldmode = ttymode, rval = 0;
+
+    switch (signo) {
+    case SIGTTOU:
+    case SIGTTIN:
+       /*
+        * If we are the foreground process, just resume the child.
+        * Otherwise, re-send the signal with the handler disabled.
+        */
+       if (!foreground)
+           check_foreground();
+       if (foreground) {
+           if (ttymode != TERM_RAW) {
+               do {
+                   n = term_raw(io_fds[SFD_USERTTY], 0);
+               } while (!n && errno == EINTR);
+               ttymode = TERM_RAW;
+           }
+           rval = SIGUSR1; /* resume child in foreground */
+           break;
+       }
+       ttymode = TERM_RAW;
+       /* FALLTHROUGH */
+    case SIGSTOP:
+    case SIGTSTP:
+       /* Flush any remaining output before suspending. */
+       flush_output(iobufs);
+
+       /* Restore original tty mode before suspending. */
+       if (oldmode != TERM_COOKED) {
+           do {
+               n = term_restore(io_fds[SFD_USERTTY], 0);
+           } while (!n && errno == EINTR);
+       }
+
+       /* Suspend self and continue child when we resume. */
+       sa.sa_handler = SIG_DFL;
+       sigaction(signo, &sa, &osa);
+       killpg(ppgrp, signo);
+
+       /* Check foreground/background status on resume. */
+       check_foreground();
+
+       /*
+        * Only modify term if we are foreground process and either
+        * the old tty mode was not cooked or child got SIGTT{IN,OU}
+        */
+       if (ttymode != TERM_COOKED) {
+           if (foreground) {
+               /* Set raw mode. */
+               do {
+                   n = term_raw(io_fds[SFD_USERTTY], 0);
+               } while (!n && errno == EINTR);
+           } else {
+               /* Background process, no access to tty. */
+               ttymode = TERM_COOKED;
+           }
+       }
+
+       sigaction(signo, &osa, NULL);
+       rval = ttymode == TERM_RAW ? SIGUSR1 : SIGUSR2;
+       break;
+    }
+
+    return(rval);
+}
+
+/*
+ * Like execve(2) but falls back to running through /bin/sh
+ * ala execvp(3) if we get ENOEXEC.
+ */
+static int
+my_execve(const char *path, char *argv[], char *envp[])
+{
+    execve(path, argv, envp);
+    if (errno == ENOEXEC) {
+       argv--;                 /* at least one extra slot... */
+       argv[0] = "sh";
+       argv[1] = (char *)path;
+       execve(_PATH_BSHELL, argv, envp);
+    }
+    return -1;
+}
+
+static void
+terminate_child(pid_t pid, int use_pgrp)
+{
+    /*
+     * Kill child with increasing urgency.
+     * Note that SIGCHLD will interrupt the sleep()
+     */
+    if (use_pgrp) {
+       killpg(pid, SIGHUP);
+       killpg(pid, SIGTERM);
+       sleep(2);
+       killpg(pid, SIGKILL);
+    } else {
+       kill(pid, SIGHUP);
+       kill(pid, SIGTERM);
+       sleep(2);
+       kill(pid, SIGKILL);
+    }
+}
+
+static struct io_buffer *
+io_buf_new(int rfd, int wfd, int (*action)(const char *, unsigned int),
+    struct io_buffer *head)
+{
+    struct io_buffer *iob;
+
+    iob = emalloc(sizeof(*iob));
+    zero_bytes(iob, sizeof(*iob));
+    iob->rfd = rfd;
+    iob->wfd = wfd;
+    iob->action = action;
+    iob->next = head;
+    return iob;
+}
+
+/*
+ * Read/write iobufs depending on fdsr and fdsw.
+ * Returns the number of errors.
+ */
+static int
+perform_io(struct io_buffer *iobufs, fd_set *fdsr, fd_set *fdsw)
+{
+    struct io_buffer *iob;
+    int n, errors = 0;
+
+    for (iob = iobufs; iob; iob = iob->next) {
+       if (iob->rfd != -1 && FD_ISSET(iob->rfd, fdsr)) {
+           do {
+               n = read(iob->rfd, iob->buf + iob->len,
+                   sizeof(iob->buf) - iob->len);
+           } while (n == -1 && errno == EINTR);
+           if (n == -1) {
+               if (errno != EAGAIN)
+                   break;
+           } else if (n == 0) {
+               /* got EOF */
+               safe_close(iob->rfd);
+               iob->rfd = -1;
+           } else {
+               if (!iob->action(iob->buf + iob->len, n))
+                   terminate_child(child, TRUE);
+               iob->len += n;
+           }
+       }
+       if (iob->wfd != -1 && FD_ISSET(iob->wfd, fdsw)) {
+           do {
+               n = write(iob->wfd, iob->buf + iob->off,
+                   iob->len - iob->off);
+           } while (n == -1 && errno == EINTR);
+           if (n == -1) {
+               if (errno == EPIPE) {
+                   /* other end of pipe closed */
+                   if (iob->rfd != -1) {
+                       safe_close(iob->rfd);
+                       iob->rfd = -1;
+                   }
+                   safe_close(iob->wfd);
+                   iob->wfd = -1;
+                   continue;
+               }
+               if (errno != EAGAIN)
+                   errors++;
+           } else {
+               iob->off += n;
+           }
+       }
+    }
+    return errors;
+}
+
+/*
+ * This is a little bit tricky due to how POSIX job control works and
+ * we fact that we have two different controlling terminals to deal with.
+ * There are three processes:
+ *  1) parent, which forks a child and does all the I/O passing.
+ *     Handles job control signals send by its child to bridge the
+ *     two sessions (and ttys).
+ *  2) child, creates a new session so it can receive notification of
+ *     tty stop signals (SIGTSTP, SIGTTIN, SIGTTOU).  Waits for the
+ *     command to stop or die and passes back tty stop signals to parent
+ *     so job control works in the user's shell.
+ *  3) grandchild, executes the actual command with the pty slave as its
+ *     controlling tty, belongs to child's session but has its own pgrp.
+ */
+int
+sudo_execve(const char *path, char *argv[], char *envp[],
+    struct command_status *cstat)
+{
+    sigaction_t sa;
+    struct io_buffer *iob, *iobufs = NULL;
+    int n, nready;
+    int io_pipe[3][2], sv[2];
+    fd_set *fdsr, *fdsw;
+    int rbac_enabled = 0;
+    int log_io, maxfd, status;
+
+    cstat->type = CMD_INVALID;
+
+    log_io = def_log_output || def_log_input;
+    if (log_io) {
+       pty_setup();
+       io_log_open();
+    }
+
+#ifdef HAVE_SELINUX
+    rbac_enabled = is_selinux_enabled() > 0 && user_role != NULL;
+    if (rbac_enabled) {
+       /* Must do SELinux setup before changing uid. */
+       selinux_setup(user_role, user_type,
+           log_io ? slavename : user_ttypath, io_fds[SFD_SLAVE]);
+    }
+#endif
+
+    ppgrp = getpgrp(); /* parent's pgrp, so child can signal us */
+
+    /*
+     * 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");
+
+    zero_bytes(&sa, sizeof(sa));
+    sigemptyset(&sa.sa_mask);
+
+    /* Note: HP-UX select() will not be interrupted if SA_RESTART set */
+    sa.sa_flags = 0; /* do not restart syscalls */
+    sa.sa_handler = handler;
+    sigaction(SIGALRM, &sa, NULL);
+    sigaction(SIGCHLD, &sa, NULL);
+    sigaction(SIGHUP, &sa, NULL);
+    sigaction(SIGINT, &sa, NULL);
+    sigaction(SIGPIPE, &sa, NULL);
+    sigaction(SIGQUIT, &sa, NULL);
+    sigaction(SIGTERM, &sa, NULL);
+
+    if (log_io) {
+       if (io_fds[SFD_USERTTY] != -1) {
+           sa.sa_flags = SA_RESTART;
+           sa.sa_handler = sigwinch;
+           sigaction(SIGWINCH, &sa, NULL);
+       }
+
+       /*
+        * Setup stdin/stdout/stderr for child, to be duped after forking.
+        */
+       io_fds[SFD_STDIN] = io_fds[SFD_SLAVE];
+       io_fds[SFD_STDOUT] = io_fds[SFD_SLAVE];
+       io_fds[SFD_STDERR] = io_fds[SFD_SLAVE];
+
+       /* Copy /dev/tty -> pty master */
+       if (io_fds[SFD_USERTTY] != -1) {
+           iobufs = io_buf_new(io_fds[SFD_USERTTY], io_fds[SFD_MASTER],
+               log_ttyin, iobufs);
+
+           /* Copy pty master -> /dev/tty */
+           iobufs = io_buf_new(io_fds[SFD_MASTER], io_fds[SFD_USERTTY],
+               log_ttyout, iobufs);
+
+           /* Are we the foreground process? */
+           foreground = tcgetpgrp(io_fds[SFD_USERTTY]) == ppgrp;
+       }
+
+       /*
+        * If either stdin, stdout or stderr is not a tty we use a pipe
+        * to interpose ourselves instead of duping the pty fd.
+        */
+       memset(io_pipe, 0, sizeof(io_pipe));
+       if (!isatty(STDIN_FILENO)) {
+           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],
+               log_stdin, iobufs);
+           io_fds[SFD_STDIN] = io_pipe[STDIN_FILENO][0];
+       }
+       if (!isatty(STDOUT_FILENO)) {
+           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,
+               log_stdout, iobufs);
+           io_fds[SFD_STDOUT] = io_pipe[STDOUT_FILENO][1];
+       }
+       if (!isatty(STDERR_FILENO)) {
+           if (pipe(io_pipe[STDERR_FILENO]) != 0)
+               error(1, "unable to create pipe");
+           iobufs = io_buf_new(io_pipe[STDERR_FILENO][0], STDERR_FILENO,
+               log_stderr, iobufs);
+           io_fds[SFD_STDERR] = io_pipe[STDERR_FILENO][1];
+       }
+
+       /* Job control signals to relay from parent to child. */
+       sa.sa_flags = 0; /* do not restart syscalls */
+       sa.sa_handler = handler;
+       sigaction(SIGTSTP, &sa, NULL);
+#if 0 /* XXX - add these? */
+       sigaction(SIGTTIN, &sa, NULL);
+       sigaction(SIGTTOU, &sa, NULL);
+#endif
+
+       if (foreground) {
+           /* Copy terminal attrs from user tty -> pty slave. */
+           if (term_copy(io_fds[SFD_USERTTY], io_fds[SFD_SLAVE])) {
+               tty_initialized = 1;
+               sync_ttysize(io_fds[SFD_USERTTY], io_fds[SFD_SLAVE]);
+           }
+
+           /* Start out in raw mode if we are not part of a pipeline. */
+           if (!pipeline) {
+               ttymode = TERM_RAW;
+               do {
+                   n = term_raw(io_fds[SFD_USERTTY], 0);
+               } while (!n && errno == EINTR);
+               if (!n)
+                   error(1, "Can't set terminal to raw mode");
+           }
+       }
+    }
+
+    /*
+     * Child will run the command in the pty, parent will pass data
+     * to and from pty.
+     */
+    child = fork();
+    switch (child) {
+    case -1:
+       error(1, "fork");
+       break;
+    case 0:
+       /* child */
+       close(sv[0]);
+       fcntl(sv[1], F_SETFD, FD_CLOEXEC);
+       if (exec_setup() == TRUE) {
+           /* headed for execve() */
+           if (log_io) {
+               /* Close the other end of the stdin/stdout/stderr pipes. */
+               if (io_pipe[STDIN_FILENO][1])
+                   close(io_pipe[STDIN_FILENO][1]);
+               if (io_pipe[STDOUT_FILENO][0])
+                   close(io_pipe[STDOUT_FILENO][0]);
+               if (io_pipe[STDERR_FILENO][0])
+                   close(io_pipe[STDERR_FILENO][0]);
+               exec_monitor(path, argv, envp, sv[1], rbac_enabled);
+           } else {
+               closefrom(def_closefrom);
+#ifdef HAVE_SELINUX
+               if (rbac_enabled)
+                   selinux_execve(path, argv, envp);
+               else
+#endif
+                   my_execve(path, argv, envp);
+           }
+       }
+       cstat->type = CMD_ERRNO;
+       cstat->val = errno;
+       send(sv[1], cstat, sizeof(*cstat), 0);
+       _exit(1);
+    }
+    close(sv[1]);
+
+    /* Max fd we will be selecting on. */
+    maxfd = sv[0];
+
+    if (log_io) {
+       /* Close the other end of the stdin/stdout/stderr pipes. */
+       if (io_pipe[STDIN_FILENO][0])
+           close(io_pipe[STDIN_FILENO][0]);
+       if (io_pipe[STDOUT_FILENO][1])
+           close(io_pipe[STDOUT_FILENO][1]);
+       if (io_pipe[STDERR_FILENO][1])
+           close(io_pipe[STDERR_FILENO][1]);
+
+       for (iob = iobufs; iob; iob = iob->next) {
+           /* Determine maxfd */
+           if (iob->rfd > maxfd)
+               maxfd = iob->rfd;
+           if (iob->wfd > maxfd)
+               maxfd = iob->wfd;
+
+           /* Set non-blocking mode. */
+           n = fcntl(iob->rfd, F_GETFL, 0);
+           if (n != -1 && !ISSET(n, O_NONBLOCK))
+               (void) fcntl(iob->rfd, F_SETFL, n | O_NONBLOCK);
+           n = fcntl(iob->wfd, F_GETFL, 0);
+           if (n != -1 && !ISSET(n, O_NONBLOCK))
+               (void) fcntl(iob->wfd, F_SETFL, n | O_NONBLOCK);
+       }
+    }
+
+    /*
+     * In the event loop we pass input from user tty to master
+     * and pass output from master to stdout and IO plugin.
+     */
+    fdsr = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
+    fdsw = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
+    for (;;) {
+       if (recvsig[SIGCHLD]) {
+           pid_t pid;
+
+           /*
+            * If logging I/O, child is the intermediate process,
+            * otherwise it is the command itself.
+            */
+           recvsig[SIGCHLD] = FALSE;
+           do {
+               pid = waitpid(child, &status, WNOHANG);
+           } while (pid == -1 && errno == EINTR);
+           if (pid == child) {
+               /* If not logging I/O and child has exited we are done. */
+               if (!log_io) {
+                   cstat->type = CMD_WSTATUS;
+                   cstat->val = status;
+                   return 0;
+               }
+           }
+       }
+
+       zero_bytes(fdsw, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
+       zero_bytes(fdsr, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
+
+       FD_SET(sv[0], fdsr);
+       for (iob = iobufs; iob; iob = iob->next) {
+           if (iob->rfd == -1 && iob->wfd == -1)
+               continue;
+           if (iob->off == iob->len) {
+               iob->off = iob->len = 0;
+               /* Forward the EOF from reader to writer. */
+               if (iob->rfd == -1) {
+                   safe_close(iob->wfd);
+                   iob->wfd = -1;
+               }
+           }
+           /* Don't read/write /dev/tty if we are not in the foreground. */
+           if (iob->rfd != -1 &&
+               (ttymode == TERM_RAW || iob->rfd != io_fds[SFD_USERTTY])) {
+               if (iob->len != sizeof(iob->buf))
+                   FD_SET(iob->rfd, fdsr);
+           }
+           if (iob->wfd != -1 &&
+               (foreground || iob->wfd != io_fds[SFD_USERTTY])) {
+               if (iob->len > iob->off)
+                   FD_SET(iob->wfd, fdsw);
+           }
+       }
+       for (n = 0; n < NSIG; n++) {
+           if (recvsig[n] && n != SIGCHLD) {
+               if (log_io) {
+                   FD_SET(sv[0], fdsw);
+                   break;
+               } else {
+                   /* nothing listening on sv[0], send directly */
+                   if (n == SIGALRM) {
+                       terminate_child(child, FALSE);
+                   } else {
+                       kill(child, n);
+                   }
+               }
+           }
+       }
+
+       if (recvsig[SIGCHLD])
+           continue;
+       nready = select(maxfd + 1, fdsr, fdsw, NULL, NULL);
+       if (nready == -1) {
+           if (errno == EINTR)
+               continue;
+           error(1, "select failed");
+       }
+       if (FD_ISSET(sv[0], fdsr)) {
+           /* read child status */
+           n = recv(sv[0], cstat, sizeof(*cstat), 0);
+           if (n == -1) {
+               if (errno == EINTR)
+                   continue;
+               /*
+                * If not logging I/O we will receive ECONNRESET when
+                * the command is executed.  It is safe to ignore this.
+                */
+               if (log_io && errno != EAGAIN) {
+                   cstat->type = CMD_ERRNO;
+                   cstat->val = errno;
+                   break;
+               }
+           }
+           if (cstat->type == CMD_WSTATUS) {
+               if (WIFSTOPPED(cstat->val)) {
+                   /* Suspend parent and tell child how to resume on return. */
+                   n = suspend_parent(WSTOPSIG(cstat->val), iobufs);
+                   recvsig[n] = TRUE;
+                   continue;
+               } else {
+                   /* Child exited or was killed, either way we are done. */
+                   break;
+               }
+           } else if (cstat->type == CMD_ERRNO) {
+               /* Child was unable to execute command or broken pipe. */
+               break;
+           }
+       }
+
+       if (FD_ISSET(sv[0], fdsw)) {
+           for (n = 0; n < NSIG; n++) {
+               if (!recvsig[n])
+                   continue;
+               recvsig[n] = FALSE;
+               cstat->type = CMD_SIGNO;
+               cstat->val = n;
+               do {
+                   n = send(sv[0], cstat, sizeof(*cstat), 0);
+               } while (n == -1 && errno == EINTR);
+               if (n != sizeof(*cstat)) {
+                   recvsig[n] = TRUE;
+                   break;
+               }
+           }
+       }
+       if (perform_io(iobufs, fdsr, fdsw) != 0) {
+           cstat->type = CMD_ERRNO;
+           cstat->val = errno;
+           break;
+       }
+    }
+
+    if (log_io) {
+       /* Flush any remaining output (the plugin already got it) */
+       if (io_fds[SFD_USERTTY] != -1) {
+           n = fcntl(io_fds[SFD_USERTTY], F_GETFL, 0);
+           if (n != -1 && ISSET(n, O_NONBLOCK)) {
+               CLR(n, O_NONBLOCK);
+               (void) fcntl(io_fds[SFD_USERTTY], F_SETFL, n);
+           }
+       }
+       flush_output(iobufs);
+
+       if (io_fds[SFD_USERTTY] != -1) {
+           do {
+               n = term_restore(io_fds[SFD_USERTTY], 0);
+           } while (!n && errno == EINTR);
+       }
+
+       if (cstat->type == CMD_WSTATUS && WIFSIGNALED(cstat->val)) {
+           int signo = WTERMSIG(cstat->val);
+           if (signo && signo != SIGINT && signo != SIGPIPE) {
+               char *reason = strsignal(signo);
+               n = io_fds[SFD_USERTTY] != -1 ?
+                   io_fds[SFD_USERTTY] : STDOUT_FILENO;
+               write(n, reason, strlen(reason));
+               if (WCOREDUMP(cstat->val))
+                   write(n, " (core dumped)", 14);
+               write(n, "\n", 1);
+           }
+       }
+    }
+
+#ifdef HAVE_SELINUX
+    if (rbac_enabled) {
+       /* This is probably not needed in log_io mode. */
+       if (selinux_restore_tty() != 0)
+           warningx("unable to restore tty label");
+    }
+#endif
+
+    efree(fdsr);
+    efree(fdsw);
+    while ((iob = iobufs) != NULL) {
+       iobufs = iobufs->next;
+       efree(iob);
+    }
+
+    return cstat->type == CMD_ERRNO ? -1 : 0;
+}
+
+static void
+deliver_signal(pid_t pid, int signo)
+{
+    int status;
+
+    /* Handle signal from parent. */
+    switch (signo) {
+    case SIGKILL:
+       _exit(1); /* XXX */
+       /* NOTREACHED */
+    case SIGPIPE:
+    case SIGHUP:
+    case SIGTERM:
+    case SIGINT:
+    case SIGQUIT:
+    case SIGTSTP:
+       /* relay signal to child */
+       killpg(pid, signo);
+       break;
+    case SIGALRM:
+       terminate_child(pid, TRUE);
+       break;
+    case SIGUSR1:
+       /* foreground process, grant it controlling tty. */
+       do {
+           status = tcsetpgrp(io_fds[SFD_SLAVE], pid);
+       } while (status == -1 && errno == EINTR);
+       killpg(pid, SIGCONT);
+       break;
+    case SIGUSR2:
+       /* background process, I take controlling tty. */
+       do {
+           status = tcsetpgrp(io_fds[SFD_SLAVE], getpid());
+       } while (status == -1 && errno == EINTR);
+       killpg(pid, SIGCONT);
+       break;
+    default:
+       warningx("unexpected signal from child: %d", signo);
+       break;
+    }
+}
+
+/*
+ * Send status to parent over socketpair.
+ * Return value is the same as send(2).
+ */
+static int
+send_status(int fd, struct command_status *cstat)
+{
+    int n = -1;
+
+    if (cstat->type != CMD_INVALID) {
+       do {
+           n = send(fd, cstat, sizeof(*cstat), 0);
+       } while (n == -1 && errno == EINTR);
+       cstat->type = CMD_INVALID; /* prevent re-sending */
+    }
+    return 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.
+ */
+static int
+handle_sigchld(int backchannel, struct command_status *cstat)
+{
+    int status, alive = TRUE;
+    pid_t pid;
+
+    /* read child status */
+    do {
+       pid = waitpid(child, &status, WUNTRACED|WNOHANG);
+    } while (pid == -1 && errno == EINTR);
+    if (pid == child) {
+       if (cstat->type != CMD_ERRNO) {
+           cstat->type = CMD_WSTATUS;
+           cstat->val = status;
+           if (WIFSTOPPED(status)) {
+               if (send_status(backchannel, cstat) == -1)
+                   return alive; /* XXX */
+           }
+       }
+       if (!WIFSTOPPED(status))
+           alive = FALSE;
+    }
+    return alive;
+}
+
+/*
+ * Monitor process that creates a new session with the controlling tty,
+ * resets signal handlers and forks a child to call exec_pty().
+ * Waits for status changes from the command and relays them to the
+ * parent and relays signals from the parent to the command.
+ * Returns an error if fork(2) fails, else calls _exit(2).
+ */
+int
+exec_monitor(const char *path, char *argv[], char *envp[],
+    int backchannel, int rbac)
+{
+    struct command_status cstat;
+    struct timeval tv;
+    fd_set *fdsr;
+    sigaction_t sa;
+    int errpipe[2], maxfd, n, status;
+    int alive = TRUE;
+
+    /* Close unused fds. */
+    if (io_fds[SFD_MASTER] != -1)
+       close(io_fds[SFD_MASTER]);
+    if (io_fds[SFD_USERTTY] != -1)
+       close(io_fds[SFD_USERTTY]);
+
+    /* Reset SIGWINCH and SIGALRM. */
+    zero_bytes(&sa, sizeof(sa));
+    sigemptyset(&sa.sa_mask);
+    sa.sa_flags = SA_RESTART;
+    sa.sa_handler = SIG_DFL;
+    sigaction(SIGWINCH, &sa, NULL);
+    sigaction(SIGALRM, &sa, NULL);
+
+    /* Ignore any SIGTTIN or SIGTTOU we get. */
+    sa.sa_handler = SIG_IGN;
+    sigaction(SIGTTIN, &sa, NULL);
+    sigaction(SIGTTOU, &sa, NULL);
+
+    /* Note: HP-UX select() will not be interrupted if SA_RESTART set */
+    sa.sa_flags = 0;
+    sa.sa_handler = handler;
+    sigaction(SIGCHLD, &sa, NULL);
+
+    /*
+     * Start a new session with the parent as the session leader
+     * and the slave pty as the controlling terminal.
+     * This allows us to be notified when the child has been suspended.
+     */
+#ifdef HAVE_SETSID
+    if (setsid() == -1) {
+       warning("setsid");
+       goto bad;
+    }
+#else
+# ifdef TIOCNOTTY
+    n = open(_PATH_TTY, O_RDWR|O_NOCTTY);
+    if (n >= 0) {
+       /* Disconnect from old controlling tty. */
+       if (ioctl(n, TIOCNOTTY, NULL) == -1)
+           warning("cannot disconnect controlling tty");
+       close(n);
+    }
+# endif
+    setpgrp(0, 0);
+#endif
+    if (io_fds[SFD_SLAVE] != -1) {
+#ifdef TIOCSCTTY
+       if (ioctl(io_fds[SFD_SLAVE], TIOCSCTTY, NULL) != 0)
+           error(1, "unable to set controlling tty");
+#else
+       /* Set controlling tty by reopening slave. */
+       if ((n = open(slavename, O_RDWR)) >= 0)
+           close(n);
+#endif
+    }
+
+    /*
+     * If stdin/stdout is not a tty, start command in the background
+     * since it might be part of a pipeline that reads from /dev/tty.
+     * In this case, we rely on the command receiving SIGTTOU or SIGTTIN
+     * when it needs access to the controlling tty.
+     */
+    if (pipeline)
+       foreground = 0;
+
+    /* Start command and wait for it to stop or exit */
+    if (pipe(errpipe) == -1)
+       error(1, "unable to create pipe");
+    child = fork();
+    if (child == -1) {
+       warning("Can't fork");
+       goto bad;
+    }
+    if (child == 0) {
+       /* We pass errno back to our parent via pipe on exec failure. */
+       close(backchannel);
+       close(errpipe[0]);
+       fcntl(errpipe[1], F_SETFD, FD_CLOEXEC);
+
+       /* setup tty and exec command */
+       exec_pty(path, argv, envp, rbac);
+       cstat.type = CMD_ERRNO;
+       cstat.val = errno;
+       write(errpipe[1], &cstat, sizeof(cstat));
+       _exit(1);
+    }
+    close(errpipe[1]);
+
+    /* If any of stdin/stdout/stderr are pipes, close them in parent. */
+    if (io_fds[SFD_STDIN] != io_fds[SFD_SLAVE])
+       close(io_fds[SFD_STDIN]);
+    if (io_fds[SFD_STDOUT] != io_fds[SFD_SLAVE])
+       close(io_fds[SFD_STDOUT]);
+    if (io_fds[SFD_STDERR] != io_fds[SFD_SLAVE])
+       close(io_fds[SFD_STDERR]);
+
+    /*
+     * Put child in its own process group.  If we are starting the command
+     * in the foreground, assign its pgrp to the tty.
+     */
+    setpgid(child, child);
+    if (foreground) {
+       do {
+           status = tcsetpgrp(io_fds[SFD_SLAVE], child);
+       } while (status == -1 && errno == EINTR);
+    }
+
+    /* Wait for errno on pipe, signal on backchannel or for SIGCHLD */
+    maxfd = MAX(errpipe[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));
+    tv.tv_sec = 0;
+    tv.tv_usec = 0;
+    for (;;) {
+       /* Read child status. */
+       if (recvsig[SIGCHLD]) {
+           recvsig[SIGCHLD] = FALSE;
+           alive = handle_sigchld(backchannel, &cstat);
+       }
+
+       /* Check for signal on backchannel or errno on errpipe. */
+       FD_SET(backchannel, fdsr);
+       if (errpipe[0] != -1)
+           FD_SET(errpipe[0], fdsr);
+       maxfd = MAX(errpipe[0], backchannel);
+
+       if (recvsig[SIGCHLD])
+           continue;
+       /* If command exited we just poll, there may be data on errpipe. */
+       n = select(maxfd + 1, fdsr, NULL, NULL, alive ? NULL : &tv);
+       if (n <= 0) {
+           if (n == 0)
+               goto done;
+           if (errno == EINTR)
+               continue;
+           error(1, "select failed");
+       }
+
+       if (errpipe[0] != -1 && FD_ISSET(errpipe[0], fdsr)) {
+           /* read errno or EOF from command pipe */
+           n = read(errpipe[0], &cstat, sizeof(cstat));
+           if (n == -1) {
+               if (errno == EINTR)
+                   continue;
+               warning("error reading from pipe");
+               goto done;
+           }
+           /* Got errno or EOF, either way we are done with errpipe. */
+           FD_CLR(errpipe[0], fdsr);
+           close(errpipe[0]);
+           errpipe[0] = -1;
+       }
+       if (FD_ISSET(backchannel, fdsr)) {
+           struct command_status cstmp;
+
+           /* read command from backchannel, should be a signal */
+           n = recv(backchannel, &cstmp, sizeof(cstmp), 0);
+           if (n == -1) {
+               if (errno == EINTR)
+                   continue;
+               warning("error reading from socketpair");
+               goto done;
+           }
+           if (cstmp.type != CMD_SIGNO) {
+               warningx("unexpected reply type on backchannel: %d", cstmp.type);
+               continue;
+           }
+           deliver_signal(child, cstmp.val);
+       }
+    }
+
+done:
+    if (alive) {
+       /* XXX An error occurred, should send an error back. */
+       kill(child, SIGKILL);
+    } else {
+       /* Send parent status. */
+       send_status(backchannel, &cstat);
+    }
+    _exit(1);
+
+bad:
+    return errno;
+}
+
+/*
+ * Flush any output buffered in iobufs or readable from the fds.
+ * Does not read from /dev/tty.
+ */
+static void
+flush_output(struct io_buffer *iobufs)
+{
+    struct io_buffer *iob;
+    struct timeval tv;
+    fd_set *fdsr, *fdsw;
+    int nready, nwriters, maxfd = -1;
+
+    /* Determine maxfd */
+    for (iob = iobufs; iob; iob = iob->next) {
+       if (iob->rfd > maxfd)
+           maxfd = iob->rfd;
+       if (iob->wfd > maxfd)
+           maxfd = iob->wfd;
+    }
+    if (maxfd == -1)
+       return;
+
+    fdsr = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
+    fdsw = (fd_set *)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));
+
+       nwriters = 0;
+       for (iob = iobufs; iob; iob = iob->next) {
+           /* Don't read from /dev/tty while flushing. */
+           if (io_fds[SFD_USERTTY] != -1 && iob->rfd == io_fds[SFD_USERTTY])
+               continue;
+           if (iob->rfd == -1 && iob->wfd == -1)
+               continue;
+           if (iob->off == iob->len) {
+               iob->off = iob->len = 0;
+               /* Forward the EOF from reader to writer. */
+               if (iob->rfd == -1) {
+                   safe_close(iob->wfd);
+                   iob->wfd = -1;
+               }
+           }
+           if (iob->rfd != -1) {
+               if (iob->len != sizeof(iob->buf))
+                   FD_SET(iob->rfd, fdsr);
+           }
+           if (iob->wfd != -1) {
+               if (iob->len > iob->off) {
+                   nwriters++;
+                   FD_SET(iob->wfd, fdsw);
+               }
+           }
+       }
+
+       /* Don't sleep in select if there are no buffers that need writing. */
+       tv.tv_sec = 0;
+       tv.tv_usec = 0;
+       nready = select(maxfd + 1, fdsr, fdsw, NULL, nwriters ? NULL : &tv);
+       if (nready <= 0) {
+           if (nready == 0)
+               break; /* all I/O flushed */
+           if (errno == EINTR)
+               continue;
+           error(1, "select failed");
+       }
+       if (perform_io(iobufs, fdsr, fdsw) != 0)
+           break;
+    }
+    efree(fdsr);
+    efree(fdsw);
+}
+
+/*
+ * Sets up std{in,out,err} and executes the actual command.
+ * Returns only if execve() fails.
+ */
+static void
+exec_pty(const char *path, char *argv[], char *envp[],
+    int rbac_enabled)
+{
+    sigaction_t sa;
+    pid_t self = getpid();
+
+    /* Reset signal handlers. */
+    zero_bytes(&sa, sizeof(sa));
+    sigemptyset(&sa.sa_mask);
+    sa.sa_flags = SA_RESTART;
+    sa.sa_handler = SIG_DFL;
+    sigaction(SIGHUP, &sa, NULL);
+    sigaction(SIGTERM, &sa, NULL);
+    sigaction(SIGINT, &sa, NULL);
+    sigaction(SIGQUIT, &sa, NULL);
+    sigaction(SIGTSTP, &sa, NULL);
+    sigaction(SIGTTIN, &sa, NULL);
+    sigaction(SIGTTOU, &sa, NULL);
+    sigaction(SIGUSR1, &sa, NULL);
+    sigaction(SIGUSR2, &sa, NULL);
+    sigaction(SIGCHLD, &sa, NULL);
+
+    /* Set child process group here too to avoid a race. */
+    setpgid(0, self);
+
+    /* Wire up standard fds, note that stdout/stderr may be pipes. */
+    dup2(io_fds[SFD_STDIN], STDIN_FILENO);
+    dup2(io_fds[SFD_STDOUT], STDOUT_FILENO);
+    dup2(io_fds[SFD_STDERR], STDERR_FILENO);
+
+    /* Wait for parent to grant us the tty if we are foreground. */
+    if (foreground) {
+       while (tcgetpgrp(io_fds[SFD_SLAVE]) != self)
+           ; /* spin */
+    }
+
+    /* We have guaranteed that the slave fd is > 2 */
+    if (io_fds[SFD_SLAVE] != -1)
+       close(io_fds[SFD_SLAVE]);
+    if (io_fds[SFD_STDIN] != io_fds[SFD_SLAVE])
+       close(io_fds[SFD_STDIN]);
+    if (io_fds[SFD_STDOUT] != io_fds[SFD_SLAVE])
+       close(io_fds[SFD_STDOUT]);
+    if (io_fds[SFD_STDERR] != io_fds[SFD_SLAVE])
+       close(io_fds[SFD_STDERR]);
+
+    closefrom(def_closefrom);
+#ifdef HAVE_SELINUX
+    if (rbac_enabled)
+       selinux_execve(path, argv, envp);
+    else
+#endif
+       my_execve(path, argv, envp);
+}
+
+/*
+ * Propagates tty size change signals to pty being used by the command.
+ */
+static void
+sync_ttysize(int src, int dst)
+{
+#ifdef TIOCGSIZE
+    struct ttysize tsize;
+    pid_t pgrp;
+
+    if (ioctl(src, TIOCGSIZE, &tsize) == 0) {
+           ioctl(dst, TIOCSSIZE, &tsize);
+#ifdef TIOCGPGRP
+           if (ioctl(dst, TIOCGPGRP, &pgrp) == 0)
+                   killpg(pgrp, SIGWINCH);
+#endif
+    }
+#endif
+}
+
+/*
+ * Generic handler for signals passed from parent -> child.
+ * The recvsig[] array is checked in the main event loop.
+ */
+static void
+handler(int s)
+{
+    recvsig[s] = TRUE;
+}
+
+/*
+ * Handler for SIGWINCH in parent.
+ */
+static void
+sigwinch(int s)
+{
+    int serrno = errno;
+
+    sync_ttysize(io_fds[SFD_USERTTY], io_fds[SFD_SLAVE]);
+    errno = serrno;
+}
+
+/*
+ * Only close the fd if it is not /dev/tty or std{in,out,err}.
+ * Return value is the same as send(2).
+ */
+static int
+safe_close(int fd)
+{
+    /* Avoid closing /dev/tty or std{in,out,err}. */
+    if (fd < 3 || fd == io_fds[SFD_USERTTY]) {
+       errno = EINVAL;
+       return -1;
+    }
+    return close(fd);
+}
diff --git a/gram.c b/gram.c
index add47a40ab177fb08b5edd6db2554d058bc6d00e..f4fca2ea8b730d7d2197f748edc6070b3ace94b7 100644 (file)
--- a/gram.c
+++ b/gram.c
@@ -1,11 +1,3 @@
-#ifndef lint
-/*static char yysccsid[] = "from: @(#)yaccpar  1.9 (Berkeley) 02/21/93";*/
-static char yyrcsid[]
-#if __GNUC__ >= 2
-  __attribute__ ((unused))
-#endif /* __GNUC__ >= 2 */
-  = "$OpenBSD: skeleton.c,v 1.29 2008/07/08 15:06:50 otto Exp $";
-#endif
 #include <stdlib.h>
 #include <string.h>
 #define YYBYACC 1
@@ -127,7 +119,7 @@ yyerror(s)
     }
     parse_error = TRUE;
 }
-#line 117 "gram.y"
+#line 113 "gram.y"
 #ifndef YYSTYPE_DEFINED
 #define YYSTYPE_DEFINED
 typedef union {
@@ -143,7 +135,7 @@ typedef union {
     int tok;
 } YYSTYPE;
 #endif /* YYSTYPE_DEFINED */
-#line 151 "y.tab.c"
+#line 139 "y.tab.c"
 #define COMMAND 257
 #define ALIAS 258
 #define DEFVAR 259
@@ -162,17 +154,19 @@ typedef union {
 #define EXEC 272
 #define SETENV 273
 #define NOSETENV 274
-#define TRANSCRIPT 275
-#define NOTRANSCRIPT 276
-#define ALL 277
-#define COMMENT 278
-#define HOSTALIAS 279
-#define CMNDALIAS 280
-#define USERALIAS 281
-#define RUNASALIAS 282
-#define ERROR 283
-#define TYPE 284
-#define ROLE 285
+#define LOG_INPUT 275
+#define NOLOG_INPUT 276
+#define LOG_OUTPUT 277
+#define NOLOG_OUTPUT 278
+#define ALL 279
+#define COMMENT 280
+#define HOSTALIAS 281
+#define CMNDALIAS 282
+#define USERALIAS 283
+#define RUNASALIAS 284
+#define ERROR 285
+#define TYPE 286
+#define ROLE 287
 #define YYERRCODE 256
 #if defined(__cplusplus) || defined(__STDC__)
 const short yylhs[] =
@@ -185,11 +179,11 @@ short yylhs[] =
     3,    3,    3,   20,   20,   19,   10,   10,    8,    8,
     8,    8,    8,    2,    2,    1,    6,    6,   23,   24,
    22,   22,   22,   22,   22,   17,   17,   18,   18,   18,
-   21,   21,   21,   21,   21,   21,   21,   21,   21,    5,
-    5,    5,   28,   28,   31,    9,    9,   29,   29,   32,
-    7,    7,   30,   30,   33,   27,   27,   34,   13,   13,
-   11,   11,   12,   12,   12,   12,   12,   16,   16,   14,
-   14,   15,   15,   15,
+   21,   21,   21,   21,   21,   21,   21,   21,   21,   21,
+   21,    5,    5,    5,   28,   28,   31,    9,    9,   29,
+   29,   32,    7,    7,   30,   30,   33,   27,   27,   34,
+   13,   13,   11,   11,   12,   12,   12,   12,   12,   16,
+   16,   14,   14,   15,   15,   15,
 };
 #if defined(__cplusplus) || defined(__STDC__)
 const short yylen[] =
@@ -202,11 +196,11 @@ short yylen[] =
     3,    3,    3,    1,    3,    3,    1,    2,    1,    1,
     1,    1,    1,    1,    3,    4,    1,    2,    3,    3,
     0,    1,    1,    2,    2,    0,    3,    1,    3,    2,
-    0,    2,    2,    2,    2,    2,    2,    2,    2,    1,
-    1,    1,    1,    3,    3,    1,    3,    1,    3,    3,
-    1,    3,    1,    3,    3,    1,    3,    3,    1,    3,
-    1,    2,    1,    1,    1,    1,    1,    1,    3,    1,
-    2,    1,    1,    1,
+    0,    2,    2,    2,    2,    2,    2,    2,    2,    2,
+    2,    1,    1,    1,    1,    3,    3,    1,    3,    1,
+    3,    3,    1,    3,    1,    3,    3,    1,    3,    3,
+    1,    3,    1,    2,    1,    1,    1,    1,    1,    1,
+    3,    1,    2,    1,    1,    1,
 };
 #if defined(__cplusplus) || defined(__STDC__)
 const short yydefred[] =
@@ -214,21 +208,21 @@ const short yydefred[] =
 short yydefred[] =
 #endif
        {                                      0,
-    0,   83,   85,   86,   87,    0,    0,    0,    0,    0,
-   84,    5,    0,    0,    0,    0,    0,    0,   79,   81,
+    0,   85,   87,   88,   89,    0,    0,    0,    0,    0,
+   86,    5,    0,    0,    0,    0,    0,    0,   81,   83,
     0,    0,    3,    6,    0,    0,   17,    0,   29,   32,
-   31,   33,   30,    0,   27,    0,   66,    0,    0,   62,
-   61,   60,    0,   37,   71,    0,    0,    0,   63,    0,
-    0,   68,    0,    0,   76,    0,    0,   73,   82,    0,
+   31,   33,   30,    0,   27,    0,   68,    0,    0,   64,
+   63,   62,    0,   37,   73,    0,    0,    0,   65,    0,
+    0,   70,    0,    0,   78,    0,    0,   75,   84,    0,
     0,   24,    0,    4,    0,    0,    0,   20,    0,   28,
     0,    0,    0,    0,   38,    0,    0,    0,    0,    0,
-    0,    0,    0,    0,    0,   80,    0,    0,   21,   22,
-   23,   18,   67,   72,    0,   64,    0,   69,    0,   77,
-    0,   74,    0,   34,    0,    0,   25,    0,    0,    0,
-    0,    0,    0,   51,    0,    0,   92,   94,   93,    0,
-   88,   90,    0,    0,   47,   35,    0,    0,    0,   44,
-   45,   91,    0,    0,   40,   39,   52,   53,   54,   55,
-   56,   57,   58,   59,   36,   89,
+    0,    0,    0,    0,    0,   82,    0,    0,   21,   22,
+   23,   18,   69,   74,    0,   66,    0,   71,    0,   79,
+    0,   76,    0,   34,    0,    0,   25,    0,    0,    0,
+    0,    0,    0,   51,    0,    0,   94,   96,   95,    0,
+   90,   92,    0,    0,   47,   35,    0,    0,    0,   44,
+   45,   93,    0,    0,   40,   39,   52,   53,   54,   55,
+   56,   57,   58,   59,   60,   61,   36,   91,
 };
 #if defined(__cplusplus) || defined(__STDC__)
 const short yydgoto[] =
@@ -246,238 +240,250 @@ const short yysindex[] =
 #else
 short yysindex[] =
 #endif
-       {                                    -26,
- -272,    0,    0,    0,    0,  -16,  481,  485,  485,  -31,
-    0,    0, -237, -232, -228, -227, -238,    0,    0,    0,
-  417,  -26,    0,    0,   -2, -217,    0,    7,    0,    0,
-    0,    0,    0, -223,    0,  -28,    0,  -25,  -25,    0,
-    0,    0, -248,    0,    0,  -11,  -14,   -6,    0,   -3,
-   -5,    0,   -1,    4,    0,    2,    6,    0,    0,  485,
-  -17,    0,    8,    0, -198, -196, -195,    0,  -16,    0,
-  481,    7,    7,    7,    0,  -31,    7,  481, -237,  -31,
- -232,  485, -228,  485, -227,    0,   29,  481,    0,    0,
-    0,    0,    0,    0,   26,    0,   30,    0,   31,    0,
-   31,    0,  457,    0,   32, -270,    0,  -30,   -8,   36,
-   29,   11,   12,    0, -206, -205,    0,    0,    0, -245,
-    0,    0,   35,  -30,    0,    0, -182, -180,  453,    0,
-    0,    0,  -30,   35,    0,    0,    0,    0,    0,    0,
-    0,    0,    0,    0,    0,    0,};
+       {                                    475,
+ -270,    0,    0,    0,    0,  -29,  567,  594,  594,   -2,
+    0,    0, -240, -222, -216, -212, -241,    0,    0,    0,
+  -25,  475,    0,    0,  -10, -207,    0,    9,    0,    0,
+    0,    0,    0, -235,    0,  -33,    0,  -31,  -31,    0,
+    0,    0, -242,    0,    0,  -30,   -7,    3,    0,   -6,
+    4,    0,   -5,    6,    0,   -1,    8,    0,    0,  594,
+  -20,    0,   10,    0, -205, -196, -194,    0,  -29,    0,
+  567,    9,    9,    9,    0,   -2,    9,  567, -240,   -2,
+ -222,  594, -216,  594, -212,    0,   31,  567,    0,    0,
+    0,    0,    0,    0,   26,    0,   28,    0,   29,    0,
+   29,    0,  541,    0,   32, -247,    0,   86,  -15,   33,
+   31,   14,   16,    0, -208, -204,    0,    0,    0, -231,
+    0,    0,   38,   86,    0,    0, -179, -178,  491,    0,
+    0,    0,   86,   38,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,};
 #if defined(__cplusplus) || defined(__STDC__)
 const short yyrindex[] =
 #else
 short yyrindex[] =
 #endif
-       {                                     84,
+       {                                     87,
     0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
     0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-    0,   89,    0,    0,    1,    0,    0,  163,    0,    0,
+    0,   90,    0,    0,    1,    0,    0,  177,    0,    0,
     0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-    0,    0,    0,    0,    0,    0,    0,  190,    0,    0,
-  217,    0,    0,  244,    0,    0,  271,    0,    0,    0,
-    0,    0,  298,    0,    0,    0,    0,    0,    0,    0,
-    0,  325,  352,  379,    0,    0,  406,    0,    0,    0,
-    0,    0,    0,    0,    0,    0,  432,    0,    0,    0,
-    0,    0,    0,    0,   28,    0,   55,    0,   82,    0,
-  109,    0,    0,    0,  136,  480,    0,    0,   49,    0,
-  432,    0,    0,    0,  507,  534,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,  207,    0,    0,
+  237,    0,    0,  271,    0,    0,  300,    0,    0,    0,
+    0,    0,  329,    0,    0,    0,    0,    0,    0,    0,
+    0,  358,  387,  417,    0,    0,  446,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,  -26,    0,    0,    0,
+    0,    0,    0,    0,   30,    0,   59,    0,   89,    0,
+  118,    0,    0,    0,  148,  514,    0,    0,   45,    0,
+  -26,    0,    0,    0,  537,  565,    0,    0,    0,    0,
     0,    0,   50,    0,    0,    0,    0,    0,    0,    0,
-    0,    0,    0,   51,    0,    0,    0,    0,    0,    0,
-    0,    0,    0,    0,    0,    0,};
+    0,    0,    0,   52,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,};
 #if defined(__cplusplus) || defined(__STDC__)
 const short yygindex[] =
 #else
 short yygindex[] =
 #endif
        {                                      0,
-  -18,    0,   25,   10,   52,  -72,   16,   63,   -7,   27,
-   39,   83,    3,  -32,  -15,  -22,    0,    0,   15,    0,
-    0,    0,  -12,   -4,    0,   85,    0,    0,    0,    0,
-   33,   37,   23,   34,
+  -17,    0,   27,   11,   54,  -64,   15,   64,    2,   34,
+   39,   84,   -3,  -27,  -18,  -21,    0,    0,   19,    0,
+    0,    0,  -12,   -4,    0,   88,    0,    0,    0,    0,
+   35,   40,   23,   37,
 };
-#define YYTABLESIZE 811
+#define YYTABLESIZE 873
 #if defined(__cplusplus) || defined(__STDC__)
 const short yytable[] =
 #else
 short yytable[] =
 #endif
-       {                                      36,
-   19,   43,  120,   94,   26,   24,   17,   26,   40,   41,
-   38,   39,  117,  112,  113,   71,   26,  118,   60,    2,
-   47,   26,    3,    4,    5,   50,   71,   65,   42,   53,
-   56,  119,   76,   19,   29,   60,   30,   31,   11,   32,
-   66,   68,   67,   87,   19,   72,   78,   73,   74,  124,
-   69,   79,   81,   33,   70,   77,  145,   80,   65,   82,
-   65,   83,   84,   85,   89,   88,   90,   91,  103,   71,
-   95,  127,  128,   76,   60,  111,  125,  112,  133,  113,
-  135,   78,  136,    1,   99,   65,  101,   70,    2,   48,
-   50,   49,  126,   92,   75,   97,   70,   93,   86,   59,
-  146,  134,  107,  131,  132,  109,   64,  102,   75,    0,
-  130,   96,   70,    0,   78,    0,  100,   98,    0,    0,
+       {                                      26,
+   19,   26,   26,   26,   38,   39,   46,   34,   36,   24,
+   71,   94,   60,   76,   40,   41,    2,   47,   60,    3,
+    4,    5,   29,   71,   30,   31,  117,   32,   60,   67,
+   43,  118,   66,   19,   67,   50,   42,   11,  112,  113,
+   87,   53,  124,   33,   19,   56,   72,  119,   73,   74,
+   65,   68,   69,   78,   80,   82,   77,   89,   72,   84,
+   79,   81,   67,   83,  147,   85,   90,   88,   91,   71,
+  103,   76,   60,  125,  127,  111,  128,  112,   99,   95,
+  101,  133,  113,  135,  136,   48,    1,   67,   80,    2,
+   50,   72,   49,  126,   97,   92,   75,   70,   86,  109,
+   59,  132,  134,  131,   93,  148,  107,  102,    0,   64,
+  130,    0,    0,   96,    0,    0,   72,   77,  120,  100,
+   98,   80,    0,    0,    0,    0,    0,    0,    0,    0,
     0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-    0,    0,    0,    0,    0,   26,    0,    0,    0,   78,
-    0,   75,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,   80,   26,    0,    0,
+   77,    0,    0,    0,    0,    0,    0,    0,    0,    0,
     0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-    0,    0,   12,    0,    0,    0,   75,    0,   26,    0,
+    0,    0,    0,    0,    0,   77,   12,    0,    0,    0,
+   26,    0,    0,    0,    0,    0,    0,    0,    0,    0,
     0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-    0,    0,    0,    0,    0,    0,    0,    0,    0,    9,
-    0,    0,    0,   26,    0,   12,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,   26,    9,    0,    0,   12,
     0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-    0,    0,    0,    0,    0,    0,   10,    0,    0,    0,
-    0,    0,    9,    0,    0,   40,   41,  117,    0,    1,
-   25,    2,  118,   25,    3,    4,    5,    6,    7,    8,
-    9,   10,   25,    8,    0,   42,  119,   25,    0,   10,
-   11,   12,   13,   14,   15,   16,   19,    0,   19,    0,
-    0,   19,   19,   19,   19,   19,   19,   19,   19,    0,
-   11,    0,    0,    0,    0,    0,    8,   19,   19,   19,
-   19,   19,   19,   65,    0,   65,    0,    0,   65,   65,
-   65,   65,   65,   65,   65,   65,    0,    7,    0,    0,
-    0,    0,    0,   11,   65,   65,   65,   65,   65,   65,
-   70,    0,   70,    0,    0,   70,   70,   70,   70,   70,
-   70,   70,   70,    0,   15,    0,    0,    0,    0,    0,
-    7,   70,   70,   70,   70,   70,   70,   78,    0,   78,
-    0,    0,   78,   78,   78,   78,   78,   78,   78,   78,
-    0,   13,    0,    0,    0,    0,    0,   15,   78,   78,
-   78,   78,   78,   78,   75,    0,   75,    0,    0,   75,
-   75,   75,   75,   75,   75,   75,   75,    0,   14,    0,
-    0,    0,    0,    0,   13,   75,   75,   75,   75,   75,
-   75,   26,    0,   26,    0,    0,   26,   26,   26,   26,
-   26,   26,   26,   26,    0,   16,    0,    0,    0,    0,
-    0,   14,   26,   26,   26,   26,   26,   26,   12,    0,
-   12,    0,    0,   12,   12,   12,   12,   12,   12,   12,
-   12,    0,    0,    0,    0,    0,    0,    0,   16,   12,
-   12,   12,   12,   12,   12,    9,    0,    9,    0,   34,
-    9,    9,    9,    9,    9,    9,    9,    9,    0,    0,
-   60,    0,    0,    0,   46,    0,    9,    9,    9,    9,
-    9,    9,   10,    0,   10,    0,    0,   10,   10,   10,
-   10,   10,   10,   10,   10,   43,    0,    0,    0,   17,
-    0,    0,    0,   10,   10,   10,   10,   10,   10,    8,
-    0,    8,    0,    0,    8,    8,    8,    8,    8,    8,
-    8,    8,   41,   34,  108,    0,    0,   17,    0,    0,
-    8,    8,    8,    8,    8,    8,   11,    0,   11,    0,
-    0,   11,   11,   11,   11,   11,   11,   11,   11,   42,
-    0,    0,    0,    0,    0,    0,    0,   11,   11,   11,
-   11,   11,   11,    7,    0,    7,    0,    0,    7,    7,
-    7,    7,    7,    7,    7,    7,   43,    0,    0,    0,
-    0,    0,    0,    0,    7,    7,    7,    7,    7,    7,
-   15,    0,   15,    0,    0,   15,   15,   15,   15,   15,
-   15,   15,   15,    0,    0,    0,    0,    0,    0,    0,
-    0,   15,   15,   15,   15,   15,   15,   13,    0,   13,
-    0,    0,   13,   13,   13,   13,   13,   13,   13,   13,
-    0,    0,    0,    0,    0,    0,    0,    0,   13,   13,
-   13,   13,   13,   13,   14,    0,   14,    0,    0,   14,
-   14,   14,   14,   14,   14,   14,   14,    0,    0,    0,
+    0,    0,    0,    0,    0,   25,    0,   25,   25,   25,
+   46,   46,   29,    0,   30,   31,   10,   32,    0,    9,
+    0,    0,   46,   46,   46,   46,   46,   46,   46,   46,
+   46,   46,   46,   33,   40,   41,   19,    0,   19,   46,
+   46,   19,   19,   19,   19,   19,   19,   19,   19,   10,
+    8,    0,    0,    0,    0,    0,   42,    0,    0,   19,
+   19,   19,   19,   19,   19,   67,    0,   67,    0,    0,
+   67,   67,   67,   67,   67,   67,   67,   67,    0,   11,
+    0,    0,    0,    8,    0,    0,    0,    0,   67,   67,
+   67,   67,   67,   67,   72,    0,   72,    0,    0,   72,
+   72,   72,   72,   72,   72,   72,   72,    0,    7,    0,
+    0,    0,   11,    0,    0,    0,    0,   72,   72,   72,
+   72,   72,   72,  117,   80,    0,   80,    0,  118,   80,
+   80,   80,   80,   80,   80,   80,   80,   15,    0,    0,
+    0,    7,    0,    0,  119,    0,    0,   80,   80,   80,
+   80,   80,   80,   77,    0,   77,    0,    0,   77,   77,
+   77,   77,   77,   77,   77,   77,   13,    0,    0,    0,
+   15,    0,    0,    0,    0,    0,   77,   77,   77,   77,
+   77,   77,    0,   26,    0,   26,    0,    0,   26,   26,
+   26,   26,   26,   26,   26,   26,   14,    0,    0,   13,
+    0,    0,    0,    0,    0,    0,   26,   26,   26,   26,
+   26,   26,   12,    0,   12,    0,    0,   12,   12,   12,
+   12,   12,   12,   12,   12,   16,    0,    0,    0,   14,
+    0,    0,    0,    0,    0,   12,   12,   12,   12,   12,
+   12,    0,    9,    0,    9,    0,    0,    9,    9,    9,
+    9,    9,    9,    9,    9,    0,    0,    0,   16,    0,
+    0,    0,    0,    0,    0,    9,    9,    9,    9,    9,
+    9,    0,   10,    0,   10,    0,    0,   10,   10,   10,
+   10,   10,   10,   10,   10,    0,    0,   17,    0,    0,
+    0,    0,    0,    0,    0,   10,   10,   10,   10,   10,
+   10,    0,    0,   43,    0,    0,    8,    0,    8,    0,
+    0,    8,    8,    8,    8,    8,    8,    8,    8,    0,
+    0,    0,    0,    0,    0,    0,   41,    0,    0,    8,
+    8,    8,    8,    8,    8,   11,    0,   11,    0,    0,
+   11,   11,   11,   11,   11,   11,   11,   11,    0,   42,
+    0,    0,    0,   17,    0,    0,    0,    0,   11,   11,
+   11,   11,   11,   11,    7,    0,    7,    0,    0,    7,
+    7,    7,    7,    7,    7,    7,    7,   43,  108,   34,
+    0,    0,    0,    0,    0,    0,    0,    7,    7,    7,
+    7,    7,    7,   15,    0,   15,    0,    0,   15,   15,
+   15,   15,   15,   15,   15,   15,   17,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,   15,   15,   15,   15,
+   15,   15,   13,    0,   13,    0,    0,   13,   13,   13,
+   13,   13,   13,   13,   13,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,   13,   13,   13,   13,   13,
+   13,    0,   14,    0,   14,    0,    0,   14,   14,   14,
+   14,   14,   14,   14,   14,    0,    0,    0,    0,    0,
     0,    0,    0,    0,    0,   14,   14,   14,   14,   14,
    14,   16,    0,   16,    0,    0,   16,   16,   16,   16,
-   16,   16,   16,   16,   29,    0,   30,   31,    0,   32,
-    0,    0,   16,   16,   16,   16,   16,   16,   46,   46,
-    0,    0,    0,   33,    0,    0,    0,    0,    0,    0,
-   46,   46,   46,   46,   46,   46,   46,   46,   46,   40,
-   41,    0,    0,    0,    2,   46,   46,    3,    4,    5,
-    0,  137,  138,  139,  140,  141,  142,  143,  144,   42,
-    0,    0,    0,   11,    0,    0,   41,   41,   29,    0,
-   30,   31,    2,   32,    0,    3,    4,    5,   41,   41,
-   41,   41,   41,   41,   41,   41,   41,   33,    0,    0,
-    0,   11,    0,   42,   42,    0,    0,    0,    0,    0,
-    0,    0,    0,    0,    0,   42,   42,   42,   42,   42,
-   42,   42,   42,   42,    0,    0,    0,    0,    0,    0,
-   43,   43,    0,    0,    0,    0,    0,    0,    0,    0,
-    0,    0,   43,   43,   43,   43,   43,   43,   43,   43,
-   43,
+   16,   16,   16,   16,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,   16,   16,   16,   16,   16,   16,
+    1,    0,    2,    0,    0,    3,    4,    5,    6,    7,
+    8,    9,   10,    0,    0,    0,    0,   40,   41,    0,
+    0,    0,    0,   11,   12,   13,   14,   15,   16,  137,
+  138,  139,  140,  141,  142,  143,  144,  145,  146,   42,
+   41,   41,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,   41,   41,   41,   41,   41,   41,   41,   41,
+   41,   41,   41,   42,   42,    0,    0,    0,    2,    0,
+    0,    3,    4,    5,    0,   42,   42,   42,   42,   42,
+   42,   42,   42,   42,   42,   42,    0,    0,    0,   11,
+    0,   43,   43,    0,   29,    0,   30,   31,    0,   32,
+    0,    0,    0,   43,   43,   43,   43,   43,   43,   43,
+   43,   43,   43,   43,    0,   33,    0,    0,    0,    0,
+    0,    2,    0,    0,    3,    4,    5,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,   11,
 };
 #if defined(__cplusplus) || defined(__STDC__)
 const short yycheck[] =
 #else
 short yycheck[] =
 #endif
-       {                                       7,
-    0,   33,   33,   76,   33,  278,   33,   33,  257,  258,
-    8,    9,  258,  284,  285,   44,   33,  263,   44,  258,
-  258,   33,  261,  262,  263,  258,   44,    0,  277,  258,
-  258,  277,   44,   33,  258,   44,  260,  261,  277,  263,
-   43,  259,   45,   61,   44,   36,   61,   38,   39,   58,
-   44,   58,   58,  277,    0,   46,  129,   61,   61,   61,
-   33,   58,   61,   58,  263,   58,  263,  263,   40,   44,
-   78,   61,   61,   44,   44,   44,   41,  284,   44,  285,
-  263,    0,  263,    0,   82,   58,   84,   33,    0,   41,
-   41,   41,  111,   69,   43,   80,   34,   71,   60,   17,
-  133,  124,   88,  116,  120,  103,   22,   85,    0,   -1,
-  115,   79,   58,   -1,   33,   -1,   83,   81,   -1,   -1,
+       {                                      33,
+    0,   33,   33,   33,    8,    9,   33,   33,    7,  280,
+   44,   76,   44,   44,  257,  258,  258,  258,   44,  261,
+  262,  263,  258,   44,  260,  261,  258,  263,   44,    0,
+   33,  263,   43,   33,   45,  258,  279,  279,  286,  287,
+   61,  258,   58,  279,   44,  258,   36,  279,   38,   39,
+   61,  259,   44,   61,   61,   61,   46,  263,    0,   61,
+   58,   58,   33,   58,  129,   58,  263,   58,  263,   44,
+   40,   44,   44,   41,   61,   44,   61,  286,   82,   78,
+   84,   44,  287,  263,  263,   41,    0,   58,    0,    0,
+   41,   33,   41,  111,   80,   69,   43,   34,   60,  103,
+   17,  120,  124,  116,   71,  133,   88,   85,   -1,   22,
+  115,   -1,   -1,   79,   -1,   -1,   58,    0,   33,   83,
+   81,   33,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-   -1,   -1,   -1,   -1,   -1,    0,   -1,   -1,   -1,   58,
-   -1,   33,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   58,    0,   -1,   -1,
+   33,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-   -1,   -1,    0,   -1,   -1,   -1,   58,   -1,   33,   -1,
+   -1,   -1,   -1,   -1,   -1,   58,    0,   -1,   -1,   -1,
+   33,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,    0,
-   -1,   -1,   -1,   58,   -1,   33,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   58,    0,   -1,   -1,   33,
    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-   -1,   -1,   -1,   -1,   -1,   -1,    0,   -1,   -1,   -1,
-   -1,   -1,   33,   -1,   -1,  257,  258,  258,   -1,  256,
-  259,  258,  263,  259,  261,  262,  263,  264,  265,  266,
-  267,  268,  259,    0,   -1,  277,  277,  259,   -1,   33,
-  277,  278,  279,  280,  281,  282,  256,   -1,  258,   -1,
-   -1,  261,  262,  263,  264,  265,  266,  267,  268,   -1,
-    0,   -1,   -1,   -1,   -1,   -1,   33,  277,  278,  279,
-  280,  281,  282,  256,   -1,  258,   -1,   -1,  261,  262,
-  263,  264,  265,  266,  267,  268,   -1,    0,   -1,   -1,
-   -1,   -1,   -1,   33,  277,  278,  279,  280,  281,  282,
-  256,   -1,  258,   -1,   -1,  261,  262,  263,  264,  265,
-  266,  267,  268,   -1,    0,   -1,   -1,   -1,   -1,   -1,
-   33,  277,  278,  279,  280,  281,  282,  256,   -1,  258,
-   -1,   -1,  261,  262,  263,  264,  265,  266,  267,  268,
-   -1,    0,   -1,   -1,   -1,   -1,   -1,   33,  277,  278,
-  279,  280,  281,  282,  256,   -1,  258,   -1,   -1,  261,
+   -1,   -1,   -1,   -1,   -1,  259,   -1,  259,  259,  259,
+  257,  258,  258,   -1,  260,  261,    0,  263,   -1,   33,
+   -1,   -1,  269,  270,  271,  272,  273,  274,  275,  276,
+  277,  278,  279,  279,  257,  258,  256,   -1,  258,  286,
+  287,  261,  262,  263,  264,  265,  266,  267,  268,   33,
+    0,   -1,   -1,   -1,   -1,   -1,  279,   -1,   -1,  279,
+  280,  281,  282,  283,  284,  256,   -1,  258,   -1,   -1,
+  261,  262,  263,  264,  265,  266,  267,  268,   -1,    0,
+   -1,   -1,   -1,   33,   -1,   -1,   -1,   -1,  279,  280,
+  281,  282,  283,  284,  256,   -1,  258,   -1,   -1,  261,
   262,  263,  264,  265,  266,  267,  268,   -1,    0,   -1,
-   -1,   -1,   -1,   -1,   33,  277,  278,  279,  280,  281,
-  282,  256,   -1,  258,   -1,   -1,  261,  262,  263,  264,
-  265,  266,  267,  268,   -1,    0,   -1,   -1,   -1,   -1,
-   -1,   33,  277,  278,  279,  280,  281,  282,  256,   -1,
-  258,   -1,   -1,  261,  262,  263,  264,  265,  266,  267,
-  268,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   33,  277,
-  278,  279,  280,  281,  282,  256,   -1,  258,   -1,   33,
-  261,  262,  263,  264,  265,  266,  267,  268,   -1,   -1,
-   44,   -1,   -1,   -1,   33,   -1,  277,  278,  279,  280,
-  281,  282,  256,   -1,  258,   -1,   -1,  261,  262,  263,
-  264,  265,  266,  267,  268,   33,   -1,   -1,   -1,   33,
-   -1,   -1,   -1,  277,  278,  279,  280,  281,  282,  256,
-   -1,  258,   -1,   -1,  261,  262,  263,  264,  265,  266,
-  267,  268,   33,   33,   58,   -1,   -1,   33,   -1,   -1,
-  277,  278,  279,  280,  281,  282,  256,   -1,  258,   -1,
-   -1,  261,  262,  263,  264,  265,  266,  267,  268,   33,
-   -1,   -1,   -1,   -1,   -1,   -1,   -1,  277,  278,  279,
-  280,  281,  282,  256,   -1,  258,   -1,   -1,  261,  262,
+   -1,   -1,   33,   -1,   -1,   -1,   -1,  279,  280,  281,
+  282,  283,  284,  258,  256,   -1,  258,   -1,  263,  261,
+  262,  263,  264,  265,  266,  267,  268,    0,   -1,   -1,
+   -1,   33,   -1,   -1,  279,   -1,   -1,  279,  280,  281,
+  282,  283,  284,  256,   -1,  258,   -1,   -1,  261,  262,
+  263,  264,  265,  266,  267,  268,    0,   -1,   -1,   -1,
+   33,   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,
+  283,  284,   -1,  256,   -1,  258,   -1,   -1,  261,  262,
+  263,  264,  265,  266,  267,  268,    0,   -1,   -1,   33,
+   -1,   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,
+  283,  284,  256,   -1,  258,   -1,   -1,  261,  262,  263,
+  264,  265,  266,  267,  268,    0,   -1,   -1,   -1,   33,
+   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,
+  284,   -1,  256,   -1,  258,   -1,   -1,  261,  262,  263,
+  264,  265,  266,  267,  268,   -1,   -1,   -1,   33,   -1,
+   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,
+  284,   -1,  256,   -1,  258,   -1,   -1,  261,  262,  263,
+  264,  265,  266,  267,  268,   -1,   -1,   33,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,
+  284,   -1,   -1,   33,   -1,   -1,  256,   -1,  258,   -1,
+   -1,  261,  262,  263,  264,  265,  266,  267,  268,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   33,   -1,   -1,  279,
+  280,  281,  282,  283,  284,  256,   -1,  258,   -1,   -1,
+  261,  262,  263,  264,  265,  266,  267,  268,   -1,   33,
+   -1,   -1,   -1,   33,   -1,   -1,   -1,   -1,  279,  280,
+  281,  282,  283,  284,  256,   -1,  258,   -1,   -1,  261,
+  262,  263,  264,  265,  266,  267,  268,   33,   58,   33,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  279,  280,  281,
+  282,  283,  284,  256,   -1,  258,   -1,   -1,  261,  262,
   263,  264,  265,  266,  267,  268,   33,   -1,   -1,   -1,
-   -1,   -1,   -1,   -1,  277,  278,  279,  280,  281,  282,
+   -1,   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,
+  283,  284,  256,   -1,  258,   -1,   -1,  261,  262,  263,
+  264,  265,  266,  267,  268,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,
+  284,   -1,  256,   -1,  258,   -1,   -1,  261,  262,  263,
+  264,  265,  266,  267,  268,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,
+  284,  256,   -1,  258,   -1,   -1,  261,  262,  263,  264,
+  265,  266,  267,  268,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  279,  280,  281,  282,  283,  284,
   256,   -1,  258,   -1,   -1,  261,  262,  263,  264,  265,
-  266,  267,  268,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-   -1,  277,  278,  279,  280,  281,  282,  256,   -1,  258,
-   -1,   -1,  261,  262,  263,  264,  265,  266,  267,  268,
-   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  277,  278,
-  279,  280,  281,  282,  256,   -1,  258,   -1,   -1,  261,
-  262,  263,  264,  265,  266,  267,  268,   -1,   -1,   -1,
-   -1,   -1,   -1,   -1,   -1,  277,  278,  279,  280,  281,
-  282,  256,   -1,  258,   -1,   -1,  261,  262,  263,  264,
-  265,  266,  267,  268,  258,   -1,  260,  261,   -1,  263,
-   -1,   -1,  277,  278,  279,  280,  281,  282,  257,  258,
-   -1,   -1,   -1,  277,   -1,   -1,   -1,   -1,   -1,   -1,
-  269,  270,  271,  272,  273,  274,  275,  276,  277,  257,
-  258,   -1,   -1,   -1,  258,  284,  285,  261,  262,  263,
-   -1,  269,  270,  271,  272,  273,  274,  275,  276,  277,
-   -1,   -1,   -1,  277,   -1,   -1,  257,  258,  258,   -1,
-  260,  261,  258,  263,   -1,  261,  262,  263,  269,  270,
-  271,  272,  273,  274,  275,  276,  277,  277,   -1,   -1,
-   -1,  277,   -1,  257,  258,   -1,   -1,   -1,   -1,   -1,
-   -1,   -1,   -1,   -1,   -1,  269,  270,  271,  272,  273,
-  274,  275,  276,  277,   -1,   -1,   -1,   -1,   -1,   -1,
+  266,  267,  268,   -1,   -1,   -1,   -1,  257,  258,   -1,
+   -1,   -1,   -1,  279,  280,  281,  282,  283,  284,  269,
+  270,  271,  272,  273,  274,  275,  276,  277,  278,  279,
   257,  258,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    -1,   -1,  269,  270,  271,  272,  273,  274,  275,  276,
-  277,
+  277,  278,  279,  257,  258,   -1,   -1,   -1,  258,   -1,
+   -1,  261,  262,  263,   -1,  269,  270,  271,  272,  273,
+  274,  275,  276,  277,  278,  279,   -1,   -1,   -1,  279,
+   -1,  257,  258,   -1,  258,   -1,  260,  261,   -1,  263,
+   -1,   -1,   -1,  269,  270,  271,  272,  273,  274,  275,
+  276,  277,  278,  279,   -1,  279,   -1,   -1,   -1,   -1,
+   -1,  258,   -1,   -1,  261,  262,  263,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,  279,
 };
 #define YYFINAL 18
 #ifndef YYDEBUG
 #define YYDEBUG 0
 #endif
-#define YYMAXTOKEN 285
+#define YYMAXTOKEN 287
 #if YYDEBUG
 #if defined(__cplusplus) || defined(__STDC__)
 const char * const yyname[] =
@@ -494,9 +500,9 @@ char *yyname[] =
 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 "COMMAND","ALIAS","DEFVAR","NTWKADDR","NETGROUP","USERGROUP","WORD","DEFAULTS",
 "DEFAULTS_HOST","DEFAULTS_USER","DEFAULTS_RUNAS","DEFAULTS_CMND","NOPASSWD",
-"PASSWD","NOEXEC","EXEC","SETENV","NOSETENV","TRANSCRIPT","NOTRANSCRIPT","ALL",
-"COMMENT","HOSTALIAS","CMNDALIAS","USERALIAS","RUNASALIAS","ERROR","TYPE",
-"ROLE",
+"PASSWD","NOEXEC","EXEC","SETENV","NOSETENV","LOG_INPUT","NOLOG_INPUT",
+"LOG_OUTPUT","NOLOG_OUTPUT","ALL","COMMENT","HOSTALIAS","CMNDALIAS","USERALIAS",
+"RUNASALIAS","ERROR","TYPE","ROLE",
 };
 #if defined(__cplusplus) || defined(__STDC__)
 const char * const yyrule[] =
@@ -561,8 +567,10 @@ char *yyrule[] =
 "cmndtag : cmndtag EXEC",
 "cmndtag : cmndtag SETENV",
 "cmndtag : cmndtag NOSETENV",
-"cmndtag : cmndtag TRANSCRIPT",
-"cmndtag : cmndtag NOTRANSCRIPT",
+"cmndtag : cmndtag LOG_INPUT",
+"cmndtag : cmndtag NOLOG_INPUT",
+"cmndtag : cmndtag LOG_OUTPUT",
+"cmndtag : cmndtag NOLOG_OUTPUT",
 "cmnd : ALL",
 "cmnd : ALIAS",
 "cmnd : COMMAND",
@@ -625,7 +633,7 @@ short *yyss;
 short *yysslim;
 YYSTYPE *yyvs;
 int yystacksize;
-#line 600 "gram.y"
+#line 607 "gram.y"
 static struct defaults *
 new_default(var, val, op)
     char *var;
@@ -816,7 +824,7 @@ init_parser(path, quiet)
     sudolineno = 1;
     verbose = !quiet;
 }
-#line 772 "y.tab.c"
+#line 776 "y.tab.c"
 /* allocate initial stack or double stack size, up to YYMAXDEPTH */
 #if defined(__cplusplus) || defined(__STDC__)
 static int yygrowstack(void)
@@ -1022,127 +1030,127 @@ yyreduce:
     switch (yyn)
     {
 case 1:
-#line 190 "gram.y"
+#line 188 "gram.y"
 { ; }
 break;
 case 5:
-#line 198 "gram.y"
+#line 196 "gram.y"
 {
                            ;
                        }
 break;
 case 6:
-#line 201 "gram.y"
+#line 199 "gram.y"
 {
                            yyerrok;
                        }
 break;
 case 7:
-#line 204 "gram.y"
+#line 202 "gram.y"
 {
                            add_userspec(yyvsp[-1].member, yyvsp[0].privilege);
                        }
 break;
 case 8:
-#line 207 "gram.y"
+#line 205 "gram.y"
 {
                            ;
                        }
 break;
 case 9:
-#line 210 "gram.y"
+#line 208 "gram.y"
 {
                            ;
                        }
 break;
 case 10:
-#line 213 "gram.y"
+#line 211 "gram.y"
 {
                            ;
                        }
 break;
 case 11:
-#line 216 "gram.y"
+#line 214 "gram.y"
 {
                            ;
                        }
 break;
 case 12:
-#line 219 "gram.y"
+#line 217 "gram.y"
 {
                            add_defaults(DEFAULTS, NULL, yyvsp[0].defaults);
                        }
 break;
 case 13:
-#line 222 "gram.y"
+#line 220 "gram.y"
 {
                            add_defaults(DEFAULTS_USER, yyvsp[-1].member, yyvsp[0].defaults);
                        }
 break;
 case 14:
-#line 225 "gram.y"
+#line 223 "gram.y"
 {
                            add_defaults(DEFAULTS_RUNAS, yyvsp[-1].member, yyvsp[0].defaults);
                        }
 break;
 case 15:
-#line 228 "gram.y"
+#line 226 "gram.y"
 {
                            add_defaults(DEFAULTS_HOST, yyvsp[-1].member, yyvsp[0].defaults);
                        }
 break;
 case 16:
-#line 231 "gram.y"
+#line 229 "gram.y"
 {
                            add_defaults(DEFAULTS_CMND, yyvsp[-1].member, yyvsp[0].defaults);
                        }
 break;
 case 18:
-#line 237 "gram.y"
+#line 235 "gram.y"
 {
                            list_append(yyvsp[-2].defaults, yyvsp[0].defaults);
                            yyval.defaults = yyvsp[-2].defaults;
                        }
 break;
 case 19:
-#line 243 "gram.y"
+#line 241 "gram.y"
 {
                            yyval.defaults = new_default(yyvsp[0].string, NULL, TRUE);
                        }
 break;
 case 20:
-#line 246 "gram.y"
+#line 244 "gram.y"
 {
                            yyval.defaults = new_default(yyvsp[0].string, NULL, FALSE);
                        }
 break;
 case 21:
-#line 249 "gram.y"
+#line 247 "gram.y"
 {
                            yyval.defaults = new_default(yyvsp[-2].string, yyvsp[0].string, TRUE);
                        }
 break;
 case 22:
-#line 252 "gram.y"
+#line 250 "gram.y"
 {
                            yyval.defaults = new_default(yyvsp[-2].string, yyvsp[0].string, '+');
                        }
 break;
 case 23:
-#line 255 "gram.y"
+#line 253 "gram.y"
 {
                            yyval.defaults = new_default(yyvsp[-2].string, yyvsp[0].string, '-');
                        }
 break;
 case 25:
-#line 261 "gram.y"
+#line 259 "gram.y"
 {
                            list_append(yyvsp[-2].privilege, yyvsp[0].privilege);
                            yyval.privilege = yyvsp[-2].privilege;
                        }
 break;
 case 26:
-#line 267 "gram.y"
+#line 265 "gram.y"
 {
                            struct privilege *p = emalloc(sizeof(*p));
                            list2tq(&p->hostlist, yyvsp[-2].member);
@@ -1153,51 +1161,51 @@ case 26:
                        }
 break;
 case 27:
-#line 277 "gram.y"
+#line 275 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = FALSE;
                        }
 break;
 case 28:
-#line 281 "gram.y"
+#line 279 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = TRUE;
                        }
 break;
 case 29:
-#line 287 "gram.y"
+#line 285 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, ALIAS);
                        }
 break;
 case 30:
-#line 290 "gram.y"
+#line 288 "gram.y"
 {
                            yyval.member = new_member(NULL, ALL);
                        }
 break;
 case 31:
-#line 293 "gram.y"
+#line 291 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, NETGROUP);
                        }
 break;
 case 32:
-#line 296 "gram.y"
+#line 294 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, NTWKADDR);
                        }
 break;
 case 33:
-#line 299 "gram.y"
+#line 297 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, WORD);
                        }
 break;
 case 35:
-#line 305 "gram.y"
+#line 303 "gram.y"
 {
                            list_append(yyvsp[-2].cmndspec, yyvsp[0].cmndspec);
 #ifdef HAVE_SELINUX
@@ -1215,8 +1223,10 @@ case 35:
                            if (yyvsp[0].cmndspec->tags.setenv == UNSPEC &&
                                yyvsp[0].cmndspec->prev->tags.setenv != IMPLIED)
                                yyvsp[0].cmndspec->tags.setenv = yyvsp[0].cmndspec->prev->tags.setenv;
-                           if (yyvsp[0].cmndspec->tags.transcript == UNSPEC)
-                               yyvsp[0].cmndspec->tags.transcript = yyvsp[0].cmndspec->prev->tags.transcript;
+                           if (yyvsp[0].cmndspec->tags.log_input == UNSPEC)
+                               yyvsp[0].cmndspec->tags.log_input = yyvsp[0].cmndspec->prev->tags.log_input;
+                           if (yyvsp[0].cmndspec->tags.log_output == UNSPEC)
+                               yyvsp[0].cmndspec->tags.log_output = yyvsp[0].cmndspec->prev->tags.log_output;
                            if ((tq_empty(&yyvsp[0].cmndspec->runasuserlist) &&
                                 tq_empty(&yyvsp[0].cmndspec->runasgrouplist)) &&
                                (!tq_empty(&yyvsp[0].cmndspec->prev->runasuserlist) ||
@@ -1354,71 +1364,84 @@ break;
 case 51:
 #line 428 "gram.y"
 {
-                           yyval.tag.nopasswd = yyval.tag.noexec = yyval.tag.setenv = yyval.tag.transcript = UNSPEC;
+                           yyval.tag.nopasswd = yyval.tag.noexec = yyval.tag.setenv =
+                               yyval.tag.log_input = yyval.tag.log_output = UNSPEC;
                        }
 break;
 case 52:
-#line 431 "gram.y"
+#line 432 "gram.y"
 {
                            yyval.tag.nopasswd = TRUE;
                        }
 break;
 case 53:
-#line 434 "gram.y"
+#line 435 "gram.y"
 {
                            yyval.tag.nopasswd = FALSE;
                        }
 break;
 case 54:
-#line 437 "gram.y"
+#line 438 "gram.y"
 {
                            yyval.tag.noexec = TRUE;
                        }
 break;
 case 55:
-#line 440 "gram.y"
+#line 441 "gram.y"
 {
                            yyval.tag.noexec = FALSE;
                        }
 break;
 case 56:
-#line 443 "gram.y"
+#line 444 "gram.y"
 {
                            yyval.tag.setenv = TRUE;
                        }
 break;
 case 57:
-#line 446 "gram.y"
+#line 447 "gram.y"
 {
                            yyval.tag.setenv = FALSE;
                        }
 break;
 case 58:
-#line 449 "gram.y"
+#line 450 "gram.y"
 {
-                           yyval.tag.transcript = TRUE;
+                           yyval.tag.log_input = TRUE;
                        }
 break;
 case 59:
-#line 452 "gram.y"
+#line 453 "gram.y"
 {
-                           yyval.tag.transcript = FALSE;
+                           yyval.tag.log_input = FALSE;
                        }
 break;
 case 60:
-#line 457 "gram.y"
+#line 456 "gram.y"
 {
-                           yyval.member = new_member(NULL, ALL);
+                           yyval.tag.log_output = TRUE;
                        }
 break;
 case 61:
-#line 460 "gram.y"
+#line 459 "gram.y"
 {
-                           yyval.member = new_member(yyvsp[0].string, ALIAS);
+                           yyval.tag.log_output = FALSE;
                        }
 break;
 case 62:
-#line 463 "gram.y"
+#line 464 "gram.y"
+{
+                           yyval.member = new_member(NULL, ALL);
+                       }
+break;
+case 63:
+#line 467 "gram.y"
+{
+                           yyval.member = new_member(yyvsp[0].string, ALIAS);
+                       }
+break;
+case 64:
+#line 470 "gram.y"
 {
                            struct sudo_command *c = emalloc(sizeof(*c));
                            c->cmnd = yyvsp[0].command.cmnd;
@@ -1426,8 +1449,8 @@ case 62:
                            yyval.member = new_member((char *)c, COMMAND);
                        }
 break;
-case 65:
-#line 475 "gram.y"
+case 67:
+#line 482 "gram.y"
 {
                            char *s;
                            if ((s = alias_add(yyvsp[-2].string, HOSTALIAS, yyvsp[0].member)) != NULL) {
@@ -1436,15 +1459,15 @@ case 65:
                            }
                        }
 break;
-case 67:
-#line 485 "gram.y"
+case 69:
+#line 492 "gram.y"
 {
                            list_append(yyvsp[-2].member, yyvsp[0].member);
                            yyval.member = yyvsp[-2].member;
                        }
 break;
-case 70:
-#line 495 "gram.y"
+case 72:
+#line 502 "gram.y"
 {
                            char *s;
                            if ((s = alias_add(yyvsp[-2].string, CMNDALIAS, yyvsp[0].member)) != NULL) {
@@ -1453,15 +1476,15 @@ case 70:
                            }
                        }
 break;
-case 72:
-#line 505 "gram.y"
+case 74:
+#line 512 "gram.y"
 {
                            list_append(yyvsp[-2].member, yyvsp[0].member);
                            yyval.member = yyvsp[-2].member;
                        }
 break;
-case 75:
-#line 515 "gram.y"
+case 77:
+#line 522 "gram.y"
 {
                            char *s;
                            if ((s = alias_add(yyvsp[-2].string, RUNASALIAS, yyvsp[0].member)) != NULL) {
@@ -1470,8 +1493,8 @@ case 75:
                            }
                        }
 break;
-case 78:
-#line 528 "gram.y"
+case 80:
+#line 535 "gram.y"
 {
                            char *s;
                            if ((s = alias_add(yyvsp[-2].string, USERALIAS, yyvsp[0].member)) != NULL) {
@@ -1480,97 +1503,97 @@ case 78:
                            }
                        }
 break;
-case 80:
-#line 538 "gram.y"
+case 82:
+#line 545 "gram.y"
 {
                            list_append(yyvsp[-2].member, yyvsp[0].member);
                            yyval.member = yyvsp[-2].member;
                        }
 break;
-case 81:
-#line 544 "gram.y"
+case 83:
+#line 551 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = FALSE;
                        }
 break;
-case 82:
-#line 548 "gram.y"
+case 84:
+#line 555 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = TRUE;
                        }
 break;
-case 83:
-#line 554 "gram.y"
+case 85:
+#line 561 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, ALIAS);
                        }
 break;
-case 84:
-#line 557 "gram.y"
+case 86:
+#line 564 "gram.y"
 {
                            yyval.member = new_member(NULL, ALL);
                        }
 break;
-case 85:
-#line 560 "gram.y"
+case 87:
+#line 567 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, NETGROUP);
                        }
 break;
-case 86:
-#line 563 "gram.y"
+case 88:
+#line 570 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, USERGROUP);
                        }
 break;
-case 87:
-#line 566 "gram.y"
+case 89:
+#line 573 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, WORD);
                        }
 break;
-case 89:
-#line 572 "gram.y"
+case 91:
+#line 579 "gram.y"
 {
                            list_append(yyvsp[-2].member, yyvsp[0].member);
                            yyval.member = yyvsp[-2].member;
                        }
 break;
-case 90:
-#line 578 "gram.y"
+case 92:
+#line 585 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = FALSE;
                        }
 break;
-case 91:
-#line 582 "gram.y"
+case 93:
+#line 589 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = TRUE;
                        }
 break;
-case 92:
-#line 588 "gram.y"
+case 94:
+#line 595 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, ALIAS);
                        }
 break;
-case 93:
-#line 591 "gram.y"
+case 95:
+#line 598 "gram.y"
 {
                            yyval.member = new_member(NULL, ALL);
                        }
 break;
-case 94:
-#line 594 "gram.y"
+case 96:
+#line 601 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, WORD);
                        }
 break;
-#line 1526 "y.tab.c"
+#line 1545 "y.tab.c"
     }
     yyssp -= yym;
     yystate = *yyssp;
diff --git a/gram.h b/gram.h
index 1d1c6d590064c4597f844390ecb92f67ec93f748..2bec420543b137625617894d715d6127e9cbc906 100644 (file)
--- a/gram.h
+++ b/gram.h
 #define EXEC 272
 #define SETENV 273
 #define NOSETENV 274
-#define TRANSCRIPT 275
-#define NOTRANSCRIPT 276
-#define ALL 277
-#define COMMENT 278
-#define HOSTALIAS 279
-#define CMNDALIAS 280
-#define USERALIAS 281
-#define RUNASALIAS 282
-#define ERROR 283
-#define TYPE 284
-#define ROLE 285
+#define LOG_INPUT 275
+#define NOLOG_INPUT 276
+#define LOG_OUTPUT 277
+#define NOLOG_OUTPUT 278
+#define ALL 279
+#define COMMENT 280
+#define HOSTALIAS 281
+#define CMNDALIAS 282
+#define USERALIAS 283
+#define RUNASALIAS 284
+#define ERROR 285
+#define TYPE 286
+#define ROLE 287
 #ifndef YYSTYPE_DEFINED
 #define YYSTYPE_DEFINED
 typedef union {
diff --git a/gram.y b/gram.y
index 561862a3eb3795cd567719a79f6a5425dfe31518..1c669aab51f4b899e8a75fbebfe70acfcc595146 100644 (file)
--- a/gram.y
+++ b/gram.y
@@ -142,8 +142,10 @@ yyerror(s)
 %token <tok>    EXEC                   /* don't preload dummy execve() */
 %token <tok>    SETENV                 /* user may set environment for cmnd */
 %token <tok>    NOSETENV               /* user may not set environment */
-%token <tok>    TRANSCRIPT             /* log a transcript of the cmnd */
-%token <tok>    NOTRANSCRIPT           /* don't log a transcript of the cmnd */
+%token <tok>    LOG_INPUT              /* log user's cmnd input */
+%token <tok>    NOLOG_INPUT            /* don't log user's cmnd input */
+%token <tok>    LOG_OUTPUT             /* log cmnd output */
+%token <tok>    NOLOG_OUTPUT           /* don't log cmnd output */
 %token <tok>    ALL                    /* ALL keyword */
 %token <tok>    COMMENT                /* comment and/or carriage return */
 %token <tok>    HOSTALIAS              /* Host_Alias keyword */
@@ -315,8 +317,10 @@ cmndspeclist       :       cmndspec
                            if ($3->tags.setenv == UNSPEC &&
                                $3->prev->tags.setenv != IMPLIED)
                                $3->tags.setenv = $3->prev->tags.setenv;
-                           if ($3->tags.transcript == UNSPEC)
-                               $3->tags.transcript = $3->prev->tags.transcript;
+                           if ($3->tags.log_input == UNSPEC)
+                               $3->tags.log_input = $3->prev->tags.log_input;
+                           if ($3->tags.log_output == UNSPEC)
+                               $3->tags.log_output = $3->prev->tags.log_output;
                            if ((tq_empty(&$3->runasuserlist) &&
                                 tq_empty(&$3->runasgrouplist)) &&
                                (!tq_empty(&$3->prev->runasuserlist) ||
@@ -422,7 +426,8 @@ runaslist   :       userlist {
                ;
 
 cmndtag                :       /* empty */ {
-                           $$.nopasswd = $$.noexec = $$.setenv = $$.transcript = UNSPEC;
+                           $$.nopasswd = $$.noexec = $$.setenv =
+                               $$.log_input = $$.log_output = UNSPEC;
                        }
                |       cmndtag NOPASSWD {
                            $$.nopasswd = TRUE;
@@ -442,11 +447,17 @@ cmndtag           :       /* empty */ {
                |       cmndtag NOSETENV {
                            $$.setenv = FALSE;
                        }
-               |       cmndtag TRANSCRIPT {
-                           $$.transcript = TRUE;
+               |       cmndtag LOG_INPUT {
+                           $$.log_input = TRUE;
                        }
-               |       cmndtag NOTRANSCRIPT {
-                           $$.transcript = FALSE;
+               |       cmndtag NOLOG_INPUT {
+                           $$.log_input = FALSE;
+                       }
+               |       cmndtag LOG_OUTPUT {
+                           $$.log_output = TRUE;
+                       }
+               |       cmndtag NOLOG_OUTPUT {
+                           $$.log_output = FALSE;
                        }
                ;
 
diff --git a/iolog.c b/iolog.c
new file mode 100644 (file)
index 0000000..4ac05b0
--- /dev/null
+++ b/iolog.c
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <stdio.h>
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif /* STDC_HEADERS */
+#ifdef HAVE_STRING_H
+# include <string.h>
+#else
+# ifdef HAVE_STRINGS_H
+#  include <strings.h>
+# endif
+#endif /* HAVE_STRING_H */
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#if TIME_WITH_SYS_TIME
+# include <time.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#ifdef HAVE_ZLIB
+# include <zlib.h>
+#endif
+
+#include "sudo.h"
+
+union io_fd {
+    FILE *f;
+#ifdef HAVE_ZLIB
+    gzFile g;
+#endif
+    void *v;
+};
+
+struct script_buf {
+    int len; /* buffer length (how much read in) */
+    int off; /* write position (how much already consumed) */
+    char buf[16 * 1024];
+};
+
+#define IOFD_STDIN     0
+#define IOFD_STDOUT    1
+#define IOFD_STDERR    2
+#define IOFD_TTYIN     3
+#define IOFD_TTYOUT    4
+#define IOFD_TIMING    5
+#define IOFD_MAX       6
+
+static sigset_t ttyblock;
+static struct timeval last_time;
+static union io_fd io_fds[IOFD_MAX];
+
+void
+io_nextid(void)
+{
+    struct stat sb;
+    char buf[32], *ep;
+    int fd, i, ch;
+    unsigned long id = 0;
+    int len;
+    ssize_t nread;
+    char pathbuf[PATH_MAX];
+
+    /*
+     * Create _PATH_SUDO_IO_LOGDIR if it doesn't already exist.
+     */
+    if (stat(_PATH_SUDO_IO_LOGDIR, &sb) != 0) {
+       if (mkdir(_PATH_SUDO_IO_LOGDIR, S_IRWXU) != 0)
+           log_error(USE_ERRNO, "Can't mkdir %s", _PATH_SUDO_IO_LOGDIR);
+    } else if (!S_ISDIR(sb.st_mode)) {
+       log_error(0, "%s exists but is not a directory (0%o)",
+           _PATH_SUDO_IO_LOGDIR, (unsigned int) sb.st_mode);
+    }
+
+    /*
+     * Open sequence file
+     */
+    len = snprintf(pathbuf, sizeof(pathbuf), "%s/seq", _PATH_SUDO_IO_LOGDIR);
+    if (len <= 0 || len >= sizeof(pathbuf)) {
+       errno = ENAMETOOLONG;
+       log_error(USE_ERRNO, "%s/seq", pathbuf);
+    }
+    fd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
+    if (fd == -1)
+       log_error(USE_ERRNO, "cannot open %s", pathbuf);
+    lock_file(fd, SUDO_LOCK);
+
+    /* Read seq number (base 36). */
+    nread = read(fd, buf, sizeof(buf));
+    if (nread != 0) {
+       if (nread == -1)
+           log_error(USE_ERRNO, "cannot read %s", pathbuf);
+       id = strtoul(buf, &ep, 36);
+       if (buf == ep || id >= 2176782336U)
+           log_error(0, "invalid sequence number %s", pathbuf);
+    }
+    id++;
+
+    /*
+     * Convert id to a string and stash in sudo_user.sessid.
+     * Note that that least significant digits go at the end of the string.
+     */
+    for (i = 5; i >= 0; i--) {
+       ch = id % 36;
+       id /= 36;
+       buf[i] = ch < 10 ? ch + '0' : ch - 10 + 'A';
+    }
+    buf[6] = '\n';
+
+    /* Stash id logging purposes */
+    memcpy(sudo_user.sessid, buf, 6);
+    sudo_user.sessid[6] = '\0';
+
+    /* Rewind and overwrite old seq file. */
+    if (lseek(fd, 0, SEEK_SET) == (off_t)-1 || write(fd, buf, 7) != 7)
+       log_error(USE_ERRNO, "Can't write to %s", pathbuf);
+    close(fd);
+}
+
+static int
+build_idpath(char *pathbuf, size_t pathsize)
+{
+    struct stat sb;
+    int i, len;
+
+    if (sudo_user.sessid[0] == '\0')
+       log_error(0, "tried to build a session id path without a session id");
+
+    /*
+     * Path is of the form /var/log/sudo-session/00/00/01.
+     */
+    len = snprintf(pathbuf, pathsize, "%s/%c%c/%c%c/%c%c", _PATH_SUDO_IO_LOGDIR,
+       sudo_user.sessid[0], sudo_user.sessid[1], sudo_user.sessid[2],
+       sudo_user.sessid[3], sudo_user.sessid[4], sudo_user.sessid[5]);
+    if (len <= 0 && len >= pathsize) {
+       errno = ENAMETOOLONG;
+       log_error(USE_ERRNO, "%s/%s", _PATH_SUDO_IO_LOGDIR, sudo_user.sessid);
+    }
+
+    /*
+     * Create the intermediate subdirs as needed.
+     */
+    for (i = 6; i > 0; i -= 3) {
+       pathbuf[len - i] = '\0';
+       if (stat(pathbuf, &sb) != 0) {
+           if (mkdir(pathbuf, S_IRWXU) != 0)
+               log_error(USE_ERRNO, "Can't mkdir %s", pathbuf);
+       } else if (!S_ISDIR(sb.st_mode)) {
+           log_error(0, "%s: %s", pathbuf, strerror(ENOTDIR));
+       }
+       pathbuf[len - i] = '/';
+    }
+
+    return(len);
+}
+
+static void *
+open_io_fd(char *pathbuf, int len, const char *suffix, int docompress)
+{
+    void *vfd = NULL;
+    int fd;
+
+    pathbuf[len] = '\0';
+    strlcat(pathbuf, suffix, PATH_MAX);
+    fd = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
+    if (fd != -1) {
+       fcntl(fd, F_SETFD, FD_CLOEXEC);
+#ifdef HAVE_ZLIB
+       if (docompress)
+           vfd = gzdopen(fd, "w");
+       else
+#endif
+           vfd = fdopen(fd, "w");
+    }
+    return vfd;
+}
+
+int
+io_log_open()
+{
+    char pathbuf[PATH_MAX];
+    FILE *io_logfile;
+    int len;
+
+    if (!def_log_input && !def_log_output)
+       return FALSE;
+
+    /*
+     * Build a path containing the session id split into two-digit subdirs,
+     * so ID 000001 becomes /var/log/sudo-session/00/00/01.
+     */
+    len = build_idpath(pathbuf, sizeof(pathbuf));
+    if (len == -1)
+       return -1;
+
+    if (mkdir(pathbuf, S_IRUSR|S_IWUSR|S_IXUSR) != 0)
+       log_error(USE_ERRNO, "Can't mkdir %s", pathbuf);
+
+    /*
+     * We create 7 files: a log file, a timing file and 5 for input/output.
+     */
+    io_logfile = open_io_fd(pathbuf, len, "/log", FALSE);
+    if (io_logfile == NULL)
+       log_error(USE_ERRNO, "Can't create %s", pathbuf);
+
+    io_fds[IOFD_TIMING].v = open_io_fd(pathbuf, len, "/timing", def_compress_io);
+    if (io_fds[IOFD_TIMING].v == NULL)
+       log_error(USE_ERRNO, "Can't create %s", pathbuf);
+
+    io_fds[IOFD_TTYIN].v = open_io_fd(pathbuf, len, "/ttyin", def_compress_io);
+    if (io_fds[IOFD_TTYIN].v == NULL)
+       log_error(USE_ERRNO, "Can't create %s", pathbuf);
+
+    io_fds[IOFD_TTYOUT].v = open_io_fd(pathbuf, len, "/ttyout", def_compress_io);
+    if (io_fds[IOFD_TTYOUT].v == NULL)
+       log_error(USE_ERRNO, "Can't create %s", pathbuf);
+
+    io_fds[IOFD_STDIN].v = open_io_fd(pathbuf, len, "/stdin", def_compress_io);
+    if (io_fds[IOFD_STDIN].v == NULL)
+       log_error(USE_ERRNO, "Can't create %s", pathbuf);
+
+    io_fds[IOFD_STDOUT].v = open_io_fd(pathbuf, len, "/stdout", def_compress_io);
+    if (io_fds[IOFD_STDOUT].v == NULL)
+       log_error(USE_ERRNO, "Can't create %s", pathbuf);
+
+    io_fds[IOFD_STDERR].v = open_io_fd(pathbuf, len, "/stderr", def_compress_io);
+    if (io_fds[IOFD_STDERR].v == NULL)
+       log_error(USE_ERRNO, "Can't create %s", pathbuf);
+
+    /* So we can block tty-generated signals */
+    sigemptyset(&ttyblock);
+    sigaddset(&ttyblock, SIGINT);
+    sigaddset(&ttyblock, SIGQUIT);
+    sigaddset(&ttyblock, SIGTSTP);
+    sigaddset(&ttyblock, SIGTTIN);
+    sigaddset(&ttyblock, SIGTTOU);
+
+    gettimeofday(&last_time, NULL);
+
+    /* XXX - log more stuff?  window size? environment? */
+    fprintf(io_logfile, "%ld:%s:%s:%s:%s\n", last_time.tv_sec, user_name,
+        runas_pw->pw_name, runas_gr ? runas_gr->gr_name : "", user_tty);
+    fprintf(io_logfile, "%s\n", user_cwd);
+    fprintf(io_logfile, "%s%s%s\n", user_cmnd, user_args ? " " : "",
+        user_args ? user_args : "");
+    fclose(io_logfile);
+
+    return TRUE;
+}
+
+void
+io_log_close()
+{
+    int i;
+
+    for (i = 0; i < IOFD_MAX; i++) {
+       if (io_fds[i].v == NULL)
+           continue;
+#ifdef HAVE_ZLIB
+       if (def_compress_io)
+           gzclose(io_fds[i].g);
+       else
+#endif
+           fclose(io_fds[i].f);
+    }
+}
+
+static int
+log_io(const char *buf, unsigned int len, int idx)
+{
+    struct timeval now, tv;
+    sigset_t omask;
+
+    gettimeofday(&now, NULL);
+
+    sigprocmask(SIG_BLOCK, &ttyblock, &omask);
+
+#ifdef HAVE_ZLIB
+    if (def_compress_io)
+       gzwrite(io_fds[idx].g, buf, len);
+    else
+#endif
+       fwrite(buf, 1, len, io_fds[idx].f);
+    timersub(&now, &last_time, &tv);
+#ifdef HAVE_ZLIB
+    if (def_compress_io)
+       gzprintf(io_fds[IOFD_TIMING].g, "%d %f %d\n", idx,
+           tv.tv_sec + ((double)tv.tv_usec / 1000000), len);
+    else
+#endif
+       fprintf(io_fds[IOFD_TIMING].f, "%d %f %d\n", idx,
+           tv.tv_sec + ((double)tv.tv_usec / 1000000), len);
+    last_time.tv_sec = now.tv_sec;
+    last_time.tv_usec = now.tv_usec;
+
+    sigprocmask(SIG_SETMASK, &omask, NULL);
+
+    return TRUE;
+}
+
+int
+log_ttyin(const char *buf, unsigned int len)
+{
+    return log_io(buf, len, IOFD_TTYIN);
+}
+
+int
+log_ttyout(const char *buf, unsigned int len)
+{
+    return log_io(buf, len, IOFD_TTYOUT);
+}
+
+int
+log_stdin(const char *buf, unsigned int len)
+{
+    return log_io(buf, len, IOFD_STDIN);
+}
+
+int
+log_stdout(const char *buf, unsigned int len)
+{
+    return log_io(buf, len, IOFD_STDOUT);
+}
+
+int
+log_stderr(const char *buf, unsigned int len)
+{
+    return log_io(buf, len, IOFD_STDERR);
+}
diff --git a/parse.c b/parse.c
index da026997d8572791d1a2809186fd481bd8b7283e..63dedd479b34a5d551c40af8fa1eb009c835ffa4 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -250,8 +250,10 @@ sudo_file_lookup(nss, validated, pwflag)
                def_noexec = tags->noexec;
            if (tags->setenv != UNSPEC)
                def_setenv = tags->setenv;
-           if (tags->transcript != UNSPEC)
-               def_transcript = tags->transcript;
+           if (tags->log_input != UNSPEC)
+               def_log_input = tags->log_input;
+           if (tags->log_output != UNSPEC)
+               def_log_output = tags->log_output;
        }
     } else if (match == DENY) {
        SET(validated, VALIDATE_NOT_OK);
@@ -293,10 +295,15 @@ sudo_file_append_cmnd(cs, tags, lbuf)
            "PASSWD: ", NULL);
        tags->nopasswd = cs->tags.nopasswd;
     }
-    if (TAG_CHANGED(transcript)) {
-       lbuf_append(lbuf, cs->tags.transcript ? "SCRIPT: " :
-           "NOSCRIPT: ", NULL);
-       tags->transcript = cs->tags.transcript;
+    if (TAG_CHANGED(log_input)) {
+       lbuf_append(lbuf, cs->tags.log_input ? "LOG_INPUT: " :
+           "NOLOG_INPUT: ", NULL);
+       tags->log_input = cs->tags.log_input;
+    }
+    if (TAG_CHANGED(log_output)) {
+       lbuf_append(lbuf, cs->tags.log_output ? "LOG_OUTPUT: " :
+           "NOLOG_OUTPUT: ", NULL);
+       tags->log_output = cs->tags.log_output;
     }
     m = cs->cmnd;
     print_member(lbuf, m->name, m->type, m->negated,
@@ -321,7 +328,8 @@ sudo_file_display_priv_short(pw, us, lbuf)
        tags.noexec = UNSPEC;
        tags.setenv = UNSPEC;
        tags.nopasswd = UNSPEC;
-       tags.transcript = UNSPEC;
+       tags.log_input = UNSPEC;
+       tags.log_output = UNSPEC;
        lbuf_append(lbuf, "    ", NULL);
        tq_foreach_fwd(&priv->cmndlist, cs) {
            if (cs != tq_first(&priv->cmndlist))
@@ -375,7 +383,8 @@ sudo_file_display_priv_long(pw, us, lbuf)
        tags.noexec = UNSPEC;
        tags.setenv = UNSPEC;
        tags.nopasswd = UNSPEC;
-       tags.transcript = UNSPEC;
+       tags.log_input = UNSPEC;
+       tags.log_output = UNSPEC;
        lbuf_append(lbuf, "\nSudoers entry:\n", NULL);
        tq_foreach_fwd(&priv->cmndlist, cs) {
            lbuf_append(lbuf, "    RunAsUsers: ", NULL);
diff --git a/parse.h b/parse.h
index af8179cf52a0bb0c432541697c5b54df02d988b1..9d881f463ea7118b0bd508c264cb1d89172ac212 100644 (file)
--- a/parse.h
+++ b/parse.h
@@ -40,10 +40,11 @@ struct sudo_command {
  * Possible valus: TRUE, FALSE, UNSPEC.
  */
 struct cmndtag {
-    __signed char nopasswd;
-    __signed char noexec;
-    __signed char setenv;
-    __signed char transcript;
+    __signed int nopasswd: 3;
+    __signed int noexec: 3;
+    __signed int setenv: 3;
+    __signed int log_input: 3;
+    __signed int log_output: 3;
 };
 
 /*
index ccacfb49feeb4a339c4073ef46f4f01f9c547e8e..656aca482a111a3af1d05b3824d33ad687b2f0dd 100644 (file)
 #endif /* _PATH_SUDO_TIMEDIR */
 
 /*
- * Where to put the session files.  Defaults to /var/log/sudo-session,
- * /var/adm/sudo-session or /usr/adm/sudo-session depending on what exists.
+ * Where to put the I/O log files.  Defaults to /var/log/sudo-io,
+ * /var/adm/sudo-io or /usr/adm/sudo-io depending on what exists.
  */
-#ifndef _PATH_SUDO_TRANSCRIPT
-#undef _PATH_SUDO_TRANSCRIPT
-#endif /* _PATH_SUDO_TRANSCRIPT */
+#ifndef _PATH_SUDO_IO_LOGDIR
+#undef _PATH_SUDO_IO_LOGDIR
+#endif /* _PATH_SUDO_IO_LOGDIR */
 
 /*
  * Where to put the sudo log file when logging to a file.  Defaults to
diff --git a/pty.c b/pty.c
index 7d4f15a9de717d888e700cce942dd461ff1b08f9..65f1afd1245b70b2171f5d9a9949c8e7dc897276 100644 (file)
--- a/pty.c
+++ b/pty.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
diff --git a/script.c b/script.c
deleted file mode 100644 (file)
index 67087d1..0000000
--- a/script.c
+++ /dev/null
@@ -1,1130 +0,0 @@
-/*
- * Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <config.h>
-
-#include <sys/types.h>
-#include <sys/param.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/wait.h>
-#ifdef HAVE_TERMIOS_H
-# include <termios.h>
-#else
-# ifdef HAVE_TERMIO_H
-#  include <termio.h>
-# else
-#  include <sgtty.h>
-# endif /* HAVE_TERMIO_H */
-#endif /* HAVE_TERMIOS_H */
-#include <sys/ioctl.h>
-#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
-#endif /* HAVE_SYS_SELECT_H */
-#include <stdio.h>
-#ifdef STDC_HEADERS
-# include <stdlib.h>
-# include <stddef.h>
-#else
-# ifdef HAVE_STDLIB_H
-#  include <stdlib.h>
-# endif
-#endif /* STDC_HEADERS */
-#ifdef HAVE_STRING_H
-# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
-#  include <memory.h>
-# endif
-# include <string.h>
-#else
-# ifdef HAVE_STRINGS_H
-#  include <strings.h>
-# endif
-#endif /* HAVE_STRING_H */
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif /* HAVE_UNISTD_H */
-#if TIME_WITH_SYS_TIME
-# include <time.h>
-#endif
-#include <errno.h>
-#include <fcntl.h>
-#include <grp.h>
-#include <pwd.h>
-#include <signal.h>
-#ifdef HAVE_SELINUX
-# include <selinux/selinux.h>
-#endif
-#ifdef HAVE_ZLIB
-# include <zlib.h>
-#endif
-
-#include "sudo.h"
-
-#define SFD_MASTER     0
-#define SFD_SLAVE      1
-#define SFD_LOG                2
-#define SFD_OUTPUT     3
-#define SFD_TIMING     4
-#define SFD_USERTTY    5
-
-#define TERM_COOKED    0
-#define TERM_CBREAK    1
-#define TERM_RAW       2
-
-union script_fd {
-    FILE *f;
-#ifdef HAVE_ZLIB
-    gzFile g;
-#endif
-};
-
-struct script_buf {
-    int len; /* buffer length (how much read in) */
-    int off; /* write position (how much already consumed) */
-    char buf[16 * 1024];
-};
-
-static int script_fds[6];
-static int ttyout;
-
-static sig_atomic_t alive = 1;
-static sig_atomic_t recvsig = 0;
-static sig_atomic_t ttymode = TERM_COOKED;
-static sig_atomic_t tty_initialized = 0;
-
-static sigset_t ttyblock;
-
-static pid_t ppgrp, child;
-static int child_status;
-static int foreground;
-
-static char slavename[PATH_MAX];
-
-static int suspend_parent __P((int signo, struct script_buf *output,
-    struct timeval *then, struct timeval *now, union script_fd ofile,
-    union script_fd tfile));
-static void flush_output __P((struct script_buf *output, struct timeval *then,
-    struct timeval *now, union script_fd ofile, union script_fd tfile));
-static void handler __P((int s));
-static void script_child __P((char *path, char *argv[], int, int));
-static void script_run __P((char *path, char *argv[], int));
-static void sigchild __P((int s));
-static void sigwinch __P((int s));
-static void sync_winsize __P((int src, int dst));
-
-extern int get_pty __P((int *master, int *slave, char *name, size_t namesz));
-
-/*
- * TODO: run monitor as root?
- */
-
-static int
-fdcompar(v1, v2)
-    const void *v1;
-    const void *v2;
-{
-    int i = *(int *)v1;
-    int j = *(int *)v2;
-
-    return(script_fds[i] - script_fds[j]);
-}
-
-void
-script_nextid()
-{
-    struct stat sb;
-    char buf[32], *ep;
-    int fd, i, ch;
-    unsigned long id = 0;
-    int len;
-    ssize_t nread;
-    char pathbuf[PATH_MAX];
-
-    /*
-     * Create _PATH_SUDO_TRANSCRIPT if it doesn't already exist.
-     */
-    if (stat(_PATH_SUDO_TRANSCRIPT, &sb) != 0) {
-       if (mkdir(_PATH_SUDO_TRANSCRIPT, S_IRWXU) != 0)
-           log_error(USE_ERRNO, "Can't mkdir %s", _PATH_SUDO_TRANSCRIPT);
-    } else if (!S_ISDIR(sb.st_mode)) {
-       log_error(0, "%s exists but is not a directory (0%o)",
-           _PATH_SUDO_TRANSCRIPT, (unsigned int) sb.st_mode);
-    }
-
-    /*
-     * Open sequence file
-     */
-    len = snprintf(pathbuf, sizeof(pathbuf), "%s/seq", _PATH_SUDO_TRANSCRIPT);
-    if (len <= 0 || len >= sizeof(pathbuf)) {
-       errno = ENAMETOOLONG;
-       log_error(USE_ERRNO, "%s/seq", pathbuf);
-    }
-    fd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
-    if (fd == -1)
-       log_error(USE_ERRNO, "cannot open %s", pathbuf);
-    lock_file(fd, SUDO_LOCK);
-
-    /* Read seq number (base 36). */
-    nread = read(fd, buf, sizeof(buf));
-    if (nread != 0) {
-       if (nread == -1)
-           log_error(USE_ERRNO, "cannot read %s", pathbuf);
-       id = strtoul(buf, &ep, 36);
-       if (buf == ep || id >= (unsigned long)2176782336)
-           log_error(0, "invalid sequence number %s", pathbuf);
-    }
-    id++;
-
-    /*
-     * Convert id to a string and stash in sudo_user.sessid.
-     * Note that that least significant digits go at the end of the string.
-     */
-    for (i = 5; i >= 0; i--) {
-       ch = id % 36;
-       id /= 36;
-       buf[i] = ch < 10 ? ch + '0' : ch - 10 + 'A';
-    }
-    buf[6] = '\n';
-
-    /* Stash id logging purposes */
-    memcpy(sudo_user.sessid, buf, 6);
-    sudo_user.sessid[6] = '\0';
-
-    /* Rewind and overwrite old seq file. */
-    if (lseek(fd, 0, SEEK_SET) == (off_t)-1 || write(fd, buf, 7) != 7)
-       log_error(USE_ERRNO, "Can't write to %s", pathbuf);
-    close(fd);
-}
-
-static int
-build_idpath(pathbuf)
-    char *pathbuf;
-{
-    struct stat sb;
-    int i, len;
-
-    if (sudo_user.sessid[0] == '\0')
-       log_error(0, "tried to build a session id path without a session id");
-
-    /*
-     * Path is of the form /var/log/sudo-session/00/00/01.
-     */
-    len = snprintf(pathbuf, PATH_MAX, "%s/%c%c/%c%c/%c%c", _PATH_SUDO_TRANSCRIPT,
-       sudo_user.sessid[0], sudo_user.sessid[1], sudo_user.sessid[2],
-       sudo_user.sessid[3], sudo_user.sessid[4], sudo_user.sessid[5]);
-    if (len <= 0 && len >= PATH_MAX) {
-       errno = ENAMETOOLONG;
-       log_error(USE_ERRNO, "%s/%s", _PATH_SUDO_TRANSCRIPT, sudo_user.sessid);
-    }
-
-    /*
-     * Create the intermediate subdirs as needed.
-     */
-    for (i = 6; i > 0; i -= 3) {
-       pathbuf[len - i] = '\0';
-       if (stat(pathbuf, &sb) != 0) {
-           if (mkdir(pathbuf, S_IRWXU) != 0)
-               log_error(USE_ERRNO, "Can't mkdir %s", pathbuf);
-       } else if (!S_ISDIR(sb.st_mode)) {
-           log_error(0, "%s: %s", pathbuf, strerror(ENOTDIR));
-       }
-       pathbuf[len - i] = '/';
-    }
-
-    return(len);
-}
-
-void
-script_setup()
-{
-    char pathbuf[PATH_MAX];
-    int len;
-
-    script_fds[SFD_USERTTY] = open(_PATH_TTY, O_RDWR|O_NOCTTY, 0);
-    if (script_fds[SFD_USERTTY] == -1)
-       log_error(0, "tty required for transcript support"); /* XXX */
-
-    if (!get_pty(&script_fds[SFD_MASTER], &script_fds[SFD_SLAVE],
-       slavename, sizeof(slavename)))
-       log_error(USE_ERRNO, "Can't get pty");
-
-    /*
-     * Build a path containing the session id split into two-digit subdirs,
-     * so ID 000001 becomes /var/log/sudo-session/00/00/01.
-     */
-    len = build_idpath(pathbuf);
-
-    /*
-     * We create 3 files: a log file, one for the raw session data,
-     * and one for the timing info.
-     */
-    script_fds[SFD_LOG] = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
-    if (script_fds[SFD_LOG] == -1)
-       log_error(USE_ERRNO, "Can't create %s", pathbuf);
-
-    strlcat(pathbuf, ".scr", sizeof(pathbuf));
-    script_fds[SFD_OUTPUT] = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY,
-       S_IRUSR|S_IWUSR);
-    if (script_fds[SFD_OUTPUT] == -1)
-       log_error(USE_ERRNO, "Can't create %s", pathbuf);
-
-    pathbuf[len] = '\0';
-    strlcat(pathbuf, ".tim", sizeof(pathbuf));
-    script_fds[SFD_TIMING] = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
-    if (script_fds[SFD_TIMING] == -1)
-       log_error(USE_ERRNO, "Can't create %s", pathbuf);
-}
-
-int
-script_duplow(fd)
-    int fd;
-{
-    int i, j, indices[6];
-
-    /* sort fds so we can dup them safely */
-    for (i = 0; i < 6; i++)
-       indices[i] = i;
-    qsort(indices, 6, sizeof(int), fdcompar);
-
-    /* Move pty master/slave and session fds to low numbered fds. */
-    for (i = 0; i < 6; i++) {
-       j = indices[i];
-       if (script_fds[j] != fd) {
-#ifdef HAVE_DUP2
-           dup2(script_fds[j], fd);
-#else
-           close(fd);
-           dup(script_fds[j]);
-           close(script_fds[j]);
-#endif
-       }
-       script_fds[j] = fd++;
-    }
-    return(fd);
-}
-
-/* Update output and timing files. */
-static void
-log_output(buf, n, then, now, ofile, tfile)
-    char *buf;
-    int n;
-    struct timeval *then;
-    struct timeval *now;
-    union script_fd ofile;
-    union script_fd tfile;
-{
-    struct timeval tv;
-    sigset_t omask;
-
-    sigprocmask(SIG_BLOCK, &ttyblock, &omask);
-
-#ifdef HAVE_ZLIB
-    if (def_compress_transcript)
-       gzwrite(ofile.g, buf, n);
-    else
-#endif
-       fwrite(buf, 1, n, ofile.f);
-    timersub(now, then, &tv);
-#ifdef HAVE_ZLIB
-    if (def_compress_transcript)
-       gzprintf(tfile.g, "%f %d\n",
-           tv.tv_sec + ((double)tv.tv_usec / 1000000), n);
-    else
-#endif
-       fprintf(tfile.f, "%f %d\n",
-           tv.tv_sec + ((double)tv.tv_usec / 1000000), n);
-    then->tv_sec = now->tv_sec;
-    then->tv_usec = now->tv_usec;
-
-    sigprocmask(SIG_SETMASK, &omask, NULL);
-}
-
-static void
-check_foreground()
-{
-    foreground = tcgetpgrp(script_fds[SFD_USERTTY]) == ppgrp;
-    if (foreground && !tty_initialized) {
-       if (term_copy(script_fds[SFD_USERTTY], script_fds[SFD_SLAVE], ttyout)) {
-           tty_initialized = 1;
-           sync_winsize(script_fds[SFD_USERTTY], script_fds[SFD_SLAVE]);
-       }
-    }
-}
-
-/*
- * Suspend sudo if the underlying command is suspended.
- * Returns SIGUSR1 if the child should be resume in foreground else SIGUSR2.
- */
-static int
-suspend_parent(signo, output, then, now, ofile, tfile)
-    int signo;
-    struct script_buf *output;
-    struct timeval *then;
-    struct timeval *now;
-    union script_fd ofile;
-    union script_fd tfile;
-{
-    sigaction_t sa, osa;
-    int n, oldmode = ttymode, rval = 0;
-
-    switch (signo) {
-    case SIGTTOU:
-    case SIGTTIN:
-       /*
-        * If we are the foreground process, just resume the child.
-        * Otherwise, re-send the signal with the handler disabled.
-        */
-       if (!foreground)
-           check_foreground();
-       if (foreground) {
-           if (ttymode != TERM_RAW) {
-               do {
-                   n = term_raw(script_fds[SFD_USERTTY], !ttyout, 0);
-               } while (!n && errno == EINTR);
-               ttymode = TERM_RAW;
-           }
-           rval = SIGUSR1; /* resume child in foreground */
-           break;
-       }
-       ttymode = TERM_RAW;
-       /* FALLTHROUGH */
-    case SIGTSTP:
-       /* Flush any remaining output to master tty. */
-       flush_output(output, then, now, ofile, tfile);
-
-       /* Restore original tty mode before suspending. */
-       if (oldmode != TERM_COOKED) {
-           do {
-               n = term_restore(script_fds[SFD_USERTTY], 0);
-           } while (!n && errno == EINTR);
-       }
-
-       /* Suspend self and continue child when we resume. */
-       sa.sa_handler = SIG_DFL;
-       sigaction(signo, &sa, &osa);
-#ifdef SCRIPT_DEBUG
-       warningx("kill parent %d", signo);
-#endif
-       killpg(ppgrp, signo);
-
-       /* Check foreground/background status on resume. */
-       check_foreground();
-
-       /*
-        * Only modify term if we are foreground process and either
-        * the old tty mode was not cooked or child got SIGTT{IN,OU}
-        */
-#ifdef SCRIPT_DEBUG
-       warningx("parent is in %sground, ttymode %d -> %d",
-           foreground ? "fore" : "back", oldmode, ttymode);
-#endif
-
-       if (ttymode != TERM_COOKED) {
-           if (foreground) {
-               /* Set raw/cbreak mode. */
-               do {
-                   n = term_raw(script_fds[SFD_USERTTY], !ttyout,
-                       ttymode == TERM_CBREAK);
-               } while (!n && errno == EINTR);
-           } else {
-               /* background process, no access to tty. */
-               ttymode = TERM_COOKED;
-           }
-       }
-
-       sigaction(signo, &osa, NULL);
-       rval = ttymode == TERM_RAW ? SIGUSR1 : SIGUSR2;
-       break;
-    }
-
-    return(rval);
-}
-
-/*
- * This is a little bit tricky due to how POSIX job control works and
- * we fact that we have two different controlling terminals to deal with.
- * There are three processes:
- *  1) parent, which forks a child and does all the I/O passing.
- *     Handles job control signals send by its child to bridge the
- *     two sessions (and ttys).
- *  2) child, creates a new session so it can receive notification of
- *     tty stop signals (SIGTSTP, SIGTTIN, SIGTTOU).  Waits for the
- *     command to stop or die and passes back tty stop signals to parent
- *     so job control works in the user's shell.
- *  3) grandchild, executes the actual command with the pty slave as its
- *     controlling tty, belongs to child's session but has its own pgrp.
- */
-int
-script_execv(path, argv)
-    char *path;
-    char *argv[];
-{
-    sigaction_t sa;
-    struct script_buf input, output;
-    struct timeval now, then;
-    int n, nready, exitcode = 1;
-    int relaysig, sv[2];
-    fd_set *fdsr, *fdsw;
-    FILE *idfile;
-    union script_fd ofile, tfile;
-    int rbac_enabled = 0;
-
-#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)
-           log_error(USE_ERRNO, "cannot open %s", slavename);
-    }
-#endif
-
-    /* Are we the foreground process? */
-    ppgrp = getpgrp(); /* so child can pass signals back to us */
-    foreground = tcgetpgrp(script_fds[SFD_USERTTY]) == ppgrp;
-
-    /* So we can block tty-generated signals */
-    sigemptyset(&ttyblock);
-    sigaddset(&ttyblock, SIGINT);
-    sigaddset(&ttyblock, SIGQUIT);
-    sigaddset(&ttyblock, SIGTSTP);
-    sigaddset(&ttyblock, SIGTTIN);
-    sigaddset(&ttyblock, SIGTTOU);
-
-    /* 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;
-    sigaction(SIGCHLD, &sa, NULL);
-
-    /* Ignore SIGPIPE from other end of socketpair. */
-    sa.sa_handler = SIG_IGN;
-    sigaction(SIGPIPE, &sa, NULL);
-
-    /* Signals to relay from parent to child. */
-    sa.sa_flags = 0; /* do not restart syscalls for these */
-    sa.sa_handler = handler;
-    sigaction(SIGHUP, &sa, NULL);
-    sigaction(SIGTERM, &sa, NULL);
-    sigaction(SIGINT, &sa, NULL);
-    sigaction(SIGQUIT, &sa, NULL);
-    sigaction(SIGTSTP, &sa, NULL);
-#if 0 /* XXX - keep? */
-    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_STREAM, 0, sv) != 0)
-       log_error(USE_ERRNO, "cannot create sockets");
-
-    if (foreground) {
-       /* Copy terminal attrs from user tty -> pty slave. */
-       if (term_copy(script_fds[SFD_USERTTY], script_fds[SFD_SLAVE], ttyout)) {
-           tty_initialized = 1;
-           sync_winsize(script_fds[SFD_USERTTY], script_fds[SFD_SLAVE]);
-       }
-
-       /* Start out in raw mode is stdout is a tty. */
-       ttymode = ttyout ? TERM_RAW : TERM_CBREAK;
-       do {
-           n = term_raw(script_fds[SFD_USERTTY], !ttyout,
-               ttymode == TERM_CBREAK);
-       } while (!n && errno == EINTR);
-       if (!n)
-           log_error(USE_ERRNO, "Can't set terminal to raw mode");
-    }
-
-    /*
-     * Child will run the command in the pty, parent will pass data
-     * to and from pty.
-     */
-    child = fork();
-    switch (child) {
-    case -1:
-       log_error(USE_ERRNO, "fork");
-       break;
-    case 0:
-       close(sv[0]);
-       script_child(path, argv, sv[1], rbac_enabled);
-       /* NOTREACHED */
-       break;
-    }
-    close(sv[1]);
-
-    if ((idfile = fdopen(script_fds[SFD_LOG], "w")) == NULL)
-       log_error(USE_ERRNO, "fdopen");
-#ifdef HAVE_ZLIB
-    if (def_compress_transcript) {
-       if ((ofile.g = gzdopen(script_fds[SFD_OUTPUT], "w")) == NULL)
-           log_error(USE_ERRNO, "gzdopen");
-       if ((tfile.g = gzdopen(script_fds[SFD_TIMING], "w")) == NULL)
-           log_error(USE_ERRNO, "gzdopen");
-    } else
-#endif
-    {
-       if ((ofile.f = fdopen(script_fds[SFD_OUTPUT], "w")) == NULL)
-           log_error(USE_ERRNO, "fdopen");
-       if ((tfile.f = fdopen(script_fds[SFD_TIMING], "w")) == NULL)
-           log_error(USE_ERRNO, "fdopen");
-    }
-
-    gettimeofday(&then, NULL);
-
-    /* XXX - log more stuff?  window size? environment? */
-    fprintf(idfile, "%ld:%s:%s:%s:%s\n", then.tv_sec, user_name,
-       runas_pw->pw_name, runas_gr ? runas_gr->gr_name : "", user_tty);
-    fprintf(idfile, "%s\n", user_cwd);
-    fprintf(idfile, "%s%s%s\n", user_cmnd, user_args ? " " : "",
-       user_args ? user_args : "");
-    fclose(idfile);
-
-    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
-     * and pass output from master to stdout and ofile.  Note that
-     * we've set things up such that master is > 3 (see sudo.c).
-     */
-    fdsr = (fd_set *)emalloc2(howmany(sv[0] + 1, NFDBITS), sizeof(fd_mask));
-    fdsw = (fd_set *)emalloc2(howmany(sv[0] + 1, NFDBITS), sizeof(fd_mask));
-    zero_bytes(&input, sizeof(input));
-    zero_bytes(&output, sizeof(output));
-    while (alive) {
-       /* XXX - racey */
-       if (!relaysig && recvsig != SIGCHLD) {
-           relaysig = recvsig;
-           recvsig = 0;
-       }
-
-       if (input.off == input.len)
-           input.off = input.len = 0;
-       if (output.off == output.len)
-           output.off = output.len = 0;
-
-       zero_bytes(fdsw, howmany(sv[0] + 1, NFDBITS) * sizeof(fd_mask));
-       zero_bytes(fdsr, howmany(sv[0] + 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);
-       FD_SET(sv[0], fdsr);
-       if (relaysig)
-           FD_SET(sv[0], fdsw);
-
-       nready = select(sv[0] + 1, fdsr, fdsw, NULL, NULL);
-       if (nready == -1) {
-           if (errno == EINTR)
-               continue;
-           log_error(USE_ERRNO, "select failed");
-       }
-       if (FD_ISSET(sv[0], fdsr)) {
-           /* read child status */
-           n = read(sv[0], &child_status, sizeof(child_status));
-           if (n == -1) {
-               if (errno == EINTR)
-                   continue;
-               if (errno != EAGAIN)
-                   break;
-           } else if (n != sizeof(child_status)) {
-               break; /* EOF? */
-           }
-           if (WIFSTOPPED(child_status)) {
-               /* Suspend parent and tell child how to resume on return. */
-#ifdef SCRIPT_DEBUG
-               warningx("child stopped, suspending parent");
-#endif
-               relaysig = suspend_parent(WSTOPSIG(child_status),
-                   &output, &then, &now, ofile, tfile);
-               /* XXX - write relaysig immediately? */
-               continue;
-           } else {
-               /* Child exited or was killed, either way we are done. */
-               if (WIFEXITED(child_status))
-                   exitcode = WEXITSTATUS(child_status);
-               else if (WIFSIGNALED(child_status))
-                   exitcode = WTERMSIG(child_status) | 128;
-               break;
-           }
-       }
-       if (FD_ISSET(sv[0], fdsw)) {
-           /* XXX - we rely on child to be suspended before we suspend us */
-           n = write(sv[0], &relaysig, sizeof(relaysig));
-           relaysig = 0;
-           if (n == -1) {
-               if (errno == EINTR)
-                   continue;
-               if (errno != EAGAIN)
-                   break;
-           } else if (n != sizeof(relaysig)) {
-               break; /* should not happen */
-           }
-       }
-       if (FD_ISSET(script_fds[SFD_USERTTY], fdsr)) {
-           n = read(script_fds[SFD_USERTTY], input.buf + input.len,
-               sizeof(input.buf) - input.len);
-           if (n == -1) {
-               if (errno == EINTR)
-                   continue;
-               if (errno != EAGAIN)
-                   break;
-           } else {
-               if (n == 0)
-                   break; /* got EOF */
-               input.len += n;
-           }
-       }
-       if (FD_ISSET(script_fds[SFD_MASTER], fdsw)) {
-           n = write(script_fds[SFD_MASTER], input.buf + input.off,
-               input.len - input.off);
-           if (n == -1) {
-               if (errno == EINTR)
-                   continue;
-               if (errno != EAGAIN)
-                   break;
-           } else {
-               input.off += n;
-           }
-       }
-       if (FD_ISSET(script_fds[SFD_MASTER], fdsr)) {
-           gettimeofday(&now, NULL);
-           n = read(script_fds[SFD_MASTER], output.buf + output.len,
-               sizeof(output.buf) - output.len);
-           if (n == -1) {
-               if (errno == EINTR)
-                   continue;
-               if (errno != EAGAIN)
-                   break;
-           } else {
-               if (n == 0)
-                   break; /* got EOF */
-
-               /* Update output and timing files. */
-               log_output(output.buf + output.len, n, &then, &now, ofile, tfile);
-               output.len += n;
-           }
-       }
-       if (FD_ISSET(STDOUT_FILENO, fdsw)) {
-           n = write(STDOUT_FILENO, output.buf + output.off,
-               output.len - output.off);
-           if (n == -1) {
-               if (errno == EINTR)
-                   continue;
-               if (errno != EAGAIN)
-                   break;
-           } else {
-               output.off += n;
-           }
-       }
-    }
-
-    /* Flush any remaining output to stdout (already updated output file). */
-    n = fcntl(STDOUT_FILENO, F_GETFL, 0);
-    if (n != -1) {
-       n &= ~O_NONBLOCK;
-       (void) fcntl(STDOUT_FILENO, F_SETFL, n);
-    }
-    flush_output(&output, &then, &now, ofile, tfile);
-
-#ifdef HAVE_ZLIB
-    if (def_compress_transcript) {
-       gzclose(ofile.g);
-       gzclose(tfile.g);
-    } else
-#endif
-    {
-       fclose(ofile.f);
-       fclose(tfile.f);
-    }
-
-#ifdef HAVE_STRSIGNAL
-    if (WIFSIGNALED(child_status)) {
-       int signo = WTERMSIG(child_status);
-       if (signo && signo != SIGINT && signo != SIGPIPE) {
-           char *reason = strsignal(signo);
-           write(STDOUT_FILENO, reason, strlen(reason));
-           if (WCOREDUMP(child_status))
-               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);
-
-    exit(exitcode);
-}
-
-void
-script_child(path, argv, backchannel, rbac_enabled)
-    char *path;
-    char *argv[];
-    int backchannel;
-    int rbac_enabled;
-{
-    sigaction_t sa;
-    pid_t pid, self = getpid();
-    int nread, signo, status;
-#ifndef TIOCSCTTY
-    int n;
-#endif
-
-    recvsig = 0;
-
-    /* Close unused fds. */
-    close(script_fds[SFD_MASTER]);
-    close(script_fds[SFD_LOG]);
-    close(script_fds[SFD_OUTPUT]);
-    close(script_fds[SFD_TIMING]);
-    close(script_fds[SFD_USERTTY]);
-
-    /* Reset signal handlers. */
-    zero_bytes(&sa, sizeof(sa));
-    sigemptyset(&sa.sa_mask);
-    sa.sa_flags = SA_RESTART;
-    sa.sa_handler = SIG_DFL;
-    sigaction(SIGCONT, &sa, NULL);
-    sigaction(SIGWINCH, &sa, NULL);
-
-    /* Ignore any SIGTT{IN,OU} or SIGPIPE we get. */
-    sa.sa_handler = SIG_IGN;
-    sigaction(SIGPIPE, &sa, NULL);
-    sigaction(SIGTTIN, &sa, NULL);
-    sigaction(SIGTTOU, &sa, NULL);
-
-    /* We want SIGCHLD to interrupt us. */
-    sa.sa_flags = 0; /* do not restart syscalls for these signals. */
-    sa.sa_handler = handler;
-    sigaction(SIGCHLD, &sa, NULL);
-
-    /*
-     * Start a new session with the parent as the session leader
-     * and the slave pty as the controlling terminal.
-     * This allows us to be notified when the child has been suspended.
-     */
-#ifdef HAVE_SETSID
-    if (setsid() == -1) {
-       warning("setsid");
-       _exit(1);
-    }
-#else
-# ifdef TIOCNOTTY
-    n = open(_PATH_TTY, O_RDWR|O_NOCTTY);
-    if (n >= 0) {
-       /* Disconnect from old controlling tty. */
-       if (ioctl(n, TIOCNOTTY, NULL) == -1)
-           warning("cannot disconnect controlling tty");
-       close(n);
-    }
-# endif
-    setpgrp(0, 0);
-#endif
-#ifdef TIOCSCTTY
-    if (ioctl(script_fds[SFD_SLAVE], TIOCSCTTY, NULL) != 0)
-       log_error(USE_ERRNO, "unable to set controlling tty");
-#else
-    /* Set controlling tty by reopening slave. */
-    if ((n = open(slavename, O_RDWR)) >= 0)
-       close(n);
-#endif
-
-    if (foreground && !ttyout)
-       foreground = 0;
-
-    /* Start command and wait for it to stop or exit */
-    child = fork();
-    if (child == -1) {
-       warning("Can't fork");
-       _exit(1);
-    }
-    if (child == 0) {
-       /* Reset signal handlers. */
-       sa.sa_flags = SA_RESTART;
-       sa.sa_handler = SIG_DFL;
-       sigaction(SIGHUP, &sa, NULL);
-       sigaction(SIGTERM, &sa, NULL);
-       sigaction(SIGINT, &sa, NULL);
-       sigaction(SIGQUIT, &sa, NULL);
-       sigaction(SIGTSTP, &sa, NULL);
-       sigaction(SIGTTIN, &sa, NULL);
-       sigaction(SIGTTOU, &sa, NULL);
-       sigaction(SIGUSR1, &sa, NULL);
-       sigaction(SIGUSR2, &sa, NULL);
-
-       /* setup tty and exec command */
-       script_run(path, argv, rbac_enabled);
-       warning("unable to execute %s", path);
-       _exit(127);
-    }
-
-    /*
-     * Put child in its own process group.  If we are starting the command
-     * in the foreground, assign its pgrp to the tty.
-     */
-    setpgid(child, child);
-    if (foreground) {
-       do {
-           status = tcsetpgrp(script_fds[SFD_SLAVE], child);
-       } while (status == -1 && errno == EINTR);
-    }
-
-    /* Wait for signal on backchannel or for SIGCHLD */
-    for (;;) {
-       /* Read child status, assumes recvsig can only be SIGCHLD */
-       while (recvsig) {
-           recvsig = 0;
-           /* read child status and relay to parent */
-           do {
-               pid = waitpid(child, &status, WUNTRACED|WNOHANG);
-           } while (pid == -1 && errno == EINTR);
-           if (pid == child) {
-#ifdef SCRIPT_DEBUG
-               if (WIFSTOPPED(status))
-                   warningx("command stopped, signal %d", WSTOPSIG(status));
-               else if (WIFSIGNALED(status))
-                   warningx("command killed, signal %d", WTERMSIG(status));
-               else
-                   warningx("command exited?");
-#endif
-               if (write(backchannel, &status, sizeof(status)) != sizeof(status))
-                   break; /* XXX - error, kill child and exit */
-#ifdef SCRIPT_DEBUG
-               warningx("sent signo to parent");
-#endif
-               if (!WIFSTOPPED(status)) {
-                   _exit(1); /* child dead */
-               }
-           }
-       }
-       nread = read(backchannel, &signo, sizeof(signo));
-       if (nread == -1) {
-           if (errno != EINTR)
-               break; /* XXX - error, kill child and exit */
-           continue;
-       }
-       if (nread != sizeof(signo)) {
-           /* EOF? */
-           break;
-       }
-
-       /* Handle signal from parent. */
-#ifdef SCRIPT_DEBUG
-       warningx("signal %d from parent", signo);
-#endif
-       switch (signo) {
-       case SIGKILL:
-           _exit(1);
-           /* 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);
-}
-
-static void
-flush_output(output, then, now, ofile, tfile)
-    struct script_buf *output;
-    struct timeval *then;
-    struct timeval *now;
-    union script_fd ofile;
-    union script_fd tfile;
-{
-    int n;
-
-    while (output->len > output->off) {
-       n = write(STDOUT_FILENO, output->buf + output->off,
-           output->len - output->off);
-       if (n <= 0)
-           break;
-       output->off += n;
-    }
-
-    /* Make sure there is no output remaining on the master pty. */
-    for (;;) {
-       n = read(script_fds[SFD_MASTER], output->buf, sizeof(output->buf));
-       if (n <= 0)
-           break;
-       log_output(output->buf, n, &then, &now, ofile, tfile);
-       output->off = 0;
-       output->len = n;
-       do {
-           n = write(STDOUT_FILENO, output->buf + output->off,
-               output->len - output->off);
-           if (n <= 0)
-               break;
-           output->off += n;
-       } while (output->len > output->off);
-    }
-}
-
-static void
-script_run(path, argv, rbac_enabled)
-    char *path;
-    char *argv[];
-    int rbac_enabled;
-{
-    pid_t self = getpid();
-
-    /* Set child process group here too to avoid a race. */
-    setpgid(0, self);
-
-    /*
-     * We have guaranteed that the slave fd > 3
-     */
-    if (isatty(STDIN_FILENO))
-       dup2(script_fds[SFD_SLAVE], STDIN_FILENO);
-    dup2(script_fds[SFD_SLAVE], STDOUT_FILENO);
-    dup2(script_fds[SFD_SLAVE], STDERR_FILENO);
-    close(script_fds[SFD_SLAVE]);
-
-    /* Wait for parent to grant us the tty if we are foreground. */
-    if (foreground) {
-       while (tcgetpgrp(STDOUT_FILENO) != self)
-           ; /* spin */
-    }
-
-#ifdef HAVE_SELINUX
-    if (rbac_enabled)
-       selinux_execv(path, argv);
-    else
-#endif
-    execv(path, argv);
-}
-
-static void
-sync_winsize(src, dst)
-    int src;
-    int dst;
-{
-#ifdef TIOCGWINSZ
-    struct winsize win;
-    pid_t pgrp;
-
-    if (ioctl(src, TIOCGWINSZ, &win) == 0) {
-           ioctl(dst, TIOCSWINSZ, &win);
-#ifdef TIOCGPGRP
-           if (ioctl(dst, TIOCGPGRP, &pgrp) == 0)
-                   killpg(pgrp, SIGWINCH);
-#endif
-    }
-#endif
-}
-
-/*
- * Handler for SIGCHLD in parent
- */
-static void
-sigchild(s)
-    int s;
-{
-    pid_t pid;
-    int serrno = errno;
-
-    do {
-       pid = waitpid(child, &child_status, WNOHANG | WUNTRACED);
-    } while (pid == -1 && errno == EINTR);
-    if (pid == child) {
-       if (WIFSTOPPED(child_status))
-           recvsig = WSTOPSIG(child_status);
-       else
-           alive = 0;
-    }
-
-    errno = serrno;
-}
-
-/*
- * Generic handler for signals passed from parent -> child
- */
-static void
-handler(s)
-    int s;
-{
-    recvsig = s;
-}
-
-/*
- * Handler for SIGWINCH in parent
- */
-static void
-sigwinch(s)
-    int s;
-{
-    int serrno = errno;
-
-    sync_winsize(script_fds[SFD_USERTTY], script_fds[SFD_SLAVE]);
-    errno = serrno;
-}
index 1e71acefca830f5304d60d92ee3bf09eeb9057d9..22416f46ec8e85bd167ff7c59a46914633871b0b 100644 (file)
--- a/selinux.c
+++ b/selinux.c
@@ -1,4 +1,5 @@
 /*
+ * Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com>
  * Copyright (c) 2008 Dan Walsh <dwalsh@redhat.com>
  *
  * Borrowed heavily from newrole source code
 #include "sudo.h"
 #include "pathnames.h"
 
-static security_context_t old_context;
-static security_context_t new_context;
-static security_context_t tty_context;
-static security_context_t new_tty_context;
-static int enforcing;
+static struct selinux_state {
+    security_context_t old_context;
+    security_context_t new_context;
+    security_context_t tty_context;
+    security_context_t new_tty_context;
+    const char *ttyn;
+    int ttyfd;
+    int enforcing;
+} se_state;
 
 /*
  * This function attempts to revert the relabeling done to the tty.
  * fd             - referencing the opened ttyn
  * ttyn                   - name of tty to restore
- * tty_context    - original context of the tty
- * new_tty_context - context tty was relabeled to
  *
  * Returns zero on success, non-zero otherwise
  */
-static int
-restore_tty_label(int fd, const char *ttyn, security_context_t tty_context,
-    security_context_t new_tty_context)
+/* XXX - should also be called as part of cleanup() */
+int
+selinux_restore_tty(void)
 {
-    int rc = 0;
+    int retval = 0;
     security_context_t chk_tty_context = NULL;
 
-    if (!ttyn)
-           goto skip_relabel;
-
-    if (!new_tty_context)
-           goto skip_relabel;
+    if (se_state.ttyfd == -1 || se_state.new_tty_context == NULL)
+       goto skip_relabel;
 
     /* Verify that the tty still has the context set by sudo. */
-    if ((rc = fgetfilecon(fd, &chk_tty_context)) < 0) {
-           warning("unable to fgetfilecon %s", ttyn);
-           goto skip_relabel;
+    if ((retval = fgetfilecon(se_state.ttyfd, &chk_tty_context)) < 0) {
+       warning("unable to fgetfilecon %s", se_state.ttyn);
+       goto skip_relabel;
     }
 
-    if ((rc = strcmp(chk_tty_context, new_tty_context))) {
-           warningx("%s changed labels.", ttyn);
-           goto skip_relabel;
+    if ((retval = strcmp(chk_tty_context, se_state.new_tty_context))) {
+       warningx("%s changed labels.", se_state.ttyn);
+       goto skip_relabel;
     }
 
-    if ((rc = fsetfilecon(fd, tty_context)) < 0)
-       warning("unable to restore context for %s", ttyn);
+    if ((retval = fsetfilecon(se_state.ttyfd, se_state.tty_context)) < 0)
+       warning("unable to restore context for %s", se_state.ttyn);
 
 skip_relabel:
+    if (se_state.ttyfd != -1)
+       close(se_state.ttyfd);
     freecon(chk_tty_context);
-    return(rc);
+    return retval;
 }
 
 /*
@@ -104,41 +106,90 @@ skip_relabel:
  * in permissive mode.
  */
 static int
-relabel_tty(int ttyfd, security_context_t new_context,
-    security_context_t *tty_context, security_context_t *new_tty_context,
-    int enforcing)
+relabel_tty(const char *ttyn, int ptyfd)
 {
     security_context_t tty_con = NULL;
     security_context_t new_tty_con = NULL;
 
-    if (fgetfilecon(ttyfd, &tty_con) < 0) {
+    se_state.ttyfd = ptyfd;
+
+    /* It is perfectly legal to have no tty. */
+    if (ptyfd == -1 && ttyn == NULL)
+       return 0;
+
+    /* If sudo is not allocating a pty for the command, open current tty. */
+    if (ptyfd == -1) {
+       se_state.ttyfd = open(ttyn, O_RDWR|O_NONBLOCK);
+       if (se_state.ttyfd == -1) {
+           warning("unable to open %s, not relabeling tty", ttyn);
+           if (se_state.enforcing)
+               goto bad;
+       }
+       (void)fcntl(se_state.ttyfd, F_SETFL,
+           fcntl(se_state.ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
+    }
+
+    if (fgetfilecon(se_state.ttyfd, &tty_con) < 0) {
        warning("unable to get current tty context, not relabeling tty");
-       if (enforcing)
-           goto error;
+       if (se_state.enforcing)
+           goto bad;
     }
 
-    if (tty_con && (security_compute_relabel(new_context, tty_con,
+    if (tty_con && (security_compute_relabel(se_state.new_context, tty_con,
        SECCLASS_CHR_FILE, &new_tty_con) < 0)) {
        warning("unable to get new tty context, not relabeling tty");
-       if (enforcing)
-           goto error;
+       if (se_state.enforcing)
+           goto bad;
     }
 
     if (new_tty_con != NULL) {
-       if (fsetfilecon(ttyfd, new_tty_con) < 0) {
+       if (fsetfilecon(se_state.ttyfd, new_tty_con) < 0) {
            warning("unable to set new tty context");
-           if (enforcing)
-               goto error;
+           if (se_state.enforcing)
+               goto bad;
        }
     }
 
-    *tty_context = tty_con;
-    *new_tty_context = new_tty_con;
-    return(0);
-
-error:
+    if (ptyfd != -1) {
+       /* Reopen pty that was relabeled, std{in,out,err} are reset later. */
+       se_state.ttyfd = open(ttyn, O_RDWR|O_NOCTTY, 0);
+       if (se_state.ttyfd == -1) {
+           warning("cannot open %s", ttyn);
+           if (se_state.enforcing)
+               goto bad;
+       }
+       dup2(se_state.ttyfd, ptyfd);
+    } else {
+       /* Re-open tty to get new label and reset std{in,out,err} */
+       close(se_state.ttyfd);
+       se_state.ttyfd = open(ttyn, O_RDWR|O_NONBLOCK);
+       if (se_state.ttyfd == -1)
+           warning("unable to open %s", ttyn);
+       else
+           (void)fcntl(se_state.ttyfd, F_SETFL,
+               fcntl(se_state.ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
+       if (isatty(STDIN_FILENO))
+           dup2(se_state.ttyfd, STDIN_FILENO);
+       if (isatty(STDOUT_FILENO))
+           dup2(se_state.ttyfd, STDOUT_FILENO);
+       if (isatty(STDERR_FILENO))
+           dup2(se_state.ttyfd, STDERR_FILENO);
+    }
+    /* Retain se_state.ttyfd so we can restore label when command finishes. */
+    (void)fcntl(se_state.ttyfd, F_SETFD, FD_CLOEXEC);
+
+    se_state.ttyn = ttyn;
+    se_state.tty_context = tty_con;
+    se_state.new_tty_context = new_tty_con;
+    return 0;
+
+bad:
+    if (se_state.ttyfd != -1 && se_state.ttyfd != ptyfd) {
+       close(se_state.ttyfd);
+       se_state.ttyfd = -1;
+    }
     freecon(tty_con);
-    return(-1);
+    return -1;
 }
 
 /*
@@ -146,7 +197,7 @@ error:
  * specified role and type.
  */
 security_context_t
-get_exec_context(security_context_t old_context, char *role, char *type)
+get_exec_context(security_context_t old_context, const char *role, const char *type)
 {
     security_context_t new_context = NULL;
     context_t context = NULL;
@@ -155,12 +206,12 @@ get_exec_context(security_context_t old_context, char *role, char *type)
     /* We must have a role, the type is optional (we can use the default). */
     if (!role) {
        warningx("you must specify a role.");
-       return(NULL);
+       return NULL;
     }
     if (!type) {
        if (get_default_type(role, &typebuf)) {
            warningx("unable to get default type");
-           return(NULL);
+           return NULL;
        }
        type = typebuf;
     }
@@ -177,11 +228,11 @@ get_exec_context(security_context_t old_context, char *role, char *type)
      */
     if (context_role_set(context, role)) {
        warningx("failed to set new role %s", role);
-       goto error;
+       goto bad;
     }
     if (context_type_set(context, type)) {
        warningx("failed to set new type %s", type);
-       goto error;
+       goto bad;
     }
       
     /*
@@ -190,7 +241,7 @@ get_exec_context(security_context_t old_context, char *role, char *type)
     new_context = estrdup(context_str(context));
     if (security_check_context(new_context) < 0) {
        warningx("%s is not a valid context", new_context);
-       goto error;
+       goto bad;
     }
 
 #ifdef DEBUG
@@ -198,64 +249,71 @@ get_exec_context(security_context_t old_context, char *role, char *type)
 #endif
 
     context_free(context);
-    return(new_context);
+    return new_context;
 
-error:
+bad:
     free(typebuf);
     context_free(context);
     freecon(new_context);
-    return(NULL);
+    return NULL;
 }
 
 /* 
- * Set the tty context in preparation for fork/exec.
+ * Set the exec and tty contexts in preparation for fork/exec.
+ * Must run as root, before the uid change.
+ * If ptyfd is not -1, it indicates we are running
+ * in a pty and do not need to reset std{in,out,err}.
  */
 void
-selinux_prefork(char *role, char *type, int ttyfd)
+selinux_setup(const char *role, const char *type, const char *ttyn,
+    int ptyfd)
 {
     /* Store the caller's SID in old_context. */
-    if (getprevcon(&old_context))
+    if (getprevcon(&se_state.old_context))
        error(EXIT_FAILURE, "failed to get old_context");
 
-    enforcing = security_getenforce();
-    if (enforcing < 0)
+    se_state.enforcing = security_getenforce();
+    if (se_state.enforcing < 0)
        error(EXIT_FAILURE, "unable to determine enforcing mode.");
 
 #ifdef DEBUG
-    warningx("your old context was %s", old_context);
+    warningx("your old context was %s", se_state.old_context);
 #endif
-    new_context = get_exec_context(old_context, role, type);
-    if (!new_context)
+    se_state.new_context = get_exec_context(se_state.old_context, role, type);
+    if (!se_state.new_context)
        error(EXIT_FAILURE, "unable to get exec context");
     
-    ttyfd = relabel_tty(ttyfd, new_context, &tty_context,
-       &new_tty_context, enforcing);
-    if (ttyfd < 0)
-       error(EXIT_FAILURE, "unable to setup tty context for %s", new_context);
+    if (relabel_tty(ttyn, ptyfd) < 0)
+       error(EXIT_FAILURE, "unable to setup tty context for %s", se_state.new_context);
 
 #ifdef DEBUG
-    warningx("your old tty context is %s", tty_context);
-    warningx("your new tty context is %s", new_tty_context);
+    if (se_state.ttyfd != -1) {
+       warningx("your old tty context is %s", se_state.tty_context);
+       warningx("your new tty context is %s", se_state.new_tty_context);
+    }
 #endif
+
 }
 
 void
-selinux_execv(char *path, char **argv)
+selinux_execve(const char *path, char *argv[], char *envp[])
 {
-    if (setexeccon(new_context)) {
-       warning("unable to set exec context to %s", new_context);
-       if (enforcing)
+    int serrno;
+
+    if (setexeccon(se_state.new_context)) {
+       warning("unable to set exec context to %s", se_state.new_context);
+       if (se_state.enforcing)
            return;
     }
 
-    if (setkeycreatecon(new_context)) {
-       warning("unable to set key creation context to %s", new_context);
-       if (enforcing)
+    if (setkeycreatecon(se_state.new_context)) {
+       warning("unable to set key creation context to %s", se_state.new_context);
+       if (se_state.enforcing)
            return;
     }
 
 #ifdef WITH_AUDIT
-    if (send_audit_message(1, old_context, new_context, user_ttypath)) 
+    if (send_audit_message(1, se_state.old_context, se_state.new_context, se_state.ttyn)) 
        return;
 #endif
 
@@ -264,86 +322,5 @@ selinux_execv(char *path, char **argv)
     argv[0] = *argv[1] == '-' ? "-sesh" : "sesh";
     argv[1] = path;
 
-    execv(_PATH_SUDO_SESH, argv);
-    warning("%s", path);
-}
-
-/* 
- * If the program is being run with a different security context we
- * need to go through an intermediary process for the transition to
- * be allowed by the policy.  We use the "sesh" shell for this, which
- * will simply execute the command pass to it on the command line.
- */
-void
-selinux_exec(char *role, char *type, char **argv)
-{
-    pid_t childPid;
-    int ttyfd;
-
-    /* Must have a tty. */
-    if (user_ttypath == NULL || *user_ttypath == '\0')
-       error(EXIT_FAILURE, "unable to determine tty");
-
-    /* Re-open TTY descriptor */
-    ttyfd = open(user_ttypath, O_RDWR | O_NONBLOCK);
-    if (ttyfd == -1)
-       error(EXIT_FAILURE, "unable to open %s", user_ttypath);
-    (void)fcntl(ttyfd, F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
-
-    /*
-     * Get the old and new security and tty contexts, sets the new
-     * tty context on ttyfd.
-     */
-    selinux_prefork(role, type, ttyfd);
-
-    childPid = fork();
-    if (childPid < 0) {
-       /* fork failed, no child to worry about */
-       warning("unable to fork");
-       if (restore_tty_label(ttyfd, user_ttypath, tty_context, new_tty_context))
-           warningx("unable to restore tty label");
-       exit(EXIT_FAILURE);
-    } else if (childPid) {
-       pid_t pid;
-       int status;
-       
-       /* Parent, wait for child to finish. */
-       do {
-               pid = waitpid(childPid, &status, 0);
-       } while (pid == -1 && errno == EINTR);
-
-       if (pid == -1)
-           error(EXIT_FAILURE, "waitpid");
-       
-       if (restore_tty_label(ttyfd, user_ttypath, tty_context, new_tty_context))
-           errorx(EXIT_FAILURE, "unable to restore tty label");
-
-       /* Preserve child exit status. */
-       if (WIFEXITED(status))
-           exit(WEXITSTATUS(status));
-       exit(EXIT_FAILURE);
-    }
-    /* Child */
-    /* Close the tty and reopen descriptors 0 through 2 */
-    if (close(ttyfd) || close(STDIN_FILENO) || close(STDOUT_FILENO) ||
-       close(STDERR_FILENO)) {
-       warning("could not close descriptors");
-       goto error;
-    }
-    ttyfd = open(user_ttypath, O_RDONLY | O_NONBLOCK);
-    if (ttyfd != STDIN_FILENO)
-       goto error;
-    fcntl(ttyfd, F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
-    ttyfd = open(user_ttypath, O_RDWR | O_NONBLOCK);
-    if (ttyfd != STDOUT_FILENO)
-       goto error;
-    fcntl(ttyfd, F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
-    ttyfd = dup(STDOUT_FILENO);
-    if (ttyfd != STDERR_FILENO)
-       goto error;
-
-    selinux_execv(safe_cmnd, argv);
-
-error:
-    _exit(EXIT_FAILURE);
+    execve(_PATH_SUDO_SESH, argv, envp);
 }
diff --git a/sudo.c b/sudo.c
index 09cc7ec6072c20aefceb04ad9bb027f9b41ad7dc..36f9a46a2bb8ccaf4555b1019d86954ecef2c884 100644 (file)
--- a/sudo.c
+++ b/sudo.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1993-1996, 1998-2009 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 1993-1996, 1998-2010 Todd C. Miller <Todd.Miller@courtesan.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -31,6 +31,7 @@
 
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/wait.h>
 #include <sys/param.h>
 #include <sys/socket.h>
 #ifdef HAVE_SETRLIMIT
@@ -159,6 +160,7 @@ sigaction_t saved_sa_int, saved_sa_quit, saved_sa_tstp;
 static char *runas_user;
 static char *runas_group;
 static struct sudo_nss_list *snl;
+static int sudo_mode;
 
 /* For getopt(3) */
 extern char *optarg;
@@ -171,7 +173,7 @@ main(argc, argv, envp)
     char **envp;
 {
     int sources = 0, validated;
-    int fd, cmnd_status, sudo_mode, pwflag, rc = 0;
+    int fd, cmnd_status, pwflag, rc = 0;
     sigaction_t sa;
     struct sudo_nss *nss;
 #if defined(SUDO_DEVEL) && defined(__OpenBSD__)
@@ -445,6 +447,9 @@ main(argc, argv, envp)
     }
 
     if (ISSET(validated, VALIDATE_OK)) {
+       struct command_status cstat;
+       int exitcode = 1;
+
        /* Finally tell the user if the command did not exist. */
        if (cmnd_status == NOT_FOUND_DOT) {
            audit_failure(NewArgv, "command in current directory");
@@ -463,12 +468,11 @@ main(argc, argv, envp)
                validate_env_vars(sudo_user.env_vars);
        }
 
-#ifdef _PATH_SUDO_TRANSCRIPT
+#ifdef _PATH_SUDO_IO_LOGDIR
        /* Get next session ID so we can log it. */
-       if (def_transcript && ISSET(sudo_mode, (MODE_RUN | MODE_EDIT)))
-           script_nextid();
+       if (ISSET(sudo_mode, (MODE_RUN | MODE_EDIT)) && (def_log_input || def_log_output))
+           io_nextid();
 #endif
-
        log_allowed(validated);
        if (ISSET(sudo_mode, MODE_CHECK))
            rc = display_cmnd(snl, list_pw ? list_pw : sudo_user.pw);
@@ -483,39 +487,10 @@ main(argc, argv, envp)
        if (ISSET(sudo_mode, (MODE_VALIDATE|MODE_CHECK|MODE_LIST)))
            exit(rc);
 
-       /*
-        * Set umask based on sudoers.
-        * If user's umask is more restrictive, OR in those bits too
-        * unless umask_override is set.
-        */
-       if (def_umask != 0777) {
-           if (def_umask_override) {
-               umask(def_umask);
-           } else {
-               mode_t mask = umask(def_umask);
-               mask |= def_umask;
-               if (mask != def_umask)
-                   umask(mask);
-           }
-       }
-
-       /* Restore coredumpsize resource limit. */
-#if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
-       (void) setrlimit(RLIMIT_CORE, &corelimit);
-#endif /* RLIMIT_CORE && !SUDO_DEVEL */
-
        /* Must audit before uid change. */
        audit_success(NewArgv);
 
-#ifdef _PATH_SUDO_TRANSCRIPT
-       /* Open tty as needed */
-       if (def_transcript)
-           script_setup();
-#endif
-
        /* Become specified user or root if executing a command. */
-       if (ISSET(sudo_mode, MODE_RUN))
-           set_perms(PERM_FULL_RUNAS);
 
        if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
            char *p;
@@ -526,10 +501,6 @@ main(argc, argv, envp)
            *p = '-';
            NewArgv[0] = p;
 
-           /* Change to target user's homedir. */
-           if (chdir(runas_pw->pw_dir) == -1)
-               warning("unable to change directory to %s", runas_pw->pw_dir);
-
 #if defined(__linux__) || defined(_AIX)
            /* Insert system-wide environment variables. */
            read_env_file(_PATH_ENVIRONMENT, TRUE);
@@ -555,43 +526,31 @@ main(argc, argv, envp)
        sudo_endpwent();
        sudo_endgrent();
 
-       /* Move pty master/slave to low numbered fd and close the rest. */
-#ifdef _PATH_SUDO_TRANSCRIPT
-       fd = def_transcript ? script_duplow(def_closefrom) : def_closefrom;
-       closefrom(fd);
-#else
-       closefrom(def_closefrom);
-#endif
-
 #ifdef PROFILING
        exit(0);
 #endif
-       if (ISSET(sudo_mode, MODE_BACKGROUND) && fork() > 0) {
-           syslog(LOG_AUTH|LOG_ERR, "fork"); /* XXX */
-           exit(0);
+       sudo_execve(safe_cmnd, NewArgv, environ, &cstat);
+       switch (cstat.type) {
+       case CMD_ERRNO:
+           /* exec_setup() or execve() returned an error. */
+           warningx("unable to execute %s: %s", safe_cmnd, strerror(cstat.val));
+           exitcode = 127;
+           break;
+       case CMD_WSTATUS:
+           /* Command ran, exited or was killed. */
+           if (WIFEXITED(cstat.val))
+               exitcode = WEXITSTATUS(cstat.val);
+           else if (WIFSIGNALED(cstat.val))
+               exitcode = WTERMSIG(cstat.val) | 128;
+           break;
+       default:
+           warningx("unexpected child termination condition: %d", cstat.type);
+           break;
        }
-#ifdef _PATH_SUDO_TRANSCRIPT
-       if (def_transcript)
-           script_execv(safe_cmnd, NewArgv);
-       else
+#ifdef _PATH_SUDO_IO_LOGDIR
+       io_log_close();
 #endif
-#ifdef HAVE_SELINUX
-       if (is_selinux_enabled() > 0 && user_role != NULL)
-           selinux_exec(user_role, user_type, NewArgv);
-       else
-#endif
-       execv(safe_cmnd, NewArgv);
-       /*
-        * If we got here then execve() failed...
-        */
-       if (errno == ENOEXEC) {
-           NewArgv--;                  /* at least one extra slot... */
-           NewArgv[0] = "sh";
-           NewArgv[1] = safe_cmnd;
-           execv(_PATH_BSHELL, NewArgv);
-       }
-       warning("unable to execute %s", safe_cmnd);
-       exit(127);
+       exit(exitcode);
     } else if (ISSET(validated, FLAG_NO_USER | FLAG_NO_HOST)) {
        audit_failure(NewArgv, "No user or host");
        log_denial(validated, 1);
@@ -878,6 +837,66 @@ set_cmnd(sudo_mode)
     return(rval);
 }
 
+/*
+ * Setup the execution environment immediately prior to the call to execve()
+ * Returns TRUE on success and FALSE on failure.
+ */
+int
+exec_setup()
+{
+    int rval = FALSE;
+
+    /*
+     * Set umask based on sudoers.
+     * If user's umask is more restrictive, OR in those bits too
+     * unless umask_override is set.
+     */
+    if (def_umask != 0777) {
+       if (def_umask_override) {
+           umask(def_umask);
+       } else {
+           mode_t mask = umask(def_umask);
+           mask |= def_umask;
+           if (mask != def_umask)
+               umask(mask);
+       }
+    }
+
+    /* Restore coredumpsize resource limit. */
+#if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
+    (void) setrlimit(RLIMIT_CORE, &corelimit);
+#endif /* RLIMIT_CORE && !SUDO_DEVEL */
+
+    if (ISSET(sudo_mode, MODE_RUN))
+       set_perms(PERM_FULL_RUNAS);
+
+    if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
+       /* Change to target user's homedir. */
+       if (chdir(runas_pw->pw_dir) == -1)
+           warning("unable to change directory to %s", runas_pw->pw_dir);
+           goto done;
+    }
+
+    if (ISSET(sudo_mode, MODE_BACKGROUND)) {
+       switch (fork()) {
+           case -1:
+               warning("fork");
+               goto done;
+           case 0:
+               /* child continues */
+               break;
+           default:
+               /* parent exists */
+               exit(0);
+       }
+    }
+
+    rval = TRUE;
+
+done:
+    return(rval);
+}
+
 /*
  * Command line argument parsing.
  * Sets NewArgc and NewArgv which corresponds to the argc/argv we'll use
@@ -1481,11 +1500,11 @@ cleanup(gotsignal)
        }
        sudo_endpwent();
        sudo_endgrent();
-    }
-#ifdef _PATH_SUDO_TRANSCRIPT
-    if (def_transcript)
-       term_restore(STDIN_FILENO, 0);
+#ifdef _PATH_SUDO_IO_LOGDIR
+       io_log_close();
 #endif
+    }
+    term_restore(STDIN_FILENO, 0);
 }
 
 static void
diff --git a/sudo.h b/sudo.h
index 86710551392c4af1ed732a3fbbfdda5c3a823ef2..035a4f857b988dc6b4e97c75801a86ed1985d06e 100644 (file)
--- a/sudo.h
+++ b/sudo.h
@@ -74,6 +74,16 @@ struct sudo_user {
 #endif
 };
 
+/* Status passed between parent and child via socketpair */
+struct command_status {
+#define CMD_INVALID 0
+#define CMD_ERRNO 1
+#define CMD_WSTATUS 2
+#define CMD_SIGNO 3
+    int type;
+    int val;
+};
+
 /*
  * Return values for sudoers_lookup(), also used as arguments for log_auth()
  * Note: cannot use '0' as a value here.
@@ -274,20 +284,33 @@ void selinux_prefork __P((char *, char *, int));
 #ifdef HAVE_GETUSERATTR
 void aix_setlimits __P((char *));
 #endif
-int script_duplow __P((int));
-int script_execv __P((char *, char **));
-void script_nextid __P((void));
-void script_setup __P((void));
 int term_cbreak __P((int));
-int term_copy __P((int, int, int));
+int term_copy __P((int, int));
 int term_noecho __P((int));
-int term_raw __P((int, int, int));
+int term_raw __P((int, int));
 int term_restore __P((int, int));
 char *get_timestr __P((time_t, int));
 int get_boottime __P((struct timeval *));
 int user_in_group __P((struct passwd *, const char *));
+int exec_setup __P((void));
 YY_DECL;
 
+/* exec.c */
+int sudo_execve __P((const char *, char *[], char *[], struct command_status *cstat));
+
+/* iolog.c */
+int io_log_open __P((void));
+int log_stderr __P((const char *buf, unsigned int len));
+int log_stdin __P((const char *buf, unsigned int len));
+int log_stdout __P((const char *buf, unsigned int len));
+int log_ttyin __P((const char *buf, unsigned int len));
+int log_ttyout __P((const char *buf, unsigned int len));
+void io_log_close __P((void));
+void io_nextid __P((void));
+
+/* pty.c */
+int get_pty __P((int *master, int *slave, char *name, size_t namesz));
+
 /* Only provide extern declarations outside of sudo.c. */
 #ifndef _SUDO_MAIN
 extern struct sudo_user sudo_user;
@@ -300,5 +323,6 @@ extern uid_t timestamp_uid;
 #ifndef errno
 extern int errno;
 #endif
+extern char **environ; /* XXX */
 
 #endif /* _SUDO_SUDO_H */
index 40b8f30e6f9c4f7df25112d3902f51c4a6893b64..4252e6e7cd68082179a55d91ba45bb69874821ee 100644 (file)
@@ -61,7 +61,7 @@ D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN
 
 
 
-1.7.3b2                   April  7, 2010                        1
+1.7.3b3                   June  3, 2010                         1
 
 
 
@@ -127,7 +127,7 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
 
-1.7.3b2                   April  7, 2010                        2
+1.7.3b3                   June  3, 2010                         2
 
 
 
@@ -193,7 +193,7 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
 
-1.7.3b2                   April  7, 2010                        3
+1.7.3b3                   June  3, 2010                         3
 
 
 
@@ -259,7 +259,7 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
 
-1.7.3b2                   April  7, 2010                        4
+1.7.3b3                   June  3, 2010                         4
 
 
 
@@ -280,7 +280,8 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
         Runas_Spec ::= '(' Runas_List? (':' Runas_List)? ')'
 
         Tag_Spec ::= ('NOPASSWD:' | 'PASSWD:' | 'NOEXEC:' | 'EXEC:' |
-                      'SETENV:' | 'NOSETENV:' | 'TRANSCRIPT:' | 'NOTRANSCRIPT:')
+                      'SETENV:' | 'NOSETENV:' | 'LOG_INPUT:' | 'NOLOG_INPUT:' |
+                      'LOG_OUTPUT:' | 'NOLOG_OUTPUT:')
 
        A u\bus\bse\ber\br s\bsp\bpe\bec\bci\bif\bfi\bic\bca\bat\bti\bio\bon\bn determines which commands a user may run (and as
        what user) on specified hosts.  By default, commands are run as r\bro\boo\bot\bt,
@@ -324,8 +325,7 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
 
-
-1.7.3b2                   April  7, 2010                        5
+1.7.3b3                   June  3, 2010                         5
 
 
 
@@ -350,10 +350,10 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
    T\bTa\bag\bg_\b_S\bSp\bpe\bec\bc
        A command may have zero or more tags associated with it.  There are
        eight possible tag values, NOPASSWD, PASSWD, NOEXEC, EXEC, SETENV,
-       NOSETENV, TRANSCRIPT and NOTRANSCRIPT.  Once a tag is set on a Cmnd,
-       subsequent Cmnds in the Cmnd_Spec_List, inherit the tag unless it is
-       overridden by the opposite tag (i.e.: PASSWD overrides NOPASSWD and
-       NOEXEC overrides EXEC).
+       NOSETENV, LOG_INPUT, NOLOG_INPUT, LOG_OUTPUT and NOLOG_OUTPUT.  Once a
+       tag is set on a Cmnd, subsequent Cmnds in the Cmnd_Spec_List, inherit
+       the tag unless it is overridden by the opposite tag (i.e.: PASSWD
+       overrides NOPASSWD and NOEXEC overrides EXEC).
 
        _\bN_\bO_\bP_\bA_\bS_\bS_\bW_\bD _\ba_\bn_\bd _\bP_\bA_\bS_\bS_\bW_\bD
 
@@ -391,7 +391,7 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
 
-1.7.3b2                   April  7, 2010                        6
+1.7.3b3                   June  3, 2010                         6
 
 
 
@@ -418,10 +418,16 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
        If the command matched is A\bAL\bLL\bL, the SETENV tag is implied for that
        command; this default may be overridden by use of the UNSETENV tag.
 
-       _\bT_\bR_\bA_\bN_\bS_\bC_\bR_\bI_\bP_\bT _\ba_\bn_\bd _\bN_\bO_\bT_\bR_\bA_\bN_\bS_\bC_\bR_\bI_\bP_\bT
+       _\bL_\bO_\bG_\b__\bI_\bN_\bP_\bU_\bT _\ba_\bn_\bd _\bN_\bO_\bL_\bO_\bG_\b__\bI_\bN_\bP_\bU_\bT
 
-       These tags override the value of the _\bt_\br_\ba_\bn_\bs_\bc_\br_\bi_\bp_\bt option on a per-command
-       basis.  For more information, see the description of _\bt_\br_\ba_\bn_\bs_\bc_\br_\bi_\bp_\bt in the
+       These tags override the value of the _\bl_\bo_\bg_\b__\bi_\bn_\bp_\bu_\bt option on a per-command
+       basis.  For more information, see the description of _\bl_\bo_\bg_\b__\bi_\bn_\bp_\bu_\bt in the
+       "SUDOERS OPTIONS" section below.
+
+       _\bL_\bO_\bG_\b__\bO_\bU_\bT_\bP_\bU_\bT _\ba_\bn_\bd _\bN_\bO_\bL_\bO_\bG_\b__\bO_\bU_\bT_\bP_\bU_\bT
+
+       These tags override the value of the _\bl_\bo_\bg_\b__\bo_\bu_\bt_\bp_\bu_\bt option on a per-command
+       basis.  For more information, see the description of _\bl_\bo_\bg_\b__\bo_\bu_\bt_\bp_\bu_\bt in the
        "SUDOERS OPTIONS" section below.
 
    W\bWi\bil\bld\bdc\bca\bar\brd\bds\bs
@@ -449,22 +455,22 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
        Would match any file name beginning with a letter.
 
-       Note that a forward slash ('/') will n\bno\bot\bt be matched by wildcards used
-       in the path name.  When matching the command line arguments, however, a
-       slash d\bdo\boe\bes\bs get matched by wildcards.  This is to make a path like:
 
-           /usr/bin/*
 
+1.7.3b3                   June  3, 2010                         7
 
 
-1.7.3b2                   April  7, 2010                        7
 
 
 
+SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
-SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
+       Note that a forward slash ('/') will n\bno\bot\bt be matched by wildcards used
+       in the path name.  When matching the command line arguments, however, a
+       slash d\bdo\boe\bes\bs get matched by wildcards.  This is to make a path like:
 
+           /usr/bin/*
 
        match _\b/_\bu_\bs_\br_\b/_\bb_\bi_\bn_\b/_\bw_\bh_\bo but not _\b/_\bu_\bs_\br_\b/_\bb_\bi_\bn_\b/_\bX_\b1_\b1_\b/_\bx_\bt_\be_\br_\bm.
 
@@ -514,16 +520,10 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
        sorted lexical order.  That is, _\b/_\be_\bt_\bc_\b/_\bs_\bu_\bd_\bo_\be_\br_\bs_\b._\bd_\b/_\b0_\b1_\b__\bf_\bi_\br_\bs_\bt will be parsed
        before _\b/_\be_\bt_\bc_\b/_\bs_\bu_\bd_\bo_\be_\br_\bs_\b._\bd_\b/_\b1_\b0_\b__\bs_\be_\bc_\bo_\bn_\bd.  Be aware that because the sorting is
        lexical, not numeric, _\b/_\be_\bt_\bc_\b/_\bs_\bu_\bd_\bo_\be_\br_\bs_\b._\bd_\b/_\b1_\b__\bw_\bh_\bo_\bo_\bp_\bs would be loaded a\baf\bft\bte\ber\br
-       _\b/_\be_\bt_\bc_\b/_\bs_\bu_\bd_\bo_\be_\br_\bs_\b._\bd_\b/_\b1_\b0_\b__\bs_\be_\bc_\bo_\bn_\bd.  Using a consistent number of leading zeroes
-       in the file names can be used to avoid such problems.
-
-       Note that unlike files included via #include, v\bvi\bis\bsu\bud\bdo\bo will not edit the
-       files in a #includedir directory unless one of them contains a syntax
-       error.  It is still possible to run v\bvi\bis\bsu\bud\bdo\bo with the -f flag to edit the
 
 
 
-1.7.3b2                   April  7, 2010                        8
+1.7.3b3                   June  3, 2010                         8
 
 
 
@@ -532,6 +532,12 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
+       _\b/_\be_\bt_\bc_\b/_\bs_\bu_\bd_\bo_\be_\br_\bs_\b._\bd_\b/_\b1_\b0_\b__\bs_\be_\bc_\bo_\bn_\bd.  Using a consistent number of leading zeroes
+       in the file names can be used to avoid such problems.
+
+       Note that unlike files included via #include, v\bvi\bis\bsu\bud\bdo\bo will not edit the
+       files in a #includedir directory unless one of them contains a syntax
+       error.  It is still possible to run v\bvi\bis\bsu\bud\bdo\bo with the -f flag to edit the
        files directly.
 
    O\bOt\bth\bhe\ber\br s\bsp\bpe\bec\bci\bia\bal\bl c\bch\bha\bar\bra\bac\bct\bte\ber\brs\bs a\ban\bnd\bd r\bre\bes\bse\ber\brv\bve\bed\bd w\bwo\bor\brd\bds\bs
@@ -580,16 +586,10 @@ S\bSU\bUD\bDO\bOE\bER\bRS\bS O\bOP\bPT\bTI\bIO\bON\bNS\bS
 
        authenticate    If set, users must authenticate themselves via a
                        password (or other means of authentication) before they
-                       may run commands.  This default may be overridden via
-                       the PASSWD and NOPASSWD tags.  This flag is _\bo_\bn by
-                       default.
-
-       closefrom_override
-                       If set, the user may use s\bsu\bud\bdo\bo's -\b-C\bC option which
 
 
 
-1.7.3b2                   April  7, 2010                        9
+1.7.3b3                   June  3, 2010                         9
 
 
 
@@ -598,14 +598,20 @@ S\bSU\bUD\bDO\bOE\bER\bRS\bS O\bOP\bPT\bTI\bIO\bON\bNS\bS
 SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
+                       may run commands.  This default may be overridden via
+                       the PASSWD and NOPASSWD tags.  This flag is _\bo_\bn by
+                       default.
+
+       closefrom_override
+                       If set, the user may use s\bsu\bud\bdo\bo's -\b-C\bC option which
                        overrides the default starting point at which s\bsu\bud\bdo\bo
                        begins closing open file descriptors.  This flag is _\bo_\bf_\bf
                        by default.
 
-       compress_transcript
-                       If set, and the _\bt_\br_\ba_\bn_\bs_\bc_\br_\bi_\bp_\bt flag is also set, s\bsu\bud\bdo\bo will
-                       compress the transcript logs using z\bzl\bli\bib\bb.  This flag is
-                       _\bo_\bn by default when s\bsu\bud\bdo\bo is compiled with z\bzl\bli\bib\bb support.
+       compress_io     If set, and s\bsu\bud\bdo\bo is configured to log a command's input
+                       or output, the I/O logs will be compressed using z\bzl\bli\bib\bb.
+                       This flag is _\bo_\bn by default when s\bsu\bud\bdo\bo is compiled with
+                       z\bzl\bli\bib\bb support.
 
        env_editor      If set, v\bvi\bis\bsu\bud\bdo\bo will use the value of the EDITOR or
                        VISUAL environment variables before falling back on the
@@ -615,7 +621,7 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
                        alternative is to place a colon-separated list of
                        editors in the editor variable.  v\bvi\bis\bsu\bud\bdo\bo will then only
                        use the EDITOR or VISUAL if they match a value
-                       specified in editor.  This flag is _\bo_\bf_\bf by default.
+                       specified in editor.  This flag is _\bo_\bn by default.
 
        env_reset       If set, s\bsu\bud\bdo\bo will reset the environment to only contain
                        the LOGNAME, SHELL, USER, USERNAME and the SUDO_*
@@ -646,16 +652,10 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
                        flag is _\bo_\bf_\bf by default.
 
        fqdn            Set this flag if you want to put fully qualified host
-                       names in the _\bs_\bu_\bd_\bo_\be_\br_\bs file.  I.e., instead of myhost you
-                       would use myhost.mydomain.edu.  You may still use the
-                       short form if you wish (and even mix the two).  Beware
-                       that turning on _\bf_\bq_\bd_\bn requires s\bsu\bud\bdo\bo to make DNS lookups
-                       which may make s\bsu\bud\bdo\bo unusable if DNS stops working (for
-                       example if the machine is not plugged into the
 
 
 
-1.7.3b2                   April  7, 2010                       10
+1.7.3b3                   June  3, 2010                        10
 
 
 
@@ -664,6 +664,12 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
+                       names in the _\bs_\bu_\bd_\bo_\be_\br_\bs file.  I.e., instead of myhost you
+                       would use myhost.mydomain.edu.  You may still use the
+                       short form if you wish (and even mix the two).  Beware
+                       that turning on _\bf_\bq_\bd_\bn requires s\bsu\bud\bdo\bo to make DNS lookups
+                       which may make s\bsu\bud\bdo\bo unusable if DNS stops working (for
+                       example if the machine is not plugged into the
                        network).  Also note that you must use the host's
                        official name as DNS knows it.  That is, you may not
                        use a host alias (CNAME entry) due to performance
@@ -675,7 +681,7 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
        ignore_dot      If set, s\bsu\bud\bdo\bo will ignore '.' or '' (current dir) in the
                        PATH environment variable; the PATH itself is not
-                       modified.  This flag is _\bo_\bf_\bf by default.
+                       modified.  This flag is _\bo_\bn by default.
 
        ignore_local_sudoers
                        If set via LDAP, parsing of _\b/_\be_\bt_\bc_\b/_\bs_\bu_\bd_\bo_\be_\br_\bs will be
@@ -691,7 +697,7 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
                        _\bo_\bf_\bf by default.
 
        insults         If set, s\bsu\bud\bdo\bo will insult users when they enter an
-                       incorrect password.  This flag is _\bo_\bf_\bf by default.
+                       incorrect password.  This flag is _\bo_\bn by default.
 
        log_host        If set, the host name will be logged in the (non-
                        syslog) s\bsu\bud\bdo\bo log file.  This flag is _\bo_\bf_\bf by default.
@@ -713,15 +719,9 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
                        does not enter the correct password.  This flag is _\bo_\bf_\bf
                        by default.
 
-       mail_no_host    If set, mail will be sent to the _\bm_\ba_\bi_\bl_\bt_\bo user if the
-                       invoking user exists in the _\bs_\bu_\bd_\bo_\be_\br_\bs file, but is not
-                       allowed to run commands on the current host.  This flag
-                       is _\bo_\bf_\bf by default.
-
 
 
-
-1.7.3b2                   April  7, 2010                       11
+1.7.3b3                   June  3, 2010                        11
 
 
 
@@ -730,6 +730,11 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
+       mail_no_host    If set, mail will be sent to the _\bm_\ba_\bi_\bl_\bt_\bo user if the
+                       invoking user exists in the _\bs_\bu_\bd_\bo_\be_\br_\bs file, but is not
+                       allowed to run commands on the current host.  This flag
+                       is _\bo_\bf_\bf by default.
+
        mail_no_perms   If set, mail will be sent to the _\bm_\ba_\bi_\bl_\bt_\bo user if the
                        invoking user is allowed to use s\bsu\bud\bdo\bo but the command
                        they are trying is not listed in their _\bs_\bu_\bd_\bo_\be_\br_\bs file
@@ -758,10 +763,10 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
        passprompt_override
                        The password prompt specified by _\bp_\ba_\bs_\bs_\bp_\br_\bo_\bm_\bp_\bt will
-                       normally only be used if the password prompt provided by
-                       systems such as PAM matches the string "Password:".  If
-                       _\bp_\ba_\bs_\bs_\bp_\br_\bo_\bm_\bp_\bt_\b__\bo_\bv_\be_\br_\br_\bi_\bd_\be is set, _\bp_\ba_\bs_\bs_\bp_\br_\bo_\bm_\bp_\bt will always be
-                       used.  This flag is _\bo_\bf_\bf by default.
+                       normally only be used if the password prompt provided
+                       by systems such as PAM matches the string "Password:".
+                       If _\bp_\ba_\bs_\bs_\bp_\br_\bo_\bm_\bp_\bt_\b__\bo_\bv_\be_\br_\br_\bi_\bd_\be is set, _\bp_\ba_\bs_\bs_\bp_\br_\bo_\bm_\bp_\bt will always
+                       be used.  This flag is _\bo_\bf_\bf by default.
 
        preserve_groups By default, s\bsu\bud\bdo\bo will initialize the group vector to
                        the list of groups the target user is in.  When
@@ -780,14 +785,9 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
                        able to determine the length of the password being
                        entered.  This flag is _\bo_\bf_\bf by default.
 
-       requiretty      If set, s\bsu\bud\bdo\bo will only run when the user is logged in
-                       to a real tty.  When this flag is set, s\bsu\bud\bdo\bo can only be
-                       run from a login session and not via other means such
-                       as _\bc_\br_\bo_\bn(1m) or cgi-bin scripts.  This flag is _\bo_\bf_\bf by
-
 
 
-1.7.3b2                   April  7, 2010                       12
+1.7.3b3                   June  3, 2010                        12
 
 
 
@@ -796,6 +796,10 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
+       requiretty      If set, s\bsu\bud\bdo\bo will only run when the user is logged in
+                       to a real tty.  When this flag is set, s\bsu\bud\bdo\bo can only be
+                       run from a login session and not via other means such
+                       as _\bc_\br_\bo_\bn(1m) or cgi-bin scripts.  This flag is _\bo_\bf_\bf by
                        default.
 
        root_sudo       If set, root is allowed to run s\bsu\bud\bdo\bo too.  Disabling
@@ -846,14 +850,10 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
                        shell as root (the shell is determined by the SHELL
                        environment variable if it is set, falling back on the
                        shell listed in the invoking user's /etc/passwd entry
-                       if not).  This flag is _\bo_\bf_\bf by default.
 
-       stay_setuid     Normally, when s\bsu\bud\bdo\bo executes a command the real and
-                       effective UIDs are set to the target user (root by
 
 
-
-1.7.3b2                   April  7, 2010                       13
+1.7.3b3                   June  3, 2010                        13
 
 
 
@@ -862,6 +862,10 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
+                       if not).  This flag is _\bo_\bf_\bf by default.
+
+       stay_setuid     Normally, when s\bsu\bud\bdo\bo executes a command the real and
+                       effective UIDs are set to the target user (root by
                        default).  This option changes that behavior such that
                        the real UID is left as the invoking user's UID.  In
                        other words, this makes s\bsu\bud\bdo\bo act as a setuid wrapper.
@@ -879,25 +883,31 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
                        not listed in the passwd database as an argument to the
                        -\b-u\bu option.  This flag is _\bo_\bf_\bf by default.
 
-       transcript      If set, s\bsu\bud\bdo\bo will log a transcript of the command being
-                       run, similar to the _\bs_\bc_\br_\bi_\bp_\bt(1) command.  In this mode
-                       s\bsu\bud\bdo\bo will allocate a new _\bp_\bs_\be_\bu_\bd_\bo _\bt_\bt_\by and log all input
-                       and output for the command (except when echo is turned
-                       off as when a password is entered).  Transcripts are
-                       logged to the _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bs_\be_\bs_\bs_\bi_\bo_\bn directory with a
-                       unique transcript ID that is included in the normal
-                       s\bsu\bud\bdo\bo log line, prefixed with _\bT_\bS_\bI_\bD_\b=.
-
-                       Transcripts may be viewed with the _\bs_\bu_\bd_\bo_\br_\be_\bp_\bl_\ba_\by(1m)
+       log_input       If set, s\bsu\bud\bdo\bo will run the command in a _\bp_\bs_\be_\bu_\bd_\bo _\bt_\bt_\by and
+                       log all user input.  If the standard input is not
+                       connected to the user's tty, due to I/O redirection or
+                       because the command is part of a pipeline, that input
+                       is also captured and stored in a separate log file.
+
+                       Input is logged to the _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bi_\bo directory using
+                       a unique session ID that is included in the normal s\bsu\bud\bdo\bo
+                       log line, prefixed with _\bT_\bS_\bI_\bD_\b=.
+
+       log_output      If set, s\bsu\bud\bdo\bo will run the command in a _\bp_\bs_\be_\bu_\bd_\bo _\bt_\bt_\by and
+                       log all output that is sent to the screen, similar to
+                       the _\bs_\bc_\br_\bi_\bp_\bt(1) command.  If the standard output or
+                       standard error is not connected to the user's tty, due
+                       to I/O redirection or because the command is part of a
+                       pipeline, that output is also captured and stored in
+                       separate log files.
+
+                       Output is logged to the _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bi_\bo directory
+                       using a unique session ID that is included in the
+                       normal s\bsu\bud\bdo\bo log line, prefixed with _\bT_\bS_\bI_\bD_\b=.
+
+                       Output logs may be viewed with the _\bs_\bu_\bd_\bo_\br_\be_\bp_\bl_\ba_\by(1m)
                        utility, which can also be used to list or search the
-                       available transcripts.
-
-                       A side effect of this mode is that it will not be
-                       possible to suspend the command being run (because it
-                       is running in a different tty with its own job
-                       control).  If a shell is being run, commands executed
-                       by that shell will have normal job control but the
-                       shell itself may not be suspended.
+                       available logs.
 
        tty_tickets     If set, users must authenticate on a per-tty basis.
                        Normally, s\bsu\bud\bdo\bo uses a directory in the ticket dir with
@@ -906,20 +916,10 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
                        user is logged in on in that directory.  This flag is
                        _\bo_\bf_\bf by default.
 
-       umask_override  If set, s\bsu\bud\bdo\bo will set the umask as specified by _\bs_\bu_\bd_\bo_\be_\br_\bs
-                       without modification.  This makes it possible to
-                       specify a more permissive umask in _\bs_\bu_\bd_\bo_\be_\br_\bs than the
-                       user's own umask and matches historical behavior.  If
-                       _\bu_\bm_\ba_\bs_\bk_\b__\bo_\bv_\be_\br_\br_\bi_\bd_\be is not set, s\bsu\bud\bdo\bo will set the umask to
-                       be the union of the user's umask and what is specified
-                       in _\bs_\bu_\bd_\bo_\be_\br_\bs.  This flag is _\bo_\bf_\bf by default.
-
-       use_loginclass  If set, s\bsu\bud\bdo\bo will apply the defaults specified for the
-                       target user's login class if one exists.  Only
 
 
 
-1.7.3b2                   April  7, 2010                       14
+1.7.3b3                   June  3, 2010                        14
 
 
 
@@ -928,6 +928,16 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
+       umask_override  If set, s\bsu\bud\bdo\bo will set the umask as specified by _\bs_\bu_\bd_\bo_\be_\br_\bs
+                       without modification.  This makes it possible to
+                       specify a more permissive umask in _\bs_\bu_\bd_\bo_\be_\br_\bs than the
+                       user's own umask and matches historical behavior.  If
+                       _\bu_\bm_\ba_\bs_\bk_\b__\bo_\bv_\be_\br_\br_\bi_\bd_\be is not set, s\bsu\bud\bdo\bo will set the umask to
+                       be the union of the user's umask and what is specified
+                       in _\bs_\bu_\bd_\bo_\be_\br_\bs.  This flag is _\bo_\bf_\bf by default.
+
+       use_loginclass  If set, s\bsu\bud\bdo\bo will apply the defaults specified for the
+                       target user's login class if one exists.  Only
                        available if s\bsu\bud\bdo\bo is configured with the
                        --with-logincap option.  This flag is _\bo_\bf_\bf by default.
 
@@ -972,20 +982,10 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
                        fractional component if minute granularity is
                        insufficient, for example 2.5.  The default is 5.  Set
                        this to 0 to always prompt for a password.  If set to a
-                       value less than 0 the user's timestamp will never
-                       expire.  This can be used to allow users to create or
-                       delete their own timestamps via sudo -v and sudo -k
-                       respectively.
 
-       umask           Umask to use when running the command.  Negate this
-                       option or set it to 0777 to preserve the user's umask.
-                       The actual umask that is used will be the union of the
-                       user's umask and 0022.  This guarantees that s\bsu\bud\bdo\bo never
-                       lowers the umask when running a command.  Note on
 
 
-
-1.7.3b2                   April  7, 2010                       15
+1.7.3b3                   June  3, 2010                        15
 
 
 
@@ -994,6 +994,16 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
+                       value less than 0 the user's timestamp will never
+                       expire.  This can be used to allow users to create or
+                       delete their own timestamps via sudo -v and sudo -k
+                       respectively.
+
+       umask           Umask to use when running the command.  Negate this
+                       option or set it to 0777 to preserve the user's umask.
+                       The actual umask that is used will be the union of the
+                       user's umask and 0022.  This guarantees that s\bsu\bud\bdo\bo never
+                       lowers the umask when running a command.  Note on
                        systems that use PAM, the default PAM configuration may
                        specify its own umask which will override the value set
                        in _\bs_\bu_\bd_\bo_\be_\br_\bs.
@@ -1038,27 +1048,27 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
                            for (respects the _\br_\bo_\bo_\bt_\bp_\bw, _\bt_\ba_\br_\bg_\be_\bt_\bp_\bw and _\br_\bu_\bn_\ba_\bs_\bp_\bw
                            flags in _\bs_\bu_\bd_\bo_\be_\br_\bs)
 
-                       %U  expanded to the login name of the user the command
-                           will be run as (defaults to root)
 
-                       %u  expanded to the invoking user's login name
 
-                       %%  two consecutive % characters are collapsed into a
-                           single % character
 
-                       The default value is Password:.
+1.7.3b3                   June  3, 2010                        16
 
 
 
 
-1.7.3b2                   April  7, 2010                       16
 
+SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
+                       %U  expanded to the login name of the user the command
+                           will be run as (defaults to root)
 
+                       %u  expanded to the invoking user's login name
 
-SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
+                       %%  two consecutive % characters are collapsed into a
+                           single % character
 
+                       The default value is Password:.
 
        runas_default   The default user to run commands as if the -\b-u\bu option is
                        not specified on the command line.  This defaults to
@@ -1105,26 +1115,26 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
                    Users in this group are exempt from password and PATH
                    requirements.  This is not set by default.
 
-       lecture     This option controls when a short lecture will be printed
-                   along with the password prompt.  It has the following
-                   possible values:
 
-                   always  Always lecture the user.
 
-                   never   Never lecture the user.
+1.7.3b3                   June  3, 2010                        17
 
-                   once    Only lecture the user the first time they run s\bsu\bud\bdo\bo.
 
 
 
-1.7.3b2                   April  7, 2010                       17
 
+SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
+       lecture     This option controls when a short lecture will be printed
+                   along with the password prompt.  It has the following
+                   possible values:
 
+                   always  Always lecture the user.
 
-SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
+                   never   Never lecture the user.
 
+                   once    Only lecture the user the first time they run s\bsu\bud\bdo\bo.
 
                    If no value is specified, a value of _\bo_\bn_\bc_\be is implied.
                    Negating the option results in a value of _\bn_\be_\bv_\be_\br being used.
@@ -1171,19 +1181,9 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
                    quotes (") to protect against s\bsu\bud\bdo\bo interpreting the @ sign.
                    Defaults to the name of the user running s\bsu\bud\bdo\bo.
 
-       mailto      Address to send warning and error mail to.  The address
-                   should be enclosed in double quotes (") to protect against
-                   s\bsu\bud\bdo\bo interpreting the @ sign.  Defaults to root.
-
-       secure_path Path used for every command run from s\bsu\bud\bdo\bo.  If you don't
-                   trust the people running s\bsu\bud\bdo\bo to have a sane PATH
-                   environment variable you may want to use this.  Another use
-                   is if you want to have the "root path" be separate from the
-                   "user path."  Users in the group specified by the
-
 
 
-1.7.3b2                   April  7, 2010                       18
+1.7.3b3                   June  3, 2010                        18
 
 
 
@@ -1192,11 +1192,20 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
+       mailto      Address to send warning and error mail to.  The address
+                   should be enclosed in double quotes (") to protect against
+                   s\bsu\bud\bdo\bo interpreting the @ sign.  Defaults to root.
+
+       secure_path Path used for every command run from s\bsu\bud\bdo\bo.  If you don't
+                   trust the people running s\bsu\bud\bdo\bo to have a sane PATH
+                   environment variable you may want to use this.  Another use
+                   is if you want to have the "root path" be separate from the
+                   "user path."  Users in the group specified by the
                    _\be_\bx_\be_\bm_\bp_\bt_\b__\bg_\br_\bo_\bu_\bp option are not affected by _\bs_\be_\bc_\bu_\br_\be_\b__\bp_\ba_\bt_\bh.  This
                    option is not set by default.
 
        syslog      Syslog facility if syslog is being used for logging (negate
-                   to disable syslog logging).  Defaults to local2.
+                   to disable syslog logging).  Defaults to authpriv.
 
        verifypw    This option controls when a password will be required when
                    a user runs s\bsu\bud\bdo\bo with the -\b-v\bv option.  It has the following
@@ -1237,19 +1246,10 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
                        environment variables to check is displayed when s\bsu\bud\bdo\bo
                        is run by root with the _\b-_\bV option.
 
-       env_delete      Environment variables to be removed from the user's
-                       environment when the _\be_\bn_\bv_\b__\br_\be_\bs_\be_\bt option is not in effect.
-                       The argument may be a double-quoted, space-separated
-                       list or a single value without double-quotes.  The list
-                       can be replaced, added to, deleted from, or disabled by
-                       using the =, +=, -=, and ! operators respectively.  The
-                       default list of environment variables to remove is
-                       displayed when s\bsu\bud\bdo\bo is run by root with the _\b-_\bV option.
-                       Note that many operating systems will remove
 
 
 
-1.7.3b2                   April  7, 2010                       19
+1.7.3b3                   June  3, 2010                        19
 
 
 
@@ -1258,6 +1258,15 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
+       env_delete      Environment variables to be removed from the user's
+                       environment when the _\be_\bn_\bv_\b__\br_\be_\bs_\be_\bt option is not in effect.
+                       The argument may be a double-quoted, space-separated
+                       list or a single value without double-quotes.  The list
+                       can be replaced, added to, deleted from, or disabled by
+                       using the =, +=, -=, and ! operators respectively.  The
+                       default list of environment variables to remove is
+                       displayed when s\bsu\bud\bdo\bo is run by root with the _\b-_\bV option.
+                       Note that many operating systems will remove
                        potentially dangerous variables from the environment of
                        any setuid process (such as s\bsu\bud\bdo\bo).
 
@@ -1286,7 +1295,7 @@ F\bFI\bIL\bLE\bES\bS
 
        _\b/_\be_\bt_\bc_\b/_\bn_\be_\bt_\bg_\br_\bo_\bu_\bp           List of network groups
 
-       _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bs_\be_\bs_\bs_\bi_\bo_\bn   Transcript logs
+       _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bi_\bo        I/O log files
 
 E\bEX\bXA\bAM\bMP\bPL\bLE\bES\bS
        Below are example _\bs_\bu_\bd_\bo_\be_\br_\bs entries.  Admittedly, some of these are a bit
@@ -1303,19 +1312,10 @@ E\bEX\bXA\bAM\bMP\bPL\bLE\bES\bS
         Runas_Alias    ADMINGRP = adm, oper
 
         # Host alias specification
-        Host_Alias     SPARC = bigtime, eclipse, moet, anchor :\
-                       SGI = grolsch, dandelion, black :\
-                       ALPHA = widget, thalamus, foobar :\
-                       HPPA = boa, nag, python
-        Host_Alias     CUNETS = 128.138.0.0/255.255.0.0
-        Host_Alias     CSNETS = 128.138.243.0, 128.138.204.0/24, 128.138.242.0
-        Host_Alias     SERVERS = master, mail, www, ns
-        Host_Alias     CDROM = orion, perseus, hercules
 
 
 
-
-1.7.3b2                   April  7, 2010                       20
+1.7.3b3                   June  3, 2010                        20
 
 
 
@@ -1324,6 +1324,15 @@ E\bEX\bXA\bAM\bMP\bPL\bLE\bES\bS
 SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
+        Host_Alias     SPARC = bigtime, eclipse, moet, anchor :\
+                       SGI = grolsch, dandelion, black :\
+                       ALPHA = widget, thalamus, foobar :\
+                       HPPA = boa, nag, python
+        Host_Alias     CUNETS = 128.138.0.0/255.255.0.0
+        Host_Alias     CSNETS = 128.138.243.0, 128.138.204.0/24, 128.138.242.0
+        Host_Alias     SERVERS = master, mail, www, ns
+        Host_Alias     CDROM = orion, perseus, hercules
+
         # Cmnd alias specification
         Cmnd_Alias     DUMPS = /usr/bin/mt, /usr/sbin/dump, /usr/sbin/rdump,\
                                /usr/sbin/restore, /usr/sbin/rrestore
@@ -1369,26 +1378,27 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
         FULLTIMERS     ALL = NOPASSWD: ALL
 
        Full time sysadmins (m\bmi\bil\bll\ble\ber\brt\bt, m\bmi\bik\bke\bef\bf, and d\bdo\bow\bwd\bdy\by) may run any command on
-       any host without authenticating themselves.
 
-        PARTTIMERS     ALL = ALL
 
-       Part time sysadmins (b\bbo\bos\bst\btl\ble\bey\by, j\bjw\bwf\bfo\box\bx, and c\bcr\bra\baw\bwl\bl) may run any command on
-       any host but they must authenticate themselves first (since the entry
-       lacks the NOPASSWD tag).
 
-        jack           CSNETS = ALL
+1.7.3b3                   June  3, 2010                        21
 
 
 
-1.7.3b2                   April  7, 2010                       21
 
 
+SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
+       any host without authenticating themselves.
 
-SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
+        PARTTIMERS     ALL = ALL
+
+       Part time sysadmins (b\bbo\bos\bst\btl\ble\bey\by, j\bjw\bwf\bfo\box\bx, and c\bcr\bra\baw\bwl\bl) may run any command on
+       any host but they must authenticate themselves first (since the entry
+       lacks the NOPASSWD tag).
 
+        jack           CSNETS = ALL
 
        The user j\bja\bac\bck\bk may run any command on the machines in the _\bC_\bS_\bN_\bE_\bT_\bS alias
        (the networks 128.138.243.0, 128.138.204.0, and 128.138.242.0).  Of
@@ -1434,27 +1444,28 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
        The user j\bji\bim\bm may run any command on machines in the _\bb_\bi_\bg_\bl_\ba_\bb netgroup.
        s\bsu\bud\bdo\bo knows that "biglab" is a netgroup due to the '+' prefix.
 
-        +secretaries   ALL = PRINTING, /usr/bin/adduser, /usr/bin/rmuser
 
-       Users in the s\bse\bec\bcr\bre\bet\bta\bar\bri\bie\bes\bs netgroup need to help manage the printers as
-       well as add and remove users, so they are allowed to run those commands
-       on all machines.
 
-        fred           ALL = (DB) NOPASSWD: ALL
 
-       The user f\bfr\bre\bed\bd can run commands as any user in the _\bD_\bB Runas_Alias
-       (o\bor\bra\bac\bcl\ble\be or s\bsy\byb\bba\bas\bse\be) without giving a password.
+1.7.3b3                   June  3, 2010                        22
 
 
 
-1.7.3b2                   April  7, 2010                       22
 
 
+SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
+        +secretaries   ALL = PRINTING, /usr/bin/adduser, /usr/bin/rmuser
 
-SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
+       Users in the s\bse\bec\bcr\bre\bet\bta\bar\bri\bie\bes\bs netgroup need to help manage the printers as
+       well as add and remove users, so they are allowed to run those commands
+       on all machines.
+
+        fred           ALL = (DB) NOPASSWD: ALL
 
+       The user f\bfr\bre\bed\bd can run commands as any user in the _\bD_\bB Runas_Alias
+       (o\bor\bra\bac\bcl\ble\be or s\bsy\byb\bba\bas\bse\be) without giving a password.
 
         john           ALPHA = /usr/bin/su [!-]*, !/usr/bin/su *root*
 
@@ -1499,28 +1510,28 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 S\bSE\bEC\bCU\bUR\bRI\bIT\bTY\bY N\bNO\bOT\bTE\bES\bS
        It is generally not effective to "subtract" commands from ALL using the
        '!' operator.  A user can trivially circumvent this by copying the
-       desired command to a different name and then executing that.  For
-       example:
 
-           bill        ALL = ALL, !SU, !SHELLS
 
-       Doesn't really prevent b\bbi\bil\bll\bl from running the commands listed in _\bS_\bU or
-       _\bS_\bH_\bE_\bL_\bL_\bS since he can simply copy those commands to a different name, or
-       use a shell escape from an editor or other program.  Therefore, these
-       kind of restrictions should be considered advisory at best (and
-       reinforced by policy).
 
+1.7.3b3                   June  3, 2010                        23
 
 
 
-1.7.3b2                   April  7, 2010                       23
 
 
+SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
+       desired command to a different name and then executing that.  For
+       example:
 
-SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
+           bill        ALL = ALL, !SU, !SHELLS
 
+       Doesn't really prevent b\bbi\bil\bll\bl from running the commands listed in _\bS_\bU or
+       _\bS_\bH_\bE_\bL_\bL_\bS since he can simply copy those commands to a different name, or
+       use a shell escape from an editor or other program.  Therefore, these
+       kind of restrictions should be considered advisory at best (and
+       reinforced by policy).
 
        Furthermore, if the _\bf_\ba_\bs_\bt_\b__\bg_\bl_\bo_\bb option is in use, it is not possible to
        reliably negate commands where the path name includes globbing (aka
@@ -1565,29 +1576,29 @@ P\bPR\bRE\bEV\bVE\bEN\bNT\bTI\bIN\bNG\bG S\bSH\bHE\bEL\bLL\bL E\bES\bSC\bCA\bAP\bPE\bES\bS
                  emulation are not affected.
 
                  To tell whether or not s\bsu\bud\bdo\bo supports _\bn_\bo_\be_\bx_\be_\bc, you can run the
-                 following as root:
 
-                     sudo -V | grep "dummy exec"
 
-                 If the resulting output contains a line that begins with:
 
-                     File containing dummy exec functions:
+1.7.3b3                   June  3, 2010                        24
 
-                 then s\bsu\bud\bdo\bo may be able to replace the exec family of functions
-                 in the standard library with its own that simply return an
-                 error.  Unfortunately, there is no foolproof way to know
 
 
 
-1.7.3b2                   April  7, 2010                       24
 
+SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
+                 following as root:
 
+                     sudo -V | grep "dummy exec"
 
-SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
+                 If the resulting output contains a line that begins with:
 
+                     File containing dummy exec functions:
 
+                 then s\bsu\bud\bdo\bo may be able to replace the exec family of functions
+                 in the standard library with its own that simply return an
+                 error.  Unfortunately, there is no foolproof way to know
                  whether or not _\bn_\bo_\be_\bx_\be_\bc will work at compile-time.  _\bn_\bo_\be_\bx_\be_\bc
                  should work on SunOS, Solaris, *BSD, Linux, IRIX, Tru64 UNIX,
                  MacOS X, and HP-UX 11.x.  It is known n\bno\bot\bt to work on AIX and
@@ -1631,29 +1642,29 @@ C\bCA\bAV\bVE\bEA\bAT\bTS\bS
 
 B\bBU\bUG\bGS\bS
        If you feel you have found a bug in s\bsu\bud\bdo\bo, please submit a bug report at
-       http://www.sudo.ws/sudo/bugs/
 
-S\bSU\bUP\bPP\bPO\bOR\bRT\bT
-       Limited free support is available via the sudo-users mailing list, see
-       http://www.sudo.ws/mailman/listinfo/sudo-users to subscribe or search
-       the archives.
 
-D\bDI\bIS\bSC\bCL\bLA\bAI\bIM\bME\bER\bR
-       s\bsu\bud\bdo\bo is provided ``AS IS'' and any express or implied warranties,
-       including, but not limited to, the implied warranties of
-       merchantability and fitness for a particular purpose are disclaimed.
 
+1.7.3b3                   June  3, 2010                        25
 
 
-1.7.3b2                   April  7, 2010                       25
 
 
 
+SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
-SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
+       http://www.sudo.ws/sudo/bugs/
 
+S\bSU\bUP\bPP\bPO\bOR\bRT\bT
+       Limited free support is available via the sudo-users mailing list, see
+       http://www.sudo.ws/mailman/listinfo/sudo-users to subscribe or search
+       the archives.
 
+D\bDI\bIS\bSC\bCL\bLA\bAI\bIM\bME\bER\bR
+       s\bsu\bud\bdo\bo is provided ``AS IS'' and any express or implied warranties,
+       including, but not limited to, the implied warranties of
+       merchantability and fitness for a particular purpose are disclaimed.
        See the LICENSE file distributed with s\bsu\bud\bdo\bo or
        http://www.sudo.ws/sudo/license.html for complete details.
 
@@ -1700,17 +1711,6 @@ SUDOERS(4)             MAINTENANCE COMMANDS            SUDOERS(4)
 
 
 
-
-
-
-
-
-
-
-
-
-
-
-1.7.3b2                   April  7, 2010                       26
+1.7.3b3                   June  3, 2010                        26
 
 
index 3a6b38eb08240a854e88ddd87fdad3daa0f65453..84bdd5da00478919c537d024d313bf26e7c684c1 100644 (file)
 .\" Agency (DARPA) and Air Force Research Laboratory, Air Force
 .\" Materiel Command, USAF, under agreement number F39502-99-1-0512.
 .\" 
+.nr SL @SEMAN@
+.nr BA @BAMAN@
+.nr LC @LCMAN@
+.\"
 .\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
 .\"
 .IX Title "SUDOERS @mansectform@"
-.TH SUDOERS @mansectform@ "April  7, 2010" "1.7.3b2" "MAINTENANCE COMMANDS"
+.TH SUDOERS @mansectform@ "June  3, 2010" "1.7.3b3" "MAINTENANCE COMMANDS"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -403,12 +407,18 @@ See \*(L"\s-1SUDOERS\s0 \s-1OPTIONS\s0\*(R" for a list of supported Defaults par
 \& Cmnd_Spec_List ::= Cmnd_Spec |
 \&                    Cmnd_Spec \*(Aq,\*(Aq Cmnd_Spec_List
 \&
-\& Cmnd_Spec ::= Runas_Spec? Tag_Spec* Cmnd
+.ie \n(SL \& Cmnd_Spec ::= Runas_Spec? SELinux_Spec? Tag_Spec* Cmnd
+.el \& Cmnd_Spec ::= Runas_Spec? Tag_Spec* Cmnd
 \&
 \& Runas_Spec ::= \*(Aq(\*(Aq Runas_List? (\*(Aq:\*(Aq Runas_List)? \*(Aq)\*(Aq
 \&
+.if \n(SL \{\
+\& SELinux_Spec ::= (\*(AqROLE=role\*(Aq | \*(AqTYPE=type\*(Aq)
+\&
+\}
 \& Tag_Spec ::= (\*(AqNOPASSWD:\*(Aq | \*(AqPASSWD:\*(Aq | \*(AqNOEXEC:\*(Aq | \*(AqEXEC:\*(Aq |
-\&               \*(AqSETENV:\*(Aq | \*(AqNOSETENV:\*(Aq | \*(AqTRANSCRIPT:\*(Aq | \*(AqNOTRANSCRIPT:\*(Aq)
+\&               \*(AqSETENV:\*(Aq | \*(AqNOSETENV:\*(Aq | \*(AqLOG_INPUT:\*(Aq | \*(AqNOLOG_INPUT:\*(Aq |
+\&               \*(AqLOG_OUTPUT:\*(Aq | \*(AqNOLOG_OUTPUT:\*(Aq)
 .Ve
 .PP
 A \fBuser specification\fR determines which commands a user may run
@@ -475,15 +485,24 @@ only the group will be set, the command still runs as user \fBtcm\fR.
 \& tcm    boulder = (:dialer) /usr/bin/tip, /usr/bin/cu, \e
 \&        /usr/local/bin/minicom
 .Ve
+.if \n(SL \{\
+.SS "SELinux_Spec"
+.IX Subsection "SELinux_Spec"
+On systems with SELinux support, \fIsudoers\fR entries may optionally have
+an SELinux role and/or type associated with a command.  If a role or
+type is specified with the command it will override any default values
+specified in \fIsudoers\fR.  A role or type specified on the command line,
+however, will supercede the values in \fIsudoers\fR.
+\}
 .SS "Tag_Spec"
 .IX Subsection "Tag_Spec"
 A command may have zero or more tags associated with it.  There are
 eight possible tag values, \f(CW\*(C`NOPASSWD\*(C'\fR, \f(CW\*(C`PASSWD\*(C'\fR, \f(CW\*(C`NOEXEC\*(C'\fR,
-\&\f(CW\*(C`EXEC\*(C'\fR, \f(CW\*(C`SETENV\*(C'\fR, \f(CW\*(C`NOSETENV\*(C'\fR, \f(CW\*(C`TRANSCRIPT\*(C'\fR and \f(CW\*(C`NOTRANSCRIPT\*(C'\fR.
-Once a tag is set on a \f(CW\*(C`Cmnd\*(C'\fR, subsequent \f(CW\*(C`Cmnd\*(C'\fRs in the
-\&\f(CW\*(C`Cmnd_Spec_List\*(C'\fR, inherit the tag unless it is overridden by the
-opposite tag (i.e.: \f(CW\*(C`PASSWD\*(C'\fR overrides \f(CW\*(C`NOPASSWD\*(C'\fR and \f(CW\*(C`NOEXEC\*(C'\fR
-overrides \f(CW\*(C`EXEC\*(C'\fR).
+\&\f(CW\*(C`EXEC\*(C'\fR, \f(CW\*(C`SETENV\*(C'\fR, \f(CW\*(C`NOSETENV\*(C'\fR, \f(CW\*(C`LOG_INPUT\*(C'\fR, \f(CW\*(C`NOLOG_INPUT\*(C'\fR,
+\&\f(CW\*(C`LOG_OUTPUT\*(C'\fR and \f(CW\*(C`NOLOG_OUTPUT\*(C'\fR.  Once a tag is set on a \f(CW\*(C`Cmnd\*(C'\fR,
+subsequent \f(CW\*(C`Cmnd\*(C'\fRs in the \f(CW\*(C`Cmnd_Spec_List\*(C'\fR, inherit the tag unless
+it is overridden by the opposite tag (i.e.: \f(CW\*(C`PASSWD\*(C'\fR overrides
+\&\f(CW\*(C`NOPASSWD\*(C'\fR and \f(CW\*(C`NOEXEC\*(C'\fR overrides \f(CW\*(C`EXEC\*(C'\fR).
 .PP
 \fI\s-1NOPASSWD\s0 and \s-1PASSWD\s0\fR
 .IX Subsection "NOPASSWD and PASSWD"
@@ -547,12 +566,19 @@ variables in this manner.  If the command matched is \fB\s-1ALL\s0\fR, the
 \&\f(CW\*(C`SETENV\*(C'\fR tag is implied for that command; this default may
 be overridden by use of the \f(CW\*(C`UNSETENV\*(C'\fR tag.
 .PP
-\fI\s-1TRANSCRIPT\s0 and \s-1NOTRANSCRIPT\s0\fR
-.IX Subsection "TRANSCRIPT and NOTRANSCRIPT"
+\fI\s-1LOG_INPUT\s0 and \s-1NOLOG_INPUT\s0\fR
+.IX Subsection "LOG_INPUT and NOLOG_INPUT"
+.PP
+These tags override the value of the \fIlog_input\fR option on a
+per-command basis.  For more information, see the description of
+\&\fIlog_input\fR in the \*(L"\s-1SUDOERS\s0 \s-1OPTIONS\s0\*(R" section below.
+.PP
+\fI\s-1LOG_OUTPUT\s0 and \s-1NOLOG_OUTPUT\s0\fR
+.IX Subsection "LOG_OUTPUT and NOLOG_OUTPUT"
 .PP
-These tags override the value of the \fItranscript\fR option on a
+These tags override the value of the \fIlog_output\fR option on a
 per-command basis.  For more information, see the description of
-\&\fItranscript\fR in the \*(L"\s-1SUDOERS\s0 \s-1OPTIONS\s0\*(R" section below.
+\&\fIlog_output\fR in the \*(L"\s-1SUDOERS\s0 \s-1OPTIONS\s0\*(R" section below.
 .SS "Wildcards"
 .IX Subsection "Wildcards"
 \&\fBsudo\fR allows shell-style \fIwildcards\fR (aka meta or glob characters)
@@ -720,11 +746,11 @@ This flag is \fIon\fR by default.
 If set, the user may use \fBsudo\fR's \fB\-C\fR option which
 overrides the default starting point at which \fBsudo\fR begins
 closing open file descriptors.  This flag is \fIoff\fR by default.
-.IP "compress_transcript" 16
-.IX Item "compress_transcript"
-If set, and the \fItranscript\fR flag is also set, \fBsudo\fR will compress
-the transcript logs using \fBzlib\fR.  This flag is \fIon\fR by default
-when \fBsudo\fR is compiled with \fBzlib\fR support.
+.IP "compress_io" 16
+.IX Item "compress_io"
+If set, and \fBsudo\fR is configured to log a command's input or output,
+the I/O logs will be compressed using \fBzlib\fR.  This flag is \fIon\fR
+by default when \fBsudo\fR is compiled with \fBzlib\fR support.
 .IP "env_editor" 16
 .IX Item "env_editor"
 If set, \fBvisudo\fR will use the value of the \s-1EDITOR\s0 or \s-1VISUAL\s0
@@ -946,24 +972,32 @@ of the invoking user.  In addition, the timestamp file name will
 include the target user's name.  Note that this flag precludes the
 use of a uid not listed in the passwd database as an argument to
 the \fB\-u\fR option.  This flag is \fIoff\fR by default.
-.IP "transcript" 16
-.IX Item "transcript"
-If set, \fBsudo\fR will log a transcript of the command being run,
-similar to the \fIscript\fR\|(1) command.  In this mode \fBsudo\fR will allocate
-a new \fIpseudo tty\fR and log all input and output for the command (except
-when echo is turned off as when a password is entered).  Transcripts
-are logged to the \fI/var/log/sudo\-session\fR directory with a unique
-transcript \s-1ID\s0 that is included in the normal \fBsudo\fR log line,
-prefixed with \fITSID=\fR.
+.IP "log_input" 16
+.IX Item "log_input"
+If set, \fBsudo\fR will run the command in a \fIpseudo tty\fR and log all
+user input.
+If the standard input is not connected to the user's tty, due to
+I/O redirection or because the command is part of a pipeline, that
+input is also captured and stored in a separate log file.
+.Sp
+Input is logged to the \fI/var/log/sudo\-io\fR directory using a unique
+session \s-1ID\s0 that is included in the normal \fBsudo\fR log line, prefixed
+with \fITSID=\fR.
+.IP "log_output" 16
+.IX Item "log_output"
+If set, \fBsudo\fR will run the command in a \fIpseudo tty\fR and log all
+output that is sent to the screen, similar to the \fIscript\fR\|(1) command.
+If the standard output or standard error is not connected to the
+user's tty, due to I/O redirection or because the command is part
+of a pipeline, that output is also captured and stored in separate
+log files.
 .Sp
-Transcripts may be viewed with the \fIsudoreplay\fR\|(@mansectsu@) utility, which
-can also be used to list or search the available transcripts.
+Output is logged to the
+\&\fI/var/log/sudo\-io\fR directory using a unique session \s-1ID\s0 that is
+included in the normal \fBsudo\fR log line, prefixed with \fITSID=\fR.
 .Sp
-A side effect of this mode is that it will not be possible to suspend
-the command being run (because it is running in a different tty
-with its own job control).  If a shell is being run, commands
-executed by that shell will have normal job control but the shell
-itself may not be suspended.
+Output logs may be viewed with the \fIsudoreplay\fR\|(@mansectsu@) utility, which
+can also be used to list or search the available logs.
 .IP "tty_tickets" 16
 .IX Item "tty_tickets"
 If set, users must authenticate on a per-tty basis.  Normally,
@@ -979,11 +1013,13 @@ umask in \fIsudoers\fR than the user's own umask and matches historical
 behavior.  If \fIumask_override\fR is not set, \fBsudo\fR will set the
 umask to be the union of the user's umask and what is specified in
 \&\fIsudoers\fR.  This flag is \fIoff\fR by default.
-@LCMAN@.IP "use_loginclass" 16
-@LCMAN@.IX Item "use_loginclass"
-@LCMAN@If set, \fBsudo\fR will apply the defaults specified for the target user's
-@LCMAN@login class if one exists.  Only available if \fBsudo\fR is configured with
-@LCMAN@the \-\-with\-logincap option.  This flag is \fIoff\fR by default.
+.if \n(LC \{\
+.IP "use_loginclass" 16
+.IX Item "use_loginclass"
+If set, \fBsudo\fR will apply the defaults specified for the target user's
+login class if one exists.  Only available if \fBsudo\fR is configured with
+the \-\-with\-logincap option.  This flag is \fIoff\fR by default.
+\}
 .IP "visiblepw" 16
 .IX Item "visiblepw"
 By default, \fBsudo\fR will refuse to run if the user must enter a
@@ -1100,12 +1136,14 @@ two consecutive \f(CW\*(C`%\*(C'\fR characters are collapsed into a single \f(CW
 .Sp
 The default value is \f(CW\*(C`@passprompt@\*(C'\fR.
 .RE
-@SEMAN@.IP "role" 16
-@SEMAN@.IX Item "role"
-@SEMAN@The default SELinux role to use when constructing a new security
-@SEMAN@context to run the command.  The default role may be overridden on
-@SEMAN@a per-command basis in \fIsudoers\fR or via command line options.
-@SEMAN@This option is only available whe \fBsudo\fR is built with SELinux support.
+.if \n(SL \{\
+.IP "role" 16
+.IX Item "role"
+The default SELinux role to use when constructing a new security
+context to run the command.  The default role may be overridden on
+a per-command basis in \fIsudoers\fR or via command line options.
+This option is only available whe \fBsudo\fR is built with SELinux support.
+\}
 .IP "runas_default" 16
 .IX Item "runas_default"
 The default user to run commands as if the \fB\-u\fR option is not specified
@@ -1133,12 +1171,14 @@ The default is \fI@timedir@\fR.
 .IX Item "timestampowner"
 The owner of the timestamp directory and the timestamps stored therein.
 The default is \f(CW\*(C`root\*(C'\fR.
-@SEMAN@.IP "type" 16
-@SEMAN@.IX Item "type"
-@SEMAN@The default SELinux type to use when constructing a new security
-@SEMAN@context to run the command.  The default type may be overridden on
-@SEMAN@a per-command basis in \fIsudoers\fR or via command line options.
-@SEMAN@This option is only available whe \fBsudo\fR is built with SELinux support.
+.if \n(SL \{\
+.IP "type" 16
+.IX Item "type"
+The default SELinux type to use when constructing a new security
+context to run the command.  The default type may be overridden on
+a per-command basis in \fIsudoers\fR or via command line options.
+This option is only available whe \fBsudo\fR is built with SELinux support.
+\}
 .PP
 \&\fBStrings that can be used in a boolean context\fR:
 .IP "askpass" 12
@@ -1335,9 +1375,9 @@ Local groups file
 .IP "\fI/etc/netgroup\fR" 24
 .IX Item "/etc/netgroup"
 List of network groups
-.IP "\fI/var/log/sudo\-session\fR" 24
-.IX Item "/var/log/sudo-session"
-Transcript logs
+.IP "\fI/var/log/sudo\-io\fR" 24
+.IX Item "/var/log/sudo-io"
+I/O log files
 .SH "EXAMPLES"
 .IX Header "EXAMPLES"
 Below are example \fIsudoers\fR entries.  Admittedly, some of
index 560ac428a37cc98285b176453894ef028f002f50..aadca50dd5a8eff711d48186813ff071f3158339 100644 (file)
@@ -282,7 +282,8 @@ See L<"SUDOERS OPTIONS"> for a list of supported Defaults parameters.
  SELinux_Spec ::= ('ROLE=role' | 'TYPE=type')
 
  Tag_Spec ::= ('NOPASSWD:' | 'PASSWD:' | 'NOEXEC:' | 'EXEC:' |
-              'SETENV:' | 'NOSETENV:' | 'TRANSCRIPT:' | 'NOTRANSCRIPT:')
+              'SETENV:' | 'NOSETENV:' | 'LOG_INPUT:' | 'NOLOG_INPUT:' |
+               'LOG_OUTPUT:' | 'NOLOG_OUTPUT:')
 
 A B<user specification> determines which commands a user may run
 (and as what user) on specified hosts.  By default, commands are
@@ -352,11 +353,11 @@ however, will supercede the values in I<sudoers>.
 
 A command may have zero or more tags associated with it.  There are
 eight possible tag values, C<NOPASSWD>, C<PASSWD>, C<NOEXEC>,
-C<EXEC>, C<SETENV>, C<NOSETENV>, C<TRANSCRIPT> and C<NOTRANSCRIPT>.
-Once a tag is set on a C<Cmnd>, subsequent C<Cmnd>s in the
-C<Cmnd_Spec_List>, inherit the tag unless it is overridden by the
-opposite tag (i.e.: C<PASSWD> overrides C<NOPASSWD> and C<NOEXEC>
-overrides C<EXEC>).
+C<EXEC>, C<SETENV>, C<NOSETENV>, C<LOG_INPUT>, C<NOLOG_INPUT>,
+C<LOG_OUTPUT> and C<NOLOG_OUTPUT>.  Once a tag is set on a C<Cmnd>,
+subsequent C<Cmnd>s in the C<Cmnd_Spec_List>, inherit the tag unless
+it is overridden by the opposite tag (i.e.: C<PASSWD> overrides
+C<NOPASSWD> and C<NOEXEC> overrides C<EXEC>).
 
 =head3 NOPASSWD and PASSWD
 
@@ -411,11 +412,17 @@ variables in this manner.  If the command matched is B<ALL>, the
 C<SETENV> tag is implied for that command; this default may
 be overridden by use of the C<UNSETENV> tag.
 
-=head3 TRANSCRIPT and NOTRANSCRIPT
+=head3 LOG_INPUT and NOLOG_INPUT
 
-These tags override the value of the I<transcript> option on a
+These tags override the value of the I<log_input> option on a
 per-command basis.  For more information, see the description of
-I<transcript> in the L<"SUDOERS OPTIONS"> section below.
+I<log_input> in the L<"SUDOERS OPTIONS"> section below.
+
+=head3 LOG_OUTPUT and NOLOG_OUTPUT
+
+These tags override the value of the I<log_output> option on a
+per-command basis.  For more information, see the description of
+I<log_output> in the L<"SUDOERS OPTIONS"> section below.
 
 =head2 Wildcards
 
@@ -600,11 +607,11 @@ If set, the user may use B<sudo>'s B<-C> option which
 overrides the default starting point at which B<sudo> begins
 closing open file descriptors.  This flag is I<off> by default.
 
-=item compress_transcript
+=item compress_io
 
-If set, and the I<transcript> flag is also set, B<sudo> will compress
-the transcript logs using B<zlib>.  This flag is I<on> by default
-when B<sudo> is compiled with B<zlib> support.
+If set, and B<sudo> is configured to log a command's input or output,
+the I/O logs will be compressed using B<zlib>.  This flag is I<on>
+by default when B<sudo> is compiled with B<zlib> support.
 
 =item env_editor
 
@@ -857,24 +864,33 @@ include the target user's name.  Note that this flag precludes the
 use of a uid not listed in the passwd database as an argument to
 the B<-u> option.  This flag is I<off> by default.
 
-=item transcript
+=item log_input
+
+If set, B<sudo> will run the command in a I<pseudo tty> and log all
+user input.
+If the standard input is not connected to the user's tty, due to
+I/O redirection or because the command is part of a pipeline, that
+input is also captured and stored in a separate log file.
+
+Input is logged to the F</var/log/sudo-io> directory using a unique
+session ID that is included in the normal B<sudo> log line, prefixed
+with I<TSID=>.
+
+=item log_output
 
-If set, B<sudo> will log a transcript of the command being run,
-similar to the script(1) command.  In this mode B<sudo> will allocate
-a new I<pseudo tty> and log all input and output for the command (except
-when echo is turned off as when a password is entered).  Transcripts
-are logged to the F</var/log/sudo-session> directory with a unique
-transcript ID that is included in the normal B<sudo> log line,
-prefixed with I<TSID=>.
+If set, B<sudo> will run the command in a I<pseudo tty> and log all
+output that is sent to the screen, similar to the script(1) command.
+If the standard output or standard error is not connected to the
+user's tty, due to I/O redirection or because the command is part
+of a pipeline, that output is also captured and stored in separate
+log files.
 
-Transcripts may be viewed with the L<sudoreplay(8)> utility, which
-can also be used to list or search the available transcripts.
+Output is logged to the
+F</var/log/sudo-io> directory using a unique session ID that is
+included in the normal B<sudo> log line, prefixed with I<TSID=>.
 
-A side effect of this mode is that it will not be possible to suspend
-the command being run (because it is running in a different tty
-with its own job control).  If a shell is being run, commands
-executed by that shell will have normal job control but the shell
-itself may not be suspended.
+Output logs may be viewed with the L<sudoreplay(8)> utility, which
+can also be used to list or search the available logs.
 
 =item tty_tickets
 
@@ -1324,9 +1340,9 @@ Local groups file
 
 List of network groups
 
-=item F</var/log/sudo-session>
+=item F</var/log/sudo-io>
 
-Transcript logs
+I/O log files
 
 =back
 
index a67024a4945e8bae4ffcb2dad3529ff8089a61d9..855975b0c0013bb60aa89141ad6f9b505f84e56b 100644 (file)
@@ -94,10 +94,10 @@ extern int optind;
 
 int Argc;
 char **Argv;
-const char *session_dir = _PATH_SUDO_TRANSCRIPT;
+const char *session_dir = _PATH_SUDO_IO_LOGDIR;
 
 /*
- * Info present in the transcript log file
+ * Info present in the I/O log file
  */
 struct log_info {
     char *cwd;
@@ -614,7 +614,11 @@ list_session_dir(pathbuf, re, user, tty)
        pathbuf[plen + 0] = '/';
        pathbuf[plen + 1] = dp->d_name[0];
        pathbuf[plen + 2] = dp->d_name[1];
-       pathbuf[plen + 3] = '\0';
+       pathbuf[plen + 3] = '/';
+       pathbuf[plen + 4] = 'l';
+       pathbuf[plen + 5] = 'o';
+       pathbuf[plen + 6] = 'g';
+       pathbuf[plen + 7] = '\0';
        fp = fopen(pathbuf, "r");
        if (fp == NULL) {
            warning("unable to open %s", pathbuf);
@@ -720,6 +724,10 @@ list_sessions(argc, argv, pattern, user, tty)
 #endif /* HAVE_REGCOMP */
 
     sdlen = strlcpy(pathbuf, session_dir, sizeof(pathbuf));
+    if (sdlen + sizeof("/00/00/00/log") >= sizeof(pathbuf)) {
+       errno = ENAMETOOLONG;
+       error(1, "%s/00/00/00/log", session_dir);
+    }
 
     /*
      * Three levels of directory, e.g. 00/00/00 .. ZZ/ZZ/ZZ
index f32100b0688f83cc2d79609060ea8b45bf07f6fa..26f906af6437b14935bcc9f73bde844f3aebc172 100644 (file)
@@ -38,7 +38,7 @@ O\bOP\bPT\bTI\bIO\bON\bNS\bS
 
        -d _\bd_\bi_\br_\be_\bc_\bt_\bo_\br_\by
                    Use _\bd_\bi_\br_\be_\bc_\bt_\bo_\br_\by to for the session logs instead of the
-                   default, _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bs_\be_\bs_\bs_\bi_\bo_\bn_\bs.
+                   default, _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bi_\bo.
 
        -l          Enable "list mode".  In this mode, s\bsu\bud\bdo\bor\bre\bep\bpl\bla\bay\by will list
                    available session IDs.  If a _\bs_\be_\ba_\br_\bc_\bh _\be_\bx_\bp_\br_\be_\bs_\bs_\bi_\bo_\bn is
@@ -61,7 +61,7 @@ O\bOP\bPT\bTI\bIO\bON\bNS\bS
 
 
 
-1.7.3b2                 December 19, 2009                       1
+1.7.3b3                   June  3, 2010                         1
 
 
 
@@ -127,7 +127,7 @@ SUDOREPLAY(1m)         MAINTENANCE COMMANDS        SUDOREPLAY(1m)
 
 
 
-1.7.3b2                 December 19, 2009                       2
+1.7.3b3                   June  3, 2010                         2
 
 
 
@@ -193,7 +193,7 @@ SUDOREPLAY(1m)         MAINTENANCE COMMANDS        SUDOREPLAY(1m)
 
 
 
-1.7.3b2                 December 19, 2009                       3
+1.7.3b3                   June  3, 2010                         3
 
 
 
@@ -214,17 +214,32 @@ SUDOREPLAY(1m)         MAINTENANCE COMMANDS        SUDOREPLAY(1m)
                10:01 am, September 17, 2009.
 
 F\bFI\bIL\bLE\bES\bS
-       _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bs_\be_\bs_\bs_\bi_\bo_\bn   The default session directory.
+       _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bi_\bo        The default I/O log directory.
 
-       _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bs_\be_\bs_\bs_\bi_\bo_\bn_\b/_\b0_\b0_\b/_\b0_\b0_\b/_\b0_\b1
+       _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bi_\bo_\b/_\b0_\b0_\b/_\b0_\b0_\b/_\b0_\b1_\b/_\bl_\bo_\bg
                                Example session log info.
 
-       _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bs_\be_\bs_\bs_\bi_\bo_\bn_\b/_\b0_\b0_\b/_\b0_\b0_\b/_\b0_\b1_\b._\bs_\bc_\br
-                               Example session transcript file.
+       _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bi_\bo_\b/_\b0_\b0_\b/_\b0_\b0_\b/_\b0_\b1_\b/_\bs_\bt_\bd_\bi_\bn
+                               Example session standard input log.
 
-       _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bs_\be_\bs_\bs_\bi_\bo_\bn_\b/_\b0_\b0_\b/_\b0_\b0_\b/_\b0_\b1_\b._\bt_\bi_\bm
+       _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bi_\bo_\b/_\b0_\b0_\b/_\b0_\b0_\b/_\b0_\b1_\b/_\bs_\bt_\bd_\bo_\bu_\bt
+                               Example session standard output log.
+
+       _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bi_\bo_\b/_\b0_\b0_\b/_\b0_\b0_\b/_\b0_\b1_\b/_\bs_\bt_\bd_\be_\br_\br
+                               Example session standard error log.
+
+       _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bi_\bo_\b/_\b0_\b0_\b/_\b0_\b0_\b/_\b0_\b1_\b/_\bt_\bt_\by_\bi_\bn
+                               Example session tty input file.
+
+       _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bi_\bo_\b/_\b0_\b0_\b/_\b0_\b0_\b/_\b0_\b1_\b/_\bt_\bt_\by_\bo_\bu_\bt
+                               Example session tty output file.
+
+       _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bi_\bo_\b/_\b0_\b0_\b/_\b0_\b0_\b/_\b0_\b1_\b/_\bt_\bi_\bm_\bi_\bn_\bg
                                Example session timing file.
 
+       Note that the _\bs_\bt_\bd_\bi_\bn, _\bs_\bt_\bd_\bo_\bu_\bt and _\bs_\bt_\bd_\be_\br_\br files will be empty unless s\bsu\bud\bdo\bo
+       was used as part of a pipeline for a particular command.
+
 E\bEX\bXA\bAM\bMP\bPL\bLE\bES\bS
        List sessions run by user _\bm_\bi_\bl_\bl_\be_\br_\bt:
 
@@ -242,6 +257,17 @@ E\bEX\bXA\bAM\bMP\bPL\bLE\bES\bS
 
         sudoreplay -l ( user jeff or user bob ) tty console
 
+
+
+1.7.3b3                   June  3, 2010                         4
+
+
+
+
+
+SUDOREPLAY(1m)         MAINTENANCE COMMANDS        SUDOREPLAY(1m)
+
+
 S\bSE\bEE\bE A\bAL\bLS\bSO\bO
        _\bs_\bu_\bd_\bo(1m), _\bs_\bc_\br_\bi_\bp_\bt(1)
 
@@ -257,17 +283,6 @@ S\bSU\bUP\bPP\bPO\bOR\bRT\bT
        http://www.sudo.ws/mailman/listinfo/sudo-users to subscribe or search
        the archives.
 
-
-
-1.7.3b2                 December 19, 2009                       4
-
-
-
-
-
-SUDOREPLAY(1m)         MAINTENANCE COMMANDS        SUDOREPLAY(1m)
-
-
 D\bDI\bIS\bSC\bCL\bLA\bAI\bIM\bME\bER\bR
        s\bsu\bud\bdo\bor\bre\bep\bpl\bla\bay\by is provided ``AS IS'' and any express or implied warranties,
        including, but not limited to, the implied warranties of
@@ -310,21 +325,6 @@ D\bDI\bIS\bSC\bCL\bLA\bAI\bIM\bME\bER\bR
 
 
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-1.7.3b2                 December 19, 2009                       5
+1.7.3b3                   June  3, 2010                         5
 
 
index 93a1a82cfa42d8ac8ea9458d2b5865fd8cc0b9e6..11d17bcd539cba572b9d58da1d6f927f8cce4703 100644 (file)
@@ -1,4 +1,4 @@
-.\" Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com>
+.\" Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com>
 .\" 
 .\" Permission to use, copy, modify, and distribute this software for any
 .\" purpose with or without fee is hereby granted, provided that the above
 .\" ========================================================================
 .\"
 .IX Title "SUDOREPLAY @mansectsu@"
-.TH SUDOREPLAY @mansectsu@ "December 19, 2009" "1.7.3b2" "MAINTENANCE COMMANDS"
+.TH SUDOREPLAY @mansectsu@ "June  3, 2010" "1.7.3b3" "MAINTENANCE COMMANDS"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -179,7 +179,7 @@ Double the playback speed.
 .IP "\-d \fIdirectory\fR" 12
 .IX Item "-d directory"
 Use \fIdirectory\fR to for the session logs instead of the default,
-\&\fI/var/log/sudo\-sessions\fR.
+\&\fI/var/log/sudo\-io\fR.
 .IP "\-l" 12
 .IX Item "-l"
 Enable \*(L"list mode\*(R".  In this mode, \fBsudoreplay\fR will list available
@@ -320,18 +320,34 @@ The current time but 14 days ago.
 10:01 am, September 17, 2009.
 .SH "FILES"
 .IX Header "FILES"
-.IP "\fI/var/log/sudo\-session\fR" 24
-.IX Item "/var/log/sudo-session"
-The default session directory.
-.IP "\fI/var/log/sudo\-session/00/00/01\fR" 24
-.IX Item "/var/log/sudo-session/00/00/01"
+.IP "\fI/var/log/sudo\-io\fR" 24
+.IX Item "/var/log/sudo-io"
+The default I/O log directory.
+.IP "\fI/var/log/sudo\-io/00/00/01/log\fR" 24
+.IX Item "/var/log/sudo-io/00/00/01/log"
 Example session log info.
-.IP "\fI/var/log/sudo\-session/00/00/01.scr\fR" 24
-.IX Item "/var/log/sudo-session/00/00/01.scr"
-Example session transcript file.
-.IP "\fI/var/log/sudo\-session/00/00/01.tim\fR" 24
-.IX Item "/var/log/sudo-session/00/00/01.tim"
+.IP "\fI/var/log/sudo\-io/00/00/01/stdin\fR" 24
+.IX Item "/var/log/sudo-io/00/00/01/stdin"
+Example session standard input log.
+.IP "\fI/var/log/sudo\-io/00/00/01/stdout\fR" 24
+.IX Item "/var/log/sudo-io/00/00/01/stdout"
+Example session standard output log.
+.IP "\fI/var/log/sudo\-io/00/00/01/stderr\fR" 24
+.IX Item "/var/log/sudo-io/00/00/01/stderr"
+Example session standard error log.
+.IP "\fI/var/log/sudo\-io/00/00/01/ttyin\fR" 24
+.IX Item "/var/log/sudo-io/00/00/01/ttyin"
+Example session tty input file.
+.IP "\fI/var/log/sudo\-io/00/00/01/ttyout\fR" 24
+.IX Item "/var/log/sudo-io/00/00/01/ttyout"
+Example session tty output file.
+.IP "\fI/var/log/sudo\-io/00/00/01/timing\fR" 24
+.IX Item "/var/log/sudo-io/00/00/01/timing"
 Example session timing file.
+.PP
+Note that the \fIstdin\fR, \fIstdout\fR and \fIstderr\fR files will be empty
+unless \fBsudo\fR was used as part of a pipeline for a particular
+command.
 .SH "EXAMPLES"
 .IX Header "EXAMPLES"
 List sessions run by user \fImillert\fR:
index cf2f29c2c4b934698fb53b5d907e28febe41e107..568de5ce133cd8ad7bcdbc8e493eeb8090529506 100644 (file)
@@ -1,4 +1,4 @@
-Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com>
+Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com>
 
 Permission to use, copy, modify, and distribute this software for any
 purpose with or without fee is hereby granted, provided that the above
@@ -66,7 +66,7 @@ B<sudoreplay> accepts the following command line options:
 =item -d I<directory>
 
 Use I<directory> to for the session logs instead of the default,
-F</var/log/sudo-sessions>.
+F</var/log/sudo-io>.
 
 =item -l
 
@@ -249,24 +249,44 @@ The current time but 14 days ago.
 
 =over 24
 
-=item F</var/log/sudo-session>
+=item F</var/log/sudo-io>
 
-The default session directory.
+The default I/O log directory.
 
-=item F</var/log/sudo-session/00/00/01>
+=item F</var/log/sudo-io/00/00/01/log>
 
 Example session log info.
 
-=item F</var/log/sudo-session/00/00/01.scr>
+=item F</var/log/sudo-io/00/00/01/stdin>
 
-Example session transcript file.
+Example session standard input log.
 
-=item F</var/log/sudo-session/00/00/01.tim>
+=item F</var/log/sudo-io/00/00/01/stdout>
+
+Example session standard output log.
+
+=item F</var/log/sudo-io/00/00/01/stderr>
+
+Example session standard error log.
+
+=item F</var/log/sudo-io/00/00/01/ttyin>
+
+Example session tty input file.
+
+=item F</var/log/sudo-io/00/00/01/ttyout>
+
+Example session tty output file.
+
+=item F</var/log/sudo-io/00/00/01/timing>
 
 Example session timing file.
 
 =back
 
+Note that the I<stdin>, I<stdout> and I<stderr> files will be empty
+unless B<sudo> was used as part of a pipeline for a particular
+command.
+
 =head1 EXAMPLES
 
 List sessions run by user I<millert>:
diff --git a/term.c b/term.c
index 221a28474e9ac1100417b8964d255a84daa24e25..a21a4b6556fe639fdb6a57f2475a2e25e272a92d 100644 (file)
--- a/term.c
+++ b/term.c
@@ -140,9 +140,8 @@ term_noecho(fd)
 #if defined(HAVE_TERMIOS_H) || defined(HAVE_TERMIO_H)
 
 int
-term_raw(fd, opost, isig)
+term_raw(fd, isig)
     int fd;
-    int opost;
     int isig;
 {
     struct termios term;
@@ -157,9 +156,6 @@ term_raw(fd, opost, isig)
     if (isig)
        SET(term.c_lflag, ISIG);
     CLR(term.c_iflag, ICRNL | IGNCR | INLCR | IUCLC | IXON);
-    /* Only retain output post-processing opost flag set. */
-    if (!opost)
-       CLR(term.c_oflag, OPOST);
     if (tcsetattr(fd, TCSADRAIN|TCSASOFT, &term) == 0) {
        changed = 1;
        return(1);
@@ -192,18 +188,14 @@ term_cbreak(fd)
 }
 
 int
-term_copy(src, dst, opost)
+term_copy(src, dst)
     int src;
     int dst;
-    int opost;
 {
     struct termios tt;
 
     if (tcgetattr(src, &tt) != 0)
        return(0);
-    /* Do not do post-processing unless opost set. */
-    if (!opost)
-       CLR(tt.c_oflag, OPOST);
     /* XXX - add TCSANOW compat define */
     if (tcsetattr(dst, TCSANOW|TCSASOFT, &tt) != 0)
        return(0);
@@ -213,19 +205,17 @@ term_copy(src, dst, opost)
 #else /* SGTTY */
 
 int
-term_raw(fd, onlcr)
+term_raw(fd, isig)
     int fd;
-    int onlcr;
+    int isig;
 {
     if (!changed && ioctl(fd, TIOCGETP, &oterm) != 0)
        return(0);
     (void) memcpy(&term, &oterm, sizeof(term));
     /* Set terminal to raw mode */
+    /* XXX - how to support isig? */
     CLR(term.c_lflag, ECHO);
     SET(term.sg_flags, RAW);
-    /* Retain NL to NLCR conversion if onlcr flag set. */
-    if (onlcr)
-       SET(term.sg_flags, CRMOD);
     if (ioctl(fd, TIOCSETP, &term) == 0) {
        changed = 1;
        return(1);
@@ -253,10 +243,9 @@ term_cbreak(fd)
 }
 
 int
-term_copy(src, dst, onlcr)
+term_copy(src, dst)
     int src;
     int dst;
-    int onlcr;
 {
     struct sgttyb b;
     struct tchars tc;
@@ -268,9 +257,6 @@ term_copy(src, dst, onlcr)
        ioctl(src, TIOCLGET, &lb)) {
        return(0);
     }
-    /* Do not convert line endings from NL to NLCR. */
-    if (!onlcr)
-       CLR(b.sg_flags, CRMOD);
     if (ioctl(dst, TIOCSETP, &b) != 0 || ioctl(dst, TIOCSETC, &tc) != 0 ||
        ioctl(dst, TIOCSLTC, &lc) != 0 || ioctl(dst, TIOCLSET, &lb) != 0 ||
        ioctl(dst, TIOCSETD, &l) != 0) {