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: 7021b2e1cd ("esp4: Switch to new AEAD interface")
Reported-by: Blair Steven <Blair.Steven@alliedtelesis.co.nz>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Acked-by: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Herbert Xu 2016-06-18 13:03:36 +08:00 committed by David S. Miller
parent acd43fe85b
commit 962fcef33b

View File

@ -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);
* *
* 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)
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)
{ {
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)
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)
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)
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)
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)
* 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)
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);