Skip to content

Commit

Permalink
netfilter: synproxy: fix BUG_ON triggered by corrupt TCP packets
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Patrick McHardy authored and Pablo Neira Ayuso committed Sep 30, 2013
1 parent d1ee4fe commit f4a87e7
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 12 deletions.
2 changes: 1 addition & 1 deletion include/net/netfilter/nf_conntrack_synproxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ struct synproxy_options {

struct tcphdr;
struct xt_synproxy_info;
extern void synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
extern bool synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
const struct tcphdr *th,
struct synproxy_options *opts);
extern unsigned int synproxy_options_size(const struct synproxy_options *opts);
Expand Down
10 changes: 7 additions & 3 deletions net/ipv4/netfilter/ipt_SYNPROXY.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,8 @@ synproxy_tg4(struct sk_buff *skb, const struct xt_action_param *par)
if (th == NULL)
return NF_DROP;

synproxy_parse_options(skb, par->thoff, th, &opts);
if (!synproxy_parse_options(skb, par->thoff, th, &opts))
return NF_DROP;

if (th->syn && !(th->ack || th->fin || th->rst)) {
/* Initial SYN from client */
Expand Down Expand Up @@ -350,7 +351,8 @@ static unsigned int ipv4_synproxy_hook(unsigned int hooknum,

/* fall through */
case TCP_CONNTRACK_SYN_SENT:
synproxy_parse_options(skb, thoff, th, &opts);
if (!synproxy_parse_options(skb, thoff, th, &opts))
return NF_DROP;

if (!th->syn && th->ack &&
CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
Expand All @@ -373,7 +375,9 @@ static unsigned int ipv4_synproxy_hook(unsigned int hooknum,
if (!th->syn || !th->ack)
break;

synproxy_parse_options(skb, thoff, th, &opts);
if (!synproxy_parse_options(skb, thoff, th, &opts))
return NF_DROP;

if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
synproxy->tsoff = opts.tsval - synproxy->its;

Expand Down
10 changes: 7 additions & 3 deletions net/ipv6/netfilter/ip6t_SYNPROXY.c
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,8 @@ synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par)
if (th == NULL)
return NF_DROP;

synproxy_parse_options(skb, par->thoff, th, &opts);
if (!synproxy_parse_options(skb, par->thoff, th, &opts))
return NF_DROP;

if (th->syn && !(th->ack || th->fin || th->rst)) {
/* Initial SYN from client */
Expand Down Expand Up @@ -372,7 +373,8 @@ static unsigned int ipv6_synproxy_hook(unsigned int hooknum,

/* fall through */
case TCP_CONNTRACK_SYN_SENT:
synproxy_parse_options(skb, thoff, th, &opts);
if (!synproxy_parse_options(skb, thoff, th, &opts))
return NF_DROP;

if (!th->syn && th->ack &&
CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
Expand All @@ -395,7 +397,9 @@ static unsigned int ipv6_synproxy_hook(unsigned int hooknum,
if (!th->syn || !th->ack)
break;

synproxy_parse_options(skb, thoff, th, &opts);
if (!synproxy_parse_options(skb, thoff, th, &opts))
return NF_DROP;

if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
synproxy->tsoff = opts.tsval - synproxy->its;

Expand Down
12 changes: 7 additions & 5 deletions net/netfilter/nf_synproxy_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@
int synproxy_net_id;
EXPORT_SYMBOL_GPL(synproxy_net_id);

void
bool
synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
const struct tcphdr *th, struct synproxy_options *opts)
{
int length = (th->doff * 4) - sizeof(*th);
u8 buf[40], *ptr;

ptr = skb_header_pointer(skb, doff + sizeof(*th), length, buf);
BUG_ON(ptr == NULL);
if (ptr == NULL)
return false;

opts->options = 0;
while (length > 0) {
Expand All @@ -41,16 +42,16 @@ synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,

switch (opcode) {
case TCPOPT_EOL:
return;
return true;
case TCPOPT_NOP:
length--;
continue;
default:
opsize = *ptr++;
if (opsize < 2)
return;
return true;
if (opsize > length)
return;
return true;

switch (opcode) {
case TCPOPT_MSS:
Expand Down Expand Up @@ -84,6 +85,7 @@ synproxy_parse_options(const struct sk_buff *skb, unsigned int doff,
length -= opsize;
}
}
return true;
}
EXPORT_SYMBOL_GPL(synproxy_parse_options);

Expand Down

0 comments on commit f4a87e7

Please sign in to comment.