1 // SPDX-License-Identifier: GPL-2.0
2 //
3 // Unique filtering based on
4 // https://github.com/libbpf/libbpf-rs/tree/master/examples/capable
5 //
6 // Copyright 2022 Sony Group Corporation
7
8 #include <vmlinux.h>
9 #include <bpf/bpf_helpers.h>
10 #include <bpf/bpf_tracing.h>
11 #include "capable.h"
12
13 #define MAX_ENTRIES 10240
14
15 extern int LINUX_KERNEL_VERSION __kconfig;
16
17 const volatile pid_t my_pid = -1;
18 const volatile enum uniqueness unique_type = UNQ_OFF;
19 const volatile bool kernel_stack = false;
20 const volatile bool user_stack = false;
21 const volatile bool filter_cg = false;
22 const volatile pid_t targ_pid = -1;
23
24 struct args_t {
25 int cap;
26 int cap_opt;
27 };
28
29 struct unique_key {
30 int cap;
31 u32 tgid;
32 u64 cgroupid;
33 };
34
35 struct {
36 __uint(type, BPF_MAP_TYPE_HASH);
37 __uint(max_entries, 10240);
38 __type(key, u64);
39 __type(value, struct args_t);
40 } start SEC(".maps");
41
42 struct {
43 __uint(type, BPF_MAP_TYPE_CGROUP_ARRAY);
44 __type(key, u32);
45 __type(value, u32);
46 __uint(max_entries, 1);
47 } cgroup_map SEC(".maps");
48
49 struct {
50 __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
51 __uint(key_size, sizeof(__u32));
52 __uint(value_size, sizeof(__u32));
53 } events SEC(".maps");
54
55 struct {
56 __uint(type, BPF_MAP_TYPE_STACK_TRACE);
57 __uint(key_size, sizeof(u32));
58 } stackmap SEC(".maps");
59
60 struct {
61 __uint(type, BPF_MAP_TYPE_HASH);
62 __type(key, struct key_t);
63 __type(value, struct cap_event);
64 __uint(max_entries, MAX_ENTRIES);
65 } info SEC(".maps");
66
67 struct {
68 __uint(type, BPF_MAP_TYPE_HASH);
69 __uint(max_entries, 10240);
70 __type(key, struct unique_key);
71 __type(value, u64);
72 } seen SEC(".maps");
73
74 SEC("kprobe/cap_capable")
BPF_KPROBE(kprobe__cap_capable_entry,const struct cred * cred,struct user_namespace * targ_ns,int cap,int cap_opt)75 int BPF_KPROBE(kprobe__cap_capable_entry, const struct cred *cred, struct user_namespace *targ_ns, int cap, int cap_opt)
76 {
77 __u32 pid;
78 __u64 pid_tgid;
79
80 if (filter_cg && !bpf_current_task_under_cgroup(&cgroup_map, 0))
81 return 0;
82
83 pid_tgid = bpf_get_current_pid_tgid();
84 pid = pid_tgid >> 32;
85
86 if (pid == my_pid)
87 return 0;
88
89 if (targ_pid != -1 && targ_pid != pid)
90 return 0;
91
92 struct args_t args = {};
93 args.cap = cap;
94 args.cap_opt = cap_opt;
95 bpf_map_update_elem(&start, &pid_tgid, &args, 0);
96
97 return 0;
98 }
99
100 SEC("kretprobe/cap_capable")
BPF_KRETPROBE(kprobe__cap_capable_exit)101 int BPF_KRETPROBE(kprobe__cap_capable_exit)
102 {
103 __u64 pid_tgid;
104 struct args_t *ap;
105 struct key_t i_key;
106
107 pid_tgid = bpf_get_current_pid_tgid();
108 ap = bpf_map_lookup_elem(&start, &pid_tgid);
109 if (!ap)
110 return 0; /* missed entry */
111
112 bpf_map_delete_elem(&start, &pid_tgid);
113
114 struct cap_event event = {};
115 event.pid = pid_tgid >> 32;
116 event.tgid = pid_tgid;
117 event.cap = ap->cap;
118 event.uid = bpf_get_current_uid_gid();
119 bpf_get_current_comm(&event.task, sizeof(event.task));
120 event.ret = PT_REGS_RC(ctx);
121
122 if (LINUX_KERNEL_VERSION >= KERNEL_VERSION(5, 1, 0)) {
123 /* @opts: Bitmask of options defined in include/linux/security.h */
124 event.audit = (ap->cap_opt & 0b10) == 0;
125 event.insetid = (ap->cap_opt & 0b100) != 0;
126 } else {
127 event.audit = ap->cap_opt;
128 event.insetid = -1;
129 }
130
131 if (unique_type) {
132 struct unique_key key = {.cap = ap->cap};
133 if (unique_type == UNQ_CGROUP)
134 key.cgroupid = bpf_get_current_cgroup_id();
135 else
136 key.tgid = pid_tgid;
137
138 if (bpf_map_lookup_elem(&seen, &key) != NULL)
139 return 0;
140
141 u64 zero = 0;
142 bpf_map_update_elem(&seen, &key, &zero, 0);
143 }
144
145 if (kernel_stack || user_stack) {
146 i_key.pid = pid_tgid >> 32;
147 i_key.tgid = pid_tgid;
148
149 i_key.kern_stack_id = i_key.user_stack_id = -1;
150 if (user_stack)
151 i_key.user_stack_id = bpf_get_stackid(ctx, &stackmap, BPF_F_USER_STACK);
152 if (kernel_stack)
153 i_key.kern_stack_id = bpf_get_stackid(ctx, &stackmap, 0);
154
155 bpf_map_update_elem(&info, &i_key, &event, BPF_NOEXIST);
156 }
157 bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event));
158
159 return 0;
160 }
161
162 char LICENSE[] SEC("license") = "GPL";
163