Commit 8f5e762b authored by James Morris's avatar James Morris Committed by Sridhar Samudrala

[IPSEC] Generic ICV handling for ESP.

parent 08bec5c8
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
#define MAX_SG_ONSTACK 4 #define MAX_SG_ONSTACK 4
typedef void (icv_update_fn_t)(struct crypto_tfm *,
struct scatterlist *, unsigned int);
/* BUGS: /* BUGS:
* - we assume replay seqno is always present. * - we assume replay seqno is always present.
*/ */
...@@ -30,28 +33,24 @@ struct esp_data ...@@ -30,28 +33,24 @@ struct esp_data
struct crypto_tfm *tfm; /* crypto handle */ struct crypto_tfm *tfm; /* crypto handle */
} conf; } conf;
/* Integrity. It is active when authlen != 0 */ /* Integrity. It is active when icv_full_len != 0 */
struct { struct {
u8 *key; /* Key */ u8 *key; /* Key */
int key_len; /* Length of the key */ int key_len; /* Length of the key */
u8 *work_digest; u8 *work_icv;
/* authlen is length of trailer containing auth token. int icv_full_len;
* If it is not zero it is assumed to be int icv_trunc_len;
* >= crypto_tfm_alg_digestsize(atfm) */ void (*icv)(struct esp_data*,
int authlen; struct sk_buff *skb,
void (*digest)(struct esp_data*, int offset, int len, u8 *icv);
struct sk_buff *skb,
int offset,
int len,
u8 *digest);
struct crypto_tfm *tfm; struct crypto_tfm *tfm;
} auth; } auth;
}; };
/* Move to common area: it is shared with AH. */ /* Move to common area: it is shared with AH. */
void skb_digest_walk(const struct sk_buff *skb, struct crypto_tfm *tfm, void skb_icv_walk(const struct sk_buff *skb, struct crypto_tfm *tfm,
int offset, int len) int offset, int len, icv_update_fn_t icv_update)
{ {
int start = skb->len - skb->data_len; int start = skb->len - skb->data_len;
int i, copy = start - offset; int i, copy = start - offset;
...@@ -66,7 +65,7 @@ void skb_digest_walk(const struct sk_buff *skb, struct crypto_tfm *tfm, ...@@ -66,7 +65,7 @@ void skb_digest_walk(const struct sk_buff *skb, struct crypto_tfm *tfm,
sg.offset = (unsigned long)(skb->data + offset) % PAGE_SIZE; sg.offset = (unsigned long)(skb->data + offset) % PAGE_SIZE;
sg.length = copy; sg.length = copy;
crypto_hmac_update(tfm, &sg, 1); icv_update(tfm, &sg, 1);
if ((len -= copy) == 0) if ((len -= copy) == 0)
return; return;
...@@ -89,7 +88,7 @@ void skb_digest_walk(const struct sk_buff *skb, struct crypto_tfm *tfm, ...@@ -89,7 +88,7 @@ void skb_digest_walk(const struct sk_buff *skb, struct crypto_tfm *tfm,
sg.offset = frag->page_offset + offset-start; sg.offset = frag->page_offset + offset-start;
sg.length = copy; sg.length = copy;
crypto_hmac_update(tfm, &sg, 1); icv_update(tfm, &sg, 1);
if (!(len -= copy)) if (!(len -= copy))
return; return;
...@@ -110,7 +109,7 @@ void skb_digest_walk(const struct sk_buff *skb, struct crypto_tfm *tfm, ...@@ -110,7 +109,7 @@ void skb_digest_walk(const struct sk_buff *skb, struct crypto_tfm *tfm,
if ((copy = end - offset) > 0) { if ((copy = end - offset) > 0) {
if (copy > len) if (copy > len)
copy = len; copy = len;
skb_digest_walk(list, tfm, offset-start, copy); skb_icv_walk(list, tfm, offset-start, copy, icv_update);
if ((len -= copy) == 0) if ((len -= copy) == 0)
return; return;
offset += copy; offset += copy;
...@@ -198,12 +197,13 @@ esp_hmac_digest(struct esp_data *esp, struct sk_buff *skb, int offset, ...@@ -198,12 +197,13 @@ esp_hmac_digest(struct esp_data *esp, struct sk_buff *skb, int offset,
int len, u8 *auth_data) int len, u8 *auth_data)
{ {
struct crypto_tfm *tfm = esp->auth.tfm; struct crypto_tfm *tfm = esp->auth.tfm;
char *digest = esp->auth.work_digest; char *icv = esp->auth.work_icv;
memset(auth_data, 0, esp->auth.icv_trunc_len);
crypto_hmac_init(tfm, esp->auth.key, &esp->auth.key_len); crypto_hmac_init(tfm, esp->auth.key, &esp->auth.key_len);
skb_digest_walk(skb, tfm, offset, len); skb_icv_walk(skb, tfm, offset, len, crypto_hmac_update);
crypto_hmac_final(tfm, esp->auth.key, &esp->auth.key_len, digest); crypto_hmac_final(tfm, esp->auth.key, &esp->auth.key_len, icv);
memcpy(auth_data, digest, esp->auth.authlen); memcpy(auth_data, icv, esp->auth.icv_trunc_len);
} }
/* Check that skb data bits are writable. If they are not, copy data /* Check that skb data bits are writable. If they are not, copy data
...@@ -327,6 +327,7 @@ int esp_output(struct sk_buff *skb) ...@@ -327,6 +327,7 @@ int esp_output(struct sk_buff *skb)
struct sk_buff *trailer; struct sk_buff *trailer;
int blksize; int blksize;
int clen; int clen;
int alen;
int nfrags; int nfrags;
union { union {
struct iphdr iph; struct iphdr iph;
...@@ -357,13 +358,14 @@ int esp_output(struct sk_buff *skb) ...@@ -357,13 +358,14 @@ int esp_output(struct sk_buff *skb)
clen = skb->len; clen = skb->len;
esp = x->data; esp = x->data;
alen = esp->auth.icv_trunc_len;
tfm = esp->conf.tfm; tfm = esp->conf.tfm;
blksize = crypto_tfm_alg_blocksize(tfm); blksize = crypto_tfm_alg_blocksize(tfm);
clen = (clen + 2 + blksize-1)&~(blksize-1); clen = (clen + 2 + blksize-1)&~(blksize-1);
if (esp->conf.padlen) if (esp->conf.padlen)
clen = (clen + esp->conf.padlen-1)&~(esp->conf.padlen-1); clen = (clen + esp->conf.padlen-1)&~(esp->conf.padlen-1);
if ((nfrags = skb_cow_data(skb, clen-skb->len+esp->auth.authlen, &trailer)) < 0) if ((nfrags = skb_cow_data(skb, clen-skb->len+alen, &trailer)) < 0)
goto error; goto error;
/* Fill padding... */ /* Fill padding... */
...@@ -383,7 +385,7 @@ int esp_output(struct sk_buff *skb) ...@@ -383,7 +385,7 @@ int esp_output(struct sk_buff *skb)
top_iph->ihl = 5; top_iph->ihl = 5;
top_iph->version = 4; top_iph->version = 4;
top_iph->tos = iph->tos; /* DS disclosed */ top_iph->tos = iph->tos; /* DS disclosed */
top_iph->tot_len = htons(skb->len + esp->auth.authlen); top_iph->tot_len = htons(skb->len + alen);
top_iph->frag_off = iph->frag_off&htons(IP_DF); top_iph->frag_off = iph->frag_off&htons(IP_DF);
if (!(top_iph->frag_off)) if (!(top_iph->frag_off))
ip_select_ident(top_iph, dst, 0); ip_select_ident(top_iph, dst, 0);
...@@ -398,7 +400,7 @@ int esp_output(struct sk_buff *skb) ...@@ -398,7 +400,7 @@ int esp_output(struct sk_buff *skb)
top_iph = (struct iphdr*)skb_push(skb, iph->ihl*4); top_iph = (struct iphdr*)skb_push(skb, iph->ihl*4);
memcpy(top_iph, &tmp_iph, iph->ihl*4); memcpy(top_iph, &tmp_iph, iph->ihl*4);
iph = &tmp_iph.iph; iph = &tmp_iph.iph;
top_iph->tot_len = htons(skb->len + esp->auth.authlen); top_iph->tot_len = htons(skb->len + alen);
top_iph->protocol = IPPROTO_ESP; top_iph->protocol = IPPROTO_ESP;
top_iph->check = 0; top_iph->check = 0;
top_iph->frag_off = iph->frag_off; top_iph->frag_off = iph->frag_off;
...@@ -431,10 +433,10 @@ int esp_output(struct sk_buff *skb) ...@@ -431,10 +433,10 @@ int esp_output(struct sk_buff *skb)
crypto_cipher_get_iv(tfm, esp->conf.ivec, crypto_tfm_alg_ivsize(tfm)); crypto_cipher_get_iv(tfm, esp->conf.ivec, crypto_tfm_alg_ivsize(tfm));
} }
if (esp->auth.authlen) { if (esp->auth.icv_full_len) {
esp->auth.digest(esp, skb, (u8*)esph-skb->data, esp->auth.icv(esp, skb, (u8*)esph-skb->data,
8+esp->conf.ivlen+clen, trailer->tail); 8+esp->conf.ivlen+clen, trailer->tail);
pskb_put(skb, trailer, esp->auth.authlen); pskb_put(skb, trailer, alen);
} }
ip_send_check(top_iph); ip_send_check(top_iph);
...@@ -455,6 +457,11 @@ int esp_output(struct sk_buff *skb) ...@@ -455,6 +457,11 @@ int esp_output(struct sk_buff *skb)
return err; return err;
} }
/*
* Note: detecting truncated vs. non-truncated authentication data is very
* expensive, so we only support truncated data, which is the recommended
* and common case.
*/
int esp_input(struct xfrm_state *x, struct sk_buff *skb) int esp_input(struct xfrm_state *x, struct sk_buff *skb)
{ {
struct iphdr *iph; struct iphdr *iph;
...@@ -462,7 +469,8 @@ int esp_input(struct xfrm_state *x, struct sk_buff *skb) ...@@ -462,7 +469,8 @@ int esp_input(struct xfrm_state *x, struct sk_buff *skb)
struct esp_data *esp = x->data; struct esp_data *esp = x->data;
struct sk_buff *trailer; struct sk_buff *trailer;
int blksize = crypto_tfm_alg_blocksize(esp->conf.tfm); int blksize = crypto_tfm_alg_blocksize(esp->conf.tfm);
int elen = skb->len - 8 - esp->conf.ivlen - esp->auth.authlen; int alen = esp->auth.icv_trunc_len;
int elen = skb->len - 8 - esp->conf.ivlen - alen;
int nfrags; int nfrags;
if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr))) if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr)))
...@@ -472,17 +480,16 @@ int esp_input(struct xfrm_state *x, struct sk_buff *skb) ...@@ -472,17 +480,16 @@ int esp_input(struct xfrm_state *x, struct sk_buff *skb)
goto out; goto out;
/* If integrity check is required, do this. */ /* If integrity check is required, do this. */
if (esp->auth.authlen) { if (esp->auth.icv_full_len) {
u8 sum[esp->auth.authlen]; u8 sum[esp->auth.icv_full_len];
u8 sum1[esp->auth.authlen]; u8 sum1[alen];
esp->auth.digest(esp, skb, 0, skb->len-esp->auth.authlen, sum); esp->auth.icv(esp, skb, 0, skb->len-alen, sum);
if (skb_copy_bits(skb, skb->len-esp->auth.authlen, sum1, if (skb_copy_bits(skb, skb->len-alen, sum1, alen))
esp->auth.authlen))
BUG(); BUG();
if (unlikely(memcmp(sum, sum1, esp->auth.authlen))) { if (unlikely(memcmp(sum, sum1, alen))) {
x->stats.integrity_failed++; x->stats.integrity_failed++;
goto out; goto out;
} }
...@@ -517,8 +524,7 @@ int esp_input(struct xfrm_state *x, struct sk_buff *skb) ...@@ -517,8 +524,7 @@ int esp_input(struct xfrm_state *x, struct sk_buff *skb)
if (unlikely(sg != sgbuf)) if (unlikely(sg != sgbuf))
kfree(sg); kfree(sg);
if (skb_copy_bits(skb, skb->len-esp->auth.authlen-2, if (skb_copy_bits(skb, skb->len-alen-2, nexthdr, 2))
nexthdr, 2))
BUG(); BUG();
padlen = nexthdr[0]; padlen = nexthdr[0];
...@@ -528,7 +534,7 @@ int esp_input(struct xfrm_state *x, struct sk_buff *skb) ...@@ -528,7 +534,7 @@ int esp_input(struct xfrm_state *x, struct sk_buff *skb)
/* ... check padding bits here. Silly. :-) */ /* ... check padding bits here. Silly. :-) */
iph->protocol = nexthdr[1]; iph->protocol = nexthdr[1];
pskb_trim(skb, skb->len - esp->auth.authlen - padlen - 2); pskb_trim(skb, skb->len - alen - padlen - 2);
memcpy(workbuf, skb->nh.raw, iph->ihl*4); memcpy(workbuf, skb->nh.raw, iph->ihl*4);
skb->h.raw = skb_pull(skb, 8 + esp->conf.ivlen); skb->h.raw = skb_pull(skb, 8 + esp->conf.ivlen);
skb->nh.raw += 8 + esp->conf.ivlen; skb->nh.raw += 8 + esp->conf.ivlen;
...@@ -556,7 +562,7 @@ static u32 esp4_get_max_size(struct xfrm_state *x, int mtu) ...@@ -556,7 +562,7 @@ static u32 esp4_get_max_size(struct xfrm_state *x, int mtu)
if (esp->conf.padlen) if (esp->conf.padlen)
mtu = (mtu + esp->conf.padlen-1)&~(esp->conf.padlen-1); mtu = (mtu + esp->conf.padlen-1)&~(esp->conf.padlen-1);
return mtu + x->props.header_len + esp->auth.authlen; return mtu + x->props.header_len + esp->auth.icv_full_len;
} }
void esp4_err(struct sk_buff *skb, u32 info) void esp4_err(struct sk_buff *skb, u32 info)
...@@ -593,9 +599,9 @@ void esp_destroy(struct xfrm_state *x) ...@@ -593,9 +599,9 @@ void esp_destroy(struct xfrm_state *x)
crypto_free_tfm(esp->auth.tfm); crypto_free_tfm(esp->auth.tfm);
esp->auth.tfm = NULL; esp->auth.tfm = NULL;
} }
if (esp->auth.work_digest) { if (esp->auth.work_icv) {
kfree(esp->auth.work_digest); kfree(esp->auth.work_icv);
esp->auth.work_digest = NULL; esp->auth.work_icv = NULL;
} }
} }
...@@ -603,11 +609,12 @@ int esp_init_state(struct xfrm_state *x, void *args) ...@@ -603,11 +609,12 @@ int esp_init_state(struct xfrm_state *x, void *args)
{ {
struct esp_data *esp = NULL; struct esp_data *esp = NULL;
/* null auth and encryption can have zero length keys */
if (x->aalg) { if (x->aalg) {
if (x->aalg->alg_key_len == 0 || x->aalg->alg_key_len > 512) if (x->aalg->alg_key_len > 512)
goto error; goto error;
} }
if (x->ealg == NULL || x->ealg->alg_key_len == 0) if (x->ealg == NULL)
goto error; goto error;
esp = kmalloc(sizeof(*esp), GFP_KERNEL); esp = kmalloc(sizeof(*esp), GFP_KERNEL);
...@@ -617,21 +624,32 @@ int esp_init_state(struct xfrm_state *x, void *args) ...@@ -617,21 +624,32 @@ int esp_init_state(struct xfrm_state *x, void *args)
memset(esp, 0, sizeof(*esp)); memset(esp, 0, sizeof(*esp));
if (x->aalg) { if (x->aalg) {
int digestsize; struct xfrm_algo_desc *aalg_desc;
esp->auth.key = x->aalg->alg_key; esp->auth.key = x->aalg->alg_key;
esp->auth.key_len = (x->aalg->alg_key_len+7)/8; esp->auth.key_len = (x->aalg->alg_key_len+7)/8;
esp->auth.tfm = crypto_alloc_tfm(x->aalg->alg_name, 0); esp->auth.tfm = crypto_alloc_tfm(x->aalg->alg_name, 0);
if (esp->auth.tfm == NULL) if (esp->auth.tfm == NULL)
goto error; goto error;
esp->auth.digest = esp_hmac_digest; esp->auth.icv = esp_hmac_digest;
digestsize = crypto_tfm_alg_digestsize(esp->auth.tfm);
/* XXX RFC2403 and RFC 2404 truncate auth to 96 bit */ aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name);
esp->auth.authlen = 12; BUG_ON(!aalg_desc);
if (esp->auth.authlen > digestsize) /* XXX */
BUG(); if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
esp->auth.work_digest = kmalloc(digestsize, GFP_KERNEL); crypto_tfm_alg_digestsize(esp->auth.tfm)) {
if (!esp->auth.work_digest) printk(KERN_INFO "ESP: %s digestsize %u != %hu\n",
x->aalg->alg_name,
crypto_tfm_alg_digestsize(esp->auth.tfm),
aalg_desc->uinfo.auth.icv_fullbits/8);
goto error;
}
esp->auth.icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8;
esp->auth.icv_trunc_len = aalg_desc->uinfo.auth.icv_truncbits/8;
esp->auth.work_icv = kmalloc(esp->auth.icv_full_len, GFP_KERNEL);
if (!esp->auth.work_icv)
goto error; goto error;
} }
esp->conf.key = x->ealg->alg_key; esp->conf.key = x->ealg->alg_key;
...@@ -656,8 +674,8 @@ int esp_init_state(struct xfrm_state *x, void *args) ...@@ -656,8 +674,8 @@ int esp_init_state(struct xfrm_state *x, void *args)
if (esp) { if (esp) {
if (esp->auth.tfm) if (esp->auth.tfm)
crypto_free_tfm(esp->auth.tfm); crypto_free_tfm(esp->auth.tfm);
if (esp->auth.work_digest) if (esp->auth.work_icv)
kfree(esp->auth.work_digest); kfree(esp->auth.work_icv);
if (esp->conf.tfm) if (esp->conf.tfm)
crypto_free_tfm(esp->conf.tfm); crypto_free_tfm(esp->conf.tfm);
kfree(esp); kfree(esp);
......
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