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

Cache object: 696697accdedac3c06233519e39b26f6


[ 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.