1 | /* |
2 | * Copyright (c) 2008-2017 Apple Inc. All rights reserved. |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
5 | * |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License |
8 | * Version 2.0 (the 'License'). You may not use this file except in |
9 | * compliance with the License. The rights granted to you under the License |
10 | * may not be used to create, or enable the creation or redistribution of, |
11 | * unlawful or unlicensed copies of an Apple operating system, or to |
12 | * circumvent, violate, or enable the circumvention or violation of, any |
13 | * terms of an Apple operating system software license agreement. |
14 | * |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. |
17 | * |
18 | * The Original Code and all software distributed under the License are |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and |
24 | * limitations under the License. |
25 | * |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
27 | */ |
28 | |
29 | /* $FreeBSD: src/sys/netinet6/esp_output.c,v 1.1.2.3 2002/04/28 05:40:26 suz Exp $ */ |
30 | /* $KAME: esp_output.c,v 1.44 2001/07/26 06:53:15 jinmei Exp $ */ |
31 | |
32 | /* |
33 | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. |
34 | * All rights reserved. |
35 | * |
36 | * Redistribution and use in source and binary forms, with or without |
37 | * modification, are permitted provided that the following conditions |
38 | * are met: |
39 | * 1. Redistributions of source code must retain the above copyright |
40 | * notice, this list of conditions and the following disclaimer. |
41 | * 2. Redistributions in binary form must reproduce the above copyright |
42 | * notice, this list of conditions and the following disclaimer in the |
43 | * documentation and/or other materials provided with the distribution. |
44 | * 3. Neither the name of the project nor the names of its contributors |
45 | * may be used to endorse or promote products derived from this software |
46 | * without specific prior written permission. |
47 | * |
48 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
49 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
50 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
51 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
52 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
53 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
54 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
55 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
56 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
57 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
58 | * SUCH DAMAGE. |
59 | */ |
60 | |
61 | #define _IP_VHL |
62 | |
63 | /* |
64 | * RFC1827/2406 Encapsulated Security Payload. |
65 | */ |
66 | |
67 | #include <sys/param.h> |
68 | #include <sys/systm.h> |
69 | #include <sys/malloc.h> |
70 | #include <sys/mbuf.h> |
71 | #include <sys/domain.h> |
72 | #include <sys/protosw.h> |
73 | #include <sys/socket.h> |
74 | #include <sys/socketvar.h> |
75 | #include <sys/errno.h> |
76 | #include <sys/time.h> |
77 | #include <sys/kernel.h> |
78 | #include <sys/syslog.h> |
79 | |
80 | #include <net/if.h> |
81 | #include <net/route.h> |
82 | |
83 | #include <netinet/in.h> |
84 | #include <netinet/in_systm.h> |
85 | #include <netinet/ip.h> |
86 | #include <netinet/in_var.h> |
87 | #include <netinet/udp.h> /* for nat traversal */ |
88 | |
89 | #if INET6 |
90 | #include <netinet/ip6.h> |
91 | #include <netinet6/ip6_var.h> |
92 | #include <netinet/icmp6.h> |
93 | #endif |
94 | |
95 | #include <netinet6/ipsec.h> |
96 | #if INET6 |
97 | #include <netinet6/ipsec6.h> |
98 | #endif |
99 | #include <netinet6/ah.h> |
100 | #if INET6 |
101 | #include <netinet6/ah6.h> |
102 | #endif |
103 | #include <netinet6/esp.h> |
104 | #if INET6 |
105 | #include <netinet6/esp6.h> |
106 | #endif |
107 | #include <netkey/key.h> |
108 | #include <netkey/keydb.h> |
109 | |
110 | #include <net/net_osdep.h> |
111 | |
112 | #include <sys/kdebug.h> |
113 | #define DBG_LAYER_BEG NETDBG_CODE(DBG_NETIPSEC, 1) |
114 | #define DBG_LAYER_END NETDBG_CODE(DBG_NETIPSEC, 3) |
115 | #define DBG_FNC_ESPOUT NETDBG_CODE(DBG_NETIPSEC, (4 << 8)) |
116 | #define DBG_FNC_ENCRYPT NETDBG_CODE(DBG_NETIPSEC, (5 << 8)) |
117 | |
118 | static int esp_output(struct mbuf *, u_char *, struct mbuf *, |
119 | int, struct secasvar *sav); |
120 | |
121 | extern int esp_udp_encap_port; |
122 | extern u_int64_t natt_now; |
123 | |
124 | extern lck_mtx_t *sadb_mutex; |
125 | |
126 | /* |
127 | * compute ESP header size. |
128 | */ |
129 | size_t |
130 | esp_hdrsiz(__unused struct ipsecrequest *isr) |
131 | { |
132 | |
133 | #if 0 |
134 | /* sanity check */ |
135 | if (isr == NULL) |
136 | panic("esp_hdrsiz: NULL was passed.\n" ); |
137 | |
138 | |
139 | lck_mtx_lock(sadb_mutex); |
140 | { |
141 | struct secasvar *sav; |
142 | const struct esp_algorithm *algo; |
143 | const struct ah_algorithm *aalgo; |
144 | size_t ivlen; |
145 | size_t authlen; |
146 | size_t hdrsiz; |
147 | size_t maxpad; |
148 | |
149 | /*%%%% this needs to change - no sav in ipsecrequest any more */ |
150 | sav = isr->sav; |
151 | |
152 | if (isr->saidx.proto != IPPROTO_ESP) |
153 | panic("unsupported mode passed to esp_hdrsiz" ); |
154 | |
155 | if (sav == NULL) |
156 | goto estimate; |
157 | if (sav->state != SADB_SASTATE_MATURE |
158 | && sav->state != SADB_SASTATE_DYING) |
159 | goto estimate; |
160 | |
161 | /* we need transport mode ESP. */ |
162 | algo = esp_algorithm_lookup(sav->alg_enc); |
163 | if (!algo) |
164 | goto estimate; |
165 | ivlen = sav->ivlen; |
166 | if (ivlen < 0) |
167 | goto estimate; |
168 | |
169 | if (algo->padbound) |
170 | maxpad = algo->padbound; |
171 | else |
172 | maxpad = 4; |
173 | maxpad += 1; /* maximum 'extendsiz' is padbound + 1, see esp_output */ |
174 | |
175 | if (sav->flags & SADB_X_EXT_OLD) { |
176 | /* RFC 1827 */ |
177 | hdrsiz = sizeof(struct esp) + ivlen + maxpad; |
178 | } else { |
179 | /* RFC 2406 */ |
180 | aalgo = ah_algorithm_lookup(sav->alg_auth); |
181 | if (aalgo && sav->replay && sav->key_auth) |
182 | authlen = (aalgo->sumsiz)(sav); |
183 | else |
184 | authlen = 0; |
185 | hdrsiz = sizeof(struct newesp) + ivlen + maxpad + authlen; |
186 | } |
187 | |
188 | /* |
189 | * If the security association indicates that NATT is required, |
190 | * add the size of the NATT encapsulation header: |
191 | */ |
192 | if ((sav->flags & SADB_X_EXT_NATT) != 0) hdrsiz += sizeof(struct udphdr) + 4; |
193 | |
194 | lck_mtx_unlock(sadb_mutex); |
195 | return hdrsiz; |
196 | } |
197 | estimate: |
198 | lck_mtx_unlock(sadb_mutex); |
199 | #endif |
200 | /* |
201 | * ASSUMING: |
202 | * sizeof(struct newesp) > sizeof(struct esp). (8) |
203 | * esp_max_ivlen() = max ivlen for CBC mode |
204 | * 17 = (maximum padding length without random padding length) |
205 | * + (Pad Length field) + (Next Header field). |
206 | * 64 = maximum ICV we support. |
207 | * sizeof(struct udphdr) in case NAT traversal is used |
208 | */ |
209 | return sizeof(struct newesp) + esp_max_ivlen() + 17 + AH_MAXSUMSIZE + sizeof(struct udphdr); |
210 | } |
211 | |
212 | /* |
213 | * Modify the packet so that the payload is encrypted. |
214 | * The mbuf (m) must start with IPv4 or IPv6 header. |
215 | * On failure, free the given mbuf and return NULL. |
216 | * |
217 | * on invocation: |
218 | * m nexthdrp md |
219 | * v v v |
220 | * IP ......... payload |
221 | * during the encryption: |
222 | * m nexthdrp mprev md |
223 | * v v v v |
224 | * IP ............... esp iv payload pad padlen nxthdr |
225 | * <--><-><------><---------------> |
226 | * esplen plen extendsiz |
227 | * ivlen |
228 | * <-----> esphlen |
229 | * <-> hlen |
230 | * <-----------------> espoff |
231 | */ |
232 | static int |
233 | esp_output( |
234 | struct mbuf *m, |
235 | u_char *nexthdrp, |
236 | struct mbuf *md, |
237 | int af, |
238 | struct secasvar *sav) |
239 | { |
240 | struct mbuf *n; |
241 | struct mbuf *mprev; |
242 | struct esp *esp; |
243 | struct esptail *esptail; |
244 | const struct esp_algorithm *algo; |
245 | u_int32_t spi; |
246 | u_int8_t nxt = 0; |
247 | size_t plen; /*payload length to be encrypted*/ |
248 | size_t espoff; |
249 | size_t esphlen; /* sizeof(struct esp/newesp) + ivlen */ |
250 | int ivlen; |
251 | int afnumber; |
252 | size_t extendsiz; |
253 | int error = 0; |
254 | struct ipsecstat *stat; |
255 | struct udphdr *udp = NULL; |
256 | int udp_encapsulate = (sav->flags & SADB_X_EXT_NATT && (af == AF_INET || af == AF_INET6) && |
257 | (esp_udp_encap_port & 0xFFFF) != 0); |
258 | |
259 | KERNEL_DEBUG(DBG_FNC_ESPOUT | DBG_FUNC_START, sav->ivlen,0,0,0,0); |
260 | switch (af) { |
261 | #if INET |
262 | case AF_INET: |
263 | afnumber = 4; |
264 | stat = &ipsecstat; |
265 | break; |
266 | #endif |
267 | #if INET6 |
268 | case AF_INET6: |
269 | afnumber = 6; |
270 | stat = &ipsec6stat; |
271 | break; |
272 | #endif |
273 | default: |
274 | ipseclog((LOG_ERR, "esp_output: unsupported af %d\n" , af)); |
275 | KERNEL_DEBUG(DBG_FNC_ESPOUT | DBG_FUNC_END, 1,0,0,0,0); |
276 | return 0; /* no change at all */ |
277 | } |
278 | |
279 | /* some sanity check */ |
280 | if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) { |
281 | switch (af) { |
282 | #if INET |
283 | case AF_INET: |
284 | { |
285 | struct ip *ip; |
286 | |
287 | ip = mtod(m, struct ip *); |
288 | ipseclog((LOG_DEBUG, "esp4_output: internal error: " |
289 | "sav->replay is null: %x->%x, SPI=%u\n" , |
290 | (u_int32_t)ntohl(ip->ip_src.s_addr), |
291 | (u_int32_t)ntohl(ip->ip_dst.s_addr), |
292 | (u_int32_t)ntohl(sav->spi))); |
293 | IPSEC_STAT_INCREMENT(ipsecstat.out_inval); |
294 | break; |
295 | } |
296 | #endif /*INET*/ |
297 | #if INET6 |
298 | case AF_INET6: |
299 | ipseclog((LOG_DEBUG, "esp6_output: internal error: " |
300 | "sav->replay is null: SPI=%u\n" , |
301 | (u_int32_t)ntohl(sav->spi))); |
302 | IPSEC_STAT_INCREMENT(ipsec6stat.out_inval); |
303 | break; |
304 | #endif /*INET6*/ |
305 | default: |
306 | panic("esp_output: should not reach here" ); |
307 | } |
308 | m_freem(m); |
309 | KERNEL_DEBUG(DBG_FNC_ESPOUT | DBG_FUNC_END, 2,0,0,0,0); |
310 | return EINVAL; |
311 | } |
312 | |
313 | algo = esp_algorithm_lookup(sav->alg_enc); |
314 | if (!algo) { |
315 | ipseclog((LOG_ERR, "esp_output: unsupported algorithm: " |
316 | "SPI=%u\n" , (u_int32_t)ntohl(sav->spi))); |
317 | m_freem(m); |
318 | KERNEL_DEBUG(DBG_FNC_ESPOUT | DBG_FUNC_END, 3,0,0,0,0); |
319 | return EINVAL; |
320 | } |
321 | spi = sav->spi; |
322 | ivlen = sav->ivlen; |
323 | /* should be okey */ |
324 | if (ivlen < 0) { |
325 | panic("invalid ivlen" ); |
326 | } |
327 | |
328 | { |
329 | /* |
330 | * insert ESP header. |
331 | * XXX inserts ESP header right after IPv4 header. should |
332 | * chase the header chain. |
333 | * XXX sequential number |
334 | */ |
335 | #if INET |
336 | struct ip *ip = NULL; |
337 | #endif |
338 | #if INET6 |
339 | struct ip6_hdr *ip6 = NULL; |
340 | #endif |
341 | size_t esplen; /* sizeof(struct esp/newesp) */ |
342 | size_t hlen = 0; /* ip header len */ |
343 | |
344 | if (sav->flags & SADB_X_EXT_OLD) { |
345 | /* RFC 1827 */ |
346 | esplen = sizeof(struct esp); |
347 | } else { |
348 | /* RFC 2406 */ |
349 | if (sav->flags & SADB_X_EXT_DERIV) |
350 | esplen = sizeof(struct esp); |
351 | else |
352 | esplen = sizeof(struct newesp); |
353 | } |
354 | esphlen = esplen + ivlen; |
355 | |
356 | for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) |
357 | ; |
358 | if (mprev == NULL || mprev->m_next != md) { |
359 | ipseclog((LOG_DEBUG, "esp%d_output: md is not in chain\n" , |
360 | afnumber)); |
361 | m_freem(m); |
362 | KERNEL_DEBUG(DBG_FNC_ESPOUT | DBG_FUNC_END, 4,0,0,0,0); |
363 | return EINVAL; |
364 | } |
365 | |
366 | plen = 0; |
367 | for (n = md; n; n = n->m_next) |
368 | plen += n->m_len; |
369 | |
370 | switch (af) { |
371 | #if INET |
372 | case AF_INET: |
373 | ip = mtod(m, struct ip *); |
374 | #ifdef _IP_VHL |
375 | hlen = IP_VHL_HL(ip->ip_vhl) << 2; |
376 | #else |
377 | hlen = ip->ip_hl << 2; |
378 | #endif |
379 | break; |
380 | #endif |
381 | #if INET6 |
382 | case AF_INET6: |
383 | ip6 = mtod(m, struct ip6_hdr *); |
384 | hlen = sizeof(*ip6); |
385 | break; |
386 | #endif |
387 | } |
388 | |
389 | /* make the packet over-writable */ |
390 | mprev->m_next = NULL; |
391 | if ((md = ipsec_copypkt(md)) == NULL) { |
392 | m_freem(m); |
393 | error = ENOBUFS; |
394 | goto fail; |
395 | } |
396 | mprev->m_next = md; |
397 | |
398 | /* |
399 | * Translate UDP source port back to its original value. |
400 | * SADB_X_EXT_NATT_MULTIPLEUSERS is only set for transort mode. |
401 | */ |
402 | if ((sav->flags & SADB_X_EXT_NATT_MULTIPLEUSERS) != 0) { |
403 | /* if not UDP - drop it */ |
404 | if (ip->ip_p != IPPROTO_UDP) { |
405 | IPSEC_STAT_INCREMENT(ipsecstat.out_inval); |
406 | m_freem(m); |
407 | error = EINVAL; |
408 | goto fail; |
409 | } |
410 | |
411 | udp = mtod(md, struct udphdr *); |
412 | |
413 | /* if src port not set in sav - find it */ |
414 | if (sav->natt_encapsulated_src_port == 0) |
415 | if (key_natt_get_translated_port(sav) == 0) { |
416 | m_freem(m); |
417 | error = EINVAL; |
418 | goto fail; |
419 | } |
420 | if (sav->remote_ike_port == htons(udp->uh_dport)) { |
421 | /* translate UDP port */ |
422 | udp->uh_dport = sav->natt_encapsulated_src_port; |
423 | udp->uh_sum = 0; /* don't need checksum with ESP auth */ |
424 | } else { |
425 | /* drop the packet - can't translate the port */ |
426 | IPSEC_STAT_INCREMENT(ipsecstat.out_inval); |
427 | m_freem(m); |
428 | error = EINVAL; |
429 | goto fail; |
430 | } |
431 | } |
432 | |
433 | |
434 | espoff = m->m_pkthdr.len - plen; |
435 | |
436 | if (udp_encapsulate) { |
437 | esphlen += sizeof(struct udphdr); |
438 | espoff += sizeof(struct udphdr); |
439 | } |
440 | |
441 | /* |
442 | * grow the mbuf to accomodate ESP header. |
443 | * before: IP ... payload |
444 | * after: IP ... [UDP] ESP IV payload |
445 | */ |
446 | if (M_LEADINGSPACE(md) < esphlen || (md->m_flags & M_EXT) != 0) { |
447 | MGET(n, M_DONTWAIT, MT_DATA); |
448 | if (!n) { |
449 | m_freem(m); |
450 | error = ENOBUFS; |
451 | goto fail; |
452 | } |
453 | n->m_len = esphlen; |
454 | mprev->m_next = n; |
455 | n->m_next = md; |
456 | m->m_pkthdr.len += esphlen; |
457 | if (udp_encapsulate) { |
458 | udp = mtod(n, struct udphdr *); |
459 | esp = (struct esp *)(void *)((caddr_t)udp + sizeof(struct udphdr)); |
460 | } else { |
461 | esp = mtod(n, struct esp *); |
462 | } |
463 | } else { |
464 | md->m_len += esphlen; |
465 | md->m_data -= esphlen; |
466 | m->m_pkthdr.len += esphlen; |
467 | esp = mtod(md, struct esp *); |
468 | if (udp_encapsulate) { |
469 | udp = mtod(md, struct udphdr *); |
470 | esp = (struct esp *)(void *)((caddr_t)udp + sizeof(struct udphdr)); |
471 | } else { |
472 | esp = mtod(md, struct esp *); |
473 | } |
474 | } |
475 | |
476 | switch (af) { |
477 | #if INET |
478 | case AF_INET: |
479 | if (esphlen < (IP_MAXPACKET - ntohs(ip->ip_len))) |
480 | ip->ip_len = htons(ntohs(ip->ip_len) + esphlen); |
481 | else { |
482 | ipseclog((LOG_ERR, |
483 | "IPv4 ESP output: size exceeds limit\n" )); |
484 | IPSEC_STAT_INCREMENT(ipsecstat.out_inval); |
485 | m_freem(m); |
486 | error = EMSGSIZE; |
487 | goto fail; |
488 | } |
489 | break; |
490 | #endif |
491 | #if INET6 |
492 | case AF_INET6: |
493 | /* total packet length will be computed in ip6_output() */ |
494 | break; |
495 | #endif |
496 | } |
497 | } |
498 | |
499 | /* initialize esp header. */ |
500 | esp->esp_spi = spi; |
501 | if ((sav->flags & SADB_X_EXT_OLD) == 0) { |
502 | struct newesp *nesp; |
503 | nesp = (struct newesp *)esp; |
504 | if (sav->replay->count == ~0) { |
505 | if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) { |
506 | /* XXX Is it noisy ? */ |
507 | ipseclog((LOG_WARNING, |
508 | "replay counter overflowed. %s\n" , |
509 | ipsec_logsastr(sav))); |
510 | IPSEC_STAT_INCREMENT(stat->out_inval); |
511 | m_freem(m); |
512 | KERNEL_DEBUG(DBG_FNC_ESPOUT | DBG_FUNC_END, 5,0,0,0,0); |
513 | return EINVAL; |
514 | } |
515 | } |
516 | lck_mtx_lock(sadb_mutex); |
517 | sav->replay->count++; |
518 | lck_mtx_unlock(sadb_mutex); |
519 | /* |
520 | * XXX sequence number must not be cycled, if the SA is |
521 | * installed by IKE daemon. |
522 | */ |
523 | nesp->esp_seq = htonl(sav->replay->count); |
524 | } |
525 | |
526 | { |
527 | /* |
528 | * find the last mbuf. make some room for ESP trailer. |
529 | */ |
530 | #if INET |
531 | struct ip *ip = NULL; |
532 | #endif |
533 | size_t padbound; |
534 | u_char *extend; |
535 | int i; |
536 | int randpadmax; |
537 | |
538 | if (algo->padbound) |
539 | padbound = algo->padbound; |
540 | else |
541 | padbound = 4; |
542 | /* ESP packet, including nxthdr field, must be length of 4n */ |
543 | if (padbound < 4) |
544 | padbound = 4; |
545 | |
546 | extendsiz = padbound - (plen % padbound); |
547 | if (extendsiz == 1) |
548 | extendsiz = padbound + 1; |
549 | |
550 | /* random padding */ |
551 | switch (af) { |
552 | #if INET |
553 | case AF_INET: |
554 | randpadmax = ip4_esp_randpad; |
555 | break; |
556 | #endif |
557 | #if INET6 |
558 | case AF_INET6: |
559 | randpadmax = ip6_esp_randpad; |
560 | break; |
561 | #endif |
562 | default: |
563 | randpadmax = -1; |
564 | break; |
565 | } |
566 | if (randpadmax < 0 || plen + extendsiz >= randpadmax) |
567 | ; |
568 | else { |
569 | int pad; |
570 | |
571 | /* round */ |
572 | randpadmax = (randpadmax / padbound) * padbound; |
573 | pad = (randpadmax - plen + extendsiz) / padbound; |
574 | |
575 | if (pad > 0) |
576 | pad = (random() % pad) * padbound; |
577 | else |
578 | pad = 0; |
579 | |
580 | /* |
581 | * make sure we do not pad too much. |
582 | * MLEN limitation comes from the trailer attachment |
583 | * code below. |
584 | * 256 limitation comes from sequential padding. |
585 | * also, the 1-octet length field in ESP trailer imposes |
586 | * limitation (but is less strict than sequential padding |
587 | * as length field do not count the last 2 octets). |
588 | */ |
589 | if (extendsiz + pad <= MLEN && extendsiz + pad < 256) |
590 | extendsiz += pad; |
591 | } |
592 | |
593 | #if DIAGNOSTIC |
594 | if (extendsiz > MLEN || extendsiz >= 256) |
595 | panic("extendsiz too big in esp_output" ); |
596 | #endif |
597 | |
598 | n = m; |
599 | while (n->m_next) |
600 | n = n->m_next; |
601 | |
602 | /* |
603 | * if M_EXT, the external mbuf data may be shared among |
604 | * two consequtive TCP packets, and it may be unsafe to use the |
605 | * trailing space. |
606 | */ |
607 | if (!(n->m_flags & M_EXT) && extendsiz < M_TRAILINGSPACE(n)) { |
608 | extend = mtod(n, u_char *) + n->m_len; |
609 | n->m_len += extendsiz; |
610 | m->m_pkthdr.len += extendsiz; |
611 | } else { |
612 | struct mbuf *nn; |
613 | |
614 | MGET(nn, M_DONTWAIT, MT_DATA); |
615 | if (!nn) { |
616 | ipseclog((LOG_DEBUG, "esp%d_output: can't alloc mbuf" , |
617 | afnumber)); |
618 | m_freem(m); |
619 | error = ENOBUFS; |
620 | goto fail; |
621 | } |
622 | extend = mtod(nn, u_char *); |
623 | nn->m_len = extendsiz; |
624 | nn->m_next = NULL; |
625 | n->m_next = nn; |
626 | n = nn; |
627 | m->m_pkthdr.len += extendsiz; |
628 | } |
629 | switch (sav->flags & SADB_X_EXT_PMASK) { |
630 | case SADB_X_EXT_PRAND: |
631 | key_randomfill(extend, extendsiz); |
632 | break; |
633 | case SADB_X_EXT_PZERO: |
634 | bzero(extend, extendsiz); |
635 | break; |
636 | case SADB_X_EXT_PSEQ: |
637 | for (i = 0; i < extendsiz; i++) |
638 | extend[i] = (i + 1) & 0xff; |
639 | break; |
640 | } |
641 | |
642 | nxt = *nexthdrp; |
643 | if (udp_encapsulate) { |
644 | *nexthdrp = IPPROTO_UDP; |
645 | |
646 | /* Fill out the UDP header */ |
647 | udp->uh_sport = ntohs((u_short)esp_udp_encap_port); |
648 | udp->uh_dport = ntohs(sav->remote_ike_port); |
649 | // udp->uh_len set later, after all length tweaks are complete |
650 | udp->uh_sum = 0; |
651 | |
652 | /* Update last sent so we know if we need to send keepalive */ |
653 | sav->natt_last_activity = natt_now; |
654 | } else { |
655 | *nexthdrp = IPPROTO_ESP; |
656 | } |
657 | |
658 | /* initialize esp trailer. */ |
659 | esptail = (struct esptail *) |
660 | (mtod(n, u_int8_t *) + n->m_len - sizeof(struct esptail)); |
661 | esptail->esp_nxt = nxt; |
662 | esptail->esp_padlen = extendsiz - 2; |
663 | |
664 | /* modify IP header (for ESP header part only) */ |
665 | switch (af) { |
666 | #if INET |
667 | case AF_INET: |
668 | ip = mtod(m, struct ip *); |
669 | if (extendsiz < (IP_MAXPACKET - ntohs(ip->ip_len))) |
670 | ip->ip_len = htons(ntohs(ip->ip_len) + extendsiz); |
671 | else { |
672 | ipseclog((LOG_ERR, |
673 | "IPv4 ESP output: size exceeds limit\n" )); |
674 | IPSEC_STAT_INCREMENT(ipsecstat.out_inval); |
675 | m_freem(m); |
676 | error = EMSGSIZE; |
677 | goto fail; |
678 | } |
679 | break; |
680 | #endif |
681 | #if INET6 |
682 | case AF_INET6: |
683 | /* total packet length will be computed in ip6_output() */ |
684 | break; |
685 | #endif |
686 | } |
687 | } |
688 | |
689 | /* |
690 | * pre-compute and cache intermediate key |
691 | */ |
692 | error = esp_schedule(algo, sav); |
693 | if (error) { |
694 | m_freem(m); |
695 | IPSEC_STAT_INCREMENT(stat->out_inval); |
696 | goto fail; |
697 | } |
698 | |
699 | /* |
700 | * encrypt the packet, based on security association |
701 | * and the algorithm specified. |
702 | */ |
703 | if (!algo->encrypt) |
704 | panic("internal error: no encrypt function" ); |
705 | KERNEL_DEBUG(DBG_FNC_ENCRYPT | DBG_FUNC_START, 0,0,0,0,0); |
706 | if ((*algo->encrypt)(m, espoff, plen + extendsiz, sav, algo, ivlen)) { |
707 | /* m is already freed */ |
708 | ipseclog((LOG_ERR, "packet encryption failure\n" )); |
709 | IPSEC_STAT_INCREMENT(stat->out_inval); |
710 | error = EINVAL; |
711 | KERNEL_DEBUG(DBG_FNC_ENCRYPT | DBG_FUNC_END, 1,error,0,0,0); |
712 | goto fail; |
713 | } |
714 | KERNEL_DEBUG(DBG_FNC_ENCRYPT | DBG_FUNC_END, 2,0,0,0,0); |
715 | |
716 | /* |
717 | * calculate ICV if required. |
718 | */ |
719 | size_t siz = 0; |
720 | u_char authbuf[AH_MAXSUMSIZE] __attribute__((aligned(4))); |
721 | |
722 | if (algo->finalizeencrypt) { |
723 | siz = algo->icvlen; |
724 | if ((*algo->finalizeencrypt)(sav, authbuf, siz)) { |
725 | ipseclog((LOG_ERR, "packet encryption ICV failure\n" )); |
726 | IPSEC_STAT_INCREMENT(stat->out_inval); |
727 | error = EINVAL; |
728 | KERNEL_DEBUG(DBG_FNC_ENCRYPT | DBG_FUNC_END, 1,error,0,0,0); |
729 | goto fail; |
730 | } |
731 | goto fill_icv; |
732 | } |
733 | |
734 | if (!sav->replay) |
735 | goto noantireplay; |
736 | if (!sav->key_auth) |
737 | goto noantireplay; |
738 | if (sav->key_auth == SADB_AALG_NONE) |
739 | goto noantireplay; |
740 | |
741 | { |
742 | const struct ah_algorithm *aalgo; |
743 | |
744 | aalgo = ah_algorithm_lookup(sav->alg_auth); |
745 | if (!aalgo) |
746 | goto noantireplay; |
747 | siz = ((aalgo->sumsiz)(sav) + 3) & ~(4 - 1); |
748 | if (AH_MAXSUMSIZE < siz) |
749 | panic("assertion failed for AH_MAXSUMSIZE" ); |
750 | |
751 | if (esp_auth(m, espoff, m->m_pkthdr.len - espoff, sav, authbuf)) { |
752 | ipseclog((LOG_ERR, "ESP checksum generation failure\n" )); |
753 | m_freem(m); |
754 | error = EINVAL; |
755 | IPSEC_STAT_INCREMENT(stat->out_inval); |
756 | goto fail; |
757 | } |
758 | } |
759 | |
760 | fill_icv: |
761 | { |
762 | struct ip *ip; |
763 | u_char *p; |
764 | |
765 | n = m; |
766 | while (n->m_next) |
767 | n = n->m_next; |
768 | |
769 | if (!(n->m_flags & M_EXT) && siz < M_TRAILINGSPACE(n)) { /* XXX */ |
770 | n->m_len += siz; |
771 | m->m_pkthdr.len += siz; |
772 | p = mtod(n, u_char *) + n->m_len - siz; |
773 | } else { |
774 | struct mbuf *nn; |
775 | |
776 | MGET(nn, M_DONTWAIT, MT_DATA); |
777 | if (!nn) { |
778 | ipseclog((LOG_DEBUG, "can't alloc mbuf in esp%d_output" , |
779 | afnumber)); |
780 | m_freem(m); |
781 | error = ENOBUFS; |
782 | goto fail; |
783 | } |
784 | nn->m_len = siz; |
785 | nn->m_next = NULL; |
786 | n->m_next = nn; |
787 | n = nn; |
788 | m->m_pkthdr.len += siz; |
789 | p = mtod(nn, u_char *); |
790 | } |
791 | bcopy(authbuf, p, siz); |
792 | |
793 | /* modify IP header (for ESP header part only) */ |
794 | switch (af) { |
795 | #if INET |
796 | case AF_INET: |
797 | ip = mtod(m, struct ip *); |
798 | if (siz < (IP_MAXPACKET - ntohs(ip->ip_len))) |
799 | ip->ip_len = htons(ntohs(ip->ip_len) + siz); |
800 | else { |
801 | ipseclog((LOG_ERR, |
802 | "IPv4 ESP output: size exceeds limit\n" )); |
803 | IPSEC_STAT_INCREMENT(ipsecstat.out_inval); |
804 | m_freem(m); |
805 | error = EMSGSIZE; |
806 | goto fail; |
807 | } |
808 | break; |
809 | #endif |
810 | #if INET6 |
811 | case AF_INET6: |
812 | /* total packet length will be computed in ip6_output() */ |
813 | break; |
814 | #endif |
815 | } |
816 | } |
817 | |
818 | if (udp_encapsulate) { |
819 | struct ip *ip; |
820 | struct ip6_hdr *ip6; |
821 | |
822 | switch (af) { |
823 | case AF_INET: |
824 | ip = mtod(m, struct ip *); |
825 | udp->uh_ulen = htons(ntohs(ip->ip_len) - (IP_VHL_HL(ip->ip_vhl) << 2)); |
826 | break; |
827 | case AF_INET6: |
828 | ip6 = mtod(m, struct ip6_hdr *); |
829 | udp->uh_ulen = htons(plen + siz + extendsiz + esphlen); |
830 | udp->uh_sum = in6_pseudo(&ip6->ip6_src, &ip6->ip6_dst, htonl(ntohs(udp->uh_ulen) + IPPROTO_UDP)); |
831 | m->m_pkthdr.csum_flags = (CSUM_UDPIPV6|CSUM_ZERO_INVERT); |
832 | m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); |
833 | break; |
834 | } |
835 | } |
836 | |
837 | noantireplay: |
838 | lck_mtx_lock(sadb_mutex); |
839 | if (!m) { |
840 | ipseclog((LOG_ERR, |
841 | "NULL mbuf after encryption in esp%d_output" , afnumber)); |
842 | } else |
843 | stat->out_success++; |
844 | stat->out_esphist[sav->alg_enc]++; |
845 | lck_mtx_unlock(sadb_mutex); |
846 | key_sa_recordxfer(sav, m); |
847 | KERNEL_DEBUG(DBG_FNC_ESPOUT | DBG_FUNC_END, 6,0,0,0,0); |
848 | return 0; |
849 | |
850 | fail: |
851 | #if 1 |
852 | KERNEL_DEBUG(DBG_FNC_ESPOUT | DBG_FUNC_END, 7,error,0,0,0); |
853 | return error; |
854 | #else |
855 | panic("something bad in esp_output" ); |
856 | #endif |
857 | } |
858 | |
859 | #if INET |
860 | int |
861 | esp4_output( |
862 | struct mbuf *m, |
863 | struct secasvar *sav) |
864 | { |
865 | struct ip *ip; |
866 | if (m->m_len < sizeof(struct ip)) { |
867 | ipseclog((LOG_DEBUG, "esp4_output: first mbuf too short\n" )); |
868 | m_freem(m); |
869 | return EINVAL; |
870 | } |
871 | ip = mtod(m, struct ip *); |
872 | /* XXX assumes that m->m_next points to payload */ |
873 | return esp_output(m, &ip->ip_p, m->m_next, AF_INET, sav); |
874 | } |
875 | #endif /*INET*/ |
876 | |
877 | #if INET6 |
878 | int |
879 | esp6_output( |
880 | struct mbuf *m, |
881 | u_char *nexthdrp, |
882 | struct mbuf *md, |
883 | struct secasvar *sav) |
884 | { |
885 | if (m->m_len < sizeof(struct ip6_hdr)) { |
886 | ipseclog((LOG_DEBUG, "esp6_output: first mbuf too short\n" )); |
887 | m_freem(m); |
888 | return EINVAL; |
889 | } |
890 | return esp_output(m, nexthdrp, md, AF_INET6, sav); |
891 | } |
892 | #endif /*INET6*/ |
893 | |