diff --git a/src/dns_lkm/Makefile b/src/dns_lkm/Makefile index 3f257b1..97c0195 100644 --- a/src/dns_lkm/Makefile +++ b/src/dns_lkm/Makefile @@ -1,5 +1,5 @@ obj-m := LKM.o -LKM-objs += netfilter_LKM.o +LKM-objs += nf_conntrack_sdns.o all: sudo make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules diff --git a/src/dns_lkm/add_conn_rules.sh b/src/dns_lkm/add_conn_rules.sh new file mode 100755 index 0000000..14b46ab --- /dev/null +++ b/src/dns_lkm/add_conn_rules.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# + + +printf "Adding iptables rules to track dns stuff..\n" + +sudo iptables -A PREROUTING -t raw -p udp --dport 53 -d 8.8.8.8 -j CT --helper sdns +sudo iptables -A OUTPUT -t raw -p udp --dport 53 -d 8.8.8.8 -j CT --helper sdns + + +sudo iptables -A PREROUTING -t raw -p udp --sport 53 -s 8.8.8.8 -j CT --helper sdns +sudo iptables -A OUTPUT -t raw -p udp --sport 53 -s 8.8.8.8 -j CT --helper sdns + + + +# sudo nping --tcp --dest-ip 1.2.3.4 -p 40404 --data-string "blahblah" + + + + diff --git a/src/dns_lkm/delete_conn_rules.sh b/src/dns_lkm/delete_conn_rules.sh new file mode 100755 index 0000000..7707ca8 --- /dev/null +++ b/src/dns_lkm/delete_conn_rules.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# + + +printf "Removing iptables rules to track dns stuff..\n" + +sudo iptables -D PREROUTING -t raw -p udp --dport 53 -d 8.8.8.8 -j CT --helper sdns +sudo iptables -D OUTPUT -t raw -p udp --dport 53 -d 8.8.8.8 -j CT --helper sdns + + +sudo iptables -D PREROUTING -t raw -p udp --sport 53 -s 8.8.8.8 -j CT --helper sdns +sudo iptables -D OUTPUT -t raw -p udp --sport 53 -s 8.8.8.8 -j CT --helper sdns diff --git a/src/dns_lkm/enable_helper.sh b/src/dns_lkm/enable_helper.sh new file mode 100755 index 0000000..3d61f25 --- /dev/null +++ b/src/dns_lkm/enable_helper.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# + +sudo modprobe nf_conntrack nf_conntrack_helper + +sudo sysctl net.netfilter.nf_conntrack_helper + +# sudo iptables -A PREROUTING -t raw -p tcp --dport 2121 -d 1.2.3.4 -j CT --helper testo diff --git a/src/dns_lkm/nf_conntrack_sdns.c b/src/dns_lkm/nf_conntrack_sdns.c new file mode 100644 index 0000000..2d7e514 --- /dev/null +++ b/src/dns_lkm/nf_conntrack_sdns.c @@ -0,0 +1,196 @@ +/* SDNS extension for IP connection tracking + * (C) 2021 by Beau Kujath + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MAX_PORTS 8 +static unsigned short ports[MAX_PORTS]; +static unsigned int ports_c; + + +static char *sdns_buffer; + + +//static DEFINE_SPINLOCK(nf_sdns_lock); + + + +struct dnsinfo { + __be16 txid; +}; + + + +struct nf_ct_sdns_master { + unsigned int txid; + /* Valid seq positions for cmd matching after newline */ + //u_int32_t seq_aft_nl[IP_CT_DIR_MAX][NUM_SEQ_TO_REMEMBER]; + /* 0 means seq_match_aft_nl not set */ + //u_int16_t seq_aft_nl_num[IP_CT_DIR_MAX]; + /* pickup sequence tracking, useful for conntrackd */ + //u_int16_t flags[IP_CT_DIR_MAX]; +}; + + +unsigned int (*nf_nat_sdns_hook)(struct sk_buff *skb, + enum ip_conntrack_info ctinfo, + unsigned int protoff, + unsigned int matchoff, + unsigned int matchlen, + struct nf_conntrack_expect *exp) __read_mostly; +EXPORT_SYMBOL_GPL(nf_nat_sdns_hook); + +#define HELPER_NAME "sdns" + + + +MODULE_AUTHOR("Beau Kujath "); +MODULE_DESCRIPTION("sdns connection tracking helper"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ip_conntrack_sdns"); +MODULE_ALIAS_NFCT_HELPER(HELPER_NAME); + +module_param_array(ports, ushort, &ports_c, 0400); +MODULE_PARM_DESC(ports, "port numbers of dns servers"); + + + + + +static int help(struct sk_buff *skb, unsigned int protoff, + struct nf_conn *ct, enum ip_conntrack_info ctinfo) +{ + + + struct udphdr _udph; + struct dnsinfo _dnsi; + const struct dnsinfo *dnsi; + unsigned int dataoff, datalimit; + + struct nf_ct_sdns_master *ct_sdns_info = nfct_help_data(ct); + struct udphdr *uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph); + + printk(KERN_INFO "\ntriggering conntrack sdns helper function\n"); + + + + //int dport = ntohs(uh->dest); + //printk(KERN_INFO "original protoff: (%u), len: (%u)\n", protoff, sizeof(_udph)); + //printk(KERN_INFO "dst port for packet: %u\n", dport); + + + if (uh == NULL) + return NF_ACCEPT; + + + dataoff = protoff + sizeof(struct udphdr); + datalimit = dataoff + sizeof(struct dnsinfo); + + // could probably delete this if check + if (dataoff >= skb->len) { + pr_debug("sdns: dataoff(%u) >= skblen(%u)\n", dataoff, + skb->len); + return NF_ACCEPT; + } + + + if (datalimit >= skb->len) { + pr_debug("sdns: datalimit(%u) >= skblen(%u)\n", datalimit, + skb->len); + return NF_ACCEPT; + } + + dnsi = skb_header_pointer(skb, dataoff, + sizeof(_dnsi), &_dnsi); + + if (dnsi == NULL) + return NF_ACCEPT; + + + //printk(KERN_INFO "ctinfo val: %d\n", ctinfo); + + if (ctinfo == IP_CT_NEW) { + // keep track of txid if it is new entry + int txid = ntohs(dnsi->txid); + ct_sdns_info->txid = txid; + printk(KERN_INFO "tracking txid for new ct entry: %d\n", ct_sdns_info->txid); + + } else if (ctinfo == IP_CT_IS_REPLY) { + + // check the txid of reply on reply entry + int txid = ntohs(dnsi->txid); + printk(KERN_INFO "dns txid of reply: %d, dns txid stored in ct struct: %d \n", txid, ct_sdns_info->txid); + + if (txid == ct_sdns_info->txid) { + printk(KERN_INFO "yes, txid on reply matched so accept!"); + return NF_ACCEPT; + } else { + printk(KERN_INFO "WARNING, txid on reply did not match so removing ct entry!"); + nf_ct_delete(ct, 0, 0); + return NF_DROP; + } + } + + + return NF_ACCEPT; +} + +static struct nf_conntrack_helper sdns[MAX_PORTS] __read_mostly; +static struct nf_conntrack_expect_policy sdns_exp_policy; + +static int __init nf_conntrack_sdns_init(void) +{ + int i, ret; + + printk(KERN_INFO "initializing conntrack sdns helper\n"); + + sdns_buffer = kmalloc(65536, GFP_KERNEL); + if (!sdns_buffer) + return -ENOMEM; + + /* If no port given, default to standard sdns port */ + if (ports_c == 0) { + printk(KERN_INFO "no ports given so assigning default port\n"); + ports[ports_c++] = IRC_PORT; + } + + for (i = 0; i < ports_c; i++) { + nf_ct_helper_init(&sdns[i], AF_INET, IPPROTO_UDP, HELPER_NAME, + IRC_PORT, ports[i], i, &sdns_exp_policy, + 0, help, NULL, THIS_MODULE); + } + + ret = nf_conntrack_helpers_register(&sdns[0], ports_c); + if (ret) { + pr_err("failed to register helpers\n"); + kfree(sdns_buffer); + return ret; + } + + return 0; +} + +static void __exit nf_conntrack_sdns_fini(void) +{ + nf_conntrack_helpers_unregister(sdns, ports_c); + kfree(sdns_buffer); +} + +module_init(nf_conntrack_sdns_init); +module_exit(nf_conntrack_sdns_fini); diff --git a/src/dns_lkm/test_pings.sh b/src/dns_lkm/test_pings.sh new file mode 100755 index 0000000..bee8c83 --- /dev/null +++ b/src/dns_lkm/test_pings.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# + + + +nslookup yo.com 8.8.8.8 diff --git a/src/nf_conntrack_dns.c b/src/nf_conntrack_dns.c deleted file mode 100644 index e83c45f..0000000 --- a/src/nf_conntrack_dns.c +++ /dev/null @@ -1,362 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* DNS extension for connection tracking. */ -// CODE is based on the FTP connection tracking module. -/* (C) 2021-20XX Benjamin Mixon-Baca - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -// #include - -#define HELPER_NAME "dns" - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("ben@breakpointingbad.com>"); -MODULE_DESCRIPTION("dns connection tracking helper"); -MODULE_ALIAS("ip_conntrack_dns"); -MODULE_ALIAS_NFCT_HELPER(HELPER_NAME); - -/* This is slow, but it's simple. --RR */ -static char *dns_buffer; - -/*BEN: Hug, we might need to use locks*/ -static DEFINE_SPINLOCK(nf_dns_lock); - -#define DNS_PORT 53 -#define MAX_PORTS 8 // Rip this from FTP module -static u_int16_t dns_ports[MAX_PORTS]; -static unsigned int dns_ports_c; -module_param_array(dns_ports, ushort, &dns_ports_c, 0400); - -static bool loose; -module_param(loose, bool, 0600); - -unsigned int (*nf_nat_dns_hook)(struct sk_buff *skb, - enum ip_conntrack_info ctinfo, - enum nf_ct_ftp_type type, - unsigned int protoff, - unsigned int matchoff, - unsigned int matchlen, - struct nf_conntrack_expect *exp); -EXPORT_SYMBOL_GPL(nf_nat_dns_hook); - -static int try_rfc959(const char *, size_t, struct nf_conntrack_man *, - char, unsigned int *); -static int try_rfc1123(const char *, size_t, struct nf_conntrack_man *, - char, unsigned int *); -static int try_eprt(const char *, size_t, struct nf_conntrack_man *, - char, unsigned int *); -static int try_epsv_response(const char *, size_t, struct nf_conntrack_man *, - char, unsigned int *); - -static struct ftp_search { - const char *pattern; - size_t plen; - char skip; - char term; - enum nf_ct_ftp_type ftptype; - int (*getnum)(const char *, size_t, struct nf_conntrack_man *, char, unsigned int *); -} search[IP_CT_DIR_MAX][2] = { - [IP_CT_DIR_ORIGINAL] = { - { - .pattern = "PORT", - .plen = sizeof("PORT") - 1, - .skip = ' ', - .term = '\r', - .ftptype = NF_CT_FTP_PORT, - .getnum = try_rfc959, - }, - { - .pattern = "EPRT", - .plen = sizeof("EPRT") - 1, - .skip = ' ', - .term = '\r', - .ftptype = NF_CT_FTP_EPRT, - .getnum = try_eprt, - }, - }, - [IP_CT_DIR_REPLY] = { - { - .pattern = "227 ", - .plen = sizeof("227 ") - 1, - .ftptype = NF_CT_FTP_PASV, - .getnum = try_rfc1123, - }, - { - .pattern = "229 ", - .plen = sizeof("229 ") - 1, - .skip = '(', - .term = ')', - .ftptype = NF_CT_FTP_EPSV, - .getnum = try_epsv_response, - }, - }, -}; - -static int -get_ipv6_addr(const char *src, size_t dlen, struct in6_addr *dst, u_int8_t term) -{ - const char *end; - int ret = in6_pton(src, min_t(size_t, dlen, 0xffff), (u8 *)dst, term, &end); - if (ret > 0) - return (int)(end - src); - return 0; -} - -static int try_number(const char *data, size_t dlen, u_int32_t array[], - int array_size, char sep, char term) -{ - u_int32_t i, len; - - memset(array, 0, sizeof(array[0])*array_size); - - /* Keep data pointing at next char. */ - for (i = 0, len = 0; len < dlen && i < array_size; len++, data++) { - if (*data >= '0' && *data <= '9') { - array[i] = array[i]*10 + *data - '0'; - } - else if (*data == sep) - i++; - else { - /* Unexpected character; true if it's the - terminator (or we don't care about one) - and we're finished. */ - if ((*data == term || !term) && i == array_size - 1) - return len; - - pr_debug("Char %u (got %u nums) `%u' unexpected\n", - len, i, *data); - return 0; - } - } - pr_debug("Failed to fill %u numbers separated by %c\n", - array_size, sep); - return 0; -} - -/* Returns 0, or length of numbers: 192,168,1,1,5,6 */ -static int try_rfc959(const char *data, size_t dlen, - struct nf_conntrack_man *cmd, char term, - unsigned int *offset) -{ - int length; - u_int32_t array[6]; - - length = try_number(data, dlen, array, 6, ',', term); - if (length == 0) - return 0; - - cmd->u3.ip = htonl((array[0] << 24) | (array[1] << 16) | - (array[2] << 8) | array[3]); - cmd->u.udp.port = htons((array[4] << 8) | array[5]); - return length; -} - -/* - * From RFC 1123: - * The format of the 227 reply to a PASV command is not - * well standardized. In particular, an FTP client cannot - * assume that the parentheses shown on page 40 of RFC-959 - * will be present (and in fact, Figure 3 on page 43 omits - * them). Therefore, a User-FTP program that interprets - * the PASV reply must scan the reply for the first digit - * of the host and port numbers. - */ -static int try_rfc1123(const char *data, size_t dlen, - struct nf_conntrack_man *cmd, char term, - unsigned int *offset) -{ - int i; - for (i = 0; i < dlen; i++) - if (isdigit(data[i])) - break; - - if (i == dlen) - return 0; - - *offset += i; - - return try_rfc959(data + i, dlen - i, cmd, 0, offset); -} - -/* Grab port: number up to delimiter */ -static int get_port(const char *data, int start, size_t dlen, char delim, - __be16 *port) -{ - u_int16_t tmp_port = 0; - int i; - - for (i = start; i < dlen; i++) { - /* Finished? */ - if (data[i] == delim) { - if (tmp_port == 0) - break; - *port = htons(tmp_port); - pr_debug("get_port: return %d\n", tmp_port); - return i + 1; - } - else if (data[i] >= '0' && data[i] <= '9') - tmp_port = tmp_port*10 + data[i] - '0'; - else { /* Some other crap */ - pr_debug("get_port: invalid char.\n"); - break; - } - } - return 0; -} - -/* Returns 0, or length of numbers: |1|132.235.1.2|6275| or |2|3ffe::1|6275| */ -static int try_eprt(const char *data, size_t dlen, struct nf_conntrack_man *cmd, - char term, unsigned int *offset) -{ - char delim; - int length; - - /* First character is delimiter, then "1" for IPv4 or "2" for IPv6, - then delimiter again. */ - if (dlen <= 3) { - pr_debug("EPRT: too short\n"); - return 0; - } - delim = data[0]; - if (isdigit(delim) || delim < 33 || delim > 126 || data[2] != delim) { - pr_debug("try_eprt: invalid delimiter.\n"); - return 0; - } - - if ((cmd->l3num == PF_INET && data[1] != '1') || - (cmd->l3num == PF_INET6 && data[1] != '2')) { - pr_debug("EPRT: invalid protocol number.\n"); - return 0; - } - - pr_debug("EPRT: Got %c%c%c\n", delim, data[1], delim); - - if (data[1] == '1') { - u_int32_t array[4]; - - /* Now we have IP address. */ - length = try_number(data + 3, dlen - 3, array, 4, '.', delim); - if (length != 0) - cmd->u3.ip = htonl((array[0] << 24) | (array[1] << 16) - | (array[2] << 8) | array[3]); - } else { - /* Now we have IPv6 address. */ - length = get_ipv6_addr(data + 3, dlen - 3, - (struct in6_addr *)cmd->u3.ip6, delim); - } - - if (length == 0) - return 0; - pr_debug("EPRT: Got IP address!\n"); - /* Start offset includes initial "|1|", and trailing delimiter */ - return get_port(data, 3 + length + 1, dlen, delim, &cmd->u.udp.port); -} - -/* Returns 0, or length of numbers: |||6446| */ -static int try_epsv_response(const char *data, size_t dlen, - struct nf_conntrack_man *cmd, char term, - unsigned int *offset) -{ - char delim; - - /* Three delimiters. */ - if (dlen <= 3) return 0; - delim = data[0]; - if (isdigit(delim) || delim < 33 || delim > 126 || - data[1] != delim || data[2] != delim) - return 0; - uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph); - if (uh == NULL) - return NF_ACCEPT; - - // BEN: Where is the data offset here? - dataoff = 4 * 16; // UDP is 4 2-byte chunks, then comes the payload - /* No data? */ - if (dataoff >= skb->len) { - pr_debug("dns: dataoff(%u) >= skblen(%u)\n", dataoff, - skb->len); - return NF_ACCEPT; - } - datalen = skb->len - dataoff; - - spin_lock_bh(&nf_dns_lock); - dns_ptr = skb_header_pointer(skb, dataoff, datalen, - dns_buffer); - if (!dns_ptr) { - spin_unlock_bh(&nf_dns_lock); - return NF_ACCEPT; - } - - out: - spin_unlock_bh(&nf_dns_lock); - return ret; -} - -static struct nf_conntrack_helper dns[MAX_PORTS * 2] __read_mostly; - -static const struct nf_conntrack_expect_policy dns_exp_policy = { - .max_expected = 1, - .timeout = 5 * 60, -}; - -static void __exit nf_conntrack_ftp_fini(void) -{ - nf_conntrack_helpers_unregister(dns, dns_ports_c * 2); - kfree(dns_buffer); -} - -static int __init nf_conntrack_dns_init(void) -{ - int i, ret = 0; - - // NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_ftp_master)); - - dns_buffer = kmalloc(65536, GFP_KERNEL); - if (!dns_buffer) - return -ENOMEM; - - if (dns_ports_c == 0) - dns_ports[dns_ports_c++] = DNS_PORT; - - /* FIXME should be configurable whether IPv4 and IPv6 FTP connections - are tracked or not - YK */ - for (i = 0; i < dns_ports_c; i++) { - nf_ct_helper_init(&ftp[2 * i], AF_INET, IPPROTO_UDP, - HELPER_NAME, DNS_PORT, dns_ports[i], ports[i], - &dns_exp_policy, 0, help, - nf_ct_ftp_from_nlattr, THIS_MODULE); - nf_ct_helper_init(&ftp[2 * i + 1], AF_INET6, IPPROTO_UDP, - HELPER_NAME, DNS_PORT, dns_ports[i], ports[i], - &dns_exp_policy, 0, help, - nf_ct_ftp_from_nlattr, THIS_MODULE); - } - - ret = nf_conntrack_helpers_register(dns, dns_ports_c * 2); - if (ret < 0) { - pr_err("failed to register helpers\n"); - kfree(dns_buffer); - return ret; - } - - return 0; -} - -module_init(nf_conntrack_dns_init); -module_exit(nf_conntrack_dns_fini); -