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