diff --git a/.travis.yml b/.travis.yml index 1300ed97..8929997b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,19 +8,28 @@ matrix: include: - os: linux compiler: clang - env: BUILD_TYPE=default CFLAGS='-fsanitize=address -fsanitize=undefined' + env: BUILD_TYPE=default CFLAGS='-fsanitize=address -fsanitize=undefined' CONFFLAGS=--enable-seccomp-debug + addons: + apt: + packages: + - libseccomp-dev - os: linux compiler: clang-5.0 - env: BUILD_TYPE=default CFLAGS='-fsanitize=address -fsanitize=undefined' + env: BUILD_TYPE=default CFLAGS='-fsanitize=address -fsanitize=undefined' CONFFLAGS=--enable-seccomp-debug addons: apt: sources: - llvm-toolchain-trusty-5.0 packages: - clang-5.0 + - libseccomp-dev - os: linux compiler: gcc - env: BUILD_TYPE=default CFLAGS=-fsanitize=address + env: BUILD_TYPE=default CFLAGS=-fsanitize=address CONFFLAGS=--enable-seccomp-debug + addons: + apt: + packages: + - libseccomp-dev - os: osx compiler: clang env: BUILD_TYPE=default CFLAGS=-fsanitize=address @@ -61,7 +70,7 @@ script: case $BUILD_TYPE in default) ./autogen.sh - ./configure || (cat config.log; exit 1) + ./configure ${CONFFLAGS} || (cat config.log; exit 1) make make distcheck || (find . -name test-suite.log | xargs -t cat; exit 1) ;; diff --git a/Makefile.am b/Makefile.am index 8c7876aa..5b54deec 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,7 +4,11 @@ AM_CFLAGS=-Wall -Werror -Wextra AM_CPPFLAGS=-D_GNU_SOURCE bin_PROGRAMS=pick -pick_SOURCES=pick.c compat-reallocarray.c compat-strtonum.c compat.h +pick_SOURCES= pick.c \ + compat-reallocarray.c \ + compat-sandbox.c \ + compat-strtonum.c \ + compat.h pick_CPPFLAGS=$(AM_CPPFLAGS) $(NCURSES_CFLAGS) pick_LDADD=$(NCURSES_LIBS) diff --git a/compat-sandbox.c b/compat-sandbox.c new file mode 100644 index 00000000..69a37cfc --- /dev/null +++ b/compat-sandbox.c @@ -0,0 +1,138 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_PLEDGE + +#include +#include + +#include "compat.h" + +void +sandbox(int stage) +{ + switch (stage) { + case SANDBOX_ENTER: + if (pledge("stdio tty rpath wpath cpath", NULL) == -1) + err(1, "pledge"); + break; + case SANDBOX_MAIN_LOOP_ENTER: + if (pledge("stdio tty", NULL) == -1) + err(1, "pledge"); + break; + case SANDBOX_MAIN_LOOP_EXIT: + if (pledge("stdio", NULL) == -1) + err(1, "pledge"); + break; + } +} + +#elif HAVE_SECCOMP + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "compat.h" + +#define ALLOW(syscall) \ + (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(syscall), 0) < 0) + +#define ALLOW_IOCTL(syscall, x) \ + (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), x, \ + SCMP_A1(SCMP_CMP_EQ, syscall, 0)) < 0) + +/* + * Print out the offending syscall and exit. + * Not thread-safe and shall only be used for debugging purposes. + */ +void +handle_sigsys(int signum __attribute__((unused)), siginfo_t *info, + void *ctx __attribute__((unused))) +{ + warnx("disallowed syscall #%d", info->si_syscall); + fflush(stderr); + exit(1); +} + +void +sandbox_sighandler(void) +{ + struct sigaction act; + sigset_t mask; + + memset(&act, 0, sizeof(act)); + sigemptyset(&mask); + sigaddset(&mask, SIGSYS); + act.sa_sigaction = &handle_sigsys; + act.sa_flags = SA_SIGINFO; + if (sigaction(SIGSYS, &act, NULL) == -1) + err(1, "sigaction"); + if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) + err(1, "sigprocmask"); +} + +void +sandbox(int stage) +{ + scmp_filter_ctx ctx; + + switch (stage) { + case SANDBOX_ENTER: +#ifdef HAVE_SECCOMP_DEBUG + sandbox_sighandler(); +#endif + + if ((ctx = seccomp_init(SCMP_ACT_TRAP)) == NULL) + err(1, "seccomp_init"); + + if (ALLOW(access) || + ALLOW(close) || + ALLOW(exit_group) || + ALLOW(fstat) || + ALLOW(fstat64) || + ALLOW(mmap) || + ALLOW(mmap2) || + ALLOW(munmap) || + ALLOW(open) || + ALLOW(poll) || + ALLOW(read) || + ALLOW(rt_sigaction) || + ALLOW(sigaction) || + ALLOW(sigreturn) || + ALLOW(stat) || + ALLOW(stat64) || + ALLOW(time) || + ALLOW(write) || + ALLOW_IOCTL(TCGETS, 1) || + ALLOW_IOCTL(TCSETS, 1) || + ALLOW_IOCTL(TIOCGWINSZ, 1)) + err(1, "seccomp_rule_add"); + + if (seccomp_load(ctx) < 0) + err(1, "seccomp_load"); + + seccomp_release(ctx); + break; + case SANDBOX_MAIN_LOOP_ENTER: + break; + case SANDBOX_MAIN_LOOP_EXIT: + break; + } +} + +#else + +void +sandbox(int stage __attribute__((unused))) +{ +} + +#endif diff --git a/compat.h b/compat.h index f8bbc0ce..af579ea6 100644 --- a/compat.h +++ b/compat.h @@ -31,4 +31,10 @@ long long strtonum(const char *, long long, long long, const char **); #endif /* !HAVE_STRTONUM */ +void sandbox(int); + +#define SANDBOX_ENTER 0 +#define SANDBOX_MAIN_LOOP_ENTER 1 +#define SANDBOX_MAIN_LOOP_EXIT 2 + #endif /* COMPAT_H */ diff --git a/config.h.in b/config.h.in index 66b3dbd6..c7c2560b 100644 --- a/config.h.in +++ b/config.h.in @@ -9,6 +9,12 @@ /* Define to 1 if you have the `reallocarray' function. */ #undef HAVE_REALLOCARRAY +/* Define if seccomp is available */ +#undef HAVE_SECCOMP + +/* Define to debug seccomp */ +#undef HAVE_SECCOMP_DEBUG + /* Define to 1 if you have the `strtonum' function. */ #undef HAVE_STRTONUM diff --git a/configure.ac b/configure.ac index 08f1ed22..a03bb628 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,13 @@ AM_INIT_AUTOMAKE([subdir-objects]) AC_CONFIG_HEADERS([config.h]) AC_PROG_CC AM_PROG_CC_C_O +AC_ARG_ENABLE([seccomp-debug], + [AS_HELP_STRING([--enable-seccomp-debug], [enable seccomp debugging])], + [AC_DEFINE([HAVE_SECCOMP_DEBUG], [1], [Define to debug seccomp])], + []) AC_CHECK_FUNCS([pledge reallocarray strtonum]) +AC_SEARCH_LIBS([seccomp_init], [seccomp], + [AC_DEFINE([HAVE_SECCOMP], [1], [Define if seccomp is available])]) AC_SEARCH_LIBS([setupterm], [curses], [], [ AC_SEARCH_LIBS([setupterm], [ncursesw], [AC_DEFINE([HAVE_NCURSESW_H], [1], [Define if ncursesw is available])], diff --git a/pick.c b/pick.c index 128b47a3..a7fa1a32 100644 --- a/pick.c +++ b/pick.c @@ -124,10 +124,7 @@ main(int argc, char *argv[]) setlocale(LC_CTYPE, ""); -#ifdef HAVE_PLEDGE - if (pledge("stdio tty rpath wpath cpath", NULL) == -1) - err(1, "pledge"); -#endif + sandbox(SANDBOX_ENTER); while ((c = getopt(argc, argv, "dhoq:KSvxX")) != -1) switch (c) { @@ -181,13 +178,10 @@ main(int argc, char *argv[]) input = get_choices(); tty_init(1); -#ifdef HAVE_PLEDGE - if (pledge("stdio tty", NULL) == -1) - err(1, "pledge"); -#endif - + sandbox(SANDBOX_MAIN_LOOP_ENTER); choice = selected_choice(); tty_restore(1); + sandbox(SANDBOX_MAIN_LOOP_EXIT); if (choice != NULL) { printf("%s\n", choice->string); if (output_description)