diff --git a/.gitignore b/.gitignore index 063fc710..618eec24 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,7 @@ # IDE .idea/ *.iml + +# BPF specific files *.o +vmlinux.h \ No newline at end of file diff --git a/src/ebpf/agent.ebpf.c b/src/ebpf/agent.ebpf.c index 78e6bb79..2b8eb07b 100644 --- a/src/ebpf/agent.ebpf.c +++ b/src/ebpf/agent.ebpf.c @@ -1,13 +1,21 @@ //go:build ignore // Common header for all eBPF programs -#include "headers.h" -#include "maps.h" -#include "filters.h" -#include "common.h" +#include "include/headers.h" + +// Event logic +#include "include/events/events.h" +#include "include/events/events.c" + +// Filter logic +#include "include/filters/pci.h" +#include "include/filters/pci.c" + +#include "include/filters/filters.h" +#include "include/filters/filters.c" // All eBPF programs -#include "gotls.ebpf.c" -#include "openssl.ebpf.c" +#include "gotls/gotls.ebpf.c" +#include "openssl/openssl.ebpf.c" char _license[] SEC("license") = "GPL"; \ No newline at end of file diff --git a/src/ebpf/generate.go b/src/ebpf/generate.go index ac51285e..dd58bfd0 100644 --- a/src/ebpf/generate.go +++ b/src/ebpf/generate.go @@ -3,4 +3,4 @@ package ebpf //go:generate sh -c "bpftool btf dump file /sys/kernel/btf/vmlinux format c > ./include/vmlinux.h" -//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target $GOARCH -cc clang -no-strip -cflags "-O2 -g -Wall" Bpf ./agent.ebpf.c -- -I.:/usr/include/bpf:/usr/include/linux -I./include -I./gotls -I./openssl -DTARGET_ARCH_$GOARCH +//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target $GOARCH -cc clang -no-strip -cflags "-O2 -g -Wall" Bpf ./agent.ebpf.c -- -I.:/usr/include/bpf:/usr/include/linux -I./ -DTARGET_ARCH_$GOARCH diff --git a/src/ebpf/gotls/gotls.ebpf.c b/src/ebpf/gotls/gotls.ebpf.c index e81fbfc7..3cf7f6b3 100644 --- a/src/ebpf/gotls/gotls.ebpf.c +++ b/src/ebpf/gotls/gotls.ebpf.c @@ -1,8 +1,7 @@ //go:build ignore -#include "headers.h" -#include "maps.h" -#include "common.h" +#include "include/headers.h" +#include "include/events/events.h" // ####################################################################### // // Definitions diff --git a/src/ebpf/include/common.h b/src/ebpf/include/events/events.c similarity index 98% rename from src/ebpf/include/common.h rename to src/ebpf/include/events/events.c index 378ac272..f8a6ad49 100644 --- a/src/ebpf/include/common.h +++ b/src/ebpf/include/events/events.c @@ -1,6 +1,6 @@ #pragma once -#include "filters.h" +#include "include/filters/filters.h" static __inline __u32 get_pid() { return bpf_get_current_pid_tgid() >> 32; diff --git a/src/ebpf/include/maps.h b/src/ebpf/include/events/events.h similarity index 83% rename from src/ebpf/include/maps.h rename to src/ebpf/include/events/events.h index 8f40287c..b2c65951 100644 --- a/src/ebpf/include/maps.h +++ b/src/ebpf/include/events/events.h @@ -87,3 +87,11 @@ struct { __type(value, struct go_slice_t); __uint(max_entries, 1024); } go_tls_context SEC(".maps"); + +// ####################################################################### // +// Function declarations +// ####################################################################### // + +static __inline __u32 get_pid(); +static __inline int should_trace(); +static __inline void send_event(struct pt_regs *ctx, __u64 buf, __u64 size, __u64 total_size, enum direction_t direction) ; diff --git a/src/ebpf/include/filters.h b/src/ebpf/include/filters/filters.c similarity index 81% rename from src/ebpf/include/filters.h rename to src/ebpf/include/filters/filters.c index e1be2051..d50c4330 100644 --- a/src/ebpf/include/filters.h +++ b/src/ebpf/include/filters/filters.c @@ -1,42 +1,7 @@ -#pragma once -#define HOST_HEADER_LEN 6 -#define AUTH_HEADER_LEN 14 -#define MAX_HEADER_LENGTH 255 +#include "filters.h" -#define HOST_HEADER "Host: " -#define AUTH_HEADER "Authorization: " - -#define HOST_AWS "amazonaws.com" -#define HOST_AWS_LEN 13 - -struct http_request_t { - // Request headers - __u32 host_len; - char host[MAX_HEADER_LENGTH]; - - __u32 auth_len; - char auth[MAX_HEADER_LENGTH]; - - // Internal state - char cur_line[MAX_HEADER_LENGTH]; -}; - -struct http_request_ctx_t { - __u8 *data; - int data_len; - __u32 line_start; -}; - -struct { - __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); - __type(key, __u32); - __type(value, struct http_request_t); - __uint(max_entries, 1); -} http_request_map SEC(".maps"); - -// TODO: This should be available in the BPF helpers - could not find it -static inline int helper_memcmp(const char *s1, const char *s2, int len) { +static __inline int helper_memcmp(const char *s1, const char *s2, int len) { #pragma unroll for (int i = 0; i < len; i++) { if (s1[i] != s2[i]) { @@ -46,7 +11,7 @@ static inline int helper_memcmp(const char *s1, const char *s2, int len) { return 0; // Strings match } -static inline bool is_http_request(__u8 *data, int data_len) { +static __inline bool is_http_request(__u8 *data, int data_len) { int method_lens[] = {3, 4, 3, 6, 4, 7, 6}; const char *methods[] = {"GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS", "PATCH"}; diff --git a/src/ebpf/include/filters/filters.h b/src/ebpf/include/filters/filters.h new file mode 100644 index 00000000..7e48d76e --- /dev/null +++ b/src/ebpf/include/filters/filters.h @@ -0,0 +1,43 @@ +#pragma once + +#define HOST_HEADER_LEN 6 +#define AUTH_HEADER_LEN 14 +#define MAX_HEADER_LENGTH 255 + +#define HOST_HEADER "Host: " +#define AUTH_HEADER "Authorization: " + +#define HOST_AWS "amazonaws.com" +#define HOST_AWS_LEN 13 + +struct http_request_t { + // Request headers + __u32 host_len; + char host[MAX_HEADER_LENGTH]; + + __u32 auth_len; + char auth[MAX_HEADER_LENGTH]; + + // Internal state + char cur_line[MAX_HEADER_LENGTH]; +}; + +struct http_request_ctx_t { + __u8 *data; + int data_len; + __u32 line_start; +}; + +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __type(key, __u32); + __type(value, struct http_request_t); + __uint(max_entries, 1); +} http_request_map SEC(".maps"); + + +// ####################################################################### // +// Function declarations +// ####################################################################### // + +static __inline bool should_send_event(struct ssl_event_t *event); diff --git a/src/ebpf/include/filters/pci.c b/src/ebpf/include/filters/pci.c new file mode 100644 index 00000000..af043bd4 --- /dev/null +++ b/src/ebpf/include/filters/pci.c @@ -0,0 +1,62 @@ + +#include "pci.h" + +// Check if the character is a digit +static __inline bool is_digit(char c) { + return c >= '0' && c <= '9'; +} + +// Luhn check to validate a card number +// Ref: https://en.wikipedia.org/wiki/Luhn_algorithm +bool luhn_check(const char *card_number, int len) { + __u32 sum = 0; + bool even = true; // Start with alternating since the check digit is not doubled + + // Process all digits except the last one (check digit) + for (int i = len - 2; i >= 0; i--) { + __u32 digit = card_number[i] - '0'; // Convert character to integer + if (digit < 0 || digit > 9) return false; // value may only contain digits + if (even) digit *= 2; // double the value + if (digit > 9) digit -= 9; + + even = !even; + sum += digit; + } + + // Add the check digit (last digit) to the sum + __u32 checksum = card_number[len - 1] - '0'; + sum += checksum; + + // If the total modulo 10 is 0, the number is valid + return (sum % 10 == 0); +} + +// Helper function to check for card-like sequences +bool detect_card_number(const char *data, int data_len) { + int digit_count = 0; + char card_number[MAX_CARD_LEN]; // Store potential card number sequence + + for (int i = 0; i < data_len; i++) { + if (is_digit(data[i])) { + card_number[digit_count] = data[i]; // Store digit + digit_count++; + + // Reset count if we exceed the maximum card number length + if (digit_count > MAX_CARD_LEN) digit_count = 0; + } else { + // Check if we have a valid card number length and validate with Luhn check + if (digit_count >= MIN_CARD_LEN && digit_count <= MAX_CARD_LEN) { + if (luhn_check(card_number, digit_count)) return true; + } + + digit_count = 0; // Reset count if non-digit found + } + } + + // Check again at the end in case the card number is at the end of the string + if (digit_count >= MIN_CARD_LEN && digit_count <= MAX_CARD_LEN) { + return luhn_check(card_number, digit_count); + } + + return false; // No valid card number detected +} diff --git a/src/ebpf/include/filters/pci.h b/src/ebpf/include/filters/pci.h new file mode 100644 index 00000000..afc00322 --- /dev/null +++ b/src/ebpf/include/filters/pci.h @@ -0,0 +1,5 @@ + +#define MIN_CARD_LEN 13 +#define MAX_CARD_LEN 19 + +bool detect_card_number(const char *data, int data_len); diff --git a/src/ebpf/include/headers.h b/src/ebpf/include/headers.h index 3e940b20..d65b9463 100644 --- a/src/ebpf/include/headers.h +++ b/src/ebpf/include/headers.h @@ -5,5 +5,4 @@ #include #include #include -#include - +#include \ No newline at end of file diff --git a/src/ebpf/init.go b/src/ebpf/init.go index 2bb64f5f..3e836a12 100644 --- a/src/ebpf/init.go +++ b/src/ebpf/init.go @@ -6,9 +6,20 @@ var Objs BpfObjects var Specs BpfSpecs func init() { + // Load and assign specs + specs, err := LoadBpf() + if err != nil { + logrus.Fatalf("error loading specs: %s", err) + + } + err = specs.Assign(&Specs) + if err != nil { + logrus.Fatalf("error assigning specs: %s", err) + } + // Load pre-compiled programs and maps into the kernel. if err := LoadBpfObjects(&Objs, nil); err != nil { - logrus.Fatalf("loading objects: %s", err) + logrus.Fatalf("error loading objects: %s", err) } logrus.Info("Loaded gotls objects") diff --git a/src/ebpf/openssl/openssl.ebpf.c b/src/ebpf/openssl/openssl.ebpf.c index ed6bfcd4..a533a046 100644 --- a/src/ebpf/openssl/openssl.ebpf.c +++ b/src/ebpf/openssl/openssl.ebpf.c @@ -1,9 +1,7 @@ //go:build ignore -#include "headers.h" -#include "maps.h" -#include "common.h" - +#include "include/headers.h" +#include "include/events/events.h" SEC("uprobe/otterize_SSL_write") void BPF_KPROBE(otterize_SSL_write, void* ssl, uintptr_t buffer, int num) {