/* * lockbox_common.h - shared Landlock LSM infrastructure * https://rawnix.org * * Copyright (c) 2025 zorz@gmx.com * SPDX-License-Identifier: BSD-2-Clause */ #ifndef LOCKBOX_COMMON_H #define LOCKBOX_COMMON_H #include #include #include #include #include #include #include #include "util.h" /* strarray_t */ #ifndef O_PATH #define O_PATH 010000000 #endif /* syscall() is declared in under _GNU_SOURCE; * provide a forward declaration for strict C99 + _POSIX_C_SOURCE builds */ extern long syscall(long number, ...); /* ── Syscall numbers (Linux 5.13+) ──────────────────────────────────── */ #ifndef SYS_landlock_create_ruleset #define SYS_landlock_create_ruleset 444 #endif #ifndef SYS_landlock_add_rule #define SYS_landlock_add_rule 445 #endif #ifndef SYS_landlock_restrict_self #define SYS_landlock_restrict_self 446 #endif /* ── Access right bundles ────────────────────────────────────────────── */ /* * ACCESS_RO: read + execute (used for installed package paths) * ACCESS_RW: full access (used for build directories) * * LANDLOCK_ACCESS_FS_TRUNCATE was added in ABI v3 (kernel 6.2). * It is included here and masked out at runtime if abi < 3. */ #define ACCESS_RO ( \ LANDLOCK_ACCESS_FS_READ_FILE | \ LANDLOCK_ACCESS_FS_READ_DIR | \ LANDLOCK_ACCESS_FS_EXECUTE) #define ACCESS_RW (ACCESS_RO | \ LANDLOCK_ACCESS_FS_WRITE_FILE | \ LANDLOCK_ACCESS_FS_REMOVE_FILE | \ LANDLOCK_ACCESS_FS_REMOVE_DIR | \ 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_TRUNCATE) /* ── Syscall wrappers ────────────────────────────────────────────────── */ static inline int ll_create_ruleset( const struct landlock_ruleset_attr *attr, size_t size, uint32_t flags) { return (int)syscall(SYS_landlock_create_ruleset, attr, size, flags); } static inline int ll_add_rule(int ruleset_fd, enum landlock_rule_type type, const void *attr, uint32_t flags) { return (int)syscall(SYS_landlock_add_rule, ruleset_fd, type, attr, flags); } static inline int ll_restrict_self(int ruleset_fd) { return (int)syscall(SYS_landlock_restrict_self, ruleset_fd, 0); } /* * Add a Landlock read rule for path with access rights. * Returns 0 on success, -1 on error. * If path does not exist, silently succeeds (package may be partially * installed or the entry is a directory that was removed). */ static inline int ll_add_path(int ruleset_fd, const char *path, uint64_t access) { struct landlock_path_beneath_attr attr; int fd, ret; fd = open(path, O_PATH | O_CLOEXEC); if (fd < 0) return 0; /* missing path — skip silently */ attr.allowed_access = access; attr.parent_fd = fd; ret = ll_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &attr, 0); close(fd); return ret; } /* ── Lockbox profiles ────────────────────────────────────────────────── */ typedef enum { LOCKBOX_PROFILE_STANDARD = 0, /* full base toolchain + declared deps */ LOCKBOX_PROFILE_CARGO = 1, /* rust + musl + vendor + CLONE_NEWNET */ } lockbox_profile_t; /* ── Implicit base toolchain ─────────────────────────────────────────── */ /* * Packages always allowed, regardless of MAKEPKG depends/makedepends. * These are the core build tools present in every rawnix.org build environment. */ extern const char *lockbox_base[]; /* * Cargo profile base — reuses lockbox_base at runtime. * llvm-toolchain (linked by cargo) transitively requires the full standard * base. rust itself comes from the port's depends=(rust). */ /* ── mkpkg db walker ──────────────────────────────────────────────── */ /* * Walk /var/lib/pkg/db and add Landlock read rules for every file owned * by packages in names[]. * * names[0..base_count-1] lockbox_base — missing packages silently skipped * names[base_count..total-1] declared deps — missing packages are fatal * * Returns 0 on success, -1 if a declared dep is not installed. */ int lb_add_rules_for_packages(int ruleset_fd, const char **names, int name_count, int base_count); /* * As above, with a third severity region for closure-derived names: * * names[0 .. base_count) base — missing warns * names[base_count .. implied) declared — missing is fatal * names[implied .. name_count) implied — missing warns * * lb_add_rules_for_packages() is this with implied == name_count. */ int lb_add_rules_ex(int ruleset_fd, const char **names, int name_count, int base_count, int implied_start); /* * Transitive closure of declared[] over the "depends =" edges in * PKG_DBDIR/deps.db, excluding the declared names themselves and * anything in the implied-deny list. * * Returns a strarray the caller frees with strarray_free(), or NULL when * deps.db is absent or nothing was implied — in which case the sandbox is * exactly what it was before closure expansion existed. */ strarray_t *lb_expand_deps(const char **declared, int declared_count); /* * Set up Landlock ruleset for cargo profile: * - rust + musl from pkgdb (base), declared deps (fatal if missing) * - $SRC/vendor/ and $SRC/.cargo/ read-only * - $CARGO_HOME/ selective read (registry/ and git/ excluded) * - $SRC/, $PKG/, /tmp read-write * - /proc, /dev, /etc/ssl read-only * * Returns 0 on success, -1 on error. */ int lb_setup_cargo_ruleset(int ruleset_fd, uint64_t all_rw, const char **dep_names, int dep_count); /* convenience: max path length for lockbox internals */ #define LB_PATH_MAX 4096 #endif /* LOCKBOX_COMMON_H */