From: Thomas Graf <tgraf@redhat.com> Date: Tue, 31 Mar 2009 20:52:35 +0200 Subject: [net] iptables NAT port randomisation Message-id: 20090331185235.GA19481@plip.localdomain O-Subject: [RHEL5.4 PATCH] net: iptables NAT port randomisation (bz459943) Bugzilla: 459943 RH-Acked-by: David Miller <davem@redhat.com> RH-Acked-by: Neil Horman <nhorman@redhat.com> Hey all - This patch adds port randomisation to the netfilter NAT code. The feature was indirectly requested by the security team with the option to ignore the request if found not worthwhile. I considered it a good idea for 5.4 even though not having randomised NAT ports is not a security issue at all. Fixes BZ459943. diff --git a/drivers/char/random.c b/drivers/char/random.c index ced24b4..f9578f3 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1584,6 +1584,7 @@ u32 secure_ipv4_port_ephemeral(__u32 saddr, __u32 daddr, __u16 dport) return half_md4_transform(hash, keyptr->secret); } +EXPORT_SYMBOL_GPL(secure_ipv4_port_ephemeral); #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) u32 secure_ipv6_port_ephemeral(const __u32 *saddr, const __u32 *daddr, __u16 dport) diff --git a/include/linux/netfilter_ipv4/ip_nat.h b/include/linux/netfilter_ipv4/ip_nat.h index e9f5ed1..7812698 100644 --- a/include/linux/netfilter_ipv4/ip_nat.h +++ b/include/linux/netfilter_ipv4/ip_nat.h @@ -16,6 +16,7 @@ enum ip_nat_manip_type #define IP_NAT_RANGE_MAP_IPS 1 #define IP_NAT_RANGE_PROTO_SPECIFIED 2 +#define IP_NAT_RANGE_PROTO_RANDOM 4 /* add randomness to "port" selection */ /* NAT sequence number modifications */ struct ip_nat_seq { diff --git a/net/ipv4/netfilter/ip_nat_core.c b/net/ipv4/netfilter/ip_nat_core.c index 1741d55..2aa9131 100644 --- a/net/ipv4/netfilter/ip_nat_core.c +++ b/net/ipv4/netfilter/ip_nat_core.c @@ -262,8 +262,9 @@ get_unique_tuple(struct ip_conntrack_tuple *tuple, if (maniptype == IP_NAT_MANIP_SRC) { if (find_appropriate_src(orig_tuple, tuple, range)) { DEBUGP("get_unique_tuple: Found current src map\n"); - if (!ip_nat_used_tuple(tuple, conntrack)) - return; + if (!(range->flags & IP_NAT_RANGE_PROTO_RANDOM)) + if (!ip_nat_used_tuple(tuple, conntrack)) + return; } } @@ -277,6 +278,13 @@ get_unique_tuple(struct ip_conntrack_tuple *tuple, proto = ip_nat_proto_find_get(orig_tuple->dst.protonum); + /* Change protocol info to have some randomization */ + if (range->flags & IP_NAT_RANGE_PROTO_RANDOM) { + proto->unique_tuple(tuple, range, maniptype, conntrack); + ip_nat_proto_put(proto); + return; + } + /* Only bother mapping if it's not already in range and unique */ if ((!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) || proto->in_range(tuple, maniptype, &range->min, &range->max)) diff --git a/net/ipv4/netfilter/ip_nat_proto_tcp.c b/net/ipv4/netfilter/ip_nat_proto_tcp.c index 35d1d90..0879678 100644 --- a/net/ipv4/netfilter/ip_nat_proto_tcp.c +++ b/net/ipv4/netfilter/ip_nat_proto_tcp.c @@ -8,6 +8,7 @@ #include <linux/types.h> #include <linux/init.h> +#include <linux/random.h> #include <linux/netfilter.h> #include <linux/ip.h> #include <linux/tcp.h> @@ -75,6 +76,13 @@ tcp_unique_tuple(struct ip_conntrack_tuple *tuple, range_size = ntohs(range->max.tcp.port) - min + 1; } + /* Start from random port to avoid prediction */ + if (range->flags & IP_NAT_RANGE_PROTO_RANDOM) + port = secure_ipv4_port_ephemeral(tuple->src.ip, tuple->dst.ip, + maniptype == IP_NAT_MANIP_SRC + ? tuple->dst.u.tcp.port + : tuple->src.u.tcp.port); + for (i = 0; i < range_size; i++, port++) { *portptr = htons(min + port % range_size); if (!ip_nat_used_tuple(tuple, conntrack)) { diff --git a/net/ipv4/netfilter/ip_nat_proto_udp.c b/net/ipv4/netfilter/ip_nat_proto_udp.c index 802ceb7..f061cb7 100644 --- a/net/ipv4/netfilter/ip_nat_proto_udp.c +++ b/net/ipv4/netfilter/ip_nat_proto_udp.c @@ -8,6 +8,7 @@ #include <linux/types.h> #include <linux/init.h> +#include <linux/random.h> #include <linux/netfilter.h> #include <linux/ip.h> #include <linux/udp.h> @@ -74,6 +75,13 @@ udp_unique_tuple(struct ip_conntrack_tuple *tuple, range_size = ntohs(range->max.udp.port) - min + 1; } + /* Start from random port to avoid prediction */ + if (range->flags & IP_NAT_RANGE_PROTO_RANDOM) + port = secure_ipv4_port_ephemeral(tuple->src.ip, tuple->dst.ip, + maniptype == IP_NAT_MANIP_SRC + ? tuple->dst.u.udp.port + : tuple->src.u.udp.port); + for (i = 0; i < range_size; i++, port++) { *portptr = htons(min + port % range_size); if (!ip_nat_used_tuple(tuple, conntrack)) diff --git a/net/ipv4/netfilter/ip_nat_rule.c b/net/ipv4/netfilter/ip_nat_rule.c index 699eef7..5a9b52f 100644 --- a/net/ipv4/netfilter/ip_nat_rule.c +++ b/net/ipv4/netfilter/ip_nat_rule.c @@ -201,6 +201,10 @@ static int ipt_dnat_checkentry(const char *tablename, printk("DNAT: multiple ranges no longer supported\n"); return 0; } + if (mr->range[0].flags & IP_NAT_RANGE_PROTO_RANDOM) { + printk("DNAT: port randomization not supported\n"); + return 0; + } return 1; }