diff options
author | Patrick McHardy <kaber@trash.net> | 2013-09-30 03:51:46 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2013-09-30 06:44:38 -0400 |
commit | f4a87e7bd2eaef26a3ca25437ce8b807de2966ad (patch) | |
tree | 72359689d53c3ce656b4568f04b8ee92dd2e05f4 | |
parent | d1ee4fea0b6946dd8bc61b46db35ea80af7af34b (diff) |
netfilter: synproxy: fix BUG_ON triggered by corrupt TCP packets
TCP packets hitting the SYN proxy through the SYNPROXY target are not
validated by TCP conntrack. When th->doff is below 5, an underflow happens
when calculating the options length, causing skb_header_pointer() to
return NULL and triggering the BUG_ON().
Handle this case gracefully by checking for NULL instead of using BUG_ON().
Reported-by: Martin Topholm <mph@one.com>
Tested-by: Martin Topholm <mph@one.com>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r-- | include/net/netfilter/nf_conntrack_synproxy.h | 2 | ||||
-rw-r--r-- | net/ipv4/netfilter/ipt_SYNPROXY.c | 10 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6t_SYNPROXY.c | 10 | ||||
-rw-r--r-- | net/netfilter/nf_synproxy_core.c | 12 |
4 files changed, 22 insertions, 12 deletions
diff --git a/include/net/netfilter/nf_conntrack_synproxy.h b/include/net/netfilter/nf_conntrack_synproxy.h index 806f54a290d6..f572f313d6f1 100644 --- a/include/net/netfilter/nf_conntrack_synproxy.h +++ b/include/net/netfilter/nf_conntrack_synproxy.h | |||
@@ -56,7 +56,7 @@ struct synproxy_options { | |||
56 | 56 | ||
57 | struct tcphdr; | 57 | struct tcphdr; |
58 | struct xt_synproxy_info; | 58 | struct xt_synproxy_info; |
59 | extern void synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, | 59 | extern bool synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, |
60 | const struct tcphdr *th, | 60 | const struct tcphdr *th, |
61 | struct synproxy_options *opts); | 61 | struct synproxy_options *opts); |
62 | extern unsigned int synproxy_options_size(const struct synproxy_options *opts); | 62 | extern unsigned int synproxy_options_size(const struct synproxy_options *opts); |
diff --git a/net/ipv4/netfilter/ipt_SYNPROXY.c b/net/ipv4/netfilter/ipt_SYNPROXY.c index 67e17dcda65e..b6346bf2fde3 100644 --- a/net/ipv4/netfilter/ipt_SYNPROXY.c +++ b/net/ipv4/netfilter/ipt_SYNPROXY.c | |||
@@ -267,7 +267,8 @@ synproxy_tg4(struct sk_buff *skb, const struct xt_action_param *par) | |||
267 | if (th == NULL) | 267 | if (th == NULL) |
268 | return NF_DROP; | 268 | return NF_DROP; |
269 | 269 | ||
270 | synproxy_parse_options(skb, par->thoff, th, &opts); | 270 | if (!synproxy_parse_options(skb, par->thoff, th, &opts)) |
271 | return NF_DROP; | ||
271 | 272 | ||
272 | if (th->syn && !(th->ack || th->fin || th->rst)) { | 273 | if (th->syn && !(th->ack || th->fin || th->rst)) { |
273 | /* Initial SYN from client */ | 274 | /* Initial SYN from client */ |
@@ -350,7 +351,8 @@ static unsigned int ipv4_synproxy_hook(unsigned int hooknum, | |||
350 | 351 | ||
351 | /* fall through */ | 352 | /* fall through */ |
352 | case TCP_CONNTRACK_SYN_SENT: | 353 | case TCP_CONNTRACK_SYN_SENT: |
353 | synproxy_parse_options(skb, thoff, th, &opts); | 354 | if (!synproxy_parse_options(skb, thoff, th, &opts)) |
355 | return NF_DROP; | ||
354 | 356 | ||
355 | if (!th->syn && th->ack && | 357 | if (!th->syn && th->ack && |
356 | CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { | 358 | CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { |
@@ -373,7 +375,9 @@ static unsigned int ipv4_synproxy_hook(unsigned int hooknum, | |||
373 | if (!th->syn || !th->ack) | 375 | if (!th->syn || !th->ack) |
374 | break; | 376 | break; |
375 | 377 | ||
376 | synproxy_parse_options(skb, thoff, th, &opts); | 378 | if (!synproxy_parse_options(skb, thoff, th, &opts)) |
379 | return NF_DROP; | ||
380 | |||
377 | if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) | 381 | if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) |
378 | synproxy->tsoff = opts.tsval - synproxy->its; | 382 | synproxy->tsoff = opts.tsval - synproxy->its; |
379 | 383 | ||
diff --git a/net/ipv6/netfilter/ip6t_SYNPROXY.c b/net/ipv6/netfilter/ip6t_SYNPROXY.c index 19cfea8dbcaa..2748b042da72 100644 --- a/net/ipv6/netfilter/ip6t_SYNPROXY.c +++ b/net/ipv6/netfilter/ip6t_SYNPROXY.c | |||
@@ -282,7 +282,8 @@ synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par) | |||
282 | if (th == NULL) | 282 | if (th == NULL) |
283 | return NF_DROP; | 283 | return NF_DROP; |
284 | 284 | ||
285 | synproxy_parse_options(skb, par->thoff, th, &opts); | 285 | if (!synproxy_parse_options(skb, par->thoff, th, &opts)) |
286 | return NF_DROP; | ||
286 | 287 | ||
287 | if (th->syn && !(th->ack || th->fin || th->rst)) { | 288 | if (th->syn && !(th->ack || th->fin || th->rst)) { |
288 | /* Initial SYN from client */ | 289 | /* Initial SYN from client */ |
@@ -372,7 +373,8 @@ static unsigned int ipv6_synproxy_hook(unsigned int hooknum, | |||
372 | 373 | ||
373 | /* fall through */ | 374 | /* fall through */ |
374 | case TCP_CONNTRACK_SYN_SENT: | 375 | case TCP_CONNTRACK_SYN_SENT: |
375 | synproxy_parse_options(skb, thoff, th, &opts); | 376 | if (!synproxy_parse_options(skb, thoff, th, &opts)) |
377 | return NF_DROP; | ||
376 | 378 | ||
377 | if (!th->syn && th->ack && | 379 | if (!th->syn && th->ack && |
378 | CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { | 380 | CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { |
@@ -395,7 +397,9 @@ static unsigned int ipv6_synproxy_hook(unsigned int hooknum, | |||
395 | if (!th->syn || !th->ack) | 397 | if (!th->syn || !th->ack) |
396 | break; | 398 | break; |
397 | 399 | ||
398 | synproxy_parse_options(skb, thoff, th, &opts); | 400 | if (!synproxy_parse_options(skb, thoff, th, &opts)) |
401 | return NF_DROP; | ||
402 | |||
399 | if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) | 403 | if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) |
400 | synproxy->tsoff = opts.tsval - synproxy->its; | 404 | synproxy->tsoff = opts.tsval - synproxy->its; |
401 | 405 | ||
diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index 6fd967c6278c..cdf4567ba9b3 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c | |||
@@ -24,7 +24,7 @@ | |||
24 | int synproxy_net_id; | 24 | int synproxy_net_id; |
25 | EXPORT_SYMBOL_GPL(synproxy_net_id); | 25 | EXPORT_SYMBOL_GPL(synproxy_net_id); |
26 | 26 | ||
27 | void | 27 | bool |
28 | synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, | 28 | synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, |
29 | const struct tcphdr *th, struct synproxy_options *opts) | 29 | const struct tcphdr *th, struct synproxy_options *opts) |
30 | { | 30 | { |
@@ -32,7 +32,8 @@ synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, | |||
32 | u8 buf[40], *ptr; | 32 | u8 buf[40], *ptr; |
33 | 33 | ||
34 | ptr = skb_header_pointer(skb, doff + sizeof(*th), length, buf); | 34 | ptr = skb_header_pointer(skb, doff + sizeof(*th), length, buf); |
35 | BUG_ON(ptr == NULL); | 35 | if (ptr == NULL) |
36 | return false; | ||
36 | 37 | ||
37 | opts->options = 0; | 38 | opts->options = 0; |
38 | while (length > 0) { | 39 | while (length > 0) { |
@@ -41,16 +42,16 @@ synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, | |||
41 | 42 | ||
42 | switch (opcode) { | 43 | switch (opcode) { |
43 | case TCPOPT_EOL: | 44 | case TCPOPT_EOL: |
44 | return; | 45 | return true; |
45 | case TCPOPT_NOP: | 46 | case TCPOPT_NOP: |
46 | length--; | 47 | length--; |
47 | continue; | 48 | continue; |
48 | default: | 49 | default: |
49 | opsize = *ptr++; | 50 | opsize = *ptr++; |
50 | if (opsize < 2) | 51 | if (opsize < 2) |
51 | return; | 52 | return true; |
52 | if (opsize > length) | 53 | if (opsize > length) |
53 | return; | 54 | return true; |
54 | 55 | ||
55 | switch (opcode) { | 56 | switch (opcode) { |
56 | case TCPOPT_MSS: | 57 | case TCPOPT_MSS: |
@@ -84,6 +85,7 @@ synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, | |||
84 | length -= opsize; | 85 | length -= opsize; |
85 | } | 86 | } |
86 | } | 87 | } |
88 | return true; | ||
87 | } | 89 | } |
88 | EXPORT_SYMBOL_GPL(synproxy_parse_options); | 90 | EXPORT_SYMBOL_GPL(synproxy_parse_options); |
89 | 91 | ||