|
SeaBee
|
#include <bpf/vmlinux.h>#include <bpf/vmlinux_features.h>#include <bpf/bpf_helpers.h>#include <bpf/bpf_tracing.h>#include <bpf/bpf_core_read.h>#include "logging.h"#include "logging_types.h"#include "seabee_log.h"#include "seabee_maps.h"#include "seabee_utils.h"#include "shared_rust_types.h"
Data Structures | |
| struct | path_to_pol_id |
| struct | path_storage |
| storage path buffers which cannot fit on ebpf stack per cpu is important to prevent concurrency issues More... | |
Macros | |
| #define | FMODE_WRITE (1 << 1) |
Functions | |
| int | seabee_bpf_map (struct bpf_map *map, fmode_t fmode, int ret) |
| Blocks manipulation a protected map. | |
| int | seabee_locked_down (enum lockdown_reason what,) |
| Blocks the use of the bpf_write_user() helper. | |
| int | seabee_task_kill (struct task_struct *p, struct kernel_siginfo *info, int sig, const struct cred *cred, int ret) |
| Block unwanted signals to the seabee userspace process. | |
| int | seabee_file_open (struct file *file,) |
| make protected files read-only. | |
| int | seabee_inode_permission (struct inode *inode, int mask) |
| prevent writes to protected inodes | |
| int | seabee_inode_unlink (struct inode *dir, struct dentry *dentry) |
| Prevents unlinking/removing protected files or pins. | |
| int | seabee_inode_rmdir (struct inode *dir, struct dentry *dentry) |
| Prevents unlinking/removing protected folders. | |
| int | seabee_inode_setattr (struct dentry *dentry, struct iattr *attr) |
| prevents modification of attributes on protected inodes | |
| int | seabee_inode_setxattr (struct user_namespace *mnt_userns, struct dentry *dentry, const char *name, const void *value, size_t size, int flags) |
| prevent modification of extended attributes on protected inodes | |
| int | seabee_inode_rename (struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) |
| prevent rename of a protected inode | |
| int | seabee_sb_umount (struct vfsmount *mnt, int flags, int ret) |
| Prevent unmounting the BPF filesystem. | |
| int | seabee_kernel_module_request (char *kmod_name,) |
| Prevent a kernel module from being automatically loaded by the kernel. | |
| int | seabee_kernel_read_file (struct file *file, enum kernel_read_file_id id, bool contents) |
| Blocks the loading of a kernel module via a file handle. | |
| int | seabee_kernel_load_data (enum kernel_load_data_id id, bool contents) |
| Blocks the loading of a kernel module via a data blob. | |
| int | seabee_ptrace_access_check (struct task_struct *child, unsigned int mode, int ret) |
| Blocks attempts to ptrace a protected process. | |
| int | seabee_task_alloc (struct task_struct *task, unsigned long clone_flags, int ret) |
| Blocks attempts to fork() the seabee process. | |
| int | seabee_label_sock (struct socket *sock, struct sockaddr *address, int addrlen) |
| used to label a socket on creation. | |
| int | seabeectl_auth (struct sock *sock, struct sock *other, struct sock *newsk) |
| check if a process is allowed to connect to a socket. | |
| int | seabee_label_process (struct linux_binprm *bprm, int ret) |
| Label a process when it starts. | |
| int | seabee_label_child_process (struct task_struct *child_task, unsigned long clone_flags) |
| Label a child process with same policy id as its parent. | |
| int | seabee_label_map (struct bpf_map *map, int ret) |
| Label a bpf map on creation using the same label as the process that created it. | |
| int | seabee_unlabel_map (struct bpf_map *map, int ret) |
| Unlabel an eBPF map when it is freed. | |
| int | seabee_start_pin (int cmd, union bpf_attr *attr, unsigned int size, int ret) |
| Used to identify a bpf program is being pinned. | |
| int | seabee_label_pin (struct dentry *dentry, struct inode *inode) |
| Label an inode associated with a bpf pin. | |
| int | seabee_stop_pin (struct dentry *dentry, struct inode *inode) |
| Used to identify that a process has finished pinning. | |
| int | seabee_label_inode_runtime (struct dentry *dentry, struct inode *inode) |
| Label inodes created at runtime giving them the same label as the parent. | |
Variables | |
| char | LICENSE [] = "GPL" |
| License of the BPF program. | |
| u32 | log_level |
| The level of the logs to filter out. | |
| u32 | kmod_modification |
| The level of access for kernel modules. | |
| u32 | my_pid |
| The process id of the userspace that loads these programs. | |
| u64 | bpf_dev_id |
| The device id of the /sys/bpf mount point inode. | |
| u64 | sys_dev_id |
| The device id of the /sys mount point inode. | |
| u8 | my_binary_path [PATH_MAX] |
| The path of the seabee binary. u8 plays nicer with rust. | |
| u8 | null_path [PATH_MAX] |
| used to null a buffer | |
| struct log_ringbuf | log_ringbuf |
| eBPF Maps | |
| struct inode_storage | inode_storage |
| maps and inode to a policy id | |
| struct policy_map | policy_map |
| Hashmap from policy id to policy config. | |
| struct task_storage | task_storage |
| maps process pid to policy id | |
| struct sk_storage | sk_storage |
| struct sock to policy id | |
| struct map_to_pol_id | map_to_pol_id |
| maps a map id to a policy id | |
| struct path_to_pol_id | path_to_pol_id |
| Maps a filename to a policy id. | |
| struct path_storage | path_storage |
| storage for paths | |
| int seabee_bpf_map | ( | struct bpf_map * | map, |
| fmode_t | fmode, | ||
| int | ret | ||
| ) |
Blocks manipulation a protected map.
This is achieved by preventing BPF file descriptors from being created for protected maps.
A file descriptor for a map can be obtained via the commands:
| map | internal BPF map structure |
| fmode | file mode to open with (read / write / etc) |
| ret | return code from previous LSM hook |
| int seabee_file_open | ( | struct file * | file | ) |
make protected files read-only.
This hook is called to check if a file is allowed to be accessed. Deny access to protected files by checking file->f_mode. This tells if the file is being opened for reading or writing.
| int seabee_inode_permission | ( | struct inode * | inode, |
| int | mask | ||
| ) |
prevent writes to protected inodes
| inode | inode |
| mask | access mask |
| int seabee_inode_rename | ( | struct inode * | old_dir, |
| struct dentry * | old_dentry, | ||
| struct inode * | new_dir, | ||
| struct dentry * | new_dentry, | ||
| unsigned int | flags | ||
| ) |
prevent rename of a protected inode
| new_dentry | the new file which will be replaced by old file |
| int seabee_inode_rmdir | ( | struct inode * | dir, |
| struct dentry * | dentry | ||
| ) |
| int seabee_inode_setattr | ( | struct dentry * | dentry, |
| struct iattr * | attr | ||
| ) |
prevents modification of attributes on protected inodes
| dentry | file |
| int seabee_inode_setxattr | ( | struct user_namespace * | mnt_userns, |
| struct dentry * | dentry, | ||
| const char * | name, | ||
| const void * | value, | ||
| size_t | size, | ||
| int | flags | ||
| ) |
prevent modification of extended attributes on protected inodes
| dentry | file |
| int seabee_inode_unlink | ( | struct inode * | dir, |
| struct dentry * | dentry | ||
| ) |
| int seabee_kernel_load_data | ( | enum kernel_load_data_id | id, |
| bool | contents | ||
| ) |
Blocks the loading of a kernel module via a data blob.
lsm/kernel_load_data is invoked when userspace tries to load a data blob from its memory into the kernel, including but not limited to kernel modules loaded via init_module().
enum kernel_load_data_id is the same as __kernel_read_file_id defined in https://elixir.bootlin.com/linux/latest/source/include/linux/kernel_read_file.h#L9 It has several types including unknown, firmware, module, kexec-image, kexec-initramfs, security-policy, and x509-certificate. kernel-module seems most appropriate for our purposes, but the others should be taken into account later on.
| id | defines what kind of data is being read. |
| contents | true if security_kernel_post_load_data() will be called |
| int seabee_kernel_module_request | ( | char * | kmod_name | ) |
Prevent a kernel module from being automatically loaded by the kernel.
lsm/kernel_module_request is invoked when module auto-loading is triggered by some attempt to access kernel functionality implemented by a module. It is used internally by the kernel to check if loading a module is allowed.
| kmod_name | the name of ther kernel module to be loaded |
| int seabee_kernel_read_file | ( | struct file * | file, |
| enum kernel_read_file_id | id, | ||
| bool | contents | ||
| ) |
Blocks the loading of a kernel module via a file handle.
lsm/kernel_read_file is invoked when the kernel is about to directly read from a file or the file system specified by userspace for some purpose including but not limited to kernel modules loaded via finit_module()
enum kernel_load_data_id is the same as __kernel_read_file_id defined in https://elixir.bootlin.com/linux/latest/source/include/linux/kernel_read_file.h#L9 It has several types including unknown, firmware, module, kexec-image, kexec-initramfs, security-policy, and x509-certificate. kernel-module seems most appropriate for our purposes, but the others should be taken into account later on.
| file | the file from which to read |
| id | identifies the type of data that is being read |
| contents | true if security_post_read_file() will be called |
| int seabee_label_inode_runtime | ( | struct dentry * | dentry, |
| struct inode * | inode | ||
| ) |
Label inodes created at runtime giving them the same label as the parent.
security_d_instantiate is called whenever a dentry is first associated with an inode. That could be on creation or when it is first looked up.
| dentry | dentry |
| inode | inode |
| int seabee_label_pin | ( | struct dentry * | dentry, |
| struct inode * | inode | ||
| ) |
Label an inode associated with a bpf pin.
This hook is called when a dentry becomes associated with an inode.
| int seabee_label_process | ( | struct linux_binprm * | bprm, |
| int | ret | ||
| ) |
Label a process when it starts.
This uses the path_to_pol_id map and the linux_binprm structure to attach a label to a task based on the path of the executable that started the task. This hook can be called multiple times during an execve, for example, if executing a script.
| bprm | holds information about a binary that is going to be executed |
ALLOW since this check is just for labeling and not for enforcement | int seabee_label_sock | ( | struct socket * | sock, |
| struct sockaddr * | address, | ||
| int | addrlen | ||
| ) |
used to label a socket on creation.
This will only label sockets created by the SeaBee userspace. Specifically this is used to control access to SeaBee's listening socket to ensure only seabeectl can connect to it.
| sock | the socket being bound |
| address | requested bind address |
| addrlen | length of address |
ALLOW to allow access | int seabee_locked_down | ( | enum lockdown_reason | what | ) |
Blocks the use of the bpf_write_user() helper.
This helper function is dangerous and it is better to disable it.
| what | why the lockdown hook is firing. |
man 7 kernel_lockdown | int seabee_ptrace_access_check | ( | struct task_struct * | child, |
| unsigned int | mode, | ||
| int | ret | ||
| ) |
Blocks attempts to ptrace a protected process.
This hook is called by a "tracer" process that is trying to use ptrace on a "tracee" process. In this case, the "child" argument.
note: there is also an lsm/ptrace_traceme hook. This hook is not checked because it is only invoked by the child process.
| child | the process that is going to be traced (tracee) |
| mode | PTRACE_MODE flags, see linux/ptrace.h |
| ret | the return code of the previous LSM hook |
| int seabee_sb_umount | ( | struct vfsmount * | mnt, |
| int | flags, | ||
| int | ret | ||
| ) |
| int seabee_task_alloc | ( | struct task_struct * | task, |
| unsigned long | clone_flags, | ||
| int | ret | ||
| ) |
Blocks attempts to fork() the seabee process.
A fork() / clone() of the seabee process will inherit all of the memory and file-descriptors of the parent process. This would allow the child process to unload the BPF program or alter the map contents. This is more of a safety-net than anything.
| task | the process that is going to be forked |
| clone_flags | flags from struct kernel_clone_args |
| ret | the return code of the previous LSM hook |
| int seabee_task_kill | ( | struct task_struct * | p, |
| struct kernel_siginfo * | info, | ||
| int | sig, | ||
| const struct cred * | cred, | ||
| int | ret | ||
| ) |
Block unwanted signals to the seabee userspace process.
Deny any outside userspace signal that will stop our corresponding userspace process almost every signal will kill our process, we choose to enumerate (and allow) those which do not stop our process. Signals that originate from the kernel may not be caught because they may use a different code path that does not include this lsm hook. These signals include the ZERO signal and any signal specified in the signal_allow_mask
| p | target process |
| info | signal info, can also be NULL or 1 |
| sig | signal value |
| cred | credentials of sender, may be NULL |
| ret | the return code of the previous LSM hook |
man signal | int seabeectl_auth | ( | struct sock * | sock, |
| struct sock * | other, | ||
| struct sock * | newsk | ||
| ) |
check if a process is allowed to connect to a socket.
This is used to enforce that only seabeectl is allowed to connect to the listening socket of SeaBee in order to receive commands.
| sock | originating sock |
| other | peer sock |
| newsk | new sock |
ALLOW to allow access | struct inode_storage inode_storage |
maps and inode to a policy id
local storage for inodes
| u32 log_level |
The level of the logs to filter out.
Defined in each .bpf.c file. Specifies which logs to output to the ringbuf.
| struct log_ringbuf log_ringbuf |
eBPF Maps
Defined in each .bpf.c file. Specifies the ringbuf map to output logs to.
logs data back to userspace
| struct task_storage task_storage |
maps process pid to policy id
local storage for tasks