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-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

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

Cache object: 376d06a6c905098e890204bcb8e4f690


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