From c8f96f3e1b4980e407fe830a1b0dd1a0e06f861c Mon Sep 17 00:00:00 2001 From: Nicholas Berlin <56366649+nicholasberlin@users.noreply.github.com> Date: Tue, 29 Aug 2023 15:10:35 -0400 Subject: [PATCH] Add effective and permitted caps to fork and exec events. (#172) * Add effective and permitted caps * Fix formatting * Add caps dev package * Fix copy-paste error * Test the correct value * Add docs about kernel_cap_t changes --- .github/workflows/multikernel-tester.yml | 1 + GPL/Events/EbpfEventProto.h | 3 ++ GPL/Events/Helpers.h | 43 ++++++++++++++++++++++++ GPL/Events/Process/Probe.bpf.c | 1 + non-GPL/Events/EventsTrace/EventsTrace.c | 7 ++++ testing/test_bins/common.h | 17 ++++++++-- testing/test_bins/fork_exec.c | 9 +++++ testing/testrunner/tests.go | 4 +++ testing/testrunner/utils.go | 11 ++++++ 9 files changed, 94 insertions(+), 2 deletions(-) diff --git a/.github/workflows/multikernel-tester.yml b/.github/workflows/multikernel-tester.yml index f059dd9b..9f101f92 100644 --- a/.github/workflows/multikernel-tester.yml +++ b/.github/workflows/multikernel-tester.yml @@ -31,6 +31,7 @@ jobs: sudo apt-get install -y --no-install-recommends \ gcc-aarch64-linux-gnu \ libc6-dev-arm64-cross \ + libcap-dev \ parallel \ qemu-system-x86 \ qemu-system-arm \ diff --git a/GPL/Events/EbpfEventProto.h b/GPL/Events/EbpfEventProto.h index add99019..8485fac8 100644 --- a/GPL/Events/EbpfEventProto.h +++ b/GPL/Events/EbpfEventProto.h @@ -94,6 +94,8 @@ struct ebpf_cred_info { uint32_t egid; // Effective group ID uint32_t suid; // Saved user ID uint32_t sgid; // Saved group ID + uint64_t cap_permitted; + uint64_t cap_effective; } __attribute__((packed)); struct ebpf_tty_winsize { @@ -150,6 +152,7 @@ struct ebpf_process_fork_event { struct ebpf_event_header hdr; struct ebpf_pid_info parent_pids; struct ebpf_pid_info child_pids; + struct ebpf_cred_info creds; // Variable length fields: pids_ss_cgroup_path struct ebpf_varlen_fields_start vl_fields; diff --git a/GPL/Events/Helpers.h b/GPL/Events/Helpers.h index 0d971171..d8ee8fd6 100644 --- a/GPL/Events/Helpers.h +++ b/GPL/Events/Helpers.h @@ -235,6 +235,49 @@ static void ebpf_cred_info__fill(struct ebpf_cred_info *ci, const struct task_st ci->rgid = BPF_CORE_READ(task, cred, gid.val); ci->egid = BPF_CORE_READ(task, cred, egid.val); ci->sgid = BPF_CORE_READ(task, cred, sgid.val); + + // This check is to determine when the kernel_cap_t definition changed. + // + // Previously it was: + // typedef struct kernel_cap_struct { + // __u32 cap[_KERNEL_CAPABILITY_U32S]; + // } kernel_cap_t; + // + // Currently it is: + // typedef struct { u64 val; } kernel_cap_t; + // + // See https://github.com/torvalds/linux/commit/f122a08b197d076ccf136c73fae0146875812a88 + // + if (bpf_core_field_exists(task->cred->cap_permitted.cap)) { + kernel_cap_t dest; + + dest.cap[0] = 0; + dest.cap[1] = 0; + dest = BPF_CORE_READ(task, cred, cap_permitted); + ci->cap_permitted = (((u64)dest.cap[1]) << 32) + dest.cap[0]; + + dest.cap[0] = 0; + dest.cap[1] = 0; + dest = BPF_CORE_READ(task, cred, cap_effective); + ci->cap_effective = (((u64)dest.cap[1]) << 32) + dest.cap[0]; + } else { + const struct cred *cred = BPF_CORE_READ(task, cred); + const void *cap = NULL; + + struct new_kernel_cap_struct { + u64 val; + } dest; + + dest.val = 0; + cap = &cred->cap_permitted; + bpf_core_read(&dest, bpf_core_type_size(struct new_kernel_cap_struct), cap); + ci->cap_permitted = dest.val; + + dest.val = 0; + cap = &cred->cap_effective; + bpf_core_read(&dest, bpf_core_type_size(struct new_kernel_cap_struct), cap); + ci->cap_effective = dest.val; + } } static bool is_kernel_thread(const struct task_struct *task) diff --git a/GPL/Events/Process/Probe.bpf.c b/GPL/Events/Process/Probe.bpf.c index 02514cb6..bdc5364a 100644 --- a/GPL/Events/Process/Probe.bpf.c +++ b/GPL/Events/Process/Probe.bpf.c @@ -49,6 +49,7 @@ int BPF_PROG(sched_process_fork, const struct task_struct *parent, const struct event->hdr.ts = bpf_ktime_get_ns(); ebpf_pid_info__fill(&event->parent_pids, parent); ebpf_pid_info__fill(&event->child_pids, child); + ebpf_cred_info__fill(&event->creds, parent); // Variable length fields ebpf_vl_fields__init(&event->vl_fields); diff --git a/non-GPL/Events/EventsTrace/EventsTrace.c b/non-GPL/Events/EventsTrace/EventsTrace.c index cf0f7d26..e1231a5d 100644 --- a/non-GPL/Events/EventsTrace/EventsTrace.c +++ b/non-GPL/Events/EventsTrace/EventsTrace.c @@ -295,6 +295,10 @@ static void out_cred_info(const char *name, struct ebpf_cred_info *cred_info) out_int("suid", cred_info->suid); out_comma(); out_int("sgid", cred_info->sgid); + out_comma(); + printf("\"cap_permitted\": \"%lu\"", cred_info->cap_permitted); + out_comma(); + printf("\"cap_effective\": \"%lu\"", cred_info->cap_effective); out_object_end(); } @@ -429,6 +433,9 @@ static void out_process_fork(struct ebpf_process_fork_event *evt) out_comma(); out_pid_info("child_pids", &evt->child_pids); + out_comma(); + + out_cred_info("creds", &evt->creds); struct ebpf_varlen_field *field; FOR_EACH_VARLEN_FIELD(evt->vl_fields, field) diff --git a/testing/test_bins/common.h b/testing/test_bins/common.h index 415e3e2d..c8569441 100644 --- a/testing/test_bins/common.h +++ b/testing/test_bins/common.h @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -29,6 +30,18 @@ pid_t gettid() void gen_pid_info_json(char *buf, size_t size) { - snprintf(buf, size, "{\"tid\": %d, \"ppid\": %d, \"tgid\": %d, \"sid\": %d, \"pgid\": %d}", - gettid(), getppid(), getpid(), getsid(0), getpgid(0)); + struct __user_cap_header_struct hdr = {_LINUX_CAPABILITY_VERSION_3, 0}; + struct __user_cap_data_struct data[2] = {{0}}; + uint64_t cap_permitted = 0; + uint64_t cap_effective = 0; + + (void)capget(&hdr, data); + cap_permitted = ((uint64_t)data[1].permitted << 32) + (uint64_t)data[0].permitted; + cap_effective = ((uint64_t)data[1].effective << 32) + (uint64_t)data[0].effective; + + snprintf(buf, size, + "{\"tid\": %d, \"ppid\": %d, \"tgid\": %d, \"sid\": %d, \"pgid\": %d, " + "\"cap_permitted\": \"%lu\", \"cap_effective\":\"%lu\"}", + (pid_t)syscall(SYS_gettid), getppid(), getpid(), getsid(0), getpgid(0), cap_permitted, + cap_effective); } diff --git a/testing/test_bins/fork_exec.c b/testing/test_bins/fork_exec.c index 61bcf7dc..53bdb9c2 100644 --- a/testing/test_bins/fork_exec.c +++ b/testing/test_bins/fork_exec.c @@ -17,6 +17,15 @@ int main() { pid_t pid; + struct __user_cap_header_struct hdr = {_LINUX_CAPABILITY_VERSION_3, 0}; + struct __user_cap_data_struct data[2] = {{0}}; + + data[0].permitted = 0xffffffff; + data[1].permitted = 0; + data[0].effective = 0xf0f0f0f0; + data[1].effective = 0; + CHECK(capset(&hdr, &data[0]), -1); + CHECK(pid = fork(), -1); if (pid != 0) { diff --git a/testing/testrunner/tests.go b/testing/testrunner/tests.go index 1036f59c..116a1428 100644 --- a/testing/testrunner/tests.go +++ b/testing/testrunner/tests.go @@ -130,6 +130,10 @@ func TestForkExec(et *EventsTraceInstance) { } } + AssertUint64Equal(uint64(forkEvent.Creds.CapPermitted), uint64(0x00000000ffffffff)) + AssertUint64Equal(uint64(forkEvent.Creds.CapEffective), uint64(0x00000000f0f0f0f0)) + AssertUint64Equal(uint64(execEvent.Creds.CapPermitted), uint64(0x000001ffffffffff)) + AssertUint64Equal(uint64(execEvent.Creds.CapEffective), uint64(0x000001ffffffffff)) AssertStringsEqual(execEvent.FileName, "./do_nothing") AssertStringsEqual(execEvent.Argv[0], "./do_nothing") AssertStringsEqual(execEvent.Env[0], "TEST_ENV_KEY1=TEST_ENV_VAL1") diff --git a/testing/testrunner/utils.go b/testing/testrunner/utils.go index 69b7a58a..039a2476 100644 --- a/testing/testrunner/utils.go +++ b/testing/testrunner/utils.go @@ -31,6 +31,8 @@ type TestPidInfo struct { Ppid int64 `json:"ppid"` Pgid int64 `json:"pgid"` Sid int64 `json:"sid"` + CapPermitted uint64 `json:"cap_permitted,string"` + CapEffective uint64 `json:"cap_effective,string"` } // Definitions of types printed by EventsTrace for conversion from JSON @@ -57,6 +59,8 @@ type CredInfo struct { Egid int64 `json:"egid"` Suid int64 `json:"suid"` Sgid int64 `json:"sgid"` + CapPermitted uint64 `json:"cap_permitted,string"` + CapEffective uint64 `json:"cap_effective,string"` } type TtyInfo struct { @@ -77,6 +81,7 @@ type NetInfo struct { type ProcessForkEvent struct { ParentPids PidInfo `json:"parent_pids"` ChildPids PidInfo `json:"child_pids"` + Creds CredInfo `json:"creds"` } type ProcessExecEvent struct { @@ -223,6 +228,12 @@ func AssertInt64NotEqual(a, b int64) { } } +func AssertUint64Equal(a, b uint64) { + if a != b { + TestFail(fmt.Sprintf("Test assertion failed 0x%016x != 0x%016x", a, b)) + } +} + func PrintBPFDebugOutput() { file, err := os.Open("/sys/kernel/debug/tracing/trace") if err != nil {