X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/2d21ac55c334faf3a56e5634905ed6987fc787d4..cb3231590a3c94ab4375e2228bd5e86b0cf1ad7e:/bsd/netinet6/ah_input.c diff --git a/bsd/netinet6/ah_input.c b/bsd/netinet6/ah_input.c index b79bf0895..104f5a9c6 100644 --- a/bsd/netinet6/ah_input.c +++ b/bsd/netinet6/ah_input.c @@ -1,3 +1,31 @@ +/* + * 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 + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * 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, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * 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@ + */ + /* $FreeBSD: src/sys/netinet6/ah_input.c,v 1.1.2.6 2002/04/28 05:40:26 suz Exp $ */ /* $KAME: ah_input.c,v 1.67 2002/01/07 11:39:56 kjc Exp $ */ @@ -38,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +76,7 @@ #include #include +#include #include #include #include @@ -83,19 +113,20 @@ #if IPSEC_DEBUG #include #else -#define KEYDEBUG(lev,arg) +#define KEYDEBUG(lev, arg) #endif #include #include +#include #include #define IPLEN_FLIPPED -#if INET -extern struct protosw inetsw[]; +extern lck_mtx_t *sadb_mutex; +#if INET void ah4_input(struct mbuf *m, int off) { @@ -112,29 +143,21 @@ ah4_input(struct mbuf *m, int off) size_t stripsiz = 0; sa_family_t ifamily; -#ifndef PULLDOWN_TEST if (m->m_len < off + sizeof(struct newah)) { m = m_pullup(m, off + sizeof(struct newah)); if (!m) { ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;" - "dropping the packet for simplicity\n")); + "dropping the packet for simplicity\n")); IPSEC_STAT_INCREMENT(ipsecstat.in_inval); goto fail; } } + /* Expect 32-bit aligned data pointer on strict-align platforms */ + MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); + ip = mtod(m, struct ip *); - ah = (struct ah *)(((caddr_t)ip) + off); -#else - ip = mtod(m, struct ip *); - IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah)); - if (ah == NULL) { - ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;" - "dropping the packet for simplicity\n")); - IPSEC_STAT_INCREMENT(ipsecstat.in_inval); - goto fail; - } -#endif + ah = (struct ah *)(void *)(((caddr_t)ip) + off); nxt = ah->ah_nxt; #ifdef _IP_VHL hlen = IP_VHL_HL(ip->ip_vhl) << 2; @@ -146,8 +169,8 @@ ah4_input(struct mbuf *m, int off) spi = ah->ah_spi; if ((sav = key_allocsa(AF_INET, - (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst, - IPPROTO_AH, spi)) == 0) { + (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst, + IPPROTO_AH, spi)) == 0) { ipseclog((LOG_WARNING, "IPv4 AH input: no key association found for spi %u\n", (u_int32_t)ntohl(spi))); @@ -155,9 +178,10 @@ ah4_input(struct mbuf *m, int off) goto fail; } KEYDEBUG(KEYDEBUG_IPSEC_STAMP, - printf("DP ah4_input called to allocate SA:%p\n", sav)); + printf("DP ah4_input called to allocate SA:0x%llx\n", + (uint64_t)VM_KERNEL_ADDRPERM(sav))); if (sav->state != SADB_SASTATE_MATURE - && sav->state != SADB_SASTATE_DYING) { + && sav->state != SADB_SASTATE_DYING) { ipseclog((LOG_DEBUG, "IPv4 AH input: non-mature/dying SA found for spi %u\n", (u_int32_t)ntohl(spi))); @@ -180,79 +204,71 @@ ah4_input(struct mbuf *m, int off) /* * sanity checks for header, 1. */ - { - int sizoff; - - sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; + { + int sizoff; - /* - * Here, we do not do "siz1 == siz". This is because the way - * RFC240[34] section 2 is written. They do not require truncation - * to 96 bits. - * For example, Microsoft IPsec stack attaches 160 bits of - * authentication data for both hmac-md5 and hmac-sha1. For hmac-sha1, - * 32 bits of padding is attached. - * - * There are two downsides to this specification. - * They have no real harm, however, they leave us fuzzy feeling. - * - if we attach more than 96 bits of authentication data onto AH, - * we will never notice about possible modification by rogue - * intermediate nodes. - * Since extra bits in AH checksum is never used, this constitutes - * no real issue, however, it is wacky. - * - even if the peer attaches big authentication data, we will never - * notice the difference, since longer authentication data will just - * work. - * - * We may need some clarification in the spec. - */ - if (siz1 < siz) { - ipseclog((LOG_NOTICE, "sum length too short in IPv4 AH input " - "(%lu, should be at least %lu): %s\n", - (u_long)siz1, (u_long)siz, - ipsec4_logpacketstr(ip, spi))); - IPSEC_STAT_INCREMENT(ipsecstat.in_inval); - goto fail; - } - if ((ah->ah_len << 2) - sizoff != siz1) { - ipseclog((LOG_NOTICE, "sum length mismatch in IPv4 AH input " - "(%d should be %lu): %s\n", - (ah->ah_len << 2) - sizoff, (u_long)siz1, - ipsec4_logpacketstr(ip, spi))); - IPSEC_STAT_INCREMENT(ipsecstat.in_inval); - goto fail; - } + sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; -#ifndef PULLDOWN_TEST - if (m->m_len < off + sizeof(struct ah) + sizoff + siz1) { - m = m_pullup(m, off + sizeof(struct ah) + sizoff + siz1); - if (!m) { - ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n")); + /* + * Here, we do not do "siz1 == siz". This is because the way + * RFC240[34] section 2 is written. They do not require truncation + * to 96 bits. + * For example, Microsoft IPsec stack attaches 160 bits of + * authentication data for both hmac-md5 and hmac-sha1. For hmac-sha1, + * 32 bits of padding is attached. + * + * There are two downsides to this specification. + * They have no real harm, however, they leave us fuzzy feeling. + * - if we attach more than 96 bits of authentication data onto AH, + * we will never notice about possible modification by rogue + * intermediate nodes. + * Since extra bits in AH checksum is never used, this constitutes + * no real issue, however, it is wacky. + * - even if the peer attaches big authentication data, we will never + * notice the difference, since longer authentication data will just + * work. + * + * We may need some clarification in the spec. + */ + if (siz1 < siz) { + ipseclog((LOG_NOTICE, "sum length too short in IPv4 AH input " + "(%lu, should be at least %lu): %s\n", + (u_int32_t)siz1, (u_int32_t)siz, + ipsec4_logpacketstr(ip, spi))); + IPSEC_STAT_INCREMENT(ipsecstat.in_inval); + goto fail; + } + if ((ah->ah_len << 2) - sizoff != siz1) { + ipseclog((LOG_NOTICE, "sum length mismatch in IPv4 AH input " + "(%d should be %lu): %s\n", + (ah->ah_len << 2) - sizoff, (u_int32_t)siz1, + ipsec4_logpacketstr(ip, spi))); IPSEC_STAT_INCREMENT(ipsecstat.in_inval); goto fail; } - ip = mtod(m, struct ip *); - ah = (struct ah *)(((caddr_t)ip) + off); - } -#else - IP6_EXTHDR_GET(ah, struct ah *, m, off, - sizeof(struct ah) + sizoff + siz1); - if (ah == NULL) { - ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n")); - IPSEC_STAT_INCREMENT(ipsecstat.in_inval); - goto fail; + if (m->m_len < off + sizeof(struct ah) + sizoff + siz1) { + m = m_pullup(m, off + sizeof(struct ah) + sizoff + siz1); + if (!m) { + ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n")); + IPSEC_STAT_INCREMENT(ipsecstat.in_inval); + goto fail; + } + /* Expect 32-bit aligned data ptr on strict-align platforms */ + MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); + + ip = mtod(m, struct ip *); + ah = (struct ah *)(void *)(((caddr_t)ip) + off); + } } -#endif - } /* * check for sequence number. */ - if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { - if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav)) + if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay[0] != NULL) { + if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav, 0)) { ; /*okey*/ - else { + } else { IPSEC_STAT_INCREMENT(ipsecstat.in_ahreplay); ipseclog((LOG_WARNING, "replay packet in IPv4 AH input: %s %s\n", @@ -272,7 +288,7 @@ ah4_input(struct mbuf *m, int off) IPSEC_STAT_INCREMENT(ipsecstat.in_inval); goto fail; } - + /* * some of IP header fields are flipped to the host endian. * convert them back to network endian. VERY stupid. @@ -291,26 +307,26 @@ ah4_input(struct mbuf *m, int off) ip->ip_len = ntohs(ip->ip_len) - hlen; ip->ip_off = ntohs(ip->ip_off); - { - caddr_t sumpos = NULL; + { + caddr_t sumpos = NULL; - if (sav->flags & SADB_X_EXT_OLD) { - /* RFC 1826 */ - sumpos = (caddr_t)(ah + 1); - } else { - /* RFC 2402 */ - sumpos = (caddr_t)(((struct newah *)ah) + 1); - } + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1826 */ + sumpos = (caddr_t)(ah + 1); + } else { + /* RFC 2402 */ + sumpos = (caddr_t)(((struct newah *)ah) + 1); + } - if (bcmp(sumpos, cksum, siz) != 0) { - ipseclog((LOG_WARNING, - "checksum mismatch in IPv4 AH input: %s %s\n", - ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); - FREE(cksum, M_TEMP); - IPSEC_STAT_INCREMENT(ipsecstat.in_ahauthfail); - goto fail; + if (bcmp(sumpos, cksum, siz) != 0) { + ipseclog((LOG_WARNING, + "checksum mismatch in IPv4 AH input: %s %s\n", + ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); + FREE(cksum, M_TEMP); + IPSEC_STAT_INCREMENT(ipsecstat.in_ahauthfail); + goto fail; + } } - } FREE(cksum, M_TEMP); @@ -330,7 +346,7 @@ ah4_input(struct mbuf *m, int off) if (m->m_len < off + sizeof(struct ah) + sizoff + siz1 + hlen) { m = m_pullup(m, off + sizeof(struct ah) - + sizoff + siz1 + hlen); + + sizoff + siz1 + hlen); if (!m) { ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n")); @@ -341,7 +357,7 @@ ah4_input(struct mbuf *m, int off) nip = (struct ip *)((u_char *)(ah + 1) + sizoff + siz1); if (nip->ip_src.s_addr != ip->ip_src.s_addr - || nip->ip_dst.s_addr != ip->ip_dst.s_addr) { + || nip->ip_dst.s_addr != ip->ip_dst.s_addr) { m->m_flags &= ~M_AUTHIPHDR; m->m_flags &= ~M_AUTHIPDGM; } @@ -355,7 +371,7 @@ ah4_input(struct mbuf *m, int off) #endif /*0*/ if (m->m_flags & M_AUTHIPHDR - && m->m_flags & M_AUTHIPDGM) { + && m->m_flags & M_AUTHIPDGM) { #if 0 ipseclog((LOG_DEBUG, "IPv4 AH input: authentication succeess\n")); @@ -372,8 +388,8 @@ ah4_input(struct mbuf *m, int off) /* * update sequence number. */ - if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { - if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) { + if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay[0] != NULL) { + if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav, 0)) { IPSEC_STAT_INCREMENT(ipsecstat.in_ahreplay); goto fail; } @@ -388,6 +404,10 @@ ah4_input(struct mbuf *m, int off) stripsiz = sizeof(struct newah) + siz1; } if (ipsec4_tunnel_validate(m, off + stripsiz, nxt, sav, &ifamily)) { + ifaddr_t ifa; + struct sockaddr_storage addr; + struct sockaddr_in *ipaddr; + /* * strip off all the headers that precedes AH. * IP xx AH IP' payload -> IP' payload @@ -395,8 +415,9 @@ ah4_input(struct mbuf *m, int off) * XXX more sanity checks * XXX relationship with gif? */ - u_int8_t tos; - + u_int8_t tos, otos; + int sum; + if (ifamily == AF_INET6) { ipseclog((LOG_NOTICE, "ipsec tunnel protocol mismatch " "in IPv4 AH input: %s\n", ipsec_logsastr(sav))); @@ -412,10 +433,23 @@ ah4_input(struct mbuf *m, int off) } } ip = mtod(m, struct ip *); + otos = ip->ip_tos; /* ECN consideration. */ - ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos); + if (ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos) == 0) { + IPSEC_STAT_INCREMENT(ipsecstat.in_inval); + goto fail; + } + + if (otos != ip->ip_tos) { + sum = ~ntohs(ip->ip_sum) & 0xffff; + sum += (~otos & 0xffff) + ip->ip_tos; + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); /* add carry */ + ip->ip_sum = htons(~sum & 0xffff); + } + if (!key_checktunnelsanity(sav, AF_INET, - (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) { + (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) { ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch " "in IPv4 AH input: %s %s\n", ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav))); @@ -453,7 +487,42 @@ ah4_input(struct mbuf *m, int off) IPSEC_STAT_INCREMENT(ipsecstat.in_nomem); goto fail; } - proto_input(PF_INET, m); + + bzero(&addr, sizeof(addr)); + ipaddr = (__typeof__(ipaddr)) & addr; + ipaddr->sin_family = AF_INET; + ipaddr->sin_len = sizeof(*ipaddr); + ipaddr->sin_addr = ip->ip_dst; + + // 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 + lck_mtx_lock(sadb_mutex); + ifnet_t ipsec_if = sav->sah->ipsec_if; + if (ipsec_if != NULL) { + // If an interface is found, add a reference count before dropping the lock + ifnet_reference(ipsec_if); + } + lck_mtx_unlock(sadb_mutex); + if (ipsec_if != NULL) { + errno_t inject_error = ipsec_inject_inbound_packet(ipsec_if, m); + ifnet_release(ipsec_if); + if (inject_error == 0) { + m = NULL; + goto done; + } else { + goto fail; + } + } + + if (proto_input(PF_INET, m) != 0) { + goto fail; + } nxt = IPPROTO_DONE; } else { /* @@ -461,7 +530,6 @@ ah4_input(struct mbuf *m, int off) */ ip = mtod(m, struct ip *); -#ifndef PULLDOWN_TEST /* * We do deep-copy since KAME requires that * the packet is placed in a single external mbuf. @@ -470,34 +538,6 @@ ah4_input(struct mbuf *m, int off) m->m_data += stripsiz; m->m_len -= stripsiz; m->m_pkthdr.len -= stripsiz; -#else - /* - * even in m_pulldown case, we need to strip off AH so that - * we can compute checksum for multiple AH correctly. - */ - if (m->m_len >= stripsiz + off) { - ovbcopy((caddr_t)ip, ((caddr_t)ip) + stripsiz, off); - m->m_data += stripsiz; - m->m_len -= stripsiz; - m->m_pkthdr.len -= stripsiz; - } else { - /* - * this comes with no copy if the boundary is on - * cluster - */ - struct mbuf *n; - - n = m_split(m, off, M_DONTWAIT); - if (n == NULL) { - /* m is retained by m_split */ - goto fail; - } - m_adj(n, stripsiz); - /* m_cat does not update m_pkthdr.len */ - m->m_pkthdr.len += n->m_pkthdr.len; - m_cat(m, n); - } -#endif if (m->m_len < sizeof(*ip)) { m = m_pullup(m, sizeof(*ip)); @@ -521,21 +561,50 @@ ah4_input(struct mbuf *m, int off) goto fail; } + DTRACE_IP6(receive, struct mbuf *, m, struct inpcb *, NULL, + struct ip *, ip, struct ifnet *, m->m_pkthdr.rcvif, + struct ip *, ip, struct ip6_hdr *, NULL); + if (nxt != IPPROTO_DONE) { + // Input via IPsec interface + lck_mtx_lock(sadb_mutex); + ifnet_t ipsec_if = sav->sah->ipsec_if; + if (ipsec_if != NULL) { + // If an interface is found, add a reference count before dropping the lock + ifnet_reference(ipsec_if); + } + lck_mtx_unlock(sadb_mutex); + if (ipsec_if != NULL) { + 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); + errno_t inject_error = ipsec_inject_inbound_packet(ipsec_if, m); + ifnet_release(ipsec_if); + if (inject_error == 0) { + m = NULL; + goto done; + } else { + goto fail; + } + } + if ((ip_protox[nxt]->pr_flags & PR_LASTHDR) != 0 && ipsec4_in_reject(m, NULL)) { IPSEC_STAT_INCREMENT(ipsecstat.in_polvio); goto fail; } ip_proto_dispatch_in(m, off, nxt, 0); - } else + } else { m_freem(m); + } m = NULL; } - +done: if (sav) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, - printf("DP ah4_input call free SA:%p\n", sav)); + printf("DP ah4_input call free SA:0x%llx\n", + (uint64_t)VM_KERNEL_ADDRPERM(sav))); key_freesav(sav, KEY_SADB_UNLOCKED); } IPSEC_STAT_INCREMENT(ipsecstat.in_success); @@ -544,21 +613,22 @@ ah4_input(struct mbuf *m, int off) fail: if (sav) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, - printf("DP ah4_input call free SA:%p\n", sav)); + printf("DP ah4_input call free SA:0x%llx\n", + (uint64_t)VM_KERNEL_ADDRPERM(sav))); key_freesav(sav, KEY_SADB_UNLOCKED); } - if (m) + if (m) { m_freem(m); + } return; } #endif /* INET */ #if INET6 int -ah6_input(mp, offp) - struct mbuf **mp; - int *offp; +ah6_input(struct mbuf **mp, int *offp, int proto) { +#pragma unused(proto) struct mbuf *m = *mp; int off = *offp; struct ip6_hdr *ip6; @@ -571,19 +641,13 @@ ah6_input(mp, offp) struct secasvar *sav = NULL; u_int16_t nxt; size_t stripsiz = 0; + sa_family_t ifamily; - -#ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(struct ah), {return IPPROTO_DONE;}); - ah = (struct ah *)(mtod(m, caddr_t) + off); -#else - IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah)); - if (ah == NULL) { - ipseclog((LOG_DEBUG, "IPv6 AH input: can't pullup\n")); - ipsec6stat.in_inval++; - return IPPROTO_DONE; - } -#endif + ah = (struct ah *)(void *)(mtod(m, caddr_t) + off); + /* Expect 32-bit aligned data pointer on strict-align platforms */ + MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); + ip6 = mtod(m, struct ip6_hdr *); nxt = ah->ah_nxt; @@ -598,8 +662,8 @@ ah6_input(mp, offp) } if ((sav = key_allocsa(AF_INET6, - (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst, - IPPROTO_AH, spi)) == 0) { + (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst, + IPPROTO_AH, spi)) == 0) { ipseclog((LOG_WARNING, "IPv6 AH input: no key association found for spi %u\n", (u_int32_t)ntohl(spi))); @@ -607,9 +671,10 @@ ah6_input(mp, offp) goto fail; } KEYDEBUG(KEYDEBUG_IPSEC_STAMP, - printf("DP ah6_input called to allocate SA:%p\n", sav)); + printf("DP ah6_input called to allocate SA:0x%llx\n", + (uint64_t)VM_KERNEL_ADDRPERM(sav))); if (sav->state != SADB_SASTATE_MATURE - && sav->state != SADB_SASTATE_DYING) { + && sav->state != SADB_SASTATE_DYING) { ipseclog((LOG_DEBUG, "IPv6 AH input: non-mature/dying SA found for spi %u; ", (u_int32_t)ntohl(spi))); @@ -632,53 +697,42 @@ ah6_input(mp, offp) /* * sanity checks for header, 1. */ - { - int sizoff; + { + int sizoff; - sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; + sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; - /* - * Here, we do not do "siz1 == siz". See ah4_input() for complete - * description. - */ - if (siz1 < siz) { - ipseclog((LOG_NOTICE, "sum length too short in IPv6 AH input " - "(%lu, should be at least %lu): %s\n", - (u_long)siz1, (u_long)siz, - ipsec6_logpacketstr(ip6, spi))); - IPSEC_STAT_INCREMENT(ipsec6stat.in_inval); - goto fail; - } - if ((ah->ah_len << 2) - sizoff != siz1) { - ipseclog((LOG_NOTICE, "sum length mismatch in IPv6 AH input " - "(%d should be %lu): %s\n", - (ah->ah_len << 2) - sizoff, (u_long)siz1, - ipsec6_logpacketstr(ip6, spi))); - IPSEC_STAT_INCREMENT(ipsec6stat.in_inval); - goto fail; - } -#ifndef PULLDOWN_TEST - IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1, - {return IPPROTO_DONE;}); -#else - IP6_EXTHDR_GET(ah, struct ah *, m, off, - sizeof(struct ah) + sizoff + siz1); - if (ah == NULL) { - ipseclog((LOG_NOTICE, "couldn't pullup gather IPv6 AH checksum part")); - IPSEC_STAT_INCREMENT(ipsec6stat.in_inval); - m = NULL; - goto fail; + /* + * Here, we do not do "siz1 == siz". See ah4_input() for complete + * description. + */ + if (siz1 < siz) { + ipseclog((LOG_NOTICE, "sum length too short in IPv6 AH input " + "(%lu, should be at least %lu): %s\n", + (u_int32_t)siz1, (u_int32_t)siz, + ipsec6_logpacketstr(ip6, spi))); + IPSEC_STAT_INCREMENT(ipsec6stat.in_inval); + goto fail; + } + if ((ah->ah_len << 2) - sizoff != siz1) { + ipseclog((LOG_NOTICE, "sum length mismatch in IPv6 AH input " + "(%d should be %lu): %s\n", + (ah->ah_len << 2) - sizoff, (u_int32_t)siz1, + ipsec6_logpacketstr(ip6, spi))); + IPSEC_STAT_INCREMENT(ipsec6stat.in_inval); + goto fail; + } + IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1, + {return IPPROTO_DONE;}); } -#endif - } /* * check for sequence number. */ - if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { - if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav)) + if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay[0] != NULL) { + if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav, 0)) { ; /*okey*/ - else { + } else { IPSEC_STAT_INCREMENT(ipsec6stat.in_ahreplay); ipseclog((LOG_WARNING, "replay packet in IPv6 AH input: %s %s\n", @@ -699,7 +753,7 @@ ah6_input(mp, offp) IPSEC_STAT_INCREMENT(ipsec6stat.in_inval); goto fail; } - + if (ah6_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) { FREE(cksum, M_TEMP); IPSEC_STAT_INCREMENT(ipsec6stat.in_inval); @@ -707,26 +761,26 @@ ah6_input(mp, offp) } IPSEC_STAT_INCREMENT(ipsec6stat.in_ahhist[sav->alg_auth]); - { - caddr_t sumpos = NULL; + { + caddr_t sumpos = NULL; - if (sav->flags & SADB_X_EXT_OLD) { - /* RFC 1826 */ - sumpos = (caddr_t)(ah + 1); - } else { - /* RFC 2402 */ - sumpos = (caddr_t)(((struct newah *)ah) + 1); - } + if (sav->flags & SADB_X_EXT_OLD) { + /* RFC 1826 */ + sumpos = (caddr_t)(ah + 1); + } else { + /* RFC 2402 */ + sumpos = (caddr_t)(((struct newah *)ah) + 1); + } - if (bcmp(sumpos, cksum, siz) != 0) { - ipseclog((LOG_WARNING, - "checksum mismatch in IPv6 AH input: %s %s\n", - ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); - FREE(cksum, M_TEMP); - IPSEC_STAT_INCREMENT(ipsec6stat.in_ahauthfail); - goto fail; + if (bcmp(sumpos, cksum, siz) != 0) { + ipseclog((LOG_WARNING, + "checksum mismatch in IPv6 AH input: %s %s\n", + ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav))); + FREE(cksum, M_TEMP); + IPSEC_STAT_INCREMENT(ipsec6stat.in_ahauthfail); + goto fail; + } } - } FREE(cksum, M_TEMP); @@ -745,12 +799,12 @@ ah6_input(mp, offp) sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4; IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1 - + sizeof(struct ip6_hdr), - {return IPPROTO_DONE;}); + + sizeof(struct ip6_hdr), + {return IPPROTO_DONE;}); nip6 = (struct ip6_hdr *)((u_char *)(ah + 1) + sizoff + siz1); if (!IN6_ARE_ADDR_EQUAL(&nip6->ip6_src, &ip6->ip6_src) - || !IN6_ARE_ADDR_EQUAL(&nip6->ip6_dst, &ip6->ip6_dst)) { + || !IN6_ARE_ADDR_EQUAL(&nip6->ip6_dst, &ip6->ip6_dst)) { m->m_flags &= ~M_AUTHIPHDR; m->m_flags &= ~M_AUTHIPDGM; } @@ -764,7 +818,7 @@ ah6_input(mp, offp) #endif if (m->m_flags & M_AUTHIPHDR - && m->m_flags & M_AUTHIPDGM) { + && m->m_flags & M_AUTHIPDGM) { #if 0 ipseclog((LOG_DEBUG, "IPv6 AH input: authentication succeess\n")); @@ -781,8 +835,8 @@ ah6_input(mp, offp) /* * update sequence number. */ - if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) { - if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) { + if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay[0] != NULL) { + if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav, 0)) { IPSEC_STAT_INCREMENT(ipsec6stat.in_ahreplay); goto fail; } @@ -796,7 +850,10 @@ ah6_input(mp, offp) /* RFC 2402 */ stripsiz = sizeof(struct newah) + siz1; } - if (ipsec6_tunnel_validate(m, off + stripsiz, nxt, sav)) { + if (ipsec6_tunnel_validate(m, off + stripsiz, nxt, sav, &ifamily)) { + ifaddr_t ifa; + struct sockaddr_storage addr; + struct sockaddr_in6 *ip6addr; /* * strip off all the headers that precedes AH. * IP6 xx AH IP6' payload -> IP6' payload @@ -804,7 +861,13 @@ ah6_input(mp, offp) * XXX more sanity checks * XXX relationship with gif? */ - u_int32_t flowinfo; /*net endian*/ + u_int32_t flowinfo; /*net endian*/ + + if (ifamily == AF_INET) { + ipseclog((LOG_NOTICE, "ipsec tunnel protocol mismatch " + "in IPv6 AH input: %s\n", ipsec_logsastr(sav))); + goto fail; + } flowinfo = ip6->ip6_flow; m_adj(m, off + stripsiz); @@ -821,9 +884,12 @@ ah6_input(mp, offp) } ip6 = mtod(m, struct ip6_hdr *); /* ECN consideration. */ - ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow); + if (ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow) == 0) { + IPSEC_STAT_INCREMENT(ipsec6stat.in_inval); + goto fail; + } if (!key_checktunnelsanity(sav, AF_INET6, - (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) { + (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) { ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch " "in IPv6 AH input: %s %s\n", ipsec6_logpacketstr(ip6, spi), @@ -847,7 +913,43 @@ ah6_input(mp, offp) IPSEC_STAT_INCREMENT(ipsec6stat.in_nomem); goto fail; } - proto_input(PF_INET6, m); + + bzero(&addr, sizeof(addr)); + ip6addr = (__typeof__(ip6addr)) & addr; + ip6addr->sin6_family = AF_INET6; + ip6addr->sin6_len = sizeof(*ip6addr); + ip6addr->sin6_addr = ip6->ip6_dst; + + // 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 + lck_mtx_lock(sadb_mutex); + ifnet_t ipsec_if = sav->sah->ipsec_if; + if (ipsec_if != NULL) { + // If an interface is found, add a reference count before dropping the lock + ifnet_reference(ipsec_if); + } + lck_mtx_unlock(sadb_mutex); + if (ipsec_if != NULL) { + errno_t inject_error = ipsec_inject_inbound_packet(ipsec_if, m); + ifnet_release(ipsec_if); + if (inject_error == 0) { + m = NULL; + nxt = IPPROTO_DONE; + goto done; + } else { + goto fail; + } + } + + if (proto_input(PF_INET6, m) != 0) { + goto fail; + } nxt = IPPROTO_DONE; } else { /* @@ -864,7 +966,6 @@ ah6_input(mp, offp) *prvnxtp = nxt; ip6 = mtod(m, struct ip6_hdr *); -#ifndef PULLDOWN_TEST /* * We do deep-copy since KAME requires that * the packet is placed in a single mbuf. @@ -873,34 +974,6 @@ ah6_input(mp, offp) m->m_data += stripsiz; m->m_len -= stripsiz; m->m_pkthdr.len -= stripsiz; -#else - /* - * even in m_pulldown case, we need to strip off AH so that - * we can compute checksum for multiple AH correctly. - */ - if (m->m_len >= stripsiz + off) { - ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off); - m->m_data += stripsiz; - m->m_len -= stripsiz; - m->m_pkthdr.len -= stripsiz; - } else { - /* - * this comes with no copy if the boundary is on - * cluster - */ - struct mbuf *n; - - n = m_split(m, off, M_DONTWAIT); - if (n == NULL) { - /* m is retained by m_split */ - goto fail; - } - m_adj(n, stripsiz); - /* m_cat does not update m_pkthdr.len */ - m->m_pkthdr.len += n->m_pkthdr.len; - m_cat(m, n); - } -#endif ip6 = mtod(m, struct ip6_hdr *); /* XXX jumbogram */ ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz); @@ -910,14 +983,35 @@ ah6_input(mp, offp) IPSEC_STAT_INCREMENT(ipsec6stat.in_nomem); goto fail; } + + // Input via IPsec interface + lck_mtx_lock(sadb_mutex); + ifnet_t ipsec_if = sav->sah->ipsec_if; + if (ipsec_if != NULL) { + // If an interface is found, add a reference count before dropping the lock + ifnet_reference(ipsec_if); + } + lck_mtx_unlock(sadb_mutex); + if (ipsec_if != NULL) { + errno_t inject_error = ipsec_inject_inbound_packet(ipsec_if, m); + ifnet_release(ipsec_if); + if (inject_error == 0) { + m = NULL; + nxt = IPPROTO_DONE; + goto done; + } else { + goto fail; + } + } } +done: *offp = off; *mp = m; - if (sav) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, - printf("DP ah6_input call free SA:%p\n", sav)); + printf("DP ah6_input call free SA:0x%llx\n", + (uint64_t)VM_KERNEL_ADDRPERM(sav))); key_freesav(sav, KEY_SADB_UNLOCKED); } IPSEC_STAT_INCREMENT(ipsec6stat.in_success); @@ -926,19 +1020,18 @@ ah6_input(mp, offp) fail: if (sav) { KEYDEBUG(KEYDEBUG_IPSEC_STAMP, - printf("DP ah6_input call free SA:%p\n", sav)); + printf("DP ah6_input call free SA:0x%llx\n", + (uint64_t)VM_KERNEL_ADDRPERM(sav))); key_freesav(sav, KEY_SADB_UNLOCKED); } - if (m) + if (m) { m_freem(m); + } return IPPROTO_DONE; } void -ah6_ctlinput(cmd, sa, d) - int cmd; - struct sockaddr *sa; - void *d; +ah6_ctlinput(int cmd, struct sockaddr *sa, void *d) { const struct newah *ahp; struct newah ah; @@ -946,14 +1039,16 @@ ah6_ctlinput(cmd, sa, d) struct ip6_hdr *ip6; struct mbuf *m; struct ip6ctlparam *ip6cp = NULL; - int off; + int off = 0; struct sockaddr_in6 *sa6_src, *sa6_dst; if (sa->sa_family != AF_INET6 || - sa->sa_len != sizeof(struct sockaddr_in6)) + sa->sa_len != sizeof(struct sockaddr_in6)) { return; - if ((unsigned)cmd >= PRC_NCMDS) + } + if ((unsigned)cmd >= PRC_NCMDS) { return; + } /* if the parameter is from icmp6, decode it. */ if (d != NULL) { @@ -973,8 +1068,9 @@ ah6_ctlinput(cmd, sa, d) */ /* check if we can safely examine src and dst ports */ - if (m->m_pkthdr.len < off + sizeof(ah)) + if (m->m_pkthdr.len < off + sizeof(ah)) { return; + } if (m->m_len < off + sizeof(ah)) { /* @@ -983,8 +1079,9 @@ ah6_ctlinput(cmd, sa, d) */ m_copydata(m, off, sizeof(ah), (caddr_t)&ah); ahp = &ah; - } else - ahp = (struct newah *)(mtod(m, caddr_t) + off); + } else { + ahp = (struct newah *)(void *)(mtod(m, caddr_t) + off); + } if (cmd == PRC_MSGSIZE) { int valid = 0; @@ -994,15 +1091,16 @@ ah6_ctlinput(cmd, sa, d) * the address in the ICMP message payload. */ sa6_src = ip6cp->ip6c_src; - sa6_dst = (struct sockaddr_in6 *)sa; + sa6_dst = (struct sockaddr_in6 *)(void *)sa; sav = key_allocsa(AF_INET6, - (caddr_t)&sa6_src->sin6_addr, - (caddr_t)&sa6_dst->sin6_addr, - IPPROTO_AH, ahp->ah_spi); + (caddr_t)&sa6_src->sin6_addr, + (caddr_t)&sa6_dst->sin6_addr, + IPPROTO_AH, ahp->ah_spi); if (sav) { if (sav->state == SADB_SASTATE_MATURE || - sav->state == SADB_SASTATE_DYING) + sav->state == SADB_SASTATE_DYING) { valid++; + } key_freesav(sav, KEY_SADB_UNLOCKED); }