From 2fbfbe6cd40e0ec7c323c128bda09805ee6017ca Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Thu, 21 Feb 2008 14:29:36 +0100 Subject: [PATCH] Import Chaostables extensions Signed-off-by: Jan Engelhardt --- extensions/Kbuild | 3 + extensions/compat_xtables.c | 20 +++ extensions/compat_xtables.h | 10 +- extensions/compat_xtnu.h | 3 + extensions/libxt_CHAOS.c | 113 +++++++++++++++ extensions/libxt_CHAOS.man | 18 +++ extensions/libxt_DELUDE.c | 47 ++++++ extensions/libxt_DELUDE.man | 4 + extensions/libxt_portscan.c | 121 ++++++++++++++++ extensions/libxt_portscan.man | 27 ++++ extensions/xt_CHAOS.c | 205 ++++++++++++++++++++++++++ extensions/xt_CHAOS.h | 14 ++ extensions/xt_DELUDE.c | 175 ++++++++++++++++++++++ extensions/xt_portscan.c | 263 ++++++++++++++++++++++++++++++++++ extensions/xt_portscan.h | 8 ++ mconfig | 3 + 16 files changed, 1033 insertions(+), 1 deletion(-) create mode 100644 extensions/libxt_CHAOS.c create mode 100644 extensions/libxt_CHAOS.man create mode 100644 extensions/libxt_DELUDE.c create mode 100644 extensions/libxt_DELUDE.man create mode 100644 extensions/libxt_portscan.c create mode 100644 extensions/libxt_portscan.man create mode 100644 extensions/xt_CHAOS.c create mode 100644 extensions/xt_CHAOS.h create mode 100644 extensions/xt_DELUDE.c create mode 100644 extensions/xt_portscan.c create mode 100644 extensions/xt_portscan.h diff --git a/extensions/Kbuild b/extensions/Kbuild index 0c28673..7155e3e 100644 --- a/extensions/Kbuild +++ b/extensions/Kbuild @@ -5,8 +5,11 @@ include ${XA_TOPSRCDIR}/mconfig obj-m += compat_xtables.o +obj-${build_CHAOS} += xt_CHAOS.o +obj-${build_DELUDE} += xt_DELUDE.o obj-${build_LOGMARK} += xt_LOGMARK.o obj-${build_TARPIT} += xt_TARPIT.o obj-${build_TEE} += xt_TEE.o +obj-${build_portscan} += xt_portscan.o -include ${M}/*.Kbuild diff --git a/extensions/compat_xtables.c b/extensions/compat_xtables.c index 35dad77..2e3cd60 100644 --- a/extensions/compat_xtables.c +++ b/extensions/compat_xtables.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include "compat_xtnu.h" @@ -228,6 +229,25 @@ void xtnu_unregister_targets(struct xtnu_target *nt, unsigned int num) EXPORT_SYMBOL_GPL(xtnu_unregister_targets); #endif +struct xt_match *xtnu_request_find_match(unsigned int af, const char *name, + uint8_t revision) +{ + static const char *const xt_prefix[] = { + [AF_INET] = "ip", + [AF_INET6] = "ip6", + [NF_ARP] = "arp", + }; + struct xt_match *match; + + match = try_then_request_module(xt_find_match(af, name, revision), + "%st_%s", xt_prefix[af], name); + if (IS_ERR(match) || match == NULL) + return NULL; + + return match; +} +EXPORT_SYMBOL_GPL(xtnu_request_find_match); + #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 23) int xtnu_ip_route_me_harder(struct sk_buff *skb, unsigned int addr_type) { diff --git a/extensions/compat_xtables.h b/extensions/compat_xtables.h index 666408b..184e495 100644 --- a/extensions/compat_xtables.h +++ b/extensions/compat_xtables.h @@ -13,11 +13,18 @@ # define NF_INET_FORWARD NF_IP_FORWARD # define NF_INET_LOCAL_OUT NF_IP_LOCAL_OUT # define NF_INET_POST_ROUTING NF_IP_POST_ROUTING -# define init_net xtnu_ip_route_output_key /* yes */ +# define ip_local_out xtnu_ip_local_out # define ip_route_output_key xtnu_ip_route_output_key # include "compat_nfinetaddr.h" #endif +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 23) +# define init_net xtnu_ip_route_output_key /* yes */ +# define init_net__loopback_dev (&loopback_dev) +#else +# define init_net__loopback_dev init_net.loopback_dev +#endif + #if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 22) # define xt_match xtnu_match # define xt_register_match xtnu_register_match @@ -35,6 +42,7 @@ # define xt_unregister_targets xtnu_unregister_targets #endif +#define xt_request_find_match xtnu_request_find_match #include "compat_xtnu.h" #endif /* _XTABLES_COMPAT_H */ diff --git a/extensions/compat_xtnu.h b/extensions/compat_xtnu.h index 62d5c65..77018e7 100644 --- a/extensions/compat_xtnu.h +++ b/extensions/compat_xtnu.h @@ -61,6 +61,7 @@ static inline struct xtnu_target *xtcompat_nutarget(const struct xt_target *t) return q; } +extern int xtnu_ip_local_out(struct sk_buff *); extern int xtnu_ip_route_me_harder(struct sk_buff *, unsigned int); extern int xtnu_register_match(struct xtnu_match *); extern int xtnu_ip_route_output_key(void *, struct rtable **, struct flowi *); @@ -71,5 +72,7 @@ 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 struct xt_match *xtnu_request_find_match(unsigned int, + const char *, uint8_t); #endif /* _COMPAT_XTNU_H */ diff --git a/extensions/libxt_CHAOS.c b/extensions/libxt_CHAOS.c new file mode 100644 index 0000000..66edd3d --- /dev/null +++ b/extensions/libxt_CHAOS.c @@ -0,0 +1,113 @@ +/* + * CHAOS target for Xtables + * Copyright © CC Computer Consultants GmbH, 2006 - 2008 + * + * 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 as published by the Free Software Foundation. + */ +#include +#include +#include +#include + +#include +#include +#include "xt_CHAOS.h" + +enum { + F_DELUDE = 1 << 0, + F_TARPIT = 1 << 1, +}; + +static const struct option chaos_tg_opts[] = { + {.name = "delude", .has_arg = false, .val = 'd'}, + {.name = "tarpit", .has_arg = false, .val = 't'}, + {}, +}; + +static void chaos_tg_help(void) +{ + printf( + "CHAOS target options:\n" + " --delude Enable DELUDE processing for TCP\n" + " --tarpit Enable TARPIT processing for TCP\n"); +} + +static int chaos_tg_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_chaos_tginfo *info = (void *)((*target)->data); + + switch (c) { + case 'd': + info->variant = XTCHAOS_DELUDE; + *flags |= F_DELUDE; + return true; + case 't': + info->variant = XTCHAOS_TARPIT; + *flags |= F_TARPIT; + return true; + } + return false; +} + +static void chaos_tg_check(unsigned int flags) +{ + if (flags == (F_DELUDE | F_TARPIT)) + /* If flags == 0x03, both were specified, which should not be. */ + exit_error(PARAMETER_PROBLEM, + "CHAOS: only one of --tarpit or --delude " + "may be specified"); +} + +static void chaos_tg_print(const void *ip, + const struct xt_entry_target *target, int numeric) +{ + const struct xt_chaos_tginfo *info = (const void *)target->data; + + switch (info->variant) { + case XTCHAOS_DELUDE: + printf("DELUDE "); + break; + case XTCHAOS_TARPIT: + printf("TARPIT "); + break; + } + return; +} + +static void chaos_tg_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_chaos_tginfo *info = (const void *)target->data; + + switch (info->variant) { + case XTCHAOS_DELUDE: + printf("--delude "); + break; + case XTCHAOS_TARPIT: + printf("--tarpit "); + break; + } + return; +} + +static struct xtables_target chaos_tg_reg = { + .version = IPTABLES_VERSION, + .name = "CHAOS", + .family = AF_INET, + .size = XT_ALIGN(sizeof(struct xt_chaos_tginfo)), + .userspacesize = XT_ALIGN(sizeof(struct xt_chaos_tginfo)), + .help = chaos_tg_help, + .parse = chaos_tg_parse, + .final_check = chaos_tg_check, + .print = chaos_tg_print, + .save = chaos_tg_save, + .extra_opts = chaos_tg_opts, +}; + +void _init(void); +void _init(void) +{ + xtables_register_target(&chaos_tg_reg); +} diff --git a/extensions/libxt_CHAOS.man b/extensions/libxt_CHAOS.man new file mode 100644 index 0000000..8e24482 --- /dev/null +++ b/extensions/libxt_CHAOS.man @@ -0,0 +1,18 @@ ++Causes confusion on the other end by doing odd things with incoming packets. ++CHAOS will randomly reply (or not) with one of its configurable subtargets: ++.TP ++\fB--delude\fR ++Use the REJECT and DELUDE targets as a base to do a sudden or deferred ++connection reset, fooling some network scanners to return non-deterministic ++(randomly open/closed) results, and in case it is deemed open, it is actually ++closed/filtered. ++.TP ++\fB--tarpit\fR ++Use the REJECT and TARPIT target as a base to hold the connection until it ++times out. This consumes conntrack entries when connection tracking is loaded ++(which usually is on most machines), and routers inbetween you and the Internet ++may fail to do their connection tracking if they have to handle more ++connections than they can. ++.PP ++The randomness factor of not replying vs. replying can be set during load-time ++of the xt_CHAOS module or during runtime in /sys/modules/xt_CHAOS/parameters. diff --git a/extensions/libxt_DELUDE.c b/extensions/libxt_DELUDE.c new file mode 100644 index 0000000..e31d001 --- /dev/null +++ b/extensions/libxt_DELUDE.c @@ -0,0 +1,47 @@ +/* + * DELUDE target for Xtables + * Copyright © CC Computer Consultants GmbH, 2006 - 2008 + * + * 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 as published by the Free Software Foundation. + */ +#include +#include +#include + +#include +#include + +static void delude_tg_help(void) +{ + printf("DELUDE takes no options\n"); +} + +static int delude_tg_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + return 0; +} + +static void delude_tg_check(unsigned int flags) +{ +} + +static struct xtables_target delude_tg_reg = { + .version = IPTABLES_VERSION, + .name = "DELUDE", + .revision = 0, + .family = AF_INET, + .size = XT_ALIGN(0), + .userspacesize = XT_ALIGN(0), + .help = delude_tg_help, + .parse = delude_tg_parse, + .final_check = delude_tg_check, +}; + +void _init(void); +void _init(void) +{ + xtables_register_target(&delude_tg_reg); +} diff --git a/extensions/libxt_DELUDE.man b/extensions/libxt_DELUDE.man new file mode 100644 index 0000000..09c7832 --- /dev/null +++ b/extensions/libxt_DELUDE.man @@ -0,0 +1,4 @@ +The DELUDE target will reply to a SYN packet with SYN-ACK, and to all other +packets with an RST. This will terminate the connection much like REJECT, but +network scanners doing TCP half-open discovery can be spoofed to make them +belive the port is open rather than closed/filtered. diff --git a/extensions/libxt_portscan.c b/extensions/libxt_portscan.c new file mode 100644 index 0000000..8cea3b6 --- /dev/null +++ b/extensions/libxt_portscan.c @@ -0,0 +1,121 @@ +/* + * portscan target for Xtables + * Copyright © CC Computer Consultants GmbH, 2006 - 2008 + * + * 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 as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include + +#include +#include +#include "xt_portscan.h" + +static const struct option portscan_mt_opts[] = { + {.name = "stealth", .has_arg = false, .val = 'x'}, + {.name = "synscan", .has_arg = false, .val = 's'}, + {.name = "cnscan", .has_arg = false, .val = 'c'}, + {.name = "grscan", .has_arg = false, .val = 'g'}, + {}, +}; + +static void portscan_mt_help(void) +{ + printf( + "portscan match options:\n" + "(Combining them will make them match by OR-logic)\n" + " --stealth Match TCP Stealth packets\n" + " --synscan Match TCP SYN scans\n" + " --cnscan Match TCP Connect scans\n" + " --grscan Match Banner Grabbing scans\n"); +} + +static int portscan_mt_parse(int c, char **argv, int invert, + unsigned int *flags, const void *entry, struct xt_entry_match **match) +{ + struct xt_portscan_mtinfo *info = (void *)((*match)->data); + + switch (c) { + case 'c': + info->match_cn = true; + return true; + case 'g': + info->match_gr = true; + return true; + case 's': + info->match_syn = true; + return true; + case 'x': + info->match_stealth = true; + return true; + } + return false; +} + +static void portscan_mt_check(unsigned int flags) +{ +} + +static void portscan_mt_print(const void *ip, + const struct xt_entry_match *match, int numeric) +{ + const struct xt_portscan_mtinfo *info = (const void *)(match->data); + const char *s = ""; + + printf("portscan "); + if (info->match_stealth) { + printf("STEALTH"); + s = ","; + } + if (info->match_syn) { + printf("%sSYNSCAN", s); + s = ","; + } + if (info->match_cn) { + printf("%sCNSCAN", s); + s = ","; + } + if (info->match_gr) + printf("%sGRSCAN", s); + printf(" "); +} + +static void portscan_mt_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_portscan_mtinfo *info = (const void *)(match->data); + + if (info->match_stealth) + printf("--stealth "); + if (info->match_syn) + printf("--synscan "); + if (info->match_cn) + printf("--cnscan "); + if (info->match_gr) + printf("--grscan "); +} + +static struct xtables_match portscan_mt_reg = { + .version = IPTABLES_VERSION, + .name = "portscan", + .revision = 0, + .family = AF_INET, + .size = XT_ALIGN(sizeof(struct xt_portscan_mtinfo)), + .userspacesize = XT_ALIGN(sizeof(struct xt_portscan_mtinfo)), + .help = portscan_mt_help, + .parse = portscan_mt_parse, + .final_check = portscan_mt_check, + .print = portscan_mt_print, + .save = portscan_mt_save, + .extra_opts = portscan_mt_opts, +}; + +void _init(void); +void _init(void) +{ + xtables_register_match(&portscan_mt_reg); +} diff --git a/extensions/libxt_portscan.man b/extensions/libxt_portscan.man new file mode 100644 index 0000000..60a4c1a --- /dev/null +++ b/extensions/libxt_portscan.man @@ -0,0 +1,27 @@ +Detects simple port scan attemps based upon the packet's contents. (This is +different from other implementations, which also try to match the rate of new +connections.) Note that an attempt is only discovered after it has been carried +out, but this information can be used in conjunction with other rules to block +the remote host's future connections. So this match module will match on the +(probably) last packet the remote side will send to your machine. +.TP +\fB--stealth\fR +Match if the packet did not belong to any known TCP connection +(Stealth/FIN/XMAS/NULL scan). +.TP +\fB--synscan\fR +Match if the connection was a TCP half-open discovery (SYN scan), i.e. the +connection was torn down after the 2nd packet in the 3-way handshake. +.TP +\fB--cnscan\fR +Match if the connection was a TCP full open discovery (connect scan), i.e. the +connection was torn down after completion of the 3-way handshake. +.TP +\fB--grscan\fR +Match if data in the connection only flew in the direction of the remote side, +e.g. if the connection was terminated after a locally running daemon sent its +identification. (e.g. openssh) +.PP +NOTE: Some clients (Windows XP for example) may do what looks like a SYN scan, +so be advised to carefully use xt_portscan in conjunction with blocking rules, +as it may lock out your very own internal network. diff --git a/extensions/xt_CHAOS.c b/extensions/xt_CHAOS.c new file mode 100644 index 0000000..a9ad67b --- /dev/null +++ b/extensions/xt_CHAOS.c @@ -0,0 +1,205 @@ +/* + * CHAOS target for netfilter + * Copyright © CC Computer Consultants GmbH, 2006 - 2007 + * Contact: Jan Engelhardt + * + * 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 as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xt_CHAOS.h" +static struct xt_match *xm_tcp; +static struct xt_target *xt_delude, *xt_reject, *xt_tarpit; +#include "compat_xtables.h" +#define PFX KBUILD_MODNAME ": " + +/* Module parameters */ +static unsigned int reject_percentage = ~0U * .01; +static unsigned int delude_percentage = ~0U * .0101; +module_param(reject_percentage, uint, S_IRUGO | S_IWUSR); +module_param(delude_percentage, uint, S_IRUGO | S_IWUSR); + +/* References to other matches/targets */ + +static int have_delude, have_tarpit; + +/* Static data for other matches/targets */ +static const struct ipt_reject_info reject_params = { + .with = ICMP_HOST_UNREACH, +}; + +static const struct xt_tcp tcp_params = { + .spts = {0, ~0}, + .dpts = {0, ~0}, +}; + +/* CHAOS functions */ +static void xt_chaos_total(const struct xt_chaos_tginfo *info, + struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, unsigned int hooknum) +{ + const struct iphdr *iph = ip_hdr(skb); + const int protoff = 4 * iph->ihl; + const int offset = ntohs(iph->frag_off) & IP_OFFSET; + typeof(xt_tarpit) destiny; + bool ret; +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 22) + int hotdrop = false; +#else + bool hotdrop = false; +#endif + + ret = xm_tcp->match(skb, in, out, xm_tcp, &tcp_params, + offset, protoff, &hotdrop); + if (!ret || hotdrop || (unsigned int)net_random() > delude_percentage) + return; + + destiny = (info->variant == XTCHAOS_TARPIT) ? xt_tarpit : xt_delude; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 23) + destiny->target(&skb, in, out, hooknum, destiny, NULL); +#else + destiny->target(skb, in, out, hooknum, destiny, NULL); +#endif + return; +} + +static unsigned int chaos_tg(struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, unsigned int hooknum, + const struct xt_target *target, const void *targinfo) +{ + /* + * Equivalent to: + * -A chaos -m statistic --mode random --probability \ + * $reject_percentage -j REJECT --reject-with host-unreach; + * -A chaos -p tcp -m statistic --mode random --probability \ + * $delude_percentage -j DELUDE; + * -A chaos -j DROP; + */ + const struct xt_chaos_tginfo *info = targinfo; + const struct iphdr *iph = ip_hdr(skb); + + if ((unsigned int)net_random() <= reject_percentage) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 23) + return xt_reject->target(&skb, in, out, hooknum, + target->__compat_target, &reject_params); +#else + return xt_reject->target(skb, in, out, hooknum, target, + &reject_params); +#endif + + /* TARPIT/DELUDE may not be called from the OUTPUT chain */ + if (iph->protocol == IPPROTO_TCP && + info->variant != XTCHAOS_NORMAL && hooknum != NF_INET_LOCAL_OUT) + xt_chaos_total(info, skb, in, out, hooknum); + + return NF_DROP; +} + +static bool chaos_tg_check(const char *tablename, const void *entry, + const struct xt_target *target, void *targinfo, unsigned int hook_mask) +{ + const struct xt_chaos_tginfo *info = targinfo; + + if (info->variant == XTCHAOS_DELUDE && !have_delude) { + printk(KERN_WARNING PFX "Error: Cannot use --delude when " + "DELUDE module not available\n"); + return false; + } + if (info->variant == XTCHAOS_TARPIT && !have_tarpit) { + printk(KERN_WARNING PFX "Error: Cannot use --tarpit when " + "TARPIT module not available\n"); + return false; + } + + return true; +} + +static struct xt_target chaos_tg_reg = { + .name = "CHAOS", + .family = AF_INET, + .table = "filter", + .hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD) | + (1 << NF_INET_LOCAL_OUT), + .target = chaos_tg, + .checkentry = chaos_tg_check, + .targetsize = sizeof(struct xt_chaos_tginfo), + .me = THIS_MODULE, +}; + +static int __init chaos_tg_init(void) +{ + int ret = -EINVAL; + + xm_tcp = xt_request_find_match(AF_INET, "tcp", 0); + if (xm_tcp == NULL) { + printk(KERN_WARNING PFX "Error: Could not find or load " + "\"tcp\" match\n"); + return -EINVAL; + } + + xt_reject = xt_request_find_target(AF_INET, "REJECT", 0); + if (xt_reject == NULL) { + printk(KERN_WARNING PFX "Error: Could not find or load " + "\"REJECT\" target\n"); + goto out2; + } + + xt_tarpit = xt_request_find_target(AF_INET, "TARPIT", 0); + have_tarpit = xt_tarpit != NULL; + if (!have_tarpit) + printk(KERN_WARNING PFX "Warning: Could not find or load " + "\"TARPIT\" target\n"); + + xt_delude = xt_request_find_target(AF_INET, "DELUDE", 0); + have_delude = xt_delude != NULL; + if (!have_delude) + printk(KERN_WARNING PFX "Warning: Could not find or load " + "\"DELUDE\" target\n"); + + if ((ret = xt_register_target(&chaos_tg_reg)) != 0) { + printk(KERN_WARNING PFX "xt_register_target returned " + "error %d\n", ret); + goto out3; + } + + return 0; + + out3: + if (have_delude) + module_put(xt_delude->me); + if (have_tarpit) + module_put(xt_tarpit->me); + module_put(xt_reject->me); + out2: + module_put(xm_tcp->me); + return ret; +} + +static void __exit chaos_tg_exit(void) +{ + xt_unregister_target(&chaos_tg_reg); + module_put(xm_tcp->me); + module_put(xt_reject->me); + if (have_delude) + module_put(xt_delude->me); + if (have_tarpit) + module_put(xt_tarpit->me); + return; +} + +module_init(chaos_tg_init); +module_exit(chaos_tg_exit); +MODULE_AUTHOR("Jan Engelhardt "); +MODULE_DESCRIPTION("Xtables: Network scan slowdown with non-deterministic results"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_CHAOS"); diff --git a/extensions/xt_CHAOS.h b/extensions/xt_CHAOS.h new file mode 100644 index 0000000..d9b74fd --- /dev/null +++ b/extensions/xt_CHAOS.h @@ -0,0 +1,14 @@ +#ifndef _LINUX_NETFILTER_XT_CHAOS_H +#define _LINUX_NETFILTER_XT_CHAOS_H 1 + +enum xt_chaos_target_variant { + XTCHAOS_NORMAL, + XTCHAOS_TARPIT, + XTCHAOS_DELUDE, +}; + +struct xt_chaos_tginfo { + uint8_t variant; +}; + +#endif /* _LINUX_NETFILTER_XT_CHAOS_H */ diff --git a/extensions/xt_DELUDE.c b/extensions/xt_DELUDE.c new file mode 100644 index 0000000..cfa78d3 --- /dev/null +++ b/extensions/xt_DELUDE.c @@ -0,0 +1,175 @@ +/* + * DELUDE target + * Copyright © CC Computer Consultants GmbH, 2007 - 2008 + * + * Based upon linux-2.6.18.5/net/ipv4/netfilter/ipt_REJECT.c: + * (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2004 Netfilter Core Team + * + * xt_DELUDE acts like REJECT, but does reply with SYN-ACK on SYN. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#ifdef CONFIG_BRIDGE_NETFILTER +# include +#endif +#include +#include "compat_xtables.h" +#define PFX KBUILD_MODNAME ": " + +static void delude_send_reset(struct sk_buff *oldskb, unsigned int hook) +{ + struct tcphdr _otcph, *tcph; + const struct tcphdr *oth; + const struct iphdr *oiph; + unsigned int addr_type; + struct sk_buff *nskb; + struct iphdr *niph; + + oiph = ip_hdr(oldskb); + + /* IP header checks: fragment. */ + if (oiph->frag_off & htons(IP_OFFSET)) + return; + + oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb), + sizeof(_otcph), &_otcph); + if (oth == NULL) + return; + + /* No RST for RST. */ + if (oth->rst) + return; + + /* Check checksum */ + if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) + return; + + nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + + LL_MAX_HEADER, GFP_ATOMIC); + if (nskb == NULL) + return; + + skb_reserve(nskb, LL_MAX_HEADER); + skb_reset_network_header(nskb); + niph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr)); + niph->version = 4; + niph->ihl = sizeof(struct iphdr) / 4; + niph->tos = 0; + niph->id = 0; + niph->frag_off = htons(IP_DF); + niph->protocol = IPPROTO_TCP; + niph->check = 0; + niph->saddr = oiph->daddr; + niph->daddr = oiph->saddr; + + tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); + memset(tcph, 0, sizeof(*tcph)); + tcph->source = oth->dest; + tcph->dest = oth->source; + tcph->doff = sizeof(struct tcphdr) / 4; + + /* DELUDE essential part */ + if (oth->syn && !oth->ack && !oth->rst && !oth->fin) { + tcph->syn = true; + tcph->seq = 0; + tcph->ack = true; + tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + + oldskb->len - ip_hdrlen(oldskb) - + (oth->doff << 2)); + } else { + tcph->rst = true; + if (!oth->ack) { + tcph->seq = 0; + tcph->ack = true; + tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + + oth->fin + oldskb->len - + ip_hdrlen(oldskb) - (oth->doff << 2)); + } else { + tcph->seq = oth->ack_seq; + tcph->ack = false; + tcph->ack_seq = 0; + } + } + + tcph->check = tcp_v4_check(sizeof(struct tcphdr), niph->saddr, + niph->daddr, csum_partial((char *)tcph, + sizeof(struct tcphdr), 0)); + + addr_type = RTN_UNSPEC; +#ifdef CONFIG_BRIDGE_NETFILTER + if (hook != NF_INET_FORWARD || (nskb->nf_bridge != NULL && + nskb->nf_bridge->mask & BRNF_BRIDGED)) +#else + if (hook != NF_INET_FORWARD) +#endif + addr_type = RTN_LOCAL; + + /* ip_route_me_harder expects skb->dst to be set */ + dst_hold(oldskb->dst); + nskb->dst = oldskb->dst; + + if (ip_route_me_harder(nskb, addr_type)) + goto free_nskb; + + niph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT); + nskb->ip_summed = CHECKSUM_NONE; + + /* "Never happens" */ + if (nskb->len > dst_mtu(nskb->dst)) + goto free_nskb; + + nf_ct_attach(nskb, oldskb); + + ip_local_out(nskb); + return; + + free_nskb: + kfree_skb(nskb); +} + +static unsigned int delude_tg(struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, unsigned int hooknum, + const struct xt_target *target, const void *targinfo) +{ + /* WARNING: This code causes reentry within iptables. + This means that the iptables jump stack is now crap. We + must return an absolute verdict. --RR */ + delude_send_reset(skb, hooknum); + return NF_DROP; +} + +static struct xt_target delude_tg_reg __read_mostly = { + .name = "DELUDE", + .revision = 0, + .family = AF_INET, + .table = "filter", + .hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD), + .proto = IPPROTO_TCP, + .target = delude_tg, + .me = THIS_MODULE, +}; + +static int __init delude_tg_init(void) +{ + return xt_register_target(&delude_tg_reg); +} + +static void __exit delude_tg_exit(void) +{ + xt_unregister_target(&delude_tg_reg); +} + +module_init(delude_tg_init); +module_exit(delude_tg_exit); +MODULE_AUTHOR("Jan Engelhardt "); +MODULE_DESCRIPTION("Xtables: Close TCP connections after handshake"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_DELUDE"); diff --git a/extensions/xt_portscan.c b/extensions/xt_portscan.c new file mode 100644 index 0000000..d975c60 --- /dev/null +++ b/extensions/xt_portscan.c @@ -0,0 +1,263 @@ +/* + * portscan match for netfilter + * Copyright © CC Computer Consultants GmbH, 2006 - 2008 + * + * 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 as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xt_portscan.h" +#include "compat_xtables.h" +#define PFX KBUILD_MODNAME ": " + +enum { + TCP_FLAGS_ALL3 = TCP_FLAG_FIN | TCP_FLAG_RST | TCP_FLAG_SYN, + TCP_FLAGS_ALL4 = TCP_FLAGS_ALL3 | TCP_FLAG_ACK, + TCP_FLAGS_ALL6 = TCP_FLAGS_ALL4 | TCP_FLAG_PSH | TCP_FLAG_URG, +}; + +/* Module parameters */ +static unsigned int + connmark_mask = ~0, + packet_mask = ~0, + mark_seen = 0x9, + mark_synrcv = 0x1, + mark_closed = 0x2, + mark_synscan = 0x3, + mark_estab1 = 0x4, + mark_estab2 = 0x5, + mark_cnscan = 0x6, + mark_grscan = 0x7, + mark_valid = 0x8; + +module_param(connmark_mask, uint, S_IRUGO | S_IWUSR); +module_param(packet_mask, uint, S_IRUGO | S_IWUSR); +module_param(mark_seen, uint, S_IRUGO | S_IWUSR); +module_param(mark_synrcv, uint, S_IRUGO | S_IWUSR); +module_param(mark_closed, uint, S_IRUGO | S_IWUSR); +module_param(mark_synscan, uint, S_IRUGO | S_IWUSR); +module_param(mark_estab1, uint, S_IRUGO | S_IWUSR); +module_param(mark_estab2, uint, S_IRUGO | S_IWUSR); +module_param(mark_cnscan, uint, S_IRUGO | S_IWUSR); +module_param(mark_grscan, uint, S_IRUGO | S_IWUSR); +module_param(mark_valid, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(connmark_mask, "only set specified bits in connection mark"); +MODULE_PARM_DESC(packet_mask, "only set specified bits in packet mark"); +MODULE_PARM_DESC(mark_seen, "nfmark value for packet-seen state"); +MODULE_PARM_DESC(mark_synrcv, "connmark value for SYN Received state"); +MODULE_PARM_DESC(mark_closed, "connmark value for closed state"); +MODULE_PARM_DESC(mark_synscan, "connmark value for SYN Scan state"); +MODULE_PARM_DESC(mark_estab1, "connmark value for Established-1 state"); +MODULE_PARM_DESC(mark_estab2, "connmark value for Established-2 state"); +MODULE_PARM_DESC(mark_cnscan, "connmark value for Connect Scan state"); +MODULE_PARM_DESC(mark_grscan, "connmark value for Grab Scan state"); +MODULE_PARM_DESC(mark_valid, "connmark value for Valid state"); + +/* TCP flag functions */ +static inline bool tflg_ack4(const struct tcphdr *th) +{ + return (tcp_flag_word(th) & TCP_FLAGS_ALL4) == TCP_FLAG_ACK; +} + +static inline bool tflg_ack6(const struct tcphdr *th) +{ + return (tcp_flag_word(th) & TCP_FLAGS_ALL6) == TCP_FLAG_ACK; +} + +static inline bool tflg_fin(const struct tcphdr *th) +{ + return (tcp_flag_word(th) & TCP_FLAGS_ALL3) == TCP_FLAG_FIN; +} + +static inline bool tflg_rst(const struct tcphdr *th) +{ + return (tcp_flag_word(th) & TCP_FLAGS_ALL3) == TCP_FLAG_RST; +} + +static inline bool tflg_rstack(const struct tcphdr *th) +{ + return (tcp_flag_word(th) & TCP_FLAGS_ALL4) == + (TCP_FLAG_ACK | TCP_FLAG_RST); +} + +static inline bool tflg_syn(const struct tcphdr *th) +{ + return (tcp_flag_word(th) & TCP_FLAGS_ALL4) == TCP_FLAG_SYN; +} + +static inline bool tflg_synack(const struct tcphdr *th) +{ + return (tcp_flag_word(th) & TCP_FLAGS_ALL4) == + (TCP_FLAG_SYN | TCP_FLAG_ACK); +} + +/* portscan functions */ +static inline bool portscan_mt_stealth(const struct tcphdr *th) +{ + /* + * "Connection refused" replies to our own probes must not be matched. + */ + if (tflg_rstack(th)) + return false; + + if (tflg_rst(th) && printk_ratelimit()) { + printk(KERN_WARNING PFX "Warning: Pure RST received\n"); + return false; + } + + /* + * -p tcp ! --syn -m conntrack --ctstate INVALID: Looking for non-start + * packets that are not associated with any connection -- this will + * match most scan types (NULL, XMAS, FIN) and ridiculous flag + * combinations (SYN-RST, SYN-FIN, SYN-FIN-RST, FIN-RST, etc.). + */ + return !tflg_syn(th); +} + +static inline unsigned int portscan_mt_full(int mark, + enum ip_conntrack_info ctstate, bool loopback, const struct tcphdr *tcph, + unsigned int payload_len) +{ + if (mark == mark_estab2) { + /* + * -m connmark --mark $ESTAB2 + */ + if (tflg_ack4(tcph) && payload_len == 0) + return mark; /* keep mark */ + else if (tflg_rst(tcph) || tflg_fin(tcph)) + return mark_grscan; + else + return mark_valid; + } else if (mark == mark_estab1) { + /* + * -m connmark --mark $ESTAB1 + */ + if (tflg_rst(tcph) || tflg_fin(tcph)) + return mark_cnscan; + else if (!loopback && tflg_ack4(tcph) && payload_len == 0) + return mark_estab2; + else + return mark_valid; + } else if (mark == mark_synrcv) { + /* + * -m connmark --mark $SYN + */ + if (loopback && tflg_synack(tcph)) + return mark; /* keep mark */ + else if (loopback && tflg_rstack(tcph)) + return mark_closed; + else if (tflg_ack6(tcph)) + return mark_estab1; + else + return mark_synscan; + } else if (ctstate == IP_CT_NEW && tflg_syn(tcph)) { + /* + * -p tcp --syn --ctstate NEW + */ + return mark_synrcv; + } + return mark; +} + +static bool portscan_mt(const struct sk_buff *skb, + const struct net_device *in, const struct net_device *out, + const struct xt_match *match, const void *matchinfo, int offset, + unsigned int protoff, bool *hotdrop) +{ + const struct xt_portscan_mtinfo *info = matchinfo; + enum ip_conntrack_info ctstate; + const struct tcphdr *tcph; + struct nf_conn *ctdata; + struct tcphdr tcph_buf; + + tcph = skb_header_pointer(skb, protoff, sizeof(tcph_buf), &tcph_buf); + if (tcph == NULL) + return false; + + /* Check for invalid packets: -m conntrack --ctstate INVALID */ + if ((ctdata = nf_ct_get(skb, &ctstate)) == NULL) { + if (info->match_stealth) + return portscan_mt_stealth(tcph); + /* + * If @ctdata is NULL, we cannot match the other scan + * types, return. + */ + return false; + } + + /* + * If -m portscan was previously applied to this packet, the rules we + * simulate must not be run through again. And for speedup, do not call + * it either when the connection is already VALID. + */ + if ((ctdata->mark & connmark_mask) == mark_valid || + (skb->mark & packet_mask) != mark_seen) { + unsigned int n; + + n = portscan_mt_full(ctdata->mark & connmark_mask, ctstate, + in == init_net__loopback_dev, tcph, + skb->len - protoff - 4 * tcph->doff); + + ctdata->mark = (ctdata->mark & ~connmark_mask) | n; + ((struct sk_buff *)skb)->mark = + (skb->mark & ~packet_mask) ^ mark_seen; + } + + return (info->match_syn && ctdata->mark == mark_synscan) || + (info->match_cn && ctdata->mark == mark_cnscan) || + (info->match_gr && ctdata->mark == mark_grscan); +} + +static bool portscan_mt_check(const char *tablename, const void *entry, + const struct xt_match *match, void *matchinfo, unsigned int hook_mask) +{ + const struct xt_portscan_mtinfo *info = matchinfo; + + if ((info->match_stealth & ~1) || (info->match_syn & ~1) || + (info->match_cn & ~1) || (info->match_gr & ~1)) { + printk(KERN_WARNING PFX "Invalid flags\n"); + return false; + } + return true; +} + +static struct xt_match portscan_mt_reg __read_mostly = { + .name = "portscan", + .revision = 0, + .family = AF_INET, + .match = portscan_mt, + .checkentry = portscan_mt_check, + .matchsize = sizeof(struct xt_portscan_mtinfo), + .proto = IPPROTO_TCP, + .me = THIS_MODULE, +}; + +static int __init portscan_mt_init(void) +{ + return xt_register_match(&portscan_mt_reg); +} + +static void __exit portscan_mt_exit(void) +{ + xt_unregister_match(&portscan_mt_reg); + return; +} + +module_init(portscan_mt_init); +module_exit(portscan_mt_exit); +MODULE_AUTHOR("Jan Engelhardt "); +MODULE_DESCRIPTION("netfilter \"portscan\" match"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_portscan"); diff --git a/extensions/xt_portscan.h b/extensions/xt_portscan.h new file mode 100644 index 0000000..949a8ae --- /dev/null +++ b/extensions/xt_portscan.h @@ -0,0 +1,8 @@ +#ifndef _LINUX_NETFILTER_XT_PORTSCAN_H +#define _LINUX_NETFILTER_XT_PORTSCAN_H 1 + +struct xt_portscan_mtinfo { + uint8_t match_stealth, match_syn, match_cn, match_gr; +}; + +#endif /* _LINUX_NETFILTER_XT_PORTSCAN_H */ diff --git a/mconfig b/mconfig index fa29bfc..afbe701 100644 --- a/mconfig +++ b/mconfig @@ -3,6 +3,9 @@ # Only "build_${name}=m" (build extensions) or "build_${name}=" # (do not build) are valid! # +build_CHAOS=m +build_DELUDE=m build_LOGMARK=m build_TARPIT=m build_TEE=m +build_portscan=m