/* * This might break with something other than glibc, new versions of glibc, i2pd and other libraries, on a different architecture, etc. */ #ifdef SANDBOX #include #include #include #include "Config.h" #include "FS.h" #include "Log.h" #include "Sandbox.h" #ifndef landlock_create_ruleset static inline int landlock_create_ruleset(const struct landlock_ruleset_attr *const attr, const size_t size, const __u32 flags) { return syscall(__NR_landlock_create_ruleset, attr, size, flags); } #endif #ifndef landlock_add_rule static inline int landlock_add_rule(const int ruleset_fd, const enum landlock_rule_type rule_type, const void *const rule_attr, const __u32 flags) { return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr, flags); } #endif #ifndef landlock_restrict_self static inline int landlock_restrict_self(const int ruleset_fd, const __u32 flags) { return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); } #endif namespace i2p { namespace sandbox { int loadSeccomp() { int filter[] = { SCMP_SYS(accept), SCMP_SYS(accept4), SCMP_SYS(arch_prctl), SCMP_SYS(bind), SCMP_SYS(brk), SCMP_SYS(chdir), SCMP_SYS(clock_nanosleep), SCMP_SYS(clone), SCMP_SYS(clone3), SCMP_SYS(close), SCMP_SYS(connect), SCMP_SYS(dup), SCMP_SYS(dup2), SCMP_SYS(dup3), SCMP_SYS(epoll_create), SCMP_SYS(epoll_create1), SCMP_SYS(epoll_ctl), SCMP_SYS(epoll_wait), SCMP_SYS(epoll_pwait), SCMP_SYS(eventfd2), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(fcntl), SCMP_SYS(fstat), SCMP_SYS(ftruncate), SCMP_SYS(futex), SCMP_SYS(getdents64), SCMP_SYS(getgid), SCMP_SYS(getegid), SCMP_SYS(getuid), SCMP_SYS(geteuid), SCMP_SYS(getpeername), SCMP_SYS(getpid), SCMP_SYS(getrandom), SCMP_SYS(getsockname), SCMP_SYS(getsockopt), SCMP_SYS(gettid), SCMP_SYS(ioctl), SCMP_SYS(landlock_add_rule), SCMP_SYS(landlock_create_ruleset), SCMP_SYS(landlock_restrict_self), SCMP_SYS(listen), SCMP_SYS(lseek), SCMP_SYS(madvise), SCMP_SYS(membarrier), SCMP_SYS(mkdir), SCMP_SYS(mkdirat), SCMP_SYS(mmap), SCMP_SYS(mprotect), SCMP_SYS(munmap), SCMP_SYS(nanosleep), SCMP_SYS(newfstatat), SCMP_SYS(open), SCMP_SYS(openat), SCMP_SYS(poll), SCMP_SYS(ppoll), SCMP_SYS(prctl), SCMP_SYS(prlimit64), SCMP_SYS(read), SCMP_SYS(recvfrom), SCMP_SYS(recvmsg), SCMP_SYS(restart_syscall), SCMP_SYS(rseq), SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigprocmask), SCMP_SYS(rt_sigreturn), SCMP_SYS(sched_getaffinity), SCMP_SYS(sched_getparam), SCMP_SYS(sendmsg), SCMP_SYS(sendmmsg), SCMP_SYS(sendto), SCMP_SYS(set_robust_list), SCMP_SYS(set_tid_address), SCMP_SYS(setsid), SCMP_SYS(setsockopt), SCMP_SYS(shutdown), SCMP_SYS(socket), SCMP_SYS(stat), SCMP_SYS(statx), SCMP_SYS(sysinfo), SCMP_SYS(tgkill), SCMP_SYS(timerfd_create), SCMP_SYS(timerfd_settime), SCMP_SYS(umask), SCMP_SYS(unlink), SCMP_SYS(unlinkat), SCMP_SYS(uname), SCMP_SYS(write), SCMP_SYS(writev), }; int rc; scmp_filter_ctx ctx; /* Initialize seccomp */ ctx = seccomp_init(SCMP_ACT_KILL_PROCESS); // Kill the process if a violation occurs if (ctx == NULL) { //LogPrint(eLogError, "Sandbox: Could not initialize seccomp"); std::cerr << "Sandbox: Could not initialize seccomp" << std::endl; seccomp_release(ctx); return false; } /* Load rules */ for (int i = 0; i < (int)(sizeof(filter)/sizeof(int)); i++) { rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, filter[i], 0); if (rc != 0) { //LogPrint(eLogError, "Sandbox: Could not add seccomp rule ", i, ": ", strerror(rc)); std::cerr << "Sandbox: Could not add seccomp rule " << i << ": " << strerror(rc) << std::endl; seccomp_release(ctx); return false; } } /* Load filter */ rc = seccomp_load(ctx); if (rc != 0) { //LogPrint(eLogError, "Sandbox: Could not load seccomp filter: ", strerror(rc)); std::cerr << "Sandbox: Could not load seccomp filter: " << strerror(rc) << std::endl; seccomp_release(ctx); return false; } /* Success */ LogPrint(eLogNone, "Sandbox: Loaded seccomp filter"); seccomp_release(ctx); return true; } int addrule(const char *path, unsigned long long rules, int ruleset_fd) { struct landlock_path_beneath_attr temp; temp.allowed_access = rules; temp.parent_fd = 0; /* Open path file descriptor */ temp.parent_fd = open(path, O_PATH | O_CLOEXEC); if (temp.parent_fd < 0) { //LogPrint(eLogError, "Sandbox: Failed to open ", path, ": ", strerror(errno)); std::cerr << "Sandbox: Failed to open " << path << ": " << strerror(errno) << std::endl; close(temp.parent_fd); close(ruleset_fd); return false; } /* Add rule */ int rc = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &temp, 0); if (rc != 0) { //LogPrint(eLogError, "Sandbox: Failed to add Landlock rule for ", path, ": ", strerror(rc)); std::cerr << "Sandbox: Failed to add Landlock rule for " << path << ": " << strerror(rc) << std::endl; close(temp.parent_fd); close(ruleset_fd); return false; } close(temp.parent_fd); return true; } int loadLandlock() { /* Deny everything by default */ struct landlock_ruleset_attr ruleset_attr = { {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 | LANDLOCK_ACCESS_FS_REFER}, }; /* Check kernel compatibility */ int abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); if (abi < 2) { //LogPrint(eLogError, "Sandbox: Landlock ABI 2 is not supported by this kernel."); std::cerr << "Sandbox: Landlock ABI 2 is not supported by this kernel." << std::endl; return false; } /* Create default ruleset */ int ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); if (ruleset_fd < 0) { //LogPrint(eLogError, "Sandbox: Failed to create Landlock ruleset: ", strerror(ruleset_fd)); std::cerr << "Sandbox: Failed to create Landlock ruleset: " << strerror(ruleset_fd) << std::endl; close(ruleset_fd); return false; } /* Add rules */ if(!addrule(i2p::fs::GetDataDir().c_str(), LANDLOCK_ACCESS_FS_READ_FILE|LANDLOCK_ACCESS_FS_READ_DIR|LANDLOCK_ACCESS_FS_WRITE_FILE|LANDLOCK_ACCESS_FS_MAKE_REG|LANDLOCK_ACCESS_FS_MAKE_DIR|LANDLOCK_ACCESS_FS_REMOVE_FILE, ruleset_fd)) return false; if(!addrule(i2p::fs::GetCertsDir().c_str(), LANDLOCK_ACCESS_FS_READ_FILE|LANDLOCK_ACCESS_FS_READ_DIR, ruleset_fd)) return false; if(!addrule("/var/log/", LANDLOCK_ACCESS_FS_WRITE_FILE|LANDLOCK_ACCESS_FS_MAKE_REG|LANDLOCK_ACCESS_FS_MAKE_DIR, ruleset_fd)) return false; if(!addrule("/lib/", LANDLOCK_ACCESS_FS_READ_FILE|LANDLOCK_ACCESS_FS_READ_DIR, ruleset_fd)) return false; if(!addrule("/usr/lib/", LANDLOCK_ACCESS_FS_READ_FILE|LANDLOCK_ACCESS_FS_READ_DIR, ruleset_fd)) return false; if(!addrule("/usr/share/", LANDLOCK_ACCESS_FS_READ_FILE|LANDLOCK_ACCESS_FS_READ_DIR, ruleset_fd)) return false; if(!addrule("/proc/sys/", LANDLOCK_ACCESS_FS_READ_FILE|LANDLOCK_ACCESS_FS_READ_DIR, ruleset_fd)) return false; if(!addrule("/sys/devices/system/cpu/", LANDLOCK_ACCESS_FS_READ_FILE|LANDLOCK_ACCESS_FS_READ_DIR, ruleset_fd)) return false; if(!addrule("/dev/urandom", LANDLOCK_ACCESS_FS_READ_FILE, ruleset_fd)) return false; if(!addrule("/etc/", LANDLOCK_ACCESS_FS_READ_FILE|LANDLOCK_ACCESS_FS_READ_DIR, ruleset_fd)) return false; /* Load ruleset */ int rc = landlock_restrict_self(ruleset_fd, 0); if (rc != 0) { //LogPrint(eLogError, "Sandbox: Failed to load Landlock ruleset: ", strerror(rc)); std::cerr << "Sandbox: Failed to load Landlock ruleset: " << strerror(rc) << std::endl; close(ruleset_fd); return false; } /* Success */ LogPrint(eLogNone, "Sandbox: Loaded Landlock filter"); close(ruleset_fd); return true; } } } #endif