Class: Vivarium::Daemon
- Inherits:
-
Object
- Object
- Vivarium::Daemon
- Defined in:
- lib/vivarium.rb
Constant Summary collapse
- BPF_PROGRAM_TEMPLATE =
<<~CLANG #include <linux/socket.h> #include <uapi/linux/in.h> #include <uapi/linux/in6.h> #include <uapi/linux/ip.h> #include <uapi/linux/udp.h> #ifndef SOCK_STREAM #define SOCK_STREAM 1 #endif #ifndef SOCK_DGRAM #define SOCK_DGRAM 2 #endif struct net; struct sock; struct sk_buff; struct task_struct; struct kernel_siginfo; struct cred; struct user_namespace; struct linux_binprm; struct path { void *mnt; void *dentry; }; struct file { char __off[__VIVARIUM_F_PATH_OFFSET__]; struct path f_path; }; struct qstr { union { struct { u64 hash_len; }; struct { u32 hash; u32 len; }; }; const unsigned char *name; }; struct dentry_base { char __pad[__VIVARIUM_DENTRY_D_NAME_OFFSET__]; struct qstr d_name; }; struct sockaddr_t { u16 sa_family; unsigned char sa_data[14]; }; struct sockaddr_in_t { u16 sin_family; u16 sin_port; u32 sin_addr; unsigned char pad[8]; }; struct sockaddr_in6_t { u16 sin6_family; u16 sin6_port; u32 sin6_flowinfo; unsigned char sin6_addr[16]; u32 sin6_scope_id; }; struct sockaddr_port_t { u16 family; u16 port; }; struct iovec_t { void *iov_base; unsigned long iov_len; }; struct user_msghdr_t { void *msg_name; int msg_namelen; struct iovec_t *msg_iov; unsigned long msg_iovlen; void *msg_control; unsigned long msg_controllen; unsigned int msg_flags; }; struct mmsghdr_t { struct user_msghdr_t msg_hdr; unsigned int msg_len; }; struct sk_buff_t { unsigned char *head; unsigned char *data; u32 len; u16 mac_header; u16 network_header; u16 transport_header; }; struct event_t { u64 ktime_ns; u32 pid; char event_name[16]; char payload[#{EVENT_PAYLOAD_SIZE}]; }; BPF_HASH(config_root_targets, u32, u8, 1024); BPF_HASH(config_spawned_targets, u32, u8, 8192); BPF_HASH(dns_connected_tids, u32, u8, 8192); BPF_ARRAY(event_invoked, struct event_t, #{EVENT_CAPACITY}); BPF_ARRAY(event_write_pos, u32, 1); static __always_inline int target_enabled(u32 pid, u32 tid) { u8 *enabled_root = config_root_targets.lookup(&pid); if (enabled_root && *enabled_root == 1) { return 1; } u8 *enabled_spawned = config_spawned_targets.lookup(&tid); if (enabled_spawned && *enabled_spawned == 1) { return 1; } return 0; } static __always_inline int monitored_capability(int cap) { switch (cap) { case 1: /* CAP_DAC_OVERRIDE */ case 2: /* CAP_DAC_READ_SEARCH */ case 6: /* CAP_SETGID */ case 7: /* CAP_SETUID */ case 12: /* CAP_NET_ADMIN */ case 16: /* CAP_SYS_MODULE */ case 17: /* CAP_SYS_RAWIO */ case 19: /* CAP_SYS_PTRACE */ case 21: /* CAP_SYS_ADMIN */ case 22: /* CAP_SYS_BOOT */ case 25: /* CAP_SYS_TIME */ case 38: /* CAP_PERFMON */ case 39: /* CAP_BPF */ case 40: /* CAP_CHECKPOINT_RESTORE */ return 1; default: return 0; } } static __always_inline void submit_event(struct event_t *ev) { u32 zero = 0; u32 *write_pos = event_write_pos.lookup(&zero); if (!write_pos) { return; } ev->ktime_ns = bpf_ktime_get_ns(); u32 idx = *write_pos % #{EVENT_CAPACITY}; __sync_fetch_and_add(write_pos, 1); event_invoked.update(&idx, ev); } static __always_inline int is_dns_destination(void *addr) { u16 family = 0; bpf_probe_read_user(&family, sizeof(family), addr); if (family == AF_INET) { struct sockaddr_in_t sin = {}; bpf_probe_read_user(&sin, sizeof(sin), addr); return sin.sin_port == __constant_htons(53); } if (family == AF_INET6) { struct sockaddr_in6_t sin6 = {}; bpf_probe_read_user(&sin6, sizeof(sin6), addr); return sin6.sin6_port == __constant_htons(53); } return 0; } static __always_inline void submit_dns_req(u32 pid, unsigned char *payload, unsigned int payload_len) { unsigned int copy_len = payload_len; if (copy_len <= 12) { return; } copy_len -= 12; if (copy_len > 64) { copy_len = 64; } struct event_t ev = {}; ev.pid = pid; __builtin_memcpy(ev.event_name, "dns_req", 8); bpf_probe_read_user(&ev.payload[0], copy_len, payload + 12); submit_event(&ev); } static __always_inline int read_dentry_name(struct dentry *dentry, char *buffer, size_t max) { struct dentry_base d = {}; struct qstr qname = {}; if (!dentry || !buffer) { return -1; } bpf_probe_read_kernel(&d, sizeof(d), (void *)dentry); if (!d.d_name.name) { return -1; } unsigned int len = d.d_name.len; if (len > max) { len = max; } bpf_probe_read_kernel_str(buffer, len + 1, (void *)d.d_name.name); return len; } TRACEPOINT_PROBE(sched, sched_process_fork) { u32 parent = args->parent_pid; u32 child = args->child_pid; u8 one = 1; u8 *enabled_root = config_root_targets.lookup(&parent); if (enabled_root && *enabled_root == 1) { config_spawned_targets.update(&child, &one); return 0; } u8 *enabled_spawned = config_spawned_targets.lookup(&parent); if (enabled_spawned && *enabled_spawned == 1) { config_spawned_targets.update(&child, &one); } return 0; } TRACEPOINT_PROBE(sched, sched_process_exit) { u32 tid = (u32)bpf_get_current_pid_tgid(); config_spawned_targets.delete(&tid); dns_connected_tids.delete(&tid); return 0; } LSM_PROBE(file_open, struct file *file) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; u32 tid = (u32)pid_tgid; bpf_trace_printk("vivarium: invoked pid=%d\\n", pid); if (!target_enabled(pid, tid)) { return 0; } struct event_t ev = {}; int path_ret; ev.pid = pid; __builtin_memcpy(ev.event_name, "path_open", 9); path_ret = bpf_d_path(&file->f_path, ev.payload, sizeof(ev.payload)); if (path_ret < 0) { if (ev.payload[0] == 0) { __builtin_memcpy(ev.payload, "<path_error>", 13); bpf_trace_printk("vivarium: failed to obtain full path. pid=%d path=%s\\n", pid, ev.payload); } } bpf_trace_printk("vivarium: pid=%d path=%s\\n", pid, ev.payload); submit_event(&ev); return 0; } LSM_PROBE(socket_create, int family, int type, int protocol, int kern) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; u32 tid = (u32)pid_tgid; if (!target_enabled(pid, tid)) { return 0; } if ((family == AF_INET || family == AF_INET6) && (type == SOCK_STREAM || type == SOCK_DGRAM)) { return 0; } struct event_t ev = {}; u16 family16 = family; u16 type16 = type; u16 proto16 = protocol; ev.pid = pid; __builtin_memcpy(ev.event_name, "odd_socket", 11); __builtin_memcpy(&ev.payload[0], &family16, sizeof(family16)); __builtin_memcpy(&ev.payload[2], &type16, sizeof(type16)); __builtin_memcpy(&ev.payload[4], &proto16, sizeof(proto16)); submit_event(&ev); return 0; } LSM_PROBE(socket_connect, struct socket *sock, struct sockaddr *address, int addrlen) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; u32 tid = (u32)pid_tgid; u16 family = 0; u8 one = 1; if (!target_enabled(pid, tid)) { return 0; } if (!address) { return 0; } bpf_probe_read_kernel(&family, sizeof(family), address); struct event_t ev = {}; ev.pid = pid; __builtin_memcpy(ev.event_name, "sock_connect", 13); __builtin_memcpy(&ev.payload[0], &family, sizeof(family)); if (family == AF_INET) { struct sockaddr_in_t sin = {}; bpf_probe_read_kernel(&sin, sizeof(sin), address); __builtin_memcpy(&ev.payload[2], &sin.sin_port, sizeof(sin.sin_port)); __builtin_memcpy(&ev.payload[4], &sin.sin_addr, sizeof(sin.sin_addr)); if (sin.sin_port == __constant_htons(53)) { dns_connected_tids.update(&tid, &one); } } else if (family == AF_INET6) { struct sockaddr_in6_t sin6 = {}; bpf_probe_read_kernel(&sin6, sizeof(sin6), address); __builtin_memcpy(&ev.payload[2], &sin6.sin6_port, sizeof(sin6.sin6_port)); __builtin_memcpy(&ev.payload[4], &sin6.sin6_addr, sizeof(sin6.sin6_addr)); if (sin6.sin6_port == __constant_htons(53)) { dns_connected_tids.update(&tid, &one); } } submit_event(&ev); return 0; } TRACEPOINT_PROBE(syscalls, sys_enter_sendmsg) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; u32 tid = (u32)pid_tgid; struct user_msghdr_t msg = {}; struct iovec_t iov = {}; if (!target_enabled(pid, tid)) { return 0; } if (!args->msg) { return 0; } bpf_probe_read_user(&msg, sizeof(msg), args->msg); if (!msg.msg_iov || msg.msg_iovlen == 0) { return 0; } if (msg.msg_name && !is_dns_destination(msg.msg_name)) { return 0; } bpf_probe_read_user(&iov, sizeof(iov), msg.msg_iov); if (!iov.iov_base) { return 0; } submit_dns_req(pid, (unsigned char *)iov.iov_base, (unsigned int)iov.iov_len); return 0; } TRACEPOINT_PROBE(syscalls, sys_enter_sendto) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; u32 tid = (u32)pid_tgid; unsigned char *buff = args->buff; int dns_match = 0; if (!target_enabled(pid, tid)) { return 0; } if (!buff) { return 0; } if (args->addr) { dns_match = is_dns_destination(args->addr); } else { u8 *connected = dns_connected_tids.lookup(&tid); dns_match = connected && *connected == 1; } if (!dns_match) { return 0; } submit_dns_req(pid, buff, args->len); dns_connected_tids.delete(&tid); return 0; } TRACEPOINT_PROBE(syscalls, sys_enter_sendmmsg) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; u32 tid = (u32)pid_tgid; struct mmsghdr_t mmsg = {}; struct iovec_t iov = {}; if (!target_enabled(pid, tid)) { return 0; } if (!args->mmsg) { return 0; } bpf_probe_read_user(&mmsg, sizeof(mmsg), args->mmsg); if (mmsg.msg_hdr.msg_name && !is_dns_destination(mmsg.msg_hdr.msg_name)) { return 0; } if (!mmsg.msg_hdr.msg_iov || mmsg.msg_hdr.msg_iovlen == 0) { return 0; } bpf_probe_read_user(&iov, sizeof(iov), mmsg.msg_hdr.msg_iov); if (!iov.iov_base) { return 0; } submit_dns_req(pid, (unsigned char *)iov.iov_base, (unsigned int)iov.iov_len); return 0; } TRACEPOINT_PROBE(syscalls, sys_enter_execve) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; u32 tid = (u32)pid_tgid; const char *argv0 = 0; const char *argv1 = 0; const char *argv2 = 0; if (!target_enabled(pid, tid)) { return 0; } if (!args->filename) { return 0; } struct event_t ev = {}; ev.pid = pid; __builtin_memcpy(ev.event_name, "proc_exec", 10); bpf_probe_read_user_str(&ev.payload[0], #{PROC_EXEC_SLOT_SIZE}, args->filename); if (args->argv) { bpf_probe_read_user(&argv0, sizeof(argv0), &args->argv[0]); bpf_probe_read_user(&argv1, sizeof(argv1), &args->argv[1]); bpf_probe_read_user(&argv2, sizeof(argv2), &args->argv[2]); if (argv0) { bpf_probe_read_user_str(&ev.payload[#{PROC_EXEC_SLOT_SIZE}], #{PROC_EXEC_SLOT_SIZE}, argv0); } if (argv1) { bpf_probe_read_user_str(&ev.payload[#{PROC_EXEC_SLOT_SIZE * 2}], #{PROC_EXEC_SLOT_SIZE}, argv1); } if (argv2) { bpf_probe_read_user_str(&ev.payload[#{PROC_EXEC_SLOT_SIZE * 3}], #{PROC_EXEC_SLOT_SIZE}, argv2); } } submit_event(&ev); return 0; } LSM_PROBE(ptrace_access_check, struct task_struct *child, unsigned int mode) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; u32 tid = (u32)pid_tgid; if (!target_enabled(pid, tid)) { return 0; } struct event_t ev = {}; u32 mode32 = mode; ev.pid = pid; __builtin_memcpy(ev.event_name, "ptrace_check", 13); __builtin_memcpy(&ev.payload[0], &mode32, sizeof(mode32)); submit_event(&ev); return 0; } LSM_PROBE(sb_mount, const char *dev_name, const struct path *path, const char *type, unsigned long flags, void *data) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; u32 tid = (u32)pid_tgid; if (!target_enabled(pid, tid)) { return 0; } struct event_t ev = {}; u64 flags64 = flags; ev.pid = pid; __builtin_memcpy(ev.event_name, "sb_mount", 9); __builtin_memcpy(&ev.payload[0], &flags64, sizeof(flags64)); if (dev_name) { bpf_probe_read_kernel_str(&ev.payload[8], 120, dev_name); } if (type) { bpf_probe_read_kernel_str(&ev.payload[128], 120, type); } submit_event(&ev); return 0; } LSM_PROBE(kernel_read_file, struct file *file, int id, int contents) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; u32 tid = (u32)pid_tgid; if (!target_enabled(pid, tid)) { return 0; } struct event_t ev = {}; u32 id32 = id; u32 contents32 = contents; ev.pid = pid; __builtin_memcpy(ev.event_name, "kernel_read_file", 16); __builtin_memcpy(&ev.payload[0], &id32, sizeof(id32)); __builtin_memcpy(&ev.payload[4], &contents32, sizeof(contents32)); submit_event(&ev); return 0; } LSM_PROBE(task_kill, struct task_struct *p, struct kernel_siginfo *info, int sig, const struct cred *cred) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; u32 tid = (u32)pid_tgid; if (!target_enabled(pid, tid)) { return 0; } struct event_t ev = {}; ev.pid = pid; __builtin_memcpy(ev.event_name, "task_kill", 10); __builtin_memcpy(&ev.payload[0], &sig, sizeof(sig)); submit_event(&ev); return 0; } LSM_PROBE(task_fix_setuid, struct cred *new, const struct cred *old, int flags) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; u32 tid = (u32)pid_tgid; if (!target_enabled(pid, tid)) { return 0; } struct event_t ev = {}; u32 flags32 = flags; ev.pid = pid; __builtin_memcpy(ev.event_name, "setid_change", 13); __builtin_memcpy(&ev.payload[0], &flags32, sizeof(flags32)); submit_event(&ev); return 0; } LSM_PROBE(capable, const struct cred *cred, struct user_namespace *targ_ns, int cap, unsigned int opts) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; u32 tid = (u32)pid_tgid; if (!target_enabled(pid, tid)) { return 0; } if (!monitored_capability(cap)) { return 0; } struct event_t ev = {}; u32 cap32 = cap; u32 opts32 = opts; ev.pid = pid; __builtin_memcpy(ev.event_name, "capable_check", 14); __builtin_memcpy(&ev.payload[0], &cap32, sizeof(cap32)); __builtin_memcpy(&ev.payload[4], &opts32, sizeof(opts32)); submit_event(&ev); return 0; } LSM_PROBE(bprm_creds_from_file, struct linux_binprm *bprm, struct file *file) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; u32 tid = (u32)pid_tgid; if (!target_enabled(pid, tid)) { return 0; } struct event_t ev = {}; u8 has_file = 0; ev.pid = pid; __builtin_memcpy(ev.event_name, "bprm_creds", 11); if (file) { has_file = 1; bpf_d_path(&file->f_path, &ev.payload[1], sizeof(ev.payload) - 1); } __builtin_memcpy(&ev.payload[0], &has_file, sizeof(has_file)); submit_event(&ev); return 0; } LSM_PROBE(inode_symlink, struct inode *dir, struct dentry *dentry, const char *oldname) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; u32 tid = (u32)pid_tgid; if (!target_enabled(pid, tid)) { return 0; } struct event_t ev = {}; ev.pid = pid; __builtin_memcpy(ev.event_name, "file_symlink", 13); if (oldname) { bpf_probe_read_user_str(&ev.payload[0], 128, oldname); } if (dentry) { read_dentry_name(dentry, &ev.payload[128], 128); } submit_event(&ev); return 0; } LSM_PROBE(inode_link, struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; u32 tid = (u32)pid_tgid; if (!target_enabled(pid, tid)) { return 0; } struct event_t ev = {}; ev.pid = pid; __builtin_memcpy(ev.event_name, "file_hardlink", 14); if (old_dentry) { read_dentry_name(old_dentry, &ev.payload[0], 128); } if (new_dentry) { read_dentry_name(new_dentry, &ev.payload[128], 128); } submit_event(&ev); return 0; } LSM_PROBE(inode_rename, struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; u32 tid = (u32)pid_tgid; if (!target_enabled(pid, tid)) { return 0; } struct event_t ev = {}; ev.pid = pid; __builtin_memcpy(ev.event_name, "file_rename", 12); if (old_dentry) { read_dentry_name(old_dentry, &ev.payload[0], 128); } if (new_dentry) { read_dentry_name(new_dentry, &ev.payload[128], 128); } submit_event(&ev); return 0; } LSM_PROBE(path_chmod, struct path *path, umode_t mode) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; u32 tid = (u32)pid_tgid; if (!target_enabled(pid, tid)) { return 0; } if (!path) { return 0; } struct event_t ev = {}; u16 mode_short = mode & 0xFFFF; ev.pid = pid; __builtin_memcpy(ev.event_name, "file_chmod", 11); __builtin_memcpy(&ev.payload[0], &mode_short, sizeof(mode_short)); bpf_d_path(path, &ev.payload[2], sizeof(ev.payload) - 2); submit_event(&ev); return 0; } TRACEPOINT_PROBE(syscalls, sys_enter_getdents64) { u64 pid_tgid = bpf_get_current_pid_tgid(); u32 pid = pid_tgid >> 32; u32 tid = (u32)pid_tgid; if (!target_enabled(pid, tid)) { return 0; } struct event_t ev = {}; u32 fd = args->fd; u32 count = args->count; ev.pid = pid; __builtin_memcpy(ev.event_name, "file_getdents", 14); __builtin_memcpy(&ev.payload[0], &fd, sizeof(fd)); __builtin_memcpy(&ev.payload[4], &count, sizeof(count)); submit_event(&ev); return 0; } CLANG
Instance Method Summary collapse
-
#initialize(pin_dir: Vivarium.bpf_pin_dir) ⇒ Daemon
constructor
A new instance of Daemon.
- #run ⇒ Object
Constructor Details
#initialize(pin_dir: Vivarium.bpf_pin_dir) ⇒ Daemon
Returns a new instance of Daemon.
1267 1268 1269 |
# File 'lib/vivarium.rb', line 1267 def initialize(pin_dir: Vivarium.bpf_pin_dir) @pin_dir = pin_dir end |
Instance Method Details
#run ⇒ Object
1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 |
# File 'lib/vivarium.rb', line 1271 def run ensure_root! FileUtils.mkdir_p(@pin_dir) f_path_offset = detect_f_path_offset d_name_offset = detect_dentry_d_name_offset program = BPF_PROGRAM_TEMPLATE .gsub("__VIVARIUM_F_PATH_OFFSET__", f_path_offset.to_s) .gsub("__VIVARIUM_DENTRY_D_NAME_OFFSET__", d_name_offset.to_s) bpf = RbBCC::BCC.new(text: program) kprint_thread = start_kprint_logger(bpf) config_root_targets = bpf["config_root_targets"] config_spawned_targets = bpf["config_spawned_targets"] event_invoked = bpf["event_invoked"] event_write_pos = bpf["event_write_pos"] clear_event_slots(event_invoked) event_write_pos[0] = 0 config_spawned_targets.clear pin_map(config_root_targets, File.join(@pin_dir, "config_root_targets")) pin_map(config_spawned_targets, File.join(@pin_dir, "config_spawned_targets")) pin_map(event_invoked, File.join(@pin_dir, "event_invoked")) pin_map(event_write_pos, File.join(@pin_dir, "event_write_pos")) puts "[vivariumd] started" puts "[vivariumd] pinned maps in #{@pin_dir}" puts "[vivariumd] watching LSM file_open (f_path offset=#{f_path_offset})" puts "[vivariumd] kprint logger enabled" loop do sleep 1 end rescue Interrupt puts "\n[vivariumd] stopping" ensure if kprint_thread kprint_thread.kill kprint_thread.join(0.2) end end |