esp6.c 13.6 KB
Newer Older
L
Linus Torvalds 已提交
1 2
/*
 * Copyright (C)2002 USAGI/WIDE Project
3
 *
L
Linus Torvalds 已提交
4 5 6 7
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
8
 *
L
Linus Torvalds 已提交
9 10 11 12
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
13
 *
L
Linus Torvalds 已提交
14 15 16 17 18 19
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Authors
 *
20
 *	Mitsuru KANDA @USAGI       : IPv6 Support
L
Linus Torvalds 已提交
21 22
 * 	Kazunori MIYAZAWA @USAGI   :
 * 	Kunihiro Ishiguro <kunihiro@ipinfusion.com>
23
 *
L
Linus Torvalds 已提交
24 25 26
 * 	This file is derived from net/ipv4/esp.c
 */

27 28
#include <crypto/aead.h>
#include <crypto/authenc.h>
29
#include <linux/err.h>
L
Linus Torvalds 已提交
30 31 32 33
#include <linux/module.h>
#include <net/ip.h>
#include <net/xfrm.h>
#include <net/esp.h>
34
#include <linux/scatterlist.h>
H
Herbert Xu 已提交
35
#include <linux/kernel.h>
L
Linus Torvalds 已提交
36 37
#include <linux/pfkeyv2.h>
#include <linux/random.h>
38
#include <linux/slab.h>
39
#include <linux/spinlock.h>
L
Linus Torvalds 已提交
40 41
#include <net/icmp.h>
#include <net/ipv6.h>
42
#include <net/protocol.h>
L
Linus Torvalds 已提交
43 44
#include <linux/icmpv6.h>

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
struct esp_skb_cb {
	struct xfrm_skb_cb xfrm;
	void *tmp;
};

#define ESP_SKB_CB(__skb) ((struct esp_skb_cb *)&((__skb)->cb[0]))

/*
 * Allocate an AEAD request structure with extra space for SG and IV.
 *
 * For alignment considerations the IV is placed at the front, followed
 * by the request and finally the SG list.
 *
 * TODO: Use spare space in skb for this where possible.
 */
static void *esp_alloc_tmp(struct crypto_aead *aead, int nfrags)
{
	unsigned int len;

	len = crypto_aead_ivsize(aead);
	if (len) {
		len += crypto_aead_alignmask(aead) &
		       ~(crypto_tfm_ctx_alignment() - 1);
		len = ALIGN(len, crypto_tfm_ctx_alignment());
	}

	len += sizeof(struct aead_givcrypt_request) + crypto_aead_reqsize(aead);
	len = ALIGN(len, __alignof__(struct scatterlist));

	len += sizeof(struct scatterlist) * nfrags;

	return kmalloc(len, GFP_ATOMIC);
}

static inline u8 *esp_tmp_iv(struct crypto_aead *aead, void *tmp)
{
	return crypto_aead_ivsize(aead) ?
	       PTR_ALIGN((u8 *)tmp, crypto_aead_alignmask(aead) + 1) : tmp;
}

static inline struct aead_givcrypt_request *esp_tmp_givreq(
	struct crypto_aead *aead, u8 *iv)
{
	struct aead_givcrypt_request *req;

	req = (void *)PTR_ALIGN(iv + crypto_aead_ivsize(aead),
				crypto_tfm_ctx_alignment());
	aead_givcrypt_set_tfm(req, aead);
	return req;
}

static inline struct aead_request *esp_tmp_req(struct crypto_aead *aead, u8 *iv)
{
	struct aead_request *req;

	req = (void *)PTR_ALIGN(iv + crypto_aead_ivsize(aead),
				crypto_tfm_ctx_alignment());
	aead_request_set_tfm(req, aead);
	return req;
}

static inline struct scatterlist *esp_req_sg(struct crypto_aead *aead,
					     struct aead_request *req)
{
	return (void *)ALIGN((unsigned long)(req + 1) +
			     crypto_aead_reqsize(aead),
			     __alignof__(struct scatterlist));
}

static inline struct scatterlist *esp_givreq_sg(
	struct crypto_aead *aead, struct aead_givcrypt_request *req)
{
	return (void *)ALIGN((unsigned long)(req + 1) +
			     crypto_aead_reqsize(aead),
			     __alignof__(struct scatterlist));
}

static void esp_output_done(struct crypto_async_request *base, int err)
{
	struct sk_buff *skb = base->data;

	kfree(ESP_SKB_CB(skb)->tmp);
	xfrm_output_resume(skb, err);
}

L
Linus Torvalds 已提交
130 131 132
static int esp6_output(struct xfrm_state *x, struct sk_buff *skb)
{
	int err;
133
	struct ip_esp_hdr *esph;
134 135 136 137
	struct crypto_aead *aead;
	struct aead_givcrypt_request *req;
	struct scatterlist *sg;
	struct scatterlist *asg;
L
Linus Torvalds 已提交
138
	struct sk_buff *trailer;
139
	void *tmp;
L
Linus Torvalds 已提交
140 141 142 143
	int blksize;
	int clen;
	int alen;
	int nfrags;
144
	u8 *iv;
145
	u8 *tail;
146
	struct esp_data *esp = x->data;
L
Linus Torvalds 已提交
147

148
	/* skb is pure payload to encrypt */
L
Linus Torvalds 已提交
149 150 151 152 153
	err = -ENOMEM;

	/* Round to block size */
	clen = skb->len;

154 155 156 157
	aead = esp->aead;
	alen = crypto_aead_authsize(aead);

	blksize = ALIGN(crypto_aead_blocksize(aead), 4);
H
Herbert Xu 已提交
158
	clen = ALIGN(clen + 2, blksize);
159 160
	if (esp->padlen)
		clen = ALIGN(clen, esp->padlen);
L
Linus Torvalds 已提交
161

162
	if ((err = skb_cow_data(skb, clen - skb->len + alen, &trailer)) < 0)
L
Linus Torvalds 已提交
163
		goto error;
164 165 166 167 168 169 170 171 172 173
	nfrags = err;

	tmp = esp_alloc_tmp(aead, nfrags + 1);
	if (!tmp)
		goto error;

	iv = esp_tmp_iv(aead, tmp);
	req = esp_tmp_givreq(aead, iv);
	asg = esp_givreq_sg(aead, req);
	sg = asg + 1;
L
Linus Torvalds 已提交
174 175

	/* Fill padding... */
176
	tail = skb_tail_pointer(trailer);
L
Linus Torvalds 已提交
177 178 179
	do {
		int i;
		for (i=0; i<clen-skb->len - 2; i++)
180
			tail[i] = i + 1;
L
Linus Torvalds 已提交
181
	} while (0);
182
	tail[clen-skb->len - 2] = (clen - skb->len) - 2;
183 184
	tail[clen - skb->len - 1] = *skb_mac_header(skb);
	pskb_put(skb, trailer, clen - skb->len + alen);
L
Linus Torvalds 已提交
185

186
	skb_push(skb, -skb_network_offset(skb));
187
	esph = ip_esp_hdr(skb);
188
	*skb_mac_header(skb) = IPPROTO_ESP;
L
Linus Torvalds 已提交
189 190

	esph->spi = x->id.spi;
191
	esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq);
L
Linus Torvalds 已提交
192

193 194 195 196 197
	sg_init_table(sg, nfrags);
	skb_to_sgvec(skb, sg,
		     esph->enc_data + crypto_aead_ivsize(aead) - skb->data,
		     clen + alen);
	sg_init_one(asg, esph, sizeof(*esph));
198

199 200 201 202
	aead_givcrypt_set_callback(req, 0, esp_output_done, skb);
	aead_givcrypt_set_crypt(req, sg, sg, clen, iv);
	aead_givcrypt_set_assoc(req, asg, sizeof(*esph));
	aead_givcrypt_set_giv(req, esph->enc_data, XFRM_SKB_CB(skb)->seq);
L
Linus Torvalds 已提交
203

204 205 206 207
	ESP_SKB_CB(skb)->tmp = tmp;
	err = crypto_aead_givencrypt(req);
	if (err == -EINPROGRESS)
		goto error;
L
Linus Torvalds 已提交
208

209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
	if (err == -EBUSY)
		err = NET_XMIT_DROP;

	kfree(tmp);

error:
	return err;
}

static int esp_input_done2(struct sk_buff *skb, int err)
{
	struct xfrm_state *x = xfrm_input_state(skb);
	struct esp_data *esp = x->data;
	struct crypto_aead *aead = esp->aead;
	int alen = crypto_aead_authsize(aead);
	int hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead);
	int elen = skb->len - hlen;
	int hdr_len = skb_network_header_len(skb);
	int padlen;
	u8 nexthdr[2];

	kfree(ESP_SKB_CB(skb)->tmp);
L
Linus Torvalds 已提交
231

232
	if (unlikely(err))
233
		goto out;
234

235 236
	if (skb_copy_bits(skb, skb->len - alen - 2, nexthdr, 2))
		BUG();
L
Linus Torvalds 已提交
237

238 239 240 241 242 243
	err = -EINVAL;
	padlen = nexthdr[0];
	if (padlen + 2 + alen >= elen) {
		LIMIT_NETDEBUG(KERN_WARNING "ipsec esp packet is garbage "
			       "padlen=%d, elen=%d\n", padlen + 2, elen - alen);
		goto out;
L
Linus Torvalds 已提交
244 245
	}

246
	/* ... check padding bits here. Silly. :-) */
247

248 249 250 251 252 253 254 255 256 257 258
	pskb_trim(skb, skb->len - alen - padlen - 2);
	__skb_pull(skb, hlen);
	skb_set_transport_header(skb, -hdr_len);

	err = nexthdr[1];

	/* RFC4303: Drop dummy packets without any error */
	if (err == IPPROTO_NONE)
		err = -EINVAL;

out:
L
Linus Torvalds 已提交
259 260 261
	return err;
}

262 263 264 265 266 267 268
static void esp_input_done(struct crypto_async_request *base, int err)
{
	struct sk_buff *skb = base->data;

	xfrm_input_resume(skb, esp_input_done2(skb, err));
}

269
static int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
L
Linus Torvalds 已提交
270
{
271
	struct ip_esp_hdr *esph;
L
Linus Torvalds 已提交
272
	struct esp_data *esp = x->data;
273 274
	struct crypto_aead *aead = esp->aead;
	struct aead_request *req;
L
Linus Torvalds 已提交
275
	struct sk_buff *trailer;
276
	int elen = skb->len - sizeof(*esph) - crypto_aead_ivsize(aead);
L
Linus Torvalds 已提交
277 278
	int nfrags;
	int ret = 0;
279 280 281 282
	void *tmp;
	u8 *iv;
	struct scatterlist *sg;
	struct scatterlist *asg;
L
Linus Torvalds 已提交
283

284
	if (!pskb_may_pull(skb, sizeof(*esph))) {
L
Linus Torvalds 已提交
285
		ret = -EINVAL;
286
		goto out;
L
Linus Torvalds 已提交
287 288
	}

289
	if (elen <= 0) {
L
Linus Torvalds 已提交
290
		ret = -EINVAL;
291
		goto out;
L
Linus Torvalds 已提交
292 293
	}

294 295 296 297 298
	if ((nfrags = skb_cow_data(skb, 0, &trailer)) < 0) {
		ret = -EINVAL;
		goto out;
	}

299 300 301 302
	ret = -ENOMEM;
	tmp = esp_alloc_tmp(aead, nfrags + 1);
	if (!tmp)
		goto out;
L
Linus Torvalds 已提交
303

304 305 306 307 308
	ESP_SKB_CB(skb)->tmp = tmp;
	iv = esp_tmp_iv(aead, tmp);
	req = esp_tmp_req(aead, iv);
	asg = esp_req_sg(aead, req);
	sg = asg + 1;
L
Linus Torvalds 已提交
309

310
	skb->ip_summed = CHECKSUM_NONE;
L
Linus Torvalds 已提交
311

312
	esph = (struct ip_esp_hdr *)skb->data;
L
Linus Torvalds 已提交
313 314

	/* Get ivec. This can be wrong, check against another impls. */
315
	iv = esph->enc_data;
316

317 318 319
	sg_init_table(sg, nfrags);
	skb_to_sgvec(skb, sg, sizeof(*esph) + crypto_aead_ivsize(aead), elen);
	sg_init_one(asg, esph, sizeof(*esph));
320

321 322 323
	aead_request_set_callback(req, 0, esp_input_done, skb);
	aead_request_set_crypt(req, sg, sg, elen, iv);
	aead_request_set_assoc(req, asg, sizeof(*esph));
L
Linus Torvalds 已提交
324

325 326 327
	ret = crypto_aead_decrypt(req);
	if (ret == -EINPROGRESS)
		goto out;
328

329
	ret = esp_input_done2(skb, ret);
L
Linus Torvalds 已提交
330 331 332 333 334

out:
	return ret;
}

335
static u32 esp6_get_mtu(struct xfrm_state *x, int mtu)
L
Linus Torvalds 已提交
336 337
{
	struct esp_data *esp = x->data;
338 339
	u32 blksize = ALIGN(crypto_aead_blocksize(esp->aead), 4);
	u32 align = max_t(u32, blksize, esp->padlen);
340
	u32 rem;
L
Linus Torvalds 已提交
341

342
	mtu -= x->props.header_len + crypto_aead_authsize(esp->aead);
343 344 345 346
	rem = mtu & (align - 1);
	mtu &= ~(align - 1);

	if (x->props.mode != XFRM_MODE_TUNNEL) {
347
		u32 padsize = ((blksize - 1) & 7) + 1;
348 349
		mtu -= blksize - padsize;
		mtu += min_t(u32, blksize - padsize, rem);
L
Linus Torvalds 已提交
350 351
	}

352
	return mtu - 2;
L
Linus Torvalds 已提交
353 354 355
}

static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
356
		     int type, int code, int offset, __be32 info)
L
Linus Torvalds 已提交
357 358
{
	struct ipv6hdr *iph = (struct ipv6hdr*)skb->data;
359
	struct ip_esp_hdr *esph = (struct ip_esp_hdr *)(skb->data + offset);
L
Linus Torvalds 已提交
360 361
	struct xfrm_state *x;

362
	if (type != ICMPV6_DEST_UNREACH &&
L
Linus Torvalds 已提交
363 364 365 366 367 368
	    type != ICMPV6_PKT_TOOBIG)
		return;

	x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET6);
	if (!x)
		return;
369
	printk(KERN_DEBUG "pmtu discovery on SA ESP/%08x/" NIP6_FMT "\n",
L
Linus Torvalds 已提交
370 371 372 373 374 375 376 377 378 379 380
			ntohl(esph->spi), NIP6(iph->daddr));
	xfrm_state_put(x);
}

static void esp6_destroy(struct xfrm_state *x)
{
	struct esp_data *esp = x->data;

	if (!esp)
		return;

381
	crypto_free_aead(esp->aead);
L
Linus Torvalds 已提交
382 383 384
	kfree(esp);
}

385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
static int esp_init_aead(struct xfrm_state *x)
{
	struct esp_data *esp = x->data;
	struct crypto_aead *aead;
	int err;

	aead = crypto_alloc_aead(x->aead->alg_name, 0, 0);
	err = PTR_ERR(aead);
	if (IS_ERR(aead))
		goto error;

	esp->aead = aead;

	err = crypto_aead_setkey(aead, x->aead->alg_key,
				 (x->aead->alg_key_len + 7) / 8);
	if (err)
		goto error;

	err = crypto_aead_setauthsize(aead, x->aead->alg_icv_len / 8);
	if (err)
		goto error;

error:
	return err;
}

static int esp_init_authenc(struct xfrm_state *x)
L
Linus Torvalds 已提交
412
{
413
	struct esp_data *esp = x->data;
414 415 416 417 418 419 420 421
	struct crypto_aead *aead;
	struct crypto_authenc_key_param *param;
	struct rtattr *rta;
	char *key;
	char *p;
	char authenc_name[CRYPTO_MAX_ALG_NAME];
	unsigned int keylen;
	int err;
L
Linus Torvalds 已提交
422

423
	err = -EINVAL;
L
Linus Torvalds 已提交
424
	if (x->ealg == NULL)
425
		goto error;
426

427
	err = -ENAMETOOLONG;
428 429 430
	if (snprintf(authenc_name, CRYPTO_MAX_ALG_NAME, "authenc(%s,%s)",
		     x->aalg ? x->aalg->alg_name : "digest_null",
		     x->ealg->alg_name) >= CRYPTO_MAX_ALG_NAME)
431
		goto error;
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453

	aead = crypto_alloc_aead(authenc_name, 0, 0);
	err = PTR_ERR(aead);
	if (IS_ERR(aead))
		goto error;

	esp->aead = aead;

	keylen = (x->aalg ? (x->aalg->alg_key_len + 7) / 8 : 0) +
		 (x->ealg->alg_key_len + 7) / 8 + RTA_SPACE(sizeof(*param));
	err = -ENOMEM;
	key = kmalloc(keylen, GFP_KERNEL);
	if (!key)
		goto error;

	p = key;
	rta = (void *)p;
	rta->rta_type = CRYPTO_AUTHENC_KEYA_PARAM;
	rta->rta_len = RTA_LENGTH(sizeof(*param));
	param = RTA_DATA(rta);
	p += RTA_SPACE(sizeof(*param));

L
Linus Torvalds 已提交
454 455 456
	if (x->aalg) {
		struct xfrm_algo_desc *aalg_desc;

457 458
		memcpy(p, x->aalg->alg_key, (x->aalg->alg_key_len + 7) / 8);
		p += (x->aalg->alg_key_len + 7) / 8;
459

L
Linus Torvalds 已提交
460 461
		aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
		BUG_ON(!aalg_desc);
462

463
		err = -EINVAL;
L
Linus Torvalds 已提交
464
		if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
465
		    crypto_aead_authsize(aead)) {
466 467
			NETDEBUG(KERN_INFO "ESP: %s digestsize %u != %hu\n",
				 x->aalg->alg_name,
468
				 crypto_aead_authsize(aead),
469
				 aalg_desc->uinfo.auth.icv_fullbits/8);
470
			goto free_key;
L
Linus Torvalds 已提交
471
		}
472

473 474 475 476
		err = crypto_aead_setauthsize(
			aead, aalg_desc->uinfo.auth.icv_truncbits / 8);
		if (err)
			goto free_key;
L
Linus Torvalds 已提交
477
	}
478 479 480 481 482 483 484 485 486

	param->enckeylen = cpu_to_be32((x->ealg->alg_key_len + 7) / 8);
	memcpy(p, x->ealg->alg_key, (x->ealg->alg_key_len + 7) / 8);

	err = crypto_aead_setkey(aead, key, keylen);

free_key:
	kfree(key);

487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
error:
	return err;
}

static int esp6_init_state(struct xfrm_state *x)
{
	struct esp_data *esp;
	struct crypto_aead *aead;
	u32 align;
	int err;

	if (x->encap)
		return -EINVAL;

	esp = kzalloc(sizeof(*esp), GFP_KERNEL);
	if (esp == NULL)
		return -ENOMEM;

	x->data = esp;

	if (x->aead)
		err = esp_init_aead(x);
	else
		err = esp_init_authenc(x);

512
	if (err)
L
Linus Torvalds 已提交
513
		goto error;
514

515 516 517 518
	aead = esp->aead;

	esp->padlen = 0;

519 520
	x->props.header_len = sizeof(struct ip_esp_hdr) +
			      crypto_aead_ivsize(aead);
521 522 523 524 525
	switch (x->props.mode) {
	case XFRM_MODE_BEET:
	case XFRM_MODE_TRANSPORT:
		break;
	case XFRM_MODE_TUNNEL:
L
Linus Torvalds 已提交
526
		x->props.header_len += sizeof(struct ipv6hdr);
527
		break;
528 529 530
	default:
		goto error;
	}
531 532 533 534 535

	align = ALIGN(crypto_aead_blocksize(aead), 4);
	if (esp->padlen)
		align = max_t(u32, align, esp->padlen);
	x->props.trailer_len = align + 1 + crypto_aead_authsize(esp->aead);
L
Linus Torvalds 已提交
536 537

error:
538
	return err;
L
Linus Torvalds 已提交
539 540
}

541
static const struct xfrm_type esp6_type =
L
Linus Torvalds 已提交
542 543 544 545
{
	.description	= "ESP6",
	.owner	     	= THIS_MODULE,
	.proto	     	= IPPROTO_ESP,
546
	.flags		= XFRM_TYPE_REPLAY_PROT,
L
Linus Torvalds 已提交
547 548
	.init_state	= esp6_init_state,
	.destructor	= esp6_destroy,
549
	.get_mtu	= esp6_get_mtu,
L
Linus Torvalds 已提交
550
	.input		= esp6_input,
551 552
	.output		= esp6_output,
	.hdr_offset	= xfrm6_find_1stfragopt,
L
Linus Torvalds 已提交
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
};

static struct inet6_protocol esp6_protocol = {
	.handler 	=	xfrm6_rcv,
	.err_handler	=	esp6_err,
	.flags		=	INET6_PROTO_NOPOLICY,
};

static int __init esp6_init(void)
{
	if (xfrm_register_type(&esp6_type, AF_INET6) < 0) {
		printk(KERN_INFO "ipv6 esp init: can't add xfrm type\n");
		return -EAGAIN;
	}
	if (inet6_add_protocol(&esp6_protocol, IPPROTO_ESP) < 0) {
		printk(KERN_INFO "ipv6 esp init: can't add protocol\n");
		xfrm_unregister_type(&esp6_type, AF_INET6);
		return -EAGAIN;
	}

	return 0;
}

static void __exit esp6_fini(void)
{
	if (inet6_del_protocol(&esp6_protocol, IPPROTO_ESP) < 0)
		printk(KERN_INFO "ipv6 esp close: can't remove protocol\n");
	if (xfrm_unregister_type(&esp6_type, AF_INET6) < 0)
		printk(KERN_INFO "ipv6 esp close: can't remove xfrm type\n");
}

module_init(esp6_init);
module_exit(esp6_fini);

MODULE_LICENSE("GPL");
588
MODULE_ALIAS_XFRM_TYPE(AF_INET6, XFRM_PROTO_ESP);