From 109f2885075f771e4d3f17695e7bfb9c23dbb304 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 and per_cpu maps, etc.), we need to split generic LSM sensor into three parts (collections) and load them in this order: - bpf_generic_output sends event using perf buffer - bpf_generic_lsm_ima_* calculates hash using IMA helpers - bpf_generic_lsm_core does everything else Signed-off-by: Andrei Fedotov --- bpf/Makefile | 25 +++-- bpf/include/api.h | 4 + bpf/lib/common.h | 1 + bpf/lib/generic.h | 1 + ...f_generic_lsm.c => bpf_generic_lsm_core.c} | 41 ++++++-- bpf/process/bpf_generic_lsm_ima_bprm.c | 58 +++++++++++ bpf/process/bpf_generic_lsm_ima_file.c | 58 +++++++++++ bpf/process/bpf_generic_lsm_output.c | 99 +++++++++++++++++++ bpf/process/generic_calls.h | 2 + bpf/process/types/basic.h | 21 +++- pkg/sensors/tracing/genericlsm.go | 29 +++++- 11 files changed, 317 insertions(+), 22 deletions(-) rename bpf/process/{bpf_generic_lsm.c => bpf_generic_lsm_core.c} (84%) create mode 100644 bpf/process/bpf_generic_lsm_ima_bprm.c create mode 100644 bpf/process/bpf_generic_lsm_ima_file.c create mode 100644 bpf/process/bpf_generic_lsm_output.c diff --git a/bpf/Makefile b/bpf/Makefile index 1a6fb86c093..ecc54d95b96 100644 --- a/bpf/Makefile +++ b/bpf/Makefile @@ -15,7 +15,9 @@ PROCESS = bpf_execve_event.o bpf_execve_event_v53.o bpf_fork.o bpf_exit.o bpf_ge bpf_multi_kprobe_v53.o bpf_multi_retkprobe_v53.o \ bpf_generic_tracepoint.o bpf_generic_tracepoint_v53.o \ bpf_generic_uprobe.o bpf_generic_uprobe_v53.o \ - bpf_generic_lsm.o bpf_generic_lsm_v511.o \ + bpf_generic_lsm_core.o bpf_generic_lsm_output.o \ + bpf_generic_lsm_core_v511.o bpf_generic_lsm_output_v511.o \ + bpf_generic_lsm_ima_file_v511.o bpf_generic_lsm_ima_bprm_v511.o \ bpf_execve_event_v61.o \ bpf_generic_kprobe_v61.o bpf_generic_retkprobe_v61.o \ bpf_generic_tracepoint_v61.o \ @@ -27,7 +29,8 @@ PROCESS = bpf_execve_event.o bpf_execve_event_v53.o bpf_fork.o bpf_exit.o bpf_ge bpf_generic_tracepoint_v511.o \ bpf_multi_kprobe_v511.o bpf_multi_retkprobe_v511.o \ bpf_generic_uprobe_v511.o \ - bpf_generic_lsm_v61.o \ + bpf_generic_lsm_core_v61.o bpf_generic_lsm_output_v61.o \ + bpf_generic_lsm_ima_file_v61.o bpf_generic_lsm_ima_bprm_v61.o \ bpf_loader.o \ bpf_cgroup.o \ bpf_enforcer.o bpf_multi_enforcer.o bpf_fmodret_enforcer.o \ @@ -72,7 +75,10 @@ deps/bpf_multi_retkprobe_$$(VAR).d: process/bpf_generic_retkprobe.c deps/bpf_generic_tracepoint_$$(VAR).d: process/bpf_generic_tracepoint.c deps/bpf_generic_uprobe_$$(VAR).d: process/bpf_generic_uprobe.c deps/bpf_multi_uprobe_$$(VAR).d: process/bpf_generic_uprobe.c -deps/bpf_generic_lsm_$$(VAR).d: process/bpf_generic_lsm.c +deps/bpf_generic_lsm_core_$$(VAR).d: process/bpf_generic_lsm_core.c +deps/bpf_generic_lsm_output_$$(VAR).d: process/bpf_generic_lsm_output.c +deps/bpf_generic_lsm_ima_bprm_$$(VAR).d: process/bpf_generic_lsm_ima_bprm.c +deps/bpf_generic_lsm_ima_file_$$(VAR).d: process/bpf_generic_lsm_ima_file.c endef # Generic build targets for each sub-dir @@ -150,11 +156,18 @@ objs/%_v511.ll: $(DEPSDIR)%_v511.d: $(CLANG) $(CLANG_FLAGS) -D__LARGE_BPF_PROG -D__LARGE_MAP_KEYS -MM -MP -MT $(patsubst $(DEPSDIR)%.d, $(OBJSDIR)%.ll, $@) $< > $@ -objs/bpf_generic_lsm.ll: process/bpf_generic_lsm.c +objs/bpf_generic_lsm_core.ll: process/bpf_generic_lsm_core.c $(CLANG) $(CLANG_FLAGS) -D__LARGE_BPF_PROG -c $< -o $@ -$(DEPSDIR)/bpf_generic_lsm.d: - $(CLANG) $(CLANG_FLAGS) -D__LARGE_BPF_PROG -MM -MP -MT $(patsubst $(DEPSDIR)bpf_generic_lsm.d, $(OBJSDIR)bpf_generic_lsm.ll, $@) $< > $@ +$(DEPSDIR)/bpf_generic_lsm_core.d: + $(CLANG) $(CLANG_FLAGS) -D__LARGE_BPF_PROG -MM -MP -MT $(patsubst $(DEPSDIR)bpf_generic_lsm_core.d, $(OBJSDIR)bpf_generic_lsm_core.ll, $@) $< > $@ + +objs/bpf_generic_lsm_output.ll: process/bpf_generic_lsm_output.c + $(CLANG) $(CLANG_FLAGS) -D__LARGE_BPF_PROG -c $< -o $@ + +$(DEPSDIR)/bpf_generic_lsm_output.d: + $(CLANG) $(CLANG_FLAGS) -D__LARGE_BPF_PROG -MM -MP -MT $(patsubst $(DEPSDIR)bpf_generic_lsm_output.d, $(OBJSDIR)bpf_generic_lsm_output.ll, $@) $< > $@ + # BPFTESTDIR objs/%.ll: $(BPFTESTDIR)%.c diff --git a/bpf/include/api.h b/bpf/include/api.h index 4d42ba8c237..c216b92bb66 100644 --- a/bpf/include/api.h +++ b/bpf/include/api.h @@ -269,6 +269,10 @@ static void BPF_FUNC(dynptr_data, const struct bpf_dynptr *ptr, uint32_t offset, static long BPF_FUNC(sock_ops_cb_flags_set, struct bpf_sock_ops *bpf_sock, int argval); +/* 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/lib/generic.h b/bpf/lib/generic.h index bd693950e5f..cd91f04c2d6 100644 --- a/bpf/lib/generic.h +++ b/bpf/lib/generic.h @@ -55,6 +55,7 @@ struct msg_generic_kprobe { __u32 tailcall_index_process; // recursion index for generic_process_event __u32 tailcall_index_selector; // recursion index for filter_read_arg int pass; + bool post; // true if event needs to be posted. This flag is for LSM sensor }; FUNC_INLINE size_t generic_kprobe_common_size(void) diff --git a/bpf/process/bpf_generic_lsm.c b/bpf/process/bpf_generic_lsm_core.c similarity index 84% rename from bpf/process/bpf_generic_lsm.c rename to bpf/process/bpf_generic_lsm_core.c index 69c53848e2c..4e2d119b9ea 100644 --- a/bpf/process/bpf_generic_lsm.c +++ b/bpf/process/bpf_generic_lsm_core.c @@ -56,6 +56,15 @@ struct { __type(value, struct event_config); } config_map SEC(".maps"); +#ifdef __LARGE_MAP_KEYS +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, __u64); + __type(value, char[65]); +} ima_hash_map SEC(".maps"); +#endif + #ifdef __LARGE_BPF_PROG struct { __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); @@ -89,7 +98,7 @@ FUNC_INLINE int try_override(void *ctx) return (long)*error; } -#define MAIN "lsm/generic_lsm" +#define MAIN "lsm/generic_lsm_core" __attribute__((section((MAIN)), used)) int generic_lsm_event(struct pt_regs *ctx) @@ -146,14 +155,28 @@ generic_lsm_actions(void *ctx) { generic_actions(ctx, &maps); - // If NoPost action is set, check for Override action here - return try_override(ctx); -} + struct msg_generic_kprobe *e; + int zero = 0; -__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); + e = map_lookup_elem(&process_call_heap, &zero); + if (!e) + return 0; + +#ifdef __LARGE_MAP_KEYS + // Set dummy hash entry for ima program + if (e && e->common.flags & MSG_COMMON_FLAG_IMA_HASH) { + char hash[65]; + + __u64 pid_tgid = get_current_pid_tgid(); + + memset(&hash, 0, 65); + map_update_elem(&ima_hash_map, &pid_tgid, &hash, BPF_ANY); + } +#endif + + // If NoPost action is set, check for Override action here + if (!e->post) + return try_override(ctx); - return try_override(ctx); + return 0; } diff --git a/bpf/process/bpf_generic_lsm_ima_bprm.c b/bpf/process/bpf_generic_lsm_ima_bprm.c new file mode 100644 index 00000000000..bb7dbd866fa --- /dev/null +++ b/bpf/process/bpf_generic_lsm_ima_bprm.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* Copyright Authors of Cilium */ + +#include "vmlinux.h" +#include "api.h" + +#define GENERIC_LSM + +#include "compiler.h" +#include "bpf_event.h" +#include "bpf_task.h" +#include "retprobe_map.h" +#include "types/basic.h" + +char _license[] __attribute__((section("license"), used)) = "Dual BSD/GPL"; + +struct filter_map_value { + unsigned char buf[FILTER_SIZE]; +}; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, struct filter_map_value); +} filter_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, struct event_config); +} config_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, __u64); + __type(value, char[65]); +} ima_hash_map SEC(".maps"); + +__attribute__((section("lsm.s/generic_lsm_ima_bprm"), used)) int +BPF_PROG(ima_bprm, struct linux_binprm *bprm) +{ + char hash[65]; + __u64 pid_tgid = get_current_pid_tgid(); + char *dummy = map_lookup_elem(&ima_hash_map, &pid_tgid); + + if (dummy) { +#ifdef __V61_BPF_PROG + hash[0] = ima_file_hash(bprm->file, &hash[1], 64); +#else + hash[0] = ima_inode_hash(bprm->file->f_inode, &hash[1], 64); +#endif + map_update_elem(&ima_hash_map, &pid_tgid, &hash, BPF_ANY); + } + return 0; +} diff --git a/bpf/process/bpf_generic_lsm_ima_file.c b/bpf/process/bpf_generic_lsm_ima_file.c new file mode 100644 index 00000000000..707ba387e87 --- /dev/null +++ b/bpf/process/bpf_generic_lsm_ima_file.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* Copyright Authors of Cilium */ + +#include "vmlinux.h" +#include "api.h" + +#define GENERIC_LSM + +#include "compiler.h" +#include "bpf_event.h" +#include "bpf_task.h" +#include "retprobe_map.h" +#include "types/basic.h" + +char _license[] __attribute__((section("license"), used)) = "Dual BSD/GPL"; + +struct filter_map_value { + unsigned char buf[FILTER_SIZE]; +}; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, struct filter_map_value); +} filter_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, struct event_config); +} config_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, __u64); + __type(value, char[65]); +} ima_hash_map SEC(".maps"); + +__attribute__((section("lsm.s/generic_lsm_ima_file"), used)) int +BPF_PROG(ima_file, struct file *file) +{ + char hash[65]; + __u64 pid_tgid = get_current_pid_tgid(); + char *dummy = map_lookup_elem(&ima_hash_map, &pid_tgid); + + if (dummy) { +#ifdef __V61_BPF_PROG + hash[0] = ima_file_hash(file, &hash[1], 64); +#else + hash[0] = ima_inode_hash(file->f_inode, &hash[1], 64); +#endif + map_update_elem(&ima_hash_map, &pid_tgid, &hash, BPF_ANY); + } + return 0; +} diff --git a/bpf/process/bpf_generic_lsm_output.c b/bpf/process/bpf_generic_lsm_output.c new file mode 100644 index 00000000000..975ff74291d --- /dev/null +++ b/bpf/process/bpf_generic_lsm_output.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* Copyright Authors of Cilium */ + +#include "vmlinux.h" +#include "api.h" + +#define GENERIC_LSM + +#include "compiler.h" +#include "bpf_event.h" +#include "bpf_task.h" +#include "retprobe_map.h" +#include "types/basic.h" + +char _license[] __attribute__((section("license"), used)) = "Dual BSD/GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, struct msg_generic_kprobe); +} process_call_heap SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 32768); + __type(key, __u64); + __type(value, __s32); +} override_tasks SEC(".maps"); + +struct filter_map_value { + unsigned char buf[FILTER_SIZE]; +}; + +/* Arrays of size 1 will be rewritten to direct loads in verifier */ +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, struct filter_map_value); +} filter_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, struct event_config); +} config_map SEC(".maps"); + +#ifdef __LARGE_MAP_KEYS +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, __u64); + __type(value, char[65]); +} ima_hash_map SEC(".maps"); +#endif + +FUNC_INLINE int try_override(void *ctx) +{ + __u64 id = get_current_pid_tgid(); + __s32 *error; + + error = map_lookup_elem(&override_tasks, &id); + if (!error) + return 0; + + map_delete_elem(&override_tasks, &id); + return (long)*error; +} + +__attribute__((section("lsm/generic_lsm_output"), used)) int +generic_lsm_output(void *ctx) +{ + struct msg_generic_kprobe *e; + int zero = 0; + + e = map_lookup_elem(&process_call_heap, &zero); + if (!e) + return 0; +#ifdef __LARGE_MAP_KEYS + if (e && e->common.flags & MSG_COMMON_FLAG_IMA_HASH) { + __u64 pid_tgid = get_current_pid_tgid(); + char *hash = map_lookup_elem(&ima_hash_map, &pid_tgid); + + if (hash) { + // Copy hash after all arguments + if (e->common.size + 65 <= 16383) { + probe_read(&e->args[e->common.size & 16383], 65, hash); + e->common.size += 65; + } + map_delete_elem(&ima_hash_map, &pid_tgid); + } + } +#endif + if (e->post) + generic_output(ctx, (struct bpf_map_def *)&process_call_heap, MSG_OP_GENERIC_LSM); + return try_override(ctx); +} diff --git a/bpf/process/generic_calls.h b/bpf/process/generic_calls.h index 603a313a765..a10cc41eddc 100644 --- a/bpf/process/generic_calls.h +++ b/bpf/process/generic_calls.h @@ -55,6 +55,8 @@ generic_start_process_filter(void *ctx, struct generic_maps *maps) msg->sel.match_cap = 0; #endif + msg->post = false; + /* Tail call into filters. */ tail_call(ctx, maps->calls, TAIL_CALL_FILTER); return 0; diff --git a/bpf/process/types/basic.h b/bpf/process/types/basic.h index bf93e6be20d..650b13529cc 100644 --- a/bpf/process/types/basic.h +++ b/bpf/process/types/basic.h @@ -2181,15 +2181,19 @@ do_action(void *ctx, __u32 i, struct selector_action *actions, return 0; switch (action) { - case ACTION_NOPOST: + case ACTION_NOPOST: { *post = false; + e->post = false; break; + } case ACTION_POST: { __u64 ratelimit_interval __maybe_unused = actions->act[++i]; __u64 ratelimit_scope __maybe_unused = actions->act[++i]; #ifdef __LARGE_BPF_PROG - if (rate_limit(ratelimit_interval, ratelimit_scope, e)) + if (rate_limit(ratelimit_interval, ratelimit_scope, e)) { *post = false; + e->post = false; + } #endif /* __LARGE_BPF_PROG */ __u32 kernel_stack_trace = actions->act[++i]; @@ -2211,6 +2215,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; } @@ -2286,6 +2296,13 @@ do_actions(void *ctx, struct selector_action *actions, struct generic_maps *maps { bool post = true; __u32 l, i = 0; + int zero = 0; + struct msg_generic_kprobe *e; + + e = map_lookup_elem(maps->heap, &zero); + if (!e) + return 0; + e->post = true; #ifndef __LARGE_BPF_PROG #pragma unroll diff --git a/pkg/sensors/tracing/genericlsm.go b/pkg/sensors/tracing/genericlsm.go index 7688e427427..7197c68a715 100644 --- a/pkg/sensors/tracing/genericlsm.go +++ b/pkg/sensors/tracing/genericlsm.go @@ -345,17 +345,29 @@ func createGenericLsmSensor( func createLsmSensorFromEntry(lsmEntry *genericLsm, progs []*program.Program, maps []*program.Map) ([]*program.Program, []*program.Map) { - loadProgName := "bpf_generic_lsm.o" + loadProgCoreName := "bpf_generic_lsm_core.o" + loadProgOutputName := "bpf_generic_lsm_output.o" if kernels.EnableV61Progs() { - loadProgName = "bpf_generic_lsm_v61.o" + loadProgCoreName = "bpf_generic_lsm_core_v61.o" + loadProgOutputName = "bpf_generic_lsm_output_v61.o" } else if kernels.MinKernelVersion("5.11") { - loadProgName = "bpf_generic_lsm_v511.o" + loadProgCoreName = "bpf_generic_lsm_core_v511.o" + loadProgOutputName = "bpf_generic_lsm_output_v511.o" } + loadOutput := program.Builder( + path.Join(option.Config.HubbleLib, loadProgOutputName), + lsmEntry.hook, + "lsm/generic_lsm_output", + lsmEntry.hook, + "generic_lsm"). + SetLoaderData(lsmEntry.tableId) + progs = append(progs, loadOutput) + load := program.Builder( - path.Join(option.Config.HubbleLib, loadProgName), + path.Join(option.Config.HubbleLib, loadProgCoreName), lsmEntry.hook, - "lsm/generic_lsm", + "lsm/generic_lsm_core", lsmEntry.hook, "generic_lsm"). SetLoaderData(lsmEntry.tableId) @@ -376,6 +388,8 @@ func createLsmSensorFromEntry(lsmEntry *genericLsm, callHeap := program.MapBuilderProgram("process_call_heap", load) maps = append(maps, callHeap) + callHeapOutput := program.MapBuilderProgram("process_call_heap", loadOutput) + maps = append(maps, callHeapOutput) selMatchBinariesMap := program.MapBuilderProgram("tg_mb_sel_opts", load) maps = append(maps, selMatchBinariesMap) @@ -388,6 +402,11 @@ func createLsmSensorFromEntry(lsmEntry *genericLsm, } maps = append(maps, matchBinariesPaths) + overrideTasksMap := program.MapBuilderProgram("override_tasks", load) + maps = append(maps, overrideTasksMap) + overrideTasksMapOutput := program.MapBuilderProgram("override_tasks", loadOutput) + maps = append(maps, overrideTasksMapOutput) + logger.GetLogger(). Infof("Added generic lsm sensor: %s -> %s", load.Name, load.Attach) return progs, maps