92 lines
3.0 KiB
C++
92 lines
3.0 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// SPDX-FileCopyrightText: 2025 ai-ad4 and the FreePDFEditor contributors
|
|
//
|
|
// Sandbox.cpp — seccomp-bpf filter for the sandboxed document process.
|
|
// Allow-list: default-deny, permit only memory/thread-sync/exit and the
|
|
// already-open IPC socket fds. DENIED: socket, connect, open/openat, unlink,
|
|
// fork, exec.
|
|
|
|
#include "Sandbox.h"
|
|
|
|
#include <seccomp.h>
|
|
|
|
#include <cerrno>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <string>
|
|
|
|
namespace freepdfeditor::spike::e {
|
|
|
|
namespace {
|
|
|
|
// Add a syscall to the allow-list with the given argument restrictions (or
|
|
// none for a full allow). Returns 0 on success, negative on failure.
|
|
int allow_syscall(scmp_filter_ctx ctx, int nr)
|
|
{
|
|
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, nr, 0) < 0) return -1;
|
|
return 0;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int install_sandbox()
|
|
{
|
|
scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL_PROCESS);
|
|
if (!ctx) {
|
|
std::fprintf(stderr, "seccomp_init failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
// Memory management.
|
|
allow_syscall(ctx, SCMP_SYS(mmap));
|
|
allow_syscall(ctx, SCMP_SYS(munmap));
|
|
allow_syscall(ctx, SCMP_SYS(mprotect));
|
|
allow_syscall(ctx, SCMP_SYS(mremap));
|
|
allow_syscall(ctx, SCMP_SYS(brk));
|
|
allow_syscall(ctx, SCMP_SYS(madvise));
|
|
|
|
// Thread synchronisation and futex.
|
|
allow_syscall(ctx, SCMP_SYS(futex));
|
|
allow_syscall(ctx, SCMP_SYS(set_robust_list));
|
|
|
|
// The IPC channel: read/write/close on already-open fds. We do NOT allow
|
|
// open/openat/socket/connect — the document process works only with fds
|
|
// the UI process handed it.
|
|
allow_syscall(ctx, SCMP_SYS(read));
|
|
allow_syscall(ctx, SCMP_SYS(write));
|
|
allow_syscall(ctx, SCMP_SYS(close));
|
|
allow_syscall(ctx, SCMP_SYS(readv));
|
|
allow_syscall(ctx, SCMP_SYS(writev));
|
|
// poll/ppoll/epoll are needed by Cap'n Proto's event loop.
|
|
allow_syscall(ctx, SCMP_SYS(poll));
|
|
allow_syscall(ctx, SCMP_SYS(ppoll));
|
|
allow_syscall(ctx, SCMP_SYS(epoll_create1));
|
|
allow_syscall(ctx, SCMP_SYS(epoll_ctl));
|
|
allow_syscall(ctx, SCMP_SYS(epoll_wait));
|
|
allow_syscall(ctx, SCMP_SYS(eventfd2));
|
|
allow_syscall(ctx, SCMP_SYS(timerfd_create));
|
|
allow_syscall(ctx, SCMP_SYS(timerfd_settime));
|
|
allow_syscall(ctx, SCMP_SYS(dup)); // Cap'n Proto may dup its socket
|
|
|
|
// Exit / signal.
|
|
allow_syscall(ctx, SCMP_SYS(exit));
|
|
allow_syscall(ctx, SCMP_SYS(exit_group));
|
|
allow_syscall(ctx, SCMP_SYS(rt_sigprocmask));
|
|
allow_syscall(ctx, SCMP_SYS(rt_sigaction));
|
|
allow_syscall(ctx, SCMP_SYS(rt_sigreturn));
|
|
allow_syscall(ctx, SCMP_SYS(sigaltstack));
|
|
|
|
// Getters for things Cap'n Proto's event loop may query.
|
|
allow_syscall(ctx, SCMP_SYS(getpid));
|
|
allow_syscall(ctx, SCMP_SYS(gettid));
|
|
|
|
int rc = seccomp_load(ctx);
|
|
seccomp_release(ctx);
|
|
if (rc < 0) {
|
|
std::fprintf(stderr, "seccomp_load failed: %s\n", std::strerror(errno));
|
|
return -errno;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
} // namespace freepdfeditor::spike::e
|