esp6.c 13.8 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) + crypto_aead_ivsize(aead))) {
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
		     u8 type, u8 code, int offset, __be32 info)
L
Linus Torvalds 已提交
358
{
A
Alexey Dobriyan 已提交
359
	struct net *net = dev_net(skb->dev);
L
Linus Torvalds 已提交
360
	struct ipv6hdr *iph = (struct ipv6hdr*)skb->data;
361
	struct ip_esp_hdr *esph = (struct ip_esp_hdr *)(skb->data + offset);
L
Linus Torvalds 已提交
362 363
	struct xfrm_state *x;

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

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

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

	if (!esp)
		return;

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

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

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

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

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

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

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

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

475
		err = crypto_aead_setauthsize(
476
			aead, x->aalg->alg_trunc_len / 8);
477 478
		if (err)
			goto free_key;
L
Linus Torvalds 已提交
479
	}
480 481 482 483 484 485 486 487 488

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

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

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

517 518 519 520
	aead = esp->aead;

	esp->padlen = 0;

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

	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 已提交
542 543

error:
544
	return err;
L
Linus Torvalds 已提交
545 546
}

547
static const struct xfrm_type esp6_type =
L
Linus Torvalds 已提交
548 549 550 551
{
	.description	= "ESP6",
	.owner	     	= THIS_MODULE,
	.proto	     	= IPPROTO_ESP,
552
	.flags		= XFRM_TYPE_REPLAY_PROT,
L
Linus Torvalds 已提交
553 554
	.init_state	= esp6_init_state,
	.destructor	= esp6_destroy,
555
	.get_mtu	= esp6_get_mtu,
L
Linus Torvalds 已提交
556
	.input		= esp6_input,
557 558
	.output		= esp6_output,
	.hdr_offset	= xfrm6_find_1stfragopt,
L
Linus Torvalds 已提交
559 560
};

561
static const struct inet6_protocol esp6_protocol = {
L
Linus Torvalds 已提交
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 589 590 591 592 593
	.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");
594
MODULE_ALIAS_XFRM_TYPE(AF_INET6, XFRM_PROTO_ESP);