SeaBee
Loading...
Searching...
No Matches
seabee_utils.h
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-only
2#ifndef SEABEE_UTILS_H_
3#define SEABEE_UTILS_H_
8#include <bpf/vmlinux.h>
9#include <bpf/bpf_helpers.h>
10
11#include "seabee_maps.h"
12#include "shared_rust_types.h"
13#include "logging.h"
14#include "logging_types.h"
15
17#define ZERO 0
19#define ALLOW 0
21#define DENY -1
22
23extern struct inode_storage inode_storage;
24extern struct task_storage task_storage;
25extern struct policy_map policy_map;
26extern struct map_to_pol_id map_to_pol_id;
27
29static __always_inline u32 get_inode_pol_id(struct inode *inode)
30{
31 u32 *policy_id = bpf_inode_storage_get(&inode_storage, inode, 0, 0);
32 if (policy_id) {
33 return *policy_id;
34 } else {
35 return NO_POL_ID;
36 }
37}
38
40static __always_inline u32 get_map_pol_id(struct bpf_map *map)
41{
42 struct bpf_map_data *data = bpf_map_lookup_elem(&map_to_pol_id, &map);
43 if (data) {
44 return data->policy_id;
45 } else {
46 return NO_POL_ID;
47 }
48}
49
53static __always_inline u32 get_pid()
54{
55 // we use tgid instead of pid because tgid == pid in userspace.
56 // we want to protect all threads in our thread group (process)
57 return bpf_get_current_pid_tgid() >> 32;
58}
59
63struct task_struct *get_task()
64{
65 return bpf_get_current_task_btf();
66}
67
76static __always_inline struct c_policy_config *get_policy_config(u32 policy_id)
77{
78 if (policy_id == NO_POL_ID) {
79 return NULL;
80 }
81 return bpf_map_lookup_elem(&policy_map, &policy_id);
82}
83
91static __always_inline bool is_valid_policy_id(u32 policy_id)
92{
93 return get_policy_config(policy_id) != NULL;
94}
95
104static __always_inline struct seabee_task_data *
105get_target_task_data(struct task_struct *t)
106{
107 return bpf_task_storage_get(&task_storage, t, 0, 0);
108}
109
116static __always_inline struct seabee_task_data *get_task_data()
117{
118 struct task_struct *task = get_task();
119 return get_target_task_data(task);
120}
121
128static __always_inline u32 get_target_task_pol_id(struct task_struct *t)
129{
130 struct seabee_task_data *data = get_target_task_data(t);
131 if (data) {
132 return data->pol_id;
133 } else {
134 return NO_POL_ID;
135 }
136}
137
143static __always_inline u32 get_task_pol_id()
144{
145 struct seabee_task_data *data = get_task_data();
146 if (data) {
147 return data->pol_id;
148 } else {
149 return NO_POL_ID;
150 }
151}
152
160static __always_inline void set_task_pinning(u32 flag)
161{
162 // Check task is tracked by SeaBee
163 struct task_struct *task = get_task();
164 struct seabee_task_data *data =
165 bpf_task_storage_get(&task_storage, task, 0, 0);
166 if (data && data->pol_id != NO_POL_ID) {
167 // Check pin protections are enabled
168 struct c_policy_config *cfg = get_policy_config(data->pol_id);
169 if (cfg && cfg->protect_pins) {
170 // set flag if it has changed
171 if (data->is_pinning != flag) {
172 data->is_pinning = flag;
173 u64 log[3] = { (u64)task->comm, (u64)task->tgid, (u64)flag };
174 log_generic_msg(LOG_LEVEL_TRACE, LOG_REASON_DEBUG,
175 "set task %s(%d) pinning to %lu", log,
176 sizeof(log));
177 }
178 }
179 }
180}
181
182enum object_type {
183 OBJECT_TYPE_INODE,
184 OBJECT_TYPE_TASK,
185 OBJECT_TYPE_MAP,
186};
187
196static __always_inline u32 get_object_valid_policy_id(void *object,
197 enum object_type type)
198{
199 // get current label
200 u32 current_pol_id = NO_POL_ID;
201 switch (type) {
202 case OBJECT_TYPE_TASK:
203 current_pol_id = get_target_task_pol_id((struct task_struct *)object);
204 break;
205 case OBJECT_TYPE_INODE:
206 current_pol_id = get_inode_pol_id((struct inode *)object);
207 break;
208 case OBJECT_TYPE_MAP:
209 current_pol_id = get_map_pol_id((struct bpf_map *)object);
210 break;
211 }
212 // check if label has a policy
213 if (is_valid_policy_id(current_pol_id)) {
214 return current_pol_id;
215 }
216 //TODO: should we find a way to reset of the policy ID to zero if there is no policy?
217 return NO_POL_ID;
218}
219
226static __always_inline void label_task(struct task_struct *task,
227 const unsigned char *task_name,
228 u32 policy_id)
229{
230 u32 current_pol_id = get_object_valid_policy_id(task, OBJECT_TYPE_TASK);
231 if (current_pol_id == policy_id) {
232 // Already correctly labeled
233 return;
234 } else if (!is_valid_policy_id(policy_id)) {
235 // Don't propagate invalid policy ids
236 u64 log[3] = {
237 (u64)task_name,
238 (u64)task->tgid,
239 (u64)policy_id,
240 };
241 log_generic_msg(LOG_LEVEL_TRACE, LOG_REASON_DEBUG,
242 "Did not label task %s(%d) since %d is invalid", log,
243 sizeof(log));
244 } else if (current_pol_id != NO_POL_ID) {
245 // Already labeled with different label
246 u64 log[4] = { (u64)task_name, (u64)task->tgid, (u64)policy_id,
247 (u64)current_pol_id };
248 log_generic_msg(
249 LOG_LEVEL_ERROR, LOG_REASON_ERROR,
250 "failed to label task %s(%d) as %d. Task already belongs to policy %d",
251 log, sizeof(log));
252 } else {
253 // Label with new policy id
254 struct seabee_task_data new_data = { policy_id, 0 };
255 struct seabee_task_data *new_data_blob = bpf_task_storage_get(
256 &task_storage, task, &new_data, BPF_LOCAL_STORAGE_GET_F_CREATE);
257 u64 log[3] = { (u64)task_name, (u64)task->tgid, (u64)policy_id };
258 if (new_data_blob) {
259 log_generic_msg(LOG_LEVEL_DEBUG, LOG_REASON_DEBUG,
260 "label task %s(%d) as %d", log, sizeof(log));
261 } else {
262 log_generic_msg(LOG_LEVEL_ERROR, LOG_REASON_ERROR,
263 "failed to label task %s(%d) as %d", log,
264 sizeof(log));
265 }
266 }
267}
268
276static __always_inline void label_inode(struct dentry *dentry,
277 struct inode *inode, u32 policy_id)
278{
279 const unsigned char *name = dentry->d_name.name;
280 u32 current_pol_id = get_object_valid_policy_id(inode, OBJECT_TYPE_INODE);
281 if (current_pol_id == policy_id) {
282 // Already correctly labeled
283 return;
284 } else if (!is_valid_policy_id(policy_id)) {
285 // Don't propagate invalid policy ids
286 u64 log[3] = { (u64)name, (u64)policy_id };
287 log_generic_msg(LOG_LEVEL_TRACE, LOG_REASON_DEBUG,
288 "Did not label inode %s since %d is invalid", log,
289 sizeof(log));
290 } else if (current_pol_id != NO_POL_ID) {
291 // Has a different policy id already
292 u64 log[3] = { (u64)name, (u64)policy_id, (u64)current_pol_id };
293 log_generic_msg(
294 LOG_LEVEL_ERROR, LOG_REASON_ERROR,
295 "failed to label inode for %s as %d. Inode already belongs to policy %d",
296 log, sizeof(log));
297 } else {
298 // Assign new inode policy id
299 u32 *label = bpf_inode_storage_get(&inode_storage, inode, &policy_id,
300 BPF_LOCAL_STORAGE_GET_F_CREATE);
301 u64 log[2] = { (u64)name, (u64)policy_id };
302 if (label) {
303 log_generic_msg(LOG_LEVEL_TRACE, LOG_REASON_DEBUG,
304 "label inode for '%s' as %d", log, sizeof(log));
305 } else {
306 log_generic_msg(LOG_LEVEL_ERROR, LOG_REASON_ERROR,
307 "failed to label inode for %s as %d", log,
308 sizeof(log));
309 }
310 }
311}
312
321static __always_inline int label_map_with_id(struct bpf_map *map, u32 policy_id)
322{
323 // unsure if map can be null, but this made the verifier happy
324 if (!map) {
325 return ALLOW;
326 }
327
328 struct bpf_map_data map_data;
329 map_data.policy_id = policy_id;
330 BPF_CORE_READ_STR_INTO(&map_data.name, map, name);
331
332 // NOEXIST ensures that we cannot overwrite an existing map label
333 long err =
334 bpf_map_update_elem(&map_to_pol_id, &map, &map_data, BPF_NOEXIST);
335
336 if (err < 0) {
337 u64 data[4] = { (u64)map_data.name, (u64)BPF_CORE_READ(map, id),
338 (u64)policy_id, (u64)err };
339 log_generic_msg(
340 LOG_LEVEL_ERROR, LOG_REASON_ERROR,
341 "Error: update elem failed map %s(%d) for policy %d, code: %d",
342 data, sizeof(data));
343 } else {
344 u64 data[3] = { (u64)map_data.name, (u64)BPF_CORE_READ(map, id),
345 (u64)policy_id };
346 log_generic_msg(LOG_LEVEL_TRACE, LOG_REASON_DEBUG,
347 "label map %s(%d) as %d", data, sizeof(data));
348 }
349 return ALLOW;
350}
351
361static __always_inline int label_map(struct bpf_map *map)
362{
363 return label_map_with_id(map, get_task_pol_id());
364}
365
366static __always_inline int unlabel_map(struct bpf_map *map)
367{
368 // unsure if map can be null, but this made the verifier happy
369 if (!map) {
370 return ALLOW;
371 }
372
373 // exit early if map is not tracked
374 u32 *policy_id = bpf_map_lookup_elem(&map_to_pol_id, &map);
375 if (!policy_id) {
376 return ALLOW;
377 }
378
379 // delete map
380 s32 err = bpf_map_delete_elem(&map_to_pol_id, &map);
381 char map_name[BPF_MAP_NAME_LEN];
382 BPF_CORE_READ_STR_INTO(&map_name, map, name);
383 u64 data[] = { (u64)map_name, (u64)BPF_CORE_READ(map, id) };
384 if (err < 0) {
385 log_generic_msg(LOG_LEVEL_ERROR, LOG_REASON_ERROR,
386 "Error: failed to unlabel map: %s(%d)", data,
387 sizeof(data));
388 } else {
389 log_generic_msg(LOG_LEVEL_TRACE, LOG_REASON_DEBUG, "unlabel map %s(%d)",
390 data, sizeof(data));
391 }
392 return ALLOW;
393}
394
395#endif // SEABEE_UTILS_H_
#define BPF_MAP_NAME_LEN
the length of the name of an eBPF map
Definition constants.h:26
#define ALLOW
LSM return code for allowing an operation to continue.
Definition seabee_utils.h:19
struct task_struct * get_task()
gets the task for the current context
Definition seabee_utils.h:63
Definition seabee_maps.h:60
c_policy_config contains security levels for protected objects and corresponds to a policy id.
Definition shared_rust_types.h:31
Definition seabee_maps.h:27
Definition seabee_maps.h:65
Definition seabee_maps.h:46
Definition seabee_maps.h:12
Definition seabee_maps.h:17