aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2006-10-24 19:15:10 -0400
committerDavid S. Miller <davem@davemloft.net>2006-10-24 19:15:10 -0400
commit6d381634d213580d40d431e7664dfb45f641b884 (patch)
treea8680dd059e9a4e115d17e54d4a8dcea4d196a3e /net
parent51d8b1a65291a6956b79374b6adbbadc2263bcf6 (diff)
[NETFILTER]: Fix ip6_tables extension header bypass bug
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: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/ipv6/netfilter/ip6_tables.c11
-rw-r--r--net/ipv6/netfilter/ip6t_ah.c7
-rw-r--r--net/ipv6/netfilter/ip6t_frag.c7
-rw-r--r--net/ipv6/netfilter/ip6t_hbh.c7
-rw-r--r--net/ipv6/netfilter/ip6t_rt.c7
5 files changed, 31 insertions, 8 deletions
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index f0328c7bc1c9..53bf977cca63 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -1440,6 +1440,9 @@ static void __exit ip6_tables_fini(void)
1440 * If target header is found, its offset is set in *offset and return protocol 1440 * If target header is found, its offset is set in *offset and return protocol
1441 * number. Otherwise, return -1. 1441 * number. Otherwise, return -1.
1442 * 1442 *
1443 * If the first fragment doesn't contain the final protocol header or
1444 * NEXTHDR_NONE it is considered invalid.
1445 *
1443 * Note that non-1st fragment is special case that "the protocol number 1446 * Note that non-1st fragment is special case that "the protocol number
1444 * of last header" is "next header" field in Fragment header. In this case, 1447 * of last header" is "next header" field in Fragment header. In this case,
1445 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff 1448 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
@@ -1463,12 +1466,12 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1463 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) { 1466 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
1464 if (target < 0) 1467 if (target < 0)
1465 break; 1468 break;
1466 return -1; 1469 return -ENOENT;
1467 } 1470 }
1468 1471
1469 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); 1472 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
1470 if (hp == NULL) 1473 if (hp == NULL)
1471 return -1; 1474 return -EBADMSG;
1472 if (nexthdr == NEXTHDR_FRAGMENT) { 1475 if (nexthdr == NEXTHDR_FRAGMENT) {
1473 unsigned short _frag_off, *fp; 1476 unsigned short _frag_off, *fp;
1474 fp = skb_header_pointer(skb, 1477 fp = skb_header_pointer(skb,
@@ -1477,7 +1480,7 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1477 sizeof(_frag_off), 1480 sizeof(_frag_off),
1478 &_frag_off); 1481 &_frag_off);
1479 if (fp == NULL) 1482 if (fp == NULL)
1480 return -1; 1483 return -EBADMSG;
1481 1484
1482 _frag_off = ntohs(*fp) & ~0x7; 1485 _frag_off = ntohs(*fp) & ~0x7;
1483 if (_frag_off) { 1486 if (_frag_off) {
@@ -1488,7 +1491,7 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1488 *fragoff = _frag_off; 1491 *fragoff = _frag_off;
1489 return hp->nexthdr; 1492 return hp->nexthdr;
1490 } 1493 }
1491 return -1; 1494 return -ENOENT;
1492 } 1495 }
1493 hdrlen = 8; 1496 hdrlen = 8;
1494 } else if (nexthdr == NEXTHDR_AUTH) 1497 } else if (nexthdr == NEXTHDR_AUTH)
diff --git a/net/ipv6/netfilter/ip6t_ah.c b/net/ipv6/netfilter/ip6t_ah.c
index ec1b1608156c..46486645eb75 100644
--- a/net/ipv6/netfilter/ip6t_ah.c
+++ b/net/ipv6/netfilter/ip6t_ah.c
@@ -54,9 +54,14 @@ match(const struct sk_buff *skb,
54 const struct ip6t_ah *ahinfo = matchinfo; 54 const struct ip6t_ah *ahinfo = matchinfo;
55 unsigned int ptr; 55 unsigned int ptr;
56 unsigned int hdrlen = 0; 56 unsigned int hdrlen = 0;
57 int err;
57 58
58 if (ipv6_find_hdr(skb, &ptr, NEXTHDR_AUTH, NULL) < 0) 59 err = ipv6_find_hdr(skb, &ptr, NEXTHDR_AUTH, NULL);
60 if (err < 0) {
61 if (err != -ENOENT)
62 *hotdrop = 1;
59 return 0; 63 return 0;
64 }
60 65
61 ah = skb_header_pointer(skb, ptr, sizeof(_ah), &_ah); 66 ah = skb_header_pointer(skb, ptr, sizeof(_ah), &_ah);
62 if (ah == NULL) { 67 if (ah == NULL) {
diff --git a/net/ipv6/netfilter/ip6t_frag.c b/net/ipv6/netfilter/ip6t_frag.c
index 78d9c8b9e28a..cd22eaaccdca 100644
--- a/net/ipv6/netfilter/ip6t_frag.c
+++ b/net/ipv6/netfilter/ip6t_frag.c
@@ -52,9 +52,14 @@ match(const struct sk_buff *skb,
52 struct frag_hdr _frag, *fh; 52 struct frag_hdr _frag, *fh;
53 const struct ip6t_frag *fraginfo = matchinfo; 53 const struct ip6t_frag *fraginfo = matchinfo;
54 unsigned int ptr; 54 unsigned int ptr;
55 int err;
55 56
56 if (ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT, NULL) < 0) 57 err = ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT, NULL);
58 if (err < 0) {
59 if (err != -ENOENT)
60 *hotdrop = 1;
57 return 0; 61 return 0;
62 }
58 63
59 fh = skb_header_pointer(skb, ptr, sizeof(_frag), &_frag); 64 fh = skb_header_pointer(skb, ptr, sizeof(_frag), &_frag);
60 if (fh == NULL) { 65 if (fh == NULL) {
diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c
index d32a205e3af2..3f25babe0440 100644
--- a/net/ipv6/netfilter/ip6t_hbh.c
+++ b/net/ipv6/netfilter/ip6t_hbh.c
@@ -65,9 +65,14 @@ match(const struct sk_buff *skb,
65 u8 _opttype, *tp = NULL; 65 u8 _opttype, *tp = NULL;
66 u8 _optlen, *lp = NULL; 66 u8 _optlen, *lp = NULL;
67 unsigned int optlen; 67 unsigned int optlen;
68 int err;
68 69
69 if (ipv6_find_hdr(skb, &ptr, match->data, NULL) < 0) 70 err = ipv6_find_hdr(skb, &ptr, match->data, NULL);
71 if (err < 0) {
72 if (err != -ENOENT)
73 *hotdrop = 1;
70 return 0; 74 return 0;
75 }
71 76
72 oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh); 77 oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh);
73 if (oh == NULL) { 78 if (oh == NULL) {
diff --git a/net/ipv6/netfilter/ip6t_rt.c b/net/ipv6/netfilter/ip6t_rt.c
index bcb2e168a5bc..54d7d14134fd 100644
--- a/net/ipv6/netfilter/ip6t_rt.c
+++ b/net/ipv6/netfilter/ip6t_rt.c
@@ -58,9 +58,14 @@ match(const struct sk_buff *skb,
58 unsigned int hdrlen = 0; 58 unsigned int hdrlen = 0;
59 unsigned int ret = 0; 59 unsigned int ret = 0;
60 struct in6_addr *ap, _addr; 60 struct in6_addr *ap, _addr;
61 int err;
61 62
62 if (ipv6_find_hdr(skb, &ptr, NEXTHDR_ROUTING, NULL) < 0) 63 err = ipv6_find_hdr(skb, &ptr, NEXTHDR_ROUTING, NULL);
64 if (err < 0) {
65 if (err != -ENOENT)
66 *hotdrop = 1;
63 return 0; 67 return 0;
68 }
64 69
65 rh = skb_header_pointer(skb, ptr, sizeof(_route), &_route); 70 rh = skb_header_pointer(skb, ptr, sizeof(_route), &_route);
66 if (rh == NULL) { 71 if (rh == NULL) {