From bf8594c03e6c0e9ef27a97f8538504d67171a196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20M=C3=BCnchow?= Date: Tue, 11 Oct 2016 09:26:11 +0200 Subject: [PATCH] added ts3init_reset --- src/Makefile | 13 ++- src/compat_skbuff.h | 16 +++ src/compat_user.h | 12 ++ src/compat_xtables.h | 92 +++++++++++++++ src/compat_xtnu.h | 67 +++++++++++ src/libxt_ts3init_reset.c | 61 ++++++++++ src/ts3init_module.c | 13 ++- src/ts3init_target.c | 232 ++++++++++++++++++++++++++++++++++++++ src/ts3init_target.h | 4 + 9 files changed, 504 insertions(+), 6 deletions(-) create mode 100644 src/compat_skbuff.h create mode 100644 src/compat_user.h create mode 100644 src/compat_xtables.h create mode 100644 src/compat_xtnu.h create mode 100644 src/libxt_ts3init_reset.c create mode 100644 src/ts3init_target.c create mode 100644 src/ts3init_target.h diff --git a/src/Makefile b/src/Makefile index a4bec3b..2904714 100644 --- a/src/Makefile +++ b/src/Makefile @@ -3,17 +3,20 @@ MODULES_DIR := /lib/modules/$(shell uname -r) KERNEL_DIR := ${MODULES_DIR}/build obj-m += xt_ts3init.o -xt_ts3init-objs += ts3init_module.o ts3init_match.o ts3init_cookie.o siphash24.o +xt_ts3init-objs += ts3init_module.o ts3init_match.o ts3init_cookie.o ts3init_target.o siphash24.o +so = libxt_ts3init_get_cookie.so libxt_ts3init_get_puzzle.so libxt_ts3init_reset.so all: - make -C ${KERNEL_DIR} M=$$PWD; + $(MAKE) -C ${KERNEL_DIR} M=$$PWD; + $(MAKE) -f Makefile.xtables $(so) modules: - make -C ${KERNEL_DIR} M=$$PWD $@; + $(MAKE) -C ${KERNEL_DIR} M=$$PWD $@; modules_install: - make -C ${KERNEL_DIR} M=$$PWD $@; + $(MAKE) -C ${KERNEL_DIR} M=$$PWD $@; clean: - make -C ${KERNEL_DIR} M=$$PWD $@; + $(MAKE) -C ${KERNEL_DIR} M=$$PWD $@; + rm $(so) diff --git a/src/compat_skbuff.h b/src/compat_skbuff.h new file mode 100644 index 0000000..ba064c1 --- /dev/null +++ b/src/compat_skbuff.h @@ -0,0 +1,16 @@ +#ifndef COMPAT_SKBUFF_H +#define COMPAT_SKBUFF_H 1 + +struct tcphdr; +struct udphdr; + +#define skb_ifindex(skb) (skb)->skb_iif +#define skb_nfmark(skb) (((struct sk_buff *)(skb))->mark) + +#ifdef CONFIG_NETWORK_SECMARK +# define skb_secmark(skb) ((skb)->secmark) +#else +# define skb_secmark(skb) 0 +#endif + +#endif /* COMPAT_SKBUFF_H */ diff --git a/src/compat_user.h b/src/compat_user.h new file mode 100644 index 0000000..3ad168c --- /dev/null +++ b/src/compat_user.h @@ -0,0 +1,12 @@ +/* + * Userspace-level compat hacks + */ +#ifndef _XTABLES_COMPAT_USER_H +#define _XTABLES_COMPAT_USER_H 1 + +/* linux-glibc-devel 2.6.34 header screwup */ +#ifndef ALIGN +# define ALIGN(s, n) (((s) + ((n) - 1)) & ~((n) - 1)) +#endif + +#endif /* _XTABLES_COMPAT_USER_H */ diff --git a/src/compat_xtables.h b/src/compat_xtables.h new file mode 100644 index 0000000..a6c61ac --- /dev/null +++ b/src/compat_xtables.h @@ -0,0 +1,92 @@ +#ifndef _XTABLES_COMPAT_H +#define _XTABLES_COMPAT_H 1 + +#include +#include +#include "compat_skbuff.h" +#include "compat_xtnu.h" + +#define DEBUGP Use__pr_debug__instead + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) +# warning Kernels below 3.7 not supported. +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) +# define prandom_u32() random32() +#endif + +#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) +# if !defined(CONFIG_NF_CONNTRACK_MARK) +# warning You have CONFIG_NF_CONNTRACK enabled, but CONFIG_NF_CONNTRACK_MARK is not (please enable). +# endif +# include +#else +# warning You need CONFIG_NF_CONNTRACK. +#endif + +#if !defined(NIP6) && !defined(NIP6_FMT) +# define NIP6(addr) \ + ntohs((addr).s6_addr16[0]), \ + ntohs((addr).s6_addr16[1]), \ + ntohs((addr).s6_addr16[2]), \ + ntohs((addr).s6_addr16[3]), \ + ntohs((addr).s6_addr16[4]), \ + ntohs((addr).s6_addr16[5]), \ + ntohs((addr).s6_addr16[6]), \ + ntohs((addr).s6_addr16[7]) +# define NIP6_FMT "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" +#endif +#if !defined(NIPQUAD) && !defined(NIPQUAD_FMT) +# define NIPQUAD(addr) \ + ((const unsigned char *)&addr)[0], \ + ((const unsigned char *)&addr)[1], \ + ((const unsigned char *)&addr)[2], \ + ((const unsigned char *)&addr)[3] +# define NIPQUAD_FMT "%u.%u.%u.%u" +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0) +static inline struct inode *file_inode(struct file *f) +{ + return f->f_path.dentry->d_inode; +} +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) +static inline void proc_set_user(struct proc_dir_entry *de, + typeof(de->uid) uid, typeof(de->gid) gid) +{ + de->uid = uid; + de->gid = gid; +} + +static inline void *PDE_DATA(struct inode *inode) +{ + return PDE(inode)->data; +} + +static inline void proc_remove(struct proc_dir_entry *de) +{ + if (de != NULL) + remove_proc_entry(de->name, de->parent); +} +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) +# define ip6_local_out(xnet, xsk, xskb) ip6_local_out(xskb) +# define ip6_route_me_harder(xnet, xskb) ip6_route_me_harder(xskb) +# define ip_local_out(xnet, xsk, xskb) ip_local_out(xskb) +# define ip_route_me_harder(xnet, xskb, xaddrtype) ip_route_me_harder((xskb), (xaddrtype)) +#endif + +static inline struct net *par_net(const struct xt_action_param *par) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) + return par->net; +#else + return dev_net((par->in != NULL) ? par->in : par->out); +#endif +} + +#endif /* _XTABLES_COMPAT_H */ diff --git a/src/compat_xtnu.h b/src/compat_xtnu.h new file mode 100644 index 0000000..150dc0e --- /dev/null +++ b/src/compat_xtnu.h @@ -0,0 +1,67 @@ +#ifndef _COMPAT_XTNU_H +#define _COMPAT_XTNU_H 1 + +#include + +struct module; +struct sk_buff; + +struct xtnu_match { + /* + * Making it smaller by sizeof(void *) on purpose to catch + * lossy translation, if any. + */ + char name[sizeof(((struct xt_match *)NULL)->name) - 1 - sizeof(void *)]; + uint8_t revision; + bool (*match)(const struct sk_buff *, struct xt_action_param *); + int (*checkentry)(const struct xt_mtchk_param *); + void (*destroy)(const struct xt_mtdtor_param *); + struct module *me; + const char *table; + unsigned int matchsize, hooks; + unsigned short proto, family; + + void *__compat_match; +}; + +struct xtnu_target { + char name[sizeof(((struct xt_target *)NULL)->name) - 1 - sizeof(void *)]; + uint8_t revision; + unsigned int (*target)(struct sk_buff **, + const struct xt_action_param *); + int (*checkentry)(const struct xt_tgchk_param *); + void (*destroy)(const struct xt_tgdtor_param *); + struct module *me; + const char *table; + unsigned int targetsize, hooks; + unsigned short proto, family; + + void *__compat_target; +}; + +static inline struct xtnu_match *xtcompat_numatch(const struct xt_match *m) +{ + void *q; + memcpy(&q, m->name + sizeof(m->name) - sizeof(void *), sizeof(void *)); + return q; +} + +static inline struct xtnu_target *xtcompat_nutarget(const struct xt_target *t) +{ + void *q; + memcpy(&q, t->name + sizeof(t->name) - sizeof(void *), sizeof(void *)); + return q; +} + +extern int xtnu_register_match(struct xtnu_match *); +extern void xtnu_unregister_match(struct xtnu_match *); +extern int xtnu_register_matches(struct xtnu_match *, unsigned int); +extern void xtnu_unregister_matches(struct xtnu_match *, unsigned int); +extern int xtnu_register_target(struct xtnu_target *); +extern void xtnu_unregister_target(struct xtnu_target *); +extern int xtnu_register_targets(struct xtnu_target *, unsigned int); +extern void xtnu_unregister_targets(struct xtnu_target *, unsigned int); + +extern void *HX_memmem(const void *, size_t, const void *, size_t); + +#endif /* _COMPAT_XTNU_H */ diff --git a/src/libxt_ts3init_reset.c b/src/libxt_ts3init_reset.c new file mode 100644 index 0000000..b9f8966 --- /dev/null +++ b/src/libxt_ts3init_reset.c @@ -0,0 +1,61 @@ +/* + * "libxt_ts3init_reset" target extension for iptables + * Niels Werensteijn , 2016-10-03 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License; either version 2 + * or 3 of the License, as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ts3init_target.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) + +static void ts3init_reset_help(void) +{ + printf("ts3init_reset takes no options\n\n"); +} + +static int ts3init_reset_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **targetinfo) +{ + return false; +} + +/* register and init */ +static struct xtables_target ts3init_tg_reg[] = +{ + { + .name = "ts3init_reset", + .revision = 0, + .family = NFPROTO_IPV4, + .version = XTABLES_VERSION, + .size = 0, + .userspacesize = 0, + .help = ts3init_reset_help, + .parse = ts3init_reset_parse, + }, + { + .name = "ts3init_reset", + .revision = 0, + .family = NFPROTO_IPV6, + .version = XTABLES_VERSION, + .size = 0, + .userspacesize = 0, + .help = ts3init_reset_help, + .parse = ts3init_reset_parse, + }, +}; + +static __attribute__((constructor)) void ts3init_tg_ldr(void) +{ + xtables_register_targets(ts3init_tg_reg, ARRAY_SIZE(ts3init_tg_reg)); +} + diff --git a/src/ts3init_module.c b/src/ts3init_module.c index ceacd44..ba008a8 100644 --- a/src/ts3init_module.c +++ b/src/ts3init_module.c @@ -20,6 +20,11 @@ int __init ts3init_match_init(void); void __exit ts3init_match_exit(void); +/* defined in ts3init_target.c */ +int __init ts3init_target_init(void); +void __exit ts3init_target_exit(void); + + MODULE_AUTHOR("Niels Werensteijn "); MODULE_DESCRIPTION("A module to aid in ts3 spoof protection"); MODULE_LICENSE("GPL"); @@ -28,12 +33,18 @@ MODULE_ALIAS("ip6t_ts3init"); static int __init ts3init_init(void) { - return ts3init_match_init(); + int error; + error = ts3init_match_init(); + if (error) + return error; + error = ts3init_target_init(); + return error; } static void __exit ts3init_exit(void) { ts3init_match_exit(); + ts3init_target_exit(); } module_init(ts3init_init); diff --git a/src/ts3init_target.c b/src/ts3init_target.c new file mode 100644 index 0000000..dcf4ac9 --- /dev/null +++ b/src/ts3init_target.c @@ -0,0 +1,232 @@ +/* + * "ts3init" extension for Xtables + * + * Description: A module to aid in ts3 spoof protection + * This is the "target" code + * + * Authors: + * Niels Werensteijn , 2016-10-03 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License; either version 2 + * or 3 of the License, as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_BRIDGE_NETFILTER +# include +#endif +#include +#include +#include +#include +#include "compat_xtables.h" +#include "ts3init_target.h" +#include "ts3init_header.h" + + +bool +send_ipv6(const struct sk_buff *oldskb, const struct xt_action_param *par, u8 command, const void *payload, const size_t payload_size) +{ + const struct udphdr *oldudp; + const struct ipv6hdr *oldip; + struct udphdr *newudp, oldudp_buf; + struct ipv6hdr *newip; + struct sk_buff *newskb; + struct flowi6 fl; + struct dst_entry *dst = NULL; + struct net *net = dev_net((par->in != NULL) ? par->in : par->out); + + oldip = ipv6_hdr(oldskb); + oldudp = skb_header_pointer(oldskb, par->thoff, + sizeof(*oldudp), &oldudp_buf); + if (oldudp == NULL) + return false; + if (ntohs(oldudp->len) <= sizeof(*oldudp)) + return false; + + newskb = alloc_skb(LL_MAX_HEADER + sizeof(*newip) + + sizeof(*newudp) + payload_size, GFP_ATOMIC); + if (newskb == NULL) + return false; + + skb_reserve(newskb, LL_MAX_HEADER); + newskb->protocol = oldskb->protocol; + + skb_reset_network_header(newskb); + newip = (void *)skb_put(newskb, sizeof(*newip)); + newip->version = oldip->version; + newip->priority = oldip->priority; + memcpy(newip->flow_lbl, oldip->flow_lbl, sizeof(newip->flow_lbl)); + newip->nexthdr = par->target->proto; + newip->saddr = oldip->daddr; + newip->daddr = oldip->saddr; + + skb_reset_transport_header(newskb); + newudp = (void *)skb_put(newskb, sizeof(*newudp)); + newudp->source = oldudp->dest; + newudp->dest = oldudp->source; + newudp->len = htons(sizeof(*newudp) + payload_size); + + memcpy(skb_put(newskb, payload_size), payload, payload_size); + newip->payload_len = htons(newskb->len); + + newudp->check = 0; + newudp->check = csum_ipv6_magic(&newip->saddr, &newip->daddr, + ntohs(newudp->len), IPPROTO_UDP, + csum_partial(newudp, ntohs(newudp->len), 0)); + + memset(&fl, 0, sizeof(fl)); + fl.flowi6_proto = newip->nexthdr; + memcpy(&fl.saddr, &newip->saddr, sizeof(fl.saddr)); + memcpy(&fl.daddr, &newip->daddr, sizeof(fl.daddr)); + fl.fl6_sport = newudp->source; + fl.fl6_dport = newudp->dest; + security_skb_classify_flow((struct sk_buff *)oldskb, flowi6_to_flowi(&fl)); + dst = ip6_route_output(net, NULL, &fl); + if (dst == NULL || dst->error != 0) { + dst_release(dst); + goto free_nskb; + } + + skb_dst_set(newskb, dst); + newip->hop_limit = ip6_dst_hoplimit(skb_dst(newskb)); + newskb->ip_summed = CHECKSUM_NONE; + + /* "Never happens" (?) */ + if (newskb->len > dst_mtu(skb_dst(newskb))) + goto free_nskb; + + nf_ct_attach(newskb, oldskb); + ip6_local_out(par_net(par), newskb->sk, newskb); + return true; + + free_nskb: + kfree_skb(newskb); + return false; +} + +bool +send_ipv4(const struct sk_buff *oldskb, const struct xt_action_param *par, u8 command, const void *payload, const size_t payload_size) +{ + const struct udphdr *oldudp; + const struct iphdr *oldip; + struct udphdr *newudp, oldudp_buf; + struct iphdr *newip; + struct sk_buff *newskb; + + oldip = ip_hdr(oldskb); + oldudp = skb_header_pointer(oldskb, par->thoff, + sizeof(*oldudp), &oldudp_buf); + if (oldudp == NULL) + return false; + if (ntohs(oldudp->len) <= sizeof(*oldudp)) + return false; + + newskb = alloc_skb(LL_MAX_HEADER + sizeof(*newip) + + sizeof(*newudp) + payload_size, GFP_ATOMIC); + if (newskb == NULL) + return false; + + skb_reserve(newskb, LL_MAX_HEADER); + newskb->protocol = oldskb->protocol; + + skb_reset_network_header(newskb); + newip = (void *)skb_put(newskb, sizeof(*newip)); + newip->version = oldip->version; + newip->ihl = sizeof(*newip) / 4; + newip->tos = oldip->tos; + newip->id = oldip->id; + newip->frag_off = 0; + newip->protocol = oldip->protocol; + newip->check = 0; + newip->saddr = oldip->daddr; + newip->daddr = oldip->saddr; + + skb_reset_transport_header(newskb); + newudp = (void *)skb_put(newskb, sizeof(*newudp)); + newudp->source = oldudp->dest; + newudp->dest = oldudp->source; + newudp->len = htons(sizeof(*newudp) + payload_size); + + memcpy(skb_put(newskb, payload_size), payload, payload_size); + newip->tot_len = htons(newskb->len); + + newudp->check = 0; + newudp->check = csum_tcpudp_magic(newip->saddr, newip->daddr, + ntohs(newudp->len), IPPROTO_UDP, + csum_partial(newudp, ntohs(newudp->len), 0)); + + /* ip_route_me_harder expects the skb's dst to be set */ + skb_dst_set(newskb, dst_clone(skb_dst(oldskb))); + + if (ip_route_me_harder(par_net(par), newskb, RTN_UNSPEC) != 0) + goto free_nskb; + + newip->ttl = ip4_dst_hoplimit(skb_dst(newskb)); + newskb->ip_summed = CHECKSUM_NONE; + + /* "Never happens" (?) */ + if (newskb->len > dst_mtu(skb_dst(newskb))) + goto free_nskb; + + nf_ct_attach(newskb, oldskb); + ip_local_out(par_net(par), newskb->sk, newskb); + return true; + + free_nskb: + kfree_skb(newskb); + return false; +} + +static const char reset_package[] = {'T', 'S', '3', 'I', 'N', 'I', 'T', '1', 0x65, 0, 0x88, COMMAND_RESET_PUZZLE, 0 }; + +static unsigned int +ts3init_reset_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + switch (par->family) + { + case NFPROTO_IPV4: + send_ipv4(skb, par, COMMAND_RESET_PUZZLE, reset_package, sizeof(reset_package)); + break; + + case NFPROTO_IPV6: + send_ipv6(skb, par, COMMAND_RESET_PUZZLE, reset_package, sizeof(reset_package)); + break; + } + return NF_DROP; +} + +static struct xt_target ts3init_tg_reg[] __read_mostly = { + { + .name = "ts3init_reset", + .revision = 0, + .family = NFPROTO_IPV4, + .proto = IPPROTO_UDP, + .target = ts3init_reset_tg, + .me = THIS_MODULE, + }, + { + .name = "ts3init_reset", + .revision = 0, + .family = NFPROTO_IPV6, + .proto = IPPROTO_UDP, + .target = ts3init_reset_tg, + .me = THIS_MODULE, + }, +}; + +int __init ts3init_target_init(void) +{ + return xt_register_targets(ts3init_tg_reg, ARRAY_SIZE(ts3init_tg_reg)); +} + +void __exit ts3init_target_exit(void) +{ + xt_unregister_targets(ts3init_tg_reg, ARRAY_SIZE(ts3init_tg_reg)); +} diff --git a/src/ts3init_target.h b/src/ts3init_target.h new file mode 100644 index 0000000..6d047d5 --- /dev/null +++ b/src/ts3init_target.h @@ -0,0 +1,4 @@ +#ifndef _TS3INIT_TARGET_H +#define _TS3INIT_TARGET_H + +#endif /* _TS3INIT_TARGET_H */