diff options
-rw-r--r-- | NEWS | 11 | ||||
-rw-r--r-- | aclocal.m4 | 2 | ||||
-rw-r--r-- | config.h.in | 10 | ||||
-rwxr-xr-x | configure | 43 | ||||
-rw-r--r-- | configure.ac | 14 | ||||
-rw-r--r-- | plugins/sudoers/Makefile.in | 10 | ||||
-rw-r--r-- | src/Makefile.in | 2 | ||||
-rw-r--r-- | src/sudo_noexec.c | 114 |
8 files changed, 187 insertions, 19 deletions
@@ -1,3 +1,14 @@ +What's new in Sudo 1.8.18p1 + + * When sudo_noexec.so is used, the WRDE_NOCMD flag is now added + if the wordexp() function is called. This prevents commands + from being run via wordexp() without disabling it entirely. + + * On Linux systems, sudo_noexec.so now uses a seccomp filter to + disable execute access if the kernel supports seccomp. This is + more robust than the traditional method of using stub functions + that return an error. + What's new in Sudo 1.8.18 * The sudoers locale is now set before parsing the sudoers file. diff --git a/aclocal.m4 b/aclocal.m4 index ed555540b..c2591690e 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -13,7 +13,7 @@ m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) # longlong.m4 serial 17 -dnl Copyright (C) 1999-2007, 2009-2015 Free Software Foundation, Inc. +dnl Copyright (C) 1999-2007, 2009-2016 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. diff --git a/config.h.in b/config.h.in index f9d63a5d5..6586fb74e 100644 --- a/config.h.in +++ b/config.h.in @@ -124,6 +124,10 @@ don't. */ #undef HAVE_DECL_QUAD_MIN +/* Define to 1 if you have the declaration of `SECCOMP_SET_MODE_FILTER', and + to 0 if you don't. */ +#undef HAVE_DECL_SECCOMP_SET_MODE_FILTER + /* Define to 1 if you have the declaration of `setauthdb', and to 0 if you don't. */ #undef HAVE_DECL_SETAUTHDB @@ -838,6 +842,12 @@ /* Define to 1 if you have the `vsnprintf' function. */ #undef HAVE_VSNPRINTF +/* Define to 1 if you have the `wordexp' function. */ +#undef HAVE_WORDEXP + +/* Define to 1 if you have the <wordexp.h> header file. */ +#undef HAVE_WORDEXP_H + /* Define to 1 if you have the <zlib.h> header file. */ #undef HAVE_ZLIB_H @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for sudo 1.8.18. +# Generated by GNU Autoconf 2.69 for sudo 1.8.18p1. # # Report bugs to <https://bugzilla.sudo.ws/>. # @@ -590,8 +590,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='sudo' PACKAGE_TARNAME='sudo' -PACKAGE_VERSION='1.8.18' -PACKAGE_STRING='sudo 1.8.18' +PACKAGE_VERSION='1.8.18p1' +PACKAGE_STRING='sudo 1.8.18p1' PACKAGE_BUGREPORT='https://bugzilla.sudo.ws/' PACKAGE_URL='' @@ -1526,7 +1526,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.8.18 to adapt to many kinds of systems. +\`configure' configures sudo 1.8.18p1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1591,7 +1591,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sudo 1.8.18:";; + short | recursive ) echo "Configuration of sudo 1.8.18p1:";; esac cat <<\_ACEOF @@ -1838,7 +1838,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sudo configure 1.8.18 +sudo configure 1.8.18p1 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2364,7 +2364,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.8.18, which was +It was created by sudo $as_me 1.8.18p1, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2648,6 +2648,7 @@ as_fn_append ac_header_list " netgroup.h" as_fn_append ac_header_list " paths.h" as_fn_append ac_header_list " spawn.h" as_fn_append ac_header_list " utmpx.h" +as_fn_append ac_header_list " wordexp.h" as_fn_append ac_header_list " sys/sockio.h" as_fn_append ac_header_list " sys/bsdtypes.h" as_fn_append ac_header_list " sys/select.h" @@ -2661,6 +2662,7 @@ as_fn_append ac_func_list " pread" as_fn_append ac_func_list " pwrite" as_fn_append ac_func_list " openat" as_fn_append ac_func_list " faccessat" +as_fn_append ac_func_list " wordexp" as_fn_append ac_func_list " seteuid" # Check that the precious variables saved in the cache have kept the same # value. @@ -15221,6 +15223,25 @@ fi *-*-linux*|*-*-k*bsd*-gnu) shadow_funcs="getspnam" test -z "$with_pam" && AUTH_EXCL_DEF="PAM" + # Check for SECCOMP_SET_MODE_FILTER in linux/seccomp.h + ac_fn_c_check_decl "$LINENO" "SECCOMP_SET_MODE_FILTER" "ac_cv_have_decl_SECCOMP_SET_MODE_FILTER" " +#include <sys/types.h> +#include <sys/prctl.h> +#include <asm/unistd.h> +#include <linux/seccomp.h> +#include <linux/filter.h> + +" +if test "x$ac_cv_have_decl_SECCOMP_SET_MODE_FILTER" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_SECCOMP_SET_MODE_FILTER $ac_have_decl +_ACEOF + ;; *-*-gnu*) # lockf() is broken on the Hurd @@ -16815,6 +16836,8 @@ done + + for ac_header in endian.h sys/endian.h machine/endian.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` @@ -18072,6 +18095,8 @@ done + + case "$host_os" in hpux*) if test X"$ac_cv_func_pread" = X"yes"; then @@ -24868,7 +24893,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=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.8.18, which was +This file was extended by sudo $as_me 1.8.18p1, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -24934,7 +24959,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -sudo config.status 1.8.18 +sudo config.status 1.8.18p1 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 9feddfdd9..2d7554e35 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ dnl dnl Copyright (c) 1994-1996,1998-2016 Todd C. Miller <Todd.Miller@courtesan.com> dnl AC_PREREQ([2.59]) -AC_INIT([sudo], [1.8.18], [https://bugzilla.sudo.ws/], [sudo]) +AC_INIT([sudo], [1.8.18p1], [https://bugzilla.sudo.ws/], [sudo]) AC_CONFIG_HEADER([config.h pathnames.h]) AC_CONFIG_SRCDIR([src/sudo.c]) dnl @@ -1962,6 +1962,14 @@ case "$host" in *-*-linux*|*-*-k*bsd*-gnu) shadow_funcs="getspnam" test -z "$with_pam" && AUTH_EXCL_DEF="PAM" + # Check for SECCOMP_SET_MODE_FILTER in linux/seccomp.h + AC_CHECK_DECLS([SECCOMP_SET_MODE_FILTER], [], [], [ +#include <sys/types.h> +#include <sys/prctl.h> +#include <asm/unistd.h> +#include <linux/seccomp.h> +#include <linux/filter.h> + ]) ;; *-*-gnu*) # lockf() is broken on the Hurd @@ -2261,7 +2269,7 @@ AC_HEADER_DIRENT AC_HEADER_TIME AC_HEADER_STDBOOL AC_HEADER_MAJOR -AC_CHECK_HEADERS_ONCE([netgroup.h paths.h spawn.h utmpx.h sys/sockio.h sys/bsdtypes.h sys/select.h sys/stropts.h sys/sysmacros.h]) +AC_CHECK_HEADERS_ONCE([netgroup.h paths.h spawn.h utmpx.h wordexp.h sys/sockio.h sys/bsdtypes.h sys/select.h sys/stropts.h sys/sysmacros.h]) AC_CHECK_HEADERS([endian.h] [sys/endian.h] [machine/endian.h], [break]) AC_CHECK_HEADERS([procfs.h] [sys/procfs.h], [AC_CHECK_MEMBERS(struct psinfo.pr_ttydev, [AC_CHECK_FUNCS([_ttyname_dev])], [], [AC_INCLUDES_DEFAULT #ifdef HAVE_PROCFS_H @@ -2400,7 +2408,7 @@ dnl dnl Function checks dnl AC_FUNC_GETGROUPS -AC_CHECK_FUNCS_ONCE([fexecve killpg nl_langinfo strftime pread pwrite openat faccessat]) +AC_CHECK_FUNCS_ONCE([fexecve killpg nl_langinfo strftime pread pwrite openat faccessat wordexp]) case "$host_os" in hpux*) if test X"$ac_cv_func_pread" = X"yes"; then diff --git a/plugins/sudoers/Makefile.in b/plugins/sudoers/Makefile.in index ba7f631af..6b1a3b15d 100644 --- a/plugins/sudoers/Makefile.in +++ b/plugins/sudoers/Makefile.in @@ -466,7 +466,7 @@ check: $(TEST_PROGS) visudo testsudoers clean: -$(LIBTOOL) $(LTFLAGS) --mode=clean rm -f $(PROGS) $(TEST_PROGS) \ *.lo *.o *.la *.a stamp-* core *.core core.* regress/*/*.out \ - regress/*/*.toke regress/*/*.err + regress/*/*.toke regress/*/*.err regress/*/*.json mostlyclean: clean @@ -789,9 +789,11 @@ linux_audit.lo: $(srcdir)/linux_audit.c $(devdir)/def_data.h \ $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \ $(top_builddir)/pathnames.h $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/linux_audit.c -locale.lo: $(srcdir)/locale.c $(incdir)/compat/stdbool.h \ - $(incdir)/sudo_compat.h $(incdir)/sudo_fatal.h \ - $(incdir)/sudo_gettext.h $(srcdir)/logging.h $(top_builddir)/config.h +locale.lo: $(srcdir)/locale.c $(devdir)/def_data.h $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ + $(incdir)/sudo_queue.h $(srcdir)/defaults.h $(srcdir)/logging.h \ + $(srcdir)/sudoers_debug.h $(top_builddir)/config.h $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/locale.c locale.o: locale.lo logging.lo: $(srcdir)/logging.c $(devdir)/def_data.h \ diff --git a/src/Makefile.in b/src/Makefile.in index cc92943fc..7ab3fd382 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -373,7 +373,7 @@ sudo_edit.o: $(srcdir)/sudo_edit.c $(incdir)/compat/stdbool.h \ $(top_builddir)/config.h $(top_builddir)/pathnames.h $(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/sudo_edit.c sudo_noexec.lo: $(srcdir)/sudo_noexec.c $(incdir)/sudo_compat.h \ - $(top_builddir)/config.h + $(top_builddir)/config.h $(top_builddir)/pathnames.h $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/sudo_noexec.c tgetpass.o: $(srcdir)/tgetpass.c $(incdir)/compat/stdbool.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \ diff --git a/src/sudo_noexec.c b/src/sudo_noexec.c index 91bc994d3..64a8b06e5 100644 --- a/src/sudo_noexec.c +++ b/src/sudo_noexec.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005, 2010-2015 Todd C. Miller <Todd.Miller@courtesan.com> + * Copyright (c) 2004-2005, 2010-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 @@ -18,6 +18,13 @@ #include <sys/types.h> +#if defined(HAVE_DECL_SECCOMP_SET_MODE_FILTER) && HAVE_DECL_SECCOMP_SET_MODE_FILTER +# include <sys/prctl.h> +# include <asm/unistd.h> +# include <linux/filter.h> +# include <linux/seccomp.h> +#endif + #include <errno.h> #include <stdarg.h> #include <stdio.h> @@ -26,8 +33,23 @@ #ifdef HAVE_SPAWN_H #include <spawn.h> #endif +#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 +#if defined(HAVE_SHL_LOAD) +# include <dl.h> +#elif defined(HAVE_DLOPEN) +# include <dlfcn.h> +#endif #include "sudo_compat.h" +#include "pathnames.h" #ifdef HAVE___INTERPOSE /* @@ -141,3 +163,93 @@ FN_NAME(popen)(const char *c, const char *t) return NULL; } INTERPOSE(popen) + +#if defined(HAVE_WORDEXP) && (defined(RTLD_NEXT) || defined(HAVE_SHL_LOAD) || defined(HAVE___INTERPOSE)) +/* + * We can't use a wrapper for wordexp(3) since we still want to call + * the real wordexp(3) but with WRDE_NOCMD added to the flags argument. + */ +typedef int (*sudo_fn_wordexp_t)(const char *, wordexp_t *, int); + +__dso_public int +FN_NAME(wordexp)(const char *words, wordexp_t *we, int flags) +{ +#if defined(HAVE___INTERPOSE) + return wordexp(words, we, flags | WRDE_NOCMD); +#else +# if defined(HAVE_DLOPEN) + void *fn = dlsym(RTLD_NEXT, "wordexp"); +# elif defined(HAVE_SHL_LOAD) + const char *name, *myname = _PATH_SUDO_NOEXEC; + struct shl_descriptor *desc; + void *fn = NULL; + int idx = 0; + + name = strrchr(myname, '/'); + if (name != NULL) + myname = name + 1; + + /* Search for wordexp() but skip this shared object. */ + while (shl_get(idx++, &desc) == 0) { + name = strrchr(desc->filename, '/'); + if (name == NULL) + name = desc->filename; + else + name++; + if (strcmp(name, myname) == 0) + continue; + if (shl_findsym(&desc->handle, "wordexp", TYPE_PROCEDURE, &fn) == 0) + break; + } +# else + void *fn = NULL; +# endif + if (fn == NULL) { + errno = EACCES; + return -1; + } + return ((sudo_fn_wordexp_t)fn)(words, we, flags | WRDE_NOCMD); +#endif /* HAVE___INTERPOSE */ +} +INTERPOSE(wordexp) +#endif /* HAVE_WORDEXP && (RTLD_NEXT || HAVE_SHL_LOAD || HAVE___INTERPOSE) */ + +/* + * On Linux we can use a seccomp() filter to disable exec. + */ +#if defined(HAVE_DECL_SECCOMP_SET_MODE_FILTER) && HAVE_DECL_SECCOMP_SET_MODE_FILTER + +/* Older systems may not support execveat(2). */ +#ifndef __NR_execveat +# define __NR_execveat -1 +#endif + +static void noexec_ctor(void) __attribute__((constructor)); + +static void +noexec_ctor(void) +{ + struct sock_filter exec_filter[] = { + /* Load syscall number into the accumulator */ + BPF_STMT(BPF_LD | BPF_ABS, offsetof(struct seccomp_data, nr)), + /* Jump to deny for execve/execveat */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_execve, 2, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_execveat, 1, 0), + /* Allow non-matching syscalls */ + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + /* Deny execve/execveat syscall */ + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | (EACCES & SECCOMP_RET_DATA)) + }; + const struct sock_fprog exec_fprog = { + nitems(exec_filter), + exec_filter + }; + + /* + * SECCOMP_MODE_FILTER will fail unless the process has + * CAP_SYS_ADMIN or the no_new_privs bit is set. + */ + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == 0) + (void)prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &exec_fprog); +} +#endif /* HAVE_DECL_SECCOMP_SET_MODE_FILTER */ |