LibSandbox: Add reusable sandbox building blocks
Move the Linux no_new_privs, Landlock, and seccomp policy plumbing into LibSandbox so individual services can describe the privileges they need without copying the BPF and kernel feature detection machinery. Keep ImageDecoder's sandbox policy service-local by composing the new building blocks in SandboxLinux.cpp. This preserves the existing syscall allowlist while making the policy easier to audit and reuse.
This commit is contained in:
parent
e83836b928
commit
ba87a32626
8 changed files with 937 additions and 486 deletions
|
|
@ -12,6 +12,7 @@ add_subdirectory(LibJS)
|
|||
add_subdirectory(LibMain)
|
||||
add_subdirectory(LibRegex)
|
||||
add_subdirectory(LibRequests)
|
||||
add_subdirectory(LibSandbox)
|
||||
add_subdirectory(LibSync)
|
||||
add_subdirectory(LibSyntax)
|
||||
add_subdirectory(LibTLS)
|
||||
|
|
|
|||
11
Libraries/LibSandbox/CMakeLists.txt
Normal file
11
Libraries/LibSandbox/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
set(SOURCES
|
||||
Sandbox.cpp
|
||||
)
|
||||
|
||||
if (LINUX)
|
||||
list(APPEND SOURCES
|
||||
Seccomp.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
ladybird_lib(LibSandbox sandbox)
|
||||
102
Libraries/LibSandbox/Sandbox.cpp
Normal file
102
Libraries/LibSandbox/Sandbox.cpp
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (c) 2026-present, the Ladybird developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibSandbox/Sandbox.h>
|
||||
|
||||
#if defined(AK_OS_LINUX)
|
||||
# include <AK/ScopeGuard.h>
|
||||
# include <errno.h>
|
||||
# include <linux/landlock.h>
|
||||
# include <sys/prctl.h>
|
||||
# include <sys/syscall.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(__GLIBC__)
|
||||
# include <malloc.h>
|
||||
#endif
|
||||
|
||||
namespace Sandbox {
|
||||
|
||||
ErrorOr<void> install_no_new_privileges()
|
||||
{
|
||||
#if defined(AK_OS_LINUX)
|
||||
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0)
|
||||
return Error::from_syscall("prctl(PR_SET_NO_NEW_PRIVS)"sv, errno);
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> configure_runtime()
|
||||
{
|
||||
#if defined(AK_OS_LINUX) && defined(__GLIBC__) && defined(M_ARENA_MAX) && !defined(HAS_ADDRESS_SANITIZER)
|
||||
if (mallopt(M_ARENA_MAX, 4) == 0)
|
||||
return Error::from_string_literal("mallopt(M_ARENA_MAX) failed");
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> restrict_filesystem_with_landlock()
|
||||
{
|
||||
#if defined(AK_OS_LINUX) && defined(__NR_landlock_create_ruleset) && defined(__NR_landlock_restrict_self)
|
||||
auto landlock_abi = syscall(__NR_landlock_create_ruleset, nullptr, 0, LANDLOCK_CREATE_RULESET_VERSION);
|
||||
if (landlock_abi < 0) {
|
||||
if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
|
||||
return {};
|
||||
return Error::from_syscall("landlock_create_ruleset(LANDLOCK_CREATE_RULESET_VERSION)"sv, errno);
|
||||
}
|
||||
if (landlock_abi == 0)
|
||||
return {};
|
||||
|
||||
landlock_ruleset_attr ruleset_attributes {};
|
||||
ruleset_attributes.handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE
|
||||
| LANDLOCK_ACCESS_FS_WRITE_FILE
|
||||
| LANDLOCK_ACCESS_FS_READ_FILE
|
||||
| LANDLOCK_ACCESS_FS_READ_DIR
|
||||
| LANDLOCK_ACCESS_FS_REMOVE_DIR
|
||||
| LANDLOCK_ACCESS_FS_REMOVE_FILE
|
||||
| LANDLOCK_ACCESS_FS_MAKE_CHAR
|
||||
| LANDLOCK_ACCESS_FS_MAKE_DIR
|
||||
| LANDLOCK_ACCESS_FS_MAKE_REG
|
||||
| LANDLOCK_ACCESS_FS_MAKE_SOCK
|
||||
| LANDLOCK_ACCESS_FS_MAKE_FIFO
|
||||
| LANDLOCK_ACCESS_FS_MAKE_BLOCK
|
||||
| LANDLOCK_ACCESS_FS_MAKE_SYM;
|
||||
|
||||
# ifdef LANDLOCK_ACCESS_FS_REFER
|
||||
if (landlock_abi >= 2)
|
||||
ruleset_attributes.handled_access_fs |= LANDLOCK_ACCESS_FS_REFER;
|
||||
# endif
|
||||
# ifdef LANDLOCK_ACCESS_FS_TRUNCATE
|
||||
if (landlock_abi >= 3)
|
||||
ruleset_attributes.handled_access_fs |= LANDLOCK_ACCESS_FS_TRUNCATE;
|
||||
# endif
|
||||
# if defined(LANDLOCK_ACCESS_NET_BIND_TCP) && defined(LANDLOCK_ACCESS_NET_CONNECT_TCP)
|
||||
if (landlock_abi >= 4)
|
||||
ruleset_attributes.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | LANDLOCK_ACCESS_NET_CONNECT_TCP;
|
||||
|
||||
auto ruleset_attributes_size = landlock_abi >= 4
|
||||
? sizeof(ruleset_attributes)
|
||||
: offsetof(landlock_ruleset_attr, handled_access_net);
|
||||
# else
|
||||
auto ruleset_attributes_size = sizeof(ruleset_attributes);
|
||||
# endif
|
||||
auto ruleset_fd = syscall(__NR_landlock_create_ruleset, &ruleset_attributes, ruleset_attributes_size, 0);
|
||||
if (ruleset_fd < 0)
|
||||
return Error::from_syscall("landlock_create_ruleset"sv, errno);
|
||||
|
||||
ArmedScopeGuard close_ruleset_fd = [&] {
|
||||
close(static_cast<int>(ruleset_fd));
|
||||
};
|
||||
|
||||
if (syscall(__NR_landlock_restrict_self, ruleset_fd, 0) < 0)
|
||||
return Error::from_syscall("landlock_restrict_self"sv, errno);
|
||||
#endif
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
17
Libraries/LibSandbox/Sandbox.h
Normal file
17
Libraries/LibSandbox/Sandbox.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright (c) 2026-present, the Ladybird developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Error.h>
|
||||
|
||||
namespace Sandbox {
|
||||
|
||||
[[nodiscard]] ErrorOr<void> install_no_new_privileges();
|
||||
[[nodiscard]] ErrorOr<void> configure_runtime();
|
||||
[[nodiscard]] ErrorOr<void> restrict_filesystem_with_landlock();
|
||||
|
||||
}
|
||||
750
Libraries/LibSandbox/Seccomp.cpp
Normal file
750
Libraries/LibSandbox/Seccomp.cpp
Normal file
|
|
@ -0,0 +1,750 @@
|
|||
/*
|
||||
* Copyright (c) 2026-present, the Ladybird developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibSandbox/Seccomp.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/audit.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/seccomp.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/ucontext.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace Sandbox {
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(__x86_64__)
|
||||
static constexpr u32 audit_architecture = AUDIT_ARCH_X86_64;
|
||||
#elif defined(__aarch64__)
|
||||
static constexpr u32 audit_architecture = AUDIT_ARCH_AARCH64;
|
||||
#elif defined(__riscv) && __riscv_xlen == 64
|
||||
static constexpr u32 audit_architecture = AUDIT_ARCH_RISCV64;
|
||||
#else
|
||||
# error "Add 64-bit seccomp audit architecture for this Linux architecture"
|
||||
#endif
|
||||
|
||||
static constexpr u32 thread_clone_required_flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD;
|
||||
static constexpr u32 thread_clone_allowed_flags = thread_clone_required_flags
|
||||
| CLONE_SYSVSEM
|
||||
| CLONE_SETTLS
|
||||
| CLONE_PARENT_SETTID
|
||||
| CLONE_CHILD_CLEARTID
|
||||
| CLONE_DETACHED
|
||||
| CLONE_CHILD_SETTID;
|
||||
|
||||
#ifdef O_LARGEFILE
|
||||
static constexpr unsigned read_only_open_flags = O_CLOEXEC | O_LARGEFILE;
|
||||
#else
|
||||
static constexpr unsigned read_only_open_flags = O_CLOEXEC;
|
||||
#endif
|
||||
|
||||
#define SECCOMP_LOAD_SYSCALL_NR BPF_STMT(BPF_LD | BPF_W | BPF_ABS, static_cast<unsigned int>(offsetof(seccomp_data, nr)))
|
||||
#define SECCOMP_LOAD_ARCHITECTURE BPF_STMT(BPF_LD | BPF_W | BPF_ABS, static_cast<unsigned int>(offsetof(seccomp_data, arch)))
|
||||
#define SECCOMP_LOAD_ARGUMENT(index) BPF_STMT(BPF_LD | BPF_W | BPF_ABS, static_cast<unsigned int>(offsetof(seccomp_data, args[(index)])))
|
||||
#define SECCOMP_ALLOW BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)
|
||||
#define SECCOMP_TRAP BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRAP)
|
||||
#define SECCOMP_ERRNO(error) BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ((error) & SECCOMP_RET_DATA))
|
||||
#define SECCOMP_ALLOW_NOTHING BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 0)
|
||||
#define SECCOMP_APPEND_ALLOW_SYSCALL(policy, name) \
|
||||
do { \
|
||||
(policy).append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_##name, 0, 1)); \
|
||||
(policy).append(SECCOMP_ALLOW); \
|
||||
} while (0)
|
||||
#define SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(policy, name) \
|
||||
IF_DEFINED_##name(SECCOMP_APPEND_ALLOW_SYSCALL(policy, name), (policy).append(SECCOMP_ALLOW_NOTHING))
|
||||
|
||||
#define IF_DEFINED_accept4(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_brk(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_clock_getres(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_clock_gettime(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_clock_nanosleep(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_clone(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_clone3(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_close(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_dup(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_dup3(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_exit(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_exit_group(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_fcntl(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_fcntl64(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_fstat(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_ftruncate(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_futex(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_futex_time64(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getcpu(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getegid(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_geteuid(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getgid(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getpeername(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getpid(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getrandom(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getrlimit(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getrusage(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getsockname(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getsockopt(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_gettid(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_gettimeofday(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getuid(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_ioctl(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_lseek(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_madvise(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_membarrier(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_memfd_create(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_mmap(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_mmap2(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_mprotect(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_mremap(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_munmap(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_nanosleep(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_pipe2(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_poll(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_ppoll(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_prctl(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_prlimit64(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_pselect6(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_pwrite64(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_read(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_recvfrom(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_recvmmsg(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_recvmsg(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_restart_syscall(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_rseq(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_rt_sigaction(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_rt_sigprocmask(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_rt_sigreturn(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_sched_getaffinity(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_sched_yield(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_sendmmsg(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_sendmsg(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_sendto(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_set_robust_list(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_set_tid_address(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_setsockopt(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_sigaltstack(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_socketpair(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_sysinfo(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_tgkill(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_umask(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_uname(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_write(if_defined, if_not_defined) if_defined
|
||||
|
||||
#ifndef __NR_accept4
|
||||
# undef IF_DEFINED_accept4
|
||||
# define IF_DEFINED_accept4(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_clock_getres
|
||||
# undef IF_DEFINED_clock_getres
|
||||
# define IF_DEFINED_clock_getres(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_clock_gettime
|
||||
# undef IF_DEFINED_clock_gettime
|
||||
# define IF_DEFINED_clock_gettime(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_clock_nanosleep
|
||||
# undef IF_DEFINED_clock_nanosleep
|
||||
# define IF_DEFINED_clock_nanosleep(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_clone3
|
||||
# undef IF_DEFINED_clone3
|
||||
# define IF_DEFINED_clone3(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_dup3
|
||||
# undef IF_DEFINED_dup3
|
||||
# define IF_DEFINED_dup3(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_fcntl64
|
||||
# undef IF_DEFINED_fcntl64
|
||||
# define IF_DEFINED_fcntl64(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_fstat
|
||||
# undef IF_DEFINED_fstat
|
||||
# define IF_DEFINED_fstat(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_futex_time64
|
||||
# undef IF_DEFINED_futex_time64
|
||||
# define IF_DEFINED_futex_time64(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_getcpu
|
||||
# undef IF_DEFINED_getcpu
|
||||
# define IF_DEFINED_getcpu(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_getrandom
|
||||
# undef IF_DEFINED_getrandom
|
||||
# define IF_DEFINED_getrandom(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_membarrier
|
||||
# undef IF_DEFINED_membarrier
|
||||
# define IF_DEFINED_membarrier(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_memfd_create
|
||||
# undef IF_DEFINED_memfd_create
|
||||
# define IF_DEFINED_memfd_create(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_mmap2
|
||||
# undef IF_DEFINED_mmap2
|
||||
# define IF_DEFINED_mmap2(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_pipe2
|
||||
# undef IF_DEFINED_pipe2
|
||||
# define IF_DEFINED_pipe2(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_poll
|
||||
# undef IF_DEFINED_poll
|
||||
# define IF_DEFINED_poll(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_ppoll
|
||||
# undef IF_DEFINED_ppoll
|
||||
# define IF_DEFINED_ppoll(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_prlimit64
|
||||
# undef IF_DEFINED_prlimit64
|
||||
# define IF_DEFINED_prlimit64(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_pselect6
|
||||
# undef IF_DEFINED_pselect6
|
||||
# define IF_DEFINED_pselect6(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_pwrite64
|
||||
# undef IF_DEFINED_pwrite64
|
||||
# define IF_DEFINED_pwrite64(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_recvmmsg
|
||||
# undef IF_DEFINED_recvmmsg
|
||||
# define IF_DEFINED_recvmmsg(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_restart_syscall
|
||||
# undef IF_DEFINED_restart_syscall
|
||||
# define IF_DEFINED_restart_syscall(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_rseq
|
||||
# undef IF_DEFINED_rseq
|
||||
# define IF_DEFINED_rseq(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_sendmmsg
|
||||
# undef IF_DEFINED_sendmmsg
|
||||
# define IF_DEFINED_sendmmsg(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_sigaltstack
|
||||
# undef IF_DEFINED_sigaltstack
|
||||
# define IF_DEFINED_sigaltstack(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_sysinfo
|
||||
# undef IF_DEFINED_sysinfo
|
||||
# define IF_DEFINED_sysinfo(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_umask
|
||||
# undef IF_DEFINED_umask
|
||||
# define IF_DEFINED_umask(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
|
||||
static size_t safe_string_length(char const* string)
|
||||
{
|
||||
size_t length = 0;
|
||||
while (string[length] != '\0')
|
||||
++length;
|
||||
return length;
|
||||
}
|
||||
|
||||
static void write_bytes(char const* string, size_t length)
|
||||
{
|
||||
while (length > 0) {
|
||||
auto written = write(STDERR_FILENO, string, length);
|
||||
if (written <= 0)
|
||||
return;
|
||||
string += written;
|
||||
length -= static_cast<size_t>(written);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_string(char const* string)
|
||||
{
|
||||
write_bytes(string, safe_string_length(string));
|
||||
}
|
||||
|
||||
static void write_unsigned(u64 value)
|
||||
{
|
||||
char buffer[20];
|
||||
size_t index = sizeof(buffer);
|
||||
do {
|
||||
buffer[--index] = static_cast<char>('0' + (value % 10));
|
||||
value /= 10;
|
||||
} while (value > 0);
|
||||
write_bytes(buffer + index, sizeof(buffer) - index);
|
||||
}
|
||||
|
||||
static void write_hex(u64 value)
|
||||
{
|
||||
constexpr char const* digits = "0123456789abcdef";
|
||||
char buffer[16];
|
||||
size_t index = sizeof(buffer);
|
||||
do {
|
||||
buffer[--index] = digits[value & 0xf];
|
||||
value >>= 4;
|
||||
} while (value > 0);
|
||||
write_string("0x");
|
||||
write_bytes(buffer + index, sizeof(buffer) - index);
|
||||
}
|
||||
|
||||
#define CASE_SYSCALL_NAME(name) \
|
||||
case __NR_##name: \
|
||||
return #name
|
||||
|
||||
static char const* syscall_name(long syscall_number)
|
||||
{
|
||||
switch (syscall_number) {
|
||||
#ifdef __NR_accept
|
||||
CASE_SYSCALL_NAME(accept);
|
||||
#endif
|
||||
#ifdef __NR_accept4
|
||||
CASE_SYSCALL_NAME(accept4);
|
||||
#endif
|
||||
#ifdef __NR_access
|
||||
CASE_SYSCALL_NAME(access);
|
||||
#endif
|
||||
#ifdef __NR_bind
|
||||
CASE_SYSCALL_NAME(bind);
|
||||
#endif
|
||||
#ifdef __NR_clone
|
||||
CASE_SYSCALL_NAME(clone);
|
||||
#endif
|
||||
#ifdef __NR_clone3
|
||||
CASE_SYSCALL_NAME(clone3);
|
||||
#endif
|
||||
#ifdef __NR_connect
|
||||
CASE_SYSCALL_NAME(connect);
|
||||
#endif
|
||||
#ifdef __NR_execve
|
||||
CASE_SYSCALL_NAME(execve);
|
||||
#endif
|
||||
#ifdef __NR_execveat
|
||||
CASE_SYSCALL_NAME(execveat);
|
||||
#endif
|
||||
#ifdef __NR_fcntl
|
||||
CASE_SYSCALL_NAME(fcntl);
|
||||
#endif
|
||||
#ifdef __NR_fcntl64
|
||||
CASE_SYSCALL_NAME(fcntl64);
|
||||
#endif
|
||||
#ifdef __NR_fstat
|
||||
CASE_SYSCALL_NAME(fstat);
|
||||
#endif
|
||||
#ifdef __NR_futex
|
||||
CASE_SYSCALL_NAME(futex);
|
||||
#endif
|
||||
#ifdef __NR_getdents64
|
||||
CASE_SYSCALL_NAME(getdents64);
|
||||
#endif
|
||||
#ifdef __NR_ioctl
|
||||
CASE_SYSCALL_NAME(ioctl);
|
||||
#endif
|
||||
#ifdef __NR_link
|
||||
CASE_SYSCALL_NAME(link);
|
||||
#endif
|
||||
#ifdef __NR_linkat
|
||||
CASE_SYSCALL_NAME(linkat);
|
||||
#endif
|
||||
#ifdef __NR_memfd_create
|
||||
CASE_SYSCALL_NAME(memfd_create);
|
||||
#endif
|
||||
#ifdef __NR_mkdir
|
||||
CASE_SYSCALL_NAME(mkdir);
|
||||
#endif
|
||||
#ifdef __NR_mkdirat
|
||||
CASE_SYSCALL_NAME(mkdirat);
|
||||
#endif
|
||||
#ifdef __NR_mmap
|
||||
CASE_SYSCALL_NAME(mmap);
|
||||
#endif
|
||||
#ifdef __NR_mmap2
|
||||
CASE_SYSCALL_NAME(mmap2);
|
||||
#endif
|
||||
#ifdef __NR_mprotect
|
||||
CASE_SYSCALL_NAME(mprotect);
|
||||
#endif
|
||||
#ifdef __NR_newfstatat
|
||||
CASE_SYSCALL_NAME(newfstatat);
|
||||
#endif
|
||||
#ifdef __NR_open
|
||||
CASE_SYSCALL_NAME(open);
|
||||
#endif
|
||||
#ifdef __NR_openat
|
||||
CASE_SYSCALL_NAME(openat);
|
||||
#endif
|
||||
#ifdef __NR_prctl
|
||||
CASE_SYSCALL_NAME(prctl);
|
||||
#endif
|
||||
#ifdef __NR_readlink
|
||||
CASE_SYSCALL_NAME(readlink);
|
||||
#endif
|
||||
#ifdef __NR_readlinkat
|
||||
CASE_SYSCALL_NAME(readlinkat);
|
||||
#endif
|
||||
#ifdef __NR_rename
|
||||
CASE_SYSCALL_NAME(rename);
|
||||
#endif
|
||||
#ifdef __NR_renameat
|
||||
CASE_SYSCALL_NAME(renameat);
|
||||
#endif
|
||||
#ifdef __NR_renameat2
|
||||
CASE_SYSCALL_NAME(renameat2);
|
||||
#endif
|
||||
#ifdef __NR_rmdir
|
||||
CASE_SYSCALL_NAME(rmdir);
|
||||
#endif
|
||||
#ifdef __NR_sched_setaffinity
|
||||
CASE_SYSCALL_NAME(sched_setaffinity);
|
||||
#endif
|
||||
#ifdef __NR_sched_setparam
|
||||
CASE_SYSCALL_NAME(sched_setparam);
|
||||
#endif
|
||||
#ifdef __NR_sched_setscheduler
|
||||
CASE_SYSCALL_NAME(sched_setscheduler);
|
||||
#endif
|
||||
#ifdef __NR_seccomp
|
||||
CASE_SYSCALL_NAME(seccomp);
|
||||
#endif
|
||||
#ifdef __NR_socket
|
||||
CASE_SYSCALL_NAME(socket);
|
||||
#endif
|
||||
#ifdef __NR_statx
|
||||
CASE_SYSCALL_NAME(statx);
|
||||
#endif
|
||||
#ifdef __NR_unlink
|
||||
CASE_SYSCALL_NAME(unlink);
|
||||
#endif
|
||||
#ifdef __NR_unlinkat
|
||||
CASE_SYSCALL_NAME(unlinkat);
|
||||
#endif
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
#undef CASE_SYSCALL_NAME
|
||||
|
||||
static void handle_sigsys(int, siginfo_t* info, void* context)
|
||||
{
|
||||
write_string("Sandbox violation: disallowed syscall ");
|
||||
write_string(syscall_name(info->si_syscall));
|
||||
write_string(" (");
|
||||
write_unsigned(static_cast<u64>(info->si_syscall));
|
||||
write_string("), architecture ");
|
||||
write_hex(info->si_arch);
|
||||
write_string(", instruction pointer ");
|
||||
write_hex(reinterpret_cast<FlatPtr>(info->si_call_addr));
|
||||
|
||||
#if defined(__x86_64__)
|
||||
auto* machine_context = &static_cast<ucontext_t*>(context)->uc_mcontext;
|
||||
write_string(", args [");
|
||||
write_hex(static_cast<u64>(machine_context->gregs[REG_RDI]));
|
||||
write_string(", ");
|
||||
write_hex(static_cast<u64>(machine_context->gregs[REG_RSI]));
|
||||
write_string(", ");
|
||||
write_hex(static_cast<u64>(machine_context->gregs[REG_RDX]));
|
||||
write_string(", ");
|
||||
write_hex(static_cast<u64>(machine_context->gregs[REG_R10]));
|
||||
write_string(", ");
|
||||
write_hex(static_cast<u64>(machine_context->gregs[REG_R8]));
|
||||
write_string(", ");
|
||||
write_hex(static_cast<u64>(machine_context->gregs[REG_R9]));
|
||||
write_string("]");
|
||||
#elif defined(__aarch64__)
|
||||
auto* machine_context = &static_cast<ucontext_t*>(context)->uc_mcontext;
|
||||
write_string(", args [");
|
||||
for (size_t i = 0; i < 6; ++i) {
|
||||
if (i != 0)
|
||||
write_string(", ");
|
||||
write_hex(static_cast<u64>(machine_context->regs[i]));
|
||||
}
|
||||
write_string("]");
|
||||
#endif
|
||||
|
||||
write_string("\n");
|
||||
|
||||
struct sigaction action {};
|
||||
action.sa_handler = SIG_DFL;
|
||||
sigemptyset(&action.sa_mask);
|
||||
sigaction(SIGSYS, &action, nullptr);
|
||||
raise(SIGSYS);
|
||||
_exit(128 + SIGSYS);
|
||||
}
|
||||
|
||||
static ErrorOr<void> install_sigsys_handler()
|
||||
{
|
||||
struct sigaction action {};
|
||||
action.sa_sigaction = handle_sigsys;
|
||||
action.sa_flags = SA_SIGINFO;
|
||||
if (sigemptyset(&action.sa_mask) < 0)
|
||||
return Error::from_syscall("sigemptyset"sv, errno);
|
||||
if (sigaction(SIGSYS, &action, nullptr) < 0)
|
||||
return Error::from_syscall("sigaction(SIGSYS)"sv, errno);
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SeccompPolicy::SeccompPolicy()
|
||||
{
|
||||
append_architecture_check();
|
||||
append_load_syscall_number();
|
||||
}
|
||||
|
||||
void SeccompPolicy::append(sock_filter filter)
|
||||
{
|
||||
MUST(m_filter.try_append(filter));
|
||||
}
|
||||
|
||||
void SeccompPolicy::append_architecture_check()
|
||||
{
|
||||
append(SECCOMP_LOAD_ARCHITECTURE);
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, audit_architecture, 1, 0));
|
||||
append(SECCOMP_TRAP);
|
||||
}
|
||||
|
||||
void SeccompPolicy::append_load_syscall_number()
|
||||
{
|
||||
append(SECCOMP_LOAD_SYSCALL_NR);
|
||||
}
|
||||
|
||||
void SeccompPolicy::append_kill()
|
||||
{
|
||||
append(SECCOMP_TRAP);
|
||||
}
|
||||
|
||||
void SeccompPolicy::deny_readonly_filesystem_probes()
|
||||
{
|
||||
#ifdef __NR_open
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_open, 0, 5));
|
||||
append(SECCOMP_LOAD_ARGUMENT(1));
|
||||
append(BPF_STMT(BPF_ALU | BPF_AND | BPF_K, ~read_only_open_flags));
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1));
|
||||
append(SECCOMP_ERRNO(EACCES));
|
||||
append(SECCOMP_LOAD_SYSCALL_NR);
|
||||
#endif
|
||||
#ifdef __NR_openat
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_openat, 0, 5));
|
||||
append(SECCOMP_LOAD_ARGUMENT(2));
|
||||
append(BPF_STMT(BPF_ALU | BPF_AND | BPF_K, ~read_only_open_flags));
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1));
|
||||
append(SECCOMP_ERRNO(EACCES));
|
||||
append(SECCOMP_LOAD_SYSCALL_NR);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SeccompPolicy::allow_file_descriptor_operations()
|
||||
{
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, read);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, write);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, close);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, fstat);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, dup);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, dup3);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, pipe2);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, ftruncate);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, lseek);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, pwrite64);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, memfd_create);
|
||||
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_fcntl, 0, 5));
|
||||
append(SECCOMP_LOAD_ARGUMENT(1));
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, F_GETFD, 0, 1));
|
||||
append(SECCOMP_ALLOW);
|
||||
append(SECCOMP_LOAD_SYSCALL_NR);
|
||||
append(BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 0));
|
||||
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_fcntl, 0, 5));
|
||||
append(SECCOMP_LOAD_ARGUMENT(1));
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, F_SETFD, 0, 1));
|
||||
append(SECCOMP_ALLOW);
|
||||
append(SECCOMP_LOAD_SYSCALL_NR);
|
||||
append(BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 0));
|
||||
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_fcntl, 0, 5));
|
||||
append(SECCOMP_LOAD_ARGUMENT(1));
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, F_GETFL, 0, 1));
|
||||
append(SECCOMP_ALLOW);
|
||||
append(SECCOMP_LOAD_SYSCALL_NR);
|
||||
append(BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 0));
|
||||
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_fcntl, 0, 5));
|
||||
append(SECCOMP_LOAD_ARGUMENT(1));
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, F_SETFL, 0, 1));
|
||||
append(SECCOMP_ALLOW);
|
||||
append(SECCOMP_LOAD_SYSCALL_NR);
|
||||
append(BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 0));
|
||||
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_ioctl, 0, 5));
|
||||
append(SECCOMP_LOAD_ARGUMENT(1));
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, FIONBIO, 0, 1));
|
||||
append(SECCOMP_ALLOW);
|
||||
append(SECCOMP_LOAD_SYSCALL_NR);
|
||||
append(BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 0));
|
||||
}
|
||||
|
||||
void SeccompPolicy::allow_ipc()
|
||||
{
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, poll);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, ppoll);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, pselect6);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, recvmsg);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, recvfrom);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, recvmmsg);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, sendmsg);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, sendto);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, sendmmsg);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, socketpair);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, getsockopt);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, setsockopt);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, getsockname);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, getpeername);
|
||||
}
|
||||
|
||||
void SeccompPolicy::allow_memory_without_executable_mappings()
|
||||
{
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_mmap, 0, 5));
|
||||
append(SECCOMP_LOAD_ARGUMENT(2));
|
||||
append(BPF_STMT(BPF_ALU | BPF_AND | BPF_K, PROT_EXEC));
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1));
|
||||
append(SECCOMP_ALLOW);
|
||||
append(SECCOMP_LOAD_SYSCALL_NR);
|
||||
|
||||
#ifdef __NR_mmap2
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_mmap2, 0, 5));
|
||||
append(SECCOMP_LOAD_ARGUMENT(2));
|
||||
append(BPF_STMT(BPF_ALU | BPF_AND | BPF_K, PROT_EXEC));
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1));
|
||||
append(SECCOMP_ALLOW);
|
||||
append(SECCOMP_LOAD_SYSCALL_NR);
|
||||
#endif
|
||||
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_mprotect, 0, 5));
|
||||
append(SECCOMP_LOAD_ARGUMENT(2));
|
||||
append(BPF_STMT(BPF_ALU | BPF_AND | BPF_K, PROT_EXEC));
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1));
|
||||
append(SECCOMP_ALLOW);
|
||||
append(SECCOMP_LOAD_SYSCALL_NR);
|
||||
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, mremap);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, munmap);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, madvise);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, brk);
|
||||
}
|
||||
|
||||
void SeccompPolicy::allow_threads()
|
||||
{
|
||||
#ifdef __NR_clone
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_clone, 0, 8));
|
||||
append(SECCOMP_LOAD_ARGUMENT(0));
|
||||
append(BPF_STMT(BPF_ALU | BPF_AND | BPF_K, thread_clone_required_flags));
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, thread_clone_required_flags, 0, 4));
|
||||
append(SECCOMP_LOAD_ARGUMENT(0));
|
||||
append(BPF_STMT(BPF_ALU | BPF_AND | BPF_K, ~thread_clone_allowed_flags));
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1));
|
||||
append(SECCOMP_ALLOW);
|
||||
append(SECCOMP_LOAD_SYSCALL_NR);
|
||||
#endif
|
||||
#ifdef __NR_clone3
|
||||
append(BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_clone3, 0, 1));
|
||||
append(SECCOMP_ERRNO(ENOSYS));
|
||||
#endif
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, futex);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, futex_time64);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, set_tid_address);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, set_robust_list);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, rseq);
|
||||
}
|
||||
|
||||
void SeccompPolicy::allow_signals()
|
||||
{
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, rt_sigaction);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, rt_sigprocmask);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, rt_sigreturn);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, sigaltstack);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, tgkill);
|
||||
}
|
||||
|
||||
void SeccompPolicy::allow_clocks()
|
||||
{
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, clock_gettime);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, clock_getres);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, clock_nanosleep);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, nanosleep);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, restart_syscall);
|
||||
}
|
||||
|
||||
void SeccompPolicy::allow_process_metadata()
|
||||
{
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, getpid);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, gettid);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, getuid);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, geteuid);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, getgid);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, getegid);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, getrandom);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, gettimeofday);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, getrlimit);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, prlimit64);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, getrusage);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, sched_getaffinity);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, sched_yield);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, getcpu);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, membarrier);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, sysinfo);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, uname);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, umask);
|
||||
}
|
||||
|
||||
void SeccompPolicy::allow_common_runtime()
|
||||
{
|
||||
allow_memory_without_executable_mappings();
|
||||
allow_threads();
|
||||
allow_signals();
|
||||
allow_clocks();
|
||||
allow_process_metadata();
|
||||
allow_prctl();
|
||||
allow_exit();
|
||||
}
|
||||
|
||||
void SeccompPolicy::allow_prctl()
|
||||
{
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, prctl);
|
||||
}
|
||||
|
||||
void SeccompPolicy::allow_exit()
|
||||
{
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, exit);
|
||||
SECCOMP_APPEND_ALLOW_SYSCALL_IF_DEFINED(*this, exit_group);
|
||||
}
|
||||
|
||||
ErrorOr<void> SeccompPolicy::install()
|
||||
{
|
||||
TRY(install_sigsys_handler());
|
||||
|
||||
append_kill();
|
||||
|
||||
sock_fprog program {
|
||||
.len = static_cast<unsigned short>(m_filter.size()),
|
||||
.filter = m_filter.data(),
|
||||
};
|
||||
|
||||
#ifdef __NR_seccomp
|
||||
if (syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, &program) < 0)
|
||||
return Error::from_syscall("seccomp(SECCOMP_SET_MODE_FILTER)"sv, errno);
|
||||
#else
|
||||
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &program) < 0)
|
||||
return Error::from_syscall("prctl(PR_SET_SECCOMP)"sv, errno);
|
||||
#endif
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
42
Libraries/LibSandbox/Seccomp.h
Normal file
42
Libraries/LibSandbox/Seccomp.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2026-present, the Ladybird developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <linux/filter.h>
|
||||
|
||||
namespace Sandbox {
|
||||
|
||||
class SeccompPolicy {
|
||||
public:
|
||||
SeccompPolicy();
|
||||
|
||||
void deny_readonly_filesystem_probes();
|
||||
void allow_file_descriptor_operations();
|
||||
void allow_ipc();
|
||||
void allow_memory_without_executable_mappings();
|
||||
void allow_threads();
|
||||
void allow_signals();
|
||||
void allow_clocks();
|
||||
void allow_process_metadata();
|
||||
void allow_common_runtime();
|
||||
void allow_prctl();
|
||||
void allow_exit();
|
||||
|
||||
[[nodiscard]] ErrorOr<void> install();
|
||||
|
||||
private:
|
||||
void append(sock_filter);
|
||||
void append_architecture_check();
|
||||
void append_load_syscall_number();
|
||||
void append_kill();
|
||||
|
||||
Vector<sock_filter> m_filter;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ endif()
|
|||
target_include_directories(imagedecoderservice PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../..)
|
||||
target_include_directories(imagedecoderservice PRIVATE ${LADYBIRD_SOURCE_DIR}/Services/)
|
||||
|
||||
target_link_libraries(ImageDecoder PRIVATE imagedecoderservice LibCore LibMain LibThreading)
|
||||
target_link_libraries(ImageDecoder PRIVATE imagedecoderservice LibCore LibMain LibSandbox LibThreading)
|
||||
target_link_libraries(imagedecoderservice PRIVATE LibCore LibGfx LibIPC LibImageDecoderClient LibMain LibSync LibThreading)
|
||||
|
||||
if (WIN32)
|
||||
|
|
|
|||
|
|
@ -4,497 +4,25 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Platform.h>
|
||||
#include <AK/ScopeGuard.h>
|
||||
#include <ImageDecoder/Sandbox.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/audit.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/landlock.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/seccomp.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(__GLIBC__)
|
||||
# include <malloc.h>
|
||||
#endif
|
||||
#include <LibSandbox/Sandbox.h>
|
||||
#include <LibSandbox/Seccomp.h>
|
||||
|
||||
namespace ImageDecoder {
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(__x86_64__)
|
||||
static constexpr u32 audit_architecture = AUDIT_ARCH_X86_64;
|
||||
#elif defined(__aarch64__)
|
||||
static constexpr u32 audit_architecture = AUDIT_ARCH_AARCH64;
|
||||
#elif defined(__riscv) && __riscv_xlen == 64
|
||||
static constexpr u32 audit_architecture = AUDIT_ARCH_RISCV64;
|
||||
#else
|
||||
# error "Add 64-bit seccomp audit architecture for this Linux architecture"
|
||||
#endif
|
||||
|
||||
static constexpr u32 thread_clone_required_flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD;
|
||||
static constexpr u32 thread_clone_allowed_flags = thread_clone_required_flags
|
||||
| CLONE_SYSVSEM
|
||||
| CLONE_SETTLS
|
||||
| CLONE_PARENT_SETTID
|
||||
| CLONE_CHILD_CLEARTID
|
||||
| CLONE_DETACHED
|
||||
| CLONE_CHILD_SETTID;
|
||||
|
||||
#define SECCOMP_LOAD_SYSCALL_NR BPF_STMT(BPF_LD | BPF_W | BPF_ABS, static_cast<unsigned int>(offsetof(seccomp_data, nr)))
|
||||
#define SECCOMP_LOAD_ARCHITECTURE BPF_STMT(BPF_LD | BPF_W | BPF_ABS, static_cast<unsigned int>(offsetof(seccomp_data, arch)))
|
||||
#define SECCOMP_LOAD_ARGUMENT(index) BPF_STMT(BPF_LD | BPF_W | BPF_ABS, static_cast<unsigned int>(offsetof(seccomp_data, args[(index)])))
|
||||
#define SECCOMP_ALLOW BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)
|
||||
#define SECCOMP_KILL BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS)
|
||||
#define SECCOMP_ERRNO(error) BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ((error) & SECCOMP_RET_DATA))
|
||||
#define SECCOMP_ALLOW_SYSCALL(name) \
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_##name, 0, 1), SECCOMP_ALLOW
|
||||
#define SECCOMP_ALLOW_NOTHING BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 0)
|
||||
#define SECCOMP_ALLOW_SYSCALL_IF_DEFINED(name) \
|
||||
IF_DEFINED_##name(SECCOMP_ALLOW_SYSCALL(name), SECCOMP_ALLOW_NOTHING)
|
||||
#define SECCOMP_DENY_READONLY_OPEN_SYSCALL(name, flags_argument_index) \
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_##name, 0, 5), \
|
||||
SECCOMP_LOAD_ARGUMENT(flags_argument_index), BPF_STMT(BPF_ALU | BPF_AND | BPF_K, ~read_only_open_flags), \
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1), SECCOMP_ERRNO(EACCES), SECCOMP_LOAD_SYSCALL_NR
|
||||
#define SECCOMP_ALLOW_THREAD_CLONE_SYSCALL(name) \
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_##name, 0, 8), \
|
||||
SECCOMP_LOAD_ARGUMENT(0), BPF_STMT(BPF_ALU | BPF_AND | BPF_K, thread_clone_required_flags), \
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, thread_clone_required_flags, 0, 4), SECCOMP_LOAD_ARGUMENT(0), \
|
||||
BPF_STMT(BPF_ALU | BPF_AND | BPF_K, ~thread_clone_allowed_flags), \
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1), SECCOMP_ALLOW, SECCOMP_LOAD_SYSCALL_NR
|
||||
|
||||
#ifdef O_LARGEFILE
|
||||
static constexpr unsigned read_only_open_flags = O_CLOEXEC | O_LARGEFILE;
|
||||
#else
|
||||
static constexpr unsigned read_only_open_flags = O_CLOEXEC;
|
||||
#endif
|
||||
|
||||
#define IF_DEFINED_accept4(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_brk(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_clock_getres(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_clock_gettime(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_clock_nanosleep(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_clone(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_clone3(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_close(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_dup(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_dup3(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_exit(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_exit_group(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_fcntl(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_fcntl64(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_fstat(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_ftruncate(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_futex(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_futex_time64(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getcpu(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getegid(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_geteuid(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getgid(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getpeername(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getpid(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getrandom(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getrlimit(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getrusage(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getsockname(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getsockopt(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_gettid(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_gettimeofday(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_getuid(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_ioctl(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_lseek(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_madvise(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_membarrier(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_memfd_create(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_mmap(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_mmap2(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_mprotect(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_mremap(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_munmap(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_nanosleep(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_pipe2(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_poll(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_ppoll(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_prctl(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_prlimit64(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_pselect6(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_pwrite64(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_read(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_recvfrom(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_recvmmsg(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_recvmsg(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_restart_syscall(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_rseq(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_rt_sigaction(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_rt_sigprocmask(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_rt_sigreturn(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_sched_getaffinity(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_sched_yield(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_sendmmsg(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_sendmsg(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_sendto(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_set_robust_list(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_set_tid_address(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_setsockopt(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_sigaltstack(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_socketpair(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_sysinfo(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_tgkill(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_umask(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_uname(if_defined, if_not_defined) if_defined
|
||||
#define IF_DEFINED_write(if_defined, if_not_defined) if_defined
|
||||
|
||||
#ifndef __NR_accept4
|
||||
# undef IF_DEFINED_accept4
|
||||
# define IF_DEFINED_accept4(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_clock_getres
|
||||
# undef IF_DEFINED_clock_getres
|
||||
# define IF_DEFINED_clock_getres(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_clock_gettime
|
||||
# undef IF_DEFINED_clock_gettime
|
||||
# define IF_DEFINED_clock_gettime(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_clock_nanosleep
|
||||
# undef IF_DEFINED_clock_nanosleep
|
||||
# define IF_DEFINED_clock_nanosleep(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_clone3
|
||||
# undef IF_DEFINED_clone3
|
||||
# define IF_DEFINED_clone3(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_dup3
|
||||
# undef IF_DEFINED_dup3
|
||||
# define IF_DEFINED_dup3(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_fcntl64
|
||||
# undef IF_DEFINED_fcntl64
|
||||
# define IF_DEFINED_fcntl64(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_fstat
|
||||
# undef IF_DEFINED_fstat
|
||||
# define IF_DEFINED_fstat(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_futex_time64
|
||||
# undef IF_DEFINED_futex_time64
|
||||
# define IF_DEFINED_futex_time64(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_getcpu
|
||||
# undef IF_DEFINED_getcpu
|
||||
# define IF_DEFINED_getcpu(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_getrandom
|
||||
# undef IF_DEFINED_getrandom
|
||||
# define IF_DEFINED_getrandom(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_membarrier
|
||||
# undef IF_DEFINED_membarrier
|
||||
# define IF_DEFINED_membarrier(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_memfd_create
|
||||
# undef IF_DEFINED_memfd_create
|
||||
# define IF_DEFINED_memfd_create(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_mmap2
|
||||
# undef IF_DEFINED_mmap2
|
||||
# define IF_DEFINED_mmap2(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_pipe2
|
||||
# undef IF_DEFINED_pipe2
|
||||
# define IF_DEFINED_pipe2(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_poll
|
||||
# undef IF_DEFINED_poll
|
||||
# define IF_DEFINED_poll(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_ppoll
|
||||
# undef IF_DEFINED_ppoll
|
||||
# define IF_DEFINED_ppoll(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_prlimit64
|
||||
# undef IF_DEFINED_prlimit64
|
||||
# define IF_DEFINED_prlimit64(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_pselect6
|
||||
# undef IF_DEFINED_pselect6
|
||||
# define IF_DEFINED_pselect6(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_pwrite64
|
||||
# undef IF_DEFINED_pwrite64
|
||||
# define IF_DEFINED_pwrite64(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_recvmmsg
|
||||
# undef IF_DEFINED_recvmmsg
|
||||
# define IF_DEFINED_recvmmsg(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_restart_syscall
|
||||
# undef IF_DEFINED_restart_syscall
|
||||
# define IF_DEFINED_restart_syscall(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_rseq
|
||||
# undef IF_DEFINED_rseq
|
||||
# define IF_DEFINED_rseq(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_sendmmsg
|
||||
# undef IF_DEFINED_sendmmsg
|
||||
# define IF_DEFINED_sendmmsg(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_sigaltstack
|
||||
# undef IF_DEFINED_sigaltstack
|
||||
# define IF_DEFINED_sigaltstack(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_sysinfo
|
||||
# undef IF_DEFINED_sysinfo
|
||||
# define IF_DEFINED_sysinfo(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
#ifndef __NR_umask
|
||||
# undef IF_DEFINED_umask
|
||||
# define IF_DEFINED_umask(if_defined, if_not_defined) if_not_defined
|
||||
#endif
|
||||
|
||||
#define SECCOMP_ALLOW_MMAP_WITHOUT_EXEC(name, protection_argument) \
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_##name, 0, 5), \
|
||||
SECCOMP_LOAD_ARGUMENT(protection_argument), \
|
||||
BPF_STMT(BPF_ALU | BPF_AND | BPF_K, PROT_EXEC), \
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1), \
|
||||
SECCOMP_ALLOW, \
|
||||
SECCOMP_LOAD_SYSCALL_NR
|
||||
|
||||
#define SECCOMP_ALLOW_IOCTL_COMMAND(command) \
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_ioctl, 0, 5), \
|
||||
SECCOMP_LOAD_ARGUMENT(1), \
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, command, 0, 1), \
|
||||
SECCOMP_ALLOW, \
|
||||
SECCOMP_LOAD_SYSCALL_NR, \
|
||||
BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 0)
|
||||
|
||||
#define SECCOMP_ALLOW_FCNTL_COMMAND(command) \
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_fcntl, 0, 5), \
|
||||
SECCOMP_LOAD_ARGUMENT(1), \
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, command, 0, 1), \
|
||||
SECCOMP_ALLOW, \
|
||||
SECCOMP_LOAD_SYSCALL_NR, \
|
||||
BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 0)
|
||||
|
||||
ErrorOr<void> install_no_new_privileges()
|
||||
{
|
||||
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0)
|
||||
return Error::from_syscall("prctl(PR_SET_NO_NEW_PRIVS)"sv, errno);
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> configure_runtime_for_sandbox()
|
||||
{
|
||||
#if defined(__GLIBC__) && defined(M_ARENA_MAX) && !defined(HAS_ADDRESS_SANITIZER)
|
||||
if (mallopt(M_ARENA_MAX, 4) == 0)
|
||||
return Error::from_string_literal("mallopt(M_ARENA_MAX) failed");
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> restrict_filesystem_with_landlock()
|
||||
{
|
||||
#if defined(__NR_landlock_create_ruleset) && defined(__NR_landlock_restrict_self)
|
||||
auto landlock_abi = syscall(__NR_landlock_create_ruleset, nullptr, 0, LANDLOCK_CREATE_RULESET_VERSION);
|
||||
if (landlock_abi < 0) {
|
||||
if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
|
||||
return {};
|
||||
return Error::from_syscall("landlock_create_ruleset(LANDLOCK_CREATE_RULESET_VERSION)"sv, errno);
|
||||
}
|
||||
if (landlock_abi == 0)
|
||||
return {};
|
||||
|
||||
landlock_ruleset_attr ruleset_attributes {};
|
||||
ruleset_attributes.handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE
|
||||
| LANDLOCK_ACCESS_FS_WRITE_FILE
|
||||
| LANDLOCK_ACCESS_FS_READ_FILE
|
||||
| LANDLOCK_ACCESS_FS_READ_DIR
|
||||
| LANDLOCK_ACCESS_FS_REMOVE_DIR
|
||||
| LANDLOCK_ACCESS_FS_REMOVE_FILE
|
||||
| LANDLOCK_ACCESS_FS_MAKE_CHAR
|
||||
| LANDLOCK_ACCESS_FS_MAKE_DIR
|
||||
| LANDLOCK_ACCESS_FS_MAKE_REG
|
||||
| LANDLOCK_ACCESS_FS_MAKE_SOCK
|
||||
| LANDLOCK_ACCESS_FS_MAKE_FIFO
|
||||
| LANDLOCK_ACCESS_FS_MAKE_BLOCK
|
||||
| LANDLOCK_ACCESS_FS_MAKE_SYM;
|
||||
|
||||
# ifdef LANDLOCK_ACCESS_FS_REFER
|
||||
if (landlock_abi >= 2)
|
||||
ruleset_attributes.handled_access_fs |= LANDLOCK_ACCESS_FS_REFER;
|
||||
# endif
|
||||
# ifdef LANDLOCK_ACCESS_FS_TRUNCATE
|
||||
if (landlock_abi >= 3)
|
||||
ruleset_attributes.handled_access_fs |= LANDLOCK_ACCESS_FS_TRUNCATE;
|
||||
# endif
|
||||
# if defined(LANDLOCK_ACCESS_NET_BIND_TCP) && defined(LANDLOCK_ACCESS_NET_CONNECT_TCP)
|
||||
if (landlock_abi >= 4)
|
||||
ruleset_attributes.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | LANDLOCK_ACCESS_NET_CONNECT_TCP;
|
||||
|
||||
auto ruleset_attributes_size = landlock_abi >= 4
|
||||
? sizeof(ruleset_attributes)
|
||||
: offsetof(landlock_ruleset_attr, handled_access_net);
|
||||
# else
|
||||
auto ruleset_attributes_size = sizeof(ruleset_attributes);
|
||||
# endif
|
||||
auto ruleset_fd = syscall(__NR_landlock_create_ruleset, &ruleset_attributes, ruleset_attributes_size, 0);
|
||||
if (ruleset_fd < 0)
|
||||
return Error::from_syscall("landlock_create_ruleset"sv, errno);
|
||||
|
||||
ArmedScopeGuard close_ruleset_fd = [&] {
|
||||
close(static_cast<int>(ruleset_fd));
|
||||
};
|
||||
|
||||
if (syscall(__NR_landlock_restrict_self, ruleset_fd, 0) < 0)
|
||||
return Error::from_syscall("landlock_restrict_self"sv, errno);
|
||||
#endif
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> install_seccomp_filter()
|
||||
{
|
||||
sock_filter filter[] = {
|
||||
SECCOMP_LOAD_ARCHITECTURE,
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, audit_architecture, 1, 0),
|
||||
SECCOMP_KILL,
|
||||
|
||||
SECCOMP_LOAD_SYSCALL_NR,
|
||||
|
||||
// Deny read-only runtime probes without granting filesystem access.
|
||||
#ifdef __NR_open
|
||||
SECCOMP_DENY_READONLY_OPEN_SYSCALL(open, 1),
|
||||
#endif
|
||||
#ifdef __NR_openat
|
||||
SECCOMP_DENY_READONLY_OPEN_SYSCALL(openat, 2),
|
||||
#endif
|
||||
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(read),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(write),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(close),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(poll),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(ppoll),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(pselect6),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(recvmsg),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(recvfrom),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(recvmmsg),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(sendmsg),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(sendto),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(sendmmsg),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(socketpair),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(getsockopt),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(setsockopt),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(getsockname),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(getpeername),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(fstat),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(dup),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(dup3),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(pipe2),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(ftruncate),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(lseek),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(pwrite64),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(memfd_create),
|
||||
|
||||
SECCOMP_ALLOW_MMAP_WITHOUT_EXEC(mmap, 2),
|
||||
#ifdef __NR_mmap2
|
||||
SECCOMP_ALLOW_MMAP_WITHOUT_EXEC(mmap2, 2),
|
||||
#endif
|
||||
SECCOMP_ALLOW_MMAP_WITHOUT_EXEC(mprotect, 2),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(mremap),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(munmap),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(madvise),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(brk),
|
||||
|
||||
SECCOMP_ALLOW_FCNTL_COMMAND(F_GETFD),
|
||||
SECCOMP_ALLOW_FCNTL_COMMAND(F_SETFD),
|
||||
SECCOMP_ALLOW_FCNTL_COMMAND(F_GETFL),
|
||||
SECCOMP_ALLOW_FCNTL_COMMAND(F_SETFL),
|
||||
SECCOMP_ALLOW_IOCTL_COMMAND(FIONBIO),
|
||||
|
||||
#ifdef __NR_clone
|
||||
SECCOMP_ALLOW_THREAD_CLONE_SYSCALL(clone),
|
||||
#endif
|
||||
#ifdef __NR_clone3
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_clone3, 0, 1),
|
||||
SECCOMP_ERRNO(ENOSYS),
|
||||
#endif
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(futex),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(futex_time64),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(set_tid_address),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(set_robust_list),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(rseq),
|
||||
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(rt_sigaction),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(rt_sigprocmask),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(rt_sigreturn),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(sigaltstack),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(tgkill),
|
||||
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(clock_gettime),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(clock_getres),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(clock_nanosleep),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(nanosleep),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(restart_syscall),
|
||||
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(getpid),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(gettid),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(getuid),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(geteuid),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(getgid),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(getegid),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(getrandom),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(gettimeofday),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(getrlimit),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(prlimit64),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(getrusage),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(sched_getaffinity),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(sched_yield),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(getcpu),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(membarrier),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(sysinfo),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(uname),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(umask),
|
||||
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(prctl),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(exit),
|
||||
SECCOMP_ALLOW_SYSCALL_IF_DEFINED(exit_group),
|
||||
|
||||
SECCOMP_KILL,
|
||||
};
|
||||
|
||||
sock_fprog program {
|
||||
.len = static_cast<unsigned short>(sizeof(filter) / sizeof(filter[0])),
|
||||
.filter = filter,
|
||||
};
|
||||
|
||||
#ifdef __NR_seccomp
|
||||
if (syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, &program) < 0)
|
||||
return Error::from_syscall("seccomp(SECCOMP_SET_MODE_FILTER)"sv, errno);
|
||||
#else
|
||||
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &program) < 0)
|
||||
return Error::from_syscall("prctl(PR_SET_SECCOMP)"sv, errno);
|
||||
#endif
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ErrorOr<void> apply_sandbox()
|
||||
{
|
||||
TRY(install_no_new_privileges());
|
||||
TRY(configure_runtime_for_sandbox());
|
||||
TRY(restrict_filesystem_with_landlock());
|
||||
TRY(install_seccomp_filter());
|
||||
TRY(Sandbox::install_no_new_privileges());
|
||||
TRY(Sandbox::configure_runtime());
|
||||
TRY(Sandbox::restrict_filesystem_with_landlock());
|
||||
|
||||
Sandbox::SeccompPolicy policy;
|
||||
policy.deny_readonly_filesystem_probes();
|
||||
policy.allow_file_descriptor_operations();
|
||||
policy.allow_ipc();
|
||||
policy.allow_common_runtime();
|
||||
TRY(policy.install());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue