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