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

Cache object: 6da8675fe64bac71200a921103bae35f


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