Skip to content

Commit

Permalink
bpf: Add lsm.s/* bpf programs for IMA hash collection
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
anfedotoff committed Sep 19, 2024
1 parent 4475918 commit 109f288
Show file tree
Hide file tree
Showing 11 changed files with 317 additions and 22 deletions.
25 changes: 19 additions & 6 deletions bpf/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand All @@ -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 \
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions bpf/include/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions bpf/lib/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions bpf/lib/generic.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
58 changes: 58 additions & 0 deletions bpf/process/bpf_generic_lsm_ima_bprm.c
Original file line number Diff line number Diff line change
@@ -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;
}
58 changes: 58 additions & 0 deletions bpf/process/bpf_generic_lsm_ima_file.c
Original file line number Diff line number Diff line change
@@ -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;
}
99 changes: 99 additions & 0 deletions bpf/process/bpf_generic_lsm_output.c
Original file line number Diff line number Diff line change
@@ -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);
}
2 changes: 2 additions & 0 deletions bpf/process/generic_calls.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 109f288

Please sign in to comment.