mirror of
https://github.com/evilsocket/opensnitch.git
synced 2025-03-04 08:34: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,
|
UID: uid,
|
||||||
Comm: comm,
|
Comm: comm,
|
||||||
}
|
}
|
||||||
length := 128
|
length := MaxPathLen
|
||||||
if len(path) < 128 {
|
if len(path) < MaxPathLen {
|
||||||
length = len(path)
|
length = len(path)
|
||||||
}
|
}
|
||||||
copy(ev.Filename[:], path[:length])
|
copy(ev.Filename[:], path[:length])
|
||||||
|
|
|
@ -10,13 +10,20 @@ import (
|
||||||
elf "github.com/iovisor/gobpf/elf"
|
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 execEvent struct {
|
||||||
Type uint64
|
Type uint64
|
||||||
PID uint64
|
PID uint64
|
||||||
PPID uint64
|
PPID uint64
|
||||||
UID uint64
|
UID uint64
|
||||||
Filename [128]byte
|
Filename [MaxPathLen]byte
|
||||||
Comm [16]byte
|
Comm [TaskCommLen]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// Struct that holds the metadata of a connection.
|
// Struct that holds the metadata of a connection.
|
||||||
|
@ -26,7 +33,7 @@ type networkEventT struct {
|
||||||
Pid uint64
|
Pid uint64
|
||||||
UID uint64
|
UID uint64
|
||||||
Counter uint64
|
Counter uint64
|
||||||
Comm [16]byte
|
Comm [TaskCommLen]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// List of supported events
|
// List of supported events
|
||||||
|
@ -57,8 +64,9 @@ func initEventsStreamer() {
|
||||||
|
|
||||||
tracepoints := []string{
|
tracepoints := []string{
|
||||||
"tracepoint/sched/sched_process_exit",
|
"tracepoint/sched/sched_process_exit",
|
||||||
// "tracepoint/sched/sched_process_exec",
|
//"tracepoint/sched/sched_process_exec",
|
||||||
// "tracepoint/sched/sched_process_fork",
|
//"tracepoint/syscalls/sys_enter_execve",
|
||||||
|
//"tracepoint/sched/sched_process_fork",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable tracepoints first, that way if kprobes fail loading we'll still have some
|
// 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:
|
case EV_TYPE_SCHED_EXIT:
|
||||||
//log.Warning("::: EXIT EVENT -> %d", event.PID)
|
//log.Warning("::: EXIT EVENT -> %d", event.PID)
|
||||||
execEvents.delete(event.PID)
|
if _, found := execEvents.isInStore(event.PID); found {
|
||||||
|
execEvents.delete(event.PID)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// TODO: delete old events (by timeout)
|
// TODO: delete old events (by timeout)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package ebpf
|
package ebpf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
@ -131,13 +130,13 @@ func getPidFromEbpf(proto string, srcPort uint, srcIP net.IP, dstIP net.IP, dstP
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
comm := string(bytes.Trim(value.Comm[:], "\x00"))
|
comm := byteArrayToString(value.Comm[:])
|
||||||
proc = procmon.NewProcess(int(value.Pid), comm)
|
proc = procmon.NewProcess(int(value.Pid), comm)
|
||||||
// use socket's UID. A process may have dropped privileges
|
// use socket's UID. A process may have dropped privileges
|
||||||
proc.UID = int(value.UID)
|
proc.UID = int(value.UID)
|
||||||
|
|
||||||
if ev, found := execEvents.isInStore(value.Pid); found {
|
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.ReadCmdline()
|
||||||
proc.ReadCwd()
|
proc.ReadCwd()
|
||||||
proc.ReadEnv()
|
proc.ReadEnv()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package ebpf
|
package ebpf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
@ -21,6 +22,21 @@ func mountDebugFS() error {
|
||||||
return nil
|
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 {
|
func deleteEbpfEntry(proto string, key unsafe.Pointer) bool {
|
||||||
if err := m.DeleteElement(ebpfMaps[proto].bpfmap, key); err != nil {
|
if err := m.DeleteElement(ebpfMaps[proto].bpfmap, key); err != nil {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -5,7 +5,7 @@ cd opensnitch
|
||||||
wget https://github.com/torvalds/linux/archive/v5.8.tar.gz
|
wget https://github.com/torvalds/linux/archive/v5.8.tar.gz
|
||||||
tar -xf v5.8.tar.gz
|
tar -xf v5.8.tar.gz
|
||||||
patch linux-5.8/tools/lib/bpf/bpf_helpers.h < ebpf_prog/file.patch
|
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 linux-5.8 && yes "" | make oldconfig && make prepare && make headers_install # (1 min)
|
||||||
cd samples/bpf && make
|
cd samples/bpf && make
|
||||||
objdump -h opensnitch.o #you should see many section, number 1 should be called kprobe/tcp_v4_connect
|
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_BPF_EVENTS=y
|
||||||
CONFIG_KPROBES=y
|
CONFIG_KPROBES=y
|
||||||
CONFIG_KPROBE_EVENTS=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
|
//uncomment if building on x86_32
|
||||||
//#define OPENSNITCH_x86_32
|
//#define OPENSNITCH_x86_32
|
||||||
|
|
||||||
#include <linux/ptrace.h>
|
#include "common.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));
|
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps/proc-events") events = {
|
struct bpf_map_def SEC("maps/proc-events") events = {
|
||||||
// Since kernel 4.4
|
// 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
|
// initializing variables with __builtin_memset() is required
|
||||||
// for compatibility with bpf on kernel 4.4
|
// for compatibility with bpf on kernel 4.4
|
||||||
__builtin_memset(data, 0, sizeof(struct data_t));
|
|
||||||
|
|
||||||
struct task_struct *task={0};
|
struct task_struct *task={0};
|
||||||
struct task_struct *parent={0};
|
struct task_struct *parent={0};
|
||||||
|
@ -99,27 +42,33 @@ SEC("kprobe/sys_execve")
|
||||||
int kprobe__sys_execve(struct pt_regs *ctx)
|
int kprobe__sys_execve(struct pt_regs *ctx)
|
||||||
{
|
{
|
||||||
const char *filename = (const char *)PT_REGS_PARM2(ctx);
|
const char *filename = (const char *)PT_REGS_PARM2(ctx);
|
||||||
// TODO: use ringbuffer to allocate the absolute path[4096] + arguments
|
|
||||||
// TODO: extract args
|
// TODO: extract args
|
||||||
//const char *argv = (const char *)PT_REGS_PARM3(ctx);
|
//const char *argv = (const char *)PT_REGS_PARM3(ctx);
|
||||||
|
|
||||||
struct data_t data={0};
|
int zero = 0;
|
||||||
new_event(ctx, &data);
|
struct data_t *data = bpf_map_lookup_elem(&heapstore, &zero);
|
||||||
data.type = EVENT_EXEC;
|
if (!data){ return 0; }
|
||||||
bpf_probe_read_user_str(&data.filename, sizeof(data.filename), filename);
|
|
||||||
|
new_event(ctx, data);
|
||||||
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &data, sizeof(struct data_t));
|
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;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
SEC("tracepoint/sched/sched_process_exit")
|
SEC("tracepoint/sched/sched_process_exit")
|
||||||
int tracepoint__sched_sched_process_exit(struct pt_regs *ctx)
|
int tracepoint__sched_sched_process_exit(struct pt_regs *ctx)
|
||||||
{
|
{
|
||||||
struct data_t data={0};
|
int zero = 0;
|
||||||
__builtin_memset(&data, 0, sizeof(data));
|
struct data_t *data = bpf_map_lookup_elem(&heapstore, &zero);
|
||||||
new_event(ctx, &data);
|
if (!data){ return 0; }
|
||||||
data.type = EVENT_SCHED_EXIT;
|
|
||||||
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &data, sizeof(struct data_t));
|
//__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;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,48 +3,13 @@
|
||||||
//uncomment if building on x86_32
|
//uncomment if building on x86_32
|
||||||
//#define OPENSNITCH_x86_32
|
//#define OPENSNITCH_x86_32
|
||||||
|
|
||||||
#include <linux/sched.h>
|
#include "common.h"
|
||||||
#include <linux/ptrace.h>
|
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
#include <uapi/linux/bpf.h>
|
|
||||||
#include <uapi/linux/tcp.h>
|
#include <uapi/linux/tcp.h>
|
||||||
#include <bpf/bpf_helpers.h>
|
|
||||||
#include <bpf/bpf_tracing.h>
|
|
||||||
#include <net/sock.h>
|
#include <net/sock.h>
|
||||||
#include <net/udp_tunnel.h>
|
#include <net/udp_tunnel.h>
|
||||||
#include <net/inet_sock.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 {
|
struct tcp_key_t {
|
||||||
u16 sport;
|
u16 sport;
|
||||||
|
|
Loading…
Add table
Reference in a new issue