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