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@/
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)
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
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 \
* 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)
#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);
/* 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)
--- /dev/null
+/*
+ * 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;
+}