Commit 962fcef3 authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

esp: Fix ESN generation under UDP encapsulation

Blair Steven noticed that ESN in conjunction with UDP encapsulation
is broken because we set the temporary ESP header to the wrong spot.

This patch fixes this by first of all using the right spot, i.e.,
4 bytes off the real ESP header, and then saving this information
so that after encryption we can restore it properly.

Fixes: 7021b2e1 ("esp4: Switch to new AEAD interface")
Reported-by: default avatarBlair Steven <Blair.Steven@alliedtelesis.co.nz>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Acked-by: default avatarSteffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent acd43fe8
...@@ -23,6 +23,11 @@ struct esp_skb_cb { ...@@ -23,6 +23,11 @@ struct esp_skb_cb {
void *tmp; void *tmp;
}; };
struct esp_output_extra {
__be32 seqhi;
u32 esphoff;
};
#define ESP_SKB_CB(__skb) ((struct esp_skb_cb *)&((__skb)->cb[0])) #define ESP_SKB_CB(__skb) ((struct esp_skb_cb *)&((__skb)->cb[0]))
static u32 esp4_get_mtu(struct xfrm_state *x, int mtu); static u32 esp4_get_mtu(struct xfrm_state *x, int mtu);
...@@ -35,11 +40,11 @@ static u32 esp4_get_mtu(struct xfrm_state *x, int mtu); ...@@ -35,11 +40,11 @@ static u32 esp4_get_mtu(struct xfrm_state *x, int mtu);
* *
* TODO: Use spare space in skb for this where possible. * TODO: Use spare space in skb for this where possible.
*/ */
static void *esp_alloc_tmp(struct crypto_aead *aead, int nfrags, int seqhilen) static void *esp_alloc_tmp(struct crypto_aead *aead, int nfrags, int extralen)
{ {
unsigned int len; unsigned int len;
len = seqhilen; len = extralen;
len += crypto_aead_ivsize(aead); len += crypto_aead_ivsize(aead);
...@@ -57,15 +62,16 @@ static void *esp_alloc_tmp(struct crypto_aead *aead, int nfrags, int seqhilen) ...@@ -57,15 +62,16 @@ static void *esp_alloc_tmp(struct crypto_aead *aead, int nfrags, int seqhilen)
return kmalloc(len, GFP_ATOMIC); return kmalloc(len, GFP_ATOMIC);
} }
static inline __be32 *esp_tmp_seqhi(void *tmp) static inline void *esp_tmp_extra(void *tmp)
{ {
return PTR_ALIGN((__be32 *)tmp, __alignof__(__be32)); return PTR_ALIGN(tmp, __alignof__(struct esp_output_extra));
} }
static inline u8 *esp_tmp_iv(struct crypto_aead *aead, void *tmp, int seqhilen)
static inline u8 *esp_tmp_iv(struct crypto_aead *aead, void *tmp, int extralen)
{ {
return crypto_aead_ivsize(aead) ? return crypto_aead_ivsize(aead) ?
PTR_ALIGN((u8 *)tmp + seqhilen, PTR_ALIGN((u8 *)tmp + extralen,
crypto_aead_alignmask(aead) + 1) : tmp + seqhilen; crypto_aead_alignmask(aead) + 1) : tmp + extralen;
} }
static inline struct aead_request *esp_tmp_req(struct crypto_aead *aead, u8 *iv) static inline struct aead_request *esp_tmp_req(struct crypto_aead *aead, u8 *iv)
...@@ -99,7 +105,7 @@ static void esp_restore_header(struct sk_buff *skb, unsigned int offset) ...@@ -99,7 +105,7 @@ static void esp_restore_header(struct sk_buff *skb, unsigned int offset)
{ {
struct ip_esp_hdr *esph = (void *)(skb->data + offset); struct ip_esp_hdr *esph = (void *)(skb->data + offset);
void *tmp = ESP_SKB_CB(skb)->tmp; void *tmp = ESP_SKB_CB(skb)->tmp;
__be32 *seqhi = esp_tmp_seqhi(tmp); __be32 *seqhi = esp_tmp_extra(tmp);
esph->seq_no = esph->spi; esph->seq_no = esph->spi;
esph->spi = *seqhi; esph->spi = *seqhi;
...@@ -107,7 +113,11 @@ static void esp_restore_header(struct sk_buff *skb, unsigned int offset) ...@@ -107,7 +113,11 @@ static void esp_restore_header(struct sk_buff *skb, unsigned int offset)
static void esp_output_restore_header(struct sk_buff *skb) static void esp_output_restore_header(struct sk_buff *skb)
{ {
esp_restore_header(skb, skb_transport_offset(skb) - sizeof(__be32)); void *tmp = ESP_SKB_CB(skb)->tmp;
struct esp_output_extra *extra = esp_tmp_extra(tmp);
esp_restore_header(skb, skb_transport_offset(skb) + extra->esphoff -
sizeof(__be32));
} }
static void esp_output_done_esn(struct crypto_async_request *base, int err) static void esp_output_done_esn(struct crypto_async_request *base, int err)
...@@ -121,6 +131,7 @@ static void esp_output_done_esn(struct crypto_async_request *base, int err) ...@@ -121,6 +131,7 @@ static void esp_output_done_esn(struct crypto_async_request *base, int err)
static int esp_output(struct xfrm_state *x, struct sk_buff *skb) static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
{ {
int err; int err;
struct esp_output_extra *extra;
struct ip_esp_hdr *esph; struct ip_esp_hdr *esph;
struct crypto_aead *aead; struct crypto_aead *aead;
struct aead_request *req; struct aead_request *req;
...@@ -137,8 +148,7 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb) ...@@ -137,8 +148,7 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
int tfclen; int tfclen;
int nfrags; int nfrags;
int assoclen; int assoclen;
int seqhilen; int extralen;
__be32 *seqhi;
__be64 seqno; __be64 seqno;
/* skb is pure payload to encrypt */ /* skb is pure payload to encrypt */
...@@ -166,21 +176,21 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb) ...@@ -166,21 +176,21 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
nfrags = err; nfrags = err;
assoclen = sizeof(*esph); assoclen = sizeof(*esph);
seqhilen = 0; extralen = 0;
if (x->props.flags & XFRM_STATE_ESN) { if (x->props.flags & XFRM_STATE_ESN) {
seqhilen += sizeof(__be32); extralen += sizeof(*extra);
assoclen += seqhilen; assoclen += sizeof(__be32);
} }
tmp = esp_alloc_tmp(aead, nfrags, seqhilen); tmp = esp_alloc_tmp(aead, nfrags, extralen);
if (!tmp) { if (!tmp) {
err = -ENOMEM; err = -ENOMEM;
goto error; goto error;
} }
seqhi = esp_tmp_seqhi(tmp); extra = esp_tmp_extra(tmp);
iv = esp_tmp_iv(aead, tmp, seqhilen); iv = esp_tmp_iv(aead, tmp, extralen);
req = esp_tmp_req(aead, iv); req = esp_tmp_req(aead, iv);
sg = esp_req_sg(aead, req); sg = esp_req_sg(aead, req);
...@@ -247,8 +257,10 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb) ...@@ -247,8 +257,10 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
* encryption. * encryption.
*/ */
if ((x->props.flags & XFRM_STATE_ESN)) { if ((x->props.flags & XFRM_STATE_ESN)) {
esph = (void *)(skb_transport_header(skb) - sizeof(__be32)); extra->esphoff = (unsigned char *)esph -
*seqhi = esph->spi; skb_transport_header(skb);
esph = (struct ip_esp_hdr *)((unsigned char *)esph - 4);
extra->seqhi = esph->spi;
esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.hi); esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.hi);
aead_request_set_callback(req, 0, esp_output_done_esn, skb); aead_request_set_callback(req, 0, esp_output_done_esn, skb);
} }
...@@ -445,7 +457,7 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb) ...@@ -445,7 +457,7 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
goto out; goto out;
ESP_SKB_CB(skb)->tmp = tmp; ESP_SKB_CB(skb)->tmp = tmp;
seqhi = esp_tmp_seqhi(tmp); seqhi = esp_tmp_extra(tmp);
iv = esp_tmp_iv(aead, tmp, seqhilen); iv = esp_tmp_iv(aead, tmp, seqhilen);
req = esp_tmp_req(aead, iv); req = esp_tmp_req(aead, iv);
sg = esp_req_sg(aead, req); sg = esp_req_sg(aead, req);
......
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