The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netinet6/ah_input.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: a980da0b501e2edac872d7fd19e9bb77


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.