]> git.saurik.com Git - apple/xnu.git/blob - bsd/netinet6/ah_input.c
67a664ec62312ff5c89858cf89bda424b1174436
[apple/xnu.git] / bsd / netinet6 / ah_input.c
1 /*
2 * Copyright (c) 2008-2016 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/ah_input.c,v 1.1.2.6 2002/04/28 05:40:26 suz Exp $ */
30 /* $KAME: ah_input.c,v 1.67 2002/01/07 11:39:56 kjc 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 /*
62 * RFC1826/2402 authentication header.
63 */
64
65 #include <sys/param.h>
66 #include <sys/systm.h>
67 #include <sys/malloc.h>
68 #include <sys/mbuf.h>
69 #include <sys/mcache.h>
70 #include <sys/domain.h>
71 #include <sys/protosw.h>
72 #include <sys/socket.h>
73 #include <sys/errno.h>
74 #include <sys/time.h>
75 #include <sys/kernel.h>
76 #include <sys/syslog.h>
77
78 #include <net/if.h>
79 #include <net/if_ipsec.h>
80 #include <net/route.h>
81 #include <kern/cpu_number.h>
82 #include <kern/locks.h>
83
84 #include <netinet/in.h>
85 #include <netinet/in_systm.h>
86 #include <netinet/in_var.h>
87 #include <netinet/ip.h>
88 #include <netinet/ip_var.h>
89 #include <netinet/ip_ecn.h>
90 #include <netinet/in_pcb.h>
91 #if INET6
92 #include <netinet6/ip6_ecn.h>
93 #endif
94
95 #if INET6
96 #include <netinet/ip6.h>
97 #include <netinet6/ip6_var.h>
98 #include <netinet6/in6_pcb.h>
99 #include <netinet/icmp6.h>
100 #include <netinet6/ip6protosw.h>
101 #endif
102
103 #include <netinet6/ipsec.h>
104 #if INET6
105 #include <netinet6/ipsec6.h>
106 #endif
107 #include <netinet6/ah.h>
108 #if INET6
109 #include <netinet6/ah6.h>
110 #endif
111 #include <netkey/key.h>
112 #include <netkey/keydb.h>
113 #if IPSEC_DEBUG
114 #include <netkey/key_debug.h>
115 #else
116 #define KEYDEBUG(lev, arg)
117 #endif
118
119 #include <net/kpi_protocol.h>
120 #include <netinet/kpi_ipfilter_var.h>
121 #include <mach/sdt.h>
122
123 #include <net/net_osdep.h>
124
125 #define IPLEN_FLIPPED
126
127 extern lck_mtx_t *sadb_mutex;
128
129 #if INET
130 void
131 ah4_input(struct mbuf *m, int off)
132 {
133 struct ip *ip;
134 struct ah *ah;
135 u_int32_t spi;
136 const struct ah_algorithm *algo;
137 size_t siz;
138 size_t siz1;
139 u_char *cksum;
140 struct secasvar *sav = NULL;
141 u_int16_t nxt;
142 size_t hlen;
143 size_t stripsiz = 0;
144 sa_family_t ifamily;
145
146 if (m->m_len < off + sizeof(struct newah)) {
147 m = m_pullup(m, off + sizeof(struct newah));
148 if (!m) {
149 ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;"
150 "dropping the packet for simplicity\n"));
151 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
152 goto fail;
153 }
154 }
155
156 /* Expect 32-bit aligned data pointer on strict-align platforms */
157 MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m);
158
159 ip = mtod(m, struct ip *);
160 ah = (struct ah *)(void *)(((caddr_t)ip) + off);
161 nxt = ah->ah_nxt;
162 #ifdef _IP_VHL
163 hlen = IP_VHL_HL(ip->ip_vhl) << 2;
164 #else
165 hlen = ip->ip_hl << 2;
166 #endif
167
168 /* find the sassoc. */
169 spi = ah->ah_spi;
170
171 if ((sav = key_allocsa(AF_INET,
172 (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst,
173 IPPROTO_AH, spi)) == 0) {
174 ipseclog((LOG_WARNING,
175 "IPv4 AH input: no key association found for spi %u\n",
176 (u_int32_t)ntohl(spi)));
177 IPSEC_STAT_INCREMENT(ipsecstat.in_nosa);
178 goto fail;
179 }
180 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
181 printf("DP ah4_input called to allocate SA:0x%llx\n",
182 (uint64_t)VM_KERNEL_ADDRPERM(sav)));
183 if (sav->state != SADB_SASTATE_MATURE
184 && sav->state != SADB_SASTATE_DYING) {
185 ipseclog((LOG_DEBUG,
186 "IPv4 AH input: non-mature/dying SA found for spi %u\n",
187 (u_int32_t)ntohl(spi)));
188 IPSEC_STAT_INCREMENT(ipsecstat.in_badspi);
189 goto fail;
190 }
191
192 algo = ah_algorithm_lookup(sav->alg_auth);
193 if (!algo) {
194 ipseclog((LOG_DEBUG, "IPv4 AH input: "
195 "unsupported authentication algorithm for spi %u\n",
196 (u_int32_t)ntohl(spi)));
197 IPSEC_STAT_INCREMENT(ipsecstat.in_badspi);
198 goto fail;
199 }
200
201 siz = (*algo->sumsiz)(sav);
202 siz1 = ((siz + 3) & ~(4 - 1));
203
204 /*
205 * sanity checks for header, 1.
206 */
207 {
208 int sizoff;
209
210 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
211
212 /*
213 * Here, we do not do "siz1 == siz". This is because the way
214 * RFC240[34] section 2 is written. They do not require truncation
215 * to 96 bits.
216 * For example, Microsoft IPsec stack attaches 160 bits of
217 * authentication data for both hmac-md5 and hmac-sha1. For hmac-sha1,
218 * 32 bits of padding is attached.
219 *
220 * There are two downsides to this specification.
221 * They have no real harm, however, they leave us fuzzy feeling.
222 * - if we attach more than 96 bits of authentication data onto AH,
223 * we will never notice about possible modification by rogue
224 * intermediate nodes.
225 * Since extra bits in AH checksum is never used, this constitutes
226 * no real issue, however, it is wacky.
227 * - even if the peer attaches big authentication data, we will never
228 * notice the difference, since longer authentication data will just
229 * work.
230 *
231 * We may need some clarification in the spec.
232 */
233 if (siz1 < siz) {
234 ipseclog((LOG_NOTICE, "sum length too short in IPv4 AH input "
235 "(%u, should be at least %u): %s\n",
236 (u_int32_t)siz1, (u_int32_t)siz,
237 ipsec4_logpacketstr(ip, spi)));
238 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
239 goto fail;
240 }
241 if ((ah->ah_len << 2) - sizoff != siz1) {
242 ipseclog((LOG_NOTICE, "sum length mismatch in IPv4 AH input "
243 "(%d should be %u): %s\n",
244 (ah->ah_len << 2) - sizoff, (u_int32_t)siz1,
245 ipsec4_logpacketstr(ip, spi)));
246 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
247 goto fail;
248 }
249
250 if (m->m_len < off + sizeof(struct ah) + sizoff + siz1) {
251 m = m_pullup(m, off + sizeof(struct ah) + sizoff + siz1);
252 if (!m) {
253 ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n"));
254 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
255 goto fail;
256 }
257 /* Expect 32-bit aligned data ptr on strict-align platforms */
258 MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m);
259
260 ip = mtod(m, struct ip *);
261 ah = (struct ah *)(void *)(((caddr_t)ip) + off);
262 }
263 }
264
265 /*
266 * check for sequence number.
267 */
268 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay[0] != NULL) {
269 if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav, 0)) {
270 ; /*okey*/
271 } else {
272 IPSEC_STAT_INCREMENT(ipsecstat.in_ahreplay);
273 ipseclog((LOG_WARNING,
274 "replay packet in IPv4 AH input: %s %s\n",
275 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
276 goto fail;
277 }
278 }
279
280 /*
281 * alright, it seems sane. now we are going to check the
282 * cryptographic checksum.
283 */
284 cksum = _MALLOC(siz1, M_TEMP, M_NOWAIT);
285 if (!cksum) {
286 ipseclog((LOG_DEBUG, "IPv4 AH input: "
287 "couldn't alloc temporary region for cksum\n"));
288 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
289 goto fail;
290 }
291
292 /*
293 * some of IP header fields are flipped to the host endian.
294 * convert them back to network endian. VERY stupid.
295 */
296 ip->ip_len = htons(ip->ip_len + hlen);
297 ip->ip_off = htons(ip->ip_off);
298 if (ah4_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) {
299 FREE(cksum, M_TEMP);
300 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
301 goto fail;
302 }
303 IPSEC_STAT_INCREMENT(ipsecstat.in_ahhist[sav->alg_auth]);
304 /*
305 * flip them back.
306 */
307 ip->ip_len = ntohs(ip->ip_len) - hlen;
308 ip->ip_off = ntohs(ip->ip_off);
309
310 {
311 caddr_t sumpos = NULL;
312
313 if (sav->flags & SADB_X_EXT_OLD) {
314 /* RFC 1826 */
315 sumpos = (caddr_t)(ah + 1);
316 } else {
317 /* RFC 2402 */
318 sumpos = (caddr_t)(((struct newah *)ah) + 1);
319 }
320
321 if (bcmp(sumpos, cksum, siz) != 0) {
322 ipseclog((LOG_WARNING,
323 "checksum mismatch in IPv4 AH input: %s %s\n",
324 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
325 FREE(cksum, M_TEMP);
326 IPSEC_STAT_INCREMENT(ipsecstat.in_ahauthfail);
327 goto fail;
328 }
329 }
330
331 FREE(cksum, M_TEMP);
332
333 m->m_flags |= M_AUTHIPHDR;
334 m->m_flags |= M_AUTHIPDGM;
335
336 #if 0
337 /*
338 * looks okey, but we need more sanity check.
339 * XXX should elaborate.
340 */
341 if (ah->ah_nxt == IPPROTO_IPIP || ah->ah_nxt == IPPROTO_IP) {
342 struct ip *nip;
343 size_t sizoff;
344
345 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
346
347 if (m->m_len < off + sizeof(struct ah) + sizoff + siz1 + hlen) {
348 m = m_pullup(m, off + sizeof(struct ah)
349 + sizoff + siz1 + hlen);
350 if (!m) {
351 ipseclog((LOG_DEBUG,
352 "IPv4 AH input: can't pullup\n"));
353 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
354 goto fail;
355 }
356 }
357
358 nip = (struct ip *)((u_char *)(ah + 1) + sizoff + siz1);
359 if (nip->ip_src.s_addr != ip->ip_src.s_addr
360 || nip->ip_dst.s_addr != ip->ip_dst.s_addr) {
361 m->m_flags &= ~M_AUTHIPHDR;
362 m->m_flags &= ~M_AUTHIPDGM;
363 }
364 }
365 #if INET6
366 else if (ah->ah_nxt == IPPROTO_IPV6) {
367 m->m_flags &= ~M_AUTHIPHDR;
368 m->m_flags &= ~M_AUTHIPDGM;
369 }
370 #endif /*INET6*/
371 #endif /*0*/
372
373 if (m->m_flags & M_AUTHIPHDR
374 && m->m_flags & M_AUTHIPDGM) {
375 #if 0
376 ipseclog((LOG_DEBUG,
377 "IPv4 AH input: authentication succeess\n"));
378 #endif
379 IPSEC_STAT_INCREMENT(ipsecstat.in_ahauthsucc);
380 } else {
381 ipseclog((LOG_WARNING,
382 "authentication failed in IPv4 AH input: %s %s\n",
383 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
384 IPSEC_STAT_INCREMENT(ipsecstat.in_ahauthfail);
385 goto fail;
386 }
387
388 /*
389 * update sequence number.
390 */
391 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay[0] != NULL) {
392 if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav, 0)) {
393 IPSEC_STAT_INCREMENT(ipsecstat.in_ahreplay);
394 goto fail;
395 }
396 }
397
398 /* was it transmitted over the IPsec tunnel SA? */
399 if (sav->flags & SADB_X_EXT_OLD) {
400 /* RFC 1826 */
401 stripsiz = sizeof(struct ah) + siz1;
402 } else {
403 /* RFC 2402 */
404 stripsiz = sizeof(struct newah) + siz1;
405 }
406 if (ipsec4_tunnel_validate(m, off + stripsiz, nxt, sav, &ifamily)) {
407 ifaddr_t ifa;
408 struct sockaddr_storage addr;
409 struct sockaddr_in *ipaddr;
410
411 /*
412 * strip off all the headers that precedes AH.
413 * IP xx AH IP' payload -> IP' payload
414 *
415 * XXX more sanity checks
416 * XXX relationship with gif?
417 */
418 u_int8_t tos, otos;
419 int sum;
420
421 if (ifamily == AF_INET6) {
422 ipseclog((LOG_NOTICE, "ipsec tunnel protocol mismatch "
423 "in IPv4 AH input: %s\n", ipsec_logsastr(sav)));
424 goto fail;
425 }
426 tos = ip->ip_tos;
427 m_adj(m, off + stripsiz);
428 if (m->m_len < sizeof(*ip)) {
429 m = m_pullup(m, sizeof(*ip));
430 if (!m) {
431 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
432 goto fail;
433 }
434 }
435 ip = mtod(m, struct ip *);
436 otos = ip->ip_tos;
437 /* ECN consideration. */
438 if (ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos) == 0) {
439 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
440 goto fail;
441 }
442
443 if (otos != ip->ip_tos) {
444 sum = ~ntohs(ip->ip_sum) & 0xffff;
445 sum += (~otos & 0xffff) + ip->ip_tos;
446 sum = (sum >> 16) + (sum & 0xffff);
447 sum += (sum >> 16); /* add carry */
448 ip->ip_sum = htons(~sum & 0xffff);
449 }
450
451 if (!key_checktunnelsanity(sav, AF_INET,
452 (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) {
453 ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch "
454 "in IPv4 AH input: %s %s\n",
455 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
456 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
457 goto fail;
458 }
459
460 #if 1
461 /*
462 * Should the inner packet be considered authentic?
463 * My current answer is: NO.
464 *
465 * host1 -- gw1 === gw2 -- host2
466 * In this case, gw2 can trust the authenticity of the
467 * outer packet, but NOT inner. Packet may be altered
468 * between host1 and gw1.
469 *
470 * host1 -- gw1 === host2
471 * This case falls into the same scenario as above.
472 *
473 * host1 === host2
474 * This case is the only case when we may be able to leave
475 * M_AUTHIPHDR and M_AUTHIPDGM set.
476 * However, if host1 is wrongly configured, and allows
477 * attacker to inject some packet with src=host1 and
478 * dst=host2, you are in risk.
479 */
480 m->m_flags &= ~M_AUTHIPHDR;
481 m->m_flags &= ~M_AUTHIPDGM;
482 #endif
483
484 key_sa_recordxfer(sav, m);
485 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 ||
486 ipsec_addhist(m, IPPROTO_IPV4, 0) != 0) {
487 IPSEC_STAT_INCREMENT(ipsecstat.in_nomem);
488 goto fail;
489 }
490
491 bzero(&addr, sizeof(addr));
492 ipaddr = (__typeof__(ipaddr)) & addr;
493 ipaddr->sin_family = AF_INET;
494 ipaddr->sin_len = sizeof(*ipaddr);
495 ipaddr->sin_addr = ip->ip_dst;
496
497 // update the receiving interface address based on the inner address
498 ifa = ifa_ifwithaddr((struct sockaddr *)&addr);
499 if (ifa) {
500 m->m_pkthdr.rcvif = ifa->ifa_ifp;
501 IFA_REMREF(ifa);
502 }
503
504 // Input via IPsec interface
505 lck_mtx_lock(sadb_mutex);
506 ifnet_t ipsec_if = sav->sah->ipsec_if;
507 if (ipsec_if != NULL) {
508 // If an interface is found, add a reference count before dropping the lock
509 ifnet_reference(ipsec_if);
510 }
511 lck_mtx_unlock(sadb_mutex);
512 if (ipsec_if != NULL) {
513 errno_t inject_error = ipsec_inject_inbound_packet(ipsec_if, m);
514 ifnet_release(ipsec_if);
515 if (inject_error == 0) {
516 m = NULL;
517 goto done;
518 } else {
519 goto fail;
520 }
521 }
522
523 if (proto_input(PF_INET, m) != 0) {
524 goto fail;
525 }
526 nxt = IPPROTO_DONE;
527 } else {
528 /*
529 * strip off AH.
530 */
531
532 ip = mtod(m, struct ip *);
533 /*
534 * We do deep-copy since KAME requires that
535 * the packet is placed in a single external mbuf.
536 */
537 ovbcopy((caddr_t)ip, (caddr_t)(((u_char *)ip) + stripsiz), off);
538 m->m_data += stripsiz;
539 m->m_len -= stripsiz;
540 m->m_pkthdr.len -= stripsiz;
541
542 if (m->m_len < sizeof(*ip)) {
543 m = m_pullup(m, sizeof(*ip));
544 if (m == NULL) {
545 IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
546 goto fail;
547 }
548 }
549 ip = mtod(m, struct ip *);
550 #ifdef IPLEN_FLIPPED
551 ip->ip_len = ip->ip_len - stripsiz;
552 #else
553 ip->ip_len = htons(ntohs(ip->ip_len) - stripsiz);
554 #endif
555 ip->ip_p = nxt;
556 /* forget about IP hdr checksum, the check has already been passed */
557
558 key_sa_recordxfer(sav, m);
559 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) {
560 IPSEC_STAT_INCREMENT(ipsecstat.in_nomem);
561 goto fail;
562 }
563
564 DTRACE_IP6(receive, struct mbuf *, m, struct inpcb *, NULL,
565 struct ip *, ip, struct ifnet *, m->m_pkthdr.rcvif,
566 struct ip *, ip, struct ip6_hdr *, NULL);
567
568 if (nxt != IPPROTO_DONE) {
569 // Input via IPsec interface
570 lck_mtx_lock(sadb_mutex);
571 ifnet_t ipsec_if = sav->sah->ipsec_if;
572 if (ipsec_if != NULL) {
573 // If an interface is found, add a reference count before dropping the lock
574 ifnet_reference(ipsec_if);
575 }
576 lck_mtx_unlock(sadb_mutex);
577 if (ipsec_if != NULL) {
578 ip->ip_len = htons(ip->ip_len + hlen);
579 ip->ip_off = htons(ip->ip_off);
580 ip->ip_sum = 0;
581 ip->ip_sum = ip_cksum_hdr_in(m, hlen);
582 errno_t inject_error = ipsec_inject_inbound_packet(ipsec_if, m);
583 ifnet_release(ipsec_if);
584 if (inject_error == 0) {
585 m = NULL;
586 goto done;
587 } else {
588 goto fail;
589 }
590 }
591
592 if ((ip_protox[nxt]->pr_flags & PR_LASTHDR) != 0 &&
593 ipsec4_in_reject(m, NULL)) {
594 IPSEC_STAT_INCREMENT(ipsecstat.in_polvio);
595 goto fail;
596 }
597 ip_proto_dispatch_in(m, off, nxt, 0);
598 } else {
599 m_freem(m);
600 }
601 m = NULL;
602 }
603 done:
604 if (sav) {
605 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
606 printf("DP ah4_input call free SA:0x%llx\n",
607 (uint64_t)VM_KERNEL_ADDRPERM(sav)));
608 key_freesav(sav, KEY_SADB_UNLOCKED);
609 }
610 IPSEC_STAT_INCREMENT(ipsecstat.in_success);
611 return;
612
613 fail:
614 if (sav) {
615 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
616 printf("DP ah4_input call free SA:0x%llx\n",
617 (uint64_t)VM_KERNEL_ADDRPERM(sav)));
618 key_freesav(sav, KEY_SADB_UNLOCKED);
619 }
620 if (m) {
621 m_freem(m);
622 }
623 return;
624 }
625 #endif /* INET */
626
627 #if INET6
628 int
629 ah6_input(struct mbuf **mp, int *offp, int proto)
630 {
631 #pragma unused(proto)
632 struct mbuf *m = *mp;
633 int off = *offp;
634 struct ip6_hdr *ip6;
635 struct ah *ah;
636 u_int32_t spi;
637 const struct ah_algorithm *algo;
638 size_t siz;
639 size_t siz1;
640 u_char *cksum;
641 struct secasvar *sav = NULL;
642 u_int16_t nxt;
643 size_t stripsiz = 0;
644 sa_family_t ifamily;
645
646 IP6_EXTHDR_CHECK(m, off, sizeof(struct ah), {return IPPROTO_DONE;});
647 ah = (struct ah *)(void *)(mtod(m, caddr_t) + off);
648 /* Expect 32-bit aligned data pointer on strict-align platforms */
649 MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m);
650
651 ip6 = mtod(m, struct ip6_hdr *);
652 nxt = ah->ah_nxt;
653
654 /* find the sassoc. */
655 spi = ah->ah_spi;
656
657 if (ntohs(ip6->ip6_plen) == 0) {
658 ipseclog((LOG_ERR, "IPv6 AH input: "
659 "AH with IPv6 jumbogram is not supported.\n"));
660 IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
661 goto fail;
662 }
663
664 if ((sav = key_allocsa(AF_INET6,
665 (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst,
666 IPPROTO_AH, spi)) == 0) {
667 ipseclog((LOG_WARNING,
668 "IPv6 AH input: no key association found for spi %u\n",
669 (u_int32_t)ntohl(spi)));
670 IPSEC_STAT_INCREMENT(ipsec6stat.in_nosa);
671 goto fail;
672 }
673 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
674 printf("DP ah6_input called to allocate SA:0x%llx\n",
675 (uint64_t)VM_KERNEL_ADDRPERM(sav)));
676 if (sav->state != SADB_SASTATE_MATURE
677 && sav->state != SADB_SASTATE_DYING) {
678 ipseclog((LOG_DEBUG,
679 "IPv6 AH input: non-mature/dying SA found for spi %u; ",
680 (u_int32_t)ntohl(spi)));
681 IPSEC_STAT_INCREMENT(ipsec6stat.in_badspi);
682 goto fail;
683 }
684
685 algo = ah_algorithm_lookup(sav->alg_auth);
686 if (!algo) {
687 ipseclog((LOG_DEBUG, "IPv6 AH input: "
688 "unsupported authentication algorithm for spi %u\n",
689 (u_int32_t)ntohl(spi)));
690 IPSEC_STAT_INCREMENT(ipsec6stat.in_badspi);
691 goto fail;
692 }
693
694 siz = (*algo->sumsiz)(sav);
695 siz1 = ((siz + 3) & ~(4 - 1));
696
697 /*
698 * sanity checks for header, 1.
699 */
700 {
701 int sizoff;
702
703 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
704
705 /*
706 * Here, we do not do "siz1 == siz". See ah4_input() for complete
707 * description.
708 */
709 if (siz1 < siz) {
710 ipseclog((LOG_NOTICE, "sum length too short in IPv6 AH input "
711 "(%u, should be at least %u): %s\n",
712 (u_int32_t)siz1, (u_int32_t)siz,
713 ipsec6_logpacketstr(ip6, spi)));
714 IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
715 goto fail;
716 }
717 if ((ah->ah_len << 2) - sizoff != siz1) {
718 ipseclog((LOG_NOTICE, "sum length mismatch in IPv6 AH input "
719 "(%d should be %u): %s\n",
720 (ah->ah_len << 2) - sizoff, (u_int32_t)siz1,
721 ipsec6_logpacketstr(ip6, spi)));
722 IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
723 goto fail;
724 }
725 IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1,
726 {return IPPROTO_DONE;});
727 }
728
729 /*
730 * check for sequence number.
731 */
732 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay[0] != NULL) {
733 if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav, 0)) {
734 ; /*okey*/
735 } else {
736 IPSEC_STAT_INCREMENT(ipsec6stat.in_ahreplay);
737 ipseclog((LOG_WARNING,
738 "replay packet in IPv6 AH input: %s %s\n",
739 ipsec6_logpacketstr(ip6, spi),
740 ipsec_logsastr(sav)));
741 goto fail;
742 }
743 }
744
745 /*
746 * alright, it seems sane. now we are going to check the
747 * cryptographic checksum.
748 */
749 cksum = _MALLOC(siz1, M_TEMP, M_NOWAIT);
750 if (!cksum) {
751 ipseclog((LOG_DEBUG, "IPv6 AH input: "
752 "couldn't alloc temporary region for cksum\n"));
753 IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
754 goto fail;
755 }
756
757 if (ah6_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) {
758 FREE(cksum, M_TEMP);
759 IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
760 goto fail;
761 }
762 IPSEC_STAT_INCREMENT(ipsec6stat.in_ahhist[sav->alg_auth]);
763
764 {
765 caddr_t sumpos = NULL;
766
767 if (sav->flags & SADB_X_EXT_OLD) {
768 /* RFC 1826 */
769 sumpos = (caddr_t)(ah + 1);
770 } else {
771 /* RFC 2402 */
772 sumpos = (caddr_t)(((struct newah *)ah) + 1);
773 }
774
775 if (bcmp(sumpos, cksum, siz) != 0) {
776 ipseclog((LOG_WARNING,
777 "checksum mismatch in IPv6 AH input: %s %s\n",
778 ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
779 FREE(cksum, M_TEMP);
780 IPSEC_STAT_INCREMENT(ipsec6stat.in_ahauthfail);
781 goto fail;
782 }
783 }
784
785 FREE(cksum, M_TEMP);
786
787 m->m_flags |= M_AUTHIPHDR;
788 m->m_flags |= M_AUTHIPDGM;
789
790 #if 0
791 /*
792 * looks okey, but we need more sanity check.
793 * XXX should elaborate.
794 */
795 if (ah->ah_nxt == IPPROTO_IPV6) {
796 struct ip6_hdr *nip6;
797 size_t sizoff;
798
799 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
800
801 IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1
802 + sizeof(struct ip6_hdr),
803 {return IPPROTO_DONE;});
804
805 nip6 = (struct ip6_hdr *)((u_char *)(ah + 1) + sizoff + siz1);
806 if (!IN6_ARE_ADDR_EQUAL(&nip6->ip6_src, &ip6->ip6_src)
807 || !IN6_ARE_ADDR_EQUAL(&nip6->ip6_dst, &ip6->ip6_dst)) {
808 m->m_flags &= ~M_AUTHIPHDR;
809 m->m_flags &= ~M_AUTHIPDGM;
810 }
811 } else if (ah->ah_nxt == IPPROTO_IPIP) {
812 m->m_flags &= ~M_AUTHIPHDR;
813 m->m_flags &= ~M_AUTHIPDGM;
814 } else if (ah->ah_nxt == IPPROTO_IP) {
815 m->m_flags &= ~M_AUTHIPHDR;
816 m->m_flags &= ~M_AUTHIPDGM;
817 }
818 #endif
819
820 if (m->m_flags & M_AUTHIPHDR
821 && m->m_flags & M_AUTHIPDGM) {
822 #if 0
823 ipseclog((LOG_DEBUG,
824 "IPv6 AH input: authentication succeess\n"));
825 #endif
826 IPSEC_STAT_INCREMENT(ipsec6stat.in_ahauthsucc);
827 } else {
828 ipseclog((LOG_WARNING,
829 "authentication failed in IPv6 AH input: %s %s\n",
830 ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
831 IPSEC_STAT_INCREMENT(ipsec6stat.in_ahauthfail);
832 goto fail;
833 }
834
835 /*
836 * update sequence number.
837 */
838 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay[0] != NULL) {
839 if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav, 0)) {
840 IPSEC_STAT_INCREMENT(ipsec6stat.in_ahreplay);
841 goto fail;
842 }
843 }
844
845 /* was it transmitted over the IPsec tunnel SA? */
846 if (sav->flags & SADB_X_EXT_OLD) {
847 /* RFC 1826 */
848 stripsiz = sizeof(struct ah) + siz1;
849 } else {
850 /* RFC 2402 */
851 stripsiz = sizeof(struct newah) + siz1;
852 }
853 if (ipsec6_tunnel_validate(m, off + stripsiz, nxt, sav, &ifamily)) {
854 ifaddr_t ifa;
855 struct sockaddr_storage addr;
856 struct sockaddr_in6 *ip6addr;
857 /*
858 * strip off all the headers that precedes AH.
859 * IP6 xx AH IP6' payload -> IP6' payload
860 *
861 * XXX more sanity checks
862 * XXX relationship with gif?
863 */
864 u_int32_t flowinfo; /*net endian*/
865
866 if (ifamily == AF_INET) {
867 ipseclog((LOG_NOTICE, "ipsec tunnel protocol mismatch "
868 "in IPv6 AH input: %s\n", ipsec_logsastr(sav)));
869 goto fail;
870 }
871
872 flowinfo = ip6->ip6_flow;
873 m_adj(m, off + stripsiz);
874 if (m->m_len < sizeof(*ip6)) {
875 /*
876 * m_pullup is prohibited in KAME IPv6 input processing
877 * but there's no other way!
878 */
879 m = m_pullup(m, sizeof(*ip6));
880 if (!m) {
881 IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
882 goto fail;
883 }
884 }
885 ip6 = mtod(m, struct ip6_hdr *);
886 /* ECN consideration. */
887 if (ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow) == 0) {
888 IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
889 goto fail;
890 }
891 if (!key_checktunnelsanity(sav, AF_INET6,
892 (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) {
893 ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch "
894 "in IPv6 AH input: %s %s\n",
895 ipsec6_logpacketstr(ip6, spi),
896 ipsec_logsastr(sav)));
897 IPSEC_STAT_INCREMENT(ipsec6stat.in_inval);
898 goto fail;
899 }
900
901 #if 1
902 /*
903 * should the inner packet be considered authentic?
904 * see comment in ah4_input().
905 */
906 m->m_flags &= ~M_AUTHIPHDR;
907 m->m_flags &= ~M_AUTHIPDGM;
908 #endif
909
910 key_sa_recordxfer(sav, m);
911 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 ||
912 ipsec_addhist(m, IPPROTO_IPV6, 0) != 0) {
913 IPSEC_STAT_INCREMENT(ipsec6stat.in_nomem);
914 goto fail;
915 }
916
917 bzero(&addr, sizeof(addr));
918 ip6addr = (__typeof__(ip6addr)) & addr;
919 ip6addr->sin6_family = AF_INET6;
920 ip6addr->sin6_len = sizeof(*ip6addr);
921 ip6addr->sin6_addr = ip6->ip6_dst;
922
923 // update the receiving interface address based on the inner address
924 ifa = ifa_ifwithaddr((struct sockaddr *)&addr);
925 if (ifa) {
926 m->m_pkthdr.rcvif = ifa->ifa_ifp;
927 IFA_REMREF(ifa);
928 }
929
930 // Input via IPsec interface
931 lck_mtx_lock(sadb_mutex);
932 ifnet_t ipsec_if = sav->sah->ipsec_if;
933 if (ipsec_if != NULL) {
934 // If an interface is found, add a reference count before dropping the lock
935 ifnet_reference(ipsec_if);
936 }
937 lck_mtx_unlock(sadb_mutex);
938 if (ipsec_if != NULL) {
939 errno_t inject_error = ipsec_inject_inbound_packet(ipsec_if, m);
940 ifnet_release(ipsec_if);
941 if (inject_error == 0) {
942 m = NULL;
943 nxt = IPPROTO_DONE;
944 goto done;
945 } else {
946 goto fail;
947 }
948 }
949
950 if (proto_input(PF_INET6, m) != 0) {
951 goto fail;
952 }
953 nxt = IPPROTO_DONE;
954 } else {
955 /*
956 * strip off AH.
957 */
958 char *prvnxtp;
959
960 /*
961 * Copy the value of the next header field of AH to the
962 * next header field of the previous header.
963 * This is necessary because AH will be stripped off below.
964 */
965 prvnxtp = ip6_get_prevhdr(m, off); /* XXX */
966 *prvnxtp = nxt;
967
968 ip6 = mtod(m, struct ip6_hdr *);
969 /*
970 * We do deep-copy since KAME requires that
971 * the packet is placed in a single mbuf.
972 */
973 ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off);
974 m->m_data += stripsiz;
975 m->m_len -= stripsiz;
976 m->m_pkthdr.len -= stripsiz;
977 ip6 = mtod(m, struct ip6_hdr *);
978 /* XXX jumbogram */
979 ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz);
980
981 key_sa_recordxfer(sav, m);
982 if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) {
983 IPSEC_STAT_INCREMENT(ipsec6stat.in_nomem);
984 goto fail;
985 }
986
987 // Input via IPsec interface
988 lck_mtx_lock(sadb_mutex);
989 ifnet_t ipsec_if = sav->sah->ipsec_if;
990 if (ipsec_if != NULL) {
991 // If an interface is found, add a reference count before dropping the lock
992 ifnet_reference(ipsec_if);
993 }
994 lck_mtx_unlock(sadb_mutex);
995 if (ipsec_if != NULL) {
996 errno_t inject_error = ipsec_inject_inbound_packet(ipsec_if, m);
997 ifnet_release(ipsec_if);
998 if (inject_error == 0) {
999 m = NULL;
1000 nxt = IPPROTO_DONE;
1001 goto done;
1002 } else {
1003 goto fail;
1004 }
1005 }
1006 }
1007
1008 done:
1009 *offp = off;
1010 *mp = m;
1011 if (sav) {
1012 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
1013 printf("DP ah6_input call free SA:0x%llx\n",
1014 (uint64_t)VM_KERNEL_ADDRPERM(sav)));
1015 key_freesav(sav, KEY_SADB_UNLOCKED);
1016 }
1017 IPSEC_STAT_INCREMENT(ipsec6stat.in_success);
1018 return nxt;
1019
1020 fail:
1021 if (sav) {
1022 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
1023 printf("DP ah6_input call free SA:0x%llx\n",
1024 (uint64_t)VM_KERNEL_ADDRPERM(sav)));
1025 key_freesav(sav, KEY_SADB_UNLOCKED);
1026 }
1027 if (m) {
1028 m_freem(m);
1029 }
1030 return IPPROTO_DONE;
1031 }
1032
1033 void
1034 ah6_ctlinput(int cmd, struct sockaddr *sa, void *d)
1035 {
1036 const struct newah *ahp;
1037 struct newah ah;
1038 struct secasvar *sav;
1039 struct ip6_hdr *ip6;
1040 struct mbuf *m;
1041 struct ip6ctlparam *ip6cp = NULL;
1042 int off = 0;
1043 struct sockaddr_in6 *sa6_src, *sa6_dst;
1044
1045 if (sa->sa_family != AF_INET6 ||
1046 sa->sa_len != sizeof(struct sockaddr_in6)) {
1047 return;
1048 }
1049 if ((unsigned)cmd >= PRC_NCMDS) {
1050 return;
1051 }
1052
1053 /* if the parameter is from icmp6, decode it. */
1054 if (d != NULL) {
1055 ip6cp = (struct ip6ctlparam *)d;
1056 m = ip6cp->ip6c_m;
1057 ip6 = ip6cp->ip6c_ip6;
1058 off = ip6cp->ip6c_off;
1059 } else {
1060 m = NULL;
1061 ip6 = NULL;
1062 }
1063
1064 if (ip6) {
1065 /*
1066 * XXX: We assume that when ip6 is non NULL,
1067 * M and OFF are valid.
1068 */
1069
1070 /* check if we can safely examine src and dst ports */
1071 if (m->m_pkthdr.len < off + sizeof(ah)) {
1072 return;
1073 }
1074
1075 if (m->m_len < off + sizeof(ah)) {
1076 /*
1077 * this should be rare case,
1078 * so we compromise on this copy...
1079 */
1080 m_copydata(m, off, sizeof(ah), (caddr_t)&ah);
1081 ahp = &ah;
1082 } else {
1083 ahp = (struct newah *)(void *)(mtod(m, caddr_t) + off);
1084 }
1085
1086 if (cmd == PRC_MSGSIZE) {
1087 int valid = 0;
1088
1089 /*
1090 * Check to see if we have a valid SA corresponding to
1091 * the address in the ICMP message payload.
1092 */
1093 sa6_src = ip6cp->ip6c_src;
1094 sa6_dst = (struct sockaddr_in6 *)(void *)sa;
1095 sav = key_allocsa(AF_INET6,
1096 (caddr_t)&sa6_src->sin6_addr,
1097 (caddr_t)&sa6_dst->sin6_addr,
1098 IPPROTO_AH, ahp->ah_spi);
1099 if (sav) {
1100 if (sav->state == SADB_SASTATE_MATURE ||
1101 sav->state == SADB_SASTATE_DYING) {
1102 valid++;
1103 }
1104 key_freesav(sav, KEY_SADB_UNLOCKED);
1105 }
1106
1107 /* XXX Further validation? */
1108
1109 /*
1110 * Depending on the value of "valid" and routing table
1111 * size (mtudisc_{hi,lo}wat), we will:
1112 * - recalcurate the new MTU and create the
1113 * corresponding routing entry, or
1114 * - ignore the MTU change notification.
1115 */
1116 icmp6_mtudisc_update((struct ip6ctlparam *)d, valid);
1117 }
1118
1119 /* we normally notify single pcb here */
1120 } else {
1121 /* we normally notify any pcb here */
1122 }
1123 }
1124 #endif /* INET6 */