Commit 7862e912 authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

[AH6]: Disallow mutable bits after AH header.

As we discussed before, mutable headers should not be allowed after
the AH header.  In fact, this appears to be the intention of RFC 2402.
It is further clarified in section 3.1.1 of

http://www.ietf.org/internet-drafts/draft-ietf-ipsec-rfc2402bis-07.txt

This allows us to simplify the code in ah6.c.  As a result, this also
fixes the following issues:

* Dependence on skb->h in ah6_output().
* Bogus clearing of auth_data of 2nd AH header in ipv6_clear_mutable_options().
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@redhat.com>
parent 44a492a9
...@@ -74,34 +74,29 @@ static int zero_out_mutable_opts(struct ipv6_opt_hdr *opthdr) ...@@ -74,34 +74,29 @@ static int zero_out_mutable_opts(struct ipv6_opt_hdr *opthdr)
return 0; return 0;
} }
static int ipv6_clear_mutable_options(struct sk_buff *skb, u16 *nh_offset, int dir) static int ipv6_clear_mutable_options(struct sk_buff *skb,
unsigned int hdr_len)
{ {
u16 offset = sizeof(struct ipv6hdr); u16 offset = sizeof(struct ipv6hdr);
struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
unsigned int packet_len = skb->tail - skb->nh.raw;
u8 nexthdr = skb->nh.ipv6h->nexthdr; u8 nexthdr = skb->nh.ipv6h->nexthdr;
u8 nextnexthdr = 0;
*nh_offset = ((unsigned char *)&skb->nh.ipv6h->nexthdr) - skb->nh.raw; while (offset + 1 <= hdr_len) {
while (offset + 1 <= packet_len) {
switch (nexthdr) { switch (nexthdr) {
case NEXTHDR_HOP: case NEXTHDR_HOP:
*nh_offset = offset;
offset += ipv6_optlen(exthdr); offset += ipv6_optlen(exthdr);
if (!zero_out_mutable_opts(exthdr)) { if (!zero_out_mutable_opts(exthdr)) {
LIMIT_NETDEBUG( LIMIT_NETDEBUG(
printk(KERN_WARNING "overrun hopopts\n")); printk(KERN_WARNING "overrun hopopts\n"));
return 0; return -EINVAL;
} }
nexthdr = exthdr->nexthdr; nexthdr = exthdr->nexthdr;
exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
break; break;
case NEXTHDR_ROUTING: case NEXTHDR_ROUTING:
*nh_offset = offset;
offset += ipv6_optlen(exthdr); offset += ipv6_optlen(exthdr);
((struct ipv6_rt_hdr*)exthdr)->segments_left = 0; ((struct ipv6_rt_hdr*)exthdr)->segments_left = 0;
nexthdr = exthdr->nexthdr; nexthdr = exthdr->nexthdr;
...@@ -109,39 +104,22 @@ static int ipv6_clear_mutable_options(struct sk_buff *skb, u16 *nh_offset, int d ...@@ -109,39 +104,22 @@ static int ipv6_clear_mutable_options(struct sk_buff *skb, u16 *nh_offset, int d
break; break;
case NEXTHDR_DEST: case NEXTHDR_DEST:
*nh_offset = offset;
offset += ipv6_optlen(exthdr); offset += ipv6_optlen(exthdr);
if (!zero_out_mutable_opts(exthdr)) { if (!zero_out_mutable_opts(exthdr)) {
LIMIT_NETDEBUG( LIMIT_NETDEBUG(
printk(KERN_WARNING "overrun destopt\n")); printk(KERN_WARNING "overrun destopt\n"));
return 0; return -EINVAL;
} }
nexthdr = exthdr->nexthdr; nexthdr = exthdr->nexthdr;
exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
break; break;
case NEXTHDR_AUTH:
if (dir == XFRM_POLICY_OUT) {
memset(((struct ipv6_auth_hdr*)exthdr)->auth_data, 0,
(((struct ipv6_auth_hdr*)exthdr)->hdrlen - 1) << 2);
}
if (exthdr->nexthdr == NEXTHDR_DEST) {
offset += (((struct ipv6_auth_hdr*)exthdr)->hdrlen + 2) << 2;
exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
nextnexthdr = exthdr->nexthdr;
if (!zero_out_mutable_opts(exthdr)) {
LIMIT_NETDEBUG(
printk(KERN_WARNING "overrun destopt\n"));
return 0;
}
}
return nexthdr;
default : default :
return nexthdr; return 0;
} }
} }
return nexthdr; return 0;
} }
int ah6_output(struct sk_buff **pskb) int ah6_output(struct sk_buff **pskb)
...@@ -153,7 +131,6 @@ int ah6_output(struct sk_buff **pskb) ...@@ -153,7 +131,6 @@ int ah6_output(struct sk_buff **pskb)
struct ipv6hdr *iph = NULL; struct ipv6hdr *iph = NULL;
struct ip_auth_hdr *ah; struct ip_auth_hdr *ah;
struct ah_data *ahp; struct ah_data *ahp;
u16 nh_offset = 0;
u8 nexthdr; u8 nexthdr;
if ((*pskb)->ip_summed == CHECKSUM_HW) { if ((*pskb)->ip_summed == CHECKSUM_HW) {
...@@ -184,7 +161,11 @@ int ah6_output(struct sk_buff **pskb) ...@@ -184,7 +161,11 @@ int ah6_output(struct sk_buff **pskb)
ah = (struct ip_auth_hdr*)((*pskb)->nh.ipv6h+1); ah = (struct ip_auth_hdr*)((*pskb)->nh.ipv6h+1);
ah->nexthdr = IPPROTO_IPV6; ah->nexthdr = IPPROTO_IPV6;
} else { } else {
hdr_len = (*pskb)->h.raw - (*pskb)->nh.raw; u8 *prevhdr;
hdr_len = ip6_find_1stfragopt(*pskb, &prevhdr);
nexthdr = *prevhdr;
*prevhdr = IPPROTO_AH;
iph = kmalloc(hdr_len, GFP_ATOMIC); iph = kmalloc(hdr_len, GFP_ATOMIC);
if (!iph) { if (!iph) {
err = -ENOMEM; err = -ENOMEM;
...@@ -192,13 +173,12 @@ int ah6_output(struct sk_buff **pskb) ...@@ -192,13 +173,12 @@ int ah6_output(struct sk_buff **pskb)
} }
memcpy(iph, (*pskb)->data, hdr_len); memcpy(iph, (*pskb)->data, hdr_len);
(*pskb)->nh.ipv6h = (struct ipv6hdr*)skb_push(*pskb, x->props.header_len); (*pskb)->nh.ipv6h = (struct ipv6hdr*)skb_push(*pskb, x->props.header_len);
iph->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr));
memcpy((*pskb)->nh.ipv6h, iph, hdr_len); memcpy((*pskb)->nh.ipv6h, iph, hdr_len);
nexthdr = ipv6_clear_mutable_options(*pskb, &nh_offset, XFRM_POLICY_OUT); err = ipv6_clear_mutable_options(*pskb, hdr_len);
if (nexthdr == 0) if (err)
goto error_free_iph; goto error_free_iph;
(*pskb)->nh.raw[nh_offset] = IPPROTO_AH;
(*pskb)->nh.ipv6h->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr));
ah = (struct ip_auth_hdr*)((*pskb)->nh.raw+hdr_len); ah = (struct ip_auth_hdr*)((*pskb)->nh.raw+hdr_len);
(*pskb)->h.raw = (unsigned char*) ah; (*pskb)->h.raw = (unsigned char*) ah;
ah->nexthdr = nexthdr; ah->nexthdr = nexthdr;
...@@ -229,8 +209,6 @@ int ah6_output(struct sk_buff **pskb) ...@@ -229,8 +209,6 @@ int ah6_output(struct sk_buff **pskb)
IP6_ECN_clear((*pskb)->nh.ipv6h); IP6_ECN_clear((*pskb)->nh.ipv6h);
} else { } else {
memcpy((*pskb)->nh.ipv6h, iph, hdr_len); memcpy((*pskb)->nh.ipv6h, iph, hdr_len);
(*pskb)->nh.raw[nh_offset] = IPPROTO_AH;
(*pskb)->nh.ipv6h->payload_len = htons((*pskb)->len - sizeof(struct ipv6hdr));
kfree (iph); kfree (iph);
} }
...@@ -259,7 +237,6 @@ int ah6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_bu ...@@ -259,7 +237,6 @@ int ah6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_bu
* Before process AH * Before process AH
* [IPv6][Ext1][Ext2][AH][Dest][Payload] * [IPv6][Ext1][Ext2][AH][Dest][Payload]
* |<-------------->| hdr_len * |<-------------->| hdr_len
* |<------------------------>| cleared_hlen
* *
* To erase AH: * To erase AH:
* Keeping copy of cleared headers. After AH processing, * Keeping copy of cleared headers. After AH processing,
...@@ -276,7 +253,6 @@ int ah6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_bu ...@@ -276,7 +253,6 @@ int ah6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_bu
unsigned char *tmp_hdr = NULL; unsigned char *tmp_hdr = NULL;
u16 hdr_len; u16 hdr_len;
u16 ah_hlen; u16 ah_hlen;
u16 cleared_hlen;
u16 nh_offset = 0; u16 nh_offset = 0;
u8 nexthdr = 0; u8 nexthdr = 0;
u8 *prevhdr; u8 *prevhdr;
...@@ -291,17 +267,10 @@ int ah6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_bu ...@@ -291,17 +267,10 @@ int ah6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_bu
goto out; goto out;
hdr_len = skb->data - skb->nh.raw; hdr_len = skb->data - skb->nh.raw;
cleared_hlen = hdr_len;
ah = (struct ipv6_auth_hdr*)skb->data; ah = (struct ipv6_auth_hdr*)skb->data;
ahp = x->data; ahp = x->data;
nexthdr = ah->nexthdr; nexthdr = ah->nexthdr;
ah_hlen = (ah->hdrlen + 2) << 2; ah_hlen = (ah->hdrlen + 2) << 2;
cleared_hlen += ah_hlen;
if (nexthdr == NEXTHDR_DEST) {
struct ipv6_opt_hdr *dsthdr = (struct ipv6_opt_hdr*)(skb->data + ah_hlen);
cleared_hlen += ipv6_optlen(dsthdr);
}
if (ah_hlen != XFRM_ALIGN8(sizeof(struct ipv6_auth_hdr) + ahp->icv_full_len) && if (ah_hlen != XFRM_ALIGN8(sizeof(struct ipv6_auth_hdr) + ahp->icv_full_len) &&
ah_hlen != XFRM_ALIGN8(sizeof(struct ipv6_auth_hdr) + ahp->icv_trunc_len)) ah_hlen != XFRM_ALIGN8(sizeof(struct ipv6_auth_hdr) + ahp->icv_trunc_len))
...@@ -310,11 +279,12 @@ int ah6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_bu ...@@ -310,11 +279,12 @@ int ah6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_bu
if (!pskb_may_pull(skb, ah_hlen)) if (!pskb_may_pull(skb, ah_hlen))
goto out; goto out;
tmp_hdr = kmalloc(cleared_hlen, GFP_ATOMIC); tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC);
if (!tmp_hdr) if (!tmp_hdr)
goto out; goto out;
memcpy(tmp_hdr, skb->nh.raw, cleared_hlen); memcpy(tmp_hdr, skb->nh.raw, hdr_len);
ipv6_clear_mutable_options(skb, &nh_offset, XFRM_POLICY_IN); if (ipv6_clear_mutable_options(skb, hdr_len))
goto out;
skb->nh.ipv6h->priority = 0; skb->nh.ipv6h->priority = 0;
skb->nh.ipv6h->flow_lbl[0] = 0; skb->nh.ipv6h->flow_lbl[0] = 0;
skb->nh.ipv6h->flow_lbl[1] = 0; skb->nh.ipv6h->flow_lbl[1] = 0;
...@@ -338,11 +308,6 @@ int ah6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_bu ...@@ -338,11 +308,6 @@ int ah6_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_bu
skb->nh.raw = skb_pull(skb, ah_hlen); skb->nh.raw = skb_pull(skb, ah_hlen);
memcpy(skb->nh.raw, tmp_hdr, hdr_len); memcpy(skb->nh.raw, tmp_hdr, hdr_len);
if (nexthdr == NEXTHDR_DEST) {
memcpy(skb->nh.raw + hdr_len,
tmp_hdr + hdr_len + ah_hlen,
cleared_hlen - hdr_len - ah_hlen);
}
prevhdr = (u8*)(skb->nh.raw + nh_offset); prevhdr = (u8*)(skb->nh.raw + nh_offset);
*prevhdr = nexthdr; *prevhdr = nexthdr;
skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
......
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