ipsec.c revision 206111
1105197Ssam/* $FreeBSD: head/sys/netipsec/ipsec.c 206111 2010-04-02 18:15:23Z eri $ */ 2105197Ssam/* $KAME: ipsec.c,v 1.103 2001/05/24 07:14:18 sakane Exp $ */ 3105197Ssam 4139823Simp/*- 5105197Ssam * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 6105197Ssam * All rights reserved. 7105197Ssam * 8105197Ssam * Redistribution and use in source and binary forms, with or without 9105197Ssam * modification, are permitted provided that the following conditions 10105197Ssam * are met: 11105197Ssam * 1. Redistributions of source code must retain the above copyright 12105197Ssam * notice, this list of conditions and the following disclaimer. 13105197Ssam * 2. Redistributions in binary form must reproduce the above copyright 14105197Ssam * notice, this list of conditions and the following disclaimer in the 15105197Ssam * documentation and/or other materials provided with the distribution. 16105197Ssam * 3. Neither the name of the project nor the names of its contributors 17105197Ssam * may be used to endorse or promote products derived from this software 18105197Ssam * without specific prior written permission. 19105197Ssam * 20105197Ssam * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21105197Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22105197Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23105197Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24105197Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25105197Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26105197Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27105197Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28105197Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29105197Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30105197Ssam * SUCH DAMAGE. 31105197Ssam */ 32105197Ssam 33105197Ssam/* 34105197Ssam * IPsec controller part. 35105197Ssam */ 36105197Ssam 37105197Ssam#include "opt_inet.h" 38105197Ssam#include "opt_inet6.h" 39105197Ssam#include "opt_ipsec.h" 40105197Ssam 41105197Ssam#include <sys/param.h> 42105197Ssam#include <sys/systm.h> 43105197Ssam#include <sys/malloc.h> 44105197Ssam#include <sys/mbuf.h> 45105197Ssam#include <sys/domain.h> 46164056Srwatson#include <sys/priv.h> 47105197Ssam#include <sys/protosw.h> 48105197Ssam#include <sys/socket.h> 49105197Ssam#include <sys/socketvar.h> 50105197Ssam#include <sys/errno.h> 51105197Ssam#include <sys/time.h> 52105197Ssam#include <sys/kernel.h> 53105197Ssam#include <sys/syslog.h> 54105197Ssam#include <sys/sysctl.h> 55105197Ssam#include <sys/proc.h> 56105197Ssam 57105197Ssam#include <net/if.h> 58105197Ssam#include <net/route.h> 59195699Srwatson#include <net/vnet.h> 60105197Ssam 61105197Ssam#include <netinet/in.h> 62105197Ssam#include <netinet/in_systm.h> 63105197Ssam#include <netinet/ip.h> 64105197Ssam#include <netinet/ip_var.h> 65105197Ssam#include <netinet/in_var.h> 66105197Ssam#include <netinet/udp.h> 67105197Ssam#include <netinet/udp_var.h> 68105197Ssam#include <netinet/tcp.h> 69105197Ssam#include <netinet/udp.h> 70105197Ssam 71105197Ssam#include <netinet/ip6.h> 72105197Ssam#ifdef INET6 73105197Ssam#include <netinet6/ip6_var.h> 74105197Ssam#endif 75105197Ssam#include <netinet/in_pcb.h> 76105197Ssam#ifdef INET6 77105197Ssam#include <netinet/icmp6.h> 78105197Ssam#endif 79105197Ssam 80171133Sgnn#include <sys/types.h> 81105197Ssam#include <netipsec/ipsec.h> 82105197Ssam#ifdef INET6 83105197Ssam#include <netipsec/ipsec6.h> 84105197Ssam#endif 85105197Ssam#include <netipsec/ah_var.h> 86105197Ssam#include <netipsec/esp_var.h> 87105197Ssam#include <netipsec/ipcomp.h> /*XXX*/ 88105197Ssam#include <netipsec/ipcomp_var.h> 89105197Ssam 90105197Ssam#include <netipsec/key.h> 91105197Ssam#include <netipsec/keydb.h> 92105197Ssam#include <netipsec/key_debug.h> 93105197Ssam 94105197Ssam#include <netipsec/xform.h> 95105197Ssam 96105197Ssam#include <machine/in_cksum.h> 97105197Ssam 98167820Ssam#include <opencrypto/cryptodev.h> 99167820Ssam 100195699Srwatson#ifdef IPSEC_DEBUG 101195699SrwatsonVNET_DEFINE(int, ipsec_debug) = 1; 102195699Srwatson#else 103195699SrwatsonVNET_DEFINE(int, ipsec_debug) = 0; 104185895Szec#endif 105195699Srwatson/* NB: name changed so netstat doesn't use it. */ 106195699SrwatsonVNET_DEFINE(struct ipsecstat, ipsec4stat); 107195699SrwatsonVNET_DEFINE(int, ip4_ah_offsetmask) = 0; /* maybe IP_DF? */ 108195699Srwatson/* DF bit on encap. 0: clear 1: set 2: copy */ 109195699SrwatsonVNET_DEFINE(int, ip4_ipsec_dfbit) = 0; 110195699SrwatsonVNET_DEFINE(int, ip4_esp_trans_deflev) = IPSEC_LEVEL_USE; 111195699SrwatsonVNET_DEFINE(int, ip4_esp_net_deflev) = IPSEC_LEVEL_USE; 112195699SrwatsonVNET_DEFINE(int, ip4_ah_trans_deflev) = IPSEC_LEVEL_USE; 113195699SrwatsonVNET_DEFINE(int, ip4_ah_net_deflev) = IPSEC_LEVEL_USE; 114195699SrwatsonVNET_DEFINE(struct secpolicy, ip4_def_policy); 115195699Srwatson/* ECN ignore(-1)/forbidden(0)/allowed(1) */ 116195699SrwatsonVNET_DEFINE(int, ip4_ipsec_ecn) = 0; 117195699SrwatsonVNET_DEFINE(int, ip4_esp_randpad) = -1; 118185895Szec 119105197Ssam/* 120105197Ssam * Crypto support requirements: 121105197Ssam * 122105197Ssam * 1 require hardware support 123105197Ssam * -1 require software support 124105197Ssam * 0 take anything 125105197Ssam */ 126195699SrwatsonVNET_DEFINE(int, crypto_support) = CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE; 127105197Ssam 128105197SsamSYSCTL_DECL(_net_inet_ipsec); 129105197Ssam 130105197Ssam/* net.inet.ipsec */ 131195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEF_POLICY, def_policy, 132195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_def_policy).policy, 0, 133183550Szec "IPsec default policy."); 134195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev, 135195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_esp_trans_deflev), 0, 136183550Szec "Default ESP transport mode level"); 137195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_NETLEV, esp_net_deflev, 138195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_esp_net_deflev), 0, 139183550Szec "Default ESP tunnel mode level."); 140195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_TRANSLEV, ah_trans_deflev, 141195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_ah_trans_deflev), 0, 142183550Szec "AH transfer mode default level."); 143195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev, 144195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_ah_net_deflev), 0, 145183550Szec "AH tunnel mode default level."); 146195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_AH_CLEARTOS, ah_cleartos, 147195699Srwatson CTLFLAG_RW, &VNET_NAME(ah_cleartos), 0, 148183550Szec "If set clear type-of-service field when doing AH computation."); 149195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_AH_OFFSETMASK, ah_offsetmask, 150195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_ah_offsetmask), 0, 151183550Szec "If not set clear offset field mask when doing AH computation."); 152195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DFBIT, dfbit, 153195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_ipsec_dfbit), 0, 154183550Szec "Do not fragment bit on encap."); 155195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_ECN, ecn, 156195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_ipsec_ecn), 0, 157183550Szec "Explicit Congestion Notification handling."); 158195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEBUG, debug, 159195699Srwatson CTLFLAG_RW, &VNET_NAME(ipsec_debug), 0, 160183550Szec "Enable IPsec debugging output when set."); 161195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, OID_AUTO, crypto_support, 162195699Srwatson CTLFLAG_RW, &VNET_NAME(crypto_support), 0, 163183550Szec "Crypto driver selection."); 164195699SrwatsonSYSCTL_VNET_STRUCT(_net_inet_ipsec, OID_AUTO, ipsecstats, 165195699Srwatson CTLFLAG_RD, &VNET_NAME(ipsec4stat), ipsecstat, 166183550Szec "IPsec IPv4 statistics."); 167105197Ssam 168157634Spjd#ifdef REGRESSION 169157613Spjd/* 170157613Spjd * When set to 1, IPsec will send packets with the same sequence number. 171157613Spjd * This allows to verify if the other side has proper replay attacks detection. 172157613Spjd */ 173195699SrwatsonVNET_DEFINE(int, ipsec_replay) = 0; 174195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, OID_AUTO, test_replay, 175195699Srwatson CTLFLAG_RW, &VNET_NAME(ipsec_replay), 0, 176195699Srwatson "Emulate replay attack"); 177157613Spjd/* 178157613Spjd * When set 1, IPsec will send packets with corrupted HMAC. 179157613Spjd * This allows to verify if the other side properly detects modified packets. 180157613Spjd */ 181195699SrwatsonVNET_DEFINE(int, ipsec_integrity) = 0; 182195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, OID_AUTO, test_integrity, 183195699Srwatson CTLFLAG_RW, &VNET_NAME(ipsec_integrity), 0, 184195699Srwatson "Emulate man-in-the-middle attack"); 185157634Spjd#endif 186157613Spjd 187171133Sgnn#ifdef INET6 188195699SrwatsonVNET_DEFINE(struct ipsecstat, ipsec6stat); 189195699SrwatsonVNET_DEFINE(int, ip6_esp_trans_deflev) = IPSEC_LEVEL_USE; 190195699SrwatsonVNET_DEFINE(int, ip6_esp_net_deflev) = IPSEC_LEVEL_USE; 191195699SrwatsonVNET_DEFINE(int, ip6_ah_trans_deflev) = IPSEC_LEVEL_USE; 192195699SrwatsonVNET_DEFINE(int, ip6_ah_net_deflev) = IPSEC_LEVEL_USE; 193195699SrwatsonVNET_DEFINE(int, ip6_ipsec_ecn) = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */ 194105197Ssam 195105197SsamSYSCTL_DECL(_net_inet6_ipsec6); 196105197Ssam 197105197Ssam/* net.inet6.ipsec6 */ 198105197Ssam#ifdef COMPAT_KAME 199105197SsamSYSCTL_OID(_net_inet6_ipsec6, IPSECCTL_STATS, stats, CTLFLAG_RD, 200180820Strhodes 0, 0, compat_ipsecstats_sysctl, "S", "IPsec IPv6 statistics."); 201105197Ssam#endif /* COMPAT_KAME */ 202195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEF_POLICY, def_policy, CTLFLAG_RW, 203195699Srwatson &VNET_NAME(ip4_def_policy).policy, 0, 204183550Szec "IPsec default policy."); 205195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_TRANSLEV, 206195699Srwatson esp_trans_deflev, CTLFLAG_RW, &VNET_NAME(ip6_esp_trans_deflev), 0, 207183550Szec "Default ESP transport mode level."); 208195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_NETLEV, 209195699Srwatson esp_net_deflev, CTLFLAG_RW, &VNET_NAME(ip6_esp_net_deflev), 0, 210183550Szec "Default ESP tunnel mode level."); 211195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_TRANSLEV, 212195699Srwatson ah_trans_deflev, CTLFLAG_RW, &VNET_NAME(ip6_ah_trans_deflev), 0, 213183550Szec "AH transfer mode default level."); 214195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_NETLEV, 215195699Srwatson ah_net_deflev, CTLFLAG_RW, &VNET_NAME(ip6_ah_net_deflev), 0, 216183550Szec "AH tunnel mode default level."); 217195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_ECN, 218195699Srwatson ecn, CTLFLAG_RW, &VNET_NAME(ip6_ipsec_ecn), 0, 219183550Szec "Explicit Congestion Notification handling."); 220195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEBUG, debug, CTLFLAG_RW, 221195699Srwatson &VNET_NAME(ipsec_debug), 0, 222183550Szec "Enable IPsec debugging output when set."); 223195699SrwatsonSYSCTL_VNET_STRUCT(_net_inet6_ipsec6, IPSECCTL_STATS, 224195699Srwatson ipsecstats, CTLFLAG_RD, &VNET_NAME(ipsec6stat), ipsecstat, 225183550Szec "IPsec IPv6 statistics."); 226105197Ssam#endif /* INET6 */ 227105197Ssam 228188306Sbzstatic int ipsec_setspidx_inpcb __P((struct mbuf *, struct inpcb *)); 229105197Ssamstatic int ipsec_setspidx __P((struct mbuf *, struct secpolicyindex *, int)); 230105197Ssamstatic void ipsec4_get_ulp __P((struct mbuf *m, struct secpolicyindex *, int)); 231105197Ssamstatic int ipsec4_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); 232105197Ssam#ifdef INET6 233105197Ssamstatic void ipsec6_get_ulp __P((struct mbuf *m, struct secpolicyindex *, int)); 234105197Ssamstatic int ipsec6_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); 235105197Ssam#endif 236105197Ssamstatic void ipsec_delpcbpolicy __P((struct inpcbpolicy *)); 237105197Ssamstatic struct secpolicy *ipsec_deepcopy_policy __P((struct secpolicy *src)); 238105197Ssamstatic void vshiftl __P((unsigned char *, int, int)); 239105197Ssam 240119643SsamMALLOC_DEFINE(M_IPSEC_INPCB, "inpcbpolicy", "inpcb-resident ipsec policy"); 241119643Ssam 242105197Ssam/* 243105197Ssam * Return a held reference to the default SP. 244105197Ssam */ 245105197Ssamstatic struct secpolicy * 246105197Ssamkey_allocsp_default(const char* where, int tag) 247105197Ssam{ 248105197Ssam struct secpolicy *sp; 249105197Ssam 250105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 251105197Ssam printf("DP key_allocsp_default from %s:%u\n", where, tag)); 252105197Ssam 253181803Sbz sp = &V_ip4_def_policy; 254105197Ssam if (sp->policy != IPSEC_POLICY_DISCARD && 255105197Ssam sp->policy != IPSEC_POLICY_NONE) { 256105197Ssam ipseclog((LOG_INFO, "fixed system default policy: %d->%d\n", 257105197Ssam sp->policy, IPSEC_POLICY_NONE)); 258105197Ssam sp->policy = IPSEC_POLICY_NONE; 259105197Ssam } 260135947Ssam key_addref(sp); 261105197Ssam 262105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 263105197Ssam printf("DP key_allocsp_default returns SP:%p (%u)\n", 264105197Ssam sp, sp->refcnt)); 265186531Sbz return (sp); 266105197Ssam} 267105197Ssam#define KEY_ALLOCSP_DEFAULT() \ 268105197Ssam key_allocsp_default(__FILE__, __LINE__) 269105197Ssam 270105197Ssam/* 271105197Ssam * For OUTBOUND packet having a socket. Searching SPD for packet, 272105197Ssam * and return a pointer to SP. 273105197Ssam * OUT: NULL: no apropreate SP found, the following value is set to error. 274105197Ssam * 0 : bypass 275105197Ssam * EACCES : discard packet. 276105197Ssam * ENOENT : ipsec_acquire() in progress, maybe. 277105197Ssam * others : error occured. 278105197Ssam * others: a pointer to SP 279105197Ssam * 280105197Ssam * NOTE: IPv6 mapped adddress concern is implemented here. 281105197Ssam */ 282105197Ssamstruct secpolicy * 283105197Ssamipsec_getpolicy(struct tdb_ident *tdbi, u_int dir) 284105197Ssam{ 285105197Ssam struct secpolicy *sp; 286105197Ssam 287120585Ssam IPSEC_ASSERT(tdbi != NULL, ("null tdbi")); 288120585Ssam IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND, 289120585Ssam ("invalid direction %u", dir)); 290105197Ssam 291105197Ssam sp = KEY_ALLOCSP2(tdbi->spi, &tdbi->dst, tdbi->proto, dir); 292105197Ssam if (sp == NULL) /*XXX????*/ 293105197Ssam sp = KEY_ALLOCSP_DEFAULT(); 294120585Ssam IPSEC_ASSERT(sp != NULL, ("null SP")); 295186531Sbz return (sp); 296105197Ssam} 297105197Ssam 298105197Ssam/* 299105197Ssam * For OUTBOUND packet having a socket. Searching SPD for packet, 300105197Ssam * and return a pointer to SP. 301105197Ssam * OUT: NULL: no apropreate SP found, the following value is set to error. 302105197Ssam * 0 : bypass 303105197Ssam * EACCES : discard packet. 304105197Ssam * ENOENT : ipsec_acquire() in progress, maybe. 305105197Ssam * others : error occured. 306105197Ssam * others: a pointer to SP 307105197Ssam * 308105197Ssam * NOTE: IPv6 mapped adddress concern is implemented here. 309105197Ssam */ 310186508Sbzstatic struct secpolicy * 311186508Sbzipsec_getpolicybysock(struct mbuf *m, u_int dir, struct inpcb *inp, int *error) 312105197Ssam{ 313188306Sbz struct inpcbpolicy *pcbsp; 314186531Sbz struct secpolicy *currsp = NULL; /* Policy on socket. */ 315105197Ssam struct secpolicy *sp; 316105197Ssam 317120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 318120585Ssam IPSEC_ASSERT(inp != NULL, ("null inpcb")); 319120585Ssam IPSEC_ASSERT(error != NULL, ("null error")); 320120585Ssam IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND, 321120585Ssam ("invalid direction %u", dir)); 322105197Ssam 323186531Sbz /* Set spidx in pcb. */ 324188306Sbz *error = ipsec_setspidx_inpcb(m, inp); 325105197Ssam if (*error) 326186531Sbz return (NULL); 327105197Ssam 328188306Sbz pcbsp = inp->inp_sp; 329120585Ssam IPSEC_ASSERT(pcbsp != NULL, ("null pcbsp")); 330105197Ssam switch (dir) { 331105197Ssam case IPSEC_DIR_INBOUND: 332105197Ssam currsp = pcbsp->sp_in; 333105197Ssam break; 334105197Ssam case IPSEC_DIR_OUTBOUND: 335105197Ssam currsp = pcbsp->sp_out; 336105197Ssam break; 337105197Ssam } 338120585Ssam IPSEC_ASSERT(currsp != NULL, ("null currsp")); 339105197Ssam 340186531Sbz if (pcbsp->priv) { /* When privilieged socket. */ 341105197Ssam switch (currsp->policy) { 342105197Ssam case IPSEC_POLICY_BYPASS: 343105197Ssam case IPSEC_POLICY_IPSEC: 344135947Ssam key_addref(currsp); 345105197Ssam sp = currsp; 346105197Ssam break; 347105197Ssam 348105197Ssam case IPSEC_POLICY_ENTRUST: 349186531Sbz /* Look for a policy in SPD. */ 350105197Ssam sp = KEY_ALLOCSP(&currsp->spidx, dir); 351186531Sbz if (sp == NULL) /* No SP found. */ 352105197Ssam sp = KEY_ALLOCSP_DEFAULT(); 353105197Ssam break; 354105197Ssam 355105197Ssam default: 356120585Ssam ipseclog((LOG_ERR, "%s: Invalid policy for PCB %d\n", 357120585Ssam __func__, currsp->policy)); 358105197Ssam *error = EINVAL; 359186531Sbz return (NULL); 360105197Ssam } 361186531Sbz } else { /* Unpriv, SPD has policy. */ 362105197Ssam sp = KEY_ALLOCSP(&currsp->spidx, dir); 363186531Sbz if (sp == NULL) { /* No SP found. */ 364105197Ssam switch (currsp->policy) { 365105197Ssam case IPSEC_POLICY_BYPASS: 366120585Ssam ipseclog((LOG_ERR, "%s: Illegal policy for " 367120585Ssam "non-priviliged defined %d\n", 368120585Ssam __func__, currsp->policy)); 369105197Ssam *error = EINVAL; 370186531Sbz return (NULL); 371105197Ssam 372105197Ssam case IPSEC_POLICY_ENTRUST: 373105197Ssam sp = KEY_ALLOCSP_DEFAULT(); 374105197Ssam break; 375105197Ssam 376105197Ssam case IPSEC_POLICY_IPSEC: 377135947Ssam key_addref(currsp); 378105197Ssam sp = currsp; 379105197Ssam break; 380105197Ssam 381105197Ssam default: 382120585Ssam ipseclog((LOG_ERR, "%s: Invalid policy for " 383120585Ssam "PCB %d\n", __func__, currsp->policy)); 384105197Ssam *error = EINVAL; 385186531Sbz return (NULL); 386105197Ssam } 387105197Ssam } 388105197Ssam } 389120585Ssam IPSEC_ASSERT(sp != NULL, 390120585Ssam ("null SP (priv %u policy %u", pcbsp->priv, currsp->policy)); 391105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 392120585Ssam printf("DP %s (priv %u policy %u) allocate SP:%p (refcnt %u)\n", 393120585Ssam __func__, pcbsp->priv, currsp->policy, sp, sp->refcnt)); 394186531Sbz return (sp); 395105197Ssam} 396105197Ssam 397105197Ssam/* 398105197Ssam * For FORWADING packet or OUTBOUND without a socket. Searching SPD for packet, 399105197Ssam * and return a pointer to SP. 400105197Ssam * OUT: positive: a pointer to the entry for security policy leaf matched. 401105197Ssam * NULL: no apropreate SP found, the following value is set to error. 402105197Ssam * 0 : bypass 403105197Ssam * EACCES : discard packet. 404105197Ssam * ENOENT : ipsec_acquire() in progress, maybe. 405105197Ssam * others : error occured. 406105197Ssam */ 407105197Ssamstruct secpolicy * 408186530Sbzipsec_getpolicybyaddr(struct mbuf *m, u_int dir, int flag, int *error) 409105197Ssam{ 410105197Ssam struct secpolicyindex spidx; 411105197Ssam struct secpolicy *sp; 412105197Ssam 413120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 414120585Ssam IPSEC_ASSERT(error != NULL, ("null error")); 415120585Ssam IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND, 416120585Ssam ("invalid direction %u", dir)); 417105197Ssam 418105197Ssam sp = NULL; 419105197Ssam if (key_havesp(dir)) { 420108533Sschweikh /* Make an index to look for a policy. */ 421105197Ssam *error = ipsec_setspidx(m, &spidx, 422105197Ssam (flag & IP_FORWARDING) ? 0 : 1); 423105197Ssam if (*error != 0) { 424120585Ssam DPRINTF(("%s: setpidx failed, dir %u flag %u\n", 425120585Ssam __func__, dir, flag)); 426186531Sbz return (NULL); 427105197Ssam } 428105197Ssam spidx.dir = dir; 429105197Ssam 430105197Ssam sp = KEY_ALLOCSP(&spidx, dir); 431105197Ssam } 432186531Sbz if (sp == NULL) /* No SP found, use system default. */ 433105197Ssam sp = KEY_ALLOCSP_DEFAULT(); 434120585Ssam IPSEC_ASSERT(sp != NULL, ("null SP")); 435186531Sbz return (sp); 436105197Ssam} 437105197Ssam 438105197Ssamstruct secpolicy * 439186530Sbzipsec4_checkpolicy(struct mbuf *m, u_int dir, u_int flag, int *error, 440186530Sbz struct inpcb *inp) 441105197Ssam{ 442105197Ssam struct secpolicy *sp; 443105197Ssam 444105197Ssam *error = 0; 445105197Ssam if (inp == NULL) 446105197Ssam sp = ipsec_getpolicybyaddr(m, dir, flag, error); 447105197Ssam else 448105197Ssam sp = ipsec_getpolicybysock(m, dir, inp, error); 449105197Ssam if (sp == NULL) { 450120585Ssam IPSEC_ASSERT(*error != 0, ("getpolicy failed w/o error")); 451181803Sbz V_ipsec4stat.ips_out_inval++; 452186531Sbz return (NULL); 453105197Ssam } 454120585Ssam IPSEC_ASSERT(*error == 0, ("sp w/ error set to %u", *error)); 455105197Ssam switch (sp->policy) { 456105197Ssam case IPSEC_POLICY_ENTRUST: 457105197Ssam default: 458120585Ssam printf("%s: invalid policy %u\n", __func__, sp->policy); 459186531Sbz /* FALLTHROUGH */ 460105197Ssam case IPSEC_POLICY_DISCARD: 461181803Sbz V_ipsec4stat.ips_out_polvio++; 462186531Sbz *error = -EINVAL; /* Packet is discarded by caller. */ 463105197Ssam break; 464105197Ssam case IPSEC_POLICY_BYPASS: 465105197Ssam case IPSEC_POLICY_NONE: 466105197Ssam KEY_FREESP(&sp); 467186531Sbz sp = NULL; /* NB: force NULL result. */ 468105197Ssam break; 469105197Ssam case IPSEC_POLICY_IPSEC: 470186531Sbz if (sp->req == NULL) /* Acquire a SA. */ 471105197Ssam *error = key_spdacquire(sp); 472105197Ssam break; 473105197Ssam } 474105197Ssam if (*error != 0) { 475105197Ssam KEY_FREESP(&sp); 476105197Ssam sp = NULL; 477105197Ssam } 478186531Sbz return (sp); 479105197Ssam} 480105197Ssam 481105197Ssamstatic int 482188306Sbzipsec_setspidx_inpcb(struct mbuf *m, struct inpcb *inp) 483105197Ssam{ 484105197Ssam int error; 485105197Ssam 486186532Sbz IPSEC_ASSERT(inp != NULL, ("null inp")); 487186532Sbz IPSEC_ASSERT(inp->inp_sp != NULL, ("null inp_sp")); 488186532Sbz IPSEC_ASSERT(inp->inp_sp->sp_out != NULL && inp->inp_sp->sp_in != NULL, 489120585Ssam ("null sp_in || sp_out")); 490105197Ssam 491186532Sbz error = ipsec_setspidx(m, &inp->inp_sp->sp_in->spidx, 1); 492105197Ssam if (error == 0) { 493186532Sbz inp->inp_sp->sp_in->spidx.dir = IPSEC_DIR_INBOUND; 494186532Sbz inp->inp_sp->sp_out->spidx = inp->inp_sp->sp_in->spidx; 495186532Sbz inp->inp_sp->sp_out->spidx.dir = IPSEC_DIR_OUTBOUND; 496105197Ssam } else { 497186532Sbz bzero(&inp->inp_sp->sp_in->spidx, 498186532Sbz sizeof (inp->inp_sp->sp_in->spidx)); 499186532Sbz bzero(&inp->inp_sp->sp_out->spidx, 500186532Sbz sizeof (inp->inp_sp->sp_in->spidx)); 501105197Ssam } 502186531Sbz return (error); 503105197Ssam} 504105197Ssam 505105197Ssam/* 506186531Sbz * Configure security policy index (src/dst/proto/sport/dport) 507105197Ssam * by looking at the content of mbuf. 508186531Sbz * The caller is responsible for error recovery (like clearing up spidx). 509105197Ssam */ 510105197Ssamstatic int 511186530Sbzipsec_setspidx(struct mbuf *m, struct secpolicyindex *spidx, int needport) 512105197Ssam{ 513105197Ssam struct ip *ip = NULL; 514105197Ssam struct ip ipbuf; 515105197Ssam u_int v; 516105197Ssam struct mbuf *n; 517105197Ssam int len; 518105197Ssam int error; 519105197Ssam 520120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 521105197Ssam 522105197Ssam /* 523186531Sbz * Validate m->m_pkthdr.len. We see incorrect length if we 524105197Ssam * mistakenly call this function with inconsistent mbuf chain 525186531Sbz * (like 4.4BSD tcp/udp processing). XXX Should we panic here? 526105197Ssam */ 527105197Ssam len = 0; 528105197Ssam for (n = m; n; n = n->m_next) 529105197Ssam len += n->m_len; 530105197Ssam if (m->m_pkthdr.len != len) { 531105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 532120585Ssam printf("%s: pkthdr len(%d) mismatch (%d), ignored.\n", 533120585Ssam __func__, len, m->m_pkthdr.len)); 534186531Sbz return (EINVAL); 535105197Ssam } 536105197Ssam 537105197Ssam if (m->m_pkthdr.len < sizeof(struct ip)) { 538105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 539120585Ssam printf("%s: pkthdr len(%d) too small (v4), ignored.\n", 540120585Ssam __func__, m->m_pkthdr.len)); 541186531Sbz return (EINVAL); 542105197Ssam } 543105197Ssam 544105197Ssam if (m->m_len >= sizeof(*ip)) 545105197Ssam ip = mtod(m, struct ip *); 546105197Ssam else { 547105197Ssam m_copydata(m, 0, sizeof(ipbuf), (caddr_t)&ipbuf); 548105197Ssam ip = &ipbuf; 549105197Ssam } 550105197Ssam#ifdef _IP_VHL 551105197Ssam v = _IP_VHL_V(ip->ip_vhl); 552105197Ssam#else 553105197Ssam v = ip->ip_v; 554105197Ssam#endif 555105197Ssam switch (v) { 556105197Ssam case 4: 557105197Ssam error = ipsec4_setspidx_ipaddr(m, spidx); 558105197Ssam if (error) 559186531Sbz return (error); 560105197Ssam ipsec4_get_ulp(m, spidx, needport); 561186531Sbz return (0); 562105197Ssam#ifdef INET6 563105197Ssam case 6: 564105197Ssam if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) { 565105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 566120585Ssam printf("%s: pkthdr len(%d) too small (v6), " 567120585Ssam "ignored\n", __func__, m->m_pkthdr.len)); 568186531Sbz return (EINVAL); 569105197Ssam } 570105197Ssam error = ipsec6_setspidx_ipaddr(m, spidx); 571105197Ssam if (error) 572186531Sbz return (error); 573105197Ssam ipsec6_get_ulp(m, spidx, needport); 574186531Sbz return (0); 575105197Ssam#endif 576105197Ssam default: 577105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 578120585Ssam printf("%s: " "unknown IP version %u, ignored.\n", 579120585Ssam __func__, v)); 580186531Sbz return (EINVAL); 581105197Ssam } 582105197Ssam} 583105197Ssam 584105197Ssamstatic void 585105197Ssamipsec4_get_ulp(struct mbuf *m, struct secpolicyindex *spidx, int needport) 586105197Ssam{ 587105197Ssam u_int8_t nxt; 588105197Ssam int off; 589105197Ssam 590186531Sbz /* Sanity check. */ 591120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 592120585Ssam IPSEC_ASSERT(m->m_pkthdr.len >= sizeof(struct ip),("packet too short")); 593105197Ssam 594186531Sbz /* NB: ip_input() flips it into host endian. XXX Need more checking. */ 595206111Seri if (m->m_len >= sizeof (struct ip)) { 596105197Ssam struct ip *ip = mtod(m, struct ip *); 597105197Ssam if (ip->ip_off & (IP_MF | IP_OFFMASK)) 598105197Ssam goto done; 599105197Ssam#ifdef _IP_VHL 600105197Ssam off = _IP_VHL_HL(ip->ip_vhl) << 2; 601105197Ssam#else 602105197Ssam off = ip->ip_hl << 2; 603105197Ssam#endif 604105197Ssam nxt = ip->ip_p; 605105197Ssam } else { 606105197Ssam struct ip ih; 607105197Ssam 608105197Ssam m_copydata(m, 0, sizeof (struct ip), (caddr_t) &ih); 609105197Ssam if (ih.ip_off & (IP_MF | IP_OFFMASK)) 610105197Ssam goto done; 611105197Ssam#ifdef _IP_VHL 612105197Ssam off = _IP_VHL_HL(ih.ip_vhl) << 2; 613105197Ssam#else 614105197Ssam off = ih.ip_hl << 2; 615105197Ssam#endif 616105197Ssam nxt = ih.ip_p; 617105197Ssam } 618105197Ssam 619105197Ssam while (off < m->m_pkthdr.len) { 620105197Ssam struct ip6_ext ip6e; 621105197Ssam struct tcphdr th; 622105197Ssam struct udphdr uh; 623105197Ssam 624105197Ssam switch (nxt) { 625105197Ssam case IPPROTO_TCP: 626105197Ssam spidx->ul_proto = nxt; 627105197Ssam if (!needport) 628105197Ssam goto done_proto; 629105197Ssam if (off + sizeof(struct tcphdr) > m->m_pkthdr.len) 630105197Ssam goto done; 631105197Ssam m_copydata(m, off, sizeof (th), (caddr_t) &th); 632105197Ssam spidx->src.sin.sin_port = th.th_sport; 633105197Ssam spidx->dst.sin.sin_port = th.th_dport; 634105197Ssam return; 635105197Ssam case IPPROTO_UDP: 636105197Ssam spidx->ul_proto = nxt; 637105197Ssam if (!needport) 638105197Ssam goto done_proto; 639105197Ssam if (off + sizeof(struct udphdr) > m->m_pkthdr.len) 640105197Ssam goto done; 641105197Ssam m_copydata(m, off, sizeof (uh), (caddr_t) &uh); 642105197Ssam spidx->src.sin.sin_port = uh.uh_sport; 643105197Ssam spidx->dst.sin.sin_port = uh.uh_dport; 644105197Ssam return; 645105197Ssam case IPPROTO_AH: 646143323Ssam if (off + sizeof(ip6e) > m->m_pkthdr.len) 647105197Ssam goto done; 648186531Sbz /* XXX Sigh, this works but is totally bogus. */ 649105197Ssam m_copydata(m, off, sizeof(ip6e), (caddr_t) &ip6e); 650105197Ssam off += (ip6e.ip6e_len + 2) << 2; 651105197Ssam nxt = ip6e.ip6e_nxt; 652105197Ssam break; 653105197Ssam case IPPROTO_ICMP: 654105197Ssam default: 655186531Sbz /* XXX Intermediate headers??? */ 656105197Ssam spidx->ul_proto = nxt; 657105197Ssam goto done_proto; 658105197Ssam } 659105197Ssam } 660105197Ssamdone: 661105197Ssam spidx->ul_proto = IPSEC_ULPROTO_ANY; 662105197Ssamdone_proto: 663105197Ssam spidx->src.sin.sin_port = IPSEC_PORT_ANY; 664105197Ssam spidx->dst.sin.sin_port = IPSEC_PORT_ANY; 665105197Ssam} 666105197Ssam 667186531Sbz/* Assumes that m is sane. */ 668105197Ssamstatic int 669105197Ssamipsec4_setspidx_ipaddr(struct mbuf *m, struct secpolicyindex *spidx) 670105197Ssam{ 671105197Ssam static const struct sockaddr_in template = { 672105197Ssam sizeof (struct sockaddr_in), 673105197Ssam AF_INET, 674105197Ssam 0, { 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 } 675105197Ssam }; 676105197Ssam 677105197Ssam spidx->src.sin = template; 678105197Ssam spidx->dst.sin = template; 679105197Ssam 680105197Ssam if (m->m_len < sizeof (struct ip)) { 681105197Ssam m_copydata(m, offsetof(struct ip, ip_src), 682105197Ssam sizeof (struct in_addr), 683105197Ssam (caddr_t) &spidx->src.sin.sin_addr); 684105197Ssam m_copydata(m, offsetof(struct ip, ip_dst), 685105197Ssam sizeof (struct in_addr), 686105197Ssam (caddr_t) &spidx->dst.sin.sin_addr); 687105197Ssam } else { 688105197Ssam struct ip *ip = mtod(m, struct ip *); 689105197Ssam spidx->src.sin.sin_addr = ip->ip_src; 690105197Ssam spidx->dst.sin.sin_addr = ip->ip_dst; 691105197Ssam } 692105197Ssam 693105197Ssam spidx->prefs = sizeof(struct in_addr) << 3; 694105197Ssam spidx->prefd = sizeof(struct in_addr) << 3; 695105197Ssam 696186531Sbz return (0); 697105197Ssam} 698105197Ssam 699105197Ssam#ifdef INET6 700105197Ssamstatic void 701186530Sbzipsec6_get_ulp(struct mbuf *m, struct secpolicyindex *spidx, int needport) 702105197Ssam{ 703105197Ssam int off, nxt; 704105197Ssam struct tcphdr th; 705105197Ssam struct udphdr uh; 706170121Sbz struct icmp6_hdr ih; 707105197Ssam 708186531Sbz /* Sanity check. */ 709105197Ssam if (m == NULL) 710120585Ssam panic("%s: NULL pointer was passed.\n", __func__); 711105197Ssam 712105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 713120585Ssam printf("%s:\n", __func__); kdebug_mbuf(m)); 714105197Ssam 715186531Sbz /* Set default. */ 716105197Ssam spidx->ul_proto = IPSEC_ULPROTO_ANY; 717105197Ssam ((struct sockaddr_in6 *)&spidx->src)->sin6_port = IPSEC_PORT_ANY; 718105197Ssam ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = IPSEC_PORT_ANY; 719105197Ssam 720105197Ssam nxt = -1; 721105197Ssam off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt); 722105197Ssam if (off < 0 || m->m_pkthdr.len < off) 723105197Ssam return; 724105197Ssam 725105197Ssam switch (nxt) { 726105197Ssam case IPPROTO_TCP: 727105197Ssam spidx->ul_proto = nxt; 728105197Ssam if (!needport) 729105197Ssam break; 730105197Ssam if (off + sizeof(struct tcphdr) > m->m_pkthdr.len) 731105197Ssam break; 732105197Ssam m_copydata(m, off, sizeof(th), (caddr_t)&th); 733105197Ssam ((struct sockaddr_in6 *)&spidx->src)->sin6_port = th.th_sport; 734105197Ssam ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = th.th_dport; 735105197Ssam break; 736105197Ssam case IPPROTO_UDP: 737105197Ssam spidx->ul_proto = nxt; 738105197Ssam if (!needport) 739105197Ssam break; 740105197Ssam if (off + sizeof(struct udphdr) > m->m_pkthdr.len) 741105197Ssam break; 742105197Ssam m_copydata(m, off, sizeof(uh), (caddr_t)&uh); 743105197Ssam ((struct sockaddr_in6 *)&spidx->src)->sin6_port = uh.uh_sport; 744105197Ssam ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = uh.uh_dport; 745105197Ssam break; 746105197Ssam case IPPROTO_ICMPV6: 747170121Sbz spidx->ul_proto = nxt; 748170121Sbz if (off + sizeof(struct icmp6_hdr) > m->m_pkthdr.len) 749170121Sbz break; 750170121Sbz m_copydata(m, off, sizeof(ih), (caddr_t)&ih); 751170121Sbz ((struct sockaddr_in6 *)&spidx->src)->sin6_port = 752170121Sbz htons((uint16_t)ih.icmp6_type); 753170121Sbz ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = 754170121Sbz htons((uint16_t)ih.icmp6_code); 755170121Sbz break; 756105197Ssam default: 757186531Sbz /* XXX Intermediate headers??? */ 758105197Ssam spidx->ul_proto = nxt; 759105197Ssam break; 760105197Ssam } 761105197Ssam} 762105197Ssam 763186531Sbz/* Assumes that m is sane. */ 764105197Ssamstatic int 765186530Sbzipsec6_setspidx_ipaddr(struct mbuf *m, struct secpolicyindex *spidx) 766105197Ssam{ 767105197Ssam struct ip6_hdr *ip6 = NULL; 768105197Ssam struct ip6_hdr ip6buf; 769105197Ssam struct sockaddr_in6 *sin6; 770105197Ssam 771105197Ssam if (m->m_len >= sizeof(*ip6)) 772105197Ssam ip6 = mtod(m, struct ip6_hdr *); 773105197Ssam else { 774105197Ssam m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf); 775105197Ssam ip6 = &ip6buf; 776105197Ssam } 777105197Ssam 778105197Ssam sin6 = (struct sockaddr_in6 *)&spidx->src; 779105197Ssam bzero(sin6, sizeof(*sin6)); 780105197Ssam sin6->sin6_family = AF_INET6; 781105197Ssam sin6->sin6_len = sizeof(struct sockaddr_in6); 782105197Ssam bcopy(&ip6->ip6_src, &sin6->sin6_addr, sizeof(ip6->ip6_src)); 783105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { 784105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 785105197Ssam sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]); 786105197Ssam } 787105197Ssam spidx->prefs = sizeof(struct in6_addr) << 3; 788105197Ssam 789105197Ssam sin6 = (struct sockaddr_in6 *)&spidx->dst; 790105197Ssam bzero(sin6, sizeof(*sin6)); 791105197Ssam sin6->sin6_family = AF_INET6; 792105197Ssam sin6->sin6_len = sizeof(struct sockaddr_in6); 793105197Ssam bcopy(&ip6->ip6_dst, &sin6->sin6_addr, sizeof(ip6->ip6_dst)); 794105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { 795105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 796105197Ssam sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]); 797105197Ssam } 798105197Ssam spidx->prefd = sizeof(struct in6_addr) << 3; 799105197Ssam 800186531Sbz return (0); 801105197Ssam} 802105197Ssam#endif 803105197Ssam 804105197Ssamstatic void 805186530Sbzipsec_delpcbpolicy(struct inpcbpolicy *p) 806105197Ssam{ 807186531Sbz 808119643Ssam free(p, M_IPSEC_INPCB); 809105197Ssam} 810105197Ssam 811186531Sbz/* Initialize policy in PCB. */ 812105197Ssamint 813186530Sbzipsec_init_policy(struct socket *so, struct inpcbpolicy **pcb_sp) 814105197Ssam{ 815105197Ssam struct inpcbpolicy *new; 816105197Ssam 817186531Sbz /* Sanity check. */ 818105197Ssam if (so == NULL || pcb_sp == NULL) 819120585Ssam panic("%s: NULL pointer was passed.\n", __func__); 820105197Ssam 821105197Ssam new = (struct inpcbpolicy *) malloc(sizeof(struct inpcbpolicy), 822119643Ssam M_IPSEC_INPCB, M_NOWAIT|M_ZERO); 823105197Ssam if (new == NULL) { 824120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); 825186531Sbz return (ENOBUFS); 826105197Ssam } 827105197Ssam 828120585Ssam new->priv = IPSEC_IS_PRIVILEGED_SO(so); 829105197Ssam 830105197Ssam if ((new->sp_in = KEY_NEWSP()) == NULL) { 831105197Ssam ipsec_delpcbpolicy(new); 832186531Sbz return (ENOBUFS); 833105197Ssam } 834105197Ssam new->sp_in->state = IPSEC_SPSTATE_ALIVE; 835105197Ssam new->sp_in->policy = IPSEC_POLICY_ENTRUST; 836105197Ssam 837105197Ssam if ((new->sp_out = KEY_NEWSP()) == NULL) { 838105197Ssam KEY_FREESP(&new->sp_in); 839105197Ssam ipsec_delpcbpolicy(new); 840186531Sbz return (ENOBUFS); 841105197Ssam } 842105197Ssam new->sp_out->state = IPSEC_SPSTATE_ALIVE; 843105197Ssam new->sp_out->policy = IPSEC_POLICY_ENTRUST; 844105197Ssam 845105197Ssam *pcb_sp = new; 846105197Ssam 847186531Sbz return (0); 848105197Ssam} 849105197Ssam 850186531Sbz/* Copy old IPsec policy into new. */ 851105197Ssamint 852186530Sbzipsec_copy_policy(struct inpcbpolicy *old, struct inpcbpolicy *new) 853105197Ssam{ 854105197Ssam struct secpolicy *sp; 855105197Ssam 856105197Ssam sp = ipsec_deepcopy_policy(old->sp_in); 857105197Ssam if (sp) { 858105197Ssam KEY_FREESP(&new->sp_in); 859105197Ssam new->sp_in = sp; 860105197Ssam } else 861186531Sbz return (ENOBUFS); 862105197Ssam 863105197Ssam sp = ipsec_deepcopy_policy(old->sp_out); 864105197Ssam if (sp) { 865105197Ssam KEY_FREESP(&new->sp_out); 866105197Ssam new->sp_out = sp; 867105197Ssam } else 868186531Sbz return (ENOBUFS); 869105197Ssam 870105197Ssam new->priv = old->priv; 871105197Ssam 872186531Sbz return (0); 873105197Ssam} 874105197Ssam 875119643Ssamstruct ipsecrequest * 876119643Ssamipsec_newisr(void) 877119643Ssam{ 878119643Ssam struct ipsecrequest *p; 879119643Ssam 880119643Ssam p = malloc(sizeof(struct ipsecrequest), M_IPSEC_SR, M_NOWAIT|M_ZERO); 881119643Ssam if (p != NULL) 882120585Ssam IPSECREQUEST_LOCK_INIT(p); 883186531Sbz return (p); 884119643Ssam} 885119643Ssam 886119643Ssamvoid 887119643Ssamipsec_delisr(struct ipsecrequest *p) 888119643Ssam{ 889186531Sbz 890120585Ssam IPSECREQUEST_LOCK_DESTROY(p); 891119643Ssam free(p, M_IPSEC_SR); 892119643Ssam} 893119643Ssam 894186531Sbz/* Deep-copy a policy in PCB. */ 895105197Ssamstatic struct secpolicy * 896186530Sbzipsec_deepcopy_policy(struct secpolicy *src) 897105197Ssam{ 898105197Ssam struct ipsecrequest *newchain = NULL; 899105197Ssam struct ipsecrequest *p; 900105197Ssam struct ipsecrequest **q; 901105197Ssam struct ipsecrequest *r; 902105197Ssam struct secpolicy *dst; 903105197Ssam 904105197Ssam if (src == NULL) 905186531Sbz return (NULL); 906105197Ssam dst = KEY_NEWSP(); 907105197Ssam if (dst == NULL) 908186531Sbz return (NULL); 909105197Ssam 910105197Ssam /* 911186531Sbz * Deep-copy IPsec request chain. This is required since struct 912105197Ssam * ipsecrequest is not reference counted. 913105197Ssam */ 914105197Ssam q = &newchain; 915105197Ssam for (p = src->req; p; p = p->next) { 916119643Ssam *q = ipsec_newisr(); 917105197Ssam if (*q == NULL) 918105197Ssam goto fail; 919105197Ssam (*q)->saidx.proto = p->saidx.proto; 920105197Ssam (*q)->saidx.mode = p->saidx.mode; 921105197Ssam (*q)->level = p->level; 922105197Ssam (*q)->saidx.reqid = p->saidx.reqid; 923105197Ssam 924105197Ssam bcopy(&p->saidx.src, &(*q)->saidx.src, sizeof((*q)->saidx.src)); 925105197Ssam bcopy(&p->saidx.dst, &(*q)->saidx.dst, sizeof((*q)->saidx.dst)); 926105197Ssam 927105197Ssam (*q)->sp = dst; 928105197Ssam 929105197Ssam q = &((*q)->next); 930105197Ssam } 931105197Ssam 932105197Ssam dst->req = newchain; 933105197Ssam dst->state = src->state; 934105197Ssam dst->policy = src->policy; 935186531Sbz /* Do not touch the refcnt fields. */ 936105197Ssam 937186531Sbz return (dst); 938105197Ssam 939105197Ssamfail: 940105197Ssam for (p = newchain; p; p = r) { 941105197Ssam r = p->next; 942119643Ssam ipsec_delisr(p); 943105197Ssam p = NULL; 944105197Ssam } 945186531Sbz return (NULL); 946105197Ssam} 947105197Ssam 948186531Sbz/* Set policy and IPsec request if present. */ 949105197Ssamstatic int 950188306Sbzipsec_set_policy_internal(struct secpolicy **pcb_sp, int optname, 951188306Sbz caddr_t request, size_t len, struct ucred *cred) 952105197Ssam{ 953105197Ssam struct sadb_x_policy *xpl; 954105197Ssam struct secpolicy *newsp = NULL; 955105197Ssam int error; 956105197Ssam 957186531Sbz /* Sanity check. */ 958105197Ssam if (pcb_sp == NULL || *pcb_sp == NULL || request == NULL) 959186531Sbz return (EINVAL); 960105197Ssam if (len < sizeof(*xpl)) 961186531Sbz return (EINVAL); 962105197Ssam xpl = (struct sadb_x_policy *)request; 963105197Ssam 964105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 965120585Ssam printf("%s: passed policy\n", __func__); 966105197Ssam kdebug_sadb_x_policy((struct sadb_ext *)xpl)); 967105197Ssam 968186531Sbz /* Check policy type. */ 969188306Sbz /* ipsec_set_policy_internal() accepts IPSEC, ENTRUST and BYPASS. */ 970105197Ssam if (xpl->sadb_x_policy_type == IPSEC_POLICY_DISCARD 971105197Ssam || xpl->sadb_x_policy_type == IPSEC_POLICY_NONE) 972186531Sbz return (EINVAL); 973105197Ssam 974186531Sbz /* Check privileged socket. */ 975175892Sbz if (cred != NULL && xpl->sadb_x_policy_type == IPSEC_POLICY_BYPASS) { 976175892Sbz error = priv_check_cred(cred, PRIV_NETINET_IPSEC, 0); 977175892Sbz if (error) 978186531Sbz return (EACCES); 979175892Sbz } 980105197Ssam 981186531Sbz /* Allocating new SP entry. */ 982105197Ssam if ((newsp = key_msg2sp(xpl, len, &error)) == NULL) 983186531Sbz return (error); 984105197Ssam 985105197Ssam newsp->state = IPSEC_SPSTATE_ALIVE; 986105197Ssam 987186531Sbz /* Clear old SP and set new SP. */ 988105197Ssam KEY_FREESP(pcb_sp); 989105197Ssam *pcb_sp = newsp; 990105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 991120585Ssam printf("%s: new policy\n", __func__); 992105197Ssam kdebug_secpolicy(newsp)); 993105197Ssam 994186531Sbz return (0); 995105197Ssam} 996105197Ssam 997105197Ssamint 998188306Sbzipsec_set_policy(struct inpcb *inp, int optname, caddr_t request, 999186530Sbz size_t len, struct ucred *cred) 1000105197Ssam{ 1001105197Ssam struct sadb_x_policy *xpl; 1002105197Ssam struct secpolicy **pcb_sp; 1003105197Ssam 1004186531Sbz /* Sanity check. */ 1005105197Ssam if (inp == NULL || request == NULL) 1006186531Sbz return (EINVAL); 1007105197Ssam if (len < sizeof(*xpl)) 1008186531Sbz return (EINVAL); 1009105197Ssam xpl = (struct sadb_x_policy *)request; 1010105197Ssam 1011186531Sbz /* Select direction. */ 1012105197Ssam switch (xpl->sadb_x_policy_dir) { 1013105197Ssam case IPSEC_DIR_INBOUND: 1014105197Ssam pcb_sp = &inp->inp_sp->sp_in; 1015105197Ssam break; 1016105197Ssam case IPSEC_DIR_OUTBOUND: 1017105197Ssam pcb_sp = &inp->inp_sp->sp_out; 1018105197Ssam break; 1019105197Ssam default: 1020120585Ssam ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__, 1021105197Ssam xpl->sadb_x_policy_dir)); 1022186531Sbz return (EINVAL); 1023105197Ssam } 1024105197Ssam 1025188306Sbz return (ipsec_set_policy_internal(pcb_sp, optname, request, len, cred)); 1026105197Ssam} 1027105197Ssam 1028105197Ssamint 1029188306Sbzipsec_get_policy(struct inpcb *inp, caddr_t request, size_t len, 1030186530Sbz struct mbuf **mp) 1031105197Ssam{ 1032105197Ssam struct sadb_x_policy *xpl; 1033105197Ssam struct secpolicy *pcb_sp; 1034105197Ssam 1035186531Sbz /* Sanity check. */ 1036105197Ssam if (inp == NULL || request == NULL || mp == NULL) 1037186531Sbz return (EINVAL); 1038120585Ssam IPSEC_ASSERT(inp->inp_sp != NULL, ("null inp_sp")); 1039105197Ssam if (len < sizeof(*xpl)) 1040186531Sbz return (EINVAL); 1041105197Ssam xpl = (struct sadb_x_policy *)request; 1042105197Ssam 1043186531Sbz /* Select direction. */ 1044105197Ssam switch (xpl->sadb_x_policy_dir) { 1045105197Ssam case IPSEC_DIR_INBOUND: 1046105197Ssam pcb_sp = inp->inp_sp->sp_in; 1047105197Ssam break; 1048105197Ssam case IPSEC_DIR_OUTBOUND: 1049105197Ssam pcb_sp = inp->inp_sp->sp_out; 1050105197Ssam break; 1051105197Ssam default: 1052120585Ssam ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__, 1053105197Ssam xpl->sadb_x_policy_dir)); 1054186531Sbz return (EINVAL); 1055105197Ssam } 1056105197Ssam 1057188306Sbz /* Sanity check. Should be an IPSEC_ASSERT. */ 1058188306Sbz if (pcb_sp == NULL) 1059188306Sbz return (EINVAL); 1060188306Sbz 1061188306Sbz *mp = key_sp2msg(pcb_sp); 1062188306Sbz if (!*mp) { 1063188306Sbz ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); 1064188306Sbz return (ENOBUFS); 1065188306Sbz } 1066188306Sbz 1067188306Sbz (*mp)->m_type = MT_DATA; 1068188306Sbz KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 1069188306Sbz printf("%s:\n", __func__); kdebug_mbuf(*mp)); 1070188306Sbz 1071188306Sbz return (0); 1072105197Ssam} 1073105197Ssam 1074186531Sbz/* Delete policy in PCB. */ 1075105197Ssamint 1076186530Sbzipsec_delete_pcbpolicy(struct inpcb *inp) 1077105197Ssam{ 1078120585Ssam IPSEC_ASSERT(inp != NULL, ("null inp")); 1079105197Ssam 1080105197Ssam if (inp->inp_sp == NULL) 1081186531Sbz return (0); 1082105197Ssam 1083105197Ssam if (inp->inp_sp->sp_in != NULL) 1084105197Ssam KEY_FREESP(&inp->inp_sp->sp_in); 1085105197Ssam 1086105197Ssam if (inp->inp_sp->sp_out != NULL) 1087105197Ssam KEY_FREESP(&inp->inp_sp->sp_out); 1088105197Ssam 1089105197Ssam ipsec_delpcbpolicy(inp->inp_sp); 1090105197Ssam inp->inp_sp = NULL; 1091105197Ssam 1092186531Sbz return (0); 1093105197Ssam} 1094105197Ssam 1095105197Ssam/* 1096186531Sbz * Return current level. 1097105197Ssam * Either IPSEC_LEVEL_USE or IPSEC_LEVEL_REQUIRE are always returned. 1098105197Ssam */ 1099105197Ssamu_int 1100186530Sbzipsec_get_reqlevel(struct ipsecrequest *isr) 1101105197Ssam{ 1102105197Ssam u_int level = 0; 1103105197Ssam u_int esp_trans_deflev, esp_net_deflev; 1104105197Ssam u_int ah_trans_deflev, ah_net_deflev; 1105105197Ssam 1106120585Ssam IPSEC_ASSERT(isr != NULL && isr->sp != NULL, ("null argument")); 1107120585Ssam IPSEC_ASSERT(isr->sp->spidx.src.sa.sa_family == isr->sp->spidx.dst.sa.sa_family, 1108120585Ssam ("af family mismatch, src %u, dst %u", 1109105197Ssam isr->sp->spidx.src.sa.sa_family, 1110105197Ssam isr->sp->spidx.dst.sa.sa_family)); 1111105197Ssam 1112186531Sbz/* XXX Note that we have ipseclog() expanded here - code sync issue. */ 1113105197Ssam#define IPSEC_CHECK_DEFAULT(lev) \ 1114105197Ssam (((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE \ 1115105197Ssam && (lev) != IPSEC_LEVEL_UNIQUE) \ 1116181803Sbz ? (V_ipsec_debug \ 1117105197Ssam ? log(LOG_INFO, "fixed system default level " #lev ":%d->%d\n",\ 1118105197Ssam (lev), IPSEC_LEVEL_REQUIRE) \ 1119105197Ssam : 0), \ 1120105197Ssam (lev) = IPSEC_LEVEL_REQUIRE, \ 1121105197Ssam (lev) \ 1122105197Ssam : (lev)) 1123105197Ssam 1124186531Sbz /* Set default level. */ 1125105197Ssam switch (((struct sockaddr *)&isr->sp->spidx.src)->sa_family) { 1126105197Ssam#ifdef INET 1127105197Ssam case AF_INET: 1128181803Sbz esp_trans_deflev = IPSEC_CHECK_DEFAULT(V_ip4_esp_trans_deflev); 1129181803Sbz esp_net_deflev = IPSEC_CHECK_DEFAULT(V_ip4_esp_net_deflev); 1130181803Sbz ah_trans_deflev = IPSEC_CHECK_DEFAULT(V_ip4_ah_trans_deflev); 1131181803Sbz ah_net_deflev = IPSEC_CHECK_DEFAULT(V_ip4_ah_net_deflev); 1132105197Ssam break; 1133105197Ssam#endif 1134105197Ssam#ifdef INET6 1135105197Ssam case AF_INET6: 1136181803Sbz esp_trans_deflev = IPSEC_CHECK_DEFAULT(V_ip6_esp_trans_deflev); 1137181803Sbz esp_net_deflev = IPSEC_CHECK_DEFAULT(V_ip6_esp_net_deflev); 1138181803Sbz ah_trans_deflev = IPSEC_CHECK_DEFAULT(V_ip6_ah_trans_deflev); 1139181803Sbz ah_net_deflev = IPSEC_CHECK_DEFAULT(V_ip6_ah_net_deflev); 1140105197Ssam break; 1141105197Ssam#endif /* INET6 */ 1142105197Ssam default: 1143120585Ssam panic("%s: unknown af %u", 1144120585Ssam __func__, isr->sp->spidx.src.sa.sa_family); 1145105197Ssam } 1146105197Ssam 1147105197Ssam#undef IPSEC_CHECK_DEFAULT 1148105197Ssam 1149186531Sbz /* Set level. */ 1150105197Ssam switch (isr->level) { 1151105197Ssam case IPSEC_LEVEL_DEFAULT: 1152105197Ssam switch (isr->saidx.proto) { 1153105197Ssam case IPPROTO_ESP: 1154105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) 1155105197Ssam level = esp_net_deflev; 1156105197Ssam else 1157105197Ssam level = esp_trans_deflev; 1158105197Ssam break; 1159105197Ssam case IPPROTO_AH: 1160105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) 1161105197Ssam level = ah_net_deflev; 1162105197Ssam else 1163105197Ssam level = ah_trans_deflev; 1164125100Ssam break; 1165105197Ssam case IPPROTO_IPCOMP: 1166105197Ssam /* 1167186531Sbz * We don't really care, as IPcomp document says that 1168186531Sbz * we shouldn't compress small packets. 1169105197Ssam */ 1170105197Ssam level = IPSEC_LEVEL_USE; 1171105197Ssam break; 1172105197Ssam default: 1173120585Ssam panic("%s: Illegal protocol defined %u\n", __func__, 1174105197Ssam isr->saidx.proto); 1175105197Ssam } 1176105197Ssam break; 1177105197Ssam 1178105197Ssam case IPSEC_LEVEL_USE: 1179105197Ssam case IPSEC_LEVEL_REQUIRE: 1180105197Ssam level = isr->level; 1181105197Ssam break; 1182105197Ssam case IPSEC_LEVEL_UNIQUE: 1183105197Ssam level = IPSEC_LEVEL_REQUIRE; 1184105197Ssam break; 1185105197Ssam 1186105197Ssam default: 1187120585Ssam panic("%s: Illegal IPsec level %u\n", __func__, isr->level); 1188105197Ssam } 1189105197Ssam 1190186531Sbz return (level); 1191105197Ssam} 1192105197Ssam 1193105197Ssam/* 1194105197Ssam * Check security policy requirements against the actual 1195105197Ssam * packet contents. Return one if the packet should be 1196105197Ssam * reject as "invalid"; otherwiser return zero to have the 1197105197Ssam * packet treated as "valid". 1198105197Ssam * 1199105197Ssam * OUT: 1200105197Ssam * 0: valid 1201105197Ssam * 1: invalid 1202105197Ssam */ 1203105197Ssamint 1204105197Ssamipsec_in_reject(struct secpolicy *sp, struct mbuf *m) 1205105197Ssam{ 1206105197Ssam struct ipsecrequest *isr; 1207105197Ssam int need_auth; 1208105197Ssam 1209105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 1210120585Ssam printf("%s: using SP\n", __func__); kdebug_secpolicy(sp)); 1211105197Ssam 1212186531Sbz /* Check policy. */ 1213105197Ssam switch (sp->policy) { 1214105197Ssam case IPSEC_POLICY_DISCARD: 1215186531Sbz return (1); 1216105197Ssam case IPSEC_POLICY_BYPASS: 1217105197Ssam case IPSEC_POLICY_NONE: 1218186531Sbz return (0); 1219105197Ssam } 1220105197Ssam 1221120585Ssam IPSEC_ASSERT(sp->policy == IPSEC_POLICY_IPSEC, 1222120585Ssam ("invalid policy %u", sp->policy)); 1223105197Ssam 1224186531Sbz /* XXX Should compare policy against IPsec header history. */ 1225105197Ssam 1226105197Ssam need_auth = 0; 1227105197Ssam for (isr = sp->req; isr != NULL; isr = isr->next) { 1228105197Ssam if (ipsec_get_reqlevel(isr) != IPSEC_LEVEL_REQUIRE) 1229105197Ssam continue; 1230105197Ssam switch (isr->saidx.proto) { 1231105197Ssam case IPPROTO_ESP: 1232105197Ssam if ((m->m_flags & M_DECRYPTED) == 0) { 1233105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 1234120585Ssam printf("%s: ESP m_flags:%x\n", __func__, 1235105197Ssam m->m_flags)); 1236186531Sbz return (1); 1237105197Ssam } 1238105197Ssam 1239105197Ssam if (!need_auth && 1240105197Ssam isr->sav != NULL && 1241105197Ssam isr->sav->tdb_authalgxform != NULL && 1242105197Ssam (m->m_flags & M_AUTHIPDGM) == 0) { 1243105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 1244120585Ssam printf("%s: ESP/AH m_flags:%x\n", __func__, 1245105197Ssam m->m_flags)); 1246186531Sbz return (1); 1247105197Ssam } 1248105197Ssam break; 1249105197Ssam case IPPROTO_AH: 1250105197Ssam need_auth = 1; 1251105197Ssam if ((m->m_flags & M_AUTHIPHDR) == 0) { 1252105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 1253120585Ssam printf("%s: AH m_flags:%x\n", __func__, 1254105197Ssam m->m_flags)); 1255186531Sbz return (1); 1256105197Ssam } 1257105197Ssam break; 1258105197Ssam case IPPROTO_IPCOMP: 1259105197Ssam /* 1260186531Sbz * We don't really care, as IPcomp document 1261105197Ssam * says that we shouldn't compress small 1262186531Sbz * packets. IPComp policy should always be 1263105197Ssam * treated as being in "use" level. 1264105197Ssam */ 1265105197Ssam break; 1266105197Ssam } 1267105197Ssam } 1268186531Sbz return (0); /* Valid. */ 1269105197Ssam} 1270105197Ssam 1271188306Sbzstatic int 1272188306Sbzipsec46_in_reject(struct mbuf *m, struct inpcb *inp) 1273105197Ssam{ 1274105197Ssam struct secpolicy *sp; 1275105197Ssam int error; 1276105197Ssam int result; 1277105197Ssam 1278120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 1279105197Ssam 1280186531Sbz /* 1281186531Sbz * Get SP for this packet. 1282105197Ssam * When we are called from ip_forward(), we call 1283105197Ssam * ipsec_getpolicybyaddr() with IP_FORWARDING flag. 1284105197Ssam */ 1285105197Ssam if (inp == NULL) 1286105197Ssam sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND, IP_FORWARDING, &error); 1287105197Ssam else 1288105197Ssam sp = ipsec_getpolicybysock(m, IPSEC_DIR_INBOUND, inp, &error); 1289105197Ssam 1290105197Ssam if (sp != NULL) { 1291105197Ssam result = ipsec_in_reject(sp, m); 1292105197Ssam KEY_FREESP(&sp); 1293105197Ssam } else { 1294186531Sbz result = 0; /* XXX Should be panic? 1295105197Ssam * -> No, there may be error. */ 1296105197Ssam } 1297186531Sbz return (result); 1298105197Ssam} 1299105197Ssam 1300188306Sbz/* 1301188306Sbz * Check AH/ESP integrity. 1302188306Sbz * This function is called from tcp_input(), udp_input(), 1303188306Sbz * and {ah,esp}4_input for tunnel mode. 1304188306Sbz */ 1305188306Sbzint 1306188306Sbzipsec4_in_reject(struct mbuf *m, struct inpcb *inp) 1307188306Sbz{ 1308188306Sbz int result; 1309188306Sbz 1310188306Sbz result = ipsec46_in_reject(m, inp); 1311188306Sbz if (result) 1312188306Sbz V_ipsec4stat.ips_in_polvio++; 1313188306Sbz 1314188306Sbz return (result); 1315188306Sbz} 1316188306Sbz 1317105197Ssam#ifdef INET6 1318105197Ssam/* 1319105197Ssam * Check AH/ESP integrity. 1320105197Ssam * This function is called from tcp6_input(), udp6_input(), 1321186531Sbz * and {ah,esp}6_input for tunnel mode. 1322105197Ssam */ 1323105197Ssamint 1324186530Sbzipsec6_in_reject(struct mbuf *m, struct inpcb *inp) 1325105197Ssam{ 1326105197Ssam int result; 1327105197Ssam 1328188306Sbz result = ipsec46_in_reject(m, inp); 1329188306Sbz if (result) 1330188306Sbz V_ipsec6stat.ips_in_polvio++; 1331105197Ssam 1332186531Sbz return (result); 1333105197Ssam} 1334105197Ssam#endif 1335105197Ssam 1336105197Ssam/* 1337186531Sbz * Compute the byte size to be occupied by IPsec header. 1338186531Sbz * In case it is tunnelled, it includes the size of outer IP header. 1339186531Sbz * NOTE: SP passed is freed in this function. 1340105197Ssam */ 1341105197Ssamstatic size_t 1342188306Sbzipsec_hdrsiz_internal(struct secpolicy *sp) 1343105197Ssam{ 1344105197Ssam struct ipsecrequest *isr; 1345186532Sbz size_t size; 1346105197Ssam 1347105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 1348120585Ssam printf("%s: using SP\n", __func__); kdebug_secpolicy(sp)); 1349105197Ssam 1350105197Ssam switch (sp->policy) { 1351105197Ssam case IPSEC_POLICY_DISCARD: 1352105197Ssam case IPSEC_POLICY_BYPASS: 1353105197Ssam case IPSEC_POLICY_NONE: 1354186531Sbz return (0); 1355105197Ssam } 1356105197Ssam 1357120585Ssam IPSEC_ASSERT(sp->policy == IPSEC_POLICY_IPSEC, 1358120585Ssam ("invalid policy %u", sp->policy)); 1359105197Ssam 1360186532Sbz size = 0; 1361105197Ssam for (isr = sp->req; isr != NULL; isr = isr->next) { 1362105197Ssam size_t clen = 0; 1363105197Ssam 1364105197Ssam switch (isr->saidx.proto) { 1365105197Ssam case IPPROTO_ESP: 1366105197Ssam clen = esp_hdrsiz(isr->sav); 1367105197Ssam break; 1368105197Ssam case IPPROTO_AH: 1369105197Ssam clen = ah_hdrsiz(isr->sav); 1370105197Ssam break; 1371105197Ssam case IPPROTO_IPCOMP: 1372105197Ssam clen = sizeof(struct ipcomp); 1373105197Ssam break; 1374105197Ssam } 1375105197Ssam 1376105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { 1377105197Ssam switch (isr->saidx.dst.sa.sa_family) { 1378105197Ssam case AF_INET: 1379105197Ssam clen += sizeof(struct ip); 1380105197Ssam break; 1381105197Ssam#ifdef INET6 1382105197Ssam case AF_INET6: 1383105197Ssam clen += sizeof(struct ip6_hdr); 1384105197Ssam break; 1385105197Ssam#endif 1386105197Ssam default: 1387120585Ssam ipseclog((LOG_ERR, "%s: unknown AF %d in " 1388120585Ssam "IPsec tunnel SA\n", __func__, 1389105197Ssam ((struct sockaddr *)&isr->saidx.dst)->sa_family)); 1390105197Ssam break; 1391105197Ssam } 1392105197Ssam } 1393186532Sbz size += clen; 1394105197Ssam } 1395105197Ssam 1396186532Sbz return (size); 1397105197Ssam} 1398105197Ssam 1399188306Sbz/* 1400188306Sbz * This function is called from ipsec_hdrsiz_tcp(), ip_ipsec_mtu(), 1401188306Sbz * disabled ip6_ipsec_mtu() and ip6_forward(). 1402188306Sbz */ 1403105197Ssamsize_t 1404188306Sbzipsec_hdrsiz(struct mbuf *m, u_int dir, struct inpcb *inp) 1405105197Ssam{ 1406105197Ssam struct secpolicy *sp; 1407105197Ssam int error; 1408105197Ssam size_t size; 1409105197Ssam 1410120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 1411105197Ssam 1412186531Sbz /* Get SP for this packet. 1413105197Ssam * When we are called from ip_forward(), we call 1414105197Ssam * ipsec_getpolicybyaddr() with IP_FORWARDING flag. 1415105197Ssam */ 1416105197Ssam if (inp == NULL) 1417105197Ssam sp = ipsec_getpolicybyaddr(m, dir, IP_FORWARDING, &error); 1418105197Ssam else 1419105197Ssam sp = ipsec_getpolicybysock(m, dir, inp, &error); 1420105197Ssam 1421105197Ssam if (sp != NULL) { 1422188306Sbz size = ipsec_hdrsiz_internal(sp); 1423105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 1424120585Ssam printf("%s: size:%lu.\n", __func__, 1425105197Ssam (unsigned long)size)); 1426105197Ssam 1427105197Ssam KEY_FREESP(&sp); 1428105197Ssam } else { 1429186531Sbz size = 0; /* XXX Should be panic? 1430174038Sbz * -> No, we are called w/o knowing if 1431174038Sbz * IPsec processing is needed. */ 1432105197Ssam } 1433186531Sbz return (size); 1434105197Ssam} 1435105197Ssam 1436105197Ssam/* 1437105197Ssam * Check the variable replay window. 1438105197Ssam * ipsec_chkreplay() performs replay check before ICV verification. 1439105197Ssam * ipsec_updatereplay() updates replay bitmap. This must be called after 1440105197Ssam * ICV verification (it also performs replay check, which is usually done 1441105197Ssam * beforehand). 1442105197Ssam * 0 (zero) is returned if packet disallowed, 1 if packet permitted. 1443105197Ssam * 1444186531Sbz * Based on RFC 2401. 1445105197Ssam */ 1446105197Ssamint 1447186530Sbzipsec_chkreplay(u_int32_t seq, struct secasvar *sav) 1448105197Ssam{ 1449105197Ssam const struct secreplay *replay; 1450105197Ssam u_int32_t diff; 1451105197Ssam int fr; 1452186531Sbz u_int32_t wsizeb; /* Constant: bits of window size. */ 1453186531Sbz int frlast; /* Constant: last frame. */ 1454105197Ssam 1455120585Ssam IPSEC_ASSERT(sav != NULL, ("Null SA")); 1456120585Ssam IPSEC_ASSERT(sav->replay != NULL, ("Null replay state")); 1457105197Ssam 1458105197Ssam replay = sav->replay; 1459105197Ssam 1460105197Ssam if (replay->wsize == 0) 1461186531Sbz return (1); /* No need to check replay. */ 1462105197Ssam 1463186531Sbz /* Constant. */ 1464105197Ssam frlast = replay->wsize - 1; 1465105197Ssam wsizeb = replay->wsize << 3; 1466105197Ssam 1467186531Sbz /* Sequence number of 0 is invalid. */ 1468105197Ssam if (seq == 0) 1469186531Sbz return (0); 1470105197Ssam 1471186531Sbz /* First time is always okay. */ 1472105197Ssam if (replay->count == 0) 1473186531Sbz return (1); 1474105197Ssam 1475105197Ssam if (seq > replay->lastseq) { 1476186531Sbz /* Larger sequences are okay. */ 1477186531Sbz return (1); 1478105197Ssam } else { 1479105197Ssam /* seq is equal or less than lastseq. */ 1480105197Ssam diff = replay->lastseq - seq; 1481105197Ssam 1482186531Sbz /* Over range to check, i.e. too old or wrapped. */ 1483105197Ssam if (diff >= wsizeb) 1484186531Sbz return (0); 1485105197Ssam 1486105197Ssam fr = frlast - diff / 8; 1487105197Ssam 1488186531Sbz /* This packet already seen? */ 1489105197Ssam if ((replay->bitmap)[fr] & (1 << (diff % 8))) 1490186531Sbz return (0); 1491105197Ssam 1492186531Sbz /* Out of order but good. */ 1493186531Sbz return (1); 1494105197Ssam } 1495105197Ssam} 1496105197Ssam 1497105197Ssam/* 1498186531Sbz * Check replay counter whether to update or not. 1499105197Ssam * OUT: 0: OK 1500105197Ssam * 1: NG 1501105197Ssam */ 1502105197Ssamint 1503186530Sbzipsec_updatereplay(u_int32_t seq, struct secasvar *sav) 1504105197Ssam{ 1505105197Ssam struct secreplay *replay; 1506105197Ssam u_int32_t diff; 1507105197Ssam int fr; 1508186531Sbz u_int32_t wsizeb; /* Constant: bits of window size. */ 1509186531Sbz int frlast; /* Constant: last frame. */ 1510105197Ssam 1511120585Ssam IPSEC_ASSERT(sav != NULL, ("Null SA")); 1512120585Ssam IPSEC_ASSERT(sav->replay != NULL, ("Null replay state")); 1513105197Ssam 1514105197Ssam replay = sav->replay; 1515105197Ssam 1516105197Ssam if (replay->wsize == 0) 1517186531Sbz goto ok; /* No need to check replay. */ 1518105197Ssam 1519186531Sbz /* Constant. */ 1520105197Ssam frlast = replay->wsize - 1; 1521105197Ssam wsizeb = replay->wsize << 3; 1522105197Ssam 1523186531Sbz /* Sequence number of 0 is invalid. */ 1524105197Ssam if (seq == 0) 1525186531Sbz return (1); 1526105197Ssam 1527186531Sbz /* First time. */ 1528105197Ssam if (replay->count == 0) { 1529105197Ssam replay->lastseq = seq; 1530105197Ssam bzero(replay->bitmap, replay->wsize); 1531105197Ssam (replay->bitmap)[frlast] = 1; 1532105197Ssam goto ok; 1533105197Ssam } 1534105197Ssam 1535105197Ssam if (seq > replay->lastseq) { 1536105197Ssam /* seq is larger than lastseq. */ 1537105197Ssam diff = seq - replay->lastseq; 1538105197Ssam 1539186531Sbz /* New larger sequence number. */ 1540105197Ssam if (diff < wsizeb) { 1541186531Sbz /* In window. */ 1542186531Sbz /* Set bit for this packet. */ 1543105197Ssam vshiftl(replay->bitmap, diff, replay->wsize); 1544105197Ssam (replay->bitmap)[frlast] |= 1; 1545105197Ssam } else { 1546186531Sbz /* This packet has a "way larger". */ 1547105197Ssam bzero(replay->bitmap, replay->wsize); 1548105197Ssam (replay->bitmap)[frlast] = 1; 1549105197Ssam } 1550105197Ssam replay->lastseq = seq; 1551105197Ssam 1552186531Sbz /* Larger is good. */ 1553105197Ssam } else { 1554105197Ssam /* seq is equal or less than lastseq. */ 1555105197Ssam diff = replay->lastseq - seq; 1556105197Ssam 1557186531Sbz /* Over range to check, i.e. too old or wrapped. */ 1558105197Ssam if (diff >= wsizeb) 1559186531Sbz return (1); 1560105197Ssam 1561105197Ssam fr = frlast - diff / 8; 1562105197Ssam 1563186531Sbz /* This packet already seen? */ 1564105197Ssam if ((replay->bitmap)[fr] & (1 << (diff % 8))) 1565186531Sbz return (1); 1566105197Ssam 1567186531Sbz /* Mark as seen. */ 1568105197Ssam (replay->bitmap)[fr] |= (1 << (diff % 8)); 1569105197Ssam 1570186531Sbz /* Out of order but good. */ 1571105197Ssam } 1572105197Ssam 1573105197Ssamok: 1574105197Ssam if (replay->count == ~0) { 1575105197Ssam 1576186531Sbz /* Set overflow flag. */ 1577105197Ssam replay->overflow++; 1578105197Ssam 1579186531Sbz /* Don't increment, no more packets accepted. */ 1580105197Ssam if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) 1581186531Sbz return (1); 1582105197Ssam 1583120585Ssam ipseclog((LOG_WARNING, "%s: replay counter made %d cycle. %s\n", 1584120585Ssam __func__, replay->overflow, ipsec_logsastr(sav))); 1585105197Ssam } 1586105197Ssam 1587105197Ssam replay->count++; 1588105197Ssam 1589186531Sbz return (0); 1590105197Ssam} 1591105197Ssam 1592105197Ssam/* 1593186531Sbz * Shift variable length buffer to left. 1594105197Ssam * IN: bitmap: pointer to the buffer 1595105197Ssam * nbit: the number of to shift. 1596105197Ssam * wsize: buffer size (bytes). 1597105197Ssam */ 1598105197Ssamstatic void 1599186530Sbzvshiftl(unsigned char *bitmap, int nbit, int wsize) 1600105197Ssam{ 1601105197Ssam int s, j, i; 1602105197Ssam unsigned char over; 1603105197Ssam 1604105197Ssam for (j = 0; j < nbit; j += 8) { 1605105197Ssam s = (nbit - j < 8) ? (nbit - j): 8; 1606105197Ssam bitmap[0] <<= s; 1607105197Ssam for (i = 1; i < wsize; i++) { 1608105197Ssam over = (bitmap[i] >> (8 - s)); 1609105197Ssam bitmap[i] <<= s; 1610105197Ssam bitmap[i-1] |= over; 1611105197Ssam } 1612105197Ssam } 1613105197Ssam} 1614105197Ssam 1615193947Sbz#ifdef INET 1616105197Ssam/* Return a printable string for the IPv4 address. */ 1617105197Ssamstatic char * 1618105197Ssaminet_ntoa4(struct in_addr ina) 1619105197Ssam{ 1620105197Ssam static char buf[4][4 * sizeof "123" + 4]; 1621105197Ssam unsigned char *ucp = (unsigned char *) &ina; 1622105197Ssam static int i = 3; 1623105197Ssam 1624186531Sbz /* XXX-BZ Returns static buffer. */ 1625105197Ssam i = (i + 1) % 4; 1626105197Ssam sprintf(buf[i], "%d.%d.%d.%d", ucp[0] & 0xff, ucp[1] & 0xff, 1627105197Ssam ucp[2] & 0xff, ucp[3] & 0xff); 1628105197Ssam return (buf[i]); 1629105197Ssam} 1630193947Sbz#endif 1631105197Ssam 1632105197Ssam/* Return a printable string for the address. */ 1633105197Ssamchar * 1634105197Ssamipsec_address(union sockaddr_union* sa) 1635105197Ssam{ 1636165222Sbz#ifdef INET6 1637165118Sbz char ip6buf[INET6_ADDRSTRLEN]; 1638165118Sbz#endif 1639186531Sbz 1640105197Ssam switch (sa->sa.sa_family) { 1641159237Spjd#ifdef INET 1642105197Ssam case AF_INET: 1643186531Sbz return (inet_ntoa4(sa->sin.sin_addr)); 1644105197Ssam#endif /* INET */ 1645159237Spjd#ifdef INET6 1646105197Ssam case AF_INET6: 1647186531Sbz return (ip6_sprintf(ip6buf, &sa->sin6.sin6_addr)); 1648105197Ssam#endif /* INET6 */ 1649105197Ssam default: 1650186531Sbz return ("(unknown address family)"); 1651105197Ssam } 1652105197Ssam} 1653105197Ssam 1654105197Ssamconst char * 1655186530Sbzipsec_logsastr(struct secasvar *sav) 1656105197Ssam{ 1657105197Ssam static char buf[256]; 1658105197Ssam char *p; 1659105197Ssam struct secasindex *saidx = &sav->sah->saidx; 1660105197Ssam 1661120585Ssam IPSEC_ASSERT(saidx->src.sa.sa_family == saidx->dst.sa.sa_family, 1662120585Ssam ("address family mismatch")); 1663105197Ssam 1664105197Ssam p = buf; 1665105197Ssam snprintf(buf, sizeof(buf), "SA(SPI=%u ", (u_int32_t)ntohl(sav->spi)); 1666105197Ssam while (p && *p) 1667105197Ssam p++; 1668186531Sbz /* NB: only use ipsec_address on one address at a time. */ 1669105197Ssam snprintf(p, sizeof (buf) - (p - buf), "src=%s ", 1670105197Ssam ipsec_address(&saidx->src)); 1671105197Ssam while (p && *p) 1672105197Ssam p++; 1673105197Ssam snprintf(p, sizeof (buf) - (p - buf), "dst=%s)", 1674105197Ssam ipsec_address(&saidx->dst)); 1675105197Ssam 1676186531Sbz return (buf); 1677105197Ssam} 1678105197Ssam 1679105197Ssamvoid 1680186530Sbzipsec_dumpmbuf(struct mbuf *m) 1681105197Ssam{ 1682105197Ssam int totlen; 1683105197Ssam int i; 1684105197Ssam u_char *p; 1685105197Ssam 1686105197Ssam totlen = 0; 1687105197Ssam printf("---\n"); 1688105197Ssam while (m) { 1689105197Ssam p = mtod(m, u_char *); 1690105197Ssam for (i = 0; i < m->m_len; i++) { 1691105197Ssam printf("%02x ", p[i]); 1692105197Ssam totlen++; 1693105197Ssam if (totlen % 16 == 0) 1694105197Ssam printf("\n"); 1695105197Ssam } 1696105197Ssam m = m->m_next; 1697105197Ssam } 1698105197Ssam if (totlen % 16 != 0) 1699105197Ssam printf("\n"); 1700105197Ssam printf("---\n"); 1701105197Ssam} 1702105197Ssam 1703125100Ssamstatic void 1704195837Srwatsonipsec_init(const void *unused __unused) 1705125100Ssam{ 1706186531Sbz 1707181803Sbz SECPOLICY_LOCK_INIT(&V_ip4_def_policy); 1708186531Sbz V_ip4_def_policy.refcnt = 1; /* NB: disallow free. */ 1709125100Ssam} 1710195837SrwatsonVNET_SYSINIT(ipsec_init, SI_SUB_PROTO_DOMAININIT, SI_ORDER_ANY, ipsec_init, 1711195837Srwatson NULL); 1712193731Szec 1713125100Ssam 1714186531Sbz/* XXX This stuff doesn't belong here... */ 1715105197Ssam 1716105197Ssamstatic struct xformsw* xforms = NULL; 1717105197Ssam 1718105197Ssam/* 1719105197Ssam * Register a transform; typically at system startup. 1720105197Ssam */ 1721105197Ssamvoid 1722105197Ssamxform_register(struct xformsw* xsp) 1723105197Ssam{ 1724186531Sbz 1725105197Ssam xsp->xf_next = xforms; 1726105197Ssam xforms = xsp; 1727105197Ssam} 1728105197Ssam 1729105197Ssam/* 1730105197Ssam * Initialize transform support in an sav. 1731105197Ssam */ 1732105197Ssamint 1733105197Ssamxform_init(struct secasvar *sav, int xftype) 1734105197Ssam{ 1735105197Ssam struct xformsw *xsp; 1736105197Ssam 1737186531Sbz if (sav->tdb_xform != NULL) /* Previously initialized. */ 1738186531Sbz return (0); 1739105197Ssam for (xsp = xforms; xsp; xsp = xsp->xf_next) 1740105197Ssam if (xsp->xf_type == xftype) 1741186531Sbz return ((*xsp->xf_init)(sav, xsp)); 1742186531Sbz return (EINVAL); 1743105197Ssam} 1744