]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
[NETFILTER]: Fix ip6_tables extension header bypass bug (CVE-2006-4572)
authorPatrick McHardy <kaber@trash.net>
Sun, 5 Nov 2006 08:04:23 +0000 (09:04 +0100)
committerAdrian Bunk <bunk@stusta.de>
Sun, 5 Nov 2006 08:04:23 +0000 (09:04 +0100)
As reported by Mark Dowd <Mark_Dowd@McAfee.com>, ip6_tables is susceptible
to a fragmentation attack causing false negatives on extension header
matches.

When extension headers occur in the non-first fragment after the fragment
header (possibly with an incorrect nexthdr value in the fragment header)
a rule looking for this extension header will never match.

Drop fragments that are at offset 0 and don't contain the final protocol
header regardless of the ruleset, since this should not happen normally.
Since all extension headers are before the protocol header this makes sure
an extension header is either not present or in the first fragment, where
we can properly parse it.

With help from Yasuyuki KOZAKAI <yasuyuki.kozakai@toshiba.co.jp>.

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
net/ipv6/netfilter/ip6_tables.c
net/ipv6/netfilter/ip6t_ah.c
net/ipv6/netfilter/ip6t_dst.c
net/ipv6/netfilter/ip6t_frag.c
net/ipv6/netfilter/ip6t_hbh.c
net/ipv6/netfilter/ip6t_rt.c

index a3e3da11b1c1a0d11420bdcf785997e24d823b92..e2bb9acd241c9fb9906d8aeec8416b564c4c163d 100644 (file)
@@ -1447,6 +1447,9 @@ static void __exit fini(void)
  * If target header is found, its offset is set in *offset and return protocol
  * number. Otherwise, return -1.
  *
+ * If the first fragment doesn't contain the final protocol header or
+ * NEXTHDR_NONE it is considered invalid.
+ *
  * Note that non-1st fragment is special case that "the protocol number
  * of last header" is "next header" field in Fragment header. In this case,
  * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
@@ -1470,12 +1473,12 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
                if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
                        if (target < 0)
                                break;
-                       return -1;
+                       return -ENOENT;
                }
 
                hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
                if (hp == NULL)
-                       return -1;
+                       return -EBADMSG;
                if (nexthdr == NEXTHDR_FRAGMENT) {
                        unsigned short _frag_off, *fp;
                        fp = skb_header_pointer(skb,
@@ -1484,7 +1487,7 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
                                                sizeof(_frag_off),
                                                &_frag_off);
                        if (fp == NULL)
-                               return -1;
+                               return -EBADMSG;
 
                        _frag_off = ntohs(*fp) & ~0x7;
                        if (_frag_off) {
@@ -1495,7 +1498,7 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
                                                *fragoff = _frag_off;
                                        return hp->nexthdr;
                                }
-                               return -1;
+                               return -ENOENT;
                        }
                        hdrlen = 8;
                } else if (nexthdr == NEXTHDR_AUTH)
index 219a30365dff27dcef63046176e9f8a6ef19d41c..002b8a1945e91702ce446c7b52ed9080d396d4d5 100644 (file)
@@ -53,9 +53,14 @@ match(const struct sk_buff *skb,
        const struct ip6t_ah *ahinfo = matchinfo;
        unsigned int ptr;
        unsigned int hdrlen = 0;
+       int err;
 
-       if (ipv6_find_hdr(skb, &ptr, NEXTHDR_AUTH, NULL) < 0)
+       err = ipv6_find_hdr(skb, &ptr, NEXTHDR_AUTH, NULL);
+       if (err < 0) {
+               if (err != -ENOENT)
+                       *hotdrop = 1;
                return 0;
+       }
 
        ah = skb_header_pointer(skb, ptr, sizeof(_ah), &_ah);
        if (ah == NULL) {
index b4c153a53500f242b3fae2d67f67ef3ec22dbe53..24412282002875419b13d08982b9997c438c1cf9 100644 (file)
@@ -69,13 +69,18 @@ match(const struct sk_buff *skb,
        u8 _opttype, *tp = NULL;
        u8 _optlen, *lp = NULL;
        unsigned int optlen;
+       int err;
 
 #if HOPBYHOP
-       if (ipv6_find_hdr(skb, &ptr, NEXTHDR_HOP, NULL) < 0)
+       err = ipv6_find_hdr(skb, &ptr, NEXTHDR_HOP, NULL);
 #else
-       if (ipv6_find_hdr(skb, &ptr, NEXTHDR_DEST, NULL) < 0)
+       err = ipv6_find_hdr(skb, &ptr, NEXTHDR_DEST, NULL);
 #endif
+       if (err < 0) {
+               if (err != -ENOENT)
+                       *hotdrop = 1;
                return 0;
+       }
 
        oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh);
        if (oh == NULL) {
index 4c14125a0e26d18ec775bcfcfe0b27661d98713e..185f583f8c876926922b84d61a4cfb649df86469 100644 (file)
@@ -51,9 +51,14 @@ match(const struct sk_buff *skb,
        struct frag_hdr _frag, *fh;
        const struct ip6t_frag *fraginfo = matchinfo;
        unsigned int ptr;
+       int err;
 
-       if (ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT, NULL) < 0)
+       err = ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT, NULL);
+       if (err < 0) {
+               if (err != -ENOENT)
+                       *hotdrop = 1;
                return 0;
+       }
 
        fh = skb_header_pointer(skb, ptr, sizeof(_frag), &_frag);
        if (fh == NULL) {
index 37a8474a7e0c911e1508aafc5a8e4f6f4d86c4a1..af56eafd4a46b72e03439cee565acac9421c4e06 100644 (file)
@@ -69,13 +69,18 @@ match(const struct sk_buff *skb,
        u8 _opttype, *tp = NULL;
        u8 _optlen, *lp = NULL;
        unsigned int optlen;
+       int err;
 
 #if HOPBYHOP
-       if (ipv6_find_hdr(skb, &ptr, NEXTHDR_HOP, NULL) < 0)
+       err = ipv6_find_hdr(skb, &ptr, NEXTHDR_HOP, NULL);
 #else
-       if (ipv6_find_hdr(skb, &ptr, NEXTHDR_DEST, NULL) < 0)
+       err = ipv6_find_hdr(skb, &ptr, NEXTHDR_DEST, NULL);
 #endif
+       if (err < 0) {
+               if (err != -ENOENT)
+                       *hotdrop = 1;
                return 0;
+       }
 
        oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh);
        if (oh == NULL) {
index 8f82476dc89e6a46d6d644fb95abbcf568718ea1..537b311e6b6f7b5c5ba130be747807cbeb22a355 100644 (file)
@@ -57,9 +57,14 @@ match(const struct sk_buff *skb,
        unsigned int hdrlen = 0;
        unsigned int ret = 0;
        struct in6_addr *ap, _addr;
+       int err;
 
-       if (ipv6_find_hdr(skb, &ptr, NEXTHDR_ROUTING, NULL) < 0)
+       err = ipv6_find_hdr(skb, &ptr, NEXTHDR_ROUTING, NULL);
+       if (err < 0) {
+               if (err != -ENOENT)
+                       *hotdrop = 1;
                return 0;
+       }
 
        rh = skb_header_pointer(skb, ptr, sizeof(_route), &_route);
        if (rh == NULL) {