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