ebpf-demo/bpf/lsm.c
2023-08-19 11:58:49 +08:00

131 lines
3.1 KiB
C

// generated by command: bpftool btf dump file /sys/kernel/btf/vmlinux format c
// > vmlinux.h
#include "vmlinux.h"
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <errno.h>
#include <sys/cdefs.h>
char __license[] SEC("license") = "Dual MIT/GPL";
char log_fmt_timeout[] = "timeout: %lld %lld";
#define SECOND (1000 * 1000 * 1000)
typedef enum status {
FILE_PROTECT_ENABLED,
FILE_PROTECT_TICK,
FILE_PROTECT_MAX,
} file_protect_state;
typedef struct check_ctx {
struct dentry *dentry;
__u64 need_to_be_checked;
__u64 return_value;
} check_ctx;
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, FILE_PROTECT_MAX);
__type(key, file_protect_state);
__type(value, __u64);
} states SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 256);
__type(key, unsigned long);
__type(value, __u8);
} roots SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1024 * 1024);
__type(key, unsigned long);
__type(value, __u64);
} banned_access SEC(".maps");
#define MAX_PATH_FRAGEMENTS 256
static __u64 check_file_need_protection(struct bpf_map *map,
unsigned long *inode, __u8 *enabled,
check_ctx *ctx) {
if (!*enabled) {
return 0;
}
struct dentry *dentry = ctx->dentry;
int count_down = MAX_PATH_FRAGEMENTS;
// enumerate from the leaf to root
while (count_down-- > 0 && dentry != NULL) {
if (dentry->d_inode->i_ino == *inode) {
ctx->need_to_be_checked = 1;
return 1;
}
}
return 0;
}
static __u64 check_service_status(struct bpf_map *map, file_protect_state *kind,
__u64 *state, check_ctx *data) {
__u64 now;
switch (*kind) {
case FILE_PROTECT_ENABLED:
if (!*state) {
data->need_to_be_checked = 0;
data->return_value = 0;
return 1; // early return to improve performance. return 1 means to stop
// iteration.
}
break;
case FILE_PROTECT_TICK:
now = bpf_ktime_get_ns();
// now - last > 3 seconds
// but 3 * SECOND will overflow
if ((now - *state) / 3 > SECOND) {
data->return_value = EPERM;
bpf_trace_printk(log_fmt_timeout, sizeof(log_fmt_timeout), now, *state);
}
break;
case FILE_PROTECT_MAX: // this branch just tell clang to not complaint about
// FILE_PROTECT_MAX
// noop
break;
// default: // TAKE CARE!!!! default branch disable enum branch checking.
// // noop
// break;
}
return 0;
}
SEC("lsm/file_open")
int BPF_PROG(check_file_open, struct file *file, int ret) {
if (ret != 0)
return ret;
check_ctx data = {
.dentry = file->f_path.dentry,
.need_to_be_checked = 0,
.return_value = 0,
};
bpf_for_each_map_elem(&roots, check_file_need_protection, &data, 0);
if (!data.need_to_be_checked) {
return 0;
}
data.need_to_be_checked = 1;
bpf_for_each_map_elem(&states, check_service_status, &data, 0);
if (!data.need_to_be_checked) {
return 0;
}
return data.return_value;
}