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