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.output);
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
	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));
202 203
	aead_givcrypt_set_giv(req, esph->enc_data,
			      XFRM_SKB_CB(skb)->seq.output);
L
Linus Torvalds 已提交
204

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

210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
	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 已提交
232

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

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

239 240 241 242 243 244
	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 已提交
245 246
	}

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

249 250 251 252 253 254 255 256 257 258 259
	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 已提交
260 261 262
	return err;
}

263 264 265 266 267 268 269
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));
}

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

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

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

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

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

305 306 307 308 309
	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 已提交
310

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

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

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

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

322 323 324
	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 已提交
325

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

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

out:
	return ret;
}

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

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

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

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

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

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

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

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

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 412
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 已提交
413
{
414
	struct esp_data *esp = x->data;
415 416 417 418 419 420 421 422
	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 已提交
423

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

428
	err = -ENAMETOOLONG;
429 430 431
	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)
432
		goto error;
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454

	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 已提交
455 456 457
	if (x->aalg) {
		struct xfrm_algo_desc *aalg_desc;

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

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

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

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

	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);

488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
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);

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

516 517 518 519
	aead = esp->aead;

	esp->padlen = 0;

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

	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 已提交
537 538

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

542
static const struct xfrm_type esp6_type =
L
Linus Torvalds 已提交
543 544 545 546
{
	.description	= "ESP6",
	.owner	     	= THIS_MODULE,
	.proto	     	= IPPROTO_ESP,
547
	.flags		= XFRM_TYPE_REPLAY_PROT,
L
Linus Torvalds 已提交
548 549
	.init_state	= esp6_init_state,
	.destructor	= esp6_destroy,
550
	.get_mtu	= esp6_get_mtu,
L
Linus Torvalds 已提交
551
	.input		= esp6_input,
552 553
	.output		= esp6_output,
	.hdr_offset	= xfrm6_find_1stfragopt,
L
Linus Torvalds 已提交
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 588
};

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");
589
MODULE_ALIAS_XFRM_TYPE(AF_INET6, XFRM_PROTO_ESP);