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