]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/esp_input.c
xnu-4570.1.46.tar.gz
[apple/xnu.git] / bsd / netinet6 / esp_input.c
index e51ea9b0f972fc870b021907bbd32488a8a5d031..912dd3983890dcb1aaa4c093ac837753bae2de18 100644 (file)
@@ -1,8 +1,8 @@
 /*
- * Copyright (c) 2008-2013 Apple Inc. All rights reserved.
+ * Copyright (c) 2008-2016 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
- * 
+ *
  * This file contains Original Code and/or Modifications of Original Code
  * as defined in and that are subject to the Apple Public Source License
  * Version 2.0 (the 'License'). You may not use this file except in
  * unlawful or unlicensed copies of an Apple operating system, or to
  * circumvent, violate, or enable the circumvention or violation of, any
  * terms of an Apple operating system software license agreement.
- * 
+ *
  * Please obtain a copy of the License at
  * http://www.opensource.apple.com/apsl/ and read it before using this file.
- * 
+ *
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
@@ -22,7 +22,7 @@
  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  * Please see the License for the specific language governing rights and
  * limitations under the License.
- * 
+ *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 
 
 #include <net/net_osdep.h>
 #include <mach/sdt.h>
+#include <corecrypto/cc.h>
 
 #include <sys/kdebug.h>
 #define DBG_LAYER_BEG          NETDBG_CODE(DBG_NETIPSEC, 1)
@@ -174,9 +175,13 @@ esp6_input_strip_udp_encap (struct mbuf *m, int ip6hlen)
 }
 
 void
-esp4_input(m, off)
-       struct mbuf *m;
-       int off;
+esp4_input(struct mbuf *m, int off)
+{
+       (void)esp4_input_extended(m, off, NULL);
+}
+
+struct mbuf *
+esp4_input_extended(struct mbuf *m, int off, ifnet_t interface)
 {
        struct ip *ip;
 #if INET6
@@ -194,6 +199,7 @@ esp4_input(m, off)
        size_t hlen;
        size_t esplen;
        sa_family_t     ifamily;
+       struct mbuf *out_m = NULL;
 
        KERNEL_DEBUG(DBG_FNC_ESPIN | DBG_FUNC_START, 0,0,0,0,0);
        /* sanity check for alignment. */
@@ -214,6 +220,8 @@ esp4_input(m, off)
                }
        }
 
+       m->m_pkthdr.csum_flags &= ~CSUM_RX_FLAGS;
+
        /* Expect 32-bit aligned data pointer on strict-align platforms */
        MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m);
 
@@ -236,9 +244,9 @@ esp4_input(m, off)
        /* find the sassoc. */
        spi = esp->esp_spi;
 
-       if ((sav = key_allocsa(AF_INET,
-                             (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst,
-                             IPPROTO_ESP, spi)) == 0) {
+       if ((sav = key_allocsa_extended(AF_INET,
+                                                                       (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst,
+                                                                       IPPROTO_ESP, spi, interface)) == 0) {
                ipseclog((LOG_WARNING,
                    "IPv4 ESP input: no key association found for spi %u\n",
                    (u_int32_t)ntohl(spi)));
@@ -337,8 +345,8 @@ esp4_input(m, off)
                goto bad;
        }
 
-       if (bcmp(sum0, sum, siz) != 0) {
-               ipseclog((LOG_WARNING, "auth fail in IPv4 ESP input: %s %s\n",
+       if (cc_cmp_safe(siz, sum0, sum)) {
+               ipseclog((LOG_WARNING, "cc_cmp fail in IPv4 ESP input: %s %s\n",
                    ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
                IPSEC_STAT_INCREMENT(ipsecstat.in_espauthfail);
                goto bad;
@@ -429,18 +437,11 @@ noreplaycheck:
 
        if (algo->finalizedecrypt)
         {
-           unsigned char tag[algo->icvlen];
-           if ((*algo->finalizedecrypt)(sav, tag, algo->icvlen)) {
+           if ((*algo->finalizedecrypt)(sav, saved_icv, algo->icvlen)) {
                ipseclog((LOG_ERR, "packet decryption ICV failure\n"));
                IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
                KERNEL_DEBUG(DBG_FNC_DECRYPT | DBG_FUNC_END, 1,0,0,0,0);
                goto bad;
-           }     
-           if (memcmp(saved_icv, tag, algo->icvlen)) {
-               ipseclog((LOG_ERR, "packet decryption ICV mismatch\n"));
-               IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
-               KERNEL_DEBUG(DBG_FNC_DECRYPT | DBG_FUNC_END, 1,0,0,0,0);
-               goto bad;
            }
        }
 
@@ -498,17 +499,6 @@ noreplaycheck:
                esp = (struct esp *)(void *)(((u_int8_t *)ip) + off);
        }
 
-       if (sav->utun_is_keepalive_fn) {
-               if (sav->utun_is_keepalive_fn(sav->utun_pcb, &m, nxt, sav->flags, (off + esplen + ivlen))) {
-                       if (m) {
-                               // not really bad, we just wanna exit
-                               IPSEC_STAT_INCREMENT(ipsecstat.in_success);
-                               m = NULL;
-                       }
-                       goto bad;
-               }
-       }
-
        /* was it transmitted over the IPsec tunnel SA? */
        if (ipsec4_tunnel_validate(m, off + esplen + ivlen, nxt, sav, &ifamily)) {
                ifaddr_t ifa;
@@ -562,13 +552,11 @@ noreplaycheck:
                                goto bad;
                        }
 
-                       if (ip_doscopedroute) {
-                               bzero(&addr, sizeof(addr));
-                               ipaddr = (__typeof__(ipaddr))&addr;
-                               ipaddr->sin_family = AF_INET;
-                               ipaddr->sin_len = sizeof(*ipaddr);
-                               ipaddr->sin_addr = ip->ip_dst;
-                       }
+                       bzero(&addr, sizeof(addr));
+                       ipaddr = (__typeof__(ipaddr))&addr;
+                       ipaddr->sin_family = AF_INET;
+                       ipaddr->sin_len = sizeof(*ipaddr);
+                       ipaddr->sin_addr = ip->ip_dst;
 #if INET6
                } else if (ifamily == AF_INET6) {
                        struct sockaddr_in6 *ip6addr;
@@ -608,13 +596,11 @@ noreplaycheck:
                                goto bad;
                        }
 
-                       if (ip6_doscopedroute) {
-                               bzero(&addr, sizeof(addr));
-                               ip6addr = (__typeof__(ip6addr))&addr;
-                               ip6addr->sin6_family = AF_INET6;
-                               ip6addr->sin6_len = sizeof(*ip6addr);
-                               ip6addr->sin6_addr = ip6->ip6_dst;
-                       }
+                       bzero(&addr, sizeof(addr));
+                       ip6addr = (__typeof__(ip6addr))&addr;
+                       ip6addr->sin6_family = AF_INET6;
+                       ip6addr->sin6_len = sizeof(*ip6addr);
+                       ip6addr->sin6_addr = ip6->ip6_dst;
 #endif /* INET6 */
                } else {
                        ipseclog((LOG_ERR, "ipsec tunnel unsupported address family "
@@ -629,13 +615,11 @@ noreplaycheck:
                        goto bad;
                }
 
-               if (ip_doscopedroute || ip6_doscopedroute) {
-                       // update the receiving interface address based on the inner address
-                       ifa = ifa_ifwithaddr((struct sockaddr *)&addr);
-                       if (ifa) {
-                               m->m_pkthdr.rcvif = ifa->ifa_ifp;
-                               IFA_REMREF(ifa);
-                       }
+               // update the receiving interface address based on the inner address
+               ifa = ifa_ifwithaddr((struct sockaddr *)&addr);
+               if (ifa) {
+                       m->m_pkthdr.rcvif = ifa->ifa_ifp;
+                       IFA_REMREF(ifa);
                }
 
                /* Clear the csum flags, they can't be valid for the inner headers */
@@ -643,6 +627,13 @@ noreplaycheck:
 
                // Input via IPSec interface
                if (sav->sah->ipsec_if != NULL) {
+                       // Return mbuf
+                       if (interface != NULL &&
+                               interface == sav->sah->ipsec_if) {
+                               out_m = m;
+                               goto done;
+                       }
+
                        if (ipsec_inject_inbound_packet(sav->sah->ipsec_if, m) == 0) {
                                m = NULL;
                                goto done;
@@ -651,14 +642,6 @@ noreplaycheck:
                        }
                }
                
-               if (sav->utun_in_fn) {
-                       if (!(sav->utun_in_fn(sav->utun_pcb, &m, ifamily == AF_INET ? PF_INET : PF_INET6))) {
-                               m = NULL;
-                               // we just wanna exit since packet has been completely processed
-                               goto bad;
-                       }
-               }
-
                if (proto_input(ifamily == AF_INET ? PF_INET : PF_INET6, m) != 0)
                        goto bad;
 
@@ -703,6 +686,7 @@ noreplaycheck:
                if (nxt == IPPROTO_TCP || nxt == IPPROTO_UDP) {
                        m->m_pkthdr.csum_flags = CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
                        m->m_pkthdr.csum_data = 0xFFFF;
+                       _CASSERT(offsetof(struct pkthdr, csum_data) == offsetof(struct pkthdr, csum_rx_val));
                }
 
                if (nxt != IPPROTO_DONE) {
@@ -751,12 +735,28 @@ noreplaycheck:
                                struct ip *, ip, struct ifnet *, m->m_pkthdr.rcvif,
                                struct ip *, ip, struct ip6_hdr *, NULL);
 
-                       // Input via IPSec interface
+                       // Input via IPsec interface legacy path
                        if (sav->sah->ipsec_if != NULL) {
+                               int mlen;
+                               if ((mlen = m_length2(m, NULL)) < hlen) {
+                                       ipseclog((LOG_DEBUG,
+                                               "IPv4 ESP input: decrypted packet too short %d < %d\n",
+                                               mlen, hlen));
+                                       IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
+                                       goto bad;
+                               }
                                ip->ip_len = htons(ip->ip_len + hlen);
                                ip->ip_off = htons(ip->ip_off);
                                ip->ip_sum = 0;
                                ip->ip_sum = ip_cksum_hdr_in(m, hlen);
+
+                               // Return mbuf
+                               if (interface != NULL &&
+                                       interface == sav->sah->ipsec_if) {
+                                       out_m = m;
+                                       goto done;
+                               }
+
                                if (ipsec_inject_inbound_packet(sav->sah->ipsec_if, m) == 0) {
                                        m = NULL;
                                        goto done;
@@ -765,17 +765,10 @@ noreplaycheck:
                                }
                        }
                        
-                       if (sav->utun_in_fn) {
-                               if (!(sav->utun_in_fn(sav->utun_pcb, &m, PF_INET))) {
-                                       m = NULL;
-                                       // we just wanna exit since packet has been completely processed
-                                       goto bad;
-                               }
-                       }
-
                        ip_proto_dispatch_in(m, off, nxt, 0);
-               } else
+               } else {
                        m_freem(m);
+               }
                m = NULL;
        }
 
@@ -787,8 +780,7 @@ done:
                key_freesav(sav, KEY_SADB_UNLOCKED);
        }
        IPSEC_STAT_INCREMENT(ipsecstat.in_success);
-       return;
-
+       return out_m;
 bad:
        if (sav) {
                KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
@@ -796,16 +788,24 @@ bad:
                    (uint64_t)VM_KERNEL_ADDRPERM(sav)));
                key_freesav(sav, KEY_SADB_UNLOCKED);
        }
-       if (m)
+       if (m) {
                m_freem(m);
+       }
        KERNEL_DEBUG(DBG_FNC_ESPIN | DBG_FUNC_END, 4,0,0,0,0);
-       return;
+       return out_m;
 }
 #endif /* INET */
 
 #if INET6
+
 int
 esp6_input(struct mbuf **mp, int *offp, int proto)
+{
+       return esp6_input_extended(mp, offp, proto, NULL);
+}
+
+int
+esp6_input_extended(struct mbuf **mp, int *offp, int proto, ifnet_t interface)
 {
 #pragma unused(proto)
        struct mbuf *m = *mp;
@@ -843,6 +843,8 @@ esp6_input(struct mbuf **mp, int *offp, int proto)
                return IPPROTO_DONE;
        }
 #endif
+       m->m_pkthdr.csum_flags &= ~CSUM_RX_FLAGS;
+
        /* Expect 32-bit data aligned pointer on strict-align platforms */
        MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m);
 
@@ -866,9 +868,9 @@ esp6_input(struct mbuf **mp, int *offp, int proto)
        /* find the sassoc. */
        spi = esp->esp_spi;
 
-       if ((sav = key_allocsa(AF_INET6,
-                             (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst,
-                             IPPROTO_ESP, spi)) == 0) {
+       if ((sav = key_allocsa_extended(AF_INET6,
+                                                                       (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst,
+                                                                       IPPROTO_ESP, spi, interface)) == 0) {
                ipseclog((LOG_WARNING,
                    "IPv6 ESP input: no key association found for spi %u\n",
                    (u_int32_t)ntohl(spi)));
@@ -967,7 +969,7 @@ esp6_input(struct mbuf **mp, int *offp, int proto)
                goto bad;
        }
 
-       if (bcmp(sum0, sum, siz) != 0) {
+       if (cc_cmp_safe(siz, sum0, sum)) {
                ipseclog((LOG_WARNING, "auth fail in IPv6 ESP input: %s %s\n",
                    ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
                IPSEC_STAT_INCREMENT(ipsec6stat.in_espauthfail);
@@ -1055,18 +1057,11 @@ noreplaycheck:
 
        if (algo->finalizedecrypt)
         {
-           unsigned char tag[algo->icvlen];
-           if ((*algo->finalizedecrypt)(sav, tag, algo->icvlen)) {
+           if ((*algo->finalizedecrypt)(sav, saved_icv, algo->icvlen)) {
                ipseclog((LOG_ERR, "packet decryption ICV failure\n"));
                IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
                KERNEL_DEBUG(DBG_FNC_DECRYPT | DBG_FUNC_END, 1,0,0,0,0);
                goto bad;
-           }     
-           if (memcmp(saved_icv, tag, algo->icvlen)) {
-               ipseclog((LOG_ERR, "packet decryption ICV mismatch\n"));
-               IPSEC_STAT_INCREMENT(ipsecstat.in_inval);
-               KERNEL_DEBUG(DBG_FNC_DECRYPT | DBG_FUNC_END, 1,0,0,0,0);
-               goto bad;
            }
        }
 
@@ -1092,17 +1087,6 @@ noreplaycheck:
        ip6 = mtod(m, struct ip6_hdr *);
        ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - taillen);
 
-       if (sav->utun_is_keepalive_fn) {
-               if (sav->utun_is_keepalive_fn(sav->utun_pcb, &m, nxt, sav->flags, (off + esplen + ivlen))) {
-                       if (m) {
-                               // not really bad, we just wanna exit
-                               IPSEC_STAT_INCREMENT(ipsec6stat.in_success);
-                               m = NULL;
-                       }
-                       goto bad;
-               }
-       }
-
        if (*nproto == IPPROTO_UDP) {
                // offset includes the outer ip and udp header lengths.
                if (m->m_len < off) {
@@ -1149,6 +1133,8 @@ noreplaycheck:
                flowinfo = ip6->ip6_flow;
                m_adj(m, off + esplen + ivlen);
                if (ifamily == AF_INET6) {
+                       struct sockaddr_in6 *ip6addr;
+
                        if (m->m_len < sizeof(*ip6)) {
 #ifndef PULLDOWN_TEST
                                /*
@@ -1180,15 +1166,11 @@ noreplaycheck:
                                goto bad;
                        }
 
-                       if (ip6_doscopedroute) {
-                               struct sockaddr_in6 *ip6addr;
-
-                               bzero(&addr, sizeof(addr));
-                               ip6addr = (__typeof__(ip6addr))&addr;
-                               ip6addr->sin6_family = AF_INET6;
-                               ip6addr->sin6_len = sizeof(*ip6addr);
-                               ip6addr->sin6_addr = ip6->ip6_dst;
-                       }
+                       bzero(&addr, sizeof(addr));
+                       ip6addr = (__typeof__(ip6addr))&addr;
+                       ip6addr->sin6_family = AF_INET6;
+                       ip6addr->sin6_len = sizeof(*ip6addr);
+                       ip6addr->sin6_addr = ip6->ip6_dst;
                } else if (ifamily == AF_INET) {
                        struct sockaddr_in *ipaddr;
 
@@ -1228,13 +1210,11 @@ noreplaycheck:
                                goto bad;
                        }
 
-                       if (ip_doscopedroute) {
-                               bzero(&addr, sizeof(addr));
-                               ipaddr = (__typeof__(ipaddr))&addr;
-                               ipaddr->sin_family = AF_INET;
-                               ipaddr->sin_len = sizeof(*ipaddr);
-                               ipaddr->sin_addr = ip->ip_dst;
-                       }
+                       bzero(&addr, sizeof(addr));
+                       ipaddr = (__typeof__(ipaddr))&addr;
+                       ipaddr->sin_family = AF_INET;
+                       ipaddr->sin_len = sizeof(*ipaddr);
+                       ipaddr->sin_addr = ip->ip_dst;
                }
 
                key_sa_recordxfer(sav, m);
@@ -1244,17 +1224,21 @@ noreplaycheck:
                        goto bad;
                }
 
-               if (ip_doscopedroute || ip6_doscopedroute) {
-                       // update the receiving interface address based on the inner address
-                       ifa = ifa_ifwithaddr((struct sockaddr *)&addr);
-                       if (ifa) {
-                               m->m_pkthdr.rcvif = ifa->ifa_ifp;
-                               IFA_REMREF(ifa);
-                       }
+               // update the receiving interface address based on the inner address
+               ifa = ifa_ifwithaddr((struct sockaddr *)&addr);
+               if (ifa) {
+                       m->m_pkthdr.rcvif = ifa->ifa_ifp;
+                       IFA_REMREF(ifa);
                }
 
                // Input via IPSec interface
                if (sav->sah->ipsec_if != NULL) {
+                       // Return mbuf
+                       if (interface != NULL &&
+                               interface == sav->sah->ipsec_if) {
+                               goto done;
+                       }
+
                        if (ipsec_inject_inbound_packet(sav->sah->ipsec_if, m) == 0) {
                                m = NULL;
                                nxt = IPPROTO_DONE;
@@ -1264,14 +1248,6 @@ noreplaycheck:
                        }
                }
                
-               if (sav->utun_in_fn) {
-                       if (!(sav->utun_in_fn(sav->utun_pcb, &m, PF_INET6))) {
-                               m = NULL;
-                               // we just wanna exit since packet has been completely processed
-                               goto bad;
-                       }
-               }
-
                if (proto_input(ifamily == AF_INET ? PF_INET : PF_INET6, m) != 0)
                        goto bad;
                nxt = IPPROTO_DONE;
@@ -1380,10 +1356,17 @@ noreplaycheck:
                if (nxt == IPPROTO_TCP || nxt == IPPROTO_UDP) {
                        m->m_pkthdr.csum_flags = CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
                        m->m_pkthdr.csum_data = 0xFFFF;
+                       _CASSERT(offsetof(struct pkthdr, csum_data) == offsetof(struct pkthdr, csum_rx_val));
                }
 
                // Input via IPSec interface
                if (sav->sah->ipsec_if != NULL) {
+                       // Return mbuf
+                       if (interface != NULL &&
+                               interface == sav->sah->ipsec_if) {
+                               goto done;
+                       }
+
                        if (ipsec_inject_inbound_packet(sav->sah->ipsec_if, m) == 0) {
                                m = NULL;
                                nxt = IPPROTO_DONE;
@@ -1393,13 +1376,6 @@ noreplaycheck:
                        }
                }
                
-               if (sav->utun_in_fn) {
-                       if (!(sav->utun_in_fn(sav->utun_pcb, &m, PF_INET6))) {
-                               m = NULL;
-                               // we just wanna exit since packet has been completely processed
-                               goto bad;
-                       }
-               }
        }
 
 done:
@@ -1421,16 +1397,17 @@ bad:
                    (uint64_t)VM_KERNEL_ADDRPERM(sav)));
                key_freesav(sav, KEY_SADB_UNLOCKED);
        }
-       if (m)
+       if (m) {
                m_freem(m);
+       }
+       if (interface != NULL) {
+               *mp = NULL;
+       }
        return IPPROTO_DONE;
 }
 
 void
-esp6_ctlinput(cmd, sa, d)
-       int cmd;
-       struct sockaddr *sa;
-       void *d;
+esp6_ctlinput(int cmd, struct sockaddr *sa, void *d, __unused struct ifnet *ifp)
 {
        const struct newesp *espp;
        struct newesp esp;
@@ -1438,7 +1415,7 @@ esp6_ctlinput(cmd, sa, d)
        struct secasvar *sav;
        struct ip6_hdr *ip6;
        struct mbuf *m;
-       int off;
+       int off = 0;
        struct sockaddr_in6 *sa6_src, *sa6_dst;
 
        if (sa->sa_family != AF_INET6 ||