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