[PLUTO-security]
[Fwd: Netfilter Security Advisory: Conntrack list_del() DoS]
Tom aka 'Dido'
tom at pluto.linux.it
Mon Aug 4 12:00:45 CEST 2003
-----Forwarded Message-----
> From: Netfilter Core Team <coreteam a netfilter.org>
> To: Netfilter Announcement List <netfilter-announce a lists.netfilter.org>, Netfilter Mailinglist <netfilter a lists.netfilter.org>, Netfilter Development Mailinglist <netfilter-devel a lists.netfilter.org>
> Subject: [SECURITY] Netfilter Security Advisory: Conntrack list_del() DoS
> Date: 02 Aug 2003 16:33:41 +0200
>
> Netfilter Core Team Security Advisory
>
> CVE: CAN-2003-0187
>
> Subject:
>
> Netfilter / Connection Tracking Remote DoS
>
> Released:
>
> 01 Aug 2003
>
> Effects:
>
> Any remote user may be able to DoS a machine with netfilter connection
> tracking when running a specific version of the Linux kernel.
>
> Estimated Severity:
> High.
>
> Systems Affected:
>
> Linux 2.4.20 kernels (kernels <= 2.4.19 and >= 2.4.21 NOT affected)
> CONFIG_IP_NF_CONNTRACK enabled, or the ip_conntrack module loaded.
>
> Solution:
>
> BEST: Upgrade to Linux kernels 2.4.21 (stable), or apply the patch below.
>
> OR: Do not use connection tracking on 2.4.20 based systems.
>
> Details:
>
> The 2.4.20 kernel introduced a change in the behaviour of the generic
> linked list support. The connection tracking core relies on the old
> behaviour to identify 'UNCONFIRMED' connections.
>
> 'UNCONFIRMED' means we've seen traffic only in one direction, but not
> in the other. Since connection tracking was unable to identify such
> connections correctly anymore, they've been assigned a very high
> timeout.
>
> The patch below changes the connection tracking core to no longer rely
> on any specific behaviour of the linux linked listed API.
>
> Vendor Statement:
>
> Red Hat: Patches for this issue were first introduced in RHSA-2003:17
> Others: unknown
>
> Credits:
> The problem was found, and the fix implemented by the Netfilter Core Team.
>
> Contact:
> coreteam a netfilter.org
>
> diff -urN --exclude-from=diff.exclude linux-2.4.20-base/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.4.20-del/include/linux/netfilter_ipv4/ip_conntrack.h
> --- linux-2.4.20-base/include/linux/netfilter_ipv4/ip_conntrack.h Fri Nov 29 00:53:15 2002
> +++ linux-2.4.20-del/include/linux/netfilter_ipv4/ip_conntrack.h Fri Feb 21 17:01:38 2003
> @@ -6,6 +6,7 @@
>
> #include <linux/config.h>
> #include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
> +#include <linux/bitops.h>
> #include <asm/atomic.h>
>
> enum ip_conntrack_info
> @@ -41,6 +42,10 @@
> /* Conntrack should never be early-expired. */
> IPS_ASSURED_BIT = 2,
> IPS_ASSURED = (1 << IPS_ASSURED_BIT),
> +
> + /* Connection is confirmed: originating packet has left box */
> + IPS_CONFIRMED_BIT = 3,
> + IPS_CONFIRMED = (1 << IPS_CONFIRMED_BIT),
> };
>
> #include <linux/netfilter_ipv4/ip_conntrack_tcp.h>
> @@ -159,7 +164,7 @@
> struct ip_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX];
>
> /* Have we seen traffic both ways yet? (bitset) */
> - volatile unsigned long status;
> + unsigned long status;
>
> /* Timer function; drops refcnt when it goes off. */
> struct timer_list timeout;
> @@ -254,7 +259,7 @@
> /* It's confirmed if it is, or has been in the hash table. */
> static inline int is_confirmed(struct ip_conntrack *ct)
> {
> - return ct->tuplehash[IP_CT_DIR_ORIGINAL].list.next != NULL;
> + return test_bit(IPS_CONFIRMED_BIT, &ct->status);
> }
>
> extern unsigned int ip_conntrack_htable_size;
> diff -urN --exclude-from=diff.exclude linux-2.4.20-base/net/ipv4/netfilter/ip_conntrack_core.c linux-2.4.20-del/net/ipv4/netfilter/ip_conntrack_core.c
> --- linux-2.4.20-base/net/ipv4/netfilter/ip_conntrack_core.c Tue Feb 18 17:08:21 2003
> +++ linux-2.4.20-del/net/ipv4/netfilter/ip_conntrack_core.c Fri Feb 21 17:01:39 2003
> @@ -292,9 +292,6 @@
> {
> DEBUGP("clean_from_lists(%p)\n", ct);
> MUST_BE_WRITE_LOCKED(&ip_conntrack_lock);
> - /* Remove from both hash lists: must not NULL out next ptrs,
> - otherwise we'll look unconfirmed. Fortunately, LIST_DELETE
> - doesn't do this. --RR */
> LIST_DELETE(&ip_conntrack_hash
> [hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple)],
> &ct->tuplehash[IP_CT_DIR_ORIGINAL]);
> @@ -467,6 +464,7 @@
> ct->timeout.expires += jiffies;
> add_timer(&ct->timeout);
> atomic_inc(&ct->ct_general.use);
> + set_bit(IPS_CONFIRMED_BIT, &ct->status);
> WRITE_UNLOCK(&ip_conntrack_lock);
> return NF_ACCEPT;
> }
> @@ -585,7 +583,7 @@
> connection. Too bad: we're in trouble anyway. */
> static inline int unreplied(const struct ip_conntrack_tuple_hash *i)
> {
> - return !(i->ctrack->status & IPS_ASSURED);
> + return !(test_bit(IPS_ASSURED_BIT, &i->ctrack->status));
> }
>
> static int early_drop(struct list_head *chain)
> @@ -720,7 +718,7 @@
> conntrack, expected);
> /* Welcome, Mr. Bond. We've been expecting you... */
> IP_NF_ASSERT(master_ct(conntrack));
> - conntrack->status = IPS_EXPECTED;
> + __set_bit(IPS_EXPECTED_BIT, &conntrack->status);
> conntrack->master = expected;
> expected->sibling = conntrack;
> LIST_DELETE(&ip_conntrack_expect_list, expected);
> @@ -768,11 +766,11 @@
> *set_reply = 1;
> } else {
> /* Once we've had two way comms, always ESTABLISHED. */
> - if (h->ctrack->status & IPS_SEEN_REPLY) {
> + if (test_bit(IPS_SEEN_REPLY_BIT, &h->ctrack->status)) {
> DEBUGP("ip_conntrack_in: normal packet for %p\n",
> h->ctrack);
> *ctinfo = IP_CT_ESTABLISHED;
> - } else if (h->ctrack->status & IPS_EXPECTED) {
> + } else if (test_bit(IPS_EXPECTED_BIT, &h->ctrack->status)) {
> DEBUGP("ip_conntrack_in: related packet for %p\n",
> h->ctrack);
> *ctinfo = IP_CT_RELATED;
> diff -urN --exclude-from=diff.exclude linux-2.4.20-base/net/ipv4/netfilter/ip_conntrack_proto_tcp.c linux-2.4.20-del/net/ipv4/netfilter/ip_conntrack_proto_tcp.c
> --- linux-2.4.20-base/net/ipv4/netfilter/ip_conntrack_proto_tcp.c Tue Feb 18 17:07:26 2003
> +++ linux-2.4.20-del/net/ipv4/netfilter/ip_conntrack_proto_tcp.c Fri Feb 21 17:03:35 2003
> @@ -192,7 +192,7 @@
> have an established connection: this is a fairly common
> problem case, so we can delete the conntrack
> immediately. --RR */
> - if (!(conntrack->status & IPS_SEEN_REPLY) && tcph->rst) {
> + if (!test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) && tcph->rst) {
> WRITE_UNLOCK(&tcp_lock);
> if (del_timer(&conntrack->timeout))
> conntrack->timeout.function((unsigned long)conntrack);
> diff -urN --exclude-from=diff.exclude linux-2.4.20-base/net/ipv4/netfilter/ip_conntrack_proto_udp.c linux-2.4.20-del/net/ipv4/netfilter/ip_conntrack_proto_udp.c
> --- linux-2.4.20-base/net/ipv4/netfilter/ip_conntrack_proto_udp.c Fri Nov 29 00:53:15 2002
> +++ linux-2.4.20-del/net/ipv4/netfilter/ip_conntrack_proto_udp.c Fri Feb 21 17:01:39 2003
> @@ -51,7 +51,7 @@
> {
> /* If we've seen traffic both ways, this is some kind of UDP
> stream. Extend timeout. */
> - if (conntrack->status & IPS_SEEN_REPLY) {
> + if (test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) {
> ip_ct_refresh(conntrack, UDP_STREAM_TIMEOUT);
> /* Also, more likely to be important, and not a probe */
> set_bit(IPS_ASSURED_BIT, &conntrack->status);
> diff -urN --exclude-from=diff.exclude linux-2.4.20-base/net/ipv4/netfilter/ip_conntrack_standalone.c linux-2.4.20-del/net/ipv4/netfilter/ip_conntrack_standalone.c
> --- linux-2.4.20-base/net/ipv4/netfilter/ip_conntrack_standalone.c Fri Nov 29 00:53:15 2002
> +++ linux-2.4.20-del/net/ipv4/netfilter/ip_conntrack_standalone.c Fri Feb 21 21:10:37 2003
> @@ -77,7 +77,7 @@
> }
>
> static unsigned int
> -print_conntrack(char *buffer, const struct ip_conntrack *conntrack)
> +print_conntrack(char *buffer, struct ip_conntrack *conntrack)
> {
> unsigned int len;
> struct ip_conntrack_protocol *proto
> @@ -95,12 +95,12 @@
> len += print_tuple(buffer + len,
> &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
> proto);
> - if (!(conntrack->status & IPS_SEEN_REPLY))
> + if (!(test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)))
> len += sprintf(buffer + len, "[UNREPLIED] ");
> len += print_tuple(buffer + len,
> &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple,
> proto);
> - if (conntrack->status & IPS_ASSURED)
> + if (test_bit(IPS_ASSURED_BIT, &conntrack->status))
> len += sprintf(buffer + len, "[ASSURED] ");
> len += sprintf(buffer + len, "use=%u ",
> atomic_read(&conntrack->ct_general.use));
>
> --
> - Harald Welte <laforge a netfilter.org> http://www.netfilter.org/
> ============================================================================
> "Fragmentation is like classful addressing -- an interesting early
> architectural error that shows how much experimentation was going
> on while IP was being designed." -- Paul Vixie
More information about the pluto-security
mailing list