blob: 83bfd98dc61cecb8f2435a34c0c107dc57c49e9e [file] [log] [blame]
Brendan Gregg12989982016-09-14 08:15:09 -07001#!/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"),
Rafael F78948e42017-03-26 14:54:25 +0200151 event.uid, event.pid, event.comm.decode(), event.cap, name,
152 event.audit))
Brendan Gregg12989982016-09-14 08:15:09 -0700153
154# loop with callback to print_event
155b["events"].open_perf_buffer(print_event)
156while 1:
157 b.kprobe_poll()