Add regress for noexec functionality

This commit is contained in:
Todd C. Miller
2016-11-14 14:21:08 -07:00
parent 068f96a213
commit 66af45eb24
8 changed files with 240 additions and 11 deletions

View File

@@ -577,6 +577,7 @@ src/openbsd.c
src/parse_args.c src/parse_args.c
src/preload.c src/preload.c
src/preserve_fds.c src/preserve_fds.c
src/regress/noexec/check_noexec.c
src/regress/ttyname/check_ttyname.c src/regress/ttyname/check_ttyname.c
src/selinux.c src/selinux.c
src/sesh.c src/sesh.c

8
configure vendored
View File

@@ -750,6 +750,7 @@ LDAP
SELINUX_USAGE SELINUX_USAGE
BSDAUTH_USAGE BSDAUTH_USAGE
DONT_LEAK_PATH_INFO DONT_LEAK_PATH_INFO
CHECK_NOEXEC
INSTALL_NOEXEC INSTALL_NOEXEC
INSTALL_BACKUP INSTALL_BACKUP
sesh_file 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_BACKUP=
INSTALL_NOEXEC= INSTALL_NOEXEC=
CHECK_NOEXEC=
exampledir='$(docdir)/examples' exampledir='$(docdir)/examples'
devdir='$(srcdir)' devdir='$(srcdir)'
PROGS="sudo" 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" PROGS="${PROGS} sudo_noexec.la"
INSTALL_NOEXEC="install-noexec" 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="$with_noexec"
_noexec_file= _noexec_file=
while test X"$noexec_file" != X"$_noexec_file"; do while test X"$noexec_file" != X"$_noexec_file"; do

View File

@@ -65,6 +65,7 @@ AC_SUBST([noexec_file])
AC_SUBST([sesh_file]) AC_SUBST([sesh_file])
AC_SUBST([INSTALL_BACKUP]) AC_SUBST([INSTALL_BACKUP])
AC_SUBST([INSTALL_NOEXEC]) AC_SUBST([INSTALL_NOEXEC])
AC_SUBST([CHECK_NOEXEC])
AC_SUBST([DONT_LEAK_PATH_INFO]) AC_SUBST([DONT_LEAK_PATH_INFO])
AC_SUBST([BSDAUTH_USAGE]) AC_SUBST([BSDAUTH_USAGE])
AC_SUBST([SELINUX_USAGE]) AC_SUBST([SELINUX_USAGE])
@@ -189,6 +190,7 @@ dnl May be overridden by environment variables..
dnl dnl
INSTALL_BACKUP= INSTALL_BACKUP=
INSTALL_NOEXEC= INSTALL_NOEXEC=
CHECK_NOEXEC=
exampledir='$(docdir)/examples' exampledir='$(docdir)/examples'
devdir='$(srcdir)' devdir='$(srcdir)'
PROGS="sudo" 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" PROGS="${PROGS} sudo_noexec.la"
INSTALL_NOEXEC="install-noexec" 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="$with_noexec"
_noexec_file= _noexec_file=
while test X"$noexec_file" != X"$_noexec_file"; do while test X"$noexec_file" != X"$_noexec_file"; do

View File

@@ -459,13 +459,11 @@ sudo_conf_sesh_path_v1(void)
return sudo_conf_paths.sesh; return sudo_conf_paths.sesh;
} }
#ifdef _PATH_SUDO_NOEXEC
const char * const char *
sudo_conf_noexec_path_v1(void) sudo_conf_noexec_path_v1(void)
{ {
return sudo_conf_paths.noexec; return sudo_conf_paths.noexec;
} }
#endif
#ifdef _PATH_SUDO_PLUGIN_DIR #ifdef _PATH_SUDO_PLUGIN_DIR
const char * const char *

View 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 SESH_OBJS = sesh.o exec_common.o
CHECK_NOEXEC_OBJS = check_noexec.o exec_common.o
CHECK_TTYNAME_OBJS = check_ttyname.o ttyname.o CHECK_TTYNAME_OBJS = check_ttyname.o ttyname.o
LIBOBJDIR = $(top_builddir)/@ac_config_libobj_dir@/ LIBOBJDIR = $(top_builddir)/@ac_config_libobj_dir@/
@@ -155,6 +157,9 @@ sudo_noexec.la: libsudo_noexec.la
sesh: $(SESH_OBJS) $(LT_LIBS) sesh: $(SESH_OBJS) $(LT_LIBS)
$(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(SESH_OBJS) $(LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(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 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) $(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 $(CPPCHECK_OPTS) -I$(incdir) -I$(top_builddir) -I. -I$(srcdir) -I$(top_srcdir) $(srcdir)/*.c 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 \ @if test X"$(cross_compiling)" != X"yes"; then \
./check_ttyname; \ ./check_ttyname; \
fi fi
check_sudo_noexec: sudo_noexec.la check_noexec
@if test X"$(cross_compiling)" != X"yes"; then \
./check_noexec .libs/$(noexecfile); \
fi
clean: clean:
-$(LIBTOOL) $(LTFLAGS) --mode=clean rm -f $(PROGS) $(TEST_PROGS) \ -$(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 mostlyclean: clean
@@ -239,6 +249,11 @@ realclean: distclean
cleandir: realclean cleandir: realclean
# Autogenerated dependencies, do not modify # 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 \ check_ttyname.o: $(srcdir)/regress/ttyname/check_ttyname.c \
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \ $(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \

View 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 * to run. On systems with privilege sets, we can remove the exec
* privilege. On other systems we use LD_PRELOAD and the like. * privilege. On other systems we use LD_PRELOAD and the like.
*/ */
static char ** char **
disable_execute(char *envp[]) disable_execute(char *envp[], const char *dso)
{ {
debug_decl(disable_execute, SUDO_DEBUG_UTIL) debug_decl(disable_execute, SUDO_DEBUG_UTIL)
@@ -167,8 +167,8 @@ disable_execute(char *envp[])
#endif /* HAVE_PRIV_SET */ #endif /* HAVE_PRIV_SET */
#ifdef _PATH_SUDO_NOEXEC #ifdef _PATH_SUDO_NOEXEC
if (sudo_conf_noexec_path() != NULL) if (dso != NULL)
envp = preload_dso(envp, sudo_conf_noexec_path()); envp = preload_dso(envp, dso);
#endif /* _PATH_SUDO_NOEXEC */ #endif /* _PATH_SUDO_NOEXEC */
debug_return_ptr(envp); 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(). */ /* Modify the environment as needed to disable further execve(). */
if (noexec) if (noexec)
envp = disable_execute(envp); envp = disable_execute(envp, sudo_conf_noexec_path());
#ifdef HAVE_FEXECVE #ifdef HAVE_FEXECVE
if (fd != -1) if (fd != -1)

View File

@@ -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;
}

View File

@@ -73,11 +73,14 @@
*/ */
/* exec.c */ /* 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; 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 */ /* exec_pty.c */
struct sudo_event_base;
struct command_details; struct command_details;
struct command_status; struct command_status;
int fork_pty(struct command_details *details, int sv[], sigset_t *omask); int fork_pty(struct command_details *details, int sv[], sigset_t *omask);