bcc icon indicating copy to clipboard operation
bcc copied to clipboard

Support libbpf-tools based capable tool

Open chethanah opened this issue 2 years ago • 4 comments

  • 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

chethanah avatar Mar 03 '22 08:03 chethanah

Thank you @eiffel-fl @chenhengqi for feedback. I've addressed the review comments and update the changes. Kindly verify.

chethanah avatar Mar 04 '22 03:03 chethanah

Rebased changes to latest.

chethanah avatar May 02 '22 07:05 chethanah

Two minor nits I found on the ebpf program.

@chethanah Could you take a look at review comments when you have some time?

KentaTada avatar Jun 20 '22 03:06 KentaTada

I've rebased to latest master changes. Also updated according to @mauriciovasquezbernal review comments.

chethanah avatar Jun 20 '22 12:06 chethanah

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.

eiffel-fl avatar Dec 01 '22 18:12 eiffel-fl

Thank you @eiffel-fl . I'll integrate the changes and refresh the PR to latest HEAD.

chethanah avatar Dec 02 '22 02:12 chethanah

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.

eiffel-fl avatar Dec 02 '22 09:12 eiffel-fl