diff --git a/configure.ac b/configure.ac index 3a24053bb..84c735fc9 100644 --- a/configure.ac +++ b/configure.ac @@ -200,6 +200,13 @@ AC_SUBST(HAVE_SODIUM, [$have_sodium]) PKG_CHECK_MODULES([LIBLZMA], [liblzma], [CXXFLAGS="$LIBLZMA_CFLAGS $CXXFLAGS"]) +# Look for libseccomp, required for Linux sandboxing. +if test "$sys_name" = linux; then + PKG_CHECK_MODULES([LIBSECCOMP], [libseccomp], + [CXXFLAGS="$LIBSECCOMP_CFLAGS $CXXFLAGS"]) +fi + + # Whether to use the Boehm garbage collector. AC_ARG_ENABLE(gc, AC_HELP_STRING([--enable-gc], [enable garbage collection in the Nix expression evaluator (requires Boehm GC) [default=no]]), diff --git a/release.nix b/release.nix index 2b101beb8..66fcd9258 100644 --- a/release.nix +++ b/release.nix @@ -27,6 +27,7 @@ let [ curl bison flex perl libxml2 libxslt bzip2 xz dblatex (dblatex.tex or tetex) nukeReferences pkgconfig sqlite libsodium docbook5 docbook5_xsl + libseccomp ] ++ lib.optional (!lib.inNixShell) git; configureFlags = '' @@ -85,6 +86,7 @@ let buildInputs = [ curl perl bzip2 xz openssl pkgconfig sqlite boehmgc ] + ++ lib.optional stdenv.isLinux libseccomp ++ lib.optional stdenv.isLinux libsodium; configureFlags = '' diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 3236bfdb6..850422ad2 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -9,6 +9,7 @@ #include "archive.hh" #include "affinity.hh" #include "builtins.hh" +#include "finally.hh" #include #include @@ -54,6 +55,7 @@ #include #include #include +#include #define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) #endif @@ -2251,6 +2253,42 @@ void DerivationGoal::startBuilder() } +void setupSeccomp() +{ +#if __linux__ + scmp_filter_ctx ctx; + + if (!(ctx = seccomp_init(SCMP_ACT_ALLOW))) + throw SysError("unable to initialize seccomp mode 2"); + + Finally cleanup([&]() { + seccomp_release(ctx); + }); + + if (seccomp_arch_add(ctx, SCMP_ARCH_X86) != 0) + throw SysError("unable to add 32-bit seccomp architecture"); + + for (int perm : { S_ISUID, S_ISGID }) { + // TODO: test chmod and fchmod. + if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(chmod), 1, + SCMP_A1(SCMP_CMP_MASKED_EQ, perm, perm)) != 0) + throw SysError("unable to add seccomp rule"); + + if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmod), 1, + SCMP_A1(SCMP_CMP_MASKED_EQ, perm, perm)) != 0) + throw SysError("unable to add seccomp rule"); + + if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmodat), 1, + SCMP_A2(SCMP_CMP_MASKED_EQ, perm, perm)) != 0) + throw SysError("unable to add seccomp rule"); + } + + if (seccomp_load(ctx) != 0) + throw SysError("unable to load seccomp BPF program"); +#endif +} + + void DerivationGoal::runChild() { /* Warning: in the child we should absolutely not make any SQLite @@ -2262,6 +2300,8 @@ void DerivationGoal::runChild() commonChildInit(builderOut); + setupSeccomp(); + #if __linux__ if (useChroot) { diff --git a/src/libstore/local.mk b/src/libstore/local.mk index e78f47949..423090a62 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -14,6 +14,10 @@ ifeq ($(OS), SunOS) libstore_LDFLAGS += -lsocket endif +ifeq ($(OS), Linux) + libstore_LDFLAGS += -lseccomp +endif + libstore_CXXFLAGS = \ -DNIX_PREFIX=\"$(prefix)\" \ -DNIX_STORE_DIR=\"$(storedir)\" \ diff --git a/src/libutil/finally.hh b/src/libutil/finally.hh new file mode 100644 index 000000000..7760cfe9a --- /dev/null +++ b/src/libutil/finally.hh @@ -0,0 +1,14 @@ +#pragma once + +#include + +/* A trivial class to run a function at the end of a scope. */ +class Finally +{ +private: + std::function fun; + +public: + Finally(std::function fun) : fun(fun) { } + ~Finally() { fun(); } +};