]> git.saurik.com Git - apple/xnu.git/blame - bsd/netinet6/esp_output.c
xnu-344.tar.gz
[apple/xnu.git] / bsd / netinet6 / esp_output.c
CommitLineData
9bccf70c
A
1/* $FreeBSD: src/sys/netinet6/esp_output.c,v 1.1.2.2 2001/07/03 11:01:50 ume Exp $ */
2/* $KAME: esp_output.c,v 1.43 2001/03/01 07:10:45 itojun Exp $ */
1c79356b
A
3
4/*
5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#define _IP_VHL
1c79356b
A
34
35/*
36 * RFC1827/2406 Encapsulated Security Payload.
37 */
38
39#include <sys/param.h>
40#include <sys/systm.h>
41#include <sys/malloc.h>
42#include <sys/mbuf.h>
43#include <sys/domain.h>
44#include <sys/protosw.h>
45#include <sys/socket.h>
46#include <sys/socketvar.h>
47#include <sys/errno.h>
48#include <sys/time.h>
49#include <sys/kernel.h>
50#include <sys/syslog.h>
51
52#include <net/if.h>
53#include <net/route.h>
54
55#include <netinet/in.h>
56#include <netinet/in_systm.h>
57#include <netinet/ip.h>
58#include <netinet/in_var.h>
59
60#if INET6
61#include <netinet/ip6.h>
62#include <netinet6/ip6_var.h>
63#include <netinet/icmp6.h>
64#endif
65
66#include <netinet6/ipsec.h>
9bccf70c
A
67#if INET6
68#include <netinet6/ipsec6.h>
69#endif
1c79356b 70#include <netinet6/ah.h>
9bccf70c
A
71#if INET6
72#include <netinet6/ah6.h>
73#endif
1c79356b 74#include <netinet6/esp.h>
9bccf70c
A
75#if INET6
76#include <netinet6/esp6.h>
77#endif
1c79356b
A
78#include <netkey/key.h>
79#include <netkey/keydb.h>
1c79356b
A
80
81#include <net/net_osdep.h>
82
83static int esp_output __P((struct mbuf *, u_char *, struct mbuf *,
84 struct ipsecrequest *, int));
85
86/*
87 * compute ESP header size.
88 */
89size_t
90esp_hdrsiz(isr)
91 struct ipsecrequest *isr;
92{
93 struct secasvar *sav;
9bccf70c
A
94 const struct esp_algorithm *algo;
95 const struct ah_algorithm *aalgo;
1c79356b
A
96 size_t ivlen;
97 size_t authlen;
98 size_t hdrsiz;
99
100 /* sanity check */
101 if (isr == NULL)
102 panic("esp_hdrsiz: NULL was passed.\n");
103
104 sav = isr->sav;
105
106 if (isr->saidx.proto != IPPROTO_ESP)
107 panic("unsupported mode passed to esp_hdrsiz");
108
109 if (sav == NULL)
110 goto estimate;
111 if (sav->state != SADB_SASTATE_MATURE
112 && sav->state != SADB_SASTATE_DYING)
113 goto estimate;
114
115 /* we need transport mode ESP. */
9bccf70c 116 algo = esp_algorithm_lookup(sav->alg_enc);
1c79356b
A
117 if (!algo)
118 goto estimate;
119 ivlen = sav->ivlen;
120 if (ivlen < 0)
121 goto estimate;
122
123 /*
124 * XXX
125 * right now we don't calcurate the padding size. simply
126 * treat the padding size as constant, for simplicity.
127 *
128 * XXX variable size padding support
129 */
130 if (sav->flags & SADB_X_EXT_OLD) {
131 /* RFC 1827 */
132 hdrsiz = sizeof(struct esp) + ivlen + 9;
133 } else {
134 /* RFC 2406 */
9bccf70c
A
135 aalgo = ah_algorithm_lookup(sav->alg_auth);
136 if (aalgo && sav->replay && sav->key_auth)
137 authlen = (aalgo->sumsiz)(sav);
1c79356b
A
138 else
139 authlen = 0;
140 hdrsiz = sizeof(struct newesp) + ivlen + 9 + authlen;
141 }
142
143 return hdrsiz;
144
145 estimate:
146 /*
147 * ASSUMING:
148 * sizeof(struct newesp) > sizeof(struct esp).
9bccf70c 149 * esp_max_ivlen() = max ivlen for CBC mode
1c79356b
A
150 * 9 = (maximum padding length without random padding length)
151 * + (Pad Length field) + (Next Header field).
152 * 16 = maximum ICV we support.
153 */
9bccf70c 154 return sizeof(struct newesp) + esp_max_ivlen() + 9 + 16;
1c79356b
A
155}
156
157/*
158 * Modify the packet so that the payload is encrypted.
159 * The mbuf (m) must start with IPv4 or IPv6 header.
160 * On failure, free the given mbuf and return NULL.
161 *
162 * on invocation:
163 * m nexthdrp md
164 * v v v
165 * IP ......... payload
166 * during the encryption:
167 * m nexthdrp mprev md
168 * v v v v
169 * IP ............... esp iv payload pad padlen nxthdr
170 * <--><-><------><--------------->
171 * esplen plen extendsiz
172 * ivlen
173 * <-----> esphlen
174 * <-> hlen
175 * <-----------------> espoff
176 */
177static int
178esp_output(m, nexthdrp, md, isr, af)
179 struct mbuf *m;
180 u_char *nexthdrp;
181 struct mbuf *md;
182 struct ipsecrequest *isr;
183 int af;
184{
185 struct mbuf *n;
186 struct mbuf *mprev;
187 struct esp *esp;
188 struct esptail *esptail;
189 struct secasvar *sav = isr->sav;
9bccf70c 190 const struct esp_algorithm *algo;
1c79356b
A
191 u_int32_t spi;
192 u_int8_t nxt = 0;
193 size_t plen; /*payload length to be encrypted*/
194 size_t espoff;
195 int ivlen;
196 int afnumber;
197 size_t extendsiz;
198 int error = 0;
9bccf70c 199 struct ipsecstat *stat;
1c79356b
A
200
201 switch (af) {
202#if INET
203 case AF_INET:
204 afnumber = 4;
9bccf70c 205 stat = &ipsecstat;
1c79356b
A
206 break;
207#endif
208#if INET6
209 case AF_INET6:
210 afnumber = 6;
9bccf70c 211 stat = &ipsec6stat;
1c79356b
A
212 break;
213#endif
214 default:
215 ipseclog((LOG_ERR, "esp_output: unsupported af %d\n", af));
216 return 0; /* no change at all */
217 }
218
219 /* some sanity check */
220 if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
221 switch (af) {
222#if INET
223 case AF_INET:
224 {
225 struct ip *ip;
226
227 ip = mtod(m, struct ip *);
228 ipseclog((LOG_DEBUG, "esp4_output: internal error: "
229 "sav->replay is null: %x->%x, SPI=%u\n",
230 (u_int32_t)ntohl(ip->ip_src.s_addr),
231 (u_int32_t)ntohl(ip->ip_dst.s_addr),
232 (u_int32_t)ntohl(sav->spi)));
233 ipsecstat.out_inval++;
9bccf70c 234 break;
1c79356b
A
235 }
236#endif /*INET*/
237#if INET6
238 case AF_INET6:
1c79356b
A
239 ipseclog((LOG_DEBUG, "esp6_output: internal error: "
240 "sav->replay is null: SPI=%u\n",
241 (u_int32_t)ntohl(sav->spi)));
242 ipsec6stat.out_inval++;
9bccf70c 243 break;
1c79356b 244#endif /*INET6*/
9bccf70c
A
245 default:
246 panic("esp_output: should not reach here");
1c79356b 247 }
9bccf70c
A
248 m_freem(m);
249 return EINVAL;
1c79356b
A
250 }
251
9bccf70c
A
252 algo = esp_algorithm_lookup(sav->alg_enc);
253 if (!algo) {
254 ipseclog((LOG_ERR, "esp_output: unsupported algorithm: "
255 "SPI=%u\n", (u_int32_t)ntohl(sav->spi)));
256 m_freem(m);
257 return EINVAL;
258 }
1c79356b
A
259 spi = sav->spi;
260 ivlen = sav->ivlen;
261 /* should be okey */
262 if (ivlen < 0) {
263 panic("invalid ivlen");
264 }
265
266 {
267 /*
268 * insert ESP header.
269 * XXX inserts ESP header right after IPv4 header. should
270 * chase the header chain.
271 * XXX sequential number
272 */
273#if INET
274 struct ip *ip = NULL;
275#endif
276#if INET6
277 struct ip6_hdr *ip6 = NULL;
278#endif
279 size_t esplen; /*sizeof(struct esp/newesp)*/
280 size_t esphlen; /*sizeof(struct esp/newesp) + ivlen*/
281 size_t hlen = 0; /*ip header len*/
282
283 if (sav->flags & SADB_X_EXT_OLD) {
284 /* RFC 1827 */
285 esplen = sizeof(struct esp);
286 } else {
287 /* RFC 2406 */
288 if (sav->flags & SADB_X_EXT_DERIV)
289 esplen = sizeof(struct esp);
290 else
291 esplen = sizeof(struct newesp);
292 }
293 esphlen = esplen + ivlen;
294
295 for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next)
296 ;
297 if (mprev == NULL || mprev->m_next != md) {
298 ipseclog((LOG_DEBUG, "esp%d_output: md is not in chain\n",
299 afnumber));
300 m_freem(m);
301 return EINVAL;
302 }
303
304 plen = 0;
305 for (n = md; n; n = n->m_next)
306 plen += n->m_len;
307
308 switch (af) {
309#if INET
310 case AF_INET:
311 ip = mtod(m, struct ip *);
312#ifdef _IP_VHL
313 hlen = IP_VHL_HL(ip->ip_vhl) << 2;
314#else
315 hlen = ip->ip_hl << 2;
316#endif
317 break;
318#endif
319#if INET6
320 case AF_INET6:
321 ip6 = mtod(m, struct ip6_hdr *);
322 hlen = sizeof(*ip6);
323 break;
324#endif
325 }
326
327 /* make the packet over-writable */
328 mprev->m_next = NULL;
329 if ((md = ipsec_copypkt(md)) == NULL) {
330 m_freem(m);
331 error = ENOBUFS;
332 goto fail;
333 }
334 mprev->m_next = md;
335
336 espoff = m->m_pkthdr.len - plen;
337
338 /*
339 * grow the mbuf to accomodate ESP header.
340 * before: IP ... payload
341 * after: IP ... ESP IV payload
342 */
9bccf70c 343 if (M_LEADINGSPACE(md) < esphlen || (md->m_flags & M_EXT) != 0) {
1c79356b
A
344 MGET(n, M_DONTWAIT, MT_DATA);
345 if (!n) {
346 m_freem(m);
347 error = ENOBUFS;
348 goto fail;
349 }
350 n->m_len = esphlen;
351 mprev->m_next = n;
352 n->m_next = md;
353 m->m_pkthdr.len += esphlen;
354 esp = mtod(n, struct esp *);
355 } else {
356 md->m_len += esphlen;
357 md->m_data -= esphlen;
358 m->m_pkthdr.len += esphlen;
359 esp = mtod(md, struct esp *);
360 }
361
362 nxt = *nexthdrp;
363 *nexthdrp = IPPROTO_ESP;
364 switch (af) {
365#if INET
366 case AF_INET:
367 if (esphlen < (IP_MAXPACKET - ntohs(ip->ip_len)))
368 ip->ip_len = htons(ntohs(ip->ip_len) + esphlen);
369 else {
370 ipseclog((LOG_ERR,
371 "IPv4 ESP output: size exceeds limit\n"));
372 ipsecstat.out_inval++;
373 m_freem(m);
374 error = EMSGSIZE;
375 goto fail;
376 }
377 break;
378#endif
379#if INET6
380 case AF_INET6:
381 /* total packet length will be computed in ip6_output() */
382 break;
383#endif
384 }
385 }
386
387 /* initialize esp header. */
388 esp->esp_spi = spi;
389 if ((sav->flags & SADB_X_EXT_OLD) == 0) {
390 struct newesp *nesp;
391 nesp = (struct newesp *)esp;
392 if (sav->replay->count == ~0) {
393 if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
394 /* XXX Is it noisy ? */
395 ipseclog((LOG_WARNING,
396 "replay counter overflowed. %s\n",
397 ipsec_logsastr(sav)));
9bccf70c 398 stat->out_inval++;
1c79356b
A
399 m_freem(m);
400 return EINVAL;
401 }
402 }
403 sav->replay->count++;
404 /*
405 * XXX sequence number must not be cycled, if the SA is
406 * installed by IKE daemon.
407 */
408 nesp->esp_seq = htonl(sav->replay->count);
409 }
410
411 {
412 /*
413 * find the last mbuf. make some room for ESP trailer.
1c79356b
A
414 */
415#if INET
416 struct ip *ip = NULL;
417#endif
418 size_t padbound;
419 u_char *extend;
420 int i;
9bccf70c 421 int randpadmax;
1c79356b
A
422
423 if (algo->padbound)
424 padbound = algo->padbound;
425 else
426 padbound = 4;
427 /* ESP packet, including nxthdr field, must be length of 4n */
428 if (padbound < 4)
429 padbound = 4;
430
431 extendsiz = padbound - (plen % padbound);
432 if (extendsiz == 1)
433 extendsiz = padbound + 1;
434
9bccf70c
A
435 /* random padding */
436 switch (af) {
437#if INET
438 case AF_INET:
439 randpadmax = ip4_esp_randpad;
440 break;
441#endif
442#if INET6
443 case AF_INET6:
444 randpadmax = ip6_esp_randpad;
445 break;
446#endif
447 default:
448 randpadmax = -1;
449 break;
450 }
451 if (randpadmax < 0 || plen + extendsiz >= randpadmax)
452 ;
453 else {
454 int n;
455
456 /* round */
457 randpadmax = (randpadmax / padbound) * padbound;
458 n = (randpadmax - plen + extendsiz) / padbound;
459
460 if (n > 0)
461 n = (random() % n) * padbound;
462 else
463 n = 0;
464
465 /*
466 * make sure we do not pad too much.
467 * MLEN limitation comes from the trailer attachment
468 * code below.
469 * 256 limitation comes from sequential padding.
470 * also, the 1-octet length field in ESP trailer imposes
471 * limitation (but is less strict than sequential padding
472 * as length field do not count the last 2 octets).
473 */
474 if (extendsiz + n <= MLEN && extendsiz + n < 256)
475 extendsiz += n;
476 }
477
478#if DIAGNOSTIC
479 if (extendsiz > MLEN || extendsiz >= 256)
480 panic("extendsiz too big in esp_output");
481#endif
482
1c79356b
A
483 n = m;
484 while (n->m_next)
485 n = n->m_next;
486
487 /*
9bccf70c
A
488 * if M_EXT, the external mbuf data may be shared among
489 * two consequtive TCP packets, and it may be unsafe to use the
490 * trailing space.
1c79356b
A
491 */
492 if (!(n->m_flags & M_EXT) && extendsiz < M_TRAILINGSPACE(n)) {
493 extend = mtod(n, u_char *) + n->m_len;
494 n->m_len += extendsiz;
495 m->m_pkthdr.len += extendsiz;
496 } else {
497 struct mbuf *nn;
498
499 MGET(nn, M_DONTWAIT, MT_DATA);
500 if (!nn) {
501 ipseclog((LOG_DEBUG, "esp%d_output: can't alloc mbuf",
502 afnumber));
503 m_freem(m);
504 error = ENOBUFS;
505 goto fail;
506 }
507 extend = mtod(nn, u_char *);
508 nn->m_len = extendsiz;
509 nn->m_next = NULL;
510 n->m_next = nn;
511 n = nn;
512 m->m_pkthdr.len += extendsiz;
513 }
514 switch (sav->flags & SADB_X_EXT_PMASK) {
515 case SADB_X_EXT_PRAND:
9bccf70c 516 key_randomfill(extend, extendsiz);
1c79356b
A
517 break;
518 case SADB_X_EXT_PZERO:
519 bzero(extend, extendsiz);
520 break;
521 case SADB_X_EXT_PSEQ:
522 for (i = 0; i < extendsiz; i++)
523 extend[i] = (i + 1) & 0xff;
524 break;
525 }
526
527 /* initialize esp trailer. */
528 esptail = (struct esptail *)
529 (mtod(n, u_int8_t *) + n->m_len - sizeof(struct esptail));
530 esptail->esp_nxt = nxt;
531 esptail->esp_padlen = extendsiz - 2;
532
533 /* modify IP header (for ESP header part only) */
534 switch (af) {
535#if INET
536 case AF_INET:
537 ip = mtod(m, struct ip *);
538 if (extendsiz < (IP_MAXPACKET - ntohs(ip->ip_len)))
539 ip->ip_len = htons(ntohs(ip->ip_len) + extendsiz);
540 else {
541 ipseclog((LOG_ERR,
542 "IPv4 ESP output: size exceeds limit\n"));
543 ipsecstat.out_inval++;
544 m_freem(m);
545 error = EMSGSIZE;
546 goto fail;
547 }
548 break;
549#endif
550#if INET6
551 case AF_INET6:
552 /* total packet length will be computed in ip6_output() */
553 break;
554#endif
555 }
556 }
557
9bccf70c
A
558 /*
559 * pre-compute and cache intermediate key
560 */
561 error = esp_schedule(algo, sav);
562 if (error) {
563 m_freem(m);
564 stat->out_inval++;
565 goto fail;
566 }
567
1c79356b
A
568 /*
569 * encrypt the packet, based on security association
570 * and the algorithm specified.
571 */
572 if (!algo->encrypt)
573 panic("internal error: no encrypt function");
574 if ((*algo->encrypt)(m, espoff, plen + extendsiz, sav, algo, ivlen)) {
9bccf70c 575 /* m is already freed */
1c79356b 576 ipseclog((LOG_ERR, "packet encryption failure\n"));
9bccf70c 577 stat->out_inval++;
1c79356b
A
578 error = EINVAL;
579 goto fail;
580 }
581
582 /*
583 * calculate ICV if required.
584 */
585 if (!sav->replay)
586 goto noantireplay;
587 if (!sav->key_auth)
588 goto noantireplay;
9bccf70c 589 if (sav->key_auth == SADB_AALG_NONE)
1c79356b 590 goto noantireplay;
9bccf70c 591
1c79356b 592 {
9bccf70c 593 const struct ah_algorithm *aalgo;
1c79356b
A
594 u_char authbuf[AH_MAXSUMSIZE];
595 struct mbuf *n;
596 u_char *p;
597 size_t siz;
9bccf70c 598#if INET
1c79356b 599 struct ip *ip;
9bccf70c 600#endif
1c79356b 601
9bccf70c
A
602 aalgo = ah_algorithm_lookup(sav->alg_auth);
603 if (!aalgo)
604 goto noantireplay;
605 siz = ((aalgo->sumsiz)(sav) + 3) & ~(4 - 1);
1c79356b
A
606 if (AH_MAXSUMSIZE < siz)
607 panic("assertion failed for AH_MAXSUMSIZE");
608
9bccf70c
A
609 if (esp_auth(m, espoff, m->m_pkthdr.len - espoff, sav, authbuf)) {
610 ipseclog((LOG_ERR, "ESP checksum generation failure\n"));
611 m_freem(m);
612 error = EINVAL;
613 stat->out_inval++;
614 goto fail;
615 }
1c79356b
A
616
617 n = m;
618 while (n->m_next)
619 n = n->m_next;
620
621 if (!(n->m_flags & M_EXT) && siz < M_TRAILINGSPACE(n)) { /*XXX*/
622 n->m_len += siz;
623 m->m_pkthdr.len += siz;
624 p = mtod(n, u_char *) + n->m_len - siz;
625 } else {
626 struct mbuf *nn;
627
628 MGET(nn, M_DONTWAIT, MT_DATA);
629 if (!nn) {
630 ipseclog((LOG_DEBUG, "can't alloc mbuf in esp%d_output",
631 afnumber));
632 m_freem(m);
633 error = ENOBUFS;
634 goto fail;
635 }
636 nn->m_len = siz;
637 nn->m_next = NULL;
638 n->m_next = nn;
639 n = nn;
640 m->m_pkthdr.len += siz;
641 p = mtod(nn, u_char *);
642 }
643 bcopy(authbuf, p, siz);
644
645 /* modify IP header (for ESP header part only) */
646 switch (af) {
647#if INET
648 case AF_INET:
649 ip = mtod(m, struct ip *);
650 if (siz < (IP_MAXPACKET - ntohs(ip->ip_len)))
651 ip->ip_len = htons(ntohs(ip->ip_len) + siz);
652 else {
653 ipseclog((LOG_ERR,
654 "IPv4 ESP output: size exceeds limit\n"));
655 ipsecstat.out_inval++;
656 m_freem(m);
657 error = EMSGSIZE;
658 goto fail;
659 }
660 break;
661#endif
662#if INET6
663 case AF_INET6:
664 /* total packet length will be computed in ip6_output() */
665 break;
666#endif
667 }
668 }
669
670noantireplay:
671 if (!m) {
672 ipseclog((LOG_ERR,
673 "NULL mbuf after encryption in esp%d_output", afnumber));
9bccf70c
A
674 } else
675 stat->out_success++;
676 stat->out_esphist[sav->alg_enc]++;
1c79356b
A
677 key_sa_recordxfer(sav, m);
678 return 0;
679
680fail:
681#if 1
682 return error;
683#else
684 panic("something bad in esp_output");
685#endif
686}
687
688#if INET
689int
690esp4_output(m, isr)
691 struct mbuf *m;
692 struct ipsecrequest *isr;
693{
694 struct ip *ip;
695 if (m->m_len < sizeof(struct ip)) {
696 ipseclog((LOG_DEBUG, "esp4_output: first mbuf too short\n"));
697 m_freem(m);
9bccf70c 698 return 0;
1c79356b
A
699 }
700 ip = mtod(m, struct ip *);
701 /* XXX assumes that m->m_next points to payload */
702 return esp_output(m, &ip->ip_p, m->m_next, isr, AF_INET);
703}
704#endif /*INET*/
705
706#if INET6
707int
708esp6_output(m, nexthdrp, md, isr)
709 struct mbuf *m;
710 u_char *nexthdrp;
711 struct mbuf *md;
712 struct ipsecrequest *isr;
713{
714 if (m->m_len < sizeof(struct ip6_hdr)) {
715 ipseclog((LOG_DEBUG, "esp6_output: first mbuf too short\n"));
716 m_freem(m);
9bccf70c 717 return 0;
1c79356b
A
718 }
719 return esp_output(m, nexthdrp, md, isr, AF_INET6);
720}
721#endif /*INET6*/