]> git.saurik.com Git - apple/xnu.git/blob - bsd/netinet6/ah_input.c
xnu-201.42.3.tar.gz
[apple/xnu.git] / bsd / netinet6 / ah_input.c
1 /*
2 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the project nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 /*
31 * RFC1826/2402 authentication header.
32 */
33
34 #define _IP_VHL
35 #if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
36 #include "opt_inet.h"
37 #endif
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/malloc.h>
42 #include <sys/mbuf.h>
43 #include <sys/domain.h>
44 #include <sys/protosw.h>
45 #include <sys/socket.h>
46 #include <sys/errno.h>
47 #include <sys/time.h>
48 #include <sys/kernel.h>
49 #include <sys/syslog.h>
50
51 #include <net/if.h>
52 #include <net/route.h>
53 #include <net/netisr.h>
54 #include <kern/cpu_number.h>
55
56 #include <netinet/in.h>
57 #include <netinet/in_systm.h>
58 #include <netinet/in_var.h>
59 #include <netinet/ip.h>
60 #include <netinet/ip_var.h>
61 #include <netinet/ip_ecn.h>
62
63 #if INET6
64 #include <netinet/ip6.h>
65 #include <netinet6/ip6_var.h>
66 #include <netinet/icmp6.h>
67 #endif
68
69 #include <netinet6/ipsec.h>
70 #include <netinet6/ah.h>
71 #include <netkey/key.h>
72 #include <netkey/keydb.h>
73 #include <netkey/key_debug.h>
74
75
76 #include <net/net_osdep.h>
77
78 #define IPLEN_FLIPPED
79
80 #if INET
81 extern struct protosw inetsw[];
82 #if defined(__bsdi__) || defined(__NetBSD__)
83 extern u_char ip_protox[];
84 #endif
85
86 void
87 ah4_input(struct mbuf *m, int off)
88 {
89 struct ip *ip;
90 struct ah *ah;
91 u_int32_t spi;
92 struct ah_algorithm *algo;
93 size_t siz;
94 size_t siz1;
95 u_char *cksum;
96 struct secasvar *sav = NULL;
97 u_int16_t nxt;
98 size_t hlen;
99 int s;
100
101 #ifndef PULLDOWN_TEST
102 if (m->m_len < off + sizeof(struct newah)) {
103 m = m_pullup(m, off + sizeof(struct newah));
104 if (!m) {
105 ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;"
106 "dropping the packet for simplicity\n"));
107 ipsecstat.in_inval++;
108 goto fail;
109 }
110 }
111
112 ip = mtod(m, struct ip *);
113 ah = (struct ah *)(((caddr_t)ip) + off);
114 #else
115 ip = mtod(m, struct ip *);
116 IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah));
117 if (ah == NULL) {
118 ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;"
119 "dropping the packet for simplicity\n"));
120 ipsecstat.in_inval++;
121 goto fail;
122 }
123 #endif
124 nxt = ah->ah_nxt;
125 #ifdef _IP_VHL
126 hlen = IP_VHL_HL(ip->ip_vhl) << 2;
127 #else
128 hlen = ip->ip_hl << 2;
129 #endif
130
131 /* find the sassoc. */
132 spi = ah->ah_spi;
133
134 if ((sav = key_allocsa(AF_INET,
135 (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst,
136 IPPROTO_AH, spi)) == 0) {
137 ipseclog((LOG_WARNING,
138 "IPv4 AH input: no key association found for spi %u\n",
139 (u_int32_t)ntohl(spi)));
140 ipsecstat.in_nosa++;
141 goto fail;
142 }
143 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
144 printf("DP ah4_input called to allocate SA:%p\n", sav));
145 if (sav->state != SADB_SASTATE_MATURE
146 && sav->state != SADB_SASTATE_DYING) {
147 ipseclog((LOG_DEBUG,
148 "IPv4 AH input: non-mature/dying SA found for spi %u\n",
149 (u_int32_t)ntohl(spi)));
150 ipsecstat.in_badspi++;
151 goto fail;
152 }
153 if (sav->alg_auth == SADB_AALG_NONE) {
154 ipseclog((LOG_DEBUG, "IPv4 AH input: "
155 "unspecified authentication algorithm for spi %u\n",
156 (u_int32_t)ntohl(spi)));
157 ipsecstat.in_badspi++;
158 goto fail;
159 }
160
161 algo = &ah_algorithms[sav->alg_auth];
162
163 siz = (*algo->sumsiz)(sav);
164 siz1 = ((siz + 3) & ~(4 - 1));
165
166 /*
167 * sanity checks for header, 1.
168 */
169 {
170 int sizoff;
171
172 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
173
174 if ((ah->ah_len << 2) - sizoff != siz1) {
175 ipseclog((LOG_NOTICE, "sum length mismatch in IPv4 AH input "
176 "(%d should be %u): %s\n",
177 (ah->ah_len << 2) - sizoff, (unsigned int)siz1,
178 ipsec4_logpacketstr(ip, spi)));
179 ipsecstat.in_inval++;
180 goto fail;
181 }
182
183 #ifndef PULLDOWN_TEST
184 if (m->m_len < off + sizeof(struct ah) + sizoff + siz1) {
185 m = m_pullup(m, off + sizeof(struct ah) + sizoff + siz1);
186 if (!m) {
187 ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n"));
188 ipsecstat.in_inval++;
189 goto fail;
190 }
191
192 ip = mtod(m, struct ip *);
193 ah = (struct ah *)(((caddr_t)ip) + off);
194 }
195 #else
196 IP6_EXTHDR_GET(ah, struct ah *, m, off,
197 sizeof(struct ah) + sizoff + siz1);
198 if (ah == NULL) {
199 ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n"));
200 ipsecstat.in_inval++;
201 goto fail;
202 }
203 #endif
204 }
205
206 /*
207 * check for sequence number.
208 */
209 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
210 if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav))
211 ; /*okey*/
212 else {
213 ipsecstat.in_ahreplay++;
214 ipseclog((LOG_WARNING,
215 "replay packet in IPv4 AH input: %s %s\n",
216 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
217 goto fail;
218 }
219 }
220
221 /*
222 * alright, it seems sane. now we are going to check the
223 * cryptographic checksum.
224 */
225 cksum = _MALLOC(siz1, M_TEMP, M_NOWAIT);
226 if (!cksum) {
227 ipseclog((LOG_DEBUG, "IPv4 AH input: "
228 "couldn't alloc temporary region for cksum\n"));
229 ipsecstat.in_inval++;
230 goto fail;
231 }
232
233 {
234 #if 1
235 /*
236 * some of IP header fields are flipped to the host endian.
237 * convert them back to network endian. VERY stupid.
238 */
239 #ifndef __NetBSD__
240 ip->ip_len = htons(ip->ip_len + hlen);
241 ip->ip_id = htons(ip->ip_id);
242 #else
243 ip->ip_len = htons(ip->ip_len);
244 #endif
245 ip->ip_off = htons(ip->ip_off);
246 #endif
247 if (ah4_calccksum(m, (caddr_t)cksum, algo, sav)) {
248 _FREE(cksum, M_TEMP);
249 ipsecstat.in_inval++;
250 goto fail;
251 }
252 ipsecstat.in_ahhist[sav->alg_auth]++;
253 #if 1
254 /*
255 * flip them back.
256 */
257 #ifndef __NetBSD__
258 ip->ip_len = ntohs(ip->ip_len) - hlen;
259 ip->ip_id = ntohs(ip->ip_id);
260 #else
261 ip->ip_len = ntohs(ip->ip_len);
262 #endif
263 ip->ip_off = ntohs(ip->ip_off);
264 #endif
265 }
266
267 {
268 caddr_t sumpos = NULL;
269
270 if (sav->flags & SADB_X_EXT_OLD) {
271 /* RFC 1826 */
272 sumpos = (caddr_t)(ah + 1);
273 } else {
274 /* RFC 2402 */
275 sumpos = (caddr_t)(((struct newah *)ah) + 1);
276 }
277
278 if (bcmp(sumpos, cksum, siz) != 0) {
279 ipseclog((LOG_WARNING,
280 "checksum mismatch in IPv4 AH input: %s %s\n",
281 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
282 _FREE(cksum, M_TEMP);
283 ipsecstat.in_ahauthfail++;
284 goto fail;
285 }
286 }
287
288 _FREE(cksum, M_TEMP);
289
290 m->m_flags |= M_AUTHIPHDR;
291 m->m_flags |= M_AUTHIPDGM;
292
293 #if 0
294 /*
295 * looks okey, but we need more sanity check.
296 * XXX should elaborate.
297 */
298 if (ah->ah_nxt == IPPROTO_IPIP || ah->ah_nxt == IPPROTO_IP) {
299 struct ip *nip;
300 size_t sizoff;
301
302 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
303
304 if (m->m_len < off + sizeof(struct ah) + sizoff + siz1 + hlen) {
305 m = m_pullup(m, off + sizeof(struct ah)
306 + sizoff + siz1 + hlen);
307 if (!m) {
308 ipseclog((LOG_DEBUG,
309 "IPv4 AH input: can't pullup\n"));
310 ipsecstat.in_inval++;
311 goto fail;
312 }
313 }
314
315 nip = (struct ip *)((u_char *)(ah + 1) + sizoff + siz1);
316 if (nip->ip_src.s_addr != ip->ip_src.s_addr
317 || nip->ip_dst.s_addr != ip->ip_dst.s_addr) {
318 m->m_flags &= ~M_AUTHIPHDR;
319 m->m_flags &= ~M_AUTHIPDGM;
320 }
321 }
322 #if INET6
323 else if (ah->ah_nxt == IPPROTO_IPV6) {
324 m->m_flags &= ~M_AUTHIPHDR;
325 m->m_flags &= ~M_AUTHIPDGM;
326 }
327 #endif /*INET6*/
328 #endif /*0*/
329
330 if (m->m_flags & M_AUTHIPHDR
331 && m->m_flags & M_AUTHIPDGM) {
332 #if 0
333 ipseclog((LOG_DEBUG,
334 "IPv4 AH input: authentication succeess\n"));
335 #endif
336 ipsecstat.in_ahauthsucc++;
337 } else {
338 ipseclog((LOG_WARNING,
339 "authentication failed in IPv4 AH input: %s %s\n",
340 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
341 ipsecstat.in_ahauthfail++;
342 goto fail;
343 }
344
345 /*
346 * update sequence number.
347 */
348 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
349 if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) {
350 ipsecstat.in_ahreplay++;
351 goto fail;
352 }
353 }
354
355 /* was it transmitted over the IPsec tunnel SA? */
356 if (ipsec4_tunnel_validate(ip, nxt, sav) && nxt == IPPROTO_IPV4) {
357 /*
358 * strip off all the headers that precedes AH.
359 * IP xx AH IP' payload -> IP' payload
360 *
361 * XXX more sanity checks
362 * XXX relationship with gif?
363 */
364 size_t stripsiz = 0;
365 u_int8_t tos;
366
367 tos = ip->ip_tos;
368 if (sav->flags & SADB_X_EXT_OLD) {
369 /* RFC 1826 */
370 stripsiz = sizeof(struct ah) + siz1;
371 } else {
372 /* RFC 2402 */
373 stripsiz = sizeof(struct newah) + siz1;
374 }
375 m_adj(m, off + stripsiz);
376 if (m->m_len < sizeof(*ip)) {
377 m = m_pullup(m, sizeof(*ip));
378 if (!m) {
379 ipsecstat.in_inval++;
380 goto fail;
381 }
382 }
383 ip = mtod(m, struct ip *);
384 /* ECN consideration. */
385 ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos);
386 if (!key_checktunnelsanity(sav, AF_INET,
387 (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) {
388 ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch "
389 "in IPv4 AH input: %s %s\n",
390 ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
391 ipsecstat.in_inval++;
392 goto fail;
393 }
394
395 #if 0 /* XXX should we call ipfw rather than ipsec_in_reject? */
396 /* drop it if it does not match the default policy */
397 if (ipsec4_in_reject(m, NULL)) {
398 ipsecstat.in_polvio++;
399 goto fail;
400 }
401 #endif
402
403 #if 1
404 /*
405 * Should the inner packet be considered authentic?
406 * My current answer is: NO.
407 *
408 * host1 -- gw1 === gw2 -- host2
409 * In this case, gw2 can trust the authenticity of the
410 * outer packet, but NOT inner. Packet may be altered
411 * between host1 and gw1.
412 *
413 * host1 -- gw1 === host2
414 * This case falls into the same scenario as above.
415 *
416 * host1 === host2
417 * This case is the only case when we may be able to leave
418 * M_AUTHIPHDR and M_AUTHIPDGM set.
419 * However, if host1 is wrongly configured, and allows
420 * attacker to inject some packet with src=host1 and
421 * dst=host2, you are in risk.
422 */
423 m->m_flags &= ~M_AUTHIPHDR;
424 m->m_flags &= ~M_AUTHIPDGM;
425 #endif
426
427 key_sa_recordxfer(sav, m);
428
429 s = splimp();
430 if (IF_QFULL(&ipintrq)) {
431 ipsecstat.in_inval++;
432 goto fail;
433 }
434 IF_ENQUEUE(&ipintrq, m);
435 m = NULL;
436 schednetisr(NETISR_IP); /*can be skipped but to make sure*/
437 splx(s);
438 nxt = IPPROTO_DONE;
439 } else {
440 /*
441 * strip off AH.
442 * We do deep-copy since KAME requires that
443 * the packet is placed in a single external mbuf.
444 */
445 size_t stripsiz = 0;
446
447 if (sav->flags & SADB_X_EXT_OLD) {
448 /* RFC 1826 */
449 stripsiz = sizeof(struct ah) + siz1;
450 } else {
451 /* RFC 2402 */
452 stripsiz = sizeof(struct newah) + siz1;
453 }
454
455 ip = mtod(m, struct ip *);
456 #ifndef PULLDOWN_TEST
457 ovbcopy((caddr_t)ip, (caddr_t)(((u_char *)ip) + stripsiz), off);
458 m->m_data += stripsiz;
459 m->m_len -= stripsiz;
460 m->m_pkthdr.len -= stripsiz;
461 #else
462 /*
463 * even in m_pulldown case, we need to strip off AH so that
464 * we can compute checksum for multiple AH correctly.
465 */
466 if (m->m_len >= stripsiz + off) {
467 ovbcopy((caddr_t)ip, ((caddr_t)ip) + stripsiz, off);
468 m->m_data += stripsiz;
469 m->m_len -= stripsiz;
470 m->m_pkthdr.len -= stripsiz;
471 } else {
472 /*
473 * this comes with no copy if the boundary is on
474 * cluster
475 */
476 struct mbuf *n;
477
478 n = m_split(m, off, M_DONTWAIT);
479 if (n == NULL) {
480 /* m is retained by m_split */
481 goto fail;
482 }
483 m_adj(n, stripsiz);
484 m_cat(m, n);
485 /* m_cat does not update m_pkthdr.len */
486 m->m_pkthdr.len += n->m_pkthdr.len;
487 }
488 #endif
489
490 if (m->m_len < sizeof(*ip)) {
491 m = m_pullup(m, sizeof(*ip));
492 if (m == NULL) {
493 ipsecstat.in_inval++;
494 goto fail;
495 }
496 }
497 ip = mtod(m, struct ip *);
498 #ifdef IPLEN_FLIPPED
499 ip->ip_len = ip->ip_len - stripsiz;
500 #else
501 ip->ip_len = htons(ntohs(ip->ip_len) - stripsiz);
502 #endif
503 ip->ip_p = nxt;
504 /* forget about IP hdr checksum, the check has already been passed */
505
506 key_sa_recordxfer(sav, m);
507
508 if (nxt != IPPROTO_DONE)
509 (*ip_protox[nxt]->pr_input)(m, off);
510 else
511 m_freem(m);
512 m = NULL;
513 }
514
515 if (sav) {
516 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
517 printf("DP ah4_input call free SA:%p\n", sav));
518 key_freesav(sav);
519 }
520 ipsecstat.in_success++;
521 return;
522
523 fail:
524 if (sav) {
525 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
526 printf("DP ah4_input call free SA:%p\n", sav));
527 key_freesav(sav);
528 }
529 if (m)
530 m_freem(m);
531 return;
532 }
533 #endif /* INET */
534
535 #if INET6
536 int
537 ah6_input(mp, offp, proto)
538 struct mbuf **mp;
539 int *offp, proto;
540 {
541 struct mbuf *m = *mp;
542 int off = *offp;
543 struct ip6_hdr *ip6;
544 struct ah *ah;
545 u_int32_t spi;
546 struct ah_algorithm *algo;
547 size_t siz;
548 size_t siz1;
549 u_char *cksum;
550 struct secasvar *sav = NULL;
551 u_int16_t nxt;
552 int s;
553
554 #ifndef PULLDOWN_TEST
555 IP6_EXTHDR_CHECK(m, off, sizeof(struct ah), IPPROTO_DONE);
556 ah = (struct ah *)(mtod(m, caddr_t) + off);
557 #else
558 IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah));
559 if (ah == NULL) {
560 ipseclog((LOG_DEBUG, "IPv6 AH input: can't pullup\n"));
561 ipsecstat.in_inval++;
562 return IPPROTO_DONE;
563 }
564 #endif
565 ip6 = mtod(m, struct ip6_hdr *);
566 nxt = ah->ah_nxt;
567
568 /* find the sassoc. */
569 spi = ah->ah_spi;
570
571 if (ntohs(ip6->ip6_plen) == 0) {
572 ipseclog((LOG_ERR, "IPv6 AH input: "
573 "AH with IPv6 jumbogram is not supported.\n"));
574 ipsec6stat.in_inval++;
575 goto fail;
576 }
577
578 if ((sav = key_allocsa(AF_INET6,
579 (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst,
580 IPPROTO_AH, spi)) == 0) {
581 ipseclog((LOG_WARNING,
582 "IPv6 AH input: no key association found for spi %u\n",
583 (u_int32_t)ntohl(spi)));
584 ipsec6stat.in_nosa++;
585 goto fail;
586 }
587 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
588 printf("DP ah6_input called to allocate SA:%p\n", sav));
589 if (sav->state != SADB_SASTATE_MATURE
590 && sav->state != SADB_SASTATE_DYING) {
591 ipseclog((LOG_DEBUG,
592 "IPv6 AH input: non-mature/dying SA found for spi %u; ",
593 (u_int32_t)ntohl(spi)));
594 ipsec6stat.in_badspi++;
595 goto fail;
596 }
597 if (sav->alg_auth == SADB_AALG_NONE) {
598 ipseclog((LOG_DEBUG, "IPv6 AH input: "
599 "unspecified authentication algorithm for spi %u\n",
600 (u_int32_t)ntohl(spi)));
601 ipsec6stat.in_badspi++;
602 goto fail;
603 }
604
605 algo = &ah_algorithms[sav->alg_auth];
606
607 siz = (*algo->sumsiz)(sav);
608 siz1 = ((siz + 3) & ~(4 - 1));
609
610 /*
611 * sanity checks for header, 1.
612 */
613 {
614 int sizoff;
615
616 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
617
618 if ((ah->ah_len << 2) - sizoff != siz1) {
619 ipseclog((LOG_NOTICE, "sum length mismatch in IPv6 AH input "
620 "(%d should be %u): %s\n",
621 (ah->ah_len << 2) - sizoff, (unsigned int)siz1,
622 ipsec6_logpacketstr(ip6, spi)));
623 ipsec6stat.in_inval++;
624 goto fail;
625 }
626 #ifndef PULLDOWN_TEST
627 IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1, IPPROTO_DONE);
628 #else
629 IP6_EXTHDR_GET(ah, struct ah *, m, off,
630 sizeof(struct ah) + sizoff + siz1);
631 if (ah == NULL) {
632 ipseclog((LOG_NOTICE, "couldn't pullup gather IPv6 AH checksum part"));
633 ipsecstat.in_inval++;
634 m = NULL;
635 goto fail;
636 }
637 #endif
638 }
639
640 /*
641 * check for sequence number.
642 */
643 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
644 if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav))
645 ; /*okey*/
646 else {
647 ipsec6stat.in_ahreplay++;
648 ipseclog((LOG_WARNING,
649 "replay packet in IPv6 AH input: %s %s\n",
650 ipsec6_logpacketstr(ip6, spi),
651 ipsec_logsastr(sav)));
652 goto fail;
653 }
654 }
655
656 /*
657 * alright, it seems sane. now we are going to check the
658 * cryptographic checksum.
659 */
660 cksum = _MALLOC(siz1, M_TEMP, M_NOWAIT);
661 if (!cksum) {
662 ipseclog((LOG_DEBUG, "IPv6 AH input: "
663 "couldn't alloc temporary region for cksum\n"));
664 ipsec6stat.in_inval++;
665 goto fail;
666 }
667
668 if (ah6_calccksum(m, (caddr_t)cksum, algo, sav)) {
669 _FREE(cksum, M_TEMP);
670 ipsec6stat.in_inval++;
671 goto fail;
672 }
673 ipsec6stat.in_ahhist[sav->alg_auth]++;
674
675 {
676 caddr_t sumpos = NULL;
677
678 if (sav->flags & SADB_X_EXT_OLD) {
679 /* RFC 1826 */
680 sumpos = (caddr_t)(ah + 1);
681 } else {
682 /* RFC 2402 */
683 sumpos = (caddr_t)(((struct newah *)ah) + 1);
684 }
685
686 if (bcmp(sumpos, cksum, siz) != 0) {
687 ipseclog((LOG_WARNING,
688 "checksum mismatch in IPv6 AH input: %s %s\n",
689 ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
690 _FREE(cksum, M_TEMP);
691 ipsec6stat.in_ahauthfail++;
692 goto fail;
693 }
694 }
695
696 _FREE(cksum, M_TEMP);
697
698 m->m_flags |= M_AUTHIPHDR;
699 m->m_flags |= M_AUTHIPDGM;
700
701 #if 0
702 /*
703 * looks okey, but we need more sanity check.
704 * XXX should elaborate.
705 */
706 if (ah->ah_nxt == IPPROTO_IPV6) {
707 struct ip6_hdr *nip6;
708 size_t sizoff;
709
710 sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
711
712 IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1
713 + sizeof(struct ip6_hdr), IPPROTO_DONE);
714
715 nip6 = (struct ip6_hdr *)((u_char *)(ah + 1) + sizoff + siz1);
716 if (!IN6_ARE_ADDR_EQUAL(&nip6->ip6_src, &ip6->ip6_src)
717 || !IN6_ARE_ADDR_EQUAL(&nip6->ip6_dst, &ip6->ip6_dst)) {
718 m->m_flags &= ~M_AUTHIPHDR;
719 m->m_flags &= ~M_AUTHIPDGM;
720 }
721 } else if (ah->ah_nxt == IPPROTO_IPIP) {
722 m->m_flags &= ~M_AUTHIPHDR;
723 m->m_flags &= ~M_AUTHIPDGM;
724 } else if (ah->ah_nxt == IPPROTO_IP) {
725 m->m_flags &= ~M_AUTHIPHDR;
726 m->m_flags &= ~M_AUTHIPDGM;
727 }
728 #endif
729
730 if (m->m_flags & M_AUTHIPHDR
731 && m->m_flags & M_AUTHIPDGM) {
732 #if 0
733 ipseclog((LOG_DEBUG,
734 "IPv6 AH input: authentication succeess\n"));
735 #endif
736 ipsec6stat.in_ahauthsucc++;
737 } else {
738 ipseclog((LOG_WARNING,
739 "authentication failed in IPv6 AH input: %s %s\n",
740 ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
741 ipsec6stat.in_ahauthfail++;
742 goto fail;
743 }
744
745 /*
746 * update sequence number.
747 */
748 if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
749 if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) {
750 ipsec6stat.in_ahreplay++;
751 goto fail;
752 }
753 }
754
755 /* was it transmitted over the IPsec tunnel SA? */
756 if (ipsec6_tunnel_validate(ip6, nxt, sav) && nxt == IPPROTO_IPV6) {
757 /*
758 * strip off all the headers that precedes AH.
759 * IP6 xx AH IP6' payload -> IP6' payload
760 *
761 * XXX more sanity checks
762 * XXX relationship with gif?
763 */
764 size_t stripsiz = 0;
765 u_int32_t flowinfo; /*net endian*/
766
767 flowinfo = ip6->ip6_flow;
768 if (sav->flags & SADB_X_EXT_OLD) {
769 /* RFC 1826 */
770 stripsiz = sizeof(struct ah) + siz1;
771 } else {
772 /* RFC 2402 */
773 stripsiz = sizeof(struct newah) + siz1;
774 }
775 m_adj(m, off + stripsiz);
776 if (m->m_len < sizeof(*ip6)) {
777 /*
778 * m_pullup is prohibited in KAME IPv6 input processing
779 * but there's no other way!
780 */
781 m = m_pullup(m, sizeof(*ip6));
782 if (!m) {
783 ipsec6stat.in_inval++;
784 goto fail;
785 }
786 }
787 ip6 = mtod(m, struct ip6_hdr *);
788 /* ECN consideration. */
789 ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow);
790 if (!key_checktunnelsanity(sav, AF_INET6,
791 (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) {
792 ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch "
793 "in IPv6 AH input: %s %s\n",
794 ipsec6_logpacketstr(ip6, spi),
795 ipsec_logsastr(sav)));
796 ipsec6stat.in_inval++;
797 goto fail;
798 }
799
800 #if 0 /* XXX should we call ipfw rather than ipsec_in_reject? */
801 /* drop it if it does not match the default policy */
802 if (ipsec6_in_reject(m, NULL)) {
803 ipsec6stat.in_polvio++;
804 goto fail;
805 }
806 #endif
807
808 #if 1
809 /*
810 * should the inner packet be considered authentic?
811 * see comment in ah4_input().
812 */
813 m->m_flags &= ~M_AUTHIPHDR;
814 m->m_flags &= ~M_AUTHIPDGM;
815 #endif
816
817 key_sa_recordxfer(sav, m);
818
819 s = splimp();
820 if (IF_QFULL(&ip6intrq)) {
821 ipsec6stat.in_inval++;
822 goto fail;
823 }
824 IF_ENQUEUE(&ip6intrq, m);
825 m = NULL;
826 schednetisr(NETISR_IPV6); /*can be skipped but to make sure*/
827 splx(s);
828 nxt = IPPROTO_DONE;
829 } else {
830 /*
831 * strip off AH.
832 * We do deep-copy since KAME requires that
833 * the packet is placed in a single mbuf.
834 */
835 size_t stripsiz = 0;
836 char *prvnxtp;
837
838 /*
839 * Copy the value of the next header field of AH to the
840 * next header field of the previous header.
841 * This is necessary because AH will be stripped off below.
842 */
843 prvnxtp = ip6_get_prevhdr(m, off); /* XXX */
844 *prvnxtp = nxt;
845
846 if (sav->flags & SADB_X_EXT_OLD) {
847 /* RFC 1826 */
848 stripsiz = sizeof(struct ah) + siz1;
849 } else {
850 /* RFC 2402 */
851 stripsiz = sizeof(struct newah) + siz1;
852 }
853
854 ip6 = mtod(m, struct ip6_hdr *);
855 #ifndef PULLDOWN_TEST
856 ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off);
857 m->m_data += stripsiz;
858 m->m_len -= stripsiz;
859 m->m_pkthdr.len -= stripsiz;
860 #else
861 /*
862 * even in m_pulldown case, we need to strip off AH so that
863 * we can compute checksum for multiple AH correctly.
864 */
865 if (m->m_len >= stripsiz + off) {
866 ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off);
867 m->m_data += stripsiz;
868 m->m_len -= stripsiz;
869 m->m_pkthdr.len -= stripsiz;
870 } else {
871 /*
872 * this comes with no copy if the boundary is on
873 * cluster
874 */
875 struct mbuf *n;
876
877 n = m_split(m, off, M_DONTWAIT);
878 if (n == NULL) {
879 /* m is retained by m_split */
880 goto fail;
881 }
882 m_adj(n, stripsiz);
883 m_cat(m, n);
884 /* m_cat does not update m_pkthdr.len */
885 m->m_pkthdr.len += n->m_pkthdr.len;
886 }
887 #endif
888 ip6 = mtod(m, struct ip6_hdr *);
889 /* XXX jumbogram */
890 ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz);
891
892 key_sa_recordxfer(sav, m);
893 }
894
895 *offp = off;
896 *mp = m;
897
898 if (sav) {
899 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
900 printf("DP ah6_input call free SA:%p\n", sav));
901 key_freesav(sav);
902 }
903 ipsec6stat.in_success++;
904 return nxt;
905
906 fail:
907 if (sav) {
908 KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
909 printf("DP ah6_input call free SA:%p\n", sav));
910 key_freesav(sav);
911 }
912 if (m)
913 m_freem(m);
914 return IPPROTO_DONE;
915 }
916 #endif /* INET6 */