diff --git a/contrib/extract-keys/Makefile b/contrib/extract-keys/Makefile new file mode 100644 index 0000000..a1dd7a2 --- /dev/null +++ b/contrib/extract-keys/Makefile @@ -0,0 +1,27 @@ +ifeq ($(KERNELRELEASE),) +KERNELDIR ?= /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) +CFLAGS ?= -O3 -march=native +CFLAGS += -Wall -pedantic -std=gnu11 + +extract-keys: extract-keys.c config.h + $(CC) $(CFLAGS) $(CPPFLAGS) -D_FILE_OFFSET_BITS=64 -o $@ -lresolv $< + +config.o: config.c + $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ + objcopy -j '.rodata*' $@ $@ + +config: config.c config.o + $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ + +config.h: config + ./$< > $@ + +clean: + rm -f extract-keys config config.h + $(MAKE) -C $(KERNELDIR) M=$(PWD) clean + +.PHONY: clean +else +config-m := config.o +endif diff --git a/contrib/extract-keys/README b/contrib/extract-keys/README new file mode 100644 index 0000000..a91363d --- /dev/null +++ b/contrib/extract-keys/README @@ -0,0 +1,23 @@ +Key Extractor +============= + +This will extract the symmetric ChaCha20Poly1305 session keys from the kernel +for a WireGuard interface, for use in making packet dissectors. + + +Build: + $ make + +Run (as root): + # ./extract-keys INTERFACE + +Output: + REMOTE_KEY_ID SENDING_KEY + LOCAL_KEY_ID RECEIVING_KEY + +Example: + # ./extract-keys wg0 + 0x57b56068 tMTSEOJpEYFAQV2UviDiYooX0A1AD/ONqrzoQVHa1rQ= + 0xa182fd19 xvQSkQ5HTX5RUeJ74eAAb/xfNhdrDThxG91GXZIPKmY= + 0x01662508 LbMc84JULzXJiHotSkdSOPZ0bHh6IDwOrbxWLfwosTs= + 0xbd819021 4VA8lZ3I1HjnJcWTmhEzBdC92W1Aag9Lnyy2GkroOYI= diff --git a/contrib/extract-keys/config.c b/contrib/extract-keys/config.c new file mode 100644 index 0000000..0dc4841 --- /dev/null +++ b/contrib/extract-keys/config.c @@ -0,0 +1,32 @@ +struct def { + const char *name; + long long value; +}; +extern const struct def defs[]; + +#ifdef __KERNEL__ +#include "../../../src/wireguard.h" +const struct def defs[] = { + { "SOCK_DEVICE_OFFSET", offsetof(struct sock, sk_user_data) }, + { "DEVICE_NAME_OFFSET", -ALIGN(sizeof(struct net_device), NETDEV_ALIGN) + offsetof(struct net_device, name) }, + { "IFNAMSIZ", IFNAMSIZ }, + { "DEVICE_PEERS_OFFSET", offsetof(struct wireguard_device, peer_list) }, + { "PEERS_PEER_OFFSET", -offsetof(struct wireguard_peer, peer_list) }, + { "PEER_CURRENTKEY_OFFSET", offsetof(struct wireguard_peer, keypairs.current_keypair) }, + { "PEER_PREVIOUSKEY_OFFSET", offsetof(struct wireguard_peer, keypairs.previous_keypair) }, + { "PEER_NEXTKEY_OFFSET", offsetof(struct wireguard_peer, keypairs.next_keypair) }, + { "KEY_LOCALID_OFFSET", offsetof(struct noise_keypair, entry.index) }, + { "KEY_REMOTEID_OFFSET", offsetof(struct noise_keypair, remote_index) }, + { "KEY_SENDING_OFFSET", offsetof(struct noise_keypair, sending.key) }, + { "KEY_RECEIVING_OFFSET", offsetof(struct noise_keypair, receiving.key) }, + { NULL, 0 } +}; +#else +#include +int main(int argc, char *argv[]) +{ + for (const struct def *def = defs; def->name; ++def) + printf("#define %s %lld\n", def->name, def->value); + return 0; +} +#endif diff --git a/contrib/extract-keys/extract-keys.c b/contrib/extract-keys/extract-keys.c new file mode 100644 index 0000000..5d7de32 --- /dev/null +++ b/contrib/extract-keys/extract-keys.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "config.h" + +static int fd; + +static void open_kmem(void) +{ + fd = open("/dev/kmem", O_RDONLY); + if (fd < 0) { + perror("open(/dev/kmem)"); + exit(errno); + } +} + +static void read_kmem(void *buffer, size_t len, unsigned long addr) +{ + if (lseek(fd, addr, SEEK_SET) == (off_t)-1) { + perror("lseek"); + exit(errno); + } + if (read(fd, buffer, len) != len) { + perror("read"); + exit(errno); + } +} + +static inline unsigned int read_int(unsigned long addr) +{ + unsigned int ret; + read_kmem(&ret, sizeof(ret), addr); + return ret; +} + +static inline unsigned long read_long(unsigned long addr) +{ + unsigned long ret; + read_kmem(&ret, sizeof(ret), addr); + return ret; +} + +static unsigned long find_interface(const char *interface) +{ + FILE *f = fopen("/proc/net/udp", "r"); + char line[256], *ptr; + unsigned long addr = 0; + char name[IFNAMSIZ + 1] = { 0 }; + + if (!f) { + perror("fopen(/proc/net/udp)"); + exit(errno); + } + if (!fgets(line, 256, f)) + goto out; + while (fgets(line, 256, f)) { + ptr = line + strlen(line) - 1; + while (*--ptr == ' '); + while (*--ptr != ' '); + while (*(--ptr - 1) != ' '); + addr = strtoul(ptr, NULL, 16); + if (!addr) + continue; + addr = read_long(addr + SOCK_DEVICE_OFFSET); + if (!addr) + continue; + read_kmem(name, IFNAMSIZ, addr + DEVICE_NAME_OFFSET); + if (!strcmp(name, interface)) + goto out; + } + addr = 0; +out: + fclose(f); + return addr; +} + +static bool print_key(unsigned long key) +{ + unsigned char sending[32], receiving[32]; + char sending_b64[45], receiving_b64[45]; + unsigned int local_index, remote_index; + + if (!key) + return false; + + local_index = le32toh(read_int(key + KEY_LOCALID_OFFSET)); + remote_index = le32toh(read_int(key + KEY_REMOTEID_OFFSET)); + read_kmem(sending, 32, key + KEY_SENDING_OFFSET); + read_kmem(receiving, 32, key + KEY_RECEIVING_OFFSET); + + b64_ntop(sending, 32, sending_b64, 45); + b64_ntop(receiving, 32, receiving_b64, 45); + + printf("0x%08x %s\n", local_index, receiving_b64); + printf("0x%08x %s\n", remote_index, sending_b64); + return true; +} + +static bool walk_peers(unsigned long peer_head) +{ + unsigned long peer, peer_entry; + bool found = false; + for (peer_entry = read_long(peer_head); peer_entry != peer_head; peer_entry = read_long(peer_entry)) { + peer = peer_entry + PEERS_PEER_OFFSET; + if (print_key(read_long(peer + PEER_CURRENTKEY_OFFSET))) + found = true; + if (print_key(read_long(peer + PEER_PREVIOUSKEY_OFFSET))) + found = true; + if (print_key(read_long(peer + PEER_NEXTKEY_OFFSET))) + found = true; + } + return found; +} + +int main(int argc, char *argv[]) +{ + unsigned long wireguard_device; + if (argc < 2) { + fprintf(stderr, "Usage: %s WIREGUARD_INTERFACE\n", argv[0]); + return EINVAL; + } + open_kmem(); + wireguard_device = find_interface(argv[1]); + if (!wireguard_device) { + fprintf(stderr, "Could not find interface %s\n", argv[1]); + return EBADSLT; + } + if (!walk_peers(wireguard_device + DEVICE_PEERS_OFFSET)) { + fprintf(stderr, "No active sessions\n"); + return ENOKEY; + } + return 0; +}