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