1#!/usr/bin/env 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] [-K] [-U] 8# 9# Copyright 2016 Netflix, Inc. 10# Licensed under the Apache License, Version 2.0 (the "License") 11# 12# 13-Sep-2016 Brendan Gregg Created this. 13 14from __future__ import print_function 15from os import getpid 16from functools import partial 17from bcc import BPF 18from bcc.containers import filter_by_containers 19import errno 20import argparse 21from time import strftime 22 23# arguments 24examples = """examples: 25 ./capable # trace capability checks 26 ./capable -v # verbose: include non-audit checks 27 ./capable -p 181 # only trace PID 181 28 ./capable -K # add kernel stacks to trace 29 ./capable -U # add user-space stacks to trace 30 ./capable -x # extra fields: show TID and INSETID columns 31 ./capable --unique # don't repeat stacks for the same pid or cgroup 32 ./capable --cgroupmap mappath # only trace cgroups in this BPF map 33 ./capable --mntnsmap mappath # only trace mount namespaces in the map 34""" 35parser = argparse.ArgumentParser( 36 description="Trace security capability checks", 37 formatter_class=argparse.RawDescriptionHelpFormatter, 38 epilog=examples) 39parser.add_argument("-v", "--verbose", action="store_true", 40 help="include non-audit checks") 41parser.add_argument("-p", "--pid", 42 help="trace this PID only") 43parser.add_argument("-K", "--kernel-stack", action="store_true", 44 help="output kernel stack trace") 45parser.add_argument("-U", "--user-stack", action="store_true", 46 help="output user stack trace") 47parser.add_argument("-x", "--extra", action="store_true", 48 help="show extra fields in TID and INSETID columns") 49parser.add_argument("--cgroupmap", 50 help="trace cgroups in this BPF map only") 51parser.add_argument("--mntnsmap", 52 help="trace mount namespaces in this BPF map only") 53parser.add_argument("--unique", action="store_true", 54 help="don't repeat stacks for the same pid or cgroup") 55args = parser.parse_args() 56debug = 0 57 58# capabilities to names, generated from (and will need updating): 59# awk '/^#define.CAP_.*[0-9]$/ { print " " $3 ": \"" $2 "\"," }' \ 60# include/uapi/linux/capability.h 61capabilities = { 62 0: "CAP_CHOWN", 63 1: "CAP_DAC_OVERRIDE", 64 2: "CAP_DAC_READ_SEARCH", 65 3: "CAP_FOWNER", 66 4: "CAP_FSETID", 67 5: "CAP_KILL", 68 6: "CAP_SETGID", 69 7: "CAP_SETUID", 70 8: "CAP_SETPCAP", 71 9: "CAP_LINUX_IMMUTABLE", 72 10: "CAP_NET_BIND_SERVICE", 73 11: "CAP_NET_BROADCAST", 74 12: "CAP_NET_ADMIN", 75 13: "CAP_NET_RAW", 76 14: "CAP_IPC_LOCK", 77 15: "CAP_IPC_OWNER", 78 16: "CAP_SYS_MODULE", 79 17: "CAP_SYS_RAWIO", 80 18: "CAP_SYS_CHROOT", 81 19: "CAP_SYS_PTRACE", 82 20: "CAP_SYS_PACCT", 83 21: "CAP_SYS_ADMIN", 84 22: "CAP_SYS_BOOT", 85 23: "CAP_SYS_NICE", 86 24: "CAP_SYS_RESOURCE", 87 25: "CAP_SYS_TIME", 88 26: "CAP_SYS_TTY_CONFIG", 89 27: "CAP_MKNOD", 90 28: "CAP_LEASE", 91 29: "CAP_AUDIT_WRITE", 92 30: "CAP_AUDIT_CONTROL", 93 31: "CAP_SETFCAP", 94 32: "CAP_MAC_OVERRIDE", 95 33: "CAP_MAC_ADMIN", 96 34: "CAP_SYSLOG", 97 35: "CAP_WAKE_ALARM", 98 36: "CAP_BLOCK_SUSPEND", 99 37: "CAP_AUDIT_READ", 100 38: "CAP_PERFMON", 101 39: "CAP_BPF", 102 40: "CAP_CHECKPOINT_RESTORE", 103} 104 105class Enum(set): 106 def __getattr__(self, name): 107 if name in self: 108 return name 109 raise AttributeError 110 111# Stack trace types 112StackType = Enum(("Kernel", "User",)) 113 114# define BPF program 115bpf_text = """ 116#include <uapi/linux/ptrace.h> 117#include <linux/sched.h> 118#include <linux/security.h> 119 120struct data_t { 121 u32 tgid; 122 u32 pid; 123 u32 uid; 124 int cap; 125 int audit; 126 int insetid; 127 char comm[TASK_COMM_LEN]; 128#ifdef KERNEL_STACKS 129 int kernel_stack_id; 130#endif 131#ifdef USER_STACKS 132 int user_stack_id; 133#endif 134}; 135 136BPF_PERF_OUTPUT(events); 137 138#if UNIQUESET 139struct repeat_t { 140 int cap; 141 u32 tgid; 142#if CGROUPSET 143 u64 cgroupid; 144#endif 145#ifdef KERNEL_STACKS 146 int kernel_stack_id; 147#endif 148#ifdef USER_STACKS 149 int user_stack_id; 150#endif 151}; 152BPF_HASH(seen, struct repeat_t, u64); 153#endif 154 155#if defined(USER_STACKS) || defined(KERNEL_STACKS) 156BPF_STACK_TRACE(stacks, 2048); 157#endif 158 159int kprobe__cap_capable(struct pt_regs *ctx, const struct cred *cred, 160 struct user_namespace *targ_ns, int cap, int cap_opt) 161{ 162 u64 __pid_tgid = bpf_get_current_pid_tgid(); 163 u32 tgid = __pid_tgid >> 32; 164 u32 pid = __pid_tgid; 165 int audit; 166 int insetid; 167 168 #ifdef CAP_OPT_NONE 169 audit = (cap_opt & 0b10) == 0; 170 insetid = (cap_opt & 0b100) != 0; 171 #else 172 audit = cap_opt; 173 insetid = -1; 174 #endif 175 176 FILTER1 177 FILTER2 178 FILTER3 179 180 if (container_should_be_filtered()) { 181 return 0; 182 } 183 184 u32 uid = bpf_get_current_uid_gid(); 185 186 struct data_t data = {}; 187 188 data.tgid = tgid; 189 data.pid = pid; 190 data.uid = uid; 191 data.cap = cap; 192 data.audit = audit; 193 data.insetid = insetid; 194#ifdef KERNEL_STACKS 195 data.kernel_stack_id = stacks.get_stackid(ctx, 0); 196#endif 197#ifdef USER_STACKS 198 data.user_stack_id = stacks.get_stackid(ctx, BPF_F_USER_STACK); 199#endif 200 201#if UNIQUESET 202 struct repeat_t repeat = {0,}; 203 repeat.cap = cap; 204#if CGROUP_ID_SET 205 repeat.cgroupid = bpf_get_current_cgroup_id(); 206#else 207 repeat.tgid = tgid; 208#endif 209#ifdef KERNEL_STACKS 210 repeat.kernel_stack_id = data.kernel_stack_id; 211#endif 212#ifdef USER_STACKS 213 repeat.user_stack_id = data.user_stack_id; 214#endif 215 if (seen.lookup(&repeat) != NULL) { 216 return 0; 217 } 218 u64 zero = 0; 219 seen.update(&repeat, &zero); 220#endif 221 222 bpf_get_current_comm(&data.comm, sizeof(data.comm)); 223 events.perf_submit(ctx, &data, sizeof(data)); 224 225 return 0; 226}; 227""" 228if args.pid: 229 bpf_text = bpf_text.replace('FILTER1', 230 'if (pid != %s) { return 0; }' % args.pid) 231if not args.verbose: 232 bpf_text = bpf_text.replace('FILTER2', 'if (audit == 0) { return 0; }') 233if args.kernel_stack: 234 bpf_text = "#define KERNEL_STACKS\n" + bpf_text 235if args.user_stack: 236 bpf_text = "#define USER_STACKS\n" + bpf_text 237bpf_text = bpf_text.replace('FILTER1', '') 238bpf_text = bpf_text.replace('FILTER2', '') 239bpf_text = bpf_text.replace('FILTER3', 240 'if (pid == %s) { return 0; }' % getpid()) 241bpf_text = filter_by_containers(args) + bpf_text 242if args.unique: 243 bpf_text = bpf_text.replace('UNIQUESET', '1') 244else: 245 bpf_text = bpf_text.replace('UNIQUESET', '0') 246if debug: 247 print(bpf_text) 248 249# initialize BPF 250b = BPF(text=bpf_text) 251 252# header 253if args.extra: 254 print("%-9s %-6s %-6s %-6s %-16s %-4s %-20s %-6s %s" % ( 255 "TIME", "UID", "PID", "TID", "COMM", "CAP", "NAME", "AUDIT", "INSETID")) 256else: 257 print("%-9s %-6s %-6s %-16s %-4s %-20s %-6s" % ( 258 "TIME", "UID", "PID", "COMM", "CAP", "NAME", "AUDIT")) 259 260def stack_id_err(stack_id): 261 # -EFAULT in get_stackid normally means the stack-trace is not available, 262 # Such as getting kernel stack trace in userspace code 263 return (stack_id < 0) and (stack_id != -errno.EFAULT) 264 265def print_stack(bpf, stack_id, stack_type, tgid): 266 if stack_id_err(stack_id): 267 print(" [Missed %s Stack]" % stack_type) 268 return 269 stack = list(bpf.get_table("stacks").walk(stack_id)) 270 for addr in stack: 271 print(" ", end="") 272 print("%s" % (bpf.sym(addr, tgid, show_module=True, show_offset=True))) 273 274# process event 275def print_event(bpf, cpu, data, size): 276 event = b["events"].event(data) 277 278 if event.cap in capabilities: 279 name = capabilities[event.cap] 280 else: 281 name = "?" 282 if args.extra: 283 print("%-9s %-6d %-6d %-6d %-16s %-4d %-20s %-6d %s" % (strftime("%H:%M:%S"), 284 event.uid, event.pid, event.tgid, event.comm.decode('utf-8', 'replace'), 285 event.cap, name, event.audit, str(event.insetid) if event.insetid != -1 else "N/A")) 286 else: 287 print("%-9s %-6d %-6d %-16s %-4d %-20s %-6d" % (strftime("%H:%M:%S"), 288 event.uid, event.pid, event.comm.decode('utf-8', 'replace'), 289 event.cap, name, event.audit)) 290 if args.kernel_stack: 291 print_stack(bpf, event.kernel_stack_id, StackType.Kernel, -1) 292 if args.user_stack: 293 print_stack(bpf, event.user_stack_id, StackType.User, event.tgid) 294 295# loop with callback to print_event 296callback = partial(print_event, b) 297b["events"].open_perf_buffer(callback) 298while 1: 299 try: 300 b.perf_buffer_poll() 301 except KeyboardInterrupt: 302 exit() 303