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 | ||
