diff --git a/CHANGES b/CHANGES index d13dd99bcc..8e72464d45 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +3851. [func] Allow libseccomp based system-call filtering + on Linux; use "configure --enable-seccomp" to + turn it on. Thanks to Loganaden Velvindron for + the contribution. [RT #35347] + 3850. [bug] Disabling forwarding could trigger a REQUIRE assertion. [RT #35979] diff --git a/bin/named/include/named/seccomp.h b/bin/named/include/named/seccomp.h new file mode 100644 index 0000000000..ce8e0e260c --- /dev/null +++ b/bin/named/include/named/seccomp.h @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC 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. + */ + +#ifndef NAMED_SECCOMP_H +#define NAMED_SECCOMP_H 1 + +/*! \file */ + +#ifdef HAVE_LIBSECCOMP +#include +#include +#include +#include + +/*% + * For each architecture, the scmp_syscalls and + * scmp_syscall_names arrays MUST be kept in sync. + */ +#ifdef __x86_64__ +int scmp_syscalls[] = { + SCMP_SYS(access), + SCMP_SYS(open), + SCMP_SYS(clock_gettime), + SCMP_SYS(time), + SCMP_SYS(read), + SCMP_SYS(write), + SCMP_SYS(close), + SCMP_SYS(brk), + SCMP_SYS(poll), + SCMP_SYS(select), + SCMP_SYS(madvise), + SCMP_SYS(mmap), + SCMP_SYS(munmap), + SCMP_SYS(exit_group), + SCMP_SYS(rt_sigprocmask), + SCMP_SYS(rt_sigaction), + SCMP_SYS(fsync), + SCMP_SYS(rt_sigreturn), + SCMP_SYS(setsid), + SCMP_SYS(chdir), + SCMP_SYS(futex), + SCMP_SYS(stat), + SCMP_SYS(rt_sigsuspend), + SCMP_SYS(fstat), + SCMP_SYS(epoll_ctl), + SCMP_SYS(gettimeofday), + SCMP_SYS(unlink), + SCMP_SYS(socket), + SCMP_SYS(sendto) +}; +const char *scmp_syscall_names[] = { + "access", + "open", + "clock_gettime", + "time", + "read", + "write", + "close", + "brk", + "poll", + "select", + "madvise", + "mmap", + "munmap", + "exit_group", + "rt_sigprocmask", + "rt_sigaction", + "fsync", + "rt_sigreturn", + "setsid", + "chdir", + "futex", + "stat", + "rt_sigsuspend", + "fstat", + "epoll_ctl", + "gettimeofday", + "unlink", + "socket", + "sendto" +}; +#endif /* __x86_64__ */ +#ifdef __i386__ +int scmp_syscalls[] = { + SCMP_SYS(access), + SCMP_SYS(open), + SCMP_SYS(clock_gettime), + SCMP_SYS(time), + SCMP_SYS(read), + SCMP_SYS(write), + SCMP_SYS(close), + SCMP_SYS(brk), + SCMP_SYS(poll), + SCMP_SYS(_newselect), + SCMP_SYS(select), + SCMP_SYS(madvise), + SCMP_SYS(mmap2), + SCMP_SYS(mmap), + SCMP_SYS(munmap), + SCMP_SYS(exit_group), + SCMP_SYS(rt_sigprocmask), + SCMP_SYS(sigprocmask), + SCMP_SYS(rt_sigaction), + SCMP_SYS(socketcall), + SCMP_SYS(fsync), + SCMP_SYS(sigreturn), + SCMP_SYS(setsid), + SCMP_SYS(chdir), + SCMP_SYS(futex), + SCMP_SYS(stat64), + SCMP_SYS(rt_sigsuspend), + SCMP_SYS(fstat64), + SCMP_SYS(epoll_ctl), + SCMP_SYS(gettimeofday) + SCMP_SYS(unlink) +}; +const char *scmp_syscall_names[] = { + "access", + "open", + "clock_gettime", + "time", + "read", + "write", + "close", + "brk", + "poll", + "_newselect", + "select", + "madvise", + "mmap2", + "mmap", + "munmap", + "exit_group", + "rt_sigprocmask", + "sigprocmask", + "rt_sigaction", + "socketcall", + "fsync", + "sigreturn", + "setsid", + "chdir", + "futex", + "stat64", + "rt_sigsuspend", + "fstat64", + "epoll_ctl", + "gettimeofday" + "unlink" +}; +#endif /* __i386__ */ +#endif /* HAVE_LIBSECCOMP */ + +#endif /* NAMED_SECCOMP_H */ diff --git a/bin/named/main.c b/bin/named/main.c index a6856bbe28..266d84fb36 100644 --- a/bin/named/main.c +++ b/bin/named/main.c @@ -70,6 +70,7 @@ #include #include #include +#include #ifdef HAVE_LIBSCF #include #endif @@ -773,6 +774,55 @@ dump_symboltable(void) { } } +#ifdef HAVE_LIBSECCOMP +static void +setup_seccomp() { + scmp_filter_ctx ctx; + unsigned int i; + int ret; + + /* Make sure the lists are in sync */ + INSIST((sizeof(scmp_syscalls) / sizeof(int)) == + (sizeof(scmp_syscall_names) / sizeof(const char *))); + + ctx = seccomp_init(SCMP_ACT_KILL); + if (ctx == NULL) { + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_WARNING, + "libseccomp activation failed"); + return; + } + + for (i = 0 ; i < sizeof(scmp_syscalls)/sizeof(*(scmp_syscalls)); i++) { + ret = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, + scmp_syscalls[i], 0); + if (ret < 0) + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_WARNING, + "libseccomp rule failed: %s", + scmp_syscall_names[i]); + + else + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_DEBUG(9), + "added libseccomp rule: %s", + scmp_syscall_names[i]); + } + + ret = seccomp_load(ctx); + if (ret < 0) { + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_WARNING, + "libseccomp unable to load filter"); + return; + } + + isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "libseccomp sandboxing active"); +} +#endif /* HAVE_LIBSECCOMP */ + static void setup(void) { isc_result_t result; @@ -986,6 +1036,10 @@ setup(void) { #endif ns_server_create(ns_g_mctx, &ns_g_server); + +#ifdef HAVE_LIBSECCOMP + setup_seccomp(); +#endif /* HAVE_LIBSECCOMP */ } static void diff --git a/config.h.in b/config.h.in index 09d77b4265..1561ae59cc 100644 --- a/config.h.in +++ b/config.h.in @@ -281,6 +281,9 @@ int sigwait(const unsigned int *set, int *sig); /* Define to 1 if you have the `scf' library (-lscf). */ #undef HAVE_LIBSCF +/* Define to use libseccomp system call filtering. */ +#undef HAVE_LIBSECCOMP + /* Define to 1 if you have the `socket' library (-lsocket). */ #undef HAVE_LIBSOCKET diff --git a/configure b/configure index bb129afa9d..9aa016dafe 100755 --- a/configure +++ b/configure @@ -973,6 +973,7 @@ with_sysroot enable_libtool_lock enable_libbind enable_developer +enable_seccomp with_python enable_kqueue enable_epoll @@ -1662,6 +1663,8 @@ Optional Features: --disable-libtool-lock avoid locking (might break parallel builds) --enable-libbind deprecated --enable-developer enable developer build settings + --enable-seccomp enable support for libseccomp sysstem call filtering + [default=no] --enable-kqueue use BSD kqueue when available [default=yes] --enable-epoll use Linux epoll when available [default=auto] --enable-devpoll use /dev/poll when available [default=yes] @@ -11414,6 +11417,144 @@ yes) test "${enable_sit+set}" = set || enable_sit=yes ;; esac + +#libseccomp sandboxing +# Check whether --enable-seccomp was given. +if test "${enable_seccomp+set}" = set; then : + enableval=$enable_seccomp; +fi + +case "$enable_seccomp" in + yes|'') + case $host_os in + linux*) + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: seccomp is not supported on non-linux platforms; disabling it" >&5 +$as_echo "$as_me: WARNING: seccomp is not supported on non-linux platforms; disabling it" >&2;} + enable_seccomp=no + ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing seccomp_init" >&5 +$as_echo_n "checking for library containing seccomp_init... " >&6; } +if ${ac_cv_search_seccomp_init+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char seccomp_init (); +int +main () +{ +return seccomp_init (); + ; + return 0; +} +_ACEOF +for ac_lib in '' seccomp; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_seccomp_init=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_seccomp_init+:} false; then : + break +fi +done +if ${ac_cv_search_seccomp_init+:} false; then : + +else + ac_cv_search_seccomp_init=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_seccomp_init" >&5 +$as_echo "$ac_cv_search_seccomp_init" >&6; } +ac_res=$ac_cv_search_seccomp_init +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + + if test "$ac_cv_search_seccomp_init" = "-lseccomp" ; then + if test "$cross_compiling" = yes; then : + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run test program while cross compiling +See \`config.log' for more details" "$LINENO" 5; } +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + #include + #include + #include + #include + + int main(void) + { + int ret; + + ret = prctl(PR_GET_SECCOMP, 0, 0, 0, 0); + if (ret < 0) { + switch (errno) { + case ENOSYS: + return 1; + case EINVAL: + return 1; + default: + return 1; + } + } + ret = + prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL, 0, 0); + if (ret < 0) { + switch (errno) { + case EINVAL: + return 1; + case EFAULT: + return 0; + default: + return 1; + } + } + return 1; + } + + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +$as_echo "#define HAVE_LIBSECCOMP 1" >>confdefs.h + + +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + fi + ;; + no) + ;; +esac + # # Make very sure that these are the first files processed by # config.status, since we use the processed output as the input for @@ -23872,6 +24013,8 @@ test "$enable_fixed" = "yes" && \ echo " Allow 'fixed' rrset-order (--enable-fixed-rrset)" test "$enable_filter" = "yes" && \ echo " AAAA filtering (--enable-filter-aaaa)" +test "$enable_seccomp" = yes && \ + echo " Use libseccomp system call filtering (--enable-seccomp)" test "$want_backtrace" = "yes" && \ echo " Print backtrace on crash (--enable-backtrace)" test "$want_symtable" = "minimal" && \ @@ -23925,6 +24068,8 @@ test "X$CRYPTO" = "X" -o "$OPENSSL_GOST" = "yes" -o "$PKCS11_GOST" = "yes" || \ test "X$CRYPTO" = "X" -o "$OPENSSL_ECDSA" = "yes" -o "$PKCS11_ECDSA" = "yes" || \ echo " ECDSA algorithm support (--with-ecdsa)" +test "$enable_seccomp" = yes || \ + echo " Use libseccomp system call filtering (--enable-seccomp)" test "$want_backtrace" = "yes" || \ echo " Print backtrace on crash (--enable-backtrace)" test "$use_libtool" = "yes" || echo " Use GNU libtool (--with-libtool)" diff --git a/configure.in b/configure.in index 1f6c0044cd..d008b0079b 100644 --- a/configure.in +++ b/configure.in @@ -89,6 +89,69 @@ yes) test "${enable_sit+set}" = set || enable_sit=yes ;; esac + +#libseccomp sandboxing +AC_ARG_ENABLE(seccomp, + AS_HELP_STRING([--enable-seccomp],[enable support for libseccomp sysstem call filtering [default=no]])) +case "$enable_seccomp" in + yes|'') + case $host_os in + linux*) + ;; + *) + AC_MSG_WARN([seccomp is not supported on non-linux platforms; disabling it]) + enable_seccomp=no + ;; + esac + AC_SEARCH_LIBS(seccomp_init, [seccomp]) + if test "$ac_cv_search_seccomp_init" = "-lseccomp" ; then + AC_TRY_RUN([ + #include + #include + #include + #include + #include + + int main(void) + { + int ret; + + ret = prctl(PR_GET_SECCOMP, 0, 0, 0, 0); + if (ret < 0) { + switch (errno) { + case ENOSYS: + return 1; + case EINVAL: + return 1; + default: + return 1; + } + } + ret = + prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL, 0, 0); + if (ret < 0) { + switch (errno) { + case EINVAL: + return 1; + case EFAULT: + return 0; + default: + return 1; + } + } + return 1; + } + ] + , AC_DEFINE([HAVE_LIBSECCOMP], 1, + [Define to use libseccomp system call filtering.]) + , [] + ) + fi + ;; + no) + ;; +esac + # # Make very sure that these are the first files processed by # config.status, since we use the processed output as the input for @@ -4533,6 +4596,8 @@ test "$enable_fixed" = "yes" && \ echo " Allow 'fixed' rrset-order (--enable-fixed-rrset)" test "$enable_filter" = "yes" && \ echo " AAAA filtering (--enable-filter-aaaa)" +test "$enable_seccomp" = yes && \ + echo " Use libseccomp system call filtering (--enable-seccomp)" test "$want_backtrace" = "yes" && \ echo " Print backtrace on crash (--enable-backtrace)" test "$want_symtable" = "minimal" && \ @@ -4586,6 +4651,8 @@ test "X$CRYPTO" = "X" -o "$OPENSSL_GOST" = "yes" -o "$PKCS11_GOST" = "yes" || \ test "X$CRYPTO" = "X" -o "$OPENSSL_ECDSA" = "yes" -o "$PKCS11_ECDSA" = "yes" || \ echo " ECDSA algorithm support (--with-ecdsa)" +test "$enable_seccomp" = yes || \ + echo " Use libseccomp system call filtering (--enable-seccomp)" test "$want_backtrace" = "yes" || \ echo " Print backtrace on crash (--enable-backtrace)" test "$use_libtool" = "yes" || echo " Use GNU libtool (--with-libtool)"