]> granicus.if.org Git - sudo/commitdiff
Add regress for noexec functionality
authorTodd C. Miller <Todd.Miller@courtesan.com>
Mon, 14 Nov 2016 21:21:08 +0000 (14:21 -0700)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Mon, 14 Nov 2016 21:21:08 +0000 (14:21 -0700)
MANIFEST
configure
configure.ac
lib/util/sudo_conf.c
src/Makefile.in
src/exec_common.c
src/regress/noexec/check_noexec.c [new file with mode: 0644]
src/sudo_exec.h

index 564a1e4c26b59edf9c46738eb983867d0c502cce..fc65b619c88e91c4d589c19c5bff2085fe22b2f8 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -577,6 +577,7 @@ src/openbsd.c
 src/parse_args.c
 src/preload.c
 src/preserve_fds.c
+src/regress/noexec/check_noexec.c
 src/regress/ttyname/check_ttyname.c
 src/selinux.c
 src/sesh.c
index 8be0338add0db98c6f07b58ab81ceced78153bd3..2a10f9dd04c27a7b8fc06d0728c13b5ff531fd05 100755 (executable)
--- a/configure
+++ b/configure
@@ -750,6 +750,7 @@ LDAP
 SELINUX_USAGE
 BSDAUTH_USAGE
 DONT_LEAK_PATH_INFO
+CHECK_NOEXEC
 INSTALL_NOEXEC
 INSTALL_BACKUP
 sesh_file
@@ -2855,6 +2856,7 @@ $as_echo "$as_me: Configuring Sudo version $PACKAGE_VERSION" >&6;}
 
 
 
+
 
 
 #
@@ -2906,6 +2908,7 @@ PLUGINDIR=/usr/local/libexec/sudo
 #
 INSTALL_BACKUP=
 INSTALL_NOEXEC=
+CHECK_NOEXEC=
 exampledir='$(docdir)/examples'
 devdir='$(srcdir)'
 PROGS="sudo"
@@ -24406,6 +24409,11 @@ if test X"$with_noexec" != X"no" -o X"$with_selinux" != X"no" -o "$enabled_share
        PROGS="${PROGS} sudo_noexec.la"
        INSTALL_NOEXEC="install-noexec"
 
+       # Can't use asan with LD_PRELOAD
+       if test "$enable_asan" != "yes"; then
+           CHECK_NOEXEC=check_sudo_noexec
+       fi
+
        noexec_file="$with_noexec"
        _noexec_file=
        while test X"$noexec_file" != X"$_noexec_file"; do
index 438d25cf8ca4488f3c993344a7df687d93dd4d32..e3b284be888f1f3d99195c546583588b59868493 100644 (file)
@@ -65,6 +65,7 @@ AC_SUBST([noexec_file])
 AC_SUBST([sesh_file])
 AC_SUBST([INSTALL_BACKUP])
 AC_SUBST([INSTALL_NOEXEC])
+AC_SUBST([CHECK_NOEXEC])
 AC_SUBST([DONT_LEAK_PATH_INFO])
 AC_SUBST([BSDAUTH_USAGE])
 AC_SUBST([SELINUX_USAGE])
@@ -189,6 +190,7 @@ dnl May be overridden by environment variables..
 dnl
 INSTALL_BACKUP=
 INSTALL_NOEXEC=
+CHECK_NOEXEC=
 exampledir='$(docdir)/examples'
 devdir='$(srcdir)'
 PROGS="sudo"
@@ -4180,6 +4182,11 @@ if test X"$with_noexec" != X"no" -o X"$with_selinux" != X"no" -o "$enabled_share
        PROGS="${PROGS} sudo_noexec.la"
        INSTALL_NOEXEC="install-noexec"
 
+       # Can't use asan with LD_PRELOAD
+       if test "$enable_asan" != "yes"; then
+           CHECK_NOEXEC=check_sudo_noexec
+       fi
+
        noexec_file="$with_noexec"
        _noexec_file=
        while test X"$noexec_file" != X"$_noexec_file"; do
index b91dbac79c8edf42c9850cb57f06c4aaa1ef93b5..cd08b0e6d06246e09d817becb6a1ce0742577d4f 100644 (file)
@@ -459,13 +459,11 @@ sudo_conf_sesh_path_v1(void)
     return sudo_conf_paths.sesh;
 }
 
-#ifdef _PATH_SUDO_NOEXEC
 const char *
 sudo_conf_noexec_path_v1(void)
 {
     return sudo_conf_paths.noexec;
 }
-#endif
 
 #ifdef _PATH_SUDO_PLUGIN_DIR
 const char *
index bcc968ad84b9524c51706d2033e68aaa13249725..897dbdf89b21aca3e868c52f84121682f5644859 100644 (file)
@@ -117,6 +117,8 @@ OBJS = conversation.o env_hooks.o exec.o exec_common.o exec_pty.o \
 
 SESH_OBJS = sesh.o exec_common.o
 
+CHECK_NOEXEC_OBJS = check_noexec.o exec_common.o
+
 CHECK_TTYNAME_OBJS = check_ttyname.o ttyname.o
 
 LIBOBJDIR = $(top_builddir)/@ac_config_libobj_dir@/
@@ -155,6 +157,9 @@ sudo_noexec.la: libsudo_noexec.la
 sesh: $(SESH_OBJS) $(LT_LIBS)
        $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(SESH_OBJS) $(LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS)
 
+check_noexec: $(CHECK_NOEXEC_OBJS) $(top_builddir)/lib/util/libsudo_util.la
+       $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_NOEXEC_OBJS) $(TEST_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(TEST_LIBS)
+
 check_ttyname: $(CHECK_TTYNAME_OBJS) $(top_builddir)/lib/util/libsudo_util.la
        $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_TTYNAME_OBJS) $(TEST_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(TEST_LIBS)
 
@@ -217,14 +222,19 @@ splint:
 cppcheck:
        cppcheck $(CPPCHECK_OPTS) -I$(incdir) -I$(top_builddir) -I. -I$(srcdir) -I$(top_srcdir) $(srcdir)/*.c
 
-check: $(TEST_PROGS)
+check: $(TEST_PROGS) @CHECK_NOEXEC@
        @if test X"$(cross_compiling)" != X"yes"; then \
            ./check_ttyname; \
        fi
 
+check_sudo_noexec: sudo_noexec.la check_noexec
+       @if test X"$(cross_compiling)" != X"yes"; then \
+           ./check_noexec .libs/$(noexecfile); \
+       fi
+
 clean:
        -$(LIBTOOL) $(LTFLAGS) --mode=clean rm -f $(PROGS) $(TEST_PROGS) \
-           *.lo *.o *.la *.a stamp-* core *.core core.*
+           *.lo *.o *.la *.a stamp-* core *.core core.* nohup.out
 
 mostlyclean: clean
 
@@ -239,6 +249,11 @@ realclean: distclean
 cleandir: realclean
 
 # Autogenerated dependencies, do not modify
+check_noexec.o: $(srcdir)/regress/noexec/check_noexec.c \
+                $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
+                $(incdir)/sudo_fatal.h $(incdir)/sudo_util.h \
+                $(top_builddir)/config.h
+       $(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/noexec/check_noexec.c
 check_ttyname.o: $(srcdir)/regress/ttyname/check_ttyname.c \
                  $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
                  $(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \
index 78ad7adf79013dabbc37f3acf7aba98b30281f0f..9dd2b2b8c9899e44f47610a22c12a9325c5b3c8c 100644 (file)
@@ -151,8 +151,8 @@ preload_dso(char *envp[], const char *dso_file)
  * to run.  On systems with privilege sets, we can remove the exec
  * privilege.  On other systems we use LD_PRELOAD and the like.
  */
-static char **
-disable_execute(char *envp[])
+char **
+disable_execute(char *envp[], const char *dso)
 {
     debug_decl(disable_execute, SUDO_DEBUG_UTIL)
 
@@ -167,8 +167,8 @@ disable_execute(char *envp[])
 #endif /* HAVE_PRIV_SET */
 
 #ifdef _PATH_SUDO_NOEXEC
-    if (sudo_conf_noexec_path() != NULL)
-       envp = preload_dso(envp, sudo_conf_noexec_path());
+    if (dso != NULL)
+       envp = preload_dso(envp, dso);
 #endif /* _PATH_SUDO_NOEXEC */
 
     debug_return_ptr(envp);
@@ -187,7 +187,7 @@ sudo_execve(int fd, const char *path, char *const argv[], char *envp[], bool noe
 
     /* Modify the environment as needed to disable further execve(). */
     if (noexec)
-       envp = disable_execute(envp);
+       envp = disable_execute(envp, sudo_conf_noexec_path());
 
 #ifdef HAVE_FEXECVE
     if (fd != -1)
diff --git a/src/regress/noexec/check_noexec.c b/src/regress/noexec/check_noexec.c
new file mode 100644 (file)
index 0000000..c4c22d0
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2016 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# include "compat/stdbool.h"
+#endif /* HAVE_STDBOOL_H */
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif /* HAVE_STRING_H */
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif /* HAVE_STRINGS_H */
+#ifdef HAVE_WORDEXP_H
+# include <wordexp.h>
+#endif
+#include <signal.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+
+#include "sudo_compat.h"
+#include "sudo_fatal.h"
+#include "sudo_util.h"
+#include "sudo_exec.h"
+
+__dso_public int main(int argc, char *argv[], char *envp[]);
+
+static bool
+report_status(int status, const char *what)
+{
+    bool ret = false;
+
+    /* system() returns -1 for exec failure. */
+    if (status == -1) {
+       printf("%s: OK (%s)\n", getprogname(), what);
+       return true;
+    }
+
+    /* check exit value, expecting 127 for failure */
+    if (WIFEXITED(status)) {
+       int exitval = WEXITSTATUS(status);
+       if (exitval == 127) {
+           printf("%s: OK (%s)\n", getprogname(), what);
+           ret = true;
+       } else {
+           printf("%s: FAIL (%s) [%d]\n", getprogname(), what, exitval);
+       }
+    } else if (WIFSIGNALED(status)) {
+       printf("%s: FAIL (%s) [signal %d]\n", getprogname(), what,
+           WTERMSIG(status));
+    } else {
+       /* should not happen */
+       printf("%s: FAIL (%s) [status %d]\n", getprogname(), what, status);
+    }
+
+    return ret;
+}
+
+static int
+try_execl(void)
+{
+    pid_t child, pid;
+    int status;
+
+    child = fork();
+    switch (child) {
+    case -1:
+       sudo_fatal_nodebug("fork");
+    case 0:
+       /* child */
+       /* Try to exec /bin/true, else exit with value 127. */
+       execl("/bin/true", "true", (char *)0);
+       _exit(127);
+    default:
+       /* parent */
+       do {
+           pid = waitpid(child, &status, 0);
+       } while (pid == -1 && errno == EINTR);
+       if (pid == -1)
+           sudo_fatal_nodebug("waitpid");
+
+       if (report_status(status, "execl"))
+           return 0;
+       return 1;
+    }
+}
+
+static int
+try_system(void)
+{
+    int status;
+
+    /* Try to run /bin/true, system() returns 127 on exec failure. */
+    status = system("/bin/true > /dev/null 2>&1");
+
+    if (report_status(status, "system"))
+       return 0;
+    return 1;
+}
+
+#ifdef HAVE_WORDEXP_H
+static int
+try_wordexp(void)
+{
+    wordexp_t we;
+    int rc, ret = 1;
+
+    /*
+     * sudo_noexec.so prevents command substitution via the WRDE_NOCMD flag
+     * where possible.
+     */
+    rc = wordexp("$(/bin/echo foo)", &we, 0);
+    switch (rc) {
+    case -1:
+#ifdef WRDE_ERRNO
+    case WRDE_ERRNO:
+       /*
+        * Solaris returns WRDE_ERRNO for execve() failure and sudo's
+        * wordexp() wrapper returns -1 if RTLD_NEXT is not supported.
+        */
+       printf("%s: MOSTLY OK (wordexp)\n", getprogname());
+       ret = 0;
+       break;
+#endif
+    case WRDE_CMDSUB:
+       printf("%s: OK (wordexp)\n", getprogname());
+       ret = 0;
+       break;
+    case 0:
+       /*
+        * On HP-UX 11.00 we don't seem to be able to add WRDE_NOCMD
+        * but the execve() wrapper prevents the command substitution.
+        */
+       if (we.we_wordc == 0) {
+           printf("%s: MOSTLY OK (wordexp)\n", getprogname());
+           ret = 0;
+           break;
+       }
+       wordfree(&we);
+       /* FALLTHROUGH */
+    default:
+       printf("%s: FAIL (wordexp) [%d]\n", getprogname(), rc);
+       break;
+    }
+    return ret;
+}
+#endif
+
+int
+main(int argc, char *argv[], char *envp[])
+{
+    int errors = 0;
+
+    initprogname(argc > 0 ? argv[0] : "check_noexec");
+
+    if (argc != 2) {
+       fprintf(stderr, "usage: %s regress | /path/to/sudo_noexec.so\n", getprogname());
+       exit(1);
+    }
+
+    /* Disable execution for post-exec and re-exec ourself. */
+    if (strcmp(argv[1], "rexec") != 0) {
+       const char *noexec = argv[1];
+       argv[1] = "rexec";
+       execve(argv[0], argv, disable_execute(envp, noexec));
+       sudo_fatalx_nodebug("execve");
+    }
+
+    errors += try_execl();
+    errors += try_system();
+#ifdef HAVE_WORDEXP_H
+    errors += try_wordexp();
+#endif
+
+    return errors;
+}
index 0099cb09d12cfadc52042c2ac75fbad278ab0e78..6dc795cbc306eb198330e6b225ce617da1446633 100644 (file)
  */
 
 /* exec.c */
-struct sudo_event_base;
-int sudo_execve(int fd, const char *path, char *const argv[], char *envp[], bool noexec);
 extern volatile pid_t cmnd_pid;
 
+/* exec_common.c */
+int sudo_execve(int fd, const char *path, char *const argv[], char *envp[], bool noexec);
+char **disable_execute(char *envp[], const char *dso);
+
 /* exec_pty.c */
+struct sudo_event_base;
 struct command_details;
 struct command_status;
 int fork_pty(struct command_details *details, int sv[], sigset_t *omask);