Commit 71ffe9c7 authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso

netfilter: xt_TCPMSS: fix handling of malformed TCP header and options

Make sure the packet has enough room for the TCP header and
that it is not malformed.

While at it, store tcph->doff*4 in a variable, as it is used
several times.

This patch also fixes a possible off by one in case of malformed
TCP options.
Reported-by: default avatarJulian Anastasov <ja@ssi.bg>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent a661b43f
...@@ -52,7 +52,8 @@ tcpmss_mangle_packet(struct sk_buff *skb, ...@@ -52,7 +52,8 @@ tcpmss_mangle_packet(struct sk_buff *skb,
{ {
const struct xt_tcpmss_info *info = par->targinfo; const struct xt_tcpmss_info *info = par->targinfo;
struct tcphdr *tcph; struct tcphdr *tcph;
unsigned int tcplen, i; int len, tcp_hdrlen;
unsigned int i;
__be16 oldval; __be16 oldval;
u16 newmss; u16 newmss;
u8 *opt; u8 *opt;
...@@ -64,11 +65,14 @@ tcpmss_mangle_packet(struct sk_buff *skb, ...@@ -64,11 +65,14 @@ tcpmss_mangle_packet(struct sk_buff *skb,
if (!skb_make_writable(skb, skb->len)) if (!skb_make_writable(skb, skb->len))
return -1; return -1;
tcplen = skb->len - tcphoff; len = skb->len - tcphoff;
if (len < (int)sizeof(struct tcphdr))
return -1;
tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
tcp_hdrlen = tcph->doff * 4;
/* Header cannot be larger than the packet */ if (len < tcp_hdrlen)
if (tcplen < tcph->doff*4)
return -1; return -1;
if (info->mss == XT_TCPMSS_CLAMP_PMTU) { if (info->mss == XT_TCPMSS_CLAMP_PMTU) {
...@@ -87,9 +91,8 @@ tcpmss_mangle_packet(struct sk_buff *skb, ...@@ -87,9 +91,8 @@ tcpmss_mangle_packet(struct sk_buff *skb,
newmss = info->mss; newmss = info->mss;
opt = (u_int8_t *)tcph; opt = (u_int8_t *)tcph;
for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) { for (i = sizeof(struct tcphdr); i <= tcp_hdrlen - TCPOLEN_MSS; i += optlen(opt, i)) {
if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS && if (opt[i] == TCPOPT_MSS && opt[i+1] == TCPOLEN_MSS) {
opt[i+1] == TCPOLEN_MSS) {
u_int16_t oldmss; u_int16_t oldmss;
oldmss = (opt[i+2] << 8) | opt[i+3]; oldmss = (opt[i+2] << 8) | opt[i+3];
...@@ -112,9 +115,10 @@ tcpmss_mangle_packet(struct sk_buff *skb, ...@@ -112,9 +115,10 @@ tcpmss_mangle_packet(struct sk_buff *skb,
} }
/* There is data after the header so the option can't be added /* There is data after the header so the option can't be added
without moving it, and doing so may make the SYN packet * without moving it, and doing so may make the SYN packet
itself too large. Accept the packet unmodified instead. */ * itself too large. Accept the packet unmodified instead.
if (tcplen > tcph->doff*4) */
if (len > tcp_hdrlen)
return 0; return 0;
/* /*
...@@ -143,10 +147,10 @@ tcpmss_mangle_packet(struct sk_buff *skb, ...@@ -143,10 +147,10 @@ tcpmss_mangle_packet(struct sk_buff *skb,
newmss = min(newmss, (u16)1220); newmss = min(newmss, (u16)1220);
opt = (u_int8_t *)tcph + sizeof(struct tcphdr); opt = (u_int8_t *)tcph + sizeof(struct tcphdr);
memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr)); memmove(opt + TCPOLEN_MSS, opt, len - sizeof(struct tcphdr));
inet_proto_csum_replace2(&tcph->check, skb, inet_proto_csum_replace2(&tcph->check, skb,
htons(tcplen), htons(tcplen + TCPOLEN_MSS), 1); htons(len), htons(len + TCPOLEN_MSS), 1);
opt[0] = TCPOPT_MSS; opt[0] = TCPOPT_MSS;
opt[1] = TCPOLEN_MSS; opt[1] = TCPOLEN_MSS;
opt[2] = (newmss & 0xff00) >> 8; opt[2] = (newmss & 0xff00) >> 8;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment