]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/bpf-devices.c
pkgconfig: define variables relative to ${prefix}/${rootprefix}/${sysconfdir}
[thirdparty/systemd.git] / src / core / bpf-devices.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 #include <linux/libbpf.h>
3
4 #include "bpf-devices.h"
5 #include "bpf-program.h"
6
7 #define PASS_JUMP_OFF 4096
8
9 static int bpf_access_type(const char *acc) {
10 int r = 0;
11
12 assert(acc);
13
14 for (; *acc; acc++)
15 switch(*acc) {
16 case 'r':
17 r |= BPF_DEVCG_ACC_READ;
18 break;
19 case 'w':
20 r |= BPF_DEVCG_ACC_WRITE;
21 break;
22 case 'm':
23 r |= BPF_DEVCG_ACC_MKNOD;
24 break;
25 default:
26 return -EINVAL;
27 }
28
29 return r;
30 }
31
32 int cgroup_bpf_whitelist_device(BPFProgram *prog, int type, int major, int minor, const char *acc) {
33 struct bpf_insn insn[] = {
34 BPF_JMP_IMM(BPF_JNE, BPF_REG_2, type, 6), /* compare device type */
35 BPF_MOV32_REG(BPF_REG_1, BPF_REG_3), /* calculate access type */
36 BPF_ALU32_IMM(BPF_AND, BPF_REG_1, 0),
37 BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 3), /* compare access type */
38 BPF_JMP_IMM(BPF_JNE, BPF_REG_4, major, 2), /* compare major */
39 BPF_JMP_IMM(BPF_JNE, BPF_REG_5, minor, 1), /* compare minor */
40 BPF_JMP_A(PASS_JUMP_OFF), /* jump to PASS */
41 };
42 int r, access;
43
44 assert(prog);
45 assert(acc);
46
47 access = bpf_access_type(acc);
48 if (access <= 0)
49 return -EINVAL;
50
51 insn[2].imm = access;
52
53 r = bpf_program_add_instructions(prog, insn, ELEMENTSOF(insn));
54 if (r < 0)
55 log_error_errno(r, "Extending device control BPF program failed: %m");
56
57 return r;
58 }
59
60 int cgroup_bpf_whitelist_major(BPFProgram *prog, int type, int major, const char *acc) {
61 struct bpf_insn insn[] = {
62 BPF_JMP_IMM(BPF_JNE, BPF_REG_2, type, 5), /* compare device type */
63 BPF_MOV32_REG(BPF_REG_1, BPF_REG_3), /* calculate access type */
64 BPF_ALU32_IMM(BPF_AND, BPF_REG_1, 0),
65 BPF_JMP_REG(BPF_JNE, BPF_REG_1, BPF_REG_3, 2), /* compare access type */
66 BPF_JMP_IMM(BPF_JNE, BPF_REG_4, major, 1), /* compare major */
67 BPF_JMP_A(PASS_JUMP_OFF), /* jump to PASS */
68 };
69 int r, access;
70
71 assert(prog);
72 assert(acc);
73
74 access = bpf_access_type(acc);
75 if (access <= 0)
76 return -EINVAL;
77
78 insn[2].imm = access;
79
80 r = bpf_program_add_instructions(prog, insn, ELEMENTSOF(insn));
81 if (r < 0)
82 log_error_errno(r, "Extending device control BPF program failed: %m");
83
84 return r;
85 }
86
87 int cgroup_init_device_bpf(BPFProgram **ret, CGroupDevicePolicy policy, bool whitelist) {
88 struct bpf_insn pre_insn[] = {
89 /* load device type to r2 */
90 BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1,
91 offsetof(struct bpf_cgroup_dev_ctx, access_type)),
92
93 /* load access type to r3 */
94 BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
95 offsetof(struct bpf_cgroup_dev_ctx, access_type)),
96 BPF_ALU32_IMM(BPF_RSH, BPF_REG_3, 16),
97
98 /* load major number to r4 */
99 BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_1,
100 offsetof(struct bpf_cgroup_dev_ctx, major)),
101
102 /* load minor number to r5 */
103 BPF_LDX_MEM(BPF_W, BPF_REG_5, BPF_REG_1,
104 offsetof(struct bpf_cgroup_dev_ctx, minor)),
105 };
106
107 _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
108 int r;
109
110 assert(ret);
111
112 if (policy == CGROUP_AUTO && !whitelist)
113 return 0;
114
115 r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, &prog);
116 if (r < 0)
117 return log_error_errno(r, "Loading device control BPF program failed: %m");
118
119 if (policy == CGROUP_CLOSED || whitelist) {
120 r = bpf_program_add_instructions(prog, pre_insn, ELEMENTSOF(pre_insn));
121 if (r < 0)
122 return log_error_errno(r, "Extending device control BPF program failed: %m");
123 }
124
125 *ret = TAKE_PTR(prog);
126
127 return 0;
128 }
129
130 int cgroup_apply_device_bpf(Unit *u, BPFProgram *prog, CGroupDevicePolicy policy, bool whitelist) {
131 struct bpf_insn post_insn[] = {
132 /* return DENY */
133 BPF_MOV64_IMM(BPF_REG_0, 0),
134 BPF_JMP_A(1),
135
136 };
137
138 struct bpf_insn exit_insn[] = {
139 /* else return ALLOW */
140 BPF_MOV64_IMM(BPF_REG_0, 1),
141 BPF_EXIT_INSN()
142 };
143
144 _cleanup_free_ char *path = NULL;
145 int r;
146
147 if (!prog) {
148 /* Remove existing program. */
149 u->bpf_device_control_installed = bpf_program_unref(u->bpf_device_control_installed);
150 return 0;
151 }
152
153 if (policy != CGROUP_STRICT || whitelist) {
154 size_t off;
155
156 r = bpf_program_add_instructions(prog, post_insn, ELEMENTSOF(post_insn));
157 if (r < 0)
158 return log_error_errno(r, "Extending device control BPF program failed: %m");
159
160 /* Fixup PASS_JUMP_OFF jump offsets. */
161 for (off = 0; off < prog->n_instructions; off++) {
162 struct bpf_insn *ins = &prog->instructions[off];
163
164 if (ins->code == (BPF_JMP | BPF_JA) && ins->off == PASS_JUMP_OFF)
165 ins->off = prog->n_instructions - off - 1;
166 }
167 } else
168 /* Explicitly forbid everything. */
169 exit_insn[0].imm = 0;
170
171 r = bpf_program_add_instructions(prog, exit_insn, ELEMENTSOF(exit_insn));
172 if (r < 0)
173 return log_error_errno(r, "Extending device control BPF program failed: %m");
174
175 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &path);
176 if (r < 0)
177 return log_error_errno(r, "Failed to determine cgroup path: %m");
178
179
180 r = bpf_program_cgroup_attach(prog, BPF_CGROUP_DEVICE, path, BPF_F_ALLOW_MULTI);
181 if (r < 0)
182 return log_error_errno(r, "Attaching device control BPF program to cgroup %s failed: %m", path);
183
184 /* Unref the old BPF program (which will implicitly detach it) right before attaching the new program. */
185 u->bpf_device_control_installed = bpf_program_unref(u->bpf_device_control_installed);
186
187 /* Remember that this BPF program is installed now. */
188 u->bpf_device_control_installed = bpf_program_ref(prog);
189
190 return 0;
191 }
192
193 int bpf_devices_supported(void) {
194 struct bpf_insn trivial[] = {
195 BPF_MOV64_IMM(BPF_REG_0, 1),
196 BPF_EXIT_INSN()
197 };
198
199 _cleanup_(bpf_program_unrefp) BPFProgram *program = NULL;
200 static int supported = -1;
201 int r;
202
203 /* Checks whether BPF device controller is supported. For this, we check five things:
204 *
205 * a) whether we are privileged
206 * b) whether the unified hierarchy is being used
207 * c) the BPF implementation in the kernel supports BPF_PROG_TYPE_CGROUP_DEVICE programs, which we require
208 */
209
210 if (supported >= 0)
211 return supported;
212
213 if (geteuid() != 0) {
214 log_debug("Not enough privileges, BPF device control is not supported.");
215 return supported = 0;
216 }
217
218 r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
219 if (r < 0)
220 return log_error_errno(r, "Can't determine whether the unified hierarchy is used: %m");
221 if (r == 0) {
222 log_debug("Not running with unified cgroups, BPF device control is not supported.");
223 return supported = 0;
224 }
225
226 r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, &program);
227 if (r < 0) {
228 log_debug_errno(r, "Can't allocate CGROUP DEVICE BPF program, BPF device control is not supported: %m");
229 return supported = 0;
230 }
231
232 r = bpf_program_add_instructions(program, trivial, ELEMENTSOF(trivial));
233 if (r < 0) {
234 log_debug_errno(r, "Can't add trivial instructions to CGROUP DEVICE BPF program, BPF device control is not supported: %m");
235 return supported = 0;
236 }
237
238 r = bpf_program_load_kernel(program, NULL, 0);
239 if (r < 0) {
240 log_debug_errno(r, "Can't load kernel CGROUP DEVICE BPF program, BPF device control is not supported: %m");
241 return supported = 0;
242 }
243
244 return supported = 1;
245 }