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