From 78497c03ca3640e78a6d6f21be2bbd6a446dd458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Th=C3=B6ni?= Date: Tue, 17 Sep 2019 14:40:52 +0200 Subject: [PATCH] base-linux: enabled seccomp base-linux uses seccomp to reduce the available system calls to the minimum set needed to run base-linux. There are still some syscalls that allow accessing global state which should be further reduced. The combination of seccomp and socket descriptor caps should provide an intermediate level of security for base-linux thereby enabling base-linux as a migration path from using the Linux kernel to the use of microkernel-based Genode. Fixes #3581 --- repos/base-linux/lib/mk/base-linux.mk | 2 +- repos/base-linux/lib/mk/spec/arm/seccomp.mk | 3 + .../base-linux/lib/mk/spec/x86_32/seccomp.mk | 3 + .../base-linux/lib/mk/spec/x86_64/seccomp.mk | 3 + repos/base-linux/src/lib/base/platform.cc | 44 +++- repos/base-linux/src/lib/base/thread_linux.cc | 5 +- .../src/lib/seccomp/seccomp_bpf_policy.h | 40 ++++ .../seccomp/spec/arm/seccomp_bpf_policy.bin | Bin 0 -> 304 bytes .../spec/x86_32/seccomp_bpf_policy.bin | Bin 0 -> 336 bytes .../spec/x86_64/seccomp_bpf_policy.bin | Bin 0 -> 352 bytes repos/base-linux/src/lib/seccomp/update.sh | 15 ++ .../src/lib/syscall/linux_syscalls.h | 12 ++ tool/seccomp/Makefile | 13 ++ tool/seccomp/seccomp_bpf_compiler.h | 198 ++++++++++++++++++ tool/seccomp/seccomp_bpf_compiler_arm.cc | 23 ++ tool/seccomp/seccomp_bpf_compiler_x86_32.cc | 23 ++ tool/seccomp/seccomp_bpf_compiler_x86_64.cc | 23 ++ 17 files changed, 404 insertions(+), 3 deletions(-) create mode 100644 repos/base-linux/lib/mk/spec/arm/seccomp.mk create mode 100644 repos/base-linux/lib/mk/spec/x86_32/seccomp.mk create mode 100644 repos/base-linux/lib/mk/spec/x86_64/seccomp.mk create mode 100644 repos/base-linux/src/lib/seccomp/seccomp_bpf_policy.h create mode 100644 repos/base-linux/src/lib/seccomp/spec/arm/seccomp_bpf_policy.bin create mode 100644 repos/base-linux/src/lib/seccomp/spec/x86_32/seccomp_bpf_policy.bin create mode 100644 repos/base-linux/src/lib/seccomp/spec/x86_64/seccomp_bpf_policy.bin create mode 100755 repos/base-linux/src/lib/seccomp/update.sh create mode 100644 tool/seccomp/Makefile create mode 100644 tool/seccomp/seccomp_bpf_compiler.h create mode 100644 tool/seccomp/seccomp_bpf_compiler_arm.cc create mode 100644 tool/seccomp/seccomp_bpf_compiler_x86_32.cc create mode 100644 tool/seccomp/seccomp_bpf_compiler_x86_64.cc diff --git a/repos/base-linux/lib/mk/base-linux.mk b/repos/base-linux/lib/mk/base-linux.mk index e9529685b5..abfa2aebda 100644 --- a/repos/base-linux/lib/mk/base-linux.mk +++ b/repos/base-linux/lib/mk/base-linux.mk @@ -6,7 +6,7 @@ include $(REP_DIR)/lib/mk/base-linux.inc -LIBS += startup-linux base-linux-common cxx +LIBS += startup-linux base-linux-common cxx seccomp SRC_CC += thread.cc thread_myself.cc thread_linux.cc SRC_CC += capability_space.cc capability_raw.cc SRC_CC += attach_stack_area.cc diff --git a/repos/base-linux/lib/mk/spec/arm/seccomp.mk b/repos/base-linux/lib/mk/spec/arm/seccomp.mk new file mode 100644 index 0000000000..50889af7a3 --- /dev/null +++ b/repos/base-linux/lib/mk/spec/arm/seccomp.mk @@ -0,0 +1,3 @@ +SRC_BIN += seccomp_bpf_policy.bin + +vpath seccomp_bpf_policy.bin $(REP_DIR)/src/lib/seccomp/spec/arm diff --git a/repos/base-linux/lib/mk/spec/x86_32/seccomp.mk b/repos/base-linux/lib/mk/spec/x86_32/seccomp.mk new file mode 100644 index 0000000000..0886d43edd --- /dev/null +++ b/repos/base-linux/lib/mk/spec/x86_32/seccomp.mk @@ -0,0 +1,3 @@ +SRC_BIN += seccomp_bpf_policy.bin + +vpath seccomp_bpf_policy.bin $(REP_DIR)/src/lib/seccomp/spec/x86_32 diff --git a/repos/base-linux/lib/mk/spec/x86_64/seccomp.mk b/repos/base-linux/lib/mk/spec/x86_64/seccomp.mk new file mode 100644 index 0000000000..c0902c8bd8 --- /dev/null +++ b/repos/base-linux/lib/mk/spec/x86_64/seccomp.mk @@ -0,0 +1,3 @@ +SRC_BIN += seccomp_bpf_policy.bin + +vpath seccomp_bpf_policy.bin $(REP_DIR)/src/lib/seccomp/spec/x86_64 diff --git a/repos/base-linux/src/lib/base/platform.cc b/repos/base-linux/src/lib/base/platform.cc index 82e843931b..66fedb793a 100644 --- a/repos/base-linux/src/lib/base/platform.cc +++ b/repos/base-linux/src/lib/base/platform.cc @@ -12,9 +12,51 @@ * under the terms of the GNU Affero General Public License version 3. */ +/* Genode includes */ +#include /* base-internal includes */ #include +#include +#include +#include /* prctl */ +#include /* seccomp's constants */ -void Genode::binary_ready_hook_for_platform() { } +using namespace Genode; + +extern char _binary_seccomp_bpf_policy_bin_start[]; +extern char _binary_seccomp_bpf_policy_bin_end[]; + +struct bpf_program { + Genode::uint16_t blk_cnt; + Genode::uint64_t* blks; +}; + +void Genode::binary_ready_hook_for_platform() +{ + if (lx_prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) { + error("PR_SET_NO_NEW_PRIVS failed"); + throw Exception(); + } + + for (char* i = _binary_seccomp_bpf_policy_bin_start; + i < _binary_seccomp_bpf_policy_bin_end - sizeof(Genode::uint32_t); i++) { + Genode::uint32_t* v = reinterpret_cast(i); + if (*v == 0xCAFEAFFE) { + *v = lx_getpid(); + } + } + + bpf_program program; + program.blk_cnt = (_binary_seccomp_bpf_policy_bin_end - + _binary_seccomp_bpf_policy_bin_start) / + sizeof(Genode::uint64_t); + program.blks = (Genode::uint64_t*)_binary_seccomp_bpf_policy_bin_start; + Genode::uint64_t flags = SECCOMP_FILTER_FLAG_TSYNC; + auto ret = lx_seccomp(SECCOMP_SET_MODE_FILTER, flags, &program); + if (ret != 0) { + error("SECCOMP_SET_MODE_FILTER failed ", ret); + throw Exception(); + } +} diff --git a/repos/base-linux/src/lib/base/thread_linux.cc b/repos/base-linux/src/lib/base/thread_linux.cc index bd70ecb6e2..e6f9c49395 100644 --- a/repos/base-linux/src/lib/base/thread_linux.cc +++ b/repos/base-linux/src/lib/base/thread_linux.cc @@ -123,7 +123,10 @@ void Thread::_deinit_platform_thread() for (;;) { /* destroy thread locally */ - int ret = lx_tgkill(native_thread().pid, native_thread().tid, LX_SIGCANCEL); + int pid = native_thread().pid; + if (pid == 0) break; + + int ret = lx_tgkill(pid, native_thread().tid, LX_SIGCANCEL); if (ret < 0) break; diff --git a/repos/base-linux/src/lib/seccomp/seccomp_bpf_policy.h b/repos/base-linux/src/lib/seccomp/seccomp_bpf_policy.h new file mode 100644 index 0000000000..bdb002f138 --- /dev/null +++ b/repos/base-linux/src/lib/seccomp/seccomp_bpf_policy.h @@ -0,0 +1,40 @@ +/* + * \brief Including seccomp filter policy binary + * \author Stefan Thoeni + * \date 2019-12-13 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * Copyright (C) 2019 gapfruit AG + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _LIB__SECCOMP_BPF_POLICY_H_ +#define _LIB__SECCOMP_BPF_POLICY_H_ + +#define STR2(x) #x +#define STR(x) STR2(x) + +#define INCBIN(name, file) \ + __asm__(".section .rodata\n" \ + ".global incbin_" STR(name) "_start\n" \ + ".type incbin_" STR(name) "_start, @object\n" \ + ".balign 16\n" \ + "incbin_" STR(name) "_start:\n" \ + ".incbin \"" file "\"\n" \ + \ + ".global incbin_" STR(name) "_end\n" \ + ".type incbin_" STR(name) "_end, @object\n" \ + ".balign 1\n" \ + "incbin_" STR(name) "_end:\n" \ + ".byte 0\n" \ + ); \ + extern const __attribute__((aligned(16))) void* incbin_ ## name ## _start; \ + extern const void* incbin_ ## name ## _end; \ + +INCBIN(seccomp_bpf_policy, "seccomp_bpf_policy.bin"); + +#endif /* _LIB__SECCOMP_BPF_POLICY_H_ */ diff --git a/repos/base-linux/src/lib/seccomp/spec/arm/seccomp_bpf_policy.bin b/repos/base-linux/src/lib/seccomp/spec/arm/seccomp_bpf_policy.bin new file mode 100644 index 0000000000000000000000000000000000000000..2303a9af394ffe2256753226c1a52ae4f5693cb5 GIT binary patch literal 304 zcmYMvJq`gu7{>AcVmH2bvFn?iM8hqdzy(}EPsdqQDteVh;}njdMlkb`nB>iG-j7M< zH9*cr0;4fZjlH{*VDM!6>kkQ(4~S)-iR>|xUFNdGLbh4T7F*e5ClmHE<|rGSWSz6D zag`A_8FH6Z9 QxeMF(oNi}$ufMYS0CLI~t^fc4 literal 0 HcmV?d00001 diff --git a/repos/base-linux/src/lib/seccomp/spec/x86_32/seccomp_bpf_policy.bin b/repos/base-linux/src/lib/seccomp/spec/x86_32/seccomp_bpf_policy.bin new file mode 100644 index 0000000000000000000000000000000000000000..38a12d4e00db95a3dd042e55a7eeba47d34a9450 GIT binary patch literal 336 zcmYMwIc~y06vpwt4cMR{Fw3w9%pxIf!3nrP`Y1&Tged63S*WO>r=p?BDL4WrFwA_m z#7IxSdGltEfxHa^^BgQ=+jq?{!;{mjJ|R#(rYU0Pmw`I4Xf$^i%2=O}xeWS5KV zaFuOtvc+8*9;yZ`_I literal 0 HcmV?d00001 diff --git a/repos/base-linux/src/lib/seccomp/spec/x86_64/seccomp_bpf_policy.bin b/repos/base-linux/src/lib/seccomp/spec/x86_64/seccomp_bpf_policy.bin new file mode 100644 index 0000000000000000000000000000000000000000..0264978c1dcb8482cd1a0ca9444c6b66e87c17bc GIT binary patch literal 352 zcmYk&K~BOz6vpv?V~eS%E!qJU(E%Hc8{KH)f)I}4O*HX9?p?X=o`e$+@DQzUzIMSR zFTcKT-elT(AhvSwxPV`8&tbhnWGpSeZ)`bA1&%uoY{M}lky8$(CzZGK $@ + +seccomp_bpf_compiler_%.prg: seccomp_bpf_compiler_%.cc + @g++ $< -o $@ -lseccomp + +clean: + @rm seccomp_bpf_policy_*.bin 2> /dev/null; true + @rm seccomp_bpf_compiler_*.prg 2> /dev/null; true diff --git a/tool/seccomp/seccomp_bpf_compiler.h b/tool/seccomp/seccomp_bpf_compiler.h new file mode 100644 index 0000000000..c666547c16 --- /dev/null +++ b/tool/seccomp/seccomp_bpf_compiler.h @@ -0,0 +1,198 @@ +/* + * \brief Generate seccomp filter policy for base-linux + * \author Stefan Thoeni + * \date 2019-12-13 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * Copyright (C) 2019 gapfruit AG + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include /* printf */ +#include /* libseccomp */ +#include +#include + +class Filter +{ + private: + scmp_filter_ctx _ctx = seccomp_init(SCMP_ACT_KILL_PROCESS); + uint32_t _arch; + + + void _add_allow_rule(int syscall_number) + { + int result = seccomp_rule_add(_ctx, SCMP_ACT_ALLOW, syscall_number, 0); + if (result != 0) { + fprintf(stderr, "Add rule failed for number %i\n", syscall_number); + throw -102; + } + } + + + void _add_allow_rule(int syscall_number, scmp_arg_cmp c1) + { + int result = seccomp_rule_add(_ctx, SCMP_ACT_ALLOW, syscall_number, 1, c1); + if (result != 0) { + fprintf(stderr, "Add rule failed for number %i\n", syscall_number); + throw -102; + } + } + + + void _add_allow_rule(int syscall_number, scmp_arg_cmp c1, scmp_arg_cmp c2) + { + int result = seccomp_rule_add(_ctx, SCMP_ACT_ALLOW, syscall_number, 2, c1, c2); + if (result != 0) { + fprintf(stderr, "Add rule failed for number %i\n", syscall_number); + throw -102; + } + } + + public: + + Filter(uint32_t arch) + : _arch(arch) + { + } + + + int create() + { + /* Kill the process if the filter architecture does not fit. */ + if (seccomp_attr_set(_ctx, SCMP_FLTATR_ACT_BADARCH, SCMP_ACT_KILL_PROCESS) != 0) { + fprintf(stderr, "Failed to set bad architecture action\n"); + throw -103; + } + + /* Remove the default architecture (e.g. native architecture) from the filter.*/ + if (seccomp_arch_remove(_ctx, SCMP_ARCH_NATIVE) != 0) { + fprintf(stderr, "Failed to remove default architecture\n"); + throw -103; + } + + /* Add the desired architecture to the filter.*/ + if (seccomp_arch_add(_ctx, _arch) != 0) { + fprintf(stderr, "Failed to add architecture\n"); + throw -103; + } + + /* This syscall is safe as it create a socket pair in the + * process */ + _add_allow_rule(SCMP_SYS(socketpair)); + + /* These syscalls should be safe as they only access already open sockets. */ + _add_allow_rule(SCMP_SYS(sendmsg)); + _add_allow_rule(SCMP_SYS(recvmsg)); + _add_allow_rule(SCMP_SYS(write)); + _add_allow_rule(SCMP_SYS(poll)); + _add_allow_rule(SCMP_SYS(epoll_create)); + _add_allow_rule(SCMP_SYS(epoll_ctl)); + _add_allow_rule(SCMP_SYS(epoll_wait)); + _add_allow_rule(SCMP_SYS(close)); + _add_allow_rule(SCMP_SYS(munmap)); + _add_allow_rule(SCMP_SYS(dup)); + _add_allow_rule(SCMP_SYS(fstat)); + _add_allow_rule(SCMP_SYS(fstat64)); + + /* This syscall is used to wait for a condition. This should be safe. */ + _add_allow_rule(SCMP_SYS(futex)); + + /* This syscall ends the program. This should be safe */ + _add_allow_rule(SCMP_SYS(exit)); + + /* These syscalls are used to react to signals. They should be safe */ + _add_allow_rule(SCMP_SYS(sigaltstack)); + _add_allow_rule(SCMP_SYS(rt_sigaction)); + + /* This syscall is used to sleep. This should be safe */ + _add_allow_rule(SCMP_SYS(nanosleep)); + + /* These syscall allow access to global information. We would like + * to reduced this. */ + _add_allow_rule(SCMP_SYS(getpid)); + _add_allow_rule(SCMP_SYS(gettid)); + _add_allow_rule(SCMP_SYS(gettimeofday)); + _add_allow_rule(SCMP_SYS(getpeername)); + + int clone_flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND + | CLONE_THREAD | CLONE_SYSVSEM; + + switch (_arch) + { + case SCMP_ARCH_X86: + { + /* The tgkill syscall must be made safe by restricting parameters. + * Genode uses LX_SIGCANCEL alias SIGRTMIN to cancel threads + * the 0xCAFEAFFE will be replaced with the process ID to restrict + * tgkill to the process (= thread group). */ + _add_allow_rule(SCMP_SYS(tgkill), SCMP_CMP32(0, SCMP_CMP_EQ, 0xCAFEAFFE), + SCMP_CMP32(2, SCMP_CMP_EQ, SIGRTMIN)); + + /* The clone syscall must be made safe by restricting parameters + * The specified flags only allow creation of new threads. */ + _add_allow_rule(SCMP_SYS(clone), SCMP_CMP32(0, SCMP_CMP_EQ, clone_flags)); + + /* The nmap syscall has a different name on different architectures + * but it slould be save as it only uses an already open socket. */ + _add_allow_rule(SCMP_SYS(mmap2)); + } + break; + case SCMP_ARCH_X86_64: + { + /* The tgkill syscall must be made safe by restricting parameters. + * Genode uses LX_SIGCANCEL alias SIGRTMIN to cancel threads + * the 0xCAFEAFFE will be replaced with the process ID to restrict + * tgkill to the process (= thread group). */ + _add_allow_rule(SCMP_SYS(tgkill), SCMP_CMP64(0, SCMP_CMP_EQ, 0xCAFEAFFE), + SCMP_CMP64(2, SCMP_CMP_EQ, SIGRTMIN)); + + /* The clone syscall must be made safe by restricting parameters + * The specified flags only allow creation of new threads. */ + _add_allow_rule(SCMP_SYS(clone), SCMP_CMP64(0, SCMP_CMP_EQ, clone_flags)); + + /* The nmap syscall has a different name on different architectures + * but it slould be save as it only uses an already open socket. */ + _add_allow_rule(SCMP_SYS(mmap)); + } + break; + case SCMP_ARCH_ARM: + { + /* The tgkill syscall must be made safe by restricting parameters. + * Genode uses LX_SIGCANCEL alias SIGRTMIN to cancel threads + * the 0xCAFEAFFE will be replaced with the process ID to restrict + * tgkill to the process (= thread group). */ + _add_allow_rule(SCMP_SYS(tgkill), SCMP_CMP32(0, SCMP_CMP_EQ, 0xCAFEAFFE), + SCMP_CMP32(2, SCMP_CMP_EQ, SIGRTMIN)); + + /* The clone syscall must be made safe by restricting parameters + * The specified flags only allow creation of new threads. */ + _add_allow_rule(SCMP_SYS(clone), SCMP_CMP32(0, SCMP_CMP_EQ, clone_flags)); + + /* The nmap2 syscall has a different name on different architectures + * but it slould be save as it only uses an already open socket. */ + _add_allow_rule(SCMP_SYS(mmap2)); + + /* This syscall is only used on ARM. */ + _add_allow_rule(SCMP_SYS(cacheflush)); + + /* This syscall is only used on ARM. */ + _add_allow_rule(SCMP_SYS(sigreturn)); + } + break; + default: + fprintf(stderr, "Unsupported architecture\n"); + throw -104; + } + + // build and export + seccomp_export_bpf(_ctx, 1); + + return 0; + } +}; + diff --git a/tool/seccomp/seccomp_bpf_compiler_arm.cc b/tool/seccomp/seccomp_bpf_compiler_arm.cc new file mode 100644 index 0000000000..d47f17ae18 --- /dev/null +++ b/tool/seccomp/seccomp_bpf_compiler_arm.cc @@ -0,0 +1,23 @@ +/* + * \brief Generate seccomp filter policy for base-linux on arm + * \author Stefan Thoeni + * \date 2019-12-13 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * Copyright (C) 2019 gapfruit AG + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include /* printf */ +#include /* libseccomp */ +#include "seccomp_bpf_compiler.h" + +int main() +{ + Filter filter(SCMP_ARCH_ARM); + return filter.create(); +} diff --git a/tool/seccomp/seccomp_bpf_compiler_x86_32.cc b/tool/seccomp/seccomp_bpf_compiler_x86_32.cc new file mode 100644 index 0000000000..f3e4717f35 --- /dev/null +++ b/tool/seccomp/seccomp_bpf_compiler_x86_32.cc @@ -0,0 +1,23 @@ +/* + * \brief Generate seccomp filter policy for base-linux on x86_64 + * \author Stefan Thoeni + * \date 2019-12-13 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * Copyright (C) 2019 gapfruit AG + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include /* printf */ +#include /* libseccomp */ +#include "seccomp_bpf_compiler.h" + +int main() +{ + Filter filter(SCMP_ARCH_X86); + return filter.create(); +} diff --git a/tool/seccomp/seccomp_bpf_compiler_x86_64.cc b/tool/seccomp/seccomp_bpf_compiler_x86_64.cc new file mode 100644 index 0000000000..120cb0d2a8 --- /dev/null +++ b/tool/seccomp/seccomp_bpf_compiler_x86_64.cc @@ -0,0 +1,23 @@ +/* + * \brief Generate seccomp filter policy for base-linux on x86_64 + * \author Stefan Thoeni + * \date 2019-12-13 + */ + +/* + * Copyright (C) 2019 Genode Labs GmbH + * Copyright (C) 2019 gapfruit AG + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include /* printf */ +#include /* libseccomp */ +#include "seccomp_bpf_compiler.h" + +int main() +{ + Filter filter(SCMP_ARCH_X86_64); + return filter.create(); +}