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