• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python
2# @lint-avoid-python-3-compatibility-imports
3#
4# capable   Trace security capabilitiy checks (cap_capable()).
5#           For Linux, uses BCC, eBPF. Embedded C.
6#
7# USAGE: capable [-h] [-v] [-p PID]
8#
9# ToDo: add -s for kernel stacks.
10#
11# Copyright 2016 Netflix, Inc.
12# Licensed under the Apache License, Version 2.0 (the "License")
13#
14# 13-Sep-2016   Brendan Gregg   Created this.
15
16from __future__ import print_function
17from bcc import BPF
18import argparse
19from time import strftime
20import ctypes as ct
21
22# arguments
23examples = """examples:
24    ./capable             # trace capability checks
25    ./capable -v          # verbose: include non-audit checks
26    ./capable -p 181      # only trace PID 181
27"""
28parser = argparse.ArgumentParser(
29    description="Trace security capability checks",
30    formatter_class=argparse.RawDescriptionHelpFormatter,
31    epilog=examples)
32parser.add_argument("-v", "--verbose", action="store_true",
33    help="include non-audit checks")
34parser.add_argument("-p", "--pid",
35    help="trace this PID only")
36args = parser.parse_args()
37debug = 0
38
39# capabilities to names, generated from (and will need updating):
40# awk '/^#define.CAP_.*[0-9]$/ { print "    " $3 ": \"" $2 "\"," }' \
41#     include/uapi/linux/capability.h
42capabilities = {
43    0: "CAP_CHOWN",
44    1: "CAP_DAC_OVERRIDE",
45    2: "CAP_DAC_READ_SEARCH",
46    3: "CAP_FOWNER",
47    4: "CAP_FSETID",
48    5: "CAP_KILL",
49    6: "CAP_SETGID",
50    7: "CAP_SETUID",
51    8: "CAP_SETPCAP",
52    9: "CAP_LINUX_IMMUTABLE",
53    10: "CAP_NET_BIND_SERVICE",
54    11: "CAP_NET_BROADCAST",
55    12: "CAP_NET_ADMIN",
56    13: "CAP_NET_RAW",
57    14: "CAP_IPC_LOCK",
58    15: "CAP_IPC_OWNER",
59    16: "CAP_SYS_MODULE",
60    17: "CAP_SYS_RAWIO",
61    18: "CAP_SYS_CHROOT",
62    19: "CAP_SYS_PTRACE",
63    20: "CAP_SYS_PACCT",
64    21: "CAP_SYS_ADMIN",
65    22: "CAP_SYS_BOOT",
66    23: "CAP_SYS_NICE",
67    24: "CAP_SYS_RESOURCE",
68    25: "CAP_SYS_TIME",
69    26: "CAP_SYS_TTY_CONFIG",
70    27: "CAP_MKNOD",
71    28: "CAP_LEASE",
72    29: "CAP_AUDIT_WRITE",
73    30: "CAP_AUDIT_CONTROL",
74    31: "CAP_SETFCAP",
75    32: "CAP_MAC_OVERRIDE",
76    33: "CAP_MAC_ADMIN",
77    34: "CAP_SYSLOG",
78    35: "CAP_WAKE_ALARM",
79    36: "CAP_BLOCK_SUSPEND",
80    37: "CAP_AUDIT_READ",
81}
82
83# define BPF program
84bpf_text = """
85#include <uapi/linux/ptrace.h>
86#include <linux/sched.h>
87
88struct data_t {
89   // switch to u32s when supported
90   u64 pid;
91   u64 uid;
92   int cap;
93   int audit;
94   char comm[TASK_COMM_LEN];
95};
96
97BPF_PERF_OUTPUT(events);
98
99int kprobe__cap_capable(struct pt_regs *ctx, const struct cred *cred,
100    struct user_namespace *targ_ns, int cap, int audit)
101{
102    u32 pid = bpf_get_current_pid_tgid();
103    FILTER1
104    FILTER2
105
106    u32 uid = bpf_get_current_uid_gid();
107    struct data_t data = {.pid = pid, .uid = uid, .cap = cap, .audit = audit};
108    bpf_get_current_comm(&data.comm, sizeof(data.comm));
109    events.perf_submit(ctx, &data, sizeof(data));
110
111    return 0;
112};
113"""
114if args.pid:
115    bpf_text = bpf_text.replace('FILTER1',
116        'if (pid != %s) { return 0; }' % args.pid)
117if not args.verbose:
118    bpf_text = bpf_text.replace('FILTER2', 'if (audit == 0) { return 0; }')
119bpf_text = bpf_text.replace('FILTER1', '')
120bpf_text = bpf_text.replace('FILTER2', '')
121if debug:
122    print(bpf_text)
123
124# initialize BPF
125b = BPF(text=bpf_text)
126
127TASK_COMM_LEN = 16    # linux/sched.h
128
129class Data(ct.Structure):
130    _fields_ = [
131        ("pid", ct.c_ulonglong),
132        ("uid", ct.c_ulonglong),
133        ("cap", ct.c_int),
134        ("audit", ct.c_int),
135        ("comm", ct.c_char * TASK_COMM_LEN)
136    ]
137
138# header
139print("%-9s %-6s %-6s %-16s %-4s %-20s %s" % (
140    "TIME", "UID", "PID", "COMM", "CAP", "NAME", "AUDIT"))
141
142# process event
143def print_event(cpu, data, size):
144    event = ct.cast(data, ct.POINTER(Data)).contents
145
146    if event.cap in capabilities:
147        name = capabilities[event.cap]
148    else:
149        name = "?"
150    print("%-9s %-6d %-6d %-16s %-4d %-20s %d" % (strftime("%H:%M:%S"),
151        event.uid, event.pid, event.comm.decode('utf-8', 'replace'),
152        event.cap, name, event.audit))
153
154# loop with callback to print_event
155b["events"].open_perf_buffer(print_event)
156while 1:
157    b.perf_buffer_poll()
158