From: Todd C. Miller Date: Sat, 20 Feb 2010 14:41:49 +0000 (-0500) Subject: Modular sudo front-end which loads policy and I/O plugins that do X-Git-Tag: SUDO_1_8_0~891 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b6a4cf7233e93f8f4c1dcc9b569a9388463ffffc;p=sudo Modular sudo front-end which loads policy and I/O plugins that do most the actual work. Currently relies on dynamic loading using dlopen(). See doc/plugin.pod for the plugin API. --- diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..96246e8b5 --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +# +# Copyright (c) 2010 Todd C. Miller +# +# 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 diff --git a/configure b/configure index 2077d3682..55a6341a9 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /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 . # @@ -724,10 +724,11 @@ SHELL=${CONFIG_SHELL-/bin/sh} # 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 @@ -830,6 +831,7 @@ mansectform mansrcdir NOEXECFILE NOEXECDIR +PLUGINDIR noexec_file INSTALL_NOEXEC DONT_LEAK_PATH_INFO @@ -1418,7 +1420,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # 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]... @@ -1483,7 +1485,7 @@ fi 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 @@ -1602,6 +1604,7 @@ Optional Packages: (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 @@ -1688,7 +1691,7 @@ fi 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, @@ -1702,7 +1705,7 @@ cat >config.log <<_ACEOF 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 $@ @@ -2127,6 +2130,7 @@ echo "$as_me: Configuring Sudo version $PACKAGE_VERSION" >&6;} + timeout=5 @@ -2193,6 +2197,8 @@ test "$sysconfdir" = '${prefix}/etc' -a X"$with_stow" != X"yes" && sysconfdir='/ + + # Check whether --with-otp-only was given. if test "${with_otp_only+set}" = set; then withval=$with_otp_only; case $with_otp_only in @@ -3847,6 +3853,23 @@ fi fi +PLUGINDIR="$libexecdir" + +# Check whether --with-plugin_dir was given. +if test "${with_plugin_dir+set}" = set; then + withval=$with_plugin_dir; case $with_plugin_dir in + yes) ;; + no) ;; + *) PLUGINDIR="$with_plugin_dir" + ;; +esac +fi + +cat >>confdefs.h <&5 echo $ECHO_N "checking whether to do user authentication by default... $ECHO_C" >&6; } @@ -6471,7 +6494,7 @@ ia64-*-hpux*) ;; *-*-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=$? @@ -8335,11 +8358,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me: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. @@ -8625,11 +8648,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me: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. @@ -8729,11 +8752,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me: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 @@ -11089,7 +11112,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext < conftest.$ac_ext <confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -26580,7 +26603,7 @@ exec 6>&1 # report actual input values of CONFIG_FILES etc. instead of their # 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 @@ -26629,7 +26652,7 @@ Report bugs to ." _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'`\\" @@ -26738,13 +26761,9 @@ do 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;} @@ -26872,6 +26891,7 @@ mansectform!$mansectform$ac_delim 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 @@ -26902,7 +26922,6 @@ mailsub!$mailsub$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 @@ -26944,6 +26963,7 @@ _ACEOF 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 @@ -26989,7 +27009,7 @@ KRB5CONFIG!$KRB5CONFIG$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 diff --git a/configure.in b/configure.in index 765240ab4..1a15fb456 100644 --- a/configure.in +++ b/configure.in @@ -3,7 +3,7 @@ dnl Process this file with GNU autoconf to produce a configure script. dnl dnl Copyright (c) 1994-1996,1998-2009 Todd C. Miller 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 @@ -42,6 +42,7 @@ AC_SUBST(mansectform) 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) @@ -161,6 +162,11 @@ test "$bindir" = '${exec_prefix}/bin' && bindir='$(exec_prefix)/bin' 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 @@ -1061,6 +1067,16 @@ if test X"$with_libvas" != X"no"; then 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 @@ -2701,7 +2717,8 @@ fi 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 diff --git a/plugins/sudoers/install-sh b/install-sh similarity index 100% rename from plugins/sudoers/install-sh rename to install-sh diff --git a/pathnames.h.in b/pathnames.h.in index ccacfb49f..8402c1585 100644 --- a/pathnames.h.in +++ b/pathnames.h.in @@ -47,6 +47,13 @@ #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. */ @@ -94,6 +101,10 @@ #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 */ diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 000000000..77a8a6c12 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,208 @@ +# +# Copyright (c) 2010 Todd C. Miller +# +# 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 diff --git a/src/conversation.c b/src/conversation.c new file mode 100644 index 000000000..16d83b2fd --- /dev/null +++ b/src/conversation.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 1999-2005, 2007-2009 Todd C. Miller + * + * 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 + +#include +#include +#include +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif /* STDC_HEADERS */ +#ifdef HAVE_STRING_H +# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS) +# include +# endif +# include +#else +# ifdef HAVE_STRINGS_H +# include +# endif +#endif /* HAVE_STRING_H */ +#ifdef HAVE_UNISTD_H +# include +#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); +} diff --git a/src/fileops.c b/src/fileops.c index 1d119ced7..5e9d31dd6 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -44,7 +44,7 @@ # include #endif #ifndef HAVE_TIMESPEC -# include +# include #endif #include "sudo.h" diff --git a/src/fmt_string.c b/src/fmt_string.c new file mode 100644 index 000000000..e2aabb6b1 --- /dev/null +++ b/src/fmt_string.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2010 Todd C. Miller + * + * 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 + +#include +#include + +#include +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif /* STDC_HEADERS */ +#ifdef HAVE_STRING_H +# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS) +# include +# endif +# include +#else +# ifdef HAVE_STRINGS_H +# include +# 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); +} diff --git a/src/load_plugins.c b/src/load_plugins.c new file mode 100644 index 000000000..fc3cbc9b1 --- /dev/null +++ b/src/load_plugins.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2009 Todd C. Miller + * + * 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 + +#include +#include +#include +#include +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif /* STDC_HEADERS */ +#ifdef HAVE_STRING_H +# include +#else +# ifdef HAVE_STRINGS_H +# include +# endif +#endif /* HAVE_STRING_H */ +#ifdef HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ +#include +#include + +#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"); +} diff --git a/src/parse_args.c b/src/parse_args.c new file mode 100644 index 000000000..9ff4582cc --- /dev/null +++ b/src/parse_args.c @@ -0,0 +1,431 @@ +/* + * Copyright (c) 1993-1996, 1998-2009 Todd C. Miller + * + * 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 + +/* XXX - prune this */ + +#include +#include + +#include +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif /* STDC_HEADERS */ +#ifdef HAVE_STRING_H +# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS) +# include +# endif +# include +#else +# ifdef HAVE_STRINGS_H +# include +# endif +#endif /* HAVE_STRING_H */ +#ifdef HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ +#include +#include + +#include +#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); +} diff --git a/src/pty.c b/src/pty.c index 7d4f15a9d..bbb22f4e5 100644 --- a/src/pty.c +++ b/src/pty.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009 Todd C. Miller + * Copyright (c) 2009-2010 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -58,31 +58,23 @@ #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; @@ -95,7 +87,7 @@ get_pty(master, slave, name, namesz) close(*master); return(0); } - (void) chown(line, runas_pw->pw_uid, -1); + (void) chown(line, uid, -1); strlcpy(name, line, namesz); return(1); } @@ -117,11 +109,7 @@ posix_openpt(oflag) # 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; @@ -148,7 +136,7 @@ get_pty(master, slave, name, namesz) 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); } @@ -158,17 +146,13 @@ get_pty(master, slave, name, namesz) 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++) { @@ -182,7 +166,7 @@ get_pty(master, slave, name, namesz) 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); diff --git a/src/script.c b/src/script.c index 91c92ad8c..7c022ce9b 100644 --- a/src/script.c +++ b/src/script.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009 Todd C. Miller + * Copyright (c) 2009-2010 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -33,7 +33,7 @@ #endif /* HAVE_TERMIOS_H */ #include #ifdef HAVE_SYS_SELECT_H -#include +# include #endif /* HAVE_SYS_SELECT_H */ #include #ifdef STDC_HEADERS @@ -68,39 +68,29 @@ #ifdef HAVE_SELINUX # include #endif -#ifdef HAVE_ZLIB -# include -#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; @@ -114,247 +104,68 @@ static int foreground; static char slavename[PATH_MAX]; -static int suspend_parent __P((int signo, struct script_buf *output, - struct timeval *then, struct timeval *now, union script_fd ofile, - union script_fd tfile)); -static void flush_output __P((struct script_buf *output, struct timeval *then, - struct timeval *now, union script_fd ofile, union script_fd tfile)); -static void handler __P((int s)); -static void script_child __P((char *path, char *argv[], int, int)); -static void script_run __P((char *path, char *argv[], int)); -static void sigchild __P((int s)); -static void sigwinch __P((int s)); -static void sync_winsize __P((int src, int dst)); - -extern int get_pty __P((int *master, int *slave, char *name, size_t namesz)); - -/* - * TODO: run monitor as root? - */ - -static int -fdcompar(v1, v2) - const void *v1; - const void *v2; -{ - int i = *(int *)v1; - int j = *(int *)v2; - - return(script_fds[i] - script_fds[j]); -} - -void -script_nextid() -{ - struct stat sb; - char buf[32], *ep; - int fd, i, ch; - unsigned long id = 0; - int len; - ssize_t nread; - char pathbuf[PATH_MAX]; - - /* - * Create _PATH_SUDO_TRANSCRIPT if it doesn't already exist. - */ - if (stat(_PATH_SUDO_TRANSCRIPT, &sb) != 0) { - if (mkdir(_PATH_SUDO_TRANSCRIPT, S_IRWXU) != 0) - log_error(USE_ERRNO, "Can't mkdir %s", _PATH_SUDO_TRANSCRIPT); - } else if (!S_ISDIR(sb.st_mode)) { - log_error(0, "%s exists but is not a directory (0%o)", - _PATH_SUDO_TRANSCRIPT, (unsigned int) sb.st_mode); - } - - /* - * Open sequence file - */ - len = snprintf(pathbuf, sizeof(pathbuf), "%s/seq", _PATH_SUDO_TRANSCRIPT); - if (len <= 0 || len >= sizeof(pathbuf)) { - errno = ENAMETOOLONG; - log_error(USE_ERRNO, "%s/seq", pathbuf); - } - fd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR); - if (fd == -1) - log_error(USE_ERRNO, "cannot open %s", pathbuf); - lock_file(fd, SUDO_LOCK); - - /* Read seq number (base 36). */ - nread = read(fd, buf, sizeof(buf)); - if (nread != 0) { - if (nread == -1) - log_error(USE_ERRNO, "cannot read %s", pathbuf); - id = strtoul(buf, &ep, 36); - if (buf == ep || id >= 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) { @@ -370,13 +181,7 @@ check_foreground() * 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; @@ -404,7 +209,7 @@ suspend_parent(signo, output, then, now, ofile, tfile) /* 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) { @@ -469,19 +274,18 @@ suspend_parent(signo, output, then, now, ofile, tfile) * 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; @@ -491,7 +295,7 @@ script_execv(path, argv) 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 @@ -542,8 +346,8 @@ script_execv(path, argv) * 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. */ @@ -559,7 +363,7 @@ script_execv(path, argv) 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"); } /* @@ -569,43 +373,22 @@ script_execv(path, argv) 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; @@ -622,13 +405,19 @@ script_execv(path, argv) (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) { @@ -643,8 +432,8 @@ script_execv(path, argv) 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); @@ -658,51 +447,50 @@ script_execv(path, argv) 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 */ } } @@ -717,6 +505,7 @@ script_execv(path, argv) } else { if (n == 0) break; /* got EOF */ + log_input(input.buf + input.len, n); input.len += n; } } @@ -733,7 +522,6 @@ script_execv(path, argv) } } 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) { @@ -744,9 +532,7 @@ script_execv(path, argv) } 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; } } @@ -770,26 +556,15 @@ script_execv(path, argv) 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); } @@ -800,30 +575,22 @@ script_execv(path, argv) 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. */ @@ -840,8 +607,7 @@ script_child(path, argv, backchannel, rbac_enabled) 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); @@ -853,7 +619,7 @@ script_child(path, argv, backchannel, rbac_enabled) #ifdef HAVE_SETSID if (setsid() == -1) { warning("setsid"); - _exit(1); + goto bad; } #else # ifdef TIOCNOTTY @@ -869,7 +635,7 @@ script_child(path, argv, backchannel, rbac_enabled) #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) @@ -880,10 +646,11 @@ script_child(path, argv, backchannel, rbac_enabled) 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. */ @@ -900,9 +667,9 @@ script_child(path, argv, backchannel, rbac_enabled) 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; } /* @@ -917,6 +684,9 @@ script_child(path, argv, backchannel, rbac_enabled) } /* 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) { @@ -934,26 +704,43 @@ script_child(path, argv, backchannel, rbac_enabled) 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 @@ -961,7 +748,7 @@ script_child(path, argv, backchannel, rbac_enabled) #endif switch (signo) { case SIGKILL: - _exit(1); + _exit(1); /* XXX */ /* NOTREACHED */ case SIGHUP: case SIGTERM: @@ -991,16 +778,14 @@ script_child(path, argv, backchannel, rbac_enabled) } } - _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; @@ -1017,7 +802,8 @@ flush_output(output, then, now, ofile, tfile) 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 { @@ -1031,10 +817,7 @@ flush_output(output, then, now, ofile, tfile) } 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(); @@ -1058,16 +841,14 @@ script_run(path, argv, rbac_enabled) #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; @@ -1087,8 +868,7 @@ sync_winsize(src, dst) * Handler for SIGCHLD in parent */ static void -sigchild(s) - int s; +sigchild(int s) { pid_t pid; int serrno = errno; @@ -1110,8 +890,7 @@ sigchild(s) * Generic handler for signals passed from parent -> child */ static void -handler(s) - int s; +handler(int s) { recvsig = s; } @@ -1120,8 +899,7 @@ handler(s) * Handler for SIGWINCH in parent */ static void -sigwinch(s) - int s; +sigwinch(int s) { int serrno = errno; diff --git a/src/sudo.c b/src/sudo.c new file mode 100644 index 000000000..293c55bde --- /dev/null +++ b/src/sudo.c @@ -0,0 +1,893 @@ +/* + * Copyright (c) 2009-2010 Todd C. Miller + * + * 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 +#endif + +#include + +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_SELECT_H +# include +#endif /* HAVE_SYS_SELECT_H */ +#ifdef HAVE_SETRLIMIT +# include +# include +#endif +#include +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif /* STDC_HEADERS */ +#ifdef HAVE_STRING_H +# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS) +# include +# endif +# include +#else +# ifdef HAVE_STRINGS_H +# include +# endif +#endif /* HAVE_STRING_H */ +#ifdef HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ +#include +#include +#include +#include +#include +#include +#if TIME_WITH_SYS_TIME +# include +#endif +#ifdef HAVE_SETLOCALE +# include +#endif +#ifdef HAVE_LOGIN_CAP_H +# include +#endif + +#include +#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 diff --git a/src/sudo.h b/src/sudo.h new file mode 100644 index 000000000..cce93f07a --- /dev/null +++ b/src/sudo.h @@ -0,0 +1,234 @@ +/* + * Copyright (c) 1993-1996, 1998-2005, 2007-2009 + * Todd C. Miller + * + * 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 +#include +#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 */ diff --git a/src/sudo_plugin_int.h b/src/sudo_plugin_int.h new file mode 100644 index 000000000..3d1b802eb --- /dev/null +++ b/src/sudo_plugin_int.h @@ -0,0 +1,39 @@ +#ifndef _SUDO_PLUGIN_INT_H +#define _SUDO_PLUGIN_INT_H + +/* + * Sudo plugin internals. + */ + +struct plugin_info { + struct plugin_info *prev; /* required */ + struct plugin_info *next; /* required */ + const char *path; + const char *symbol_name; +}; +TQ_DECLARE(plugin_info) + +struct plugin_container { + struct plugin_container *prev; /* required */ + struct plugin_container *next; /* required */ + const char *name; + void *handle; + union { + struct generic_plugin *generic; + struct policy_plugin *policy; + struct io_plugin *io; + } u; +}; +TQ_DECLARE(plugin_container) + +extern struct plugin_container_list policy_plugins; +extern struct plugin_container_list io_plugins; + +int sudo_conversation(int num_msgs, const struct sudo_conv_message msgs[], + struct sudo_conv_reply replies[]); + +void sudo_load_plugins(const char *conf_file, + struct plugin_container *policy_plugin, + struct plugin_container_list *io_plugins); + +#endif /* _SUDO_PLUGIN_INT_H */ diff --git a/sudo_usage.h.in b/src/sudo_usage.h.in similarity index 100% rename from sudo_usage.h.in rename to src/sudo_usage.h.in diff --git a/src/tgetpass.c b/src/tgetpass.c index 3c51f2862..0b264cd01 100644 --- a/src/tgetpass.c +++ b/src/tgetpass.c @@ -56,6 +56,9 @@ #include "sudo.h" +/* XXX */ +char *user_askpass = NULL; + static volatile sig_atomic_t signo; static void handler __P((int)); @@ -111,7 +114,7 @@ restart: (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); @@ -123,7 +126,7 @@ restart: 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; @@ -184,7 +187,8 @@ sudo_askpass(prompt) 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);