Modular sudo front-end which loads policy and I/O plugins that do

most the actual work.  Currently relies on dynamic loading using
dlopen().  See doc/plugin.pod for the plugin API.
This commit is contained in:
Todd C. Miller
2010-02-20 09:41:49 -05:00
parent 90c06ad7f2
commit b6a4cf7233
18 changed files with 2445 additions and 452 deletions

26
Makefile Normal file
View File

@@ -0,0 +1,26 @@
#
# Copyright (c) 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.
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# XXX - add sudoers, doc, compat?
SUBDIRS = src plugins/sample
all install:
@if [ ! -s config.status ]; then \
echo "Please run configure first"; \
exit 1; \
fi
for d in $(SUBDIRS); do (cd $$d && $(MAKE) $@); done

76
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.61 for sudo 1.7.3b2. # Generated by GNU Autoconf 2.61 for sudo 1.8.0a1.
# #
# Report bugs to <http://www.sudo.ws/bugs/>. # Report bugs to <http://www.sudo.ws/bugs/>.
# #
@@ -724,10 +724,11 @@ SHELL=${CONFIG_SHELL-/bin/sh}
# Identity of this package. # Identity of this package.
PACKAGE_NAME='sudo' PACKAGE_NAME='sudo'
PACKAGE_TARNAME='sudo' PACKAGE_TARNAME='sudo'
PACKAGE_VERSION='1.7.3b2' PACKAGE_VERSION='1.8.0a1'
PACKAGE_STRING='sudo 1.7.3b2' PACKAGE_STRING='sudo 1.8.0a1'
PACKAGE_BUGREPORT='http://www.sudo.ws/bugs/' PACKAGE_BUGREPORT='http://www.sudo.ws/bugs/'
ac_config_libobj_dir=compat
# Factoring default headers for most tests. # Factoring default headers for most tests.
ac_includes_default="\ ac_includes_default="\
#include <stdio.h> #include <stdio.h>
@@ -830,6 +831,7 @@ mansectform
mansrcdir mansrcdir
NOEXECFILE NOEXECFILE
NOEXECDIR NOEXECDIR
PLUGINDIR
noexec_file noexec_file
INSTALL_NOEXEC INSTALL_NOEXEC
DONT_LEAK_PATH_INFO DONT_LEAK_PATH_INFO
@@ -1418,7 +1420,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing. # Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh. # This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF cat <<_ACEOF
\`configure' configures sudo 1.7.3b2 to adapt to many kinds of systems. \`configure' configures sudo 1.8.0a1 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]... Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1483,7 +1485,7 @@ fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in case $ac_init_help in
short | recursive ) echo "Configuration of sudo 1.7.3b2:";; short | recursive ) echo "Configuration of sudo 1.8.0a1:";;
esac esac
cat <<\_ACEOF cat <<\_ACEOF
@@ -1602,6 +1604,7 @@ Optional Packages:
(default=libvas.so) (default=libvas.so)
--with-libvas-rpath=PATH --with-libvas-rpath=PATH
Path to look for libvas in [default=/opt/quest/lib] Path to look for libvas in [default=/opt/quest/lib]
--with-plugin_dir set directory to load plugins from
--with-selinux enable SELinux support --with-selinux enable SELinux support
--with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-gnu-ld assume the C compiler uses GNU ld [default=no]
--with-pic try to use only PIC/non-PIC objects [default=use --with-pic try to use only PIC/non-PIC objects [default=use
@@ -1688,7 +1691,7 @@ fi
test -n "$ac_init_help" && exit $ac_status test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then if $ac_init_version; then
cat <<\_ACEOF cat <<\_ACEOF
sudo configure 1.7.3b2 sudo configure 1.8.0a1
generated by GNU Autoconf 2.61 generated by GNU Autoconf 2.61
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1702,7 +1705,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. running configure, to aid debugging if configure makes a mistake.
It was created by sudo $as_me 1.7.3b2, which was It was created by sudo $as_me 1.8.0a1, which was
generated by GNU Autoconf 2.61. Invocation command line was generated by GNU Autoconf 2.61. Invocation command line was
$ $0 $@ $ $0 $@
@@ -2127,6 +2130,7 @@ echo "$as_me: Configuring Sudo version $PACKAGE_VERSION" >&6;}
timeout=5 timeout=5
@@ -2193,6 +2197,8 @@ test "$sysconfdir" = '${prefix}/etc' -a X"$with_stow" != X"yes" && sysconfdir='/
# Check whether --with-otp-only was given. # Check whether --with-otp-only was given.
if test "${with_otp_only+set}" = set; then if test "${with_otp_only+set}" = set; then
withval=$with_otp_only; case $with_otp_only in withval=$with_otp_only; case $with_otp_only in
@@ -3847,6 +3853,23 @@ fi
fi fi
PLUGINDIR="$libexecdir"
# Check whether --with-plugin_dir was given.
if test "${with_plugin_dir+set}" = set; then
withval=$with_plugin_dir; case $with_plugin_dir in
yes) ;;
no) ;;
*) PLUGINDIR="$with_plugin_dir"
;;
esac
fi
cat >>confdefs.h <<EOF
#define _PATH_SUDO_PLUGIN_DIR "$PLUGINDIR"
EOF
{ echo "$as_me:$LINENO: checking whether to do user authentication by default" >&5 { echo "$as_me:$LINENO: checking whether to do user authentication by default" >&5
echo $ECHO_N "checking whether to do user authentication by default... $ECHO_C" >&6; } echo $ECHO_N "checking whether to do user authentication by default... $ECHO_C" >&6; }
@@ -6471,7 +6494,7 @@ ia64-*-hpux*)
;; ;;
*-*-irix6*) *-*-irix6*)
# Find out which ABI we are using. # Find out which ABI we are using.
echo '#line 6474 "configure"' > conftest.$ac_ext echo '#line 6497 "configure"' > conftest.$ac_ext
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
(eval $ac_compile) 2>&5 (eval $ac_compile) 2>&5
ac_status=$? ac_status=$?
@@ -8335,11 +8358,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:8338: $lt_compile\"" >&5) (eval echo "\"\$as_me:8361: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err) (eval "$lt_compile" 2>conftest.err)
ac_status=$? ac_status=$?
cat conftest.err >&5 cat conftest.err >&5
echo "$as_me:8342: \$? = $ac_status" >&5 echo "$as_me:8365: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output. # So say no if there are warnings other than the usual output.
@@ -8625,11 +8648,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:8628: $lt_compile\"" >&5) (eval echo "\"\$as_me:8651: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err) (eval "$lt_compile" 2>conftest.err)
ac_status=$? ac_status=$?
cat conftest.err >&5 cat conftest.err >&5
echo "$as_me:8632: \$? = $ac_status" >&5 echo "$as_me:8655: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output. # So say no if there are warnings other than the usual output.
@@ -8729,11 +8752,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:8732: $lt_compile\"" >&5) (eval echo "\"\$as_me:8755: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err) (eval "$lt_compile" 2>out/conftest.err)
ac_status=$? ac_status=$?
cat out/conftest.err >&5 cat out/conftest.err >&5
echo "$as_me:8736: \$? = $ac_status" >&5 echo "$as_me:8759: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext if (exit $ac_status) && test -s out/conftest2.$ac_objext
then then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized
@@ -11089,7 +11112,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 11092 "configure" #line 11115 "configure"
#include "confdefs.h" #include "confdefs.h"
#if HAVE_DLFCN_H #if HAVE_DLFCN_H
@@ -11189,7 +11212,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF cat > conftest.$ac_ext <<EOF
#line 11192 "configure" #line 11215 "configure"
#include "confdefs.h" #include "confdefs.h"
#if HAVE_DLFCN_H #if HAVE_DLFCN_H
@@ -26182,7 +26205,7 @@ _ACEOF
exec_prefix="$oexec_prefix" exec_prefix="$oexec_prefix"
fi fi
ac_config_files="$ac_config_files Makefile sudo.man visudo.man sudoers.man sudoers.ldap.man sudoreplay.man sudo_usage.h" ac_config_files="$ac_config_files src/sudo_usage.h src/Makefile plugins/sample/Makefile"
cat >confcache <<\_ACEOF cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure # This file is a shell script that caches the results of configure
@@ -26580,7 +26603,7 @@ exec 6>&1
# report actual input values of CONFIG_FILES etc. instead of their # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" ac_log="
This file was extended by sudo $as_me 1.7.3b2, which was This file was extended by sudo $as_me 1.8.0a1, which was
generated by GNU Autoconf 2.61. Invocation command line was generated by GNU Autoconf 2.61. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
@@ -26629,7 +26652,7 @@ Report bugs to <bug-autoconf@gnu.org>."
_ACEOF _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF cat >>$CONFIG_STATUS <<_ACEOF
ac_cs_version="\\ ac_cs_version="\\
sudo config.status 1.7.3b2 sudo config.status 1.8.0a1
configured by $0, generated by GNU Autoconf 2.61, configured by $0, generated by GNU Autoconf 2.61,
with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
@@ -26738,13 +26761,9 @@ do
case $ac_config_target in case $ac_config_target in
"config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
"pathnames.h") CONFIG_HEADERS="$CONFIG_HEADERS pathnames.h" ;; "pathnames.h") CONFIG_HEADERS="$CONFIG_HEADERS pathnames.h" ;;
"Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "src/sudo_usage.h") CONFIG_FILES="$CONFIG_FILES src/sudo_usage.h" ;;
"sudo.man") CONFIG_FILES="$CONFIG_FILES sudo.man" ;; "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;;
"visudo.man") CONFIG_FILES="$CONFIG_FILES visudo.man" ;; "plugins/sample/Makefile") CONFIG_FILES="$CONFIG_FILES plugins/sample/Makefile" ;;
"sudoers.man") CONFIG_FILES="$CONFIG_FILES sudoers.man" ;;
"sudoers.ldap.man") CONFIG_FILES="$CONFIG_FILES sudoers.ldap.man" ;;
"sudoreplay.man") CONFIG_FILES="$CONFIG_FILES sudoreplay.man" ;;
"sudo_usage.h") CONFIG_FILES="$CONFIG_FILES sudo_usage.h" ;;
*) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
echo "$as_me: error: invalid argument: $ac_config_target" >&2;} echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
@@ -26872,6 +26891,7 @@ mansectform!$mansectform$ac_delim
mansrcdir!$mansrcdir$ac_delim mansrcdir!$mansrcdir$ac_delim
NOEXECFILE!$NOEXECFILE$ac_delim NOEXECFILE!$NOEXECFILE$ac_delim
NOEXECDIR!$NOEXECDIR$ac_delim NOEXECDIR!$NOEXECDIR$ac_delim
PLUGINDIR!$PLUGINDIR$ac_delim
noexec_file!$noexec_file$ac_delim noexec_file!$noexec_file$ac_delim
INSTALL_NOEXEC!$INSTALL_NOEXEC$ac_delim INSTALL_NOEXEC!$INSTALL_NOEXEC$ac_delim
DONT_LEAK_PATH_INFO!$DONT_LEAK_PATH_INFO$ac_delim DONT_LEAK_PATH_INFO!$DONT_LEAK_PATH_INFO$ac_delim
@@ -26902,7 +26922,6 @@ mailsub!$mailsub$ac_delim
badpass_message!$badpass_message$ac_delim badpass_message!$badpass_message$ac_delim
fqdn!$fqdn$ac_delim fqdn!$fqdn$ac_delim
runas_default!$runas_default$ac_delim runas_default!$runas_default$ac_delim
env_editor!$env_editor$ac_delim
_ACEOF _ACEOF
if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then
@@ -26944,6 +26963,7 @@ _ACEOF
ac_delim='%!_!# ' ac_delim='%!_!# '
for ac_last_try in false false false false false :; do for ac_last_try in false false false false false :; do
cat >conf$$subs.sed <<_ACEOF cat >conf$$subs.sed <<_ACEOF
env_editor!$env_editor$ac_delim
passwd_tries!$passwd_tries$ac_delim passwd_tries!$passwd_tries$ac_delim
tty_tickets!$tty_tickets$ac_delim tty_tickets!$tty_tickets$ac_delim
insults!$insults$ac_delim insults!$insults$ac_delim
@@ -26989,7 +27009,7 @@ KRB5CONFIG!$KRB5CONFIG$ac_delim
LTLIBOBJS!$LTLIBOBJS$ac_delim LTLIBOBJS!$LTLIBOBJS$ac_delim
_ACEOF _ACEOF
if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 43; then if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 44; then
break break
elif $ac_last_try; then elif $ac_last_try; then
{ { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5

View File

@@ -3,7 +3,7 @@ dnl Process this file with GNU autoconf to produce a configure script.
dnl dnl
dnl Copyright (c) 1994-1996,1998-2009 Todd C. Miller <Todd.Miller@courtesan.com> dnl Copyright (c) 1994-1996,1998-2009 Todd C. Miller <Todd.Miller@courtesan.com>
dnl dnl
AC_INIT([sudo], [1.7.3b2], [http://www.sudo.ws/bugs/], [sudo]) AC_INIT([sudo], [1.8.0a1], [http://www.sudo.ws/bugs/], [sudo])
AC_CONFIG_HEADER(config.h pathnames.h) AC_CONFIG_HEADER(config.h pathnames.h)
dnl dnl
dnl This won't work before AC_INIT dnl This won't work before AC_INIT
@@ -42,6 +42,7 @@ AC_SUBST(mansectform)
AC_SUBST(mansrcdir) AC_SUBST(mansrcdir)
AC_SUBST(NOEXECFILE) AC_SUBST(NOEXECFILE)
AC_SUBST(NOEXECDIR) AC_SUBST(NOEXECDIR)
AC_SUBST(PLUGINDIR)
AC_SUBST(noexec_file) AC_SUBST(noexec_file)
AC_SUBST(INSTALL_NOEXEC) AC_SUBST(INSTALL_NOEXEC)
AC_SUBST(DONT_LEAK_PATH_INFO) AC_SUBST(DONT_LEAK_PATH_INFO)
@@ -161,6 +162,11 @@ test "$bindir" = '${exec_prefix}/bin' && bindir='$(exec_prefix)/bin'
test "$sbindir" = '${exec_prefix}/sbin' && sbindir='$(exec_prefix)/sbin' test "$sbindir" = '${exec_prefix}/sbin' && sbindir='$(exec_prefix)/sbin'
test "$sysconfdir" = '${prefix}/etc' -a X"$with_stow" != X"yes" && sysconfdir='/etc' test "$sysconfdir" = '${prefix}/etc' -a X"$with_stow" != X"yes" && sysconfdir='/etc'
dnl
dnl libc replacement functions live in compat
dnl
AC_CONFIG_LIBOBJ_DIR(compat)
dnl dnl
dnl Deprecated --with options (these all warn or generate an error) dnl Deprecated --with options (these all warn or generate an error)
dnl dnl
@@ -1061,6 +1067,16 @@ if test X"$with_libvas" != X"no"; then
fi fi
]) ])
PLUGINDIR="$libexecdir"
AC_ARG_WITH(plugin_dir, [AS_HELP_STRING([--with-plugin_dir], [set directory to load plugins from])],
[case $with_plugin_dir in
yes) ;;
no) ;;
*) PLUGINDIR="$with_plugin_dir"
;;
esac])
SUDO_DEFINE_UNQUOTED(_PATH_SUDO_PLUGIN_DIR, "$PLUGINDIR")
dnl dnl
dnl Options for --enable dnl Options for --enable
dnl dnl
@@ -2701,7 +2717,8 @@ fi
dnl dnl
dnl Substitute into the Makefile and man pages dnl Substitute into the Makefile and man pages
dnl dnl
AC_CONFIG_FILES([Makefile sudo.man visudo.man sudoers.man sudoers.ldap.man sudoreplay.man sudo_usage.h]) dnl AC_CONFIG_FILES([doc/sudo.man doc/visudo.man doc/sudoers.man doc/sudoers.ldap.man doc/sudoreplay.man src/Makefile src/sudo_usage.h])
AC_CONFIG_FILES([src/sudo_usage.h src/Makefile plugins/sample/Makefile])
AC_OUTPUT AC_OUTPUT
dnl dnl

View File

@@ -47,6 +47,13 @@
#define _PATH_ENVIRONMENT "/etc/environment" #define _PATH_ENVIRONMENT "/etc/environment"
#endif /* _PATH_ENVIRONMENT */ #endif /* _PATH_ENVIRONMENT */
/*
* NOTE: _PATH_SUDO_CONF is usually overridden by the Makefile.
*/
#ifndef _PATH_SUDO_CONF
#define _PATH_SUDO_CONF "/etc/sudo.conf"
#endif /* _PATH_SUDO_CONF */
/* /*
* NOTE: _PATH_SUDOERS is usually overridden by the Makefile. * NOTE: _PATH_SUDOERS is usually overridden by the Makefile.
*/ */
@@ -94,6 +101,10 @@
#undef _PATH_SUDO_ASKPASS #undef _PATH_SUDO_ASKPASS
#endif /* _PATH_SUDO_ASKPASS */ #endif /* _PATH_SUDO_ASKPASS */
#ifndef _PATH_SUDO_PLUGIN_DIR
#undef _PATH_SUDO_PLUGIN_DIR
#endif /* _PATH_SUDO_PLUGIN_DIR */
#ifndef _PATH_VI #ifndef _PATH_VI
#undef _PATH_VI #undef _PATH_VI
#endif /* _PATH_VI */ #endif /* _PATH_VI */

208
src/Makefile.in Normal file
View File

@@ -0,0 +1,208 @@
#
# Copyright (c) 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.
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# @configure_input@
#
#### Start of system configuration section. ####
srcdir = @srcdir@
devdir = @devdir@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
incdir = $(top_srcdir)/include
# Compiler & tools to use
CC = @CC@
NROFF = nroff -Tascii
# Our install program supports extra flags...
INSTALL = $(SHELL) $(top_srcdir)/install-sh -c
# Libraries
LIBS = @LIBS@ @SUDO_LIBS@ @GETGROUPS_LIB@ @NET_LIBS@
# C preprocessor flags
CPPFLAGS = -I$(incdir) -I$(top_builddir) -I$(srcdir) -I. @CPPFLAGS@
# Usually -O and/or -g
CFLAGS = @CFLAGS@
# Flags to pass to the link stage
LDFLAGS = @LDFLAGS@
# Where to install things...
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
sbindir = @sbindir@
sysconfdir = @sysconfdir@
libexecdir = @libexecdir@
datarootdir = @datarootdir@
noexecfile = @NOEXECFILE@
noexecdir = @NOEXECDIR@
# User and group ids the installed files should be "owned" by
install_uid = 0
install_gid = 0
# Pass in paths and OS dependent defines
DEFS = @OSDEFS@
#### End of system configuration section. ####
SHELL = /bin/sh
PROGS = sudo
# XXX - add back missing ones:
# sudo_edit.c selinux.c
OBJS = sudo.o parse_args.o lbuf.o alloc.o error.o zero_bytes.o \
load_plugins.o conversation.o list.o fmt_string.o tgetpass.o \
fileops.o term.o atobool.o script.o pty.o
LIB_OBJS = @LIBOBJS@
VERSION = @PACKAGE_VERSION@
# XXX - update
SUDODEP = $(srcdir)/sudo.h $(incdir)/alloc.h \
$(incdir)/compat.h $(incdir)/error.h \
$(incdir)/list.h $(incdir)/missing.h \
$(top_builddir)/pathnames.h $(top_builddir)/config.h
all: $(PROGS)
.SUFFIXES: .o .c .h .l .y .man .cat .lo
.c.o:
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $<
.c.lo:
$(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $<
sudo: $(OBJS)
$(CC) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)
sudo_noexec.lo: $(srcdir)/sudo_noexec.c
$(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/sudo_noexec.c
sudo_noexec.la: sudo_noexec.lo
$(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -o $@ sudo_noexec.lo -avoid-version -rpath $(noexecdir)
# Dependencies (not counting auth functions)
aix.o: $(srcdir)/aix.c
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/aix.c
alloc.o: $(srcdir)/alloc.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/alloc.c
audit.o: $(srcdir)/audit.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/audit.c
bsm_audit.o: $(srcdir)/bsm_audit.c $(SUDODEP) bsm_audit.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/bsm_audit.c
closefrom.o: $(srcdir)/closefrom.c $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/closefrom.c
error.o: $(srcdir)/error.c $(incdir)/compat.h $(incdir)/error.h $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/error.c
fileops.o: $(srcdir)/fileops.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/fileops.c
getcwd.o: $(srcdir)/getcwd.c $(incdir)/compat.h $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/getcwd.c
getline.o: $(srcdir)/getline.c $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/getline.c
getprogname.o: $(srcdir)/getprogname.c $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/getprogname.c
isblank.o: $(srcdir)/isblank.c $(incdir)/compat.h $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/isblank.c
lbuf.o: $(srcdir)/lbuf.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/lbuf.c
list.o: $(srcdir)/list.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/list.c
memrchr.o: $(srcdir)/memrchr.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/memrchr.c
mkstemp.o: $(srcdir)/mkstemp.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/mkstemp.c
nanosleep.o: $(srcdir)/nanosleep.c $(incdir)/compat.h $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/nanosleep.c
pty.o: $(srcdir)/pty.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/pty.c
script.o: $(srcdir)/script.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/script.c
sigaction.o: $(srcdir)/sigaction.c $(incdir)/compat.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/sigaction.c
snprintf.o: $(srcdir)/snprintf.c $(incdir)/compat.h $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/snprintf.c
strcasecmp.o: $(srcdir)/strcasecmp.c $(incdir)/compat.h $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/strcasecmp.c
strerror.o: $(srcdir)/strerror.c $(incdir)/compat.h $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/strerror.c
strlcat.o: $(srcdir)/strlcat.c $(incdir)/compat.h $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/strlcat.c
strlcpy.o: $(srcdir)/strlcpy.c $(incdir)/compat.h $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/strlcpy.c
strsignal.o: $(srcdir)/strsignal.c $(incdir)/compat.h $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/strsignal.c
selinux.o: $(srcdir)/selinux.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/selinux.c
sudo.o: $(srcdir)/sudo.c $(SUDODEP) sudo_usage.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/sudo.c
sudo_edit.o: $(srcdir)/sudo_edit.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/sudo_edit.c
sudo_noexec.o: $(srcdir)/sudo_noexec.c $(incdir)/compat.h $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/sudo_noexec.c
term.o: $(srcdir)/term.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/term.c
tgetpass.o: $(srcdir)/tgetpass.c $(SUDODEP)
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/tgetpass.c
utimes.o: $(srcdir)/utimes.c $(incdir)/compat.h $(srcdir)/emul/utime.h $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/utimes.c
zero_bytes.o: $(srcdir)/zero_bytes.c $(incdir)/compat.h $(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/zero_bytes.c
install: install-dirs install-binaries @INSTALL_NOEXEC@
install-dirs:
$(SHELL) $(top_srcdir)/mkinstalldirs $(DESTDIR)$(sudodir) \
$(DESTDIR)$(noexecdir)
install-binaries: install-dirs $(PROGS)
$(INSTALL) -O $(install_uid) -G $(install_gid) -M 4111 -s sudo $(DESTDIR)$(sudodir)/sudo
rm -f $(DESTDIR)$(sudodir)/sudoedit
ln $(DESTDIR)$(sudodir)/sudo $(DESTDIR)$(sudodir)/sudoedit
if [ -f sesh ]; then $(INSTALL) -O $(install_uid) -G $(install_gid) -M 0111 -s sesh $(DESTDIR)$(libexecdir)/sesh; fi
install-noexec: install-dirs sudo_noexec.la
if [ -f .libs/$(noexecfile) ]; then $(INSTALL) -O $(install_uid) -G $(install_gid) -M 0755 .libs/$(noexecfile) $(DESTDIR)$(noexecdir); fi
check:
@echo nothing to check
clean:
-rm -f *.a *.o *.lo stamp-* $(PROGS) core *.core core.*
mostlyclean: clean
# XXX - remove some
distclean: clean
-rm -rf Makefile pathnames.h config.h config.status config.cache \
config.log libtool sudo_noexec.lo .libs $(GENERATED) \
sudo_usage.h
clobber: distclean
realclean: distclean
rm -f TAGS tags
cleandir: realclean

109
src/conversation.c Normal file
View File

@@ -0,0 +1,109 @@
/*
* Copyright (c) 1999-2005, 2007-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.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/param.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 */
#include "sudo.h"
#include "sudo_plugin.h"
extern int tgetpass_flags; /* XXX */
/*
* Sudo conversation function.
*/
int
sudo_conversation(int num_msgs, const struct sudo_conv_message msgs[],
struct sudo_conv_reply replies[])
{
struct sudo_conv_reply *repl;
const struct sudo_conv_message *msg;
char *pass;
int n, flags = tgetpass_flags;
for (n = 0; n < num_msgs; n++) {
msg = &msgs[n];
repl = &replies[n];
switch (msg->msg_type) {
case SUDO_CONV_PROMPT_ECHO_ON:
SET(flags, TGP_ECHO);
case SUDO_CONV_PROMPT_ECHO_OFF:
/* Read the password unless interrupted. */
/* XXX - look up passwd timeout and pass in XXX */
pass = tgetpass(msg->msg, 0, flags);
if (pass == NULL)
goto err;
repl->reply = estrdup(pass);
zero_bytes(pass, strlen(pass));
break;
case SUDO_CONV_INFO_MSG:
if (msg->msg)
(void) puts(msg->msg);
break;
case SUDO_CONV_ERROR_MSG:
if (msg->msg) {
(void) fputs(msg->msg, stderr);
(void) fputc('\n', stderr);
}
break;
default:
goto err;
}
}
return(0);
err:
/* Zero and free allocated memory and return an error. */
do {
repl = &replies[n];
if (repl->reply != NULL) {
zero_bytes(repl->reply, strlen(repl->reply));
free(repl->reply);
repl->reply = NULL;
}
} while (n--);
return(-1);
}

View File

@@ -44,7 +44,7 @@
# include <time.h> # include <time.h>
#endif #endif
#ifndef HAVE_TIMESPEC #ifndef HAVE_TIMESPEC
# include <compat/timespec.h> # include <emul/timespec.h>
#endif #endif
#include "sudo.h" #include "sudo.h"

63
src/fmt_string.c Normal file
View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 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 <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 */
#include "sudo.h"
/*
* Allocate storage for a name=value string and return it.
*/
char *
fmt_string(const char *var, const char *val)
{
size_t var_len = strlen(var);
size_t val_len = strlen(val);
char *cp, *str;
cp = str = emalloc(var_len + 1 + val_len + 1);
memcpy(cp, var, var_len);
cp += var_len;
*cp++ = '=';
memcpy(cp, val, val_len);
cp += val_len;
*cp = '\0';
return(str);
}

176
src/load_plugins.c Normal file
View File

@@ -0,0 +1,176 @@
/*
* 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/stat.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 */
#include <errno.h>
#include <dlfcn.h>
#include "sudo.h"
#include "sudo_plugin.h"
#include "sudo_plugin_int.h"
/*
* Read in /etc/sudo.conf
* Returns a list of plugins.
*/
static struct plugin_info_list *
sudo_read_conf(const char *conf_file)
{
FILE *fp;
char *cp, *name, *path;
struct plugin_info *info;
static struct plugin_info_list pil; /* XXX */
if ((fp = fopen(conf_file, "r")) == NULL) {
/* Default values */
info = emalloc(sizeof(*info));
info->symbol_name = "sudoers";
info->path = "sudoers_policy";
info->prev = info;
info->next = NULL;
tq_append(&pil, info);
/* XXX - io plugin too */
goto done;
}
while ((cp = sudo_parseln(fp)) != NULL) {
/* Skip blank or comment lines */
if (*cp == '\0')
continue;
/* Look for a line starting with "Plugin" */
if (strncasecmp(cp, "Plugin", 6) != 0)
continue;
/* Parse line */
if ((name = strtok(cp + 6, " \t")) == NULL ||
(path = strtok(NULL, " \t")) == NULL) {
continue;
}
info = emalloc(sizeof(*info));
info->symbol_name = estrdup(name);
info->path = estrdup(path);
info->prev = info;
info->next = NULL;
tq_append(&pil, info);
}
fclose(fp);
done:
return(&pil);
}
/*
* Load the plugins listed in conf_file.
*/
void
sudo_load_plugins(const char *conf_file,
struct plugin_container *policy_plugin,
struct plugin_container_list *io_plugins)
{
struct generic_plugin *plugin;
struct plugin_container *container;
struct plugin_info *info;
struct plugin_info_list *plugin_list;
struct stat sb;
void *handle;
char path[PATH_MAX];
/* Parse sudo.conf */
plugin_list = sudo_read_conf(conf_file);
if (tq_empty(plugin_list))
errorx(1, "no plugins defined in %s", conf_file);
tq_foreach_fwd(plugin_list, info) {
if (info->path[0] == '/') {
if (strlcpy(path, info->path, sizeof(path) >= sizeof(path)))
errorx(1, "%s: %s", info->path, strerror(ENAMETOOLONG));
} else {
if (snprintf(path, sizeof(path), "%s/%s", _PATH_SUDO_PLUGIN_DIR,
info->path) >= sizeof(path)) {
errorx(1, "%s/%s: %s", _PATH_SUDO_PLUGIN_DIR, info->path,
strerror(ENAMETOOLONG));
}
}
if (stat(path, &sb) != 0)
error(1, "%s", path);
if (sb.st_uid != ROOT_UID)
errorx(1, "%s must be owned by uid %d", path, ROOT_UID);
if ((sb.st_mode & (S_IWGRP|S_IWOTH)) != 0)
errorx(1, "%s must be only be writable by owner", path);
/* Open plugin and map in symbol */
handle = dlopen(path, RTLD_NOW);
if (!handle)
errorx(1, "unable to dlopen %s: %s", path, dlerror());
plugin = dlsym(handle, info->symbol_name);
if (!plugin)
errorx(1, "unable to find symbol %s in %s", info->symbol_name, path);
if (plugin->type != SUDO_POLICY_PLUGIN && plugin->type != SUDO_IO_PLUGIN) {
errorx(1, "%s: unknown policy type %d", path, plugin->type);
}
if (SUDO_API_VERSION_GET_MAJOR(plugin->version) != SUDO_API_VERSION_MAJOR) {
errorx(1, "%s: incompatible policy major version %d, expected %d",
path, SUDO_API_VERSION_GET_MAJOR(plugin->version),
SUDO_API_VERSION_MAJOR);
}
if (plugin->type == SUDO_POLICY_PLUGIN) {
if (policy_plugin->handle)
errorx(1, "only a single policy plugin may be loaded");
policy_plugin->handle = handle;
policy_plugin->name = info->symbol_name;
policy_plugin->u.generic = plugin;
} else if (plugin->type == SUDO_IO_PLUGIN) {
container = emalloc(sizeof(*container));
container->prev = container;
container->next = NULL;
container->handle = handle;
container->name = info->symbol_name;
container->u.generic = plugin;
tq_append(io_plugins, container);
}
}
if (policy_plugin->handle == NULL)
errorx(1, "%s: at least one policy plugin must be specified", conf_file);
if (policy_plugin->u.policy->check_policy == NULL)
errorx(1, "policy plugin %s does not include a check_policy method");
}

431
src/parse_args.c Normal file
View File

@@ -0,0 +1,431 @@
/*
* Copyright (c) 1993-1996, 1998-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.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
#include <config.h>
/* XXX - prune this */
#include <sys/types.h>
#include <sys/param.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 */
#include <pwd.h>
#include <grp.h>
#include <sudo_usage.h>
#include "sudo.h"
#include "lbuf.h" /* XXX */
/* For getopt(3) */
extern char *optarg;
extern int optind;
/* XXX - better home for these and extern in header file */
int tgetpass_flags;
int user_closefrom = -1;
const char *list_user, *runas_user, *runas_group;
/*
* Local functions.
*/
static void usage(int) __attribute__((__noreturn__));
static void usage_excl(int) __attribute__((__noreturn__));
/*
* Mapping of command line flags to name/value settings.
*/
struct name_value_pair {
const char *name;
const char *value;
};
static struct sudo_settings {
struct name_value_pair a;
struct name_value_pair c;
struct name_value_pair D;
struct name_value_pair E;
struct name_value_pair g;
struct name_value_pair H;
struct name_value_pair i;
struct name_value_pair k;
struct name_value_pair p;
struct name_value_pair r;
struct name_value_pair t;
struct name_value_pair U;
struct name_value_pair u;
} sudo_settings = {
{ "bsdauth_type" },
{ "login_class" },
{ "debug_level" },
{ "preserve_environment" },
{ "runas_group" },
{ "set_home" },
{ "login_shell" },
{ "ignore_ticket" },
{ "prompt" },
{ "selinux_role" },
{ "selinux_type" },
{ "runas_user" }
};
static struct name_value_pair *setting_pairs[] = {
&sudo_settings.a,
&sudo_settings.c,
&sudo_settings.D,
&sudo_settings.E,
&sudo_settings.g,
&sudo_settings.H,
&sudo_settings.i,
&sudo_settings.p,
&sudo_settings.r,
&sudo_settings.t,
&sudo_settings.u
};
#define settings_size (sizeof(setting_pairs) / sizeof(struct name_value_pair))
/*
* Command line argument parsing.
* Sets nargc and nargv which corresponds to the argc/argv we'll use
* for the command to be run (if we are running one).
*/
int
parse_args(int argc, char **argv, int *nargc, char ***nargv, char ***settingsp,
char ***env_addp)
{
int mode = 0; /* what mode is sudo to be run in? */
int flags = 0; /* mode flags */
int valid_flags, ch;
int i, j;
char **settings;
char **env_add;
int nenv = 0;
int env_size = 32;
env_add = emalloc2(env_size, sizeof(char *));
env_add[0] = NULL;
/* First, check to see if we were invoked as "sudoedit". */
if (strcmp(getprogname(), "sudoedit") == 0)
mode = MODE_EDIT;
/* Returns true if the last option string was "--" */
#define got_end_of_args (optind > 1 && argv[optind - 1][0] == '-' && \
argv[optind - 1][1] == '-' && argv[optind - 1][2] == '\0')
/* Returns true if next option is an environment variable */
#define is_envar (optind < argc && argv[optind][0] != '/' && \
strchr(argv[optind], '=') != NULL)
/* Flags allowed when running a command */
valid_flags = MODE_BACKGROUND|MODE_PRESERVE_ENV|MODE_RESET_HOME|
MODE_LOGIN_SHELL|MODE_INVALIDATE|MODE_NONINTERACTIVE|
MODE_PRESERVE_GROUPS|MODE_SHELL;
/* XXX - should fill in settings at the end to avoid dupes */
for (;;) {
/*
* We disable arg permutation for GNU getopt().
* Some trickiness is required to allow environment variables
* to be interspersed with command line options.
*/
if ((ch = getopt(argc, argv, "+Aa:bC:c:D:Eeg:HhiKkLlnPp:r:Sst:U:u:Vv")) != -1) {
switch (ch) {
case 'A':
SET(tgetpass_flags, TGP_ASKPASS);
break;
#ifdef HAVE_BSD_AUTH_H
case 'a':
sudo_settings.a.value = optarg;
break;
#endif
case 'b':
SET(flags, MODE_BACKGROUND);
break;
case 'C':
if ((user_closefrom = atoi(optarg)) < 3) {
warningx("the argument to -C must be at least 3");
usage(1);
}
break;
#ifdef HAVE_LOGIN_CAP_H
case 'c':
sudo_settings.c.value = optarg;
break;
#endif
case 'D':
sudo_settings.D.value = optarg;
break;
case 'E':
sudo_settings.c.value = "true";
break;
case 'e':
if (mode && mode != MODE_EDIT)
usage_excl(1);
mode = MODE_EDIT;
valid_flags = MODE_INVALIDATE|MODE_NONINTERACTIVE;
break;
case 'g':
runas_group = optarg;
sudo_settings.g.value = optarg;
break;
case 'H':
sudo_settings.H.value = optarg;
break;
case 'h':
if (mode && mode != MODE_HELP) {
if (strcmp(getprogname(), "sudoedit") != 0)
usage_excl(1);
}
mode = MODE_HELP;
valid_flags = 0;
break;
case 'i':
sudo_settings.i.value = "true";
SET(flags, MODE_LOGIN_SHELL);
break;
case 'k':
sudo_settings.k.value = "true";
SET(flags, MODE_INVALIDATE);
break;
case 'K':
sudo_settings.k.value = "true";
if (mode && mode != MODE_KILL)
usage_excl(1);
mode = MODE_KILL;
valid_flags = 0;
break;
case 'l':
if (mode) {
if (mode == MODE_LIST)
SET(flags, MODE_LONG_LIST);
else
usage_excl(1);
}
mode = MODE_LIST;
valid_flags = MODE_INVALIDATE|MODE_NONINTERACTIVE|MODE_LONG_LIST;
break;
case 'n':
SET(flags, MODE_NONINTERACTIVE);
break;
case 'P':
SET(flags, MODE_PRESERVE_GROUPS);
break;
case 'p':
sudo_settings.i.value = optarg;
break;
#ifdef HAVE_SELINUX
case 'r':
sudo_settings.r.value = optarg;
break;
case 't':
sudo_settings.t.value = optarg;
break;
#endif
case 'S':
SET(tgetpass_flags, TGP_STDIN);
break;
case 's':
SET(flags, MODE_SHELL);
break;
case 'U':
/* XXX - sudo_getpwnam */
if ((getpwnam(optarg)) == NULL)
errorx(1, "unknown user: %s", optarg);
list_user = optarg;
break;
case 'u':
runas_user = optarg;
sudo_settings.u.value = optarg;
break;
case 'v':
if (mode && mode != MODE_VALIDATE)
usage_excl(1);
mode = MODE_VALIDATE;
valid_flags = MODE_INVALIDATE|MODE_NONINTERACTIVE;
break;
case 'V':
if (mode && mode != MODE_VERSION)
usage_excl(1);
mode = MODE_VERSION;
valid_flags = 0;
break;
default:
usage(1);
}
} else if (!got_end_of_args && is_envar) {
if (nenv == env_size - 2) {
env_size *= 2;
env_add = erealloc3(env_add, env_size, sizeof(char *));
}
env_add[nenv++] = argv[optind];
/* Crank optind and resume getopt. */
optind++;
} else {
/* Not an option or an environment variable -- we're done. */
break;
}
}
*nargc = argc - optind;
*nargv = argv + optind;
if (!mode) {
/* Defer -k mode setting until we know whether it is a flag or not */
if (ISSET(flags, MODE_INVALIDATE) && *nargc == 0) {
mode = MODE_INVALIDATE; /* -k by itself */
CLR(flags, MODE_INVALIDATE);
sudo_settings.k.value = NULL;
valid_flags = 0;
} else {
mode = MODE_RUN; /* running a command */
}
}
if (*nargc > 0 && mode == MODE_LIST)
mode = MODE_CHECK;
if (ISSET(flags, MODE_LOGIN_SHELL)) {
if (ISSET(flags, MODE_SHELL)) {
warningx("you may not specify both the `-i' and `-s' options");
usage(1);
}
if (ISSET(flags, MODE_PRESERVE_ENV)) {
warningx("you may not specify both the `-i' and `-E' options");
usage(1);
}
SET(flags, MODE_SHELL);
}
if ((flags & valid_flags) != flags)
usage(1);
if (mode == MODE_EDIT &&
(ISSET(flags, MODE_PRESERVE_ENV) || env_add[0] != NULL)) {
if (ISSET(mode, MODE_PRESERVE_ENV))
warningx("the `-E' option is not valid in edit mode");
if (env_add[0] != NULL)
warningx("you may not specify environment variables in edit mode");
usage(1);
}
if ((runas_user != NULL || runas_group != NULL) &&
!ISSET(mode, MODE_EDIT | MODE_RUN | MODE_CHECK | MODE_VALIDATE)) {
usage(1);
}
if (list_user != NULL && mode != MODE_LIST && mode != MODE_CHECK) {
warningx("the `-U' option may only be used with the `-l' option");
usage(1);
}
if (ISSET(tgetpass_flags, TGP_STDIN) && ISSET(tgetpass_flags, TGP_ASKPASS)) {
warningx("the `-A' and `-S' options may not be used together");
usage(1);
}
if ((*nargc == 0 && mode == MODE_EDIT) ||
(*nargc > 0 && !ISSET(mode, MODE_RUN | MODE_EDIT | MODE_CHECK)))
usage(1);
if (*nargc == 0 && mode == MODE_RUN && !ISSET(flags, MODE_SHELL))
SET(flags, (MODE_IMPLIED_SHELL | MODE_SHELL));
if (mode == MODE_HELP)
usage(0);
/*
* Format setting_pairs into settings array.
*/
settings = emalloc2(settings_size + 1, sizeof (char *));
for (i = 0, j = 0; i < settings_size; i++) {
if (setting_pairs[i]->value) {
settings[j++] = fmt_string(setting_pairs[i]->name,
setting_pairs[i]->value);
}
}
settings[j] = NULL;
*settingsp = settings;
*env_addp = env_add;
return(mode | flags);
}
/*
* Give usage message and exit.
* The actual usage strings are in sudo_usage.h for configure substitution.
* XXX - avoid lbuf usage
*/
static void
usage(int exit_val)
{
struct lbuf lbuf;
char *uvec[6];
int i, ulen;
/*
* Use usage vectors appropriate to the progname.
*/
if (strcmp(getprogname(), "sudoedit") == 0) {
uvec[0] = SUDO_USAGE5 + 3;
uvec[1] = NULL;
} else {
uvec[0] = SUDO_USAGE1;
uvec[1] = SUDO_USAGE2;
uvec[2] = SUDO_USAGE3;
uvec[3] = SUDO_USAGE4;
uvec[4] = SUDO_USAGE5;
uvec[5] = NULL;
}
/*
* Print usage and wrap lines as needed, depending on the
* tty width.
*/
ulen = (int)strlen(getprogname()) + 8;
lbuf_init(&lbuf, NULL, ulen, 0);
for (i = 0; uvec[i] != NULL; i++) {
lbuf_append(&lbuf, "usage: ", getprogname(), uvec[i], NULL);
lbuf_print(&lbuf);
}
lbuf_destroy(&lbuf);
exit(exit_val);
}
/*
* Tell which options are mutually exclusive and exit.
*/
static void
usage_excl(int exit_val)
{
warningx("Only one of the -e, -h, -i, -K, -l, -s, -v or -V options may be specified");
usage(exit_val);
}

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com> * Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@@ -58,31 +58,23 @@
#if defined(HAVE_OPENPTY) #if defined(HAVE_OPENPTY)
int int
get_pty(master, slave, name, namesz) get_pty(int *master, int *slave, char *name, size_t namesz, uid_t uid)
int *master;
int *slave;
char *name;
size_t namesz;
{ {
struct group *gr; struct group *gr;
gid_t ttygid = -1; gid_t ttygid = -1;
if ((gr = sudo_getgrnam("tty")) != NULL) if ((gr = getgrnam("tty")) != NULL)
ttygid = gr->gr_gid; ttygid = gr->gr_gid;
if (openpty(master, slave, name, NULL, NULL) != 0) if (openpty(master, slave, name, NULL, NULL) != 0)
return(0); return(0);
(void) chown(name, runas_pw->pw_uid, ttygid); (void) chown(name, uid, ttygid);
return(1); return(1);
} }
#elif defined(HAVE__GETPTY) #elif defined(HAVE__GETPTY)
int int
get_pty(master, slave, name, namesz) get_pty(int *master, int *slave, char *name, size_t namesz, uid_t uid)
int *master;
int *slave;
char *name;
size_t namesz;
{ {
char *line; char *line;
@@ -95,7 +87,7 @@ get_pty(master, slave, name, namesz)
close(*master); close(*master);
return(0); return(0);
} }
(void) chown(line, runas_pw->pw_uid, -1); (void) chown(line, uid, -1);
strlcpy(name, line, namesz); strlcpy(name, line, namesz);
return(1); return(1);
} }
@@ -117,11 +109,7 @@ posix_openpt(oflag)
# endif /* HAVE_POSIX_OPENPT */ # endif /* HAVE_POSIX_OPENPT */
int int
get_pty(master, slave, name, namesz) get_pty(int *master, int *slave, char *name, size_t namesz, uid_t uid)
int *master;
int *slave;
char *name;
size_t namesz;
{ {
char *line; char *line;
@@ -148,7 +136,7 @@ get_pty(master, slave, name, namesz)
ioctl(*slave, I_PUSH, "ptem"); /* pseudo tty emulation module */ ioctl(*slave, I_PUSH, "ptem"); /* pseudo tty emulation module */
ioctl(*slave, I_PUSH, "ldterm"); /* line discipline module */ ioctl(*slave, I_PUSH, "ldterm"); /* line discipline module */
# endif # endif
(void) chown(line, runas_pw->pw_uid, -1); (void) chown(line, uid, -1);
strlcpy(name, line, namesz); strlcpy(name, line, namesz);
return(1); return(1);
} }
@@ -158,17 +146,13 @@ get_pty(master, slave, name, namesz)
static char line[] = "/dev/ptyXX"; static char line[] = "/dev/ptyXX";
int int
get_pty(master, slave, name, namesz) get_pty(int *master, int *slave, char *name, size_t namesz, uid_t uid)
int *master;
int *slave;
char *name;
size_t namesz;
{ {
char *bank, *cp; char *bank, *cp;
struct group *gr; struct group *gr;
gid_t ttygid = -1; gid_t ttygid = -1;
if ((gr = sudo_getgrnam("tty")) != NULL) if ((gr = getgrnam("tty")) != NULL)
ttygid = gr->gr_gid; ttygid = gr->gr_gid;
for (bank = "pqrs"; *bank != '\0'; bank++) { for (bank = "pqrs"; *bank != '\0'; bank++) {
@@ -182,7 +166,7 @@ get_pty(master, slave, name, namesz)
continue; /* already in use */ continue; /* already in use */
} }
line[sizeof("/dev/p") - 2] = 't'; line[sizeof("/dev/p") - 2] = 't';
(void) chown(line, runas_pw->pw_uid, ttygid); (void) chown(line, uid, ttygid);
(void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP); (void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP);
# ifdef HAVE_REVOKE # ifdef HAVE_REVOKE
(void) revoke(line); (void) revoke(line);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com> * Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@@ -68,39 +68,29 @@
#ifdef HAVE_SELINUX #ifdef HAVE_SELINUX
# include <selinux/selinux.h> # include <selinux/selinux.h>
#endif #endif
#ifdef HAVE_ZLIB
# include <zlib.h>
#endif
#include "sudo.h" #include "sudo.h" /* XXX? */
#include "sudo_plugin.h"
#include "sudo_plugin_int.h"
#define SFD_MASTER 0 #define SFD_MASTER 0
#define SFD_SLAVE 1 #define SFD_SLAVE 1
#define SFD_LOG 2 #define SFD_USERTTY 2
#define SFD_OUTPUT 3
#define SFD_TIMING 4
#define SFD_USERTTY 5
#define TERM_COOKED 0 #define TERM_COOKED 0
#define TERM_CBREAK 1 #define TERM_CBREAK 1
#define TERM_RAW 2 #define TERM_RAW 2
union script_fd {
FILE *f;
#ifdef HAVE_ZLIB
gzFile g;
#endif
};
struct script_buf { struct script_buf {
int len; /* buffer length (how much read in) */ int len; /* buffer length (how much read in) */
int off; /* write position (how much already consumed) */ int off; /* write position (how much already consumed) */
char buf[16 * 1024]; char buf[16 * 1024];
}; };
static int script_fds[6]; static int script_fds[3];
static int ttyout; static int ttyout;
/* XXX - use an array of signals instead of just a single variable */
static sig_atomic_t alive = 1; static sig_atomic_t alive = 1;
static sig_atomic_t recvsig = 0; static sig_atomic_t recvsig = 0;
static sig_atomic_t ttymode = TERM_COOKED; static sig_atomic_t ttymode = TERM_COOKED;
@@ -114,247 +104,68 @@ static int foreground;
static char slavename[PATH_MAX]; static char slavename[PATH_MAX];
static int suspend_parent __P((int signo, struct script_buf *output, static int suspend_parent(int signo, struct script_buf *output);
struct timeval *then, struct timeval *now, union script_fd ofile, static void flush_output(struct script_buf *output);
union script_fd tfile)); static void handler(int s);
static void flush_output __P((struct script_buf *output, struct timeval *then, static int script_child(const char *path, char *argv[], char *envp[], int, int);
struct timeval *now, union script_fd ofile, union script_fd tfile)); static void script_run(const char *path, char *argv[], char *envp[], int);
static void handler __P((int s)); static void sigchild(int s);
static void script_child __P((char *path, char *argv[], int, int)); static void sigwinch(int s);
static void script_run __P((char *path, char *argv[], int)); static void sync_winsize(int src, int dst);
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)); /* sudo.c */
extern struct plugin_container_list io_plugins;
/*
* 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 void
script_nextid() script_setup(uid_t uid)
{ {
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 >= 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(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); script_fds[SFD_USERTTY] = open(_PATH_TTY, O_RDWR|O_NOCTTY, 0);
if (script_fds[SFD_USERTTY] == -1) if (script_fds[SFD_USERTTY] == -1)
log_error(0, "tty required for transcript support"); /* XXX */ errorx(1, "tty required for transcript support");
if (!get_pty(&script_fds[SFD_MASTER], &script_fds[SFD_SLAVE], if (!get_pty(&script_fds[SFD_MASTER], &script_fds[SFD_SLAVE],
slavename, sizeof(slavename))) slavename, sizeof(slavename), uid))
log_error(USE_ERRNO, "Can't get pty"); error(1, "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 /* Call I/O plugin input method. */
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 static void
log_output(buf, n, then, now, ofile, tfile) log_input(char *buf, unsigned int n)
char *buf;
int n;
struct timeval *then;
struct timeval *now;
union script_fd ofile;
union script_fd tfile;
{ {
struct timeval tv; struct plugin_container *plugin;
sigset_t omask; sigset_t omask;
sigprocmask(SIG_BLOCK, &ttyblock, &omask); sigprocmask(SIG_BLOCK, &ttyblock, &omask);
#ifdef HAVE_ZLIB tq_foreach_fwd(&io_plugins, plugin) {
if (def_compress_transcript) /* XXX - die if return != TRUE */
gzwrite(ofile.g, buf, n); if (plugin->u.io->log_input)
else plugin->u.io->log_input(buf, n);
#endif }
fwrite(buf, 1, n, ofile.f);
timersub(now, then, &tv); sigprocmask(SIG_SETMASK, &omask, NULL);
#ifdef HAVE_ZLIB }
if (def_compress_transcript)
gzprintf(tfile.g, "%f %d\n", /* Call I/O plugin output method. */
tv.tv_sec + ((double)tv.tv_usec / 1000000), n); static void
else log_output(char *buf, unsigned int n)
#endif {
fprintf(tfile.f, "%f %d\n", struct plugin_container *plugin;
tv.tv_sec + ((double)tv.tv_usec / 1000000), n); sigset_t omask;
then->tv_sec = now->tv_sec;
then->tv_usec = now->tv_usec; sigprocmask(SIG_BLOCK, &ttyblock, &omask);
tq_foreach_fwd(&io_plugins, plugin) {
/* XXX - die if return != TRUE */
if (plugin->u.io->log_output)
plugin->u.io->log_output(buf, n);
}
sigprocmask(SIG_SETMASK, &omask, NULL); sigprocmask(SIG_SETMASK, &omask, NULL);
} }
static void static void
check_foreground() check_foreground(void)
{ {
foreground = tcgetpgrp(script_fds[SFD_USERTTY]) == parent; foreground = tcgetpgrp(script_fds[SFD_USERTTY]) == parent;
if (foreground && !tty_initialized) { if (foreground && !tty_initialized) {
@@ -370,13 +181,7 @@ check_foreground()
* Returns SIGUSR1 if the child should be resume in foreground else SIGUSR2. * Returns SIGUSR1 if the child should be resume in foreground else SIGUSR2.
*/ */
static int static int
suspend_parent(signo, output, then, now, ofile, tfile) suspend_parent(int signo, struct script_buf *output)
int signo;
struct script_buf *output;
struct timeval *then;
struct timeval *now;
union script_fd ofile;
union script_fd tfile;
{ {
sigaction_t sa, osa; sigaction_t sa, osa;
int n, oldmode = ttymode, rval = 0; int n, oldmode = ttymode, rval = 0;
@@ -404,7 +209,7 @@ suspend_parent(signo, output, then, now, ofile, tfile)
/* FALLTHROUGH */ /* FALLTHROUGH */
case SIGTSTP: case SIGTSTP:
/* Flush any remaining output to master tty. */ /* Flush any remaining output to master tty. */
flush_output(output, then, now, ofile, tfile); flush_output(output);
/* Restore original tty mode before suspending. */ /* Restore original tty mode before suspending. */
if (oldmode != TERM_COOKED) { if (oldmode != TERM_COOKED) {
@@ -469,19 +274,18 @@ suspend_parent(signo, output, then, now, ofile, tfile)
* controlling tty, belongs to child's session but has its own pgrp. * controlling tty, belongs to child's session but has its own pgrp.
*/ */
int int
script_execv(path, argv) script_execve(struct command_details *details, char *argv[], char *envp[],
char *path; struct command_status *cstat)
char *argv[];
{ {
sigaction_t sa; sigaction_t sa;
struct script_buf input, output; struct script_buf input, output;
struct timeval now, then; int n, nready;
int n, nready, exitcode = 1;
int relaysig, sv[2]; int relaysig, sv[2];
fd_set *fdsr, *fdsw; fd_set *fdsr, *fdsw;
FILE *idfile;
union script_fd ofile, tfile;
int rbac_enabled = 0; int rbac_enabled = 0;
int maxfd;
cstat->type = 0; /* XXX */
#ifdef HAVE_SELINUX #ifdef HAVE_SELINUX
rbac_enabled = is_selinux_enabled() > 0 && user_role != NULL; rbac_enabled = is_selinux_enabled() > 0 && user_role != NULL;
@@ -491,7 +295,7 @@ script_execv(path, argv)
close(script_fds[SFD_SLAVE]); close(script_fds[SFD_SLAVE]);
script_fds[SFD_SLAVE] = open(slavename, O_RDWR|O_NOCTTY, 0); script_fds[SFD_SLAVE] = open(slavename, O_RDWR|O_NOCTTY, 0);
if (script_fds[SFD_SLAVE] == -1) if (script_fds[SFD_SLAVE] == -1)
log_error(USE_ERRNO, "cannot open %s", slavename); error(1, "cannot open %s", slavename);
} }
#endif #endif
@@ -542,8 +346,8 @@ script_execv(path, argv)
* We communicate with the child over a bi-directional pipe. * We communicate with the child over a bi-directional pipe.
* Parent sends signal info to child and child sends back wait status. * Parent sends signal info to child and child sends back wait status.
*/ */
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) != 0) if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sv) != 0)
log_error(USE_ERRNO, "cannot create sockets"); error(1, "cannot create sockets");
if (foreground) { if (foreground) {
/* Copy terminal attrs from user tty -> pty slave. */ /* Copy terminal attrs from user tty -> pty slave. */
@@ -559,7 +363,7 @@ script_execv(path, argv)
ttymode == TERM_CBREAK); ttymode == TERM_CBREAK);
} while (!n && errno == EINTR); } while (!n && errno == EINTR);
if (!n) if (!n)
log_error(USE_ERRNO, "Can't set terminal to raw mode"); error(1, "Can't set terminal to raw mode");
} }
/* /*
@@ -569,43 +373,22 @@ script_execv(path, argv)
child = fork(); child = fork();
switch (child) { switch (child) {
case -1: case -1:
log_error(USE_ERRNO, "fork"); error(1, "fork");
break; break;
case 0: case 0:
/* child */
close(sv[0]); close(sv[0]);
script_child(path, argv, sv[1], rbac_enabled); if (exec_setup(details) == 0) {
/* NOTREACHED */ /* headed for execve() */
break; script_child(details->command, argv, envp, sv[1], rbac_enabled);
}
cstat->type = CMD_ERRNO;
cstat->val = errno;
send(sv[1], cstat, sizeof(*cstat), 0);
_exit(1);
} }
close(sv[1]); 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); n = fcntl(script_fds[SFD_MASTER], F_GETFL, 0);
if (n != -1) { if (n != -1) {
n |= O_NONBLOCK; n |= O_NONBLOCK;
@@ -622,13 +405,19 @@ script_execv(path, argv)
(void) fcntl(STDOUT_FILENO, F_SETFL, n); (void) fcntl(STDOUT_FILENO, F_SETFL, n);
} }
/* Max fd we will be selecting on. */
maxfd = sv[0];
if (maxfd < script_fds[SFD_MASTER])
maxfd = script_fds[SFD_MASTER];
if (maxfd < script_fds[SFD_USERTTY])
maxfd = script_fds[SFD_USERTTY];
/* /*
* In the event loop we pass input from user tty to master * In the event loop we pass input from user tty to master
* and pass output from master to stdout and ofile. Note that * and pass output from master to stdout and IO plugin.
* 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)); fdsr = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
fdsw = (fd_set *)emalloc2(howmany(sv[0] + 1, NFDBITS), sizeof(fd_mask)); fdsw = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
zero_bytes(&input, sizeof(input)); zero_bytes(&input, sizeof(input));
zero_bytes(&output, sizeof(output)); zero_bytes(&output, sizeof(output));
while (alive) { while (alive) {
@@ -643,8 +432,8 @@ script_execv(path, argv)
if (output.off == output.len) if (output.off == output.len)
output.off = output.len = 0; output.off = output.len = 0;
zero_bytes(fdsw, howmany(sv[0] + 1, NFDBITS) * sizeof(fd_mask)); zero_bytes(fdsw, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
zero_bytes(fdsr, howmany(sv[0] + 1, NFDBITS) * sizeof(fd_mask)); zero_bytes(fdsr, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
if (ttymode == TERM_RAW && input.len != sizeof(input.buf)) if (ttymode == TERM_RAW && input.len != sizeof(input.buf))
FD_SET(script_fds[SFD_USERTTY], fdsr); FD_SET(script_fds[SFD_USERTTY], fdsr);
@@ -658,51 +447,50 @@ script_execv(path, argv)
if (relaysig) if (relaysig)
FD_SET(sv[0], fdsw); FD_SET(sv[0], fdsw);
nready = select(sv[0] + 1, fdsr, fdsw, NULL, NULL); nready = select(maxfd + 1, fdsr, fdsw, NULL, NULL);
if (nready == -1) { if (nready == -1) {
if (errno == EINTR) if (errno == EINTR)
continue; continue;
log_error(USE_ERRNO, "select failed"); error(1, "select failed");
} }
if (FD_ISSET(sv[0], fdsr)) { if (FD_ISSET(sv[0], fdsr)) {
/* read child status */ /* read child status */
n = read(sv[0], &child_status, sizeof(child_status)); n = recv(sv[0], cstat, sizeof(*cstat), 0);
if (n == -1) { if (n == -1) {
if (errno == EINTR) if (errno == EINTR)
continue; continue;
if (errno != EAGAIN) if (errno != EAGAIN)
break; break;
} else if (n != sizeof(child_status)) { } else if (n != sizeof(*cstat)) {
break; /* EOF? */ break; /* EOF? */
} }
if (WIFSTOPPED(child_status)) { if (cstat->type == CMD_WSTATUS) {
if (WIFSTOPPED(cstat->val)) {
/* Suspend parent and tell child how to resume on return. */ /* Suspend parent and tell child how to resume on return. */
#ifdef SCRIPT_DEBUG #ifdef SCRIPT_DEBUG
warningx("child stopped, suspending parent"); warningx("child stopped, suspending parent");
#endif #endif
relaysig = suspend_parent(WSTOPSIG(child_status), relaysig = suspend_parent(WSTOPSIG(cstat->val), &output);
&output, &then, &now, ofile, tfile);
/* XXX - write relaysig immediately? */ /* XXX - write relaysig immediately? */
continue; continue;
} else { } else {
/* Child exited or was killed, either way we are done. */ /* Child exited or was killed, either way we are done. */
if (WIFEXITED(child_status)) break;
exitcode = WEXITSTATUS(child_status); }
else if (WIFSIGNALED(child_status)) } else if (cstat->type == CMD_ERRNO) {
exitcode = WTERMSIG(child_status) | 128; /* Child was unable to execute command. */
break; break;
} }
} }
if (FD_ISSET(sv[0], fdsw)) { if (FD_ISSET(sv[0], fdsw)) {
/* XXX - we rely on child to be suspended before we suspend us */ /* XXX - we rely on child to be suspended before we suspend us */
n = write(sv[0], &relaysig, sizeof(relaysig)); cstat->type = CMD_SIGNO;
cstat->val = relaysig;
relaysig = 0; relaysig = 0;
if (n == -1) { do {
if (errno == EINTR) n = send(sv[0], cstat, sizeof(*cstat), 0);
continue; } while (n == -1 && errno == EINTR);
if (errno != EAGAIN) if (n != sizeof(relaysig)) {
break;
} else if (n != sizeof(relaysig)) {
break; /* should not happen */ break; /* should not happen */
} }
} }
@@ -717,6 +505,7 @@ script_execv(path, argv)
} else { } else {
if (n == 0) if (n == 0)
break; /* got EOF */ break; /* got EOF */
log_input(input.buf + input.len, n);
input.len += n; input.len += n;
} }
} }
@@ -733,7 +522,6 @@ script_execv(path, argv)
} }
} }
if (FD_ISSET(script_fds[SFD_MASTER], fdsr)) { if (FD_ISSET(script_fds[SFD_MASTER], fdsr)) {
gettimeofday(&now, NULL);
n = read(script_fds[SFD_MASTER], output.buf + output.len, n = read(script_fds[SFD_MASTER], output.buf + output.len,
sizeof(output.buf) - output.len); sizeof(output.buf) - output.len);
if (n == -1) { if (n == -1) {
@@ -744,9 +532,7 @@ script_execv(path, argv)
} else { } else {
if (n == 0) if (n == 0)
break; /* got EOF */ break; /* got EOF */
log_output(output.buf + output.len, n);
/* Update output and timing files. */
log_output(output.buf + output.len, n, &then, &now, ofile, tfile);
output.len += n; output.len += n;
} }
} }
@@ -770,26 +556,15 @@ script_execv(path, argv)
n &= ~O_NONBLOCK; n &= ~O_NONBLOCK;
(void) fcntl(STDOUT_FILENO, F_SETFL, n); (void) fcntl(STDOUT_FILENO, F_SETFL, n);
} }
flush_output(&output, &then, &now, ofile, tfile); flush_output(&output);
#ifdef HAVE_ZLIB
if (def_compress_transcript) {
gzclose(ofile.g);
gzclose(tfile.g);
} else
#endif
{
fclose(ofile.f);
fclose(tfile.f);
}
#ifdef HAVE_STRSIGNAL #ifdef HAVE_STRSIGNAL
if (WIFSIGNALED(child_status)) { if (cstat->type == CMD_WSTATUS && WIFSIGNALED(cstat->val)) {
int signo = WTERMSIG(child_status); int signo = WTERMSIG(cstat->val);
if (signo && signo != SIGINT && signo != SIGPIPE) { if (signo && signo != SIGINT && signo != SIGPIPE) {
char *reason = strsignal(signo); char *reason = strsignal(signo);
write(STDOUT_FILENO, reason, strlen(reason)); write(STDOUT_FILENO, reason, strlen(reason));
if (WCOREDUMP(child_status)) if (WCOREDUMP(cstat->val))
write(STDOUT_FILENO, " (core dumped)", 14); write(STDOUT_FILENO, " (core dumped)", 14);
write(STDOUT_FILENO, "\n", 1); write(STDOUT_FILENO, "\n", 1);
} }
@@ -800,30 +575,22 @@ script_execv(path, argv)
n = term_restore(script_fds[SFD_USERTTY], 0); n = term_restore(script_fds[SFD_USERTTY], 0);
} while (!n && errno == EINTR); } while (!n && errno == EINTR);
exit(exitcode); return cstat->type == CMD_ERRNO ? -1 : 0;
} }
void int
script_child(path, argv, backchannel, rbac_enabled) script_child(const char *path, char *argv[], char *envp[], int backchannel, int rbac)
char *path;
char *argv[];
int backchannel;
int rbac_enabled;
{ {
struct command_status cstat;
fd_set *fdsr;
sigaction_t sa; sigaction_t sa;
pid_t pid, self = getpid(); pid_t pid, self = getpid();
int nread, signo, status; int n, signo, status;
#ifndef TIOCSCTTY
int n;
#endif
recvsig = 0; recvsig = 0;
/* Close unused fds. */ /* Close unused fds. */
close(script_fds[SFD_MASTER]); 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]); close(script_fds[SFD_USERTTY]);
/* Reset signal handlers. */ /* Reset signal handlers. */
@@ -840,8 +607,7 @@ script_child(path, argv, backchannel, rbac_enabled)
sigaction(SIGTTIN, &sa, NULL); sigaction(SIGTTIN, &sa, NULL);
sigaction(SIGTTOU, &sa, NULL); sigaction(SIGTTOU, &sa, NULL);
/* We want SIGCHLD to interrupt us. */ /* SIGCHLD will interrupt select. */
sa.sa_flags = 0; /* do not restart syscalls for these signals. */
sa.sa_handler = handler; sa.sa_handler = handler;
sigaction(SIGCHLD, &sa, NULL); sigaction(SIGCHLD, &sa, NULL);
@@ -853,7 +619,7 @@ script_child(path, argv, backchannel, rbac_enabled)
#ifdef HAVE_SETSID #ifdef HAVE_SETSID
if (setsid() == -1) { if (setsid() == -1) {
warning("setsid"); warning("setsid");
_exit(1); goto bad;
} }
#else #else
# ifdef TIOCNOTTY # ifdef TIOCNOTTY
@@ -869,7 +635,7 @@ script_child(path, argv, backchannel, rbac_enabled)
#endif #endif
#ifdef TIOCSCTTY #ifdef TIOCSCTTY
if (ioctl(script_fds[SFD_SLAVE], TIOCSCTTY, NULL) != 0) if (ioctl(script_fds[SFD_SLAVE], TIOCSCTTY, NULL) != 0)
log_error(USE_ERRNO, "unable to set controlling tty"); error(1, "unable to set controlling tty");
#else #else
/* Set controlling tty by reopening slave. */ /* Set controlling tty by reopening slave. */
if ((n = open(slavename, O_RDWR)) >= 0) if ((n = open(slavename, O_RDWR)) >= 0)
@@ -880,10 +646,11 @@ script_child(path, argv, backchannel, rbac_enabled)
foreground = 0; foreground = 0;
/* Start command and wait for it to stop or exit */ /* Start command and wait for it to stop or exit */
/* XXX - use details->timeout */
child = fork(); child = fork();
if (child == -1) { if (child == -1) {
warning("Can't fork"); warning("Can't fork");
_exit(1); goto bad;
} }
if (child == 0) { if (child == 0) {
/* Reset signal handlers. */ /* Reset signal handlers. */
@@ -900,9 +667,9 @@ script_child(path, argv, backchannel, rbac_enabled)
sigaction(SIGUSR2, &sa, NULL); sigaction(SIGUSR2, &sa, NULL);
/* setup tty and exec command */ /* setup tty and exec command */
script_run(path, argv, rbac_enabled); script_run(path, argv, envp, rbac);
warning("unable to execute %s", path); warning("unable to execute %s", path); /* XXX - leave this to plugin? */
_exit(127); goto bad;
} }
/* /*
@@ -917,6 +684,9 @@ script_child(path, argv, backchannel, rbac_enabled)
} }
/* Wait for signal on backchannel or for SIGCHLD */ /* Wait for signal on backchannel or for SIGCHLD */
fdsr = (fd_set *)emalloc2(howmany(backchannel + 1, NFDBITS), sizeof(fd_mask));
zero_bytes(fdsr, howmany(backchannel + 1, NFDBITS) * sizeof(fd_mask));
FD_SET(backchannel, fdsr);
for (;;) { for (;;) {
/* Read child status, assumes recvsig can only be SIGCHLD */ /* Read child status, assumes recvsig can only be SIGCHLD */
while (recvsig) { while (recvsig) {
@@ -934,26 +704,43 @@ script_child(path, argv, backchannel, rbac_enabled)
else else
warningx("command exited?"); warningx("command exited?");
#endif #endif
if (write(backchannel, &status, sizeof(status)) != sizeof(status)) cstat.type = CMD_WSTATUS;
cstat.val = status;
do {
n = send(backchannel, &cstat, sizeof(cstat), 0);
} while (n == -1 && errno == EINTR);
if (n != sizeof(cstat))
break; /* XXX - error, kill child and exit */ break; /* XXX - error, kill child and exit */
#ifdef SCRIPT_DEBUG #ifdef SCRIPT_DEBUG
warningx("sent signo to parent"); warningx("sent signo to parent");
#endif #endif
if (!WIFSTOPPED(status)) { if (!WIFSTOPPED(status)) {
/* XXX */
_exit(1); /* child dead */ _exit(1); /* child dead */
} }
} }
} }
nread = read(backchannel, &signo, sizeof(signo)); n = select(backchannel + 1, fdsr, NULL, NULL, NULL);
if (nread == -1) { if (n == -1) {
if (errno != EINTR) if (errno == EINTR)
break; /* XXX - error, kill child and exit */
continue; continue;
error(1, "select failed");
} }
if (nread != sizeof(signo)) {
/* EOF? */ /* read child status */
n = recv(backchannel, &cstat, sizeof(cstat), 0);
if (n == -1) {
if (errno == EINTR)
continue;
} else if (n != sizeof(cstat)) {
warningx("error reading command status");
break; break;
} }
if (cstat.type != CMD_SIGNO) {
warningx("unexpected reply type on backchannel: %d", cstat.type);
continue;
}
signo = cstat.val;
/* Handle signal from parent. */ /* Handle signal from parent. */
#ifdef SCRIPT_DEBUG #ifdef SCRIPT_DEBUG
@@ -961,7 +748,7 @@ script_child(path, argv, backchannel, rbac_enabled)
#endif #endif
switch (signo) { switch (signo) {
case SIGKILL: case SIGKILL:
_exit(1); _exit(1); /* XXX */
/* NOTREACHED */ /* NOTREACHED */
case SIGHUP: case SIGHUP:
case SIGTERM: case SIGTERM:
@@ -991,16 +778,14 @@ script_child(path, argv, backchannel, rbac_enabled)
} }
} }
_exit(1); _exit(1); /* XXX */
bad:
return errno;
} }
static void static void
flush_output(output, then, now, ofile, tfile) flush_output(struct script_buf *output)
struct script_buf *output;
struct timeval *then;
struct timeval *now;
union script_fd ofile;
union script_fd tfile;
{ {
int n; int n;
@@ -1017,7 +802,8 @@ flush_output(output, then, now, ofile, tfile)
n = read(script_fds[SFD_MASTER], output->buf, sizeof(output->buf)); n = read(script_fds[SFD_MASTER], output->buf, sizeof(output->buf));
if (n <= 0) if (n <= 0)
break; break;
log_output(output->buf, n, &then, &now, ofile, tfile); /* XXX */
log_output(output->buf, n);
output->off = 0; output->off = 0;
output->len = n; output->len = n;
do { do {
@@ -1031,10 +817,7 @@ flush_output(output, then, now, ofile, tfile)
} }
static void static void
script_run(path, argv, rbac_enabled) script_run(const char *path, char *argv[], char *envp[], int rbac_enabled)
char *path;
char *argv[];
int rbac_enabled;
{ {
pid_t self = getpid(); pid_t self = getpid();
@@ -1058,16 +841,14 @@ script_run(path, argv, rbac_enabled)
#ifdef HAVE_SELINUX #ifdef HAVE_SELINUX
if (rbac_enabled) if (rbac_enabled)
selinux_execv(path, argv); selinux_execve(path, argv, envp);
else else
#endif #endif
execv(path, argv); execve(path, argv, envp);
} }
static void static void
sync_winsize(src, dst) sync_winsize(int src, int dst)
int src;
int dst;
{ {
#ifdef TIOCGWINSZ #ifdef TIOCGWINSZ
struct winsize win; struct winsize win;
@@ -1087,8 +868,7 @@ sync_winsize(src, dst)
* Handler for SIGCHLD in parent * Handler for SIGCHLD in parent
*/ */
static void static void
sigchild(s) sigchild(int s)
int s;
{ {
pid_t pid; pid_t pid;
int serrno = errno; int serrno = errno;
@@ -1110,8 +890,7 @@ sigchild(s)
* Generic handler for signals passed from parent -> child * Generic handler for signals passed from parent -> child
*/ */
static void static void
handler(s) handler(int s)
int s;
{ {
recvsig = s; recvsig = s;
} }
@@ -1120,8 +899,7 @@ handler(s)
* Handler for SIGWINCH in parent * Handler for SIGWINCH in parent
*/ */
static void static void
sigwinch(s) sigwinch(int s)
int s;
{ {
int serrno = errno; int serrno = errno;

893
src/sudo.c Normal file
View File

@@ -0,0 +1,893 @@
/*
* 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.
*/
#ifdef __TANDEM
# include <floss.h>
#endif
#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/socket.h>
#ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
#endif /* HAVE_SYS_SELECT_H */
#ifdef HAVE_SETRLIMIT
# include <sys/time.h>
# include <sys/resource.h>
#endif
#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 */
#include <pwd.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <grp.h>
#if TIME_WITH_SYS_TIME
# include <time.h>
#endif
#ifdef HAVE_SETLOCALE
# include <locale.h>
#endif
#ifdef HAVE_LOGIN_CAP_H
# include <login_cap.h>
#endif
#include <sudo_usage.h>
#include "sudo.h"
#include "sudo_plugin.h"
#include "sudo_plugin_int.h"
#ifdef USING_NONUNIX_GROUPS
# include "nonunix.h"
#endif
/*
* Local variables
*/
struct plugin_container policy_plugin;
struct plugin_container_list io_plugins;
/*
* Local functions
*/
static void fix_fds(void);
static void disable_coredumps(void);
static char **get_user_info(struct user_details *);
static void command_info_to_details(char * const info[],
struct command_details *details);
static int run_command(struct command_details *details, char *argv[],
char *envp[]);
/* XXX - header file */
extern const char *list_user, *runas_user, *runas_group;
/* Used by getprogname() unless crt0 supports getting program name. */
int Argc;
char **Argv;
#if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
static struct rlimit corelimit;
#endif /* RLIMIT_CORE && !SUDO_DEVEL */
sigaction_t saved_sa_int, saved_sa_quit, saved_sa_tstp;
int
main(int argc, char *argv[], char *envp[])
{
sigaction_t sa;
int nargc, sudo_mode;
char **nargv, **settings, **env_add;
char **user_info, **command_info, **argv_out, **user_env_out;
struct plugin_container *plugin;
struct user_details user_details;
struct command_details command_details;
int ok;
#if defined(SUDO_DEVEL) && defined(__OpenBSD__)
extern char *malloc_options;
malloc_options = "AFGJPR";
#endif
Argc = argc;
Argv = argv;
#ifdef HAVE_SETLOCALE
setlocale(LC_ALL, "");
#endif
if (geteuid() != 0)
errorx(1, "must be setuid root");
/* XXX - Must be done before shadow file lookups... */
#if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
(void) set_auth_parameters(Argc, Argv);
# ifdef HAVE_INITPRIVS
initprivs();
# endif
#endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
/*
* Signal setup:
* Ignore keyboard-generated signals so the user cannot interrupt
* us at some point and avoid the logging.
* XXX - leave this to the plugin?
*/
zero_bytes(&sa, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_IGN;
(void) sigaction(SIGINT, &sa, &saved_sa_int);
(void) sigaction(SIGQUIT, &sa, &saved_sa_quit);
(void) sigaction(SIGTSTP, &sa, &saved_sa_tstp);
/* Turn off core dumps and make sure fds 0-2 are open. */
disable_coredumps();
fix_fds();
/* Parse command line arguments. */
sudo_mode = parse_args(Argc, Argv, &nargc, &nargv, &settings, &env_add);
/* Read sudo.conf and load plugins. */
sudo_load_plugins(_PATH_SUDO_CONF, &policy_plugin, &io_plugins);
/* Fill in user_info with user name, uid, cwd, etc. */
memset(&user_details, 0, sizeof(user_details));
user_info = get_user_info(&user_details);
/* Open each plugin (XXX - check for errors). */
policy_plugin.u.policy->open(SUDO_API_VERSION, sudo_conversation,
settings, user_info, envp);
tq_foreach_fwd(&io_plugins, plugin) {
/* XXX - remove from list if open returns 0 */
plugin->u.io->open(SUDO_API_VERSION, sudo_conversation, settings,
user_info, envp);
}
/* XXX - should not need to check for MODE_INVALIDATE ORed in */
warningx("sudo_mode %d", sudo_mode); /* XXX */
switch (sudo_mode & MODE_MASK) {
case MODE_VERSION:
policy_plugin.u.policy->show_version(!user_details.uid);
tq_foreach_fwd(&io_plugins, plugin) {
plugin->u.io->show_version(!user_details.uid);
}
break;
case MODE_VALIDATE:
case MODE_VALIDATE|MODE_INVALIDATE:
if (policy_plugin.u.policy->validate == NULL) {
warningx("policy plugin %s does not support the -v flag",
policy_plugin.name);
ok = FALSE;
} else {
ok = policy_plugin.u.policy->validate();
}
exit(ok != TRUE);
case MODE_KILL:
case MODE_INVALIDATE:
if (policy_plugin.u.policy->validate == NULL) {
warningx("policy plugin %s does not support the -k/-K flags",
policy_plugin.name);
exit(1);
}
policy_plugin.u.policy->invalidate(sudo_mode == MODE_KILL);
exit(0);
break;
case MODE_CHECK:
case MODE_CHECK|MODE_INVALIDATE:
case MODE_LIST:
case MODE_LIST|MODE_INVALIDATE:
if (policy_plugin.u.policy->list == NULL) {
warningx("policy plugin %s does not support listing privileges",
policy_plugin.name);
ok = FALSE;
} else {
ok = policy_plugin.u.policy->list(nargc, nargv,
ISSET(sudo_mode, MODE_LONG_LIST), list_user);
}
exit(ok != TRUE);
case MODE_RUN:
ok = policy_plugin.u.policy->check_policy(nargc, nargv, env_add,
&command_info, &argv_out, &user_env_out);
warningx("policy plugin returns %d", ok); /* XXX */
if (ok != TRUE)
exit(ok); /* plugin printed error message */
command_info_to_details(command_info, &command_details);
/* Restore coredumpsize resource limit before running. */
#if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
(void) setrlimit(RLIMIT_CORE, &corelimit);
#endif /* RLIMIT_CORE && !SUDO_DEVEL */
/* run_command will call close for us */
run_command(&command_details, argv_out, user_env_out);
break;
case MODE_EDIT:
/* XXX - fill in */
break;
default:
errorx(1, "unexpected sudo mode 0x%x", sudo_mode);
}
exit(0);
}
/*
* Ensure that stdin, stdout and stderr are open; set to /dev/null if not.
* Some operating systems do this automatically in the kernel or libc.
*/
static void
fix_fds(void)
{
int miss[3], devnull = -1;
/*
* stdin, stdout and stderr must be open; set them to /dev/null
* if they are closed.
*/
miss[STDIN_FILENO] = fcntl(STDIN_FILENO, F_GETFL, 0) == -1;
miss[STDOUT_FILENO] = fcntl(STDOUT_FILENO, F_GETFL, 0) == -1;
miss[STDERR_FILENO] = fcntl(STDERR_FILENO, F_GETFL, 0) == -1;
if (miss[STDIN_FILENO] || miss[STDOUT_FILENO] || miss[STDERR_FILENO]) {
if ((devnull = open(_PATH_DEVNULL, O_RDWR, 0644)) != -1) {
if (miss[STDIN_FILENO])
(void) dup2(devnull, STDIN_FILENO);
if (miss[STDOUT_FILENO])
(void) dup2(devnull, STDOUT_FILENO);
if (miss[STDERR_FILENO])
(void) dup2(devnull, STDERR_FILENO);
if (devnull > STDERR_FILENO)
close(devnull);
}
}
}
static char *
get_user_groups(struct user_details *ud)
{
char *gid_list = NULL;
#ifdef HAVE_GETGROUPS
size_t glsize;
char *cp;
int i;
if ((ud->ngroups = getgroups(0, NULL)) <= 0)
return NULL;
ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
if (getgroups(ud->ngroups, ud->groups) < 0)
error(1, "can't get group vector");
glsize = sizeof("groups=") - 1 + (ud->ngroups * (MAX_UID_T_LEN + 1));
gid_list = emalloc(glsize);
memcpy(gid_list, "groups=", sizeof("groups=") - 1);
cp = gid_list + sizeof("groups=") - 1;
for (i = 0; i < ud->ngroups; i++) {
snprintf(cp, glsize - (cp - gid_list), "%lu%s",
(unsigned long)ud->groups[i], i ? "," : "");
}
#endif
return gid_list;
}
/*
* Return user information as an array of name=value pairs.
* and fill in struct user_details (which shares the same strings).
*/
static char **
get_user_info(struct user_details *ud)
{
char cwd[PATH_MAX];
char host[MAXHOSTNAMELEN];
char **user_info, *cp;
struct passwd *pw;
int i = 0;
/* XXX - bound check number of entries */
user_info = emalloc2(32, sizeof(char *));
ud->uid = getuid();
ud->euid = geteuid();
ud->gid = getgid();
ud->egid = getegid();
pw = getpwuid(ud->uid);
if (pw == NULL)
errorx(1, "unknown uid %lu: who are you?", (unsigned long)ud->uid);
user_info[i] = fmt_string("user", pw->pw_name);
ud->username = user_info[i] + sizeof("user=") - 1;
easprintf(&user_info[++i], "uid=%lu", (unsigned long)ud->uid);
easprintf(&user_info[++i], "euid=%lu", (unsigned long)ud->euid);
easprintf(&user_info[++i], "gid=%lu", (unsigned long)ud->gid);
easprintf(&user_info[++i], "egid=%lu", (unsigned long)ud->egid);
if ((cp = get_user_groups(ud)) != NULL)
user_info[++i] = cp;
if (getcwd(cwd, sizeof(cwd)) != NULL) {
user_info[++i] = fmt_string("cwd", cwd);
ud->cwd = user_info[i] + sizeof("cwd=") - 1;
}
if ((cp = ttyname(STDIN_FILENO)) || (cp = ttyname(STDOUT_FILENO)) ||
(cp = ttyname(STDERR_FILENO))) {
user_info[++i] = fmt_string("tty", cp);
ud->tty = user_info[i] + sizeof("tty=") - 1;
}
if (gethostname(host, sizeof(host)) == 0)
host[sizeof(host) - 1] = '\0';
else
strlcpy(host, "localhost", sizeof(host));
user_info[++i] = fmt_string("host", host);
ud->host = user_info[i] + sizeof("host=") - 1;
user_info[++i] = NULL;
return user_info;
}
/*
* Convert a command_info array into a command_details structure.
*/
static void
command_info_to_details(char * const info[], struct command_details *details)
{
int i;
long lval;
unsigned long ulval;
char *cp, *ep;
memset(details, 0, sizeof(*details));
for (i = 0; info[i] != NULL; i++) {
/* XXX - should ignore empty entries */
switch (info[i][0]) {
case 'c':
if (strncmp("chroot=", info[i], sizeof("chroot=") - 1) == 0) {
details->chroot = info[i] + sizeof("chroot=") - 1;
break;
}
if (strncmp("command=", info[i], sizeof("command=") - 1) == 0) {
details->command = info[i] + sizeof("command=") - 1;
break;
}
if (strncmp("cwd=", info[i], sizeof("cwd=") - 1) == 0) {
details->cwd = info[i] + sizeof("cwd=") - 1;
break;
}
break;
case 'l':
if (strncmp("login_class=", info[i], sizeof("login_class=") - 1) == 0) {
details->login_class = info[i] + sizeof("login_class=") - 1;
break;
}
break;
case 'n':
/* XXX - bounds check -NZERO to NZERO (inclusive). */
if (strncmp("nice=", info[i], sizeof("nice=") - 1) == 0) {
cp = info[i] + sizeof("nice=") - 1;
errno = 0;
lval = strtol(cp, &ep, 0);
if (*cp != '\0' && *ep == '\0' &&
!(errno == ERANGE &&
(lval == LONG_MAX || lval == LONG_MIN)) &&
lval < INT_MAX && lval > INT_MIN) {
details->priority = (int)lval;
SET(details->flags, CD_SET_PRIORITY);
}
break;
}
if (strncmp("noexec=", info[i], sizeof("noexec=") - 1) == 0) {
if (atobool(info[i] + sizeof("noexec=") - 1))
SET(details->flags, CD_NOEXEC);
break;
}
break;
case 'p':
if (strncmp("preserve_groups=", info[i], sizeof("preserve_groups=") - 1) == 0) {
if (atobool(info[i] + sizeof("preserve_groups=") - 1))
SET(details->flags, CD_PRESERVE_GROUPS);
break;
}
break;
case 'r':
if (strncmp("runas_egid=", info[i], sizeof("runas_egid=") - 1) == 0) {
cp = info[i] + sizeof("runas_egid=") - 1;
errno = 0;
ulval = strtoul(cp, &ep, 0);
if (*cp != '\0' && *ep == '\0' &&
(errno != ERANGE || ulval != ULONG_MAX)) {
details->egid = (gid_t)ulval;
SET(details->flags, CD_SET_EGID);
}
break;
}
if (strncmp("runas_euid=", info[i], sizeof("runas_euid=") - 1) == 0) {
cp = info[i] + sizeof("runas_euid=") - 1;
errno = 0;
ulval = strtoul(cp, &ep, 0);
if (*cp != '\0' && *ep == '\0' &&
(errno != ERANGE || ulval != ULONG_MAX)) {
details->euid = (uid_t)ulval;
SET(details->flags, CD_SET_EUID);
}
break;
}
if (strncmp("runas_gid=", info[i], sizeof("runas_gid=") - 1) == 0) {
cp = info[i] + sizeof("runas_gid=") - 1;
errno = 0;
ulval = strtoul(cp, &ep, 0);
if (*cp != '\0' && *ep == '\0' &&
(errno != ERANGE || ulval != ULONG_MAX)) {
details->gid = (gid_t)ulval;
SET(details->flags, CD_SET_GID);
}
break;
}
if (strncmp("runas_groups=", info[i], sizeof("runas_groups=") - 1) == 0) {
int j;
/* count groups, alloc and fill in */
cp = info[i] + sizeof("runas_groups=") - 1;
for (;;) {
details->ngroups++;
if ((cp = strchr(cp, ',')) == NULL)
break;
cp++;
}
details->groups = emalloc2(details->ngroups, sizeof(GETGROUPS_T));
cp = info[i] + sizeof("runas_groups=") - 1;
for (j = 0; j < details->ngroups;) {
errno = 0;
ulval = strtoul(cp, &ep, 0);
if (*cp != '\0' && (*ep == ',' || *ep == '\0') &&
(errno != ERANGE || ulval != ULONG_MAX)) {
details->groups[j++] = (gid_t)ulval;
}
}
details->ngroups = j;
break;
}
if (strncmp("runas_uid=", info[i], sizeof("runas_uid=") - 1) == 0) {
cp = info[i] + sizeof("runas_uid=") - 1;
errno = 0;
ulval = strtoul(cp, &ep, 0);
if (*cp != '\0' && *ep == '\0' &&
(errno != ERANGE || ulval != ULONG_MAX)) {
details->uid = (uid_t)ulval;
SET(details->flags, CD_SET_UID);
}
break;
}
break;
case 's':
if (strncmp("selinux_role=", info[i], sizeof("selinux_role=") - 1) == 0) {
details->selinux_role = info[i] + sizeof("selinux_role=") - 1;
break;
}
if (strncmp("selinux_type=", info[i], sizeof("selinux_type=") - 1) == 0) {
details->selinux_type = info[i] + sizeof("selinux_type=") - 1;
break;
}
break;
case 't':
if (strncmp("timeout=", info[i], sizeof("timeout=") - 1) == 0) {
cp = info[i] + sizeof("timeout=") - 1;
errno = 0;
lval = strtol(cp, &ep, 0);
if (*cp != '\0' && *ep == '\0' &&
!(errno == ERANGE &&
(lval == LONG_MAX || lval == LONG_MIN)) &&
lval <= INT_MAX && lval >= 0) {
details->timeout = (int)lval;
SET(details->flags, CD_SET_TIMEOUT);
}
break;
}
break;
case 'u':
if (strncmp("umask=", info[i], sizeof("umask=") - 1) == 0) {
cp = info[i] + sizeof("umask=") - 1;
errno = 0;
ulval = strtoul(cp, &ep, 8);
if (*cp != '\0' && *ep == '\0' &&
(errno != ERANGE || ulval != ULONG_MAX)) {
details->umask = (uid_t)ulval;
SET(details->flags, CD_SET_UMASK);
}
}
break;
}
}
if (!ISSET(details->flags, CD_SET_EUID))
details->euid = details->uid;
}
/*
* Disable core dumps to avoid dropping a core with user password in it.
* We will reset this limit before executing the command.
* Not all operating systems disable core dumps for setuid processes.
*/
static void
disable_coredumps(void)
{
#if defined(__linux__) || (defined(RLIMIT_CORE) && !defined(SUDO_DEVEL))
struct rlimit rl;
#endif
#if defined(__linux__)
/*
* Unlimit the number of processes since Linux's setuid() will
* apply resource limits when changing uid and return EAGAIN if
* nproc would be violated by the uid switch.
*/
rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
if (setrlimit(RLIMIT_NPROC, &rl)) {
if (getrlimit(RLIMIT_NPROC, &rl) == 0) {
rl.rlim_cur = rl.rlim_max;
(void)setrlimit(RLIMIT_NPROC, &rl);
}
}
#endif /* __linux__ */
#if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
/*
* Turn off core dumps.
*/
(void) getrlimit(RLIMIT_CORE, &corelimit);
memcpy(&rl, &corelimit, sizeof(struct rlimit));
rl.rlim_cur = 0;
(void) setrlimit(RLIMIT_CORE, &rl);
#endif /* RLIMIT_CORE && !SUDO_DEVEL */
}
/*
* Cleanup hook for error()/errorx()
*/
void
cleanup(gotsignal)
int gotsignal;
{
#if 0 /* XXX */
struct sudo_nss *nss;
if (!gotsignal) {
if (snl != NULL) {
tq_foreach_fwd(snl, nss)
nss->close(nss);
}
sudo_endpwent();
sudo_endgrent();
}
#ifdef _PATH_SUDO_TRANSCRIPT
if (def_transcript)
term_restore(STDIN_FILENO, 0);
#endif
#endif
}
/*
* Setup the execution environment immediately prior to the call to execve()
*/
int
exec_setup(struct command_details *details)
{
struct passwd *pw;
pw = getpwuid(details->euid);
if (pw != NULL) {
#ifdef HAVE_GETUSERATTR
aix_setlimits(pw->pw_name);
#endif
#ifdef HAVE_LOGIN_CAP_H
if (details->login_class) {
int flags;
login_cap_t *lc;
/*
* We only use setusercontext() to set the nice value and rlimits.
*/
lc = login_getclass((char *)details->login_class);
if (!lc) {
warningx("unknown login class %s", details->login_class);
errno = ENOENT;
goto done;
}
flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
if (setusercontext(lc, pw, pw->pw_uid, flags)) {
if (pw->pw_uid != ROOT_UID) {
warning("unable to set user context");
goto done;
} else
warning("unable to set user context");
}
}
#endif /* HAVE_LOGIN_CAP_H */
}
/*
* Set groups, including supplementary group vector.
*/
#ifdef HAVE_SETEUID
if (ISSET(details->flags, CD_SET_EGID) && setegid(details->egid)) {
warning("unable to set egid to runas gid");
goto done;
}
#endif
if (ISSET(details->flags, CD_SET_GID) && setgid(details->gid)) {
warning("unable to set gid to runas gid");
goto done;
}
if (!ISSET(details->flags, CD_PRESERVE_GROUPS)) {
/* XXX - may need to initgroups anyway--plugin may not have list */
#ifdef HAVE_GETGROUPS
if (details->ngroups >= 0) {
if (setgroups(details->ngroups, details->groups) < 0) {
warning("unable to set supplementary group IDs");
goto done;
}
}
#else
if (pw && initgroups(pw->pw_name, pw->pw_gid) < 0) {
warning("unable to set supplementary group IDs");
goto done;
}
#endif
}
if (ISSET(details->flags, CD_SET_PRIORITY)) {
if (setpriority(PRIO_PROCESS, 0, details->priority) != 0) {
warning("unable to set process priority");
goto done;
}
}
if (ISSET(details->flags, CD_SET_UMASK))
(void) umask(details->umask);
if (ISSET(details->flags, CD_SET_TIMEOUT))
alarm(details->timeout);
if (details->chroot) {
if (chroot(details->chroot) != 0 || chdir("/") != 0) {
warning("unable to change root to %s", details->chroot);
goto done;
}
}
if (details->cwd) {
/* cwd is relative to the new root, if any */
if (chdir(details->cwd) != 0) {
warning("unable to change directory to %s", details->cwd);
goto done;
}
}
/* Must set uids last */
#ifdef HAVE_SETRESUID
if (setresuid(details->uid, details->euid, details->euid) != 0) {
warning("unable to change to runas uid");
goto done;
}
#elif HAVE_SETREUID
if (setreuid(details->uid, details->euid) != 0) {
warning("unable to change to runas uid");
goto done;
}
#else
if (seteuid(details->euid) != 0 || setuid(details->euid) != 0) {
warning("unable to change to runas uid");
goto done;
}
#endif /* !HAVE_SETRESUID && !HAVE_SETREUID */
errno = 0;
done:
return errno;
}
static sig_atomic_t sigchld;
static void
sigchild(int s)
{
sigchld = 1;
}
/*
* Run the command and wait for it to complete.
*/
static int
run_command(struct command_details *details, char *argv[], char *envp[])
{
struct plugin_container *plugin;
struct command_status cstat;
int exitcode = 1;
cstat.type = CMD_INVALID;
cstat.val = 0;
/*
* XXX - missing closefrom(), may not be possible in new scheme
* also no background support
* or selinux...
*/
/* If there are I/O plugins, allocate a pty and exec */
if (!tq_empty(&io_plugins)) {
warningx("script mode"); /* XXX */
script_setup(details->euid);
script_execve(details, argv, envp, &cstat);
} else {
pid_t child, pid;
int nready, sv[2];
ssize_t nread;
sigaction_t sa;
fd_set *fdsr;
zero_bytes(&sa, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
/* Want select() to be interrupted when child dies. */
sa.sa_handler = sigchild;
sigaction(SIGCHLD, &sa, NULL);
/* Ignore SIGPIPE from other end of socketpair. */
sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sa, NULL);
if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sv) != 0)
error(1, "cannot create sockets");
child = fork();
if (child == -1)
error(1, "unable to fork");
if (child == 0) {
/* child */
close(sv[0]);
if (exec_setup(details) == 0) {
/* XXX - fallback via /bin/sh */
execve(details->command, argv, envp);
}
cstat.type = CMD_ERRNO;
cstat.val = errno;
write(sv[1], &cstat, sizeof(cstat));
_exit(1);
}
close(sv[1]);
warningx("waiting for child"); /* XXX */
/* wait for child to complete or for data on sv[0] */
fdsr = (fd_set *)emalloc2(howmany(sv[0] + 1, NFDBITS), sizeof(fd_mask));
zero_bytes(fdsr, howmany(sv[0] + 1, NFDBITS) * sizeof(fd_mask));
FD_SET(sv[0], fdsr);
for (;;) {
if (sigchld) {
sigchld = 0;
do {
pid = waitpid(child, &cstat.val, WNOHANG);
if (pid == child)
cstat.type = CMD_WSTATUS;
} while (pid == -1 && errno == EINTR);
if (cstat.type == CMD_WSTATUS) {
/* command terminated, we're done */
break;
}
}
nready = select(sv[0] + 1, fdsr, NULL, NULL, NULL);
if (nready == -1) {
if (errno == EINTR)
continue;
error(1, "select failed");
}
if (FD_ISSET(sv[0], fdsr)) {
/* read child status */
nread = recv(sv[0], &cstat, sizeof(cstat), 0);
if (nread == -1) {
if (errno == EINTR)
continue;
} else if (nread != sizeof(cstat)) {
warningx("error reading command status");
}
break; /* XXX */
}
}
}
switch (cstat.type) {
case CMD_ERRNO:
/* exec_setup() or execve() returned an error. */
policy_plugin.u.policy->close(0, cstat.val);
tq_foreach_fwd(&io_plugins, plugin) {
plugin->u.io->close(0, cstat.val);
}
exitcode = 1;
break;
case CMD_WSTATUS:
/* Command ran, exited or was killed. */
policy_plugin.u.policy->close(cstat.val, 0);
tq_foreach_fwd(&io_plugins, plugin) {
plugin->u.io->close(0, cstat.val);
}
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;
}
exit(exitcode);
}
#if 0 /* XXX - convert warning/error to something log this */
/*
* Simple debugging/logging.
* XXX - use askpass if configured?
*/
void
sudo_log(int level, const char *fmt, ...)
{
va_list ap;
switch (level) {
case SUDO_LOG_INFO:
va_start(ap, fmt);
vfprintf(stdout, fmt, ap);
va_end(ap);
break;
case SUDO_LOG_DEBUG1:
case SUDO_LOG_DEBUG2:
case SUDO_LOG_DEBUG3:
case SUDO_LOG_DEBUG4:
case SUDO_LOG_DEBUG5:
case SUDO_LOG_DEBUG6:
case SUDO_LOG_DEBUG7:
case SUDO_LOG_DEBUG8:
case SUDO_LOG_DEBUG9:
if (level > debug_level)
return;
/* FALLTHROUGH */
case SUDO_LOG_WARN:
case SUDO_LOG_ERROR:
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
}
#endif

234
src/sudo.h Normal file
View File

@@ -0,0 +1,234 @@
/*
* Copyright (c) 1993-1996, 1998-2005, 2007-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.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*
* $Sudo: sudo.h,v 1.290 2009/12/12 16:12:26 millert Exp $
*/
#ifndef _SUDO_SUDO_H
#define _SUDO_SUDO_H
#include <pathnames.h>
#include <limits.h>
#include "compat.h"
#include "alloc.h"
#include "error.h"
#include "list.h"
#include "missing.h"
#ifdef __TANDEM
# define ROOT_UID 65535
#else
# define ROOT_UID 0
#endif
/*
* Pseudo-boolean values
*/
#undef TRUE
#define TRUE 1
#undef FALSE
#define FALSE 0
/*
* Various modes sudo can be in (based on arguments) in hex
*/
#define MODE_RUN 0x00000001
#define MODE_EDIT 0x00000002
#define MODE_VALIDATE 0x00000004
#define MODE_INVALIDATE 0x00000008
#define MODE_KILL 0x00000010
#define MODE_VERSION 0x00000020
#define MODE_HELP 0x00000040
#define MODE_LIST 0x00000080
#define MODE_CHECK 0x00000100
#define MODE_MASK 0x0000ffff
/* Mode flags */
/* XXX - prune this */
#define MODE_BACKGROUND 0x00010000
#define MODE_SHELL 0x00020000
#define MODE_LOGIN_SHELL 0x00040000
#define MODE_IMPLIED_SHELL 0x00080000
#define MODE_RESET_HOME 0x00100000
#define MODE_PRESERVE_GROUPS 0x00200000
#define MODE_PRESERVE_ENV 0x00400000
#define MODE_NONINTERACTIVE 0x00800000
#define MODE_LONG_LIST 0x01000000
/*
* Used with set_perms()
*/
#define PERM_ROOT 0x00
#define PERM_USER 0x01
#define PERM_FULL_USER 0x02
#define PERM_SUDOERS 0x03
#define PERM_RUNAS 0x04
#define PERM_FULL_RUNAS 0x05
#define PERM_TIMESTAMP 0x06
#define PERM_NOEXIT 0x10 /* flag */
#define PERM_MASK 0xf0
/*
* We used to use the system definition of PASS_MAX or _PASSWD_LEN,
* but that caused problems with various alternate authentication
* methods. So, we just define our own and assume that it is >= the
* system max.
*/
#define SUDO_PASS_MAX 256
/*
* Flags for lock_file()
*/
#define SUDO_LOCK 1 /* lock a file */
#define SUDO_TLOCK 2 /* test & lock a file (non-blocking) */
#define SUDO_UNLOCK 4 /* unlock a file */
/*
* Flags for tgetpass()
*/
#define TGP_ECHO 0x01 /* leave echo on when reading passwd */
#define TGP_STDIN 0x02 /* read from stdin, not /dev/tty */
#define TGP_ASKPASS 0x04 /* read from askpass helper program */
#define TGP_FEEDBACK 0x08 /* visual feedback during input */
struct user_details {
uid_t uid;
uid_t euid;
uid_t gid;
uid_t egid;
const char *username;
const char *cwd;
const char *tty;
const char *host;
GETGROUPS_T *groups;
int ngroups;
};
#define CD_SET_UID 0x0001
#define CD_SET_EUID 0x0002
#define CD_SET_GID 0x0004
#define CD_SET_EGID 0x0008
#define CD_PRESERVE_GROUPS 0x0010
#define CD_NOEXEC 0x0020
#define CD_SET_PRIORITY 0x0040
#define CD_SET_UMASK 0x0080
#define CD_SET_TIMEOUT 0x0100
struct command_details {
uid_t uid;
uid_t euid;
gid_t gid;
gid_t egid;
mode_t umask;
int flags;
int priority;
int timeout;
int ngroups;
GETGROUPS_T *groups;
const char *command;
const char *cwd;
const char *login_class;
const char *chroot;
const char *selinux_role;
const char *selinux_type;
};
/* 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;
};
/* For error() and errorx() (XXX - needed?) */
void cleanup(int);
/* tgetpass.c */
char *tgetpass(const char *, int, int);
int tty_present(void);
/* zero_bytes.c */
void zero_bytes(volatile void *, size_t);
/* fileops.c */
int lock_file(int, int);
char *sudo_parseln(FILE *);
/* script.c */
int script_duplow(int);
int script_execve(struct command_details *details, char *argv[], char *envp[],
struct command_status *cstat);
void script_setup(uid_t);
/* term.c */
int term_cbreak(int);
int term_copy(int, int, int);
int term_noecho(int);
int term_raw(int, int, int);
int term_restore(int, int);
/* fmt_string.h */
char *fmt_string(const char *var, const char *value);
/* atobool.c */
int atobool(const char *str);
/* parse_args.c */
int parse_args(int argc, char **argv, int *nargc, char ***nargv,
char ***settingsp, char ***env_addp);
/* pty.c */
int get_pty(int *master, int *slave, char *name, size_t namesz, uid_t uid);
/* sudo.c */
int exec_setup(struct command_details *details);
extern int debug_level;
extern struct plugin_container_list io_plugins;
#ifndef errno
extern int errno;
#endif
#ifdef ntoyet
/*
* Sudo logging/debugging, printf-style.
* XXX - not hooked up yet
* The debug level may be set on the command line via the -D flag.
* A higher debug level yields more verbose debugging.
*/
#define SUDO_LOG_DEBUG1 1
#define SUDO_LOG_DEBUG2 2
#define SUDO_LOG_DEBUG3 3
#define SUDO_LOG_DEBUG4 4
#define SUDO_LOG_DEBUG5 5
#define SUDO_LOG_DEBUG6 6
#define SUDO_LOG_DEBUG7 7
#define SUDO_LOG_DEBUG8 8
#define SUDO_LOG_DEBUG9 9
#define SUDO_LOG_INFO 10
#define SUDO_LOG_WARN 11
#define SUDO_LOG_ERROR 12
void sudo_log(int level, const char *format, ...) __printflike(2, 3);
#endif
#endif /* _SUDO_SUDO_H */

39
src/sudo_plugin_int.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef _SUDO_PLUGIN_INT_H
#define _SUDO_PLUGIN_INT_H
/*
* Sudo plugin internals.
*/
struct plugin_info {
struct plugin_info *prev; /* required */
struct plugin_info *next; /* required */
const char *path;
const char *symbol_name;
};
TQ_DECLARE(plugin_info)
struct plugin_container {
struct plugin_container *prev; /* required */
struct plugin_container *next; /* required */
const char *name;
void *handle;
union {
struct generic_plugin *generic;
struct policy_plugin *policy;
struct io_plugin *io;
} u;
};
TQ_DECLARE(plugin_container)
extern struct plugin_container_list policy_plugins;
extern struct plugin_container_list io_plugins;
int sudo_conversation(int num_msgs, const struct sudo_conv_message msgs[],
struct sudo_conv_reply replies[]);
void sudo_load_plugins(const char *conf_file,
struct plugin_container *policy_plugin,
struct plugin_container_list *io_plugins);
#endif /* _SUDO_PLUGIN_INT_H */

View File

@@ -56,6 +56,9 @@
#include "sudo.h" #include "sudo.h"
/* XXX */
char *user_askpass = NULL;
static volatile sig_atomic_t signo; static volatile sig_atomic_t signo;
static void handler __P((int)); static void handler __P((int));
@@ -111,7 +114,7 @@ restart:
(void) sigaction(SIGTTIN, &sa, &savettin); (void) sigaction(SIGTTIN, &sa, &savettin);
(void) sigaction(SIGTTOU, &sa, &savettou); (void) sigaction(SIGTTOU, &sa, &savettou);
if (def_pwfeedback) if (ISSET(flags, TGP_FEEDBACK))
neednl = term_cbreak(input); neednl = term_cbreak(input);
else else
neednl = term_noecho(input); neednl = term_noecho(input);
@@ -123,7 +126,7 @@ restart:
if (timeout > 0) if (timeout > 0)
alarm(timeout); alarm(timeout);
pass = getln(input, buf, sizeof(buf), def_pwfeedback); pass = getln(input, buf, sizeof(buf), ISSET(flags, TGP_FEEDBACK));
alarm(0); alarm(0);
save_errno = errno; save_errno = errno;
@@ -184,7 +187,8 @@ sudo_askpass(prompt)
if (pid == 0) { if (pid == 0) {
/* child, point stdout to output side of the pipe and exec askpass */ /* child, point stdout to output side of the pipe and exec askpass */
(void) dup2(pfd[1], STDOUT_FILENO); (void) dup2(pfd[1], STDOUT_FILENO);
set_perms(PERM_FULL_USER); //XXX - set real and effective uid to user
//set_perms(PERM_FULL_USER);
closefrom(STDERR_FILENO + 1); closefrom(STDERR_FILENO + 1);
execl(user_askpass, user_askpass, prompt, (char *)NULL); execl(user_askpass, user_askpass, prompt, (char *)NULL);
warning("unable to run %s", user_askpass); warning("unable to run %s", user_askpass);