mirror of
https://github.com/evilsocket/opensnitch.git
synced 2025-03-04 00:24:40 +01:00
ebpf: support for long paths
Added support to report absolute path to a binary up to 4096 characters, defined here: https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/limits.h#L13
This commit is contained in:
parent
f54eb789ff
commit
e7024e3fe0
8 changed files with 148 additions and 118 deletions
|
@ -16,8 +16,8 @@ func NewExecEvent(pid, ppid, uid uint64, path string, comm [16]byte) *execEvent
|
|||
UID: uid,
|
||||
Comm: comm,
|
||||
}
|
||||
length := 128
|
||||
if len(path) < 128 {
|
||||
length := MaxPathLen
|
||||
if len(path) < MaxPathLen {
|
||||
length = len(path)
|
||||
}
|
||||
copy(ev.Filename[:], path[:length])
|
||||
|
|
|
@ -10,13 +10,20 @@ import (
|
|||
elf "github.com/iovisor/gobpf/elf"
|
||||
)
|
||||
|
||||
// MaxPathLen defines the maximum length of a path, as defined by the kernel:
|
||||
// https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/limits.h#L13
|
||||
const MaxPathLen = 4096
|
||||
|
||||
// TaskCommLen is the maximum num of characters of the comm field
|
||||
const TaskCommLen = 16
|
||||
|
||||
type execEvent struct {
|
||||
Type uint64
|
||||
PID uint64
|
||||
PPID uint64
|
||||
UID uint64
|
||||
Filename [128]byte
|
||||
Comm [16]byte
|
||||
Filename [MaxPathLen]byte
|
||||
Comm [TaskCommLen]byte
|
||||
}
|
||||
|
||||
// Struct that holds the metadata of a connection.
|
||||
|
@ -26,7 +33,7 @@ type networkEventT struct {
|
|||
Pid uint64
|
||||
UID uint64
|
||||
Counter uint64
|
||||
Comm [16]byte
|
||||
Comm [TaskCommLen]byte
|
||||
}
|
||||
|
||||
// List of supported events
|
||||
|
@ -57,8 +64,9 @@ func initEventsStreamer() {
|
|||
|
||||
tracepoints := []string{
|
||||
"tracepoint/sched/sched_process_exit",
|
||||
// "tracepoint/sched/sched_process_exec",
|
||||
// "tracepoint/sched/sched_process_fork",
|
||||
//"tracepoint/sched/sched_process_exec",
|
||||
//"tracepoint/syscalls/sys_enter_execve",
|
||||
//"tracepoint/sched/sched_process_fork",
|
||||
}
|
||||
|
||||
// Enable tracepoints first, that way if kprobes fail loading we'll still have some
|
||||
|
@ -130,7 +138,9 @@ func streamEventsWorker(id int, chn chan []byte, execEvents *eventsStore) {
|
|||
|
||||
case EV_TYPE_SCHED_EXIT:
|
||||
//log.Warning("::: EXIT EVENT -> %d", event.PID)
|
||||
execEvents.delete(event.PID)
|
||||
if _, found := execEvents.isInStore(event.PID); found {
|
||||
execEvents.delete(event.PID)
|
||||
}
|
||||
continue
|
||||
}
|
||||
// TODO: delete old events (by timeout)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package ebpf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
|
@ -131,13 +130,13 @@ func getPidFromEbpf(proto string, srcPort uint, srcIP net.IP, dstIP net.IP, dstP
|
|||
return nil
|
||||
}
|
||||
|
||||
comm := string(bytes.Trim(value.Comm[:], "\x00"))
|
||||
comm := byteArrayToString(value.Comm[:])
|
||||
proc = procmon.NewProcess(int(value.Pid), comm)
|
||||
// use socket's UID. A process may have dropped privileges
|
||||
proc.UID = int(value.UID)
|
||||
|
||||
if ev, found := execEvents.isInStore(value.Pid); found {
|
||||
proc.Path = string(bytes.Trim(ev.Event.Filename[:], "\x00")) // ev.Proc.Path
|
||||
proc.Path = byteArrayToString(ev.Event.Filename[:]) // ev.Proc.Path
|
||||
proc.ReadCmdline()
|
||||
proc.ReadCwd()
|
||||
proc.ReadEnv()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ebpf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
|
@ -21,6 +22,21 @@ func mountDebugFS() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Trim null characters, and return the left part of the byte array.
|
||||
// NOTE: using BPF_MAP_TYPE_PERCPU_ARRAY does not initialize strings to 0,
|
||||
// so we end up receiving events as follow:
|
||||
// event.filename -> /usr/bin/iptables
|
||||
// event.filename -> /bin/lsn/iptables (should be /bin/ls)
|
||||
// It turns out, that there's a 0x00 character between "/bin/ls" and "n/iptables":
|
||||
// [47 115 98 105 110 47 100 117 109 112 101 50 102 115 0 0 101 115
|
||||
// ^^^
|
||||
// TODO: investigate if there's any way of initializing the struct to 0
|
||||
// like using __builtin_memset() (can't be used with PERCPU apparently)
|
||||
func byteArrayToString(arr []byte) string {
|
||||
temp := bytes.SplitAfter(arr, []byte("\x00"))[0]
|
||||
return string(bytes.Trim(temp[:], "\x00"))
|
||||
}
|
||||
|
||||
func deleteEbpfEntry(proto string, key unsafe.Pointer) bool {
|
||||
if err := m.DeleteElement(ebpfMaps[proto].bpfmap, key); err != nil {
|
||||
return false
|
||||
|
|
|
@ -5,7 +5,7 @@ cd opensnitch
|
|||
wget https://github.com/torvalds/linux/archive/v5.8.tar.gz
|
||||
tar -xf v5.8.tar.gz
|
||||
patch linux-5.8/tools/lib/bpf/bpf_helpers.h < ebpf_prog/file.patch
|
||||
cp ebpf_prog/opensnitch*.c ebpf_prog/Makefile linux-5.8/samples/bpf
|
||||
cp ebpf_prog/opensnitch*.c ebpf_prog/common.h ebpf_prog/Makefile linux-5.8/samples/bpf
|
||||
cd linux-5.8 && yes "" | make oldconfig && make prepare && make headers_install # (1 min)
|
||||
cd samples/bpf && make
|
||||
objdump -h opensnitch.o #you should see many section, number 1 should be called kprobe/tcp_v4_connect
|
||||
|
@ -27,3 +27,12 @@ CONFIG_BPF_SYSCALL=y
|
|||
CONFIG_BPF_EVENTS=y
|
||||
CONFIG_KPROBES=y
|
||||
CONFIG_KPROBE_EVENTS=y
|
||||
|
||||
Also, in some distributions debugfs is not mounted automatically, so you need
|
||||
to do it manually:
|
||||
|
||||
$ sudo mount -t debugfs none /sys/kernel/debug
|
||||
|
||||
In order to make it permanent add it to /etc/fstab:
|
||||
|
||||
debugfs /sys/kernel/debug debugfs defaults 0 0
|
||||
|
|
82
ebpf_prog/common.h
Normal file
82
ebpf_prog/common.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
#ifndef OPENSNITCH_COMMON_H
|
||||
#define OPENSNITCH_COMMON_H
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <uapi/linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
|
||||
//https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/limits.h#L13
|
||||
#ifndef MAX_PATH_LEN
|
||||
#define MAX_PATH_LEN 4096
|
||||
#endif
|
||||
|
||||
#define MAPSIZE 12000
|
||||
|
||||
#ifndef TASK_COMM_LEN
|
||||
#define TASK_COMM_LEN 16
|
||||
#endif
|
||||
|
||||
#define BUF_SIZE_MAP_NS 256
|
||||
#define GLOBAL_MAP_NS "256"
|
||||
|
||||
//-------------------------------map definitions
|
||||
// which github.com/iovisor/gobpf/elf expects
|
||||
|
||||
typedef struct bpf_map_def {
|
||||
unsigned int type;
|
||||
unsigned int key_size;
|
||||
unsigned int value_size;
|
||||
unsigned int max_entries;
|
||||
unsigned int map_flags;
|
||||
unsigned int pinning;
|
||||
char namespace[BUF_SIZE_MAP_NS];
|
||||
} bpf_map_def;
|
||||
|
||||
enum bpf_pin_type {
|
||||
PIN_NONE = 0,
|
||||
PIN_OBJECT_NS,
|
||||
PIN_GLOBAL_NS,
|
||||
PIN_CUSTOM_NS,
|
||||
};
|
||||
|
||||
//-----------------------------------
|
||||
|
||||
// even though we only need 32 bits of pid, on x86_32 ebpf verifier complained when pid type was set to u32
|
||||
typedef u64 pid_size_t;
|
||||
typedef u64 uid_size_t;
|
||||
|
||||
enum events_type {
|
||||
EVENT_NONE = 0,
|
||||
EVENT_EXEC,
|
||||
EVENT_FORK,
|
||||
EVENT_SCHED_EXEC,
|
||||
EVENT_SCHED_EXIT,
|
||||
EVENT_BYTES_SENT,
|
||||
EVENTS_BYTES_RECV
|
||||
};
|
||||
|
||||
struct data_t {
|
||||
u64 type;
|
||||
u64 pid; // PID as in the userspace term (i.e. task->tgid in kernel)
|
||||
u64 ppid; // Parent PID as in the userspace term (i.e task->real_parent->tgid in kernel)
|
||||
u64 uid;
|
||||
//u64 bytes_sent;
|
||||
//u64 bytes_recv;
|
||||
char filename[MAX_PATH_LEN];
|
||||
char comm[TASK_COMM_LEN];
|
||||
}__attribute__((packed));
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// maps
|
||||
|
||||
struct bpf_map_def SEC("maps/heapstore") heapstore = {
|
||||
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
|
||||
.key_size = sizeof(u32),
|
||||
.value_size = sizeof(struct data_t),
|
||||
.max_entries = 1
|
||||
};
|
||||
|
||||
#endif
|
|
@ -3,63 +3,7 @@
|
|||
//uncomment if building on x86_32
|
||||
//#define OPENSNITCH_x86_32
|
||||
|
||||
#include <linux/ptrace.h>
|
||||
#include <uapi/linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
|
||||
#define ARGSIZE 128
|
||||
|
||||
#ifndef TASK_COMM_LEN
|
||||
#define TASK_COMM_LEN 16
|
||||
#endif
|
||||
|
||||
|
||||
// even though we only need 32 bits of pid, on x86_32 ebpf verifier complained when pid type was set to u32
|
||||
typedef u64 pid_size_t;
|
||||
typedef u64 uid_size_t;
|
||||
|
||||
|
||||
//-------------------------------map definitions
|
||||
// which github.com/iovisor/gobpf/elf expects
|
||||
#define BUF_SIZE_MAP_NS 256
|
||||
|
||||
typedef struct bpf_map_def {
|
||||
unsigned int type;
|
||||
unsigned int key_size;
|
||||
unsigned int value_size;
|
||||
unsigned int max_entries;
|
||||
unsigned int map_flags;
|
||||
unsigned int pinning;
|
||||
char namespace[BUF_SIZE_MAP_NS];
|
||||
} bpf_map_def;
|
||||
|
||||
enum bpf_pin_type {
|
||||
PIN_NONE = 0,
|
||||
PIN_OBJECT_NS,
|
||||
PIN_GLOBAL_NS,
|
||||
PIN_CUSTOM_NS,
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
enum events_type {
|
||||
EVENT_NONE = 0,
|
||||
EVENT_EXEC,
|
||||
EVENT_FORK,
|
||||
EVENT_SCHED_EXEC,
|
||||
EVENT_SCHED_EXIT
|
||||
};
|
||||
|
||||
struct data_t {
|
||||
u64 type;
|
||||
u64 pid; // PID as in the userspace term (i.e. task->tgid in kernel)
|
||||
u64 ppid; // Parent PID as in the userspace term (i.e task->real_parent->tgid in kernel)
|
||||
u64 uid;
|
||||
char filename[ARGSIZE];
|
||||
char comm[TASK_COMM_LEN];
|
||||
}__attribute__((packed));
|
||||
#include "common.h"
|
||||
|
||||
struct bpf_map_def SEC("maps/proc-events") events = {
|
||||
// Since kernel 4.4
|
||||
|
@ -73,7 +17,6 @@ static __always_inline void new_event(struct pt_regs *ctx, struct data_t* data)
|
|||
{
|
||||
// initializing variables with __builtin_memset() is required
|
||||
// for compatibility with bpf on kernel 4.4
|
||||
__builtin_memset(data, 0, sizeof(struct data_t));
|
||||
|
||||
struct task_struct *task={0};
|
||||
struct task_struct *parent={0};
|
||||
|
@ -99,27 +42,33 @@ SEC("kprobe/sys_execve")
|
|||
int kprobe__sys_execve(struct pt_regs *ctx)
|
||||
{
|
||||
const char *filename = (const char *)PT_REGS_PARM2(ctx);
|
||||
// TODO: use ringbuffer to allocate the absolute path[4096] + arguments
|
||||
// TODO: extract args
|
||||
//const char *argv = (const char *)PT_REGS_PARM3(ctx);
|
||||
|
||||
struct data_t data={0};
|
||||
new_event(ctx, &data);
|
||||
data.type = EVENT_EXEC;
|
||||
bpf_probe_read_user_str(&data.filename, sizeof(data.filename), filename);
|
||||
|
||||
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &data, sizeof(struct data_t));
|
||||
int zero = 0;
|
||||
struct data_t *data = bpf_map_lookup_elem(&heapstore, &zero);
|
||||
if (!data){ return 0; }
|
||||
|
||||
new_event(ctx, data);
|
||||
data->type = EVENT_EXEC;
|
||||
bpf_probe_read_user_str(&data->filename, sizeof(data->filename), filename);
|
||||
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, data, sizeof(*data));
|
||||
return 0;
|
||||
};
|
||||
|
||||
SEC("tracepoint/sched/sched_process_exit")
|
||||
int tracepoint__sched_sched_process_exit(struct pt_regs *ctx)
|
||||
{
|
||||
struct data_t data={0};
|
||||
__builtin_memset(&data, 0, sizeof(data));
|
||||
new_event(ctx, &data);
|
||||
data.type = EVENT_SCHED_EXIT;
|
||||
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &data, sizeof(struct data_t));
|
||||
int zero = 0;
|
||||
struct data_t *data = bpf_map_lookup_elem(&heapstore, &zero);
|
||||
if (!data){ return 0; }
|
||||
|
||||
//__builtin_memset(data, 0, sizeof(struct data_t));
|
||||
new_event(ctx, data);
|
||||
data->type = EVENT_SCHED_EXIT;
|
||||
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, data, sizeof(*data));
|
||||
return 0;
|
||||
};
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
|
|
@ -3,48 +3,13 @@
|
|||
//uncomment if building on x86_32
|
||||
//#define OPENSNITCH_x86_32
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include "common.h"
|
||||
#include <linux/version.h>
|
||||
#include <uapi/linux/bpf.h>
|
||||
#include <uapi/linux/tcp.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/udp_tunnel.h>
|
||||
#include <net/inet_sock.h>
|
||||
|
||||
#define MAPSIZE 12000
|
||||
|
||||
#ifndef TASK_COMM_LEN
|
||||
#define TASK_COMM_LEN 16
|
||||
#endif
|
||||
|
||||
//-------------------------------map definitions
|
||||
// which github.com/iovisor/gobpf/elf expects
|
||||
#define BUF_SIZE_MAP_NS 256
|
||||
|
||||
typedef struct bpf_map_def {
|
||||
unsigned int type;
|
||||
unsigned int key_size;
|
||||
unsigned int value_size;
|
||||
unsigned int max_entries;
|
||||
unsigned int map_flags;
|
||||
unsigned int pinning;
|
||||
char namespace[BUF_SIZE_MAP_NS];
|
||||
} bpf_map_def;
|
||||
|
||||
enum bpf_pin_type {
|
||||
PIN_NONE = 0,
|
||||
PIN_OBJECT_NS,
|
||||
PIN_GLOBAL_NS,
|
||||
PIN_CUSTOM_NS,
|
||||
};
|
||||
//-----------------------------------
|
||||
|
||||
// even though we only need 32 bits of pid, on x86_32 ebpf verifier complained when pid type was set to u32
|
||||
typedef u64 pid_size_t;
|
||||
typedef u64 uid_size_t;
|
||||
|
||||
struct tcp_key_t {
|
||||
u16 sport;
|
||||
|
|
Loading…
Add table
Reference in a new issue