--- /dev/null
+#
+# 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
#! /bin/sh
# 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/>.
#
# Identity of this package.
PACKAGE_NAME='sudo'
PACKAGE_TARNAME='sudo'
-PACKAGE_VERSION='1.7.3b2'
-PACKAGE_STRING='sudo 1.7.3b2'
+PACKAGE_VERSION='1.8.0a1'
+PACKAGE_STRING='sudo 1.8.0a1'
PACKAGE_BUGREPORT='http://www.sudo.ws/bugs/'
+ac_config_libobj_dir=compat
# Factoring default headers for most tests.
ac_includes_default="\
#include <stdio.h>
mansrcdir
NOEXECFILE
NOEXECDIR
+PLUGINDIR
noexec_file
INSTALL_NOEXEC
DONT_LEAK_PATH_INFO
# 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.
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]...
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of sudo 1.7.3b2:";;
+ short | recursive ) echo "Configuration of sudo 1.8.0a1:";;
esac
cat <<\_ACEOF
(default=libvas.so)
--with-libvas-rpath=PATH
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-gnu-ld assume the C compiler uses GNU ld [default=no]
--with-pic try to use only PIC/non-PIC objects [default=use
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-sudo configure 1.7.3b2
+sudo configure 1.8.0a1
generated by GNU Autoconf 2.61
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
This file contains any messages produced by compilers while
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
$ $0 $@
+
timeout=5
+
+
# Check whether --with-otp-only was given.
if test "${with_otp_only+set}" = set; then
withval=$with_otp_only; case $with_otp_only in
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 $ECHO_N "checking whether to do user authentication by default... $ECHO_C" >&6; }
;;
*-*-irix6*)
# 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
(eval $ac_compile) 2>&5
ac_status=$?
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:8338: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:8361: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
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
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:8628: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:8651: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
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
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:8732: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:8755: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
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
then
# The compiler can only warn and ignore the option if not recognized
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF
-#line 11092 "configure"
+#line 11115 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF
-#line 11192 "configure"
+#line 11215 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
exec_prefix="$oexec_prefix"
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
# This file is a shell script that caches the results of configure
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
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
CONFIG_FILES = $CONFIG_FILES
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF
ac_cs_version="\\
-sudo config.status 1.7.3b2
+sudo config.status 1.8.0a1
configured by $0, generated by GNU Autoconf 2.61,
with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
case $ac_config_target in
"config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
"pathnames.h") CONFIG_HEADERS="$CONFIG_HEADERS pathnames.h" ;;
- "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
- "sudo.man") CONFIG_FILES="$CONFIG_FILES sudo.man" ;;
- "visudo.man") CONFIG_FILES="$CONFIG_FILES visudo.man" ;;
- "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" ;;
+ "src/sudo_usage.h") CONFIG_FILES="$CONFIG_FILES src/sudo_usage.h" ;;
+ "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;;
+ "plugins/sample/Makefile") CONFIG_FILES="$CONFIG_FILES plugins/sample/Makefile" ;;
*) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
mansrcdir!$mansrcdir$ac_delim
NOEXECFILE!$NOEXECFILE$ac_delim
NOEXECDIR!$NOEXECDIR$ac_delim
+PLUGINDIR!$PLUGINDIR$ac_delim
noexec_file!$noexec_file$ac_delim
INSTALL_NOEXEC!$INSTALL_NOEXEC$ac_delim
DONT_LEAK_PATH_INFO!$DONT_LEAK_PATH_INFO$ac_delim
badpass_message!$badpass_message$ac_delim
fqdn!$fqdn$ac_delim
runas_default!$runas_default$ac_delim
-env_editor!$env_editor$ac_delim
_ACEOF
if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then
ac_delim='%!_!# '
for ac_last_try in false false false false false :; do
cat >conf$$subs.sed <<_ACEOF
+env_editor!$env_editor$ac_delim
passwd_tries!$passwd_tries$ac_delim
tty_tickets!$tty_tickets$ac_delim
insults!$insults$ac_delim
LTLIBOBJS!$LTLIBOBJS$ac_delim
_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
elif $ac_last_try; then
{ { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
dnl
dnl Copyright (c) 1994-1996,1998-2009 Todd C. Miller <Todd.Miller@courtesan.com>
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)
dnl
dnl This won't work before AC_INIT
AC_SUBST(mansrcdir)
AC_SUBST(NOEXECFILE)
AC_SUBST(NOEXECDIR)
+AC_SUBST(PLUGINDIR)
AC_SUBST(noexec_file)
AC_SUBST(INSTALL_NOEXEC)
AC_SUBST(DONT_LEAK_PATH_INFO)
test "$sbindir" = '${exec_prefix}/sbin' && sbindir='$(exec_prefix)/sbin'
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 Deprecated --with options (these all warn or generate an error)
dnl
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 Options for --enable
dnl
dnl
dnl Substitute into the Makefile and man pages
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
dnl
#define _PATH_ENVIRONMENT "/etc/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.
*/
#undef _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
#undef _PATH_VI
#endif /* _PATH_VI */
--- /dev/null
+#
+# 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
--- /dev/null
+/*
+ * 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);
+}
# include <time.h>
#endif
#ifndef HAVE_TIMESPEC
-# include <compat/timespec.h>
+# include <emul/timespec.h>
#endif
#include "sudo.h"
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/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");
+}
--- /dev/null
+/*
+ * 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);
+}
/*
- * Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
#if defined(HAVE_OPENPTY)
int
-get_pty(master, slave, name, namesz)
- int *master;
- int *slave;
- char *name;
- size_t namesz;
+get_pty(int *master, int *slave, char *name, size_t namesz, uid_t uid)
{
struct group *gr;
gid_t ttygid = -1;
- if ((gr = sudo_getgrnam("tty")) != NULL)
+ if ((gr = getgrnam("tty")) != NULL)
ttygid = gr->gr_gid;
if (openpty(master, slave, name, NULL, NULL) != 0)
return(0);
- (void) chown(name, runas_pw->pw_uid, ttygid);
+ (void) chown(name, uid, ttygid);
return(1);
}
#elif defined(HAVE__GETPTY)
int
-get_pty(master, slave, name, namesz)
- int *master;
- int *slave;
- char *name;
- size_t namesz;
+get_pty(int *master, int *slave, char *name, size_t namesz, uid_t uid)
{
char *line;
close(*master);
return(0);
}
- (void) chown(line, runas_pw->pw_uid, -1);
+ (void) chown(line, uid, -1);
strlcpy(name, line, namesz);
return(1);
}
# endif /* HAVE_POSIX_OPENPT */
int
-get_pty(master, slave, name, namesz)
- int *master;
- int *slave;
- char *name;
- size_t namesz;
+get_pty(int *master, int *slave, char *name, size_t namesz, uid_t uid)
{
char *line;
ioctl(*slave, I_PUSH, "ptem"); /* pseudo tty emulation module */
ioctl(*slave, I_PUSH, "ldterm"); /* line discipline module */
# endif
- (void) chown(line, runas_pw->pw_uid, -1);
+ (void) chown(line, uid, -1);
strlcpy(name, line, namesz);
return(1);
}
static char line[] = "/dev/ptyXX";
int
-get_pty(master, slave, name, namesz)
- int *master;
- int *slave;
- char *name;
- size_t namesz;
+get_pty(int *master, int *slave, char *name, size_t namesz, uid_t uid)
{
char *bank, *cp;
struct group *gr;
gid_t ttygid = -1;
- if ((gr = sudo_getgrnam("tty")) != NULL)
+ if ((gr = getgrnam("tty")) != NULL)
ttygid = gr->gr_gid;
for (bank = "pqrs"; *bank != '\0'; bank++) {
continue; /* already in use */
}
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);
# ifdef HAVE_REVOKE
(void) revoke(line);
/*
- * Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
#endif /* HAVE_TERMIOS_H */
#include <sys/ioctl.h>
#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
+# include <sys/select.h>
#endif /* HAVE_SYS_SELECT_H */
#include <stdio.h>
#ifdef STDC_HEADERS
#ifdef HAVE_SELINUX
# include <selinux/selinux.h>
#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_SLAVE 1
-#define SFD_LOG 2
-#define SFD_OUTPUT 3
-#define SFD_TIMING 4
-#define SFD_USERTTY 5
+#define SFD_USERTTY 2
#define TERM_COOKED 0
#define TERM_CBREAK 1
#define TERM_RAW 2
-union script_fd {
- FILE *f;
-#ifdef HAVE_ZLIB
- gzFile g;
-#endif
-};
-
struct script_buf {
int len; /* buffer length (how much read in) */
int off; /* write position (how much already consumed) */
char buf[16 * 1024];
};
-static int script_fds[6];
+static int script_fds[3];
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 recvsig = 0;
static sig_atomic_t ttymode = TERM_COOKED;
static char slavename[PATH_MAX];
-static int suspend_parent __P((int signo, struct script_buf *output,
- struct timeval *then, struct timeval *now, union script_fd ofile,
- union script_fd tfile));
-static void flush_output __P((struct script_buf *output, struct timeval *then,
- struct timeval *now, union script_fd ofile, union script_fd tfile));
-static void handler __P((int s));
-static void script_child __P((char *path, char *argv[], int, int));
-static void script_run __P((char *path, char *argv[], int));
-static void sigchild __P((int s));
-static void sigwinch __P((int s));
-static void sync_winsize __P((int src, int dst));
-
-extern int get_pty __P((int *master, int *slave, char *name, size_t namesz));
-
-/*
- * TODO: run monitor as root?
- */
-
-static int
-fdcompar(v1, v2)
- const void *v1;
- const void *v2;
-{
- int i = *(int *)v1;
- int j = *(int *)v2;
-
- return(script_fds[i] - script_fds[j]);
-}
-
-void
-script_nextid()
-{
- struct stat sb;
- char buf[32], *ep;
- int fd, i, ch;
- unsigned long id = 0;
- int len;
- ssize_t nread;
- char pathbuf[PATH_MAX];
-
- /*
- * Create _PATH_SUDO_TRANSCRIPT if it doesn't already exist.
- */
- if (stat(_PATH_SUDO_TRANSCRIPT, &sb) != 0) {
- if (mkdir(_PATH_SUDO_TRANSCRIPT, S_IRWXU) != 0)
- log_error(USE_ERRNO, "Can't mkdir %s", _PATH_SUDO_TRANSCRIPT);
- } else if (!S_ISDIR(sb.st_mode)) {
- log_error(0, "%s exists but is not a directory (0%o)",
- _PATH_SUDO_TRANSCRIPT, (unsigned int) sb.st_mode);
- }
-
- /*
- * Open sequence file
- */
- len = snprintf(pathbuf, sizeof(pathbuf), "%s/seq", _PATH_SUDO_TRANSCRIPT);
- if (len <= 0 || len >= sizeof(pathbuf)) {
- errno = ENAMETOOLONG;
- log_error(USE_ERRNO, "%s/seq", pathbuf);
- }
- fd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
- if (fd == -1)
- log_error(USE_ERRNO, "cannot open %s", pathbuf);
- lock_file(fd, SUDO_LOCK);
-
- /* Read seq number (base 36). */
- nread = read(fd, buf, sizeof(buf));
- if (nread != 0) {
- if (nread == -1)
- log_error(USE_ERRNO, "cannot read %s", pathbuf);
- id = strtoul(buf, &ep, 36);
- if (buf == ep || id >= 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] = '/';
- }
+static int suspend_parent(int signo, struct script_buf *output);
+static void flush_output(struct script_buf *output);
+static void handler(int s);
+static int script_child(const char *path, char *argv[], char *envp[], int, int);
+static void script_run(const char *path, char *argv[], char *envp[], int);
+static void sigchild(int s);
+static void sigwinch(int s);
+static void sync_winsize(int src, int dst);
- return(len);
-}
+/* sudo.c */
+extern struct plugin_container_list io_plugins;
void
-script_setup()
+script_setup(uid_t uid)
{
- char pathbuf[PATH_MAX];
- int len;
-
script_fds[SFD_USERTTY] = open(_PATH_TTY, O_RDWR|O_NOCTTY, 0);
if (script_fds[SFD_USERTTY] == -1)
- log_error(0, "tty required for transcript support"); /* XXX */
+ errorx(1, "tty required for transcript support");
if (!get_pty(&script_fds[SFD_MASTER], &script_fds[SFD_SLAVE],
- slavename, sizeof(slavename)))
- log_error(USE_ERRNO, "Can't get pty");
-
- /*
- * Build a path containing the session id split into two-digit subdirs,
- * so ID 000001 becomes /var/log/sudo-session/00/00/01.
- */
- len = build_idpath(pathbuf);
-
- /*
- * We create 3 files: a log file, one for the raw session data,
- * and one for the timing info.
- */
- script_fds[SFD_LOG] = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
- if (script_fds[SFD_LOG] == -1)
- log_error(USE_ERRNO, "Can't create %s", pathbuf);
-
- strlcat(pathbuf, ".scr", sizeof(pathbuf));
- script_fds[SFD_OUTPUT] = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY,
- S_IRUSR|S_IWUSR);
- if (script_fds[SFD_OUTPUT] == -1)
- log_error(USE_ERRNO, "Can't create %s", pathbuf);
-
- pathbuf[len] = '\0';
- strlcat(pathbuf, ".tim", sizeof(pathbuf));
- script_fds[SFD_TIMING] = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
- if (script_fds[SFD_TIMING] == -1)
- log_error(USE_ERRNO, "Can't create %s", pathbuf);
+ slavename, sizeof(slavename), uid))
+ error(1, "Can't get pty");
}
-int
-script_duplow(fd)
- int fd;
+/* Call I/O plugin input method. */
+static void
+log_input(char *buf, unsigned int n)
{
- 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++;
+ struct plugin_container *plugin;
+ sigset_t omask;
+
+ sigprocmask(SIG_BLOCK, &ttyblock, &omask);
+
+ tq_foreach_fwd(&io_plugins, plugin) {
+ /* XXX - die if return != TRUE */
+ if (plugin->u.io->log_input)
+ plugin->u.io->log_input(buf, n);
}
- return(fd);
+
+ sigprocmask(SIG_SETMASK, &omask, NULL);
}
-/* Update output and timing files. */
+/* Call I/O plugin output method. */
static void
-log_output(buf, n, then, now, ofile, tfile)
- char *buf;
- int n;
- struct timeval *then;
- struct timeval *now;
- union script_fd ofile;
- union script_fd tfile;
+log_output(char *buf, unsigned int n)
{
- struct timeval tv;
+ struct plugin_container *plugin;
sigset_t omask;
sigprocmask(SIG_BLOCK, &ttyblock, &omask);
-#ifdef HAVE_ZLIB
- if (def_compress_transcript)
- gzwrite(ofile.g, buf, n);
- else
-#endif
- fwrite(buf, 1, n, ofile.f);
- timersub(now, then, &tv);
-#ifdef HAVE_ZLIB
- if (def_compress_transcript)
- gzprintf(tfile.g, "%f %d\n",
- tv.tv_sec + ((double)tv.tv_usec / 1000000), n);
- else
-#endif
- fprintf(tfile.f, "%f %d\n",
- tv.tv_sec + ((double)tv.tv_usec / 1000000), n);
- then->tv_sec = now->tv_sec;
- then->tv_usec = now->tv_usec;
+ 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);
}
static void
-check_foreground()
+check_foreground(void)
{
foreground = tcgetpgrp(script_fds[SFD_USERTTY]) == parent;
if (foreground && !tty_initialized) {
* Returns SIGUSR1 if the child should be resume in foreground else SIGUSR2.
*/
static int
-suspend_parent(signo, output, then, now, ofile, tfile)
- int signo;
- struct script_buf *output;
- struct timeval *then;
- struct timeval *now;
- union script_fd ofile;
- union script_fd tfile;
+suspend_parent(int signo, struct script_buf *output)
{
sigaction_t sa, osa;
int n, oldmode = ttymode, rval = 0;
/* FALLTHROUGH */
case SIGTSTP:
/* Flush any remaining output to master tty. */
- flush_output(output, then, now, ofile, tfile);
+ flush_output(output);
/* Restore original tty mode before suspending. */
if (oldmode != TERM_COOKED) {
* controlling tty, belongs to child's session but has its own pgrp.
*/
int
-script_execv(path, argv)
- char *path;
- char *argv[];
+script_execve(struct command_details *details, char *argv[], char *envp[],
+ struct command_status *cstat)
{
sigaction_t sa;
struct script_buf input, output;
- struct timeval now, then;
- int n, nready, exitcode = 1;
+ int n, nready;
int relaysig, sv[2];
fd_set *fdsr, *fdsw;
- FILE *idfile;
- union script_fd ofile, tfile;
int rbac_enabled = 0;
+ int maxfd;
+
+ cstat->type = 0; /* XXX */
#ifdef HAVE_SELINUX
rbac_enabled = is_selinux_enabled() > 0 && user_role != NULL;
close(script_fds[SFD_SLAVE]);
script_fds[SFD_SLAVE] = open(slavename, O_RDWR|O_NOCTTY, 0);
if (script_fds[SFD_SLAVE] == -1)
- log_error(USE_ERRNO, "cannot open %s", slavename);
+ error(1, "cannot open %s", slavename);
}
#endif
* We communicate with the child over a bi-directional pipe.
* Parent sends signal info to child and child sends back wait status.
*/
- if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) != 0)
- log_error(USE_ERRNO, "cannot create sockets");
+ if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sv) != 0)
+ error(1, "cannot create sockets");
if (foreground) {
/* Copy terminal attrs from user tty -> pty slave. */
ttymode == TERM_CBREAK);
} while (!n && errno == EINTR);
if (!n)
- log_error(USE_ERRNO, "Can't set terminal to raw mode");
+ error(1, "Can't set terminal to raw mode");
}
/*
child = fork();
switch (child) {
case -1:
- log_error(USE_ERRNO, "fork");
+ error(1, "fork");
break;
case 0:
+ /* child */
close(sv[0]);
- script_child(path, argv, sv[1], rbac_enabled);
- /* NOTREACHED */
- break;
+ if (exec_setup(details) == 0) {
+ /* headed for execve() */
+ 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]);
- if ((idfile = fdopen(script_fds[SFD_LOG], "w")) == NULL)
- log_error(USE_ERRNO, "fdopen");
-#ifdef HAVE_ZLIB
- if (def_compress_transcript) {
- if ((ofile.g = gzdopen(script_fds[SFD_OUTPUT], "w")) == NULL)
- log_error(USE_ERRNO, "gzdopen");
- if ((tfile.g = gzdopen(script_fds[SFD_TIMING], "w")) == NULL)
- log_error(USE_ERRNO, "gzdopen");
- } else
-#endif
- {
- if ((ofile.f = fdopen(script_fds[SFD_OUTPUT], "w")) == NULL)
- log_error(USE_ERRNO, "fdopen");
- if ((tfile.f = fdopen(script_fds[SFD_TIMING], "w")) == NULL)
- log_error(USE_ERRNO, "fdopen");
- }
-
- gettimeofday(&then, NULL);
-
- /* XXX - log more stuff? window size? environment? */
- fprintf(idfile, "%ld:%s:%s:%s:%s\n", then.tv_sec, user_name,
- runas_pw->pw_name, runas_gr ? runas_gr->gr_name : "", user_tty);
- fprintf(idfile, "%s\n", user_cwd);
- fprintf(idfile, "%s%s%s\n", user_cmnd, user_args ? " " : "",
- user_args ? user_args : "");
- fclose(idfile);
-
n = fcntl(script_fds[SFD_MASTER], F_GETFL, 0);
if (n != -1) {
n |= O_NONBLOCK;
(void) fcntl(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
- * and pass output from master to stdout and ofile. Note that
- * we've set things up such that master is > 3 (see sudo.c).
+ * and pass output from master to stdout and IO plugin.
*/
- fdsr = (fd_set *)emalloc2(howmany(sv[0] + 1, NFDBITS), sizeof(fd_mask));
- fdsw = (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(maxfd + 1, NFDBITS), sizeof(fd_mask));
zero_bytes(&input, sizeof(input));
zero_bytes(&output, sizeof(output));
while (alive) {
if (output.off == output.len)
output.off = output.len = 0;
- zero_bytes(fdsw, howmany(sv[0] + 1, NFDBITS) * sizeof(fd_mask));
- zero_bytes(fdsr, howmany(sv[0] + 1, NFDBITS) * sizeof(fd_mask));
+ zero_bytes(fdsw, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
+ zero_bytes(fdsr, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
if (ttymode == TERM_RAW && input.len != sizeof(input.buf))
FD_SET(script_fds[SFD_USERTTY], fdsr);
if (relaysig)
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 (errno == EINTR)
continue;
- log_error(USE_ERRNO, "select failed");
+ error(1, "select failed");
}
if (FD_ISSET(sv[0], fdsr)) {
/* read child status */
- n = read(sv[0], &child_status, sizeof(child_status));
+ n = recv(sv[0], cstat, sizeof(*cstat), 0);
if (n == -1) {
if (errno == EINTR)
continue;
if (errno != EAGAIN)
break;
- } else if (n != sizeof(child_status)) {
+ } else if (n != sizeof(*cstat)) {
break; /* EOF? */
}
- if (WIFSTOPPED(child_status)) {
- /* Suspend parent and tell child how to resume on return. */
+ if (cstat->type == CMD_WSTATUS) {
+ if (WIFSTOPPED(cstat->val)) {
+ /* Suspend parent and tell child how to resume on return. */
#ifdef SCRIPT_DEBUG
- warningx("child stopped, suspending parent");
+ warningx("child stopped, suspending parent");
#endif
- relaysig = suspend_parent(WSTOPSIG(child_status),
- &output, &then, &now, ofile, tfile);
- /* XXX - write relaysig immediately? */
- continue;
- } else {
- /* Child exited or was killed, either way we are done. */
- if (WIFEXITED(child_status))
- exitcode = WEXITSTATUS(child_status);
- else if (WIFSIGNALED(child_status))
- exitcode = WTERMSIG(child_status) | 128;
+ relaysig = suspend_parent(WSTOPSIG(cstat->val), &output);
+ /* XXX - write relaysig immediately? */
+ continue;
+ } else {
+ /* Child exited or was killed, either way we are done. */
+ break;
+ }
+ } else if (cstat->type == CMD_ERRNO) {
+ /* Child was unable to execute command. */
break;
}
}
if (FD_ISSET(sv[0], fdsw)) {
/* XXX - we rely on child to be suspended before we suspend us */
- n = write(sv[0], &relaysig, sizeof(relaysig));
+ cstat->type = CMD_SIGNO;
+ cstat->val = relaysig;
relaysig = 0;
- if (n == -1) {
- if (errno == EINTR)
- continue;
- if (errno != EAGAIN)
- break;
- } else if (n != sizeof(relaysig)) {
+ do {
+ n = send(sv[0], cstat, sizeof(*cstat), 0);
+ } while (n == -1 && errno == EINTR);
+ if (n != sizeof(relaysig)) {
break; /* should not happen */
}
}
} else {
if (n == 0)
break; /* got EOF */
+ log_input(input.buf + input.len, n);
input.len += n;
}
}
}
}
if (FD_ISSET(script_fds[SFD_MASTER], fdsr)) {
- gettimeofday(&now, NULL);
n = read(script_fds[SFD_MASTER], output.buf + output.len,
sizeof(output.buf) - output.len);
if (n == -1) {
} else {
if (n == 0)
break; /* got EOF */
-
- /* Update output and timing files. */
- log_output(output.buf + output.len, n, &then, &now, ofile, tfile);
+ log_output(output.buf + output.len, n);
output.len += n;
}
}
n &= ~O_NONBLOCK;
(void) fcntl(STDOUT_FILENO, F_SETFL, n);
}
- flush_output(&output, &then, &now, ofile, tfile);
-
-#ifdef HAVE_ZLIB
- if (def_compress_transcript) {
- gzclose(ofile.g);
- gzclose(tfile.g);
- } else
-#endif
- {
- fclose(ofile.f);
- fclose(tfile.f);
- }
+ flush_output(&output);
#ifdef HAVE_STRSIGNAL
- if (WIFSIGNALED(child_status)) {
- int signo = WTERMSIG(child_status);
+ if (cstat->type == CMD_WSTATUS && WIFSIGNALED(cstat->val)) {
+ int signo = WTERMSIG(cstat->val);
if (signo && signo != SIGINT && signo != SIGPIPE) {
char *reason = strsignal(signo);
write(STDOUT_FILENO, reason, strlen(reason));
- if (WCOREDUMP(child_status))
+ if (WCOREDUMP(cstat->val))
write(STDOUT_FILENO, " (core dumped)", 14);
write(STDOUT_FILENO, "\n", 1);
}
n = term_restore(script_fds[SFD_USERTTY], 0);
} while (!n && errno == EINTR);
- exit(exitcode);
+ return cstat->type == CMD_ERRNO ? -1 : 0;
}
-void
-script_child(path, argv, backchannel, rbac_enabled)
- char *path;
- char *argv[];
- int backchannel;
- int rbac_enabled;
+int
+script_child(const char *path, char *argv[], char *envp[], int backchannel, int rbac)
{
+ struct command_status cstat;
+ fd_set *fdsr;
sigaction_t sa;
pid_t pid, self = getpid();
- int nread, signo, status;
-#ifndef TIOCSCTTY
- int n;
-#endif
+ int n, signo, status;
recvsig = 0;
/* Close unused fds. */
close(script_fds[SFD_MASTER]);
- close(script_fds[SFD_LOG]);
- close(script_fds[SFD_OUTPUT]);
- close(script_fds[SFD_TIMING]);
close(script_fds[SFD_USERTTY]);
/* Reset signal handlers. */
sigaction(SIGTTIN, &sa, NULL);
sigaction(SIGTTOU, &sa, NULL);
- /* We want SIGCHLD to interrupt us. */
- sa.sa_flags = 0; /* do not restart syscalls for these signals. */
+ /* SIGCHLD will interrupt select. */
sa.sa_handler = handler;
sigaction(SIGCHLD, &sa, NULL);
#ifdef HAVE_SETSID
if (setsid() == -1) {
warning("setsid");
- _exit(1);
+ goto bad;
}
#else
# ifdef TIOCNOTTY
#endif
#ifdef TIOCSCTTY
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
/* Set controlling tty by reopening slave. */
if ((n = open(slavename, O_RDWR)) >= 0)
foreground = 0;
/* Start command and wait for it to stop or exit */
+ /* XXX - use details->timeout */
child = fork();
if (child == -1) {
warning("Can't fork");
- _exit(1);
+ goto bad;
}
if (child == 0) {
/* Reset signal handlers. */
sigaction(SIGUSR2, &sa, NULL);
/* setup tty and exec command */
- script_run(path, argv, rbac_enabled);
- warning("unable to execute %s", path);
- _exit(127);
+ script_run(path, argv, envp, rbac);
+ warning("unable to execute %s", path); /* XXX - leave this to plugin? */
+ goto bad;
}
/*
}
/* 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 (;;) {
/* Read child status, assumes recvsig can only be SIGCHLD */
while (recvsig) {
else
warningx("command exited?");
#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 */
#ifdef SCRIPT_DEBUG
warningx("sent signo to parent");
#endif
if (!WIFSTOPPED(status)) {
+ /* XXX */
_exit(1); /* child dead */
}
}
}
- nread = read(backchannel, &signo, sizeof(signo));
- if (nread == -1) {
- if (errno != EINTR)
- break; /* XXX - error, kill child and exit */
- continue;
+ n = select(backchannel + 1, fdsr, NULL, NULL, NULL);
+ if (n == -1) {
+ if (errno == EINTR)
+ 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;
}
+ if (cstat.type != CMD_SIGNO) {
+ warningx("unexpected reply type on backchannel: %d", cstat.type);
+ continue;
+ }
+ signo = cstat.val;
/* Handle signal from parent. */
#ifdef SCRIPT_DEBUG
#endif
switch (signo) {
case SIGKILL:
- _exit(1);
+ _exit(1); /* XXX */
/* NOTREACHED */
case SIGHUP:
case SIGTERM:
}
}
- _exit(1);
+ _exit(1); /* XXX */
+
+bad:
+ return errno;
}
static void
-flush_output(output, then, now, ofile, tfile)
- struct script_buf *output;
- struct timeval *then;
- struct timeval *now;
- union script_fd ofile;
- union script_fd tfile;
+flush_output(struct script_buf *output)
{
int n;
n = read(script_fds[SFD_MASTER], output->buf, sizeof(output->buf));
if (n <= 0)
break;
- log_output(output->buf, n, &then, &now, ofile, tfile);
+ /* XXX */
+ log_output(output->buf, n);
output->off = 0;
output->len = n;
do {
}
static void
-script_run(path, argv, rbac_enabled)
- char *path;
- char *argv[];
- int rbac_enabled;
+script_run(const char *path, char *argv[], char *envp[], int rbac_enabled)
{
pid_t self = getpid();
#ifdef HAVE_SELINUX
if (rbac_enabled)
- selinux_execv(path, argv);
+ selinux_execve(path, argv, envp);
else
#endif
- execv(path, argv);
+ execve(path, argv, envp);
}
static void
-sync_winsize(src, dst)
- int src;
- int dst;
+sync_winsize(int src, int dst)
{
#ifdef TIOCGWINSZ
struct winsize win;
* Handler for SIGCHLD in parent
*/
static void
-sigchild(s)
- int s;
+sigchild(int s)
{
pid_t pid;
int serrno = errno;
* Generic handler for signals passed from parent -> child
*/
static void
-handler(s)
- int s;
+handler(int s)
{
recvsig = s;
}
* Handler for SIGWINCH in parent
*/
static void
-sigwinch(s)
- int s;
+sigwinch(int s)
{
int serrno = errno;
--- /dev/null
+/*
+ * Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+#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 */
#include "sudo.h"
+/* XXX */
+char *user_askpass = NULL;
+
static volatile sig_atomic_t signo;
static void handler __P((int));
(void) sigaction(SIGTTIN, &sa, &savettin);
(void) sigaction(SIGTTOU, &sa, &savettou);
- if (def_pwfeedback)
+ if (ISSET(flags, TGP_FEEDBACK))
neednl = term_cbreak(input);
else
neednl = term_noecho(input);
if (timeout > 0)
alarm(timeout);
- pass = getln(input, buf, sizeof(buf), def_pwfeedback);
+ pass = getln(input, buf, sizeof(buf), ISSET(flags, TGP_FEEDBACK));
alarm(0);
save_errno = errno;
if (pid == 0) {
/* child, point stdout to output side of the pipe and exec askpass */
(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);
execl(user_askpass, user_askpass, prompt, (char *)NULL);
warning("unable to run %s", user_askpass);