#include "lsm-util.h"
#include "manager.h"
#include "memory-util.h"
+#include "serialize.h"
#include "string-table.h"
/* DMVERITY_DEVICES_MAX lives in bpf-restrict-fsaccess.h for sharing with tests. */
return (supported = true);
}
+/* Partial deserialization (some FDs but not all) is fatal: continuing
+ * would leave enforcement incomplete. */
+static int restrict_fsaccess_have_deserialized_fds(Manager *m) {
+ size_t count = 0;
+
+ assert(m);
+
+ FOREACH_ELEMENT(fd, m->restrict_fsaccess_link_fds)
+ if (*fd >= 0)
+ count++;
+
+ if (count == 0)
+ return 0;
+ if (count == ELEMENTSOF(m->restrict_fsaccess_link_fds))
+ return 1;
+
+ return log_error_errno(SYNTHETIC_ERRNO(EBADFD),
+ "bpf-restrict-fsaccess: Only %zu of %zu link FDs deserialized, refusing to continue with partial enforcement.",
+ count, ELEMENTSOF(m->restrict_fsaccess_link_fds));
+}
+
/* Close the initramfs trust window after switch_root by clearing initramfs_s_dev
* in the BPF .bss map. The .bss is a BPF_F_MMAPABLE array map — mmap it and do
* a single aligned 4-byte store instead of a full-value read-modify-write via
return 0;
}
+static int bpf_get_map_id(int fd, uint32_t *ret_id) {
+ struct bpf_map_info info = {};
+ uint32_t len = sizeof(info);
+ int r;
+
+ if (fd < 0)
+ return -EBADF;
+
+ assert(ret_id);
+
+ r = sym_bpf_obj_get_info_by_fd(fd, &info, &len);
+ if (r < 0)
+ return r;
+
+ *ret_id = info.id;
+ return 0;
+}
+
+/* Validate that deserialized FDs actually reference our LSM BPF links. A
+ * corrupted serialization file could leave FDs pointing at arbitrary kernel
+ * objects; a stale FD could point at a BPF link of an entirely different type
+ * (e.g. kprobe-multi). Verify both link type and attach type so a substituted
+ * FD that happens to be a BPF link still fails the check. */
+static int restrict_fsaccess_validate_deserialized_fds(Manager *m) {
+ int r;
+
+ assert(m);
+
+ r = dlopen_bpf(LOG_WARNING);
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "bpf-restrict-fsaccess: Failed to load libbpf for FD validation, aborting.");
+
+ FOREACH_ELEMENT(fd, m->restrict_fsaccess_link_fds) {
+ struct bpf_link_info info = {};
+ uint32_t len = sizeof(info);
+ const char *name = restrict_fsaccess_link_names[fd - m->restrict_fsaccess_link_fds];
+
+ r = sym_bpf_obj_get_info_by_fd(*fd, &info, &len);
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "bpf-restrict-fsaccess: Deserialized FD for %s is not a valid BPF object, aborting.",
+ name);
+
+ if (info.type != BPF_LINK_TYPE_TRACING || info.tracing.attach_type != BPF_LSM_MAC)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "bpf-restrict-fsaccess: Deserialized FD for %s is not an LSM tracing link (type=%u attach=%u), aborting.",
+ name, info.type, info.tracing.attach_type);
+ }
+
+ if (m->restrict_fsaccess_bss_map_fd >= 0) {
+ uint32_t id;
+
+ r = bpf_get_map_id(m->restrict_fsaccess_bss_map_fd, &id);
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "bpf-restrict-fsaccess: Deserialized FD for .bss map is not a valid BPF map, aborting.");
+ }
+
+ return 0;
+}
+
int bpf_restrict_fsaccess_setup(Manager *m) {
_cleanup_(restrict_fsaccess_bpf_freep) struct restrict_fsaccess_bpf *obj = NULL;
int r;
if (!MANAGER_IS_SYSTEM(m) || m->restrict_filesystem_access <= RESTRICT_FILESYSTEM_ACCESS_NO)
return 0;
+ r = restrict_fsaccess_have_deserialized_fds(m);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ log_info("bpf-restrict-fsaccess: Recovered link FDs from previous exec, programs still attached.");
+
+ r = restrict_fsaccess_validate_deserialized_fds(m);
+ if (r < 0)
+ return r;
+ if (m->switching_root) {
+ if (m->restrict_fsaccess_bss_map_fd < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADF),
+ "bpf-restrict-fsaccess: Cannot clear initramfs trust after switch_root.");
+ r = restrict_fsaccess_clear_initramfs_trust(m->restrict_fsaccess_bss_map_fd);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+ }
+
/* Fresh setup: verify BPF LSM is available */
if (!bpf_restrict_fsaccess_supported())
return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
return restrict_fsaccess_clear_initramfs_trust(m->restrict_fsaccess_bss_map_fd);
}
+int bpf_restrict_fsaccess_serialize(Manager *m, FILE *f, FDSet *fds) {
+ int r;
+
+ assert(m);
+ assert(f);
+ assert(fds);
+
+ if (!MANAGER_IS_SYSTEM(m) || m->restrict_filesystem_access <= RESTRICT_FILESYSTEM_ACCESS_NO)
+ return 0;
+
+ FOREACH_ELEMENT(fd, m->restrict_fsaccess_link_fds) {
+ r = serialize_fd(f, fds, restrict_fsaccess_link_names[fd - m->restrict_fsaccess_link_fds], *fd);
+ if (r < 0)
+ return r;
+ }
+
+ r = serialize_fd(f, fds, "restrict-fsaccess-bss-map", m->restrict_fsaccess_bss_map_fd);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
#else /* ! BPF_FRAMEWORK || ! HAVE_LSM_INTEGRITY_TYPE */
bool bpf_restrict_fsaccess_supported(void) {
return 0;
}
+int bpf_restrict_fsaccess_serialize(Manager *m, FILE *f, FDSet *fds) {
+ return 0;
+}
+
#endif
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "alloc-util.h"
+#include "bpf-restrict-fsaccess.h"
#include "dbus.h"
#include "dynamic-user.h"
#include "fd-util.h"
if (r < 0)
return r;
+ r = bpf_restrict_fsaccess_serialize(m, f, fds);
+ if (r < 0)
+ return r;
+
(void) fputc('\n', f);
HASHMAP_FOREACH_KEY(u, t, m->units) {
manager_deserialize_uid_refs_one_internal(&m->gid_refs, value);
}
+static void deserialize_restrict_fsaccess(Manager *m, const char *l, FDSet *fds) {
+ const char *val;
+ int fd;
+
+ FOREACH_ELEMENT(name, restrict_fsaccess_link_names) {
+ val = startswith(l, *name);
+ if (!val)
+ continue;
+ val = startswith(val, "=");
+ if (!val)
+ continue;
+ fd = deserialize_fd(fds, val);
+ if (fd < 0) {
+ log_warning_errno(fd, "bpf-restrict-fsaccess: Failed to deserialize FD for %s: %m", *name);
+ return;
+ }
+ close_and_replace(m->restrict_fsaccess_link_fds[name - restrict_fsaccess_link_names], fd);
+ return;
+ }
+
+ val = startswith(l, "restrict-fsaccess-bss-map=");
+ if (!val)
+ return;
+
+ fd = deserialize_fd(fds, val);
+ if (fd < 0) {
+ log_warning_errno(fd, "bpf-restrict-fsaccess: Failed to deserialize FD for .bss map: %m");
+ return;
+ }
+ close_and_replace(m->restrict_fsaccess_bss_map_fd, fd);
+}
+
int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
int r;
else
(void) varlink_server_deserialize_one(m->varlink_server, val, fds);
- } else if ((val = startswith(l, "dump-ratelimit=")))
+ } else if (startswith(l, "restrict-fsaccess-"))
+ deserialize_restrict_fsaccess(m, l, fds);
+ else if ((val = startswith(l, "dump-ratelimit=")))
deserialize_ratelimit(&m->dump_ratelimit, "dump-ratelimit", val);
else if ((val = startswith(l, "reload-reexec-ratelimit=")))
deserialize_ratelimit(&m->reload_reexec_ratelimit, "reload-reexec-ratelimit", val);
DLSYM_PROTOTYPE(bpf_map_get_fd_by_id) = NULL;
DLSYM_PROTOTYPE(bpf_map_lookup_elem) = NULL;
DLSYM_PROTOTYPE(bpf_map_update_elem) = NULL;
+DLSYM_PROTOTYPE(bpf_obj_get_info_by_fd) = NULL;
DLSYM_PROTOTYPE(bpf_object__attach_skeleton) = NULL;
DLSYM_PROTOTYPE(bpf_object__destroy_skeleton) = NULL;
DLSYM_PROTOTYPE(bpf_object__detach_skeleton) = NULL;
DLSYM_ARG(bpf_map_get_fd_by_id),
DLSYM_ARG(bpf_map_lookup_elem),
DLSYM_ARG(bpf_map_update_elem),
+ DLSYM_ARG(bpf_obj_get_info_by_fd),
DLSYM_ARG(bpf_object__attach_skeleton),
DLSYM_ARG(bpf_object__destroy_skeleton),
DLSYM_ARG(bpf_object__detach_skeleton),