Maximilian Münchow
8 years ago
9 changed files with 504 additions and 6 deletions
@ -0,0 +1,16 @@
@@ -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 */ |
@ -0,0 +1,12 @@
@@ -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 */ |
@ -0,0 +1,92 @@
@@ -0,0 +1,92 @@
|
||||
#ifndef _XTABLES_COMPAT_H |
||||
#define _XTABLES_COMPAT_H 1 |
||||
|
||||
#include <linux/kernel.h> |
||||
#include <linux/version.h> |
||||
#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 <net/netfilter/nf_conntrack.h> |
||||
#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 */ |
@ -0,0 +1,67 @@
@@ -0,0 +1,67 @@
|
||||
#ifndef _COMPAT_XTNU_H |
||||
#define _COMPAT_XTNU_H 1 |
||||
|
||||
#include <linux/netfilter/x_tables.h> |
||||
|
||||
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 */ |
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* "libxt_ts3init_reset" target extension for iptables |
||||
* Niels Werensteijn <niels werensteijn [at] teamspeak com>, 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 <stdbool.h> |
||||
#include <stddef.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <getopt.h> |
||||
#include <xtables.h> |
||||
#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)); |
||||
} |
||||
|
@ -0,0 +1,232 @@
@@ -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 <niels werensteijn [at] teampseak com>, 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 <linux/kernel.h> |
||||
#include <linux/ip.h> |
||||
#include <linux/module.h> |
||||
#include <linux/skbuff.h> |
||||
#include <linux/udp.h> |
||||
#include <linux/netfilter/x_tables.h> |
||||
#ifdef CONFIG_BRIDGE_NETFILTER |
||||
# include <linux/netfilter_bridge.h> |
||||
#endif |
||||
#include <net/ip.h> |
||||
#include <net/ip6_checksum.h> |
||||
#include <net/ip6_route.h> |
||||
#include <net/route.h> |
||||
#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)); |
||||
} |
Loading…
Reference in new issue