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: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
Pablo Neira Ayuso 2013-07-25 10:37:49 +02:00
parent a661b43fd0
commit 71ffe9c77d
1 changed files with 17 additions and 13 deletions

View File

@ -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,
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;
tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); if (len < (int)sizeof(struct tcphdr))
return -1;
/* Header cannot be larger than the packet */ tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
if (tcplen < tcph->doff*4) tcp_hdrlen = tcph->doff * 4;
if (len < tcp_hdrlen)
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,
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,
} }
/* 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,
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;