bcc
bcc copied to clipboard
Support libbpf-tools based capable tool
- BCC supports
capable.py
for tracing security capability checks- https://github.com/chethanah/bcc/blob/master/tools/capable.py
- We've added support for tracing security capability checks for libbpf-tools as well
$ cd libbpf-tools/
$ make capable
$ sudo ./capable
libbpf: elf: skipping relo section(12) .rel.data for section(3) .data
TIME UID PID COMM CAP NAME AUDIT
13:44:59 1000 1532 Xorg 21 CAP_SYS_ADMIN 1
13:44:59 1000 1667 gnome-shell 21 CAP_SYS_ADMIN 1
13:44:59 1000 1667 gnome-shell 21 CAP_SYS_ADMIN 1
- Supported cli options similar to
capable.py
- Kernel and user stack trace
- PID filtering
- Cgroup based filtering
- Print unique output for
or - Extra fields
Please let us know for any changes if required.
Thank you
Thank you @eiffel-fl @chenhengqi for feedback. I've addressed the review comments and update the changes. Kindly verify.
Rebased changes to latest.
Two minor nits I found on the ebpf program.
@chethanah Could you take a look at review comments when you have some time?
I've rebased to latest master changes. Also updated according to @mauriciovasquezbernal review comments.
Hi.
One of my coworker extended your tool to add a "VERDICT" field, i.e. this field indicates if the asked capability was allowed or denied:
TIME UID PID COMM CAP NAME AUDIT VERDICT
19:32:02 0 188693 bash 21 CAP_SYS_ADMIN 1 allow
19:32:02 0 188693 bash 21 CAP_SYS_ADMIN 0 deny
...
If you want to integrate this feature to your tool, here is the patch:
diff --git a/libbpf-tools/capable.bpf.c b/libbpf-tools/capable.bpf.c
index d0e9c617..86b66977 100644
--- a/libbpf-tools/capable.bpf.c
+++ b/libbpf-tools/capable.bpf.c
@@ -21,12 +21,25 @@ 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);
@@ -60,11 +73,10 @@ struct {
} seen SEC(".maps");
SEC("kprobe/cap_capable")
-int BPF_KPROBE(kprobe__cap_capable, const struct cred *cred, struct user_namespace *targ_ns, int cap, int cap_opt)
+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;
- struct key_t i_key;
if (filter_cg && !bpf_current_task_under_cgroup(&cgroup_map, 0))
return 0;
@@ -78,24 +90,47 @@ int BPF_KPROBE(kprobe__cap_capable, const struct cred *cred, struct user_namespa
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;
+ event.pid = pid_tgid >> 32;
event.tgid = pid_tgid;
- event.cap = cap;
+ 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 = (cap_opt & 0b10) == 0;
- event.insetid = (cap_opt & 0b100) != 0;
+ event.audit = (ap->cap_opt & 0b10) == 0;
+ event.insetid = (ap->cap_opt & 0b100) != 0;
} else {
- event.audit = cap_opt;
+ event.audit = ap->cap_opt;
event.insetid = -1;
}
if (unique_type) {
- struct unique_key key = {.cap = cap};
+ struct unique_key key = {.cap = ap->cap};
if (unique_type == UNQ_CGROUP)
key.cgroupid = bpf_get_current_cgroup_id();
else
@@ -109,7 +144,7 @@ int BPF_KPROBE(kprobe__cap_capable, const struct cred *cred, struct user_namespa
}
if (kernel_stack || user_stack) {
- i_key.pid = pid;
+ i_key.pid = pid_tgid >> 32;
i_key.tgid = pid_tgid;
i_key.kern_stack_id = i_key.user_stack_id = -1;
diff --git a/libbpf-tools/capable.c b/libbpf-tools/capable.c
index 5abc500a..df1a0e64 100644
--- a/libbpf-tools/capable.c
+++ b/libbpf-tools/capable.c
@@ -264,10 +264,14 @@ static void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
tm = localtime(&t);
strftime(ts, sizeof(ts), "%H:%M:%S", tm);
+ char *verdict = "deny";
+ if (!e->ret)
+ verdict = "allow";
+
if (env.extra_fields)
- printf("%-8s %-5d %-7d %-7d %-16s %-7d %-20s %-7d %-7d\n", ts, e->uid, e->pid, e->tgid, e->task, e->cap, cap_name[e->cap], e->audit, e->insetid);
+ printf("%-8s %-5d %-7d %-7d %-16s %-7d %-20s %-7d %-7s %-7d\n", ts, e->uid, e->pid, e->tgid, e->task, e->cap, cap_name[e->cap], e->audit, verdict, e->insetid);
else
- printf("%-8s %-5d %-7d %-16s %-7d %-20s %-7d\n", ts, e->uid, e->pid, e->task, e->cap, cap_name[e->cap], e->audit);
+ printf("%-8s %-5d %-7d %-16s %-7d %-20s %-7d %-7s\n", ts, e->uid, e->pid, e->task, e->cap, cap_name[e->cap], e->audit, verdict);
print_map(ksyms, syms_cache);
}
@@ -385,9 +389,9 @@ int main(int argc, char **argv)
}
if (env.extra_fields)
- printf("%-8s %-5s %-7s %-7s %-16s %-7s %-20s %-7s %-7s\n", "TIME", "UID", "PID", "TID", "COMM", "CAP", "NAME", "AUDIT", "INSETID");
+ printf("%-8s %-5s %-7s %-7s %-16s %-7s %-20s %-7s %-7s %-7s\n", "TIME", "UID", "PID", "TID", "COMM", "CAP", "NAME", "AUDIT", "VERDICT", "INSETID");
else
- printf("%-8s %-5s %-7s %-16s %-7s %-20s %-7s\n", "TIME", "UID", "PID", "COMM", "CAP", "NAME", "AUDIT");
+ printf("%-8s %-5s %-7s %-16s %-7s %-20s %-7s %-7s\n", "TIME", "UID", "PID", "COMM", "CAP", "NAME", "AUDIT", "VERDICT");
/* main: poll */
while (!exiting) {
diff --git a/libbpf-tools/capable.h b/libbpf-tools/capable.h
index 36eaf546..d93aa14a 100644
--- a/libbpf-tools/capable.h
+++ b/libbpf-tools/capable.h
@@ -14,6 +14,7 @@ struct cap_event {
uid_t uid;
int audit;
int insetid;
+ int ret;
char task[TASK_COMM_LEN];
};
Best regards.
Thank you @eiffel-fl . I'll integrate the changes and refresh the PR to latest HEAD.
Thank you @eiffel-fl . I'll integrate the changes and refresh the PR to latest HEAD.
You are welcome, ping me when you are ready to get review :D.