// SPDX-License-Identifier: GPL-2.0 // // Unique filtering based on // https://github.com/libbpf/libbpf-rs/tree/master/examples/capable // // Copyright 2022 Sony Group Corporation #include #include #include #include "capable.h" #define MAX_ENTRIES 10240 extern int LINUX_KERNEL_VERSION __kconfig; const volatile pid_t my_pid = -1; const volatile enum uniqueness unique_type = UNQ_OFF; const volatile bool kernel_stack = false; const volatile bool user_stack = false; const volatile bool filter_cg = false; const volatile pid_t targ_pid = -1; struct args_t { int cap; int cap_opt; }; struct unique_key { int cap; u32 tgid; u64 cgroupid; }; struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 10240); __type(key, u64); __type(value, struct args_t); } start SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_CGROUP_ARRAY); __type(key, u32); __type(value, u32); __uint(max_entries, 1); } cgroup_map SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); __uint(key_size, sizeof(__u32)); __uint(value_size, sizeof(__u32)); } events SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_STACK_TRACE); __uint(key_size, sizeof(u32)); } stackmap SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, struct key_t); __type(value, struct cap_event); __uint(max_entries, MAX_ENTRIES); } info SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 10240); __type(key, struct unique_key); __type(value, u64); } seen SEC(".maps"); SEC("kprobe/cap_capable") int BPF_KPROBE(kprobe__cap_capable_entry, const struct cred *cred, struct user_namespace *targ_ns, int cap, int cap_opt) { __u32 pid; __u64 pid_tgid; if (filter_cg && !bpf_current_task_under_cgroup(&cgroup_map, 0)) return 0; pid_tgid = bpf_get_current_pid_tgid(); pid = pid_tgid >> 32; if (pid == my_pid) return 0; if (targ_pid != -1 && targ_pid != pid) return 0; struct args_t args = {}; args.cap = cap; args.cap_opt = cap_opt; bpf_map_update_elem(&start, &pid_tgid, &args, 0); return 0; } SEC("kretprobe/cap_capable") int BPF_KRETPROBE(kprobe__cap_capable_exit) { __u64 pid_tgid; struct args_t *ap; struct key_t i_key; pid_tgid = bpf_get_current_pid_tgid(); ap = bpf_map_lookup_elem(&start, &pid_tgid); if (!ap) return 0; /* missed entry */ bpf_map_delete_elem(&start, &pid_tgid); struct cap_event event = {}; event.pid = pid_tgid >> 32; event.tgid = pid_tgid; event.cap = ap->cap; event.uid = bpf_get_current_uid_gid(); bpf_get_current_comm(&event.task, sizeof(event.task)); event.ret = PT_REGS_RC(ctx); if (LINUX_KERNEL_VERSION >= KERNEL_VERSION(5, 1, 0)) { /* @opts: Bitmask of options defined in include/linux/security.h */ event.audit = (ap->cap_opt & 0b10) == 0; event.insetid = (ap->cap_opt & 0b100) != 0; } else { event.audit = ap->cap_opt; event.insetid = -1; } if (unique_type) { struct unique_key key = {.cap = ap->cap}; if (unique_type == UNQ_CGROUP) key.cgroupid = bpf_get_current_cgroup_id(); else key.tgid = pid_tgid; if (bpf_map_lookup_elem(&seen, &key) != NULL) return 0; u64 zero = 0; bpf_map_update_elem(&seen, &key, &zero, 0); } if (kernel_stack || user_stack) { i_key.pid = pid_tgid >> 32; i_key.tgid = pid_tgid; i_key.kern_stack_id = i_key.user_stack_id = -1; if (user_stack) i_key.user_stack_id = bpf_get_stackid(ctx, &stackmap, BPF_F_USER_STACK); if (kernel_stack) i_key.kern_stack_id = bpf_get_stackid(ctx, &stackmap, 0); bpf_map_update_elem(&info, &i_key, &event, BPF_NOEXIST); } bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event)); return 0; } char LICENSE[] SEC("license") = "GPL";