├── .gitignore ├── dkms.conf ├── Makefile ├── Makefile.dkms ├── README.md └── tcpsecrets.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.cmd 2 | *.ko 3 | *.mod 4 | *.mod.c 5 | *.o 6 | Module.symvers 7 | modules.order 8 | system_map.inc 9 | -------------------------------------------------------------------------------- /dkms.conf: -------------------------------------------------------------------------------- 1 | PACKAGE_NAME=tcpsecrets 2 | PACKAGE_VERSION= 3 | BUILT_MODULE_NAME="$PACKAGE_NAME" 4 | MAKE="KVER=$kernelver KDIR=$kernel_source_dir make" 5 | CLEAN="make clean" 6 | DEST_MODULE_LOCATION=/kernel/net/syncookied 7 | REMAKE_INITRD=no 8 | AUTOINSTALL=yes 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | obj-m := tcpsecrets.o 2 | 3 | KVER ?= $(shell uname -r) 4 | KDIR ?= /lib/modules/${KVER}/build 5 | PWD := $(shell pwd) 6 | 7 | default: 8 | $(MAKE) -C $(KDIR) M=$(PWD) modules 9 | 10 | clean: 11 | @rm -f *.o .*.cmd .*.flags *.mod.c *.order *.ko Module.symvers 12 | @rm -f .*.*.cmd *~ *.*~ TODO.* *.inc 13 | @rm -fR .tmp* 14 | @rm -rf .tmp_versions 15 | disclean: clean 16 | @rm *.ko *.symvers 17 | -------------------------------------------------------------------------------- /Makefile.dkms: -------------------------------------------------------------------------------- 1 | COMMIT_REV := $(shell git describe --always --abbrev=12) 2 | DKMS_DSTDIR ?= /var/lib/dkms/tcpsecrets/$(COMMIT_REV)/source 3 | KERNEL_VERSION ?= $(shell uname -r) 4 | 5 | .PHONY: all install build install_source clean 6 | 7 | all: install 8 | 9 | install: build 10 | dkms install -m tcpsecrets -v $(COMMIT_REV) -k $(KERNEL_VERSION) 11 | 12 | build: install_sources 13 | dkms build -m tcpsecrets -v $(COMMIT_REV) -k $(KERNEL_VERSION) 14 | 15 | install_sources: 16 | install -o root -g root -m 0755 -d $(DKMS_DSTDIR) 17 | cp -t $(DKMS_DSTDIR)/ Makefile tcpsecrets.c 18 | sed "s/PACKAGE_VERSION=/PACKAGE_VERSION=$(COMMIT_REV)/" dkms.conf > "$(DKMS_DSTDIR)/dkms.conf" 19 | 20 | clean: 21 | $(MAKE) -f Makefile clean 22 | dkms remove -m tcpsecrets -v $(COMMIT_REV) --all 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tcpsecrets 2 | Linux kernel module to provide access to tcp cookie secrets via `/proc/tcp_secrets` 3 | 4 | ## Tested kernels 5 | - 4.2.0-35-generic #40~14.04.1-Ubuntu 6 | - 4.4.0-34-generic #53~14.04.1-Ubuntu 7 | - 4.6.0-0.bpo.1-amd64 #1 SMP Debian 4.6.4-1~bpo8+1 8 | - 4.9.0-0.bpo.3-amd64 #1 SMP Debian 4.9.25-1~bpo8+1 9 | - 4.9.0-3-amd64 #1 SMP Debian 4.9.30-2+deb9u3 10 | - 4.9.255 (custom) 11 | - 5.8.0-48-generic #54~20.04.1-Ubuntu SMP 12 | - 5.10.24 (custom) 13 | 14 | ## Untested kernels 15 | - 3.16.0-4-amd64 #1 SMP Debian 3.16.36-1+deb8u1 (builds, not tested) 16 | 17 | ## Unsupported kernels 18 | - 2.6.x 19 | 20 | ## Custom kernels 21 | These options are required for module to work: 22 | 23 | ``` 24 | CONFIG_LIVEPATCH=y 25 | CONFIG_FTRACE=y 26 | CONFIG_DYNAMIC_FTRACE=y 27 | CONFIG_DYNAMIC_FTRACE_WITH_REGS=y 28 | CONFIG_FTRACE_MCOUNT_RECORD=y 29 | ``` 30 | 31 | Building for 5.7+ requires kprobes support: 32 | 33 | ``` 34 | CONFIG_KPROBES=y 35 | ``` 36 | 37 | ## Install via DKMS 38 | 39 | ``` 40 | KERNEL_VERSION=$(uname -r) make -f Makefile.dkms 41 | ``` 42 | -------------------------------------------------------------------------------- /tcpsecrets.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0) 10 | #define SIPHASH_SYNCOOKIES 11 | #endif 12 | 13 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0) 14 | #define USE_KPROBES 15 | #endif 16 | 17 | #ifdef SIPHASH_SYNCOOKIES 18 | #include 19 | #else 20 | #include 21 | #endif 22 | 23 | #ifdef USE_KPROBES 24 | #include 25 | #else 26 | #include 27 | #endif 28 | 29 | static void *cookie_v4_check_ptr; 30 | 31 | #ifdef SIPHASH_SYNCOOKIES 32 | static siphash_key_t (*syncookie_secret_ptr)[2]; 33 | #else 34 | static u32 (*syncookie_secret_ptr)[2][16-4+SHA_DIGEST_WORDS]; 35 | #endif 36 | 37 | static struct proc_dir_entry *proc_entry; 38 | 39 | static int tcp_secrets_show(struct seq_file *m, void *v) 40 | { 41 | int i, j; 42 | seq_printf(m, "%lu %lu %d\n", (unsigned long) jiffies, (unsigned long)tcp_cookie_time(), HZ); 43 | for(i = 0; i < 2; i++) { 44 | #ifdef SIPHASH_SYNCOOKIES 45 | for(j = 0; j < 2; j++) { 46 | seq_printf(m, "%.16llx.", (*syncookie_secret_ptr)[i].key[j]); 47 | } 48 | #else 49 | for(j = 0; j < 16-4+SHA_DIGEST_WORDS; j++) { 50 | seq_printf(m, "%.8x.", (*syncookie_secret_ptr)[i][j]); 51 | } 52 | #endif 53 | seq_printf(m, "\n"); 54 | } 55 | return 0; 56 | } 57 | 58 | static int tcp_secrets_open(struct inode *inode, struct file *file) 59 | { 60 | return single_open(file, tcp_secrets_show, NULL); 61 | } 62 | 63 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0) 64 | static const struct proc_ops tcp_secrets_fops = { 65 | .proc_open = tcp_secrets_open, 66 | .proc_read = seq_read, 67 | .proc_lseek = seq_lseek, 68 | .proc_release = single_release, 69 | }; 70 | #else 71 | static const struct file_operations tcp_secrets_fops = { 72 | .open = tcp_secrets_open, 73 | .read = seq_read, 74 | .llseek = seq_lseek, 75 | .release = single_release, 76 | }; 77 | #endif 78 | 79 | static int symbol_walk_callback(void *data, const char *name, 80 | struct module *mod, unsigned long addr) { 81 | if (mod) 82 | return 0; 83 | 84 | if (strcmp(name, "cookie_v4_check") == 0) { 85 | cookie_v4_check_ptr = (void *)addr; 86 | } 87 | if (strcmp(name, "syncookie_secret") == 0) { 88 | syncookie_secret_ptr = (void *)addr; 89 | } 90 | return 0; 91 | } 92 | 93 | #ifdef USE_KPROBES 94 | static int (*kallsyms_on_each_symbol_ptr)(int (*fn)(void *, const char *, 95 | struct module *, 96 | unsigned long), 97 | void *data); 98 | 99 | typedef unsigned long (*kallsyms_lookup_name_t)(const char *name); 100 | 101 | static int dummy_kprobe_handler(struct kprobe *p, struct pt_regs *regs) { 102 | return 0; 103 | } 104 | 105 | static kallsyms_lookup_name_t get_kallsyms_lookup_name_ptr(void) { 106 | struct kprobe probe; 107 | int ret; 108 | kallsyms_lookup_name_t addr; 109 | 110 | memset(&probe, 0, sizeof(probe)); 111 | probe.pre_handler = dummy_kprobe_handler; 112 | probe.symbol_name = "kallsyms_lookup_name"; 113 | ret = register_kprobe(&probe); 114 | if (ret) 115 | return NULL; 116 | addr = (kallsyms_lookup_name_t) probe.addr; 117 | unregister_kprobe(&probe); 118 | 119 | return addr; 120 | } 121 | 122 | static unsigned long lookup_name(const char *name) { 123 | static kallsyms_lookup_name_t func_ptr = NULL; 124 | if (!func_ptr) 125 | func_ptr = get_kallsyms_lookup_name_ptr(); 126 | 127 | return func_ptr(name); 128 | } 129 | #endif 130 | 131 | #if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) 132 | static struct sock *cookie_v4_check_wrapper(struct sock *sk, 133 | struct sk_buff *skb, 134 | struct ip_options *opt) 135 | { 136 | struct sock* (*old_func)(struct sock *sk, struct sk_buff *skb, struct ip_options *opt) = 137 | (void*)((unsigned long)cookie_v4_check_ptr + MCOUNT_INSN_SIZE); 138 | 139 | extern int sysctl_tcp_syncookies; 140 | 141 | if (sysctl_tcp_syncookies == 2) { 142 | tcp_synq_overflow(sk); 143 | } 144 | return old_func(sk, skb, opt); 145 | } 146 | #else 147 | static struct sock *cookie_v4_check_wrapper(struct sock *sk, 148 | struct sk_buff *skb) { 149 | struct sock* (*old_func)(struct sock *sk, struct sk_buff *skb) = 150 | (void*)((unsigned long)cookie_v4_check_ptr + MCOUNT_INSN_SIZE); 151 | 152 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4,6,0) 153 | extern int sysctl_tcp_syncookies; 154 | 155 | if (sysctl_tcp_syncookies == 2) { 156 | #else 157 | if (sock_net(sk)->ipv4.sysctl_tcp_syncookies == 2) { 158 | #endif 159 | tcp_synq_overflow(sk); 160 | } 161 | return old_func(sk, skb); 162 | } 163 | #endif 164 | 165 | static void notrace 166 | tcpsecrets_ftrace_handler(unsigned long ip, unsigned long parent_ip, 167 | struct ftrace_ops *fops, struct pt_regs *regs) 168 | { 169 | regs->ip = (unsigned long)cookie_v4_check_wrapper; 170 | } 171 | 172 | static struct ftrace_ops tcpsecrets_ftrace_ops __read_mostly = { 173 | .func = tcpsecrets_ftrace_handler, 174 | .flags = FTRACE_OPS_FL_SAVE_REGS, 175 | }; 176 | 177 | static void fix_cookie_v4_check(void) { 178 | int ret; 179 | 180 | ret = ftrace_set_filter_ip(&tcpsecrets_ftrace_ops, (unsigned long)cookie_v4_check_ptr, 0, 0); 181 | if (ret) { 182 | printk("cant set ftrace filter\n"); 183 | } 184 | ret = register_ftrace_function(&tcpsecrets_ftrace_ops); 185 | if (ret) { 186 | printk("cant set ftrace function\n"); 187 | } 188 | } 189 | 190 | #ifdef SIPHASH_SYNCOOKIES 191 | static void init_secrets(void) 192 | { 193 | struct iphdr ip; 194 | struct tcphdr tcp; 195 | u16 mssp; 196 | 197 | __cookie_v4_init_sequence(&ip, &tcp, &mssp); 198 | } 199 | #endif 200 | 201 | static int __init tcp_secrets_init(void) 202 | { 203 | int rc; 204 | #ifndef USE_KPROBES 205 | rc = kallsyms_on_each_symbol(symbol_walk_callback, NULL); 206 | #else 207 | kallsyms_on_each_symbol_ptr = (void *) lookup_name("kallsyms_on_each_symbol"); 208 | rc = kallsyms_on_each_symbol_ptr(symbol_walk_callback, NULL); 209 | #endif 210 | if (rc) { 211 | return rc; 212 | } 213 | 214 | if (cookie_v4_check_ptr) { 215 | fix_cookie_v4_check(); 216 | } else { 217 | printk("tcp_secrets: can't find cookie_v4_check function!\n"); 218 | return -1; 219 | } 220 | if (!syncookie_secret_ptr) { 221 | printk("tcp_secrets: can't find syncookie secret!\n"); 222 | return -2; 223 | } 224 | 225 | proc_entry = proc_create("tcp_secrets", 0, NULL, &tcp_secrets_fops); 226 | if (proc_entry == NULL) { 227 | printk("tcp_secrets: can't create proc entry!\n"); 228 | return -3; 229 | } 230 | 231 | #ifdef SIPHASH_SYNCOOKIES 232 | init_secrets(); 233 | #endif 234 | 235 | return 0; 236 | } 237 | 238 | module_init(tcp_secrets_init); 239 | 240 | static void __exit tcp_secrets_exit(void) 241 | { 242 | int ret; 243 | 244 | if (cookie_v4_check_ptr) { 245 | ret = unregister_ftrace_function(&tcpsecrets_ftrace_ops); 246 | if (ret) { 247 | printk("can't unregister ftrace\n"); 248 | } 249 | ret = ftrace_set_filter_ip(&tcpsecrets_ftrace_ops, (unsigned long)cookie_v4_check_ptr, 1, 0); 250 | if (ret) { 251 | printk("can't unregister filter\n"); 252 | } 253 | cookie_v4_check_ptr = 0; 254 | } 255 | syncookie_secret_ptr = 0; 256 | if (proc_entry) 257 | remove_proc_entry("tcp_secrets", 0); 258 | } 259 | 260 | module_exit(tcp_secrets_exit); 261 | 262 | MODULE_LICENSE("GPL"); 263 | MODULE_AUTHOR("Alexander Polyakov "); 264 | MODULE_DESCRIPTION("Provide access to tcp syncookie secrets via /proc/tcp_secrets"); 265 | MODULE_VERSION("1.2"); 266 | --------------------------------------------------------------------------------