From e38e7381ee99ee785e9e6b86f9909f067069600f Mon Sep 17 00:00:00 2001 From: Andrei Fedotov Date: Sun, 25 Aug 2024 11:03:07 +0300 Subject: [PATCH] bpf: Add lsm.s/* bpf programs for IMA hash collection Due to restrictions of bpf sleepable programs (no tailcalls, no perf buffer maps, etc.), we need to tail call to lsm.s program and store hashes in separate bpf_map after event is posted. Signed-off-by: Andrei Fedotov --- bpf/include/api.h | 4 ++ bpf/lib/common.h | 1 + bpf/process/bpf_generic_lsm.c | 83 +++++++++++++++++++++++++++++++++ bpf/process/types/basic.h | 7 +++ pkg/sensors/program/loader.go | 3 ++ pkg/sensors/tracing/lsm_test.go | 4 ++ 6 files changed, 102 insertions(+) diff --git a/bpf/include/api.h b/bpf/include/api.h index 9a83dbdd68e..4994824e9d4 100644 --- a/bpf/include/api.h +++ b/bpf/include/api.h @@ -267,6 +267,10 @@ static long BPF_FUNC(dynptr_read, void *dst, uint32_t len, const struct bpf_dynp static long BPF_FUNC(dynptr_write, const struct bpf_dynptr *dst, uint32_t offset, void *src, uint32_t len, uint64_t flags); static void BPF_FUNC(dynptr_data, const struct bpf_dynptr *ptr, uint32_t offset, uint32_t len); +/* LSM */ +static long BPF_FUNC(ima_file_hash, struct file *file, void *dst, uint32_t size); +static long BPF_FUNC(ima_inode_hash, struct inode *inode, void *dst, uint32_t size); + /** LLVM built-ins, mem*() routines work for constant size */ #ifndef lock_xadd diff --git a/bpf/lib/common.h b/bpf/lib/common.h index acd23496346..48a9b5718a4 100644 --- a/bpf/lib/common.h +++ b/bpf/lib/common.h @@ -7,6 +7,7 @@ #define MSG_COMMON_FLAG_RETURN BIT(0) #define MSG_COMMON_FLAG_KERNEL_STACKTRACE BIT(1) #define MSG_COMMON_FLAG_USER_STACKTRACE BIT(2) +#define MSG_COMMON_FLAG_IMA_HASH BIT(3) /* Msg Layout */ struct msg_common { diff --git a/bpf/process/bpf_generic_lsm.c b/bpf/process/bpf_generic_lsm.c index 69c53848e2c..f8dd3c0bdda 100644 --- a/bpf/process/bpf_generic_lsm.c +++ b/bpf/process/bpf_generic_lsm.c @@ -56,6 +56,20 @@ struct { __type(value, struct event_config); } config_map SEC(".maps"); +#ifdef __LARGE_MAP_KEYS +struct ima_hash { + int algo; + char hash[64]; +}; + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, 1024); + __type(key, struct msg_execve_key); + __type(value, struct ima_hash); +} ima_hash_map SEC(".maps"); +#endif + #ifdef __LARGE_BPF_PROG struct { __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); @@ -154,6 +168,75 @@ __attribute__((section("lsm/5"), used)) int generic_lsm_output(void *ctx) { generic_output(ctx, (struct bpf_map_def *)&process_call_heap, MSG_OP_GENERIC_LSM); +#ifdef __LARGE_MAP_KEYS + struct msg_generic_kprobe *e; + int zero = 0; + + e = map_lookup_elem(&process_call_heap, &zero); + if (e && e->common.flags & MSG_COMMON_FLAG_IMA_HASH) + tail_call(ctx, &lsm_calls, TAIL_CALL_IMA_HASH); +#endif + return try_override(ctx); +} + +#ifdef __LARGE_MAP_KEYS +__attribute__((section("lsm.s/file_open"), used)) int +BPF_PROG(ima_file_open, struct file *file) +{ + struct ima_hash hash; + struct msg_generic_kprobe *e; + int zero = 0; + + e = map_lookup_elem(&process_call_heap, &zero); + if (!e) + return 0; +#ifdef __V61_BPF_PROG + hash.algo = ima_file_hash(file, &hash.hash, 64); +#else + hash.algo = ima_inode_hash(file->f_inode, &hash.hash, 64); +#endif + map_update_elem(&ima_hash_map, &e->current, &hash, BPF_ANY); + + return try_override(ctx); +} + +__attribute__((section("lsm.s/mmap_file"), used)) int +BPF_PROG(ima_mmap_file, struct file *file, unsigned long prot, unsigned long flags) +{ + struct ima_hash hash; + struct msg_generic_kprobe *e; + int zero = 0; + + e = map_lookup_elem(&process_call_heap, &zero); + if (!e) + return 0; +#ifdef __V61_BPF_PROG + hash.algo = ima_file_hash(file, &hash.hash, 64); +#else + hash.algo = ima_inode_hash(file->f_inode, &hash.hash, 64); +#endif + map_update_elem(&ima_hash_map, &e->current, &hash, BPF_ANY); return try_override(ctx); } + +__attribute__((section("lsm.s/bprm_check_security"), used)) int +BPF_PROG(ima_bprm_check_security, struct linux_binprm *bprm) +{ + struct ima_hash hash; + struct msg_generic_kprobe *e; + int zero = 0; + + e = map_lookup_elem(&process_call_heap, &zero); + if (!e) + return 0; +#ifdef __V61_BPF_PROG + hash.algo = ima_file_hash(bprm->file, &hash.hash, 64); +#else + hash.algo = ima_inode_hash(bprm->file->f_inode, &hash.hash, 64); +#endif + map_update_elem(&ima_hash_map, &e->current, &hash, BPF_ANY); + + return try_override(ctx); +} +#endif diff --git a/bpf/process/types/basic.h b/bpf/process/types/basic.h index 06de0defca1..292d878f560 100644 --- a/bpf/process/types/basic.h +++ b/bpf/process/types/basic.h @@ -120,6 +120,7 @@ enum { TAIL_CALL_ARGS = 3, TAIL_CALL_ACTIONS = 4, TAIL_CALL_SEND = 5, + TAIL_CALL_IMA_HASH = 6, }; struct generic_maps { @@ -2211,6 +2212,12 @@ do_action(void *ctx, __u32 i, struct selector_action *actions, e->common.flags |= MSG_COMMON_FLAG_USER_STACKTRACE; e->user_stack_id = get_stackid(ctx, &stack_trace_map, BPF_F_USER_STACK); } +#ifdef __LARGE_MAP_KEYS + __u32 ima_hash = actions->act[++i]; + + if (ima_hash) + e->common.flags |= MSG_COMMON_FLAG_IMA_HASH; +#endif break; } diff --git a/pkg/sensors/program/loader.go b/pkg/sensors/program/loader.go index b904f2b1334..dfe23216abc 100644 --- a/pkg/sensors/program/loader.go +++ b/pkg/sensors/program/loader.go @@ -423,6 +423,9 @@ func TracingAttach() AttachFunc { func LSMOpen(load *Program) OpenFunc { return func(coll *ebpf.CollectionSpec) error { + delete(coll.Programs, "ima_file_open") + delete(coll.Programs, "ima_mmap_file") + delete(coll.Programs, "ima_bprm_check_security") for _, prog := range coll.Programs { if prog.AttachType == ebpf.AttachLSMMac { prog.AttachTo = load.Attach diff --git a/pkg/sensors/tracing/lsm_test.go b/pkg/sensors/tracing/lsm_test.go index 5fee0397753..7b9cdee895a 100644 --- a/pkg/sensors/tracing/lsm_test.go +++ b/pkg/sensors/tracing/lsm_test.go @@ -67,6 +67,10 @@ func TestLSMObjectLoad(t *testing.T) { // generic_lsm_process_event*,generic_lsm_output tus.SensorMap{Name: "tcpmon_map", Progs: []uint{1, 2, 6}}, } + if kernels.MinKernelVersion("5.11") { + // all LSM programs + sensorMaps[1] = tus.SensorMap{Name: "lsm_calls", Progs: []uint{0, 1, 2, 3, 4, 5, 6}} + } configHook := ` apiVersion: cilium.io/v1alpha1