]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/bpf-lsm.c
nulstr-util: Declare NULSTR_FOREACH() iterator inline
[thirdparty/systemd.git] / src / core / bpf-lsm.c
CommitLineData
184b4f78
ILG
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <errno.h>
4#include <fcntl.h>
5#include <linux/types.h>
6#include <sys/resource.h>
7#include <sys/stat.h>
8#include <sys/time.h>
9#include <sys/types.h>
10#include <unistd.h>
11
12#include "alloc-util.h"
13#include "bpf-lsm.h"
14#include "cgroup-util.h"
15#include "fd-util.h"
16#include "fileio.h"
17#include "filesystems.h"
18#include "log.h"
19#include "manager.h"
20#include "mkdir.h"
21#include "nulstr-util.h"
22#include "stat-util.h"
23#include "strv.h"
24
25#if BPF_FRAMEWORK
26/* libbpf, clang and llc compile time dependencies are satisfied */
27#include "bpf-dlopen.h"
28#include "bpf-link.h"
bb0b01ed 29#include "bpf-util.h"
184b4f78
ILG
30#include "bpf/restrict_fs/restrict-fs-skel.h"
31
32#define CGROUP_HASH_SIZE_MAX 2048
33
34static struct restrict_fs_bpf *restrict_fs_bpf_free(struct restrict_fs_bpf *obj) {
35 /* restrict_fs_bpf__destroy handles object == NULL case */
36 (void) restrict_fs_bpf__destroy(obj);
37
38 return NULL;
39}
40
41DEFINE_TRIVIAL_CLEANUP_FUNC(struct restrict_fs_bpf *, restrict_fs_bpf_free);
42
43static bool bpf_can_link_lsm_program(struct bpf_program *prog) {
44 _cleanup_(bpf_link_freep) struct bpf_link *link = NULL;
45
46 assert(prog);
47
48 link = sym_bpf_program__attach_lsm(prog);
184b4f78 49
f409aa5c
JK
50 /* If bpf_program__attach_lsm fails the resulting value stores libbpf error code instead of memory
51 * pointer. That is the case when the helper is called on architectures where BPF trampoline (hence
52 * BPF_LSM_MAC attach type) is not supported. */
53 return sym_libbpf_get_error(link) == 0;
184b4f78
ILG
54}
55
56static int prepare_restrict_fs_bpf(struct restrict_fs_bpf **ret_obj) {
7ab3c86d 57 _cleanup_(restrict_fs_bpf_freep) struct restrict_fs_bpf *obj = NULL;
184b4f78
ILG
58 _cleanup_close_ int inner_map_fd = -1;
59 int r;
60
61 assert(ret_obj);
62
63 obj = restrict_fs_bpf__open();
64 if (!obj)
b1acbc08 65 return log_error_errno(errno, "bpf-lsm: Failed to open BPF object: %m");
184b4f78
ILG
66
67 /* TODO Maybe choose a number based on runtime information? */
6b8085db 68 r = sym_bpf_map__set_max_entries(obj->maps.cgroup_hash, CGROUP_HASH_SIZE_MAX);
b7cba815
ZJS
69 assert(r <= 0);
70 if (r < 0)
b1acbc08 71 return log_error_errno(r, "bpf-lsm: Failed to resize BPF map '%s': %m",
b7cba815 72 sym_bpf_map__name(obj->maps.cgroup_hash));
184b4f78
ILG
73
74 /* Dummy map to satisfy the verifier */
87e462f7 75 inner_map_fd = compat_bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof(uint32_t), sizeof(uint32_t), 128U, NULL);
184b4f78 76 if (inner_map_fd < 0)
b1acbc08 77 return log_error_errno(errno, "bpf-lsm: Failed to create BPF map: %m");
184b4f78
ILG
78
79 r = sym_bpf_map__set_inner_map_fd(obj->maps.cgroup_hash, inner_map_fd);
b7cba815 80 assert(r <= 0);
184b4f78 81 if (r < 0)
b1acbc08 82 return log_error_errno(r, "bpf-lsm: Failed to set inner map fd: %m");
184b4f78
ILG
83
84 r = restrict_fs_bpf__load(obj);
b7cba815
ZJS
85 assert(r <= 0);
86 if (r < 0)
b1acbc08 87 return log_error_errno(r, "bpf-lsm: Failed to load BPF object: %m");
184b4f78
ILG
88
89 *ret_obj = TAKE_PTR(obj);
90
91 return 0;
92}
93
94static int mac_bpf_use(void) {
95 _cleanup_free_ char *lsm_list = NULL;
96 static int cached_use = -1;
97 int r;
98
99 if (cached_use >= 0)
100 return cached_use;
101
102 cached_use = 0;
103
104 r = read_one_line_file("/sys/kernel/security/lsm", &lsm_list);
105 if (r < 0) {
b7cba815 106 if (r != -ENOENT)
b1acbc08 107 log_notice_errno(r, "bpf-lsm: Failed to read /sys/kernel/security/lsm, assuming bpf is unavailable: %m");
184b4f78
ILG
108 return 0;
109 }
110
92698b0f 111 for (const char *p = lsm_list;;) {
184b4f78
ILG
112 _cleanup_free_ char *word = NULL;
113
114 r = extract_first_word(&p, &word, ",", 0);
115 if (r == 0)
b7cba815 116 return 0;
184b4f78
ILG
117 if (r == -ENOMEM)
118 return log_oom();
119 if (r < 0) {
b1acbc08 120 log_notice_errno(r, "bpf-lsm: Failed to parse /sys/kernel/security/lsm, assuming bpf is unavailable: %m");
184b4f78
ILG
121 return 0;
122 }
123
b7cba815
ZJS
124 if (streq(word, "bpf"))
125 return cached_use = 1;
184b4f78 126 }
184b4f78
ILG
127}
128
ba187c9c 129bool lsm_bpf_supported(bool initialize) {
184b4f78
ILG
130 _cleanup_(restrict_fs_bpf_freep) struct restrict_fs_bpf *obj = NULL;
131 static int supported = -1;
132 int r;
133
134 if (supported >= 0)
135 return supported;
ba187c9c
ZJS
136 if (!initialize)
137 return false;
184b4f78 138
bb0b01ed 139 if (!cgroup_bpf_supported())
389db516 140 return (supported = false);
184b4f78
ILG
141
142 r = mac_bpf_use();
143 if (r < 0) {
b1acbc08 144 log_warning_errno(r, "bpf-lsm: Can't determine whether the BPF LSM module is used: %m");
389db516 145 return (supported = false);
184b4f78
ILG
146 }
147
148 if (r == 0) {
149 log_info_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
b1acbc08 150 "bpf-lsm: BPF LSM hook not enabled in the kernel, BPF LSM not supported");
389db516 151 return (supported = false);
184b4f78
ILG
152 }
153
154 r = prepare_restrict_fs_bpf(&obj);
155 if (r < 0)
389db516 156 return (supported = false);
184b4f78 157
ccfc534d
JK
158 if (!bpf_can_link_lsm_program(obj->progs.restrict_filesystems)) {
159 log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
b1acbc08 160 "bpf-lsm: Failed to link program; assuming BPF LSM is not available");
389db516 161 return (supported = false);
184b4f78
ILG
162 }
163
389db516 164 return (supported = true);
184b4f78
ILG
165}
166
167int lsm_bpf_setup(Manager *m) {
299d9417 168 _cleanup_(restrict_fs_bpf_freep) struct restrict_fs_bpf *obj = NULL;
184b4f78
ILG
169 _cleanup_(bpf_link_freep) struct bpf_link *link = NULL;
170 int r;
171
172 assert(m);
173
174 r = prepare_restrict_fs_bpf(&obj);
175 if (r < 0)
176 return r;
177
299d9417 178 link = sym_bpf_program__attach_lsm(obj->progs.restrict_filesystems);
184b4f78
ILG
179 r = sym_libbpf_get_error(link);
180 if (r != 0)
b1acbc08 181 return log_error_errno(r, "bpf-lsm: Failed to link '%s' LSM BPF program: %m",
299d9417 182 sym_bpf_program__name(obj->progs.restrict_filesystems));
184b4f78 183
b1acbc08 184 log_info("bpf-lsm: LSM BPF program attached");
184b4f78 185
299d9417
JK
186 obj->links.restrict_filesystems = TAKE_PTR(link);
187 m->restrict_fs = TAKE_PTR(obj);
184b4f78
ILG
188
189 return 0;
190}
191
192int lsm_bpf_unit_restrict_filesystems(Unit *u, const Set *filesystems, bool allow_list) {
184b4f78
ILG
193 uint32_t dummy_value = 1, zero = 0;
194 const char *fs;
195 const statfs_f_type_t *magic;
196 int r;
197
198 assert(filesystems);
199 assert(u);
200
299d9417
JK
201 if (!u->manager->restrict_fs)
202 return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL),
b1acbc08 203 "bpf-lsm: BPF LSM object is not installed, has setup failed?");
299d9417 204
87e462f7 205 int inner_map_fd = compat_bpf_map_create(
184b4f78 206 BPF_MAP_TYPE_HASH,
6b8085db 207 NULL,
184b4f78
ILG
208 sizeof(uint32_t),
209 sizeof(uint32_t),
6b8085db
DDM
210 128U, /* Should be enough for all filesystem types */
211 NULL);
184b4f78 212 if (inner_map_fd < 0)
b1acbc08 213 return log_unit_error_errno(u, errno, "bpf-lsm: Failed to create inner BPF map: %m");
184b4f78 214
92698b0f 215 int outer_map_fd = sym_bpf_map__fd(u->manager->restrict_fs->maps.cgroup_hash);
184b4f78 216 if (outer_map_fd < 0)
b1acbc08 217 return log_unit_error_errno(u, errno, "bpf-lsm: Failed to get BPF map fd: %m");
184b4f78
ILG
218
219 if (sym_bpf_map_update_elem(outer_map_fd, &u->cgroup_id, &inner_map_fd, BPF_ANY) != 0)
b1acbc08 220 return log_unit_error_errno(u, errno, "bpf-lsm: Error populating BPF map: %m");
184b4f78
ILG
221
222 uint32_t allow = allow_list;
223
224 /* Use key 0 to store whether this is an allow list or a deny list */
225 if (sym_bpf_map_update_elem(inner_map_fd, &zero, &allow, BPF_ANY) != 0)
b1acbc08 226 return log_unit_error_errno(u, errno, "bpf-lsm: Error initializing map: %m");
184b4f78
ILG
227
228 SET_FOREACH(fs, filesystems) {
229 r = fs_type_from_string(fs, &magic);
230 if (r < 0) {
b1acbc08 231 log_unit_warning(u, "bpf-lsm: Invalid filesystem name '%s', ignoring.", fs);
184b4f78
ILG
232 continue;
233 }
234
b1acbc08 235 log_unit_debug(u, "bpf-lsm: Restricting filesystem access to '%s'", fs);
184b4f78
ILG
236
237 for (int i = 0; i < FILESYSTEM_MAGIC_MAX; i++) {
238 if (magic[i] == 0)
239 break;
240
241 if (sym_bpf_map_update_elem(inner_map_fd, &magic[i], &dummy_value, BPF_ANY) != 0) {
b1acbc08 242 r = log_unit_error_errno(u, errno, "bpf-lsm: Failed to update BPF map: %m");
184b4f78
ILG
243
244 if (sym_bpf_map_delete_elem(outer_map_fd, &u->cgroup_id) != 0)
b1acbc08 245 log_unit_debug_errno(u, errno, "bpf-lsm: Failed to delete cgroup entry from BPF map: %m");
184b4f78
ILG
246
247 return r;
248 }
249 }
250 }
251
252 return 0;
253}
254
255int lsm_bpf_cleanup(const Unit *u) {
184b4f78
ILG
256 assert(u);
257 assert(u->manager);
258
ba187c9c
ZJS
259 /* If we never successfully detected support, there is nothing to clean up. */
260 if (!lsm_bpf_supported(/* initialize = */ false))
184b4f78
ILG
261 return 0;
262
263 if (!u->manager->restrict_fs)
264 return 0;
265
92698b0f 266 int fd = sym_bpf_map__fd(u->manager->restrict_fs->maps.cgroup_hash);
184b4f78 267 if (fd < 0)
b1acbc08 268 return log_unit_error_errno(u, errno, "bpf-lsm: Failed to get BPF map fd: %m");
184b4f78
ILG
269
270 if (sym_bpf_map_delete_elem(fd, &u->cgroup_id) != 0)
b1acbc08 271 return log_unit_debug_errno(u, errno, "bpf-lsm: Failed to delete cgroup entry from LSM BPF map: %m");
184b4f78
ILG
272
273 return 0;
274}
275
276int lsm_bpf_map_restrict_fs_fd(Unit *unit) {
277 assert(unit);
278 assert(unit->manager);
279
280 if (!unit->manager->restrict_fs)
281 return -ENOMEDIUM;
282
283 return sym_bpf_map__fd(unit->manager->restrict_fs->maps.cgroup_hash);
284}
285
286void lsm_bpf_destroy(struct restrict_fs_bpf *prog) {
287 restrict_fs_bpf__destroy(prog);
288}
289#else /* ! BPF_FRAMEWORK */
ba187c9c 290bool lsm_bpf_supported(bool initialize) {
389db516 291 return false;
184b4f78
ILG
292}
293
294int lsm_bpf_setup(Manager *m) {
b1acbc08 295 return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "bpf-lsm: Failed to set up LSM BPF: %m");
184b4f78
ILG
296}
297
298int lsm_bpf_unit_restrict_filesystems(Unit *u, const Set *filesystems, const bool allow_list) {
b1acbc08 299 return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP), "bpf-lsm: Failed to restrict filesystems using LSM BPF: %m");
184b4f78
ILG
300}
301
302int lsm_bpf_cleanup(const Unit *u) {
303 return 0;
304}
305
306int lsm_bpf_map_restrict_fs_fd(Unit *unit) {
307 return -ENOMEDIUM;
308}
309
310void lsm_bpf_destroy(struct restrict_fs_bpf *prog) {
311 return;
312}
313#endif
e59ccd03
ILG
314
315int lsm_bpf_parse_filesystem(
316 const char *name,
317 Set **filesystems,
318 FilesystemParseFlags flags,
319 const char *unit,
320 const char *filename,
321 unsigned line) {
322 int r;
323
324 assert(name);
325 assert(filesystems);
326
327 if (name[0] == '@') {
328 const FilesystemSet *set;
e59ccd03
ILG
329
330 set = filesystem_set_find(name);
331 if (!set) {
332 log_syntax(unit, flags & FILESYSTEM_PARSE_LOG ? LOG_WARNING : LOG_DEBUG, filename, line, 0,
b1acbc08 333 "bpf-lsm: Unknown filesystem group, ignoring: %s", name);
e59ccd03
ILG
334 return 0;
335 }
336
337 NULSTR_FOREACH(i, set->value) {
92698b0f
ZJS
338 /* Call ourselves again, for the group to parse. Note that we downgrade logging here
339 * (i.e. take away the FILESYSTEM_PARSE_LOG flag) since any issues in the group table
340 * are our own problem, not a problem in user configuration data and we shouldn't
341 * pretend otherwise by complaining about them. */
e59ccd03
ILG
342 r = lsm_bpf_parse_filesystem(i, filesystems, flags &~ FILESYSTEM_PARSE_LOG, unit, filename, line);
343 if (r < 0)
344 return r;
345 }
346 } else {
347 /* If we previously wanted to forbid access to a filesystem and now
348 * we want to allow it, then remove it from the list. */
349 if (!(flags & FILESYSTEM_PARSE_INVERT) == !!(flags & FILESYSTEM_PARSE_ALLOW_LIST)) {
350 r = set_put_strdup(filesystems, name);
92698b0f
ZJS
351 if (r == -ENOMEM)
352 return flags & FILESYSTEM_PARSE_LOG ? log_oom() : -ENOMEM;
353 if (r < 0 && r != -EEXIST) /* When already in set, ignore */
354 return r;
e59ccd03
ILG
355 } else
356 free(set_remove(*filesystems, name));
357 }
358
359 return 0;
360}