1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include <linux/types.h>
6 #include <sys/resource.h>
12 #include "alloc-util.h"
14 #include "cgroup-util.h"
17 #include "filesystems.h"
22 #include "nulstr-util.h"
23 #include "stat-util.h"
27 /* libbpf, clang and llc compile time dependencies are satisfied */
28 #include "bpf-dlopen.h"
31 #include "bpf/restrict_fs/restrict-fs-skel.h"
33 #define CGROUP_HASH_SIZE_MAX 2048
35 static struct restrict_fs_bpf
*restrict_fs_bpf_free(struct restrict_fs_bpf
*obj
) {
36 /* restrict_fs_bpf__destroy handles object == NULL case */
37 (void) restrict_fs_bpf__destroy(obj
);
42 DEFINE_TRIVIAL_CLEANUP_FUNC(struct restrict_fs_bpf
*, restrict_fs_bpf_free
);
44 static bool bpf_can_link_lsm_program(struct bpf_program
*prog
) {
45 _cleanup_(bpf_link_freep
) struct bpf_link
*link
= NULL
;
49 link
= sym_bpf_program__attach_lsm(prog
);
51 /* If bpf_program__attach_lsm fails the resulting value stores libbpf error code instead of memory
52 * pointer. That is the case when the helper is called on architectures where BPF trampoline (hence
53 * BPF_LSM_MAC attach type) is not supported. */
54 return sym_libbpf_get_error(link
) == 0;
57 static int prepare_restrict_fs_bpf(struct restrict_fs_bpf
**ret_obj
) {
58 _cleanup_(restrict_fs_bpf_freep
) struct restrict_fs_bpf
*obj
= NULL
;
59 _cleanup_close_
int inner_map_fd
= -EBADF
;
64 obj
= restrict_fs_bpf__open();
66 return log_error_errno(errno
, "bpf-lsm: Failed to open BPF object: %m");
68 /* TODO Maybe choose a number based on runtime information? */
69 r
= sym_bpf_map__set_max_entries(obj
->maps
.cgroup_hash
, CGROUP_HASH_SIZE_MAX
);
72 return log_error_errno(r
, "bpf-lsm: Failed to resize BPF map '%s': %m",
73 sym_bpf_map__name(obj
->maps
.cgroup_hash
));
75 /* Dummy map to satisfy the verifier */
76 inner_map_fd
= compat_bpf_map_create(BPF_MAP_TYPE_HASH
, NULL
, sizeof(uint32_t), sizeof(uint32_t), 128U, NULL
);
78 return log_error_errno(errno
, "bpf-lsm: Failed to create BPF map: %m");
80 r
= sym_bpf_map__set_inner_map_fd(obj
->maps
.cgroup_hash
, inner_map_fd
);
83 return log_error_errno(r
, "bpf-lsm: Failed to set inner map fd: %m");
85 r
= restrict_fs_bpf__load(obj
);
88 return log_error_errno(r
, "bpf-lsm: Failed to load BPF object: %m");
90 *ret_obj
= TAKE_PTR(obj
);
95 bool lsm_bpf_supported(bool initialize
) {
96 _cleanup_(restrict_fs_bpf_freep
) struct restrict_fs_bpf
*obj
= NULL
;
97 static int supported
= -1;
105 if (!cgroup_bpf_supported())
106 return (supported
= false);
108 r
= lsm_supported("bpf");
110 log_warning_errno(r
, "bpf-lsm: Can't determine whether the BPF LSM module is used: %m");
111 return (supported
= false);
114 log_info_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
115 "bpf-lsm: BPF LSM hook not enabled in the kernel, BPF LSM not supported");
116 return (supported
= false);
119 r
= prepare_restrict_fs_bpf(&obj
);
121 return (supported
= false);
123 if (!bpf_can_link_lsm_program(obj
->progs
.restrict_filesystems
)) {
124 log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
125 "bpf-lsm: Failed to link program; assuming BPF LSM is not available");
126 return (supported
= false);
129 return (supported
= true);
132 int lsm_bpf_setup(Manager
*m
) {
133 _cleanup_(restrict_fs_bpf_freep
) struct restrict_fs_bpf
*obj
= NULL
;
134 _cleanup_(bpf_link_freep
) struct bpf_link
*link
= NULL
;
139 r
= prepare_restrict_fs_bpf(&obj
);
143 link
= sym_bpf_program__attach_lsm(obj
->progs
.restrict_filesystems
);
144 r
= sym_libbpf_get_error(link
);
146 return log_error_errno(r
, "bpf-lsm: Failed to link '%s' LSM BPF program: %m",
147 sym_bpf_program__name(obj
->progs
.restrict_filesystems
));
149 log_info("bpf-lsm: LSM BPF program attached");
151 obj
->links
.restrict_filesystems
= TAKE_PTR(link
);
152 m
->restrict_fs
= TAKE_PTR(obj
);
157 int lsm_bpf_restrict_filesystems(const Set
*filesystems
, uint64_t cgroup_id
, int outer_map_fd
, bool allow_list
) {
158 uint32_t dummy_value
= 1, zero
= 0;
160 const statfs_f_type_t
*magic
;
164 assert(outer_map_fd
>= 0);
166 int inner_map_fd
= compat_bpf_map_create(
171 128U, /* Should be enough for all filesystem types */
173 if (inner_map_fd
< 0)
174 return log_error_errno(errno
, "bpf-lsm: Failed to create inner BPF map: %m");
176 if (sym_bpf_map_update_elem(outer_map_fd
, &cgroup_id
, &inner_map_fd
, BPF_ANY
) != 0)
177 return log_error_errno(errno
, "bpf-lsm: Error populating BPF map: %m");
179 uint32_t allow
= allow_list
;
181 /* Use key 0 to store whether this is an allow list or a deny list */
182 if (sym_bpf_map_update_elem(inner_map_fd
, &zero
, &allow
, BPF_ANY
) != 0)
183 return log_error_errno(errno
, "bpf-lsm: Error initializing map: %m");
185 SET_FOREACH(fs
, filesystems
) {
186 r
= fs_type_from_string(fs
, &magic
);
188 log_warning("bpf-lsm: Invalid filesystem name '%s', ignoring.", fs
);
192 log_debug("bpf-lsm: Restricting filesystem access to '%s'", fs
);
194 for (int i
= 0; i
< FILESYSTEM_MAGIC_MAX
; i
++) {
198 if (sym_bpf_map_update_elem(inner_map_fd
, &magic
[i
], &dummy_value
, BPF_ANY
) != 0) {
199 r
= log_error_errno(errno
, "bpf-lsm: Failed to update BPF map: %m");
201 if (sym_bpf_map_delete_elem(outer_map_fd
, &cgroup_id
) != 0)
202 log_debug_errno(errno
, "bpf-lsm: Failed to delete cgroup entry from BPF map: %m");
212 int lsm_bpf_cleanup(const Unit
*u
) {
216 /* If we never successfully detected support, there is nothing to clean up. */
217 if (!lsm_bpf_supported(/* initialize = */ false))
220 if (!u
->manager
->restrict_fs
)
223 if (u
->cgroup_id
== 0)
226 int fd
= sym_bpf_map__fd(u
->manager
->restrict_fs
->maps
.cgroup_hash
);
228 return log_unit_error_errno(u
, errno
, "bpf-lsm: Failed to get BPF map fd: %m");
230 if (sym_bpf_map_delete_elem(fd
, &u
->cgroup_id
) != 0 && errno
!= ENOENT
)
231 return log_unit_debug_errno(u
, errno
, "bpf-lsm: Failed to delete cgroup entry from LSM BPF map: %m");
236 int lsm_bpf_map_restrict_fs_fd(Unit
*unit
) {
238 assert(unit
->manager
);
240 if (!unit
->manager
->restrict_fs
)
243 return sym_bpf_map__fd(unit
->manager
->restrict_fs
->maps
.cgroup_hash
);
246 void lsm_bpf_destroy(struct restrict_fs_bpf
*prog
) {
247 restrict_fs_bpf__destroy(prog
);
249 #else /* ! BPF_FRAMEWORK */
250 bool lsm_bpf_supported(bool initialize
) {
254 int lsm_bpf_setup(Manager
*m
) {
255 return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
), "bpf-lsm: Failed to set up LSM BPF: %m");
258 int lsm_bpf_restrict_filesystems(const Set
*filesystems
, uint64_t cgroup_id
, int outer_map_fd
, const bool allow_list
) {
259 return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
), "bpf-lsm: Failed to restrict filesystems using LSM BPF: %m");
262 int lsm_bpf_cleanup(const Unit
*u
) {
266 int lsm_bpf_map_restrict_fs_fd(Unit
*unit
) {
270 void lsm_bpf_destroy(struct restrict_fs_bpf
*prog
) {
275 int lsm_bpf_parse_filesystem(
278 FilesystemParseFlags flags
,
280 const char *filename
,
287 if (name
[0] == '@') {
288 const FilesystemSet
*set
;
290 set
= filesystem_set_find(name
);
292 log_syntax(unit
, flags
& FILESYSTEM_PARSE_LOG
? LOG_WARNING
: LOG_DEBUG
, filename
, line
, 0,
293 "bpf-lsm: Unknown filesystem group, ignoring: %s", name
);
297 NULSTR_FOREACH(i
, set
->value
) {
298 /* Call ourselves again, for the group to parse. Note that we downgrade logging here
299 * (i.e. take away the FILESYSTEM_PARSE_LOG flag) since any issues in the group table
300 * are our own problem, not a problem in user configuration data and we shouldn't
301 * pretend otherwise by complaining about them. */
302 r
= lsm_bpf_parse_filesystem(i
, filesystems
, flags
&~ FILESYSTEM_PARSE_LOG
, unit
, filename
, line
);
307 /* If we previously wanted to forbid access to a filesystem and now
308 * we want to allow it, then remove it from the list. */
309 if (!(flags
& FILESYSTEM_PARSE_INVERT
) == !!(flags
& FILESYSTEM_PARSE_ALLOW_LIST
)) {
310 r
= set_put_strdup(filesystems
, name
);
312 return flags
& FILESYSTEM_PARSE_LOG
? log_oom() : -ENOMEM
;
313 if (r
< 0 && r
!= -EEXIST
) /* When already in set, ignore */
316 free(set_remove(*filesystems
, name
));