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

Cache object: c0ec1ac88332c1a3a9b2d706678354f7


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