/* * \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 by the 'wait_for_continue' debug mechanism. */ _add_allow_rule(SCMP_SYS(read)); /* 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)); /* returning from signal handlers is safe */ _add_allow_rule(SCMP_SYS(sigreturn)); } 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)); /* returning from signal handlers is safe */ _add_allow_rule(SCMP_SYS(rt_sigreturn)); /* restarting syscalls is safe (can occur when debugging with GDB) */ _add_allow_rule(SCMP_SYS(restart_syscall)); } 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)); /* returning from signal handlers is safe */ _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; } };