[sandbox] Add backwards compatibility support for Landlock

* Support disabling Landlock at compile time or runtime if unsupported,
  without disabling seccomp
* Support older Landlock ABI versions
* Support an extra arbitrary RW path, inteded for the core dump dir
* Support database locations other than the working directory
This commit is contained in:
dongresource 2024-10-12 21:42:03 +02:00
parent 8137921154
commit c196171034
3 changed files with 104 additions and 32 deletions

View File

@ -6,6 +6,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <fcntl.h> #include <fcntl.h>
#include <filesystem>
#include <sys/prctl.h> #include <sys/prctl.h>
#include <sys/ptrace.h> #include <sys/ptrace.h>
#include <sys/mman.h> // for mmap() args #include <sys/mman.h> // for mmap() args
@ -18,7 +20,9 @@
#include <linux/audit.h> #include <linux/audit.h>
#include <linux/net.h> // for socketcall() args #include <linux/net.h> // for socketcall() args
#ifndef CONFIG_NOLANDLOCK
#include <linux/landlock.h> #include <linux/landlock.h>
#endif
/* /*
* Macros adapted from https://outflux.net/teach-seccomp/ * Macros adapted from https://outflux.net/teach-seccomp/
@ -300,25 +304,50 @@ static sock_fprog prog = {
ARRLEN(filter), filter ARRLEN(filter), filter
}; };
// Our own wrapper for the seccomp() syscall.
int seccomp(unsigned int operation, unsigned int flags, void *args) {
return syscall(__NR_seccomp, operation, flags, args);
}
#ifndef CONFIG_NOLANDLOCK
// Support compilation on systems that only have older Landlock headers.
#ifndef LANDLOCK_ACCESS_FS_REFER
#define LANDLOCK_ACCESS_FS_REFER 0
#endif
#ifndef LANDLOCK_ACCESS_FS_TRUNCATE
#define LANDLOCK_ACCESS_FS_TRUNCATE 0
#endif
struct landlock_ruleset_attr ruleset_attr = { struct landlock_ruleset_attr ruleset_attr = {
.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE
| LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE
| LANDLOCK_ACCESS_FS_READ_DIR | LANDLOCK_ACCESS_FS_READ_DIR
| LANDLOCK_ACCESS_FS_REMOVE_FILE
| LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_MAKE_REG
| LANDLOCK_ACCESS_FS_MAKE_DIR
| LANDLOCK_ACCESS_FS_MAKE_SYM
| LANDLOCK_ACCESS_FS_MAKE_SOCK
| LANDLOCK_ACCESS_FS_MAKE_FIFO
| LANDLOCK_ACCESS_FS_MAKE_BLOCK
| LANDLOCK_ACCESS_FS_REMOVE_FILE
| LANDLOCK_ACCESS_FS_REMOVE_DIR
| LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_TRUNCATE
| LANDLOCK_ACCESS_FS_REFER
}; };
uint64_t landlock_perms = LANDLOCK_ACCESS_FS_READ_FILE
| LANDLOCK_ACCESS_FS_WRITE_FILE
| LANDLOCK_ACCESS_FS_TRUNCATE
| LANDLOCK_ACCESS_FS_MAKE_REG
| LANDLOCK_ACCESS_FS_REMOVE_FILE;
int landlock_fd; int landlock_fd;
bool landlock_supported;
/* /*
* Our own wrappers for sandboxing syscalls. * Our own wrappers for Landlock syscalls.
*/ */
int seccomp(unsigned int operation, unsigned int flags, void *args) {
return syscall(__NR_seccomp, operation, flags, args);
}
int landlock_create_ruleset(const struct landlock_ruleset_attr *attr, size_t size, uint32_t flags) { int landlock_create_ruleset(const struct landlock_ruleset_attr *attr, size_t size, uint32_t flags) {
return syscall(__NR_landlock_create_ruleset, attr, size, flags); return syscall(__NR_landlock_create_ruleset, attr, size, flags);
} }
@ -331,16 +360,14 @@ int landlock_restrict_self(int ruleset_fd, uint32_t flags) {
return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); return syscall(__NR_landlock_restrict_self, ruleset_fd, flags);
} }
void landlock_path(std::string path, uint32_t perms) { static void landlock_path(std::string path, uint32_t perms) {
struct landlock_path_beneath_attr path_beneath = { struct landlock_path_beneath_attr path_beneath = {
.allowed_access = perms .allowed_access = perms
}; };
std::cout << "Landlock path: " << path << std::endl;
path_beneath.parent_fd = open(path.c_str(), O_PATH|O_CLOEXEC); path_beneath.parent_fd = open(path.c_str(), O_PATH|O_CLOEXEC);
if (path_beneath.parent_fd < 0) { if (path_beneath.parent_fd < 0) {
perror("open"); perror(path.c_str());
exit(1); exit(1);
} }
@ -352,23 +379,73 @@ void landlock_path(std::string path, uint32_t perms) {
close(path_beneath.parent_fd); close(path_beneath.parent_fd);
} }
void sandbox_init() { static bool landlock_detect() {
if (!settings::SANDBOX) { int abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
std::cout << "[WARN] Running without a sandbox" << std::endl;
return; if (abi < 0) {
if (errno == ENOSYS || errno == EOPNOTSUPP) {
std::cout << "[WARN] No Landlock support on this system" << std::endl;
return false;
}
perror("landlock_create_ruleset");
exit(1);
} }
std::cout << "[INFO] Landlock ABI version: " << abi << std::endl;
switch (abi) {
case 1:
ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER;
landlock_perms &= ~LANDLOCK_ACCESS_FS_REFER;
// fallthrough
case 2:
ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE;
landlock_perms &= ~LANDLOCK_ACCESS_FS_TRUNCATE;
}
return true;
}
static void landlock_init() {
std::cout << "[INFO] Setting up Landlock sandbox..." << std::endl; std::cout << "[INFO] Setting up Landlock sandbox..." << std::endl;
landlock_supported = landlock_detect();
if (!landlock_supported)
return;
landlock_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); landlock_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
if (landlock_fd < 0) { if (landlock_fd < 0) {
perror("landlock_create_ruleset"); perror("landlock_create_ruleset");
exit(1); exit(1);
} }
landlock_path(".", ruleset_attr.handled_access_fs); std::string dbdir = std::filesystem::path(settings::DBPATH).parent_path();
// for the DB files (we can't rely on them being in the working directory)
landlock_path(dbdir == "" ? "." : dbdir, landlock_perms);
// for writing the gruntwork file
landlock_path(settings::TDATADIR, landlock_perms);
// for passowrd salting during account creation
landlock_path("/dev/urandom", LANDLOCK_ACCESS_FS_READ_FILE); landlock_path("/dev/urandom", LANDLOCK_ACCESS_FS_READ_FILE);
//landlock_path("/tmp/coredumps", ruleset_attr.handled_access_fs); // for core dumps, optionally
if (settings::SANDBOXEXTRAPATH != "")
landlock_path(settings::SANDBOXEXTRAPATH, landlock_perms);
}
#endif // !CONFIG_NOLANDLOCK
void sandbox_init() {
if (!settings::SANDBOX) {
std::cout << "[WARN] Running without a sandbox" << std::endl;
return;
}
#ifndef CONFIG_NOLANDLOCK
landlock_init();
#else
std::cout << "[WARN] Built without Landlock" << std::endl;
#endif
} }
void sandbox_start() { void sandbox_start() {
@ -377,17 +454,7 @@ void sandbox_start() {
std::cout << "[INFO] Starting seccomp-bpf sandbox..." << std::endl; std::cout << "[INFO] Starting seccomp-bpf sandbox..." << std::endl;
#if 0 // Sandboxing starts in sandbox_thread_start().
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
perror("prctl");
exit(1);
}
if (seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, &prog) < 0) {
perror("seccomp");
exit(1);
}
#endif
} }
void sandbox_thread_start() { void sandbox_thread_start() {
@ -399,17 +466,19 @@ void sandbox_thread_start() {
exit(1); exit(1);
} }
#ifndef CONFIG_NOLANDLOCK
if (landlock_supported) {
if (landlock_restrict_self(landlock_fd, 0)) { if (landlock_restrict_self(landlock_fd, 0)) {
perror("landlock_restrict_self"); perror("landlock_restrict_self");
exit(1); exit(1);
} }
}
#endif
#if 0
if (seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog) < 0) { if (seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog) < 0) {
perror("seccomp"); perror("seccomp");
exit(1); exit(1);
} }
#endif
} }
#endif #endif

View File

@ -9,6 +9,7 @@
// defaults :) // defaults :)
int settings::VERBOSITY = 1; int settings::VERBOSITY = 1;
bool settings::SANDBOX = true; bool settings::SANDBOX = true;
std::string settings::SANDBOXEXTRAPATH = "";
int settings::LOGINPORT = 23000; int settings::LOGINPORT = 23000;
bool settings::APPROVEALLNAMES = true; bool settings::APPROVEALLNAMES = true;
@ -85,6 +86,7 @@ void settings::init() {
VERBOSITY = reader.GetInteger("", "verbosity", VERBOSITY); VERBOSITY = reader.GetInteger("", "verbosity", VERBOSITY);
SANDBOX = reader.GetBoolean("", "sandbox", SANDBOX); SANDBOX = reader.GetBoolean("", "sandbox", SANDBOX);
SANDBOXEXTRAPATH = reader.Get("", "sandboxextrapath", SANDBOXEXTRAPATH);
LOGINPORT = reader.GetInteger("login", "port", LOGINPORT); LOGINPORT = reader.GetInteger("login", "port", LOGINPORT);
APPROVEALLNAMES = reader.GetBoolean("login", "acceptallcustomnames", APPROVEALLNAMES); APPROVEALLNAMES = reader.GetBoolean("login", "acceptallcustomnames", APPROVEALLNAMES);
AUTOCREATEACCOUNTS = reader.GetBoolean("login", "autocreateaccounts", AUTOCREATEACCOUNTS); AUTOCREATEACCOUNTS = reader.GetBoolean("login", "autocreateaccounts", AUTOCREATEACCOUNTS);

View File

@ -5,6 +5,7 @@
namespace settings { namespace settings {
extern int VERBOSITY; extern int VERBOSITY;
extern bool SANDBOX; extern bool SANDBOX;
extern std::string SANDBOXEXTRAPATH;
extern int LOGINPORT; extern int LOGINPORT;
extern bool APPROVEALLNAMES; extern bool APPROVEALLNAMES;
extern bool AUTOCREATEACCOUNTS; extern bool AUTOCREATEACCOUNTS;