at compile time does not currently work.
Sudoedit is not yet hooked up to I/O logging.
--HG--
branch : 1.7
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.
#
-# 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
CC = @CC@
FLEX = @FLEX@
YACC = @YACC@
-NROFF = nroff -Tascii
+NROFF = @NROFFPROG@ -Tascii
LIBTOOL = @LIBTOOL@
AR=@AR@
RANLIB=@RANLIB@
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 \
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
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 \
$(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)
$(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)
$(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
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
])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
with_badpass_message
with_fqdn
with_timedir
+with_iologdir
with_sendmail
with_sudoers_mode
with_sudoers_uid
enable_shell_sets_home
enable_path_info
enable_env_debug
-enable_transcript
enable_warnings
with_selinux
enable_gss_krb5_ccache_name
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
--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)
+# 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
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
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"
;;
*-*-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=$?
-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.
-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.
-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
-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
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
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
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"
#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=""
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
;;
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
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
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=""
])
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
"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
#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,
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"
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 */
--- /dev/null
+/*
+ * 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);
+}
-#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
}
parse_error = TRUE;
}
-#line 117 "gram.y"
+#line 113 "gram.y"
#ifndef YYSTYPE_DEFINED
#define YYSTYPE_DEFINED
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
#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[] =
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[] =
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[] =
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[] =
#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[] =
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[] =
"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",
short *yysslim;
YYSTYPE *yyvs;
int yystacksize;
-#line 600 "gram.y"
+#line 607 "gram.y"
static struct defaults *
new_default(var, val, op)
char *var;
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)
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);
}
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
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) ||
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;
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) {
}
}
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) {
}
}
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) {
}
}
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) {
}
}
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;
#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 {
%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 */
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) ||
;
cmndtag : /* empty */ {
- $$.nopasswd = $$.noexec = $$.setenv = $$.transcript = UNSPEC;
+ $$.nopasswd = $$.noexec = $$.setenv =
+ $$.log_input = $$.log_output = UNSPEC;
}
| cmndtag NOPASSWD {
$$.nopasswd = TRUE;
| 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;
}
;
--- /dev/null
+/*
+ * 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);
+}
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);
"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,
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))
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);
* 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;
};
/*
#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
/*
- * 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
+++ /dev/null
-/*
- * 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;
-}
/*
+ * 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;
}
/*
* 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;
}
/*
* 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;
/* 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;
}
*/
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;
}
/*
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
#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
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);
}
/*
- * 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
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/wait.h>
#include <sys/param.h>
#include <sys/socket.h>
#ifdef HAVE_SETRLIMIT
static char *runas_user;
static char *runas_group;
static struct sudo_nss_list *snl;
+static int sudo_mode;
/* For getopt(3) */
extern char *optarg;
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__)
}
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");
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);
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;
*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);
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);
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
}
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
#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.
#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;
#ifndef errno
extern int errno;
#endif
+extern char **environ; /* XXX */
#endif /* _SUDO_SUDO_H */
-1.7.3b2 April 7, 2010 1
+1.7.3b3 June 3, 2010 1
-1.7.3b2 April 7, 2010 2
+1.7.3b3 June 3, 2010 2
-1.7.3b2 April 7, 2010 3
+1.7.3b3 June 3, 2010 3
-1.7.3b2 April 7, 2010 4
+1.7.3b3 June 3, 2010 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,
-
-1.7.3b2 April 7, 2010 5
+1.7.3b3 June 3, 2010 5
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
-1.7.3b2 April 7, 2010 6
+1.7.3b3 June 3, 2010 6
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
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.
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
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
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
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
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_*
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
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
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
_\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.
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
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
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
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
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
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
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.
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
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
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.
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
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.
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
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.
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
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
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
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).
_\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
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
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
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
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*
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
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
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.
-
-
-
-
-
-
-
-
-
-
-
-1.7.3b2 April 7, 2010 26
+1.7.3b3 June 3, 2010 26
.\" 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
\& 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
\& 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"
\&\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)
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
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,
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
.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
.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
.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
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
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
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
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
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
List of network groups
-=item F</var/log/sudo-session>
+=item F</var/log/sudo-io>
-Transcript logs
+I/O log files
=back
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;
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);
#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
-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
-1.7.3b2 December 19, 2009 1
+1.7.3b3 June 3, 2010 1
-1.7.3b2 December 19, 2009 2
+1.7.3b3 June 3, 2010 2
-1.7.3b2 December 19, 2009 3
+1.7.3b3 June 3, 2010 3
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:
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)
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
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-1.7.3b2 December 19, 2009 5
+1.7.3b3 June 3, 2010 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
.\" ========================================================================
.\"
.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
.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
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:
-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
=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
=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>:
#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;
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);
}
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);
#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);
}
int
-term_copy(src, dst, onlcr)
+term_copy(src, dst)
int src;
int dst;
- int onlcr;
{
struct sgttyb b;
struct tchars tc;
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) {