1105197Ssam/* $FreeBSD$ */ 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> 58195699Srwatson#include <net/vnet.h> 59105197Ssam 60105197Ssam#include <netinet/in.h> 61105197Ssam#include <netinet/in_systm.h> 62105197Ssam#include <netinet/ip.h> 63105197Ssam#include <netinet/ip_var.h> 64105197Ssam#include <netinet/in_var.h> 65105197Ssam#include <netinet/udp.h> 66105197Ssam#include <netinet/udp_var.h> 67105197Ssam#include <netinet/tcp.h> 68105197Ssam#include <netinet/udp.h> 69105197Ssam 70105197Ssam#include <netinet/ip6.h> 71105197Ssam#ifdef INET6 72105197Ssam#include <netinet6/ip6_var.h> 73105197Ssam#endif 74105197Ssam#include <netinet/in_pcb.h> 75105197Ssam#ifdef INET6 76105197Ssam#include <netinet/icmp6.h> 77105197Ssam#endif 78105197Ssam 79171133Sgnn#include <sys/types.h> 80105197Ssam#include <netipsec/ipsec.h> 81105197Ssam#ifdef INET6 82105197Ssam#include <netipsec/ipsec6.h> 83105197Ssam#endif 84105197Ssam#include <netipsec/ah_var.h> 85105197Ssam#include <netipsec/esp_var.h> 86105197Ssam#include <netipsec/ipcomp.h> /*XXX*/ 87105197Ssam#include <netipsec/ipcomp_var.h> 88105197Ssam 89105197Ssam#include <netipsec/key.h> 90105197Ssam#include <netipsec/keydb.h> 91105197Ssam#include <netipsec/key_debug.h> 92105197Ssam 93105197Ssam#include <netipsec/xform.h> 94105197Ssam 95105197Ssam#include <machine/in_cksum.h> 96105197Ssam 97167820Ssam#include <opencrypto/cryptodev.h> 98167820Ssam 99195699Srwatson#ifdef IPSEC_DEBUG 100195699SrwatsonVNET_DEFINE(int, ipsec_debug) = 1; 101195699Srwatson#else 102195699SrwatsonVNET_DEFINE(int, ipsec_debug) = 0; 103185895Szec#endif 104207369Sbz 105195699Srwatson/* NB: name changed so netstat doesn't use it. */ 106253088SaeVNET_PCPUSTAT_DEFINE(struct ipsecstat, ipsec4stat); 107253088SaeVNET_PCPUSTAT_SYSINIT(ipsec4stat); 108253088Sae 109253088Sae#ifdef VIMAGE 110253088SaeVNET_PCPUSTAT_SYSUNINIT(ipsec4stat); 111253088Sae#endif /* VIMAGE */ 112253088Sae 113195699SrwatsonVNET_DEFINE(int, ip4_ah_offsetmask) = 0; /* maybe IP_DF? */ 114195699Srwatson/* DF bit on encap. 0: clear 1: set 2: copy */ 115195699SrwatsonVNET_DEFINE(int, ip4_ipsec_dfbit) = 0; 116195699SrwatsonVNET_DEFINE(int, ip4_esp_trans_deflev) = IPSEC_LEVEL_USE; 117195699SrwatsonVNET_DEFINE(int, ip4_esp_net_deflev) = IPSEC_LEVEL_USE; 118195699SrwatsonVNET_DEFINE(int, ip4_ah_trans_deflev) = IPSEC_LEVEL_USE; 119195699SrwatsonVNET_DEFINE(int, ip4_ah_net_deflev) = IPSEC_LEVEL_USE; 120195699SrwatsonVNET_DEFINE(struct secpolicy, ip4_def_policy); 121195699Srwatson/* ECN ignore(-1)/forbidden(0)/allowed(1) */ 122195699SrwatsonVNET_DEFINE(int, ip4_ipsec_ecn) = 0; 123195699SrwatsonVNET_DEFINE(int, ip4_esp_randpad) = -1; 124185895Szec 125105197Ssam/* 126105197Ssam * Crypto support requirements: 127105197Ssam * 128105197Ssam * 1 require hardware support 129105197Ssam * -1 require software support 130105197Ssam * 0 take anything 131105197Ssam */ 132195699SrwatsonVNET_DEFINE(int, crypto_support) = CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE; 133105197Ssam 134214565SbzFEATURE(ipsec, "Internet Protocol Security (IPsec)"); 135214565Sbz#ifdef IPSEC_NAT_T 136214565SbzFEATURE(ipsec_natt, "UDP Encapsulation of IPsec ESP Packets ('NAT-T')"); 137214565Sbz#endif 138214565Sbz 139105197SsamSYSCTL_DECL(_net_inet_ipsec); 140105197Ssam 141105197Ssam/* net.inet.ipsec */ 142195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEF_POLICY, def_policy, 143195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_def_policy).policy, 0, 144183550Szec "IPsec default policy."); 145195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev, 146195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_esp_trans_deflev), 0, 147183550Szec "Default ESP transport mode level"); 148195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_NETLEV, esp_net_deflev, 149195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_esp_net_deflev), 0, 150183550Szec "Default ESP tunnel mode level."); 151195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_TRANSLEV, ah_trans_deflev, 152195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_ah_trans_deflev), 0, 153183550Szec "AH transfer mode default level."); 154195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev, 155195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_ah_net_deflev), 0, 156183550Szec "AH tunnel mode default level."); 157195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_AH_CLEARTOS, ah_cleartos, 158195699Srwatson CTLFLAG_RW, &VNET_NAME(ah_cleartos), 0, 159183550Szec "If set clear type-of-service field when doing AH computation."); 160195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_AH_OFFSETMASK, ah_offsetmask, 161195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_ah_offsetmask), 0, 162183550Szec "If not set clear offset field mask when doing AH computation."); 163195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DFBIT, dfbit, 164195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_ipsec_dfbit), 0, 165183550Szec "Do not fragment bit on encap."); 166195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_ECN, ecn, 167195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_ipsec_ecn), 0, 168183550Szec "Explicit Congestion Notification handling."); 169195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEBUG, debug, 170195699Srwatson CTLFLAG_RW, &VNET_NAME(ipsec_debug), 0, 171183550Szec "Enable IPsec debugging output when set."); 172195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, OID_AUTO, crypto_support, 173195699Srwatson CTLFLAG_RW, &VNET_NAME(crypto_support), 0, 174183550Szec "Crypto driver selection."); 175253088SaeSYSCTL_VNET_PCPUSTAT(_net_inet_ipsec, OID_AUTO, ipsecstats, struct ipsecstat, 176253088Sae ipsec4stat, "IPsec IPv4 statistics."); 177105197Ssam 178157634Spjd#ifdef REGRESSION 179157613Spjd/* 180157613Spjd * When set to 1, IPsec will send packets with the same sequence number. 181157613Spjd * This allows to verify if the other side has proper replay attacks detection. 182157613Spjd */ 183195699SrwatsonVNET_DEFINE(int, ipsec_replay) = 0; 184195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, OID_AUTO, test_replay, 185195699Srwatson CTLFLAG_RW, &VNET_NAME(ipsec_replay), 0, 186195699Srwatson "Emulate replay attack"); 187157613Spjd/* 188157613Spjd * When set 1, IPsec will send packets with corrupted HMAC. 189157613Spjd * This allows to verify if the other side properly detects modified packets. 190157613Spjd */ 191195699SrwatsonVNET_DEFINE(int, ipsec_integrity) = 0; 192195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, OID_AUTO, test_integrity, 193195699Srwatson CTLFLAG_RW, &VNET_NAME(ipsec_integrity), 0, 194195699Srwatson "Emulate man-in-the-middle attack"); 195157634Spjd#endif 196157613Spjd 197171133Sgnn#ifdef INET6 198253088SaeVNET_PCPUSTAT_DEFINE(struct ipsecstat, ipsec6stat); 199253088SaeVNET_PCPUSTAT_SYSINIT(ipsec6stat); 200253088Sae 201253088Sae#ifdef VIMAGE 202253088SaeVNET_PCPUSTAT_SYSUNINIT(ipsec6stat); 203253088Sae#endif /* VIMAGE */ 204253088Sae 205195699SrwatsonVNET_DEFINE(int, ip6_esp_trans_deflev) = IPSEC_LEVEL_USE; 206195699SrwatsonVNET_DEFINE(int, ip6_esp_net_deflev) = IPSEC_LEVEL_USE; 207195699SrwatsonVNET_DEFINE(int, ip6_ah_trans_deflev) = IPSEC_LEVEL_USE; 208195699SrwatsonVNET_DEFINE(int, ip6_ah_net_deflev) = IPSEC_LEVEL_USE; 209195699SrwatsonVNET_DEFINE(int, ip6_ipsec_ecn) = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */ 210105197Ssam 211105197SsamSYSCTL_DECL(_net_inet6_ipsec6); 212105197Ssam 213105197Ssam/* net.inet6.ipsec6 */ 214195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEF_POLICY, def_policy, CTLFLAG_RW, 215195699Srwatson &VNET_NAME(ip4_def_policy).policy, 0, 216183550Szec "IPsec default policy."); 217195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_TRANSLEV, 218195699Srwatson esp_trans_deflev, CTLFLAG_RW, &VNET_NAME(ip6_esp_trans_deflev), 0, 219183550Szec "Default ESP transport mode level."); 220195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_NETLEV, 221195699Srwatson esp_net_deflev, CTLFLAG_RW, &VNET_NAME(ip6_esp_net_deflev), 0, 222183550Szec "Default ESP tunnel mode level."); 223195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_TRANSLEV, 224195699Srwatson ah_trans_deflev, CTLFLAG_RW, &VNET_NAME(ip6_ah_trans_deflev), 0, 225183550Szec "AH transfer mode default level."); 226195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_NETLEV, 227195699Srwatson ah_net_deflev, CTLFLAG_RW, &VNET_NAME(ip6_ah_net_deflev), 0, 228183550Szec "AH tunnel mode default level."); 229195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_ECN, 230195699Srwatson ecn, CTLFLAG_RW, &VNET_NAME(ip6_ipsec_ecn), 0, 231183550Szec "Explicit Congestion Notification handling."); 232195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEBUG, debug, CTLFLAG_RW, 233195699Srwatson &VNET_NAME(ipsec_debug), 0, 234183550Szec "Enable IPsec debugging output when set."); 235253088SaeSYSCTL_VNET_PCPUSTAT(_net_inet6_ipsec6, IPSECCTL_STATS, ipsecstats, 236253088Sae struct ipsecstat, ipsec6stat, "IPsec IPv6 statistics."); 237105197Ssam#endif /* INET6 */ 238105197Ssam 239283902Saestatic int ipsec_setspidx_inpcb(struct mbuf *, struct inpcb *); 240283902Saestatic int ipsec_setspidx(struct mbuf *, struct secpolicyindex *, int); 241283902Saestatic void ipsec4_get_ulp(struct mbuf *m, struct secpolicyindex *, int); 242283902Saestatic int ipsec4_setspidx_ipaddr(struct mbuf *, struct secpolicyindex *); 243105197Ssam#ifdef INET6 244283902Saestatic void ipsec6_get_ulp(struct mbuf *m, struct secpolicyindex *, int); 245283902Saestatic int ipsec6_setspidx_ipaddr(struct mbuf *, struct secpolicyindex *); 246105197Ssam#endif 247283902Saestatic void ipsec_delpcbpolicy(struct inpcbpolicy *); 248283902Saestatic struct secpolicy *ipsec_deepcopy_policy(struct secpolicy *src); 249283902Saestatic void vshiftl(unsigned char *, int, int); 250105197Ssam 251119643SsamMALLOC_DEFINE(M_IPSEC_INPCB, "inpcbpolicy", "inpcb-resident ipsec policy"); 252119643Ssam 253105197Ssam/* 254105197Ssam * Return a held reference to the default SP. 255105197Ssam */ 256105197Ssamstatic struct secpolicy * 257105197Ssamkey_allocsp_default(const char* where, int tag) 258105197Ssam{ 259105197Ssam struct secpolicy *sp; 260105197Ssam 261105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 262105197Ssam printf("DP key_allocsp_default from %s:%u\n", where, tag)); 263105197Ssam 264181803Sbz sp = &V_ip4_def_policy; 265105197Ssam if (sp->policy != IPSEC_POLICY_DISCARD && 266105197Ssam sp->policy != IPSEC_POLICY_NONE) { 267105197Ssam ipseclog((LOG_INFO, "fixed system default policy: %d->%d\n", 268105197Ssam sp->policy, IPSEC_POLICY_NONE)); 269105197Ssam sp->policy = IPSEC_POLICY_NONE; 270105197Ssam } 271135947Ssam key_addref(sp); 272105197Ssam 273105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 274105197Ssam printf("DP key_allocsp_default returns SP:%p (%u)\n", 275105197Ssam sp, sp->refcnt)); 276186531Sbz return (sp); 277105197Ssam} 278105197Ssam#define KEY_ALLOCSP_DEFAULT() \ 279105197Ssam key_allocsp_default(__FILE__, __LINE__) 280105197Ssam 281105197Ssam/* 282105197Ssam * For OUTBOUND packet having a socket. Searching SPD for packet, 283105197Ssam * and return a pointer to SP. 284105197Ssam * OUT: NULL: no apropreate SP found, the following value is set to error. 285105197Ssam * 0 : bypass 286105197Ssam * EACCES : discard packet. 287105197Ssam * ENOENT : ipsec_acquire() in progress, maybe. 288105197Ssam * others : error occured. 289105197Ssam * others: a pointer to SP 290105197Ssam * 291105197Ssam * NOTE: IPv6 mapped adddress concern is implemented here. 292105197Ssam */ 293105197Ssamstruct secpolicy * 294105197Ssamipsec_getpolicy(struct tdb_ident *tdbi, u_int dir) 295105197Ssam{ 296105197Ssam struct secpolicy *sp; 297105197Ssam 298120585Ssam IPSEC_ASSERT(tdbi != NULL, ("null tdbi")); 299120585Ssam IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND, 300120585Ssam ("invalid direction %u", dir)); 301105197Ssam 302105197Ssam sp = KEY_ALLOCSP2(tdbi->spi, &tdbi->dst, tdbi->proto, dir); 303105197Ssam if (sp == NULL) /*XXX????*/ 304105197Ssam sp = KEY_ALLOCSP_DEFAULT(); 305120585Ssam IPSEC_ASSERT(sp != NULL, ("null SP")); 306186531Sbz return (sp); 307105197Ssam} 308105197Ssam 309105197Ssam/* 310105197Ssam * For OUTBOUND packet having a socket. Searching SPD for packet, 311105197Ssam * and return a pointer to SP. 312105197Ssam * OUT: NULL: no apropreate SP found, the following value is set to error. 313105197Ssam * 0 : bypass 314105197Ssam * EACCES : discard packet. 315105197Ssam * ENOENT : ipsec_acquire() in progress, maybe. 316105197Ssam * others : error occured. 317105197Ssam * others: a pointer to SP 318105197Ssam * 319105197Ssam * NOTE: IPv6 mapped adddress concern is implemented here. 320105197Ssam */ 321186508Sbzstatic struct secpolicy * 322186508Sbzipsec_getpolicybysock(struct mbuf *m, u_int dir, struct inpcb *inp, int *error) 323105197Ssam{ 324188306Sbz struct inpcbpolicy *pcbsp; 325186531Sbz struct secpolicy *currsp = NULL; /* Policy on socket. */ 326105197Ssam struct secpolicy *sp; 327105197Ssam 328120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 329120585Ssam IPSEC_ASSERT(inp != NULL, ("null inpcb")); 330120585Ssam IPSEC_ASSERT(error != NULL, ("null error")); 331120585Ssam IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND, 332120585Ssam ("invalid direction %u", dir)); 333105197Ssam 334186531Sbz /* Set spidx in pcb. */ 335188306Sbz *error = ipsec_setspidx_inpcb(m, inp); 336105197Ssam if (*error) 337186531Sbz return (NULL); 338105197Ssam 339188306Sbz pcbsp = inp->inp_sp; 340120585Ssam IPSEC_ASSERT(pcbsp != NULL, ("null pcbsp")); 341105197Ssam switch (dir) { 342105197Ssam case IPSEC_DIR_INBOUND: 343105197Ssam currsp = pcbsp->sp_in; 344105197Ssam break; 345105197Ssam case IPSEC_DIR_OUTBOUND: 346105197Ssam currsp = pcbsp->sp_out; 347105197Ssam break; 348105197Ssam } 349120585Ssam IPSEC_ASSERT(currsp != NULL, ("null currsp")); 350105197Ssam 351186531Sbz if (pcbsp->priv) { /* When privilieged socket. */ 352105197Ssam switch (currsp->policy) { 353105197Ssam case IPSEC_POLICY_BYPASS: 354105197Ssam case IPSEC_POLICY_IPSEC: 355135947Ssam key_addref(currsp); 356105197Ssam sp = currsp; 357105197Ssam break; 358105197Ssam 359105197Ssam case IPSEC_POLICY_ENTRUST: 360186531Sbz /* Look for a policy in SPD. */ 361105197Ssam sp = KEY_ALLOCSP(&currsp->spidx, dir); 362186531Sbz if (sp == NULL) /* No SP found. */ 363105197Ssam sp = KEY_ALLOCSP_DEFAULT(); 364105197Ssam break; 365105197Ssam 366105197Ssam default: 367120585Ssam ipseclog((LOG_ERR, "%s: Invalid policy for PCB %d\n", 368120585Ssam __func__, currsp->policy)); 369105197Ssam *error = EINVAL; 370186531Sbz return (NULL); 371105197Ssam } 372186531Sbz } else { /* Unpriv, SPD has policy. */ 373105197Ssam sp = KEY_ALLOCSP(&currsp->spidx, dir); 374186531Sbz if (sp == NULL) { /* No SP found. */ 375105197Ssam switch (currsp->policy) { 376105197Ssam case IPSEC_POLICY_BYPASS: 377120585Ssam ipseclog((LOG_ERR, "%s: Illegal policy for " 378120585Ssam "non-priviliged defined %d\n", 379120585Ssam __func__, currsp->policy)); 380105197Ssam *error = EINVAL; 381186531Sbz return (NULL); 382105197Ssam 383105197Ssam case IPSEC_POLICY_ENTRUST: 384105197Ssam sp = KEY_ALLOCSP_DEFAULT(); 385105197Ssam break; 386105197Ssam 387105197Ssam case IPSEC_POLICY_IPSEC: 388135947Ssam key_addref(currsp); 389105197Ssam sp = currsp; 390105197Ssam break; 391105197Ssam 392105197Ssam default: 393120585Ssam ipseclog((LOG_ERR, "%s: Invalid policy for " 394120585Ssam "PCB %d\n", __func__, currsp->policy)); 395105197Ssam *error = EINVAL; 396186531Sbz return (NULL); 397105197Ssam } 398105197Ssam } 399105197Ssam } 400120585Ssam IPSEC_ASSERT(sp != NULL, 401120585Ssam ("null SP (priv %u policy %u", pcbsp->priv, currsp->policy)); 402105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 403120585Ssam printf("DP %s (priv %u policy %u) allocate SP:%p (refcnt %u)\n", 404120585Ssam __func__, pcbsp->priv, currsp->policy, sp, sp->refcnt)); 405186531Sbz return (sp); 406105197Ssam} 407105197Ssam 408105197Ssam/* 409105197Ssam * For FORWADING packet or OUTBOUND without a socket. Searching SPD for packet, 410105197Ssam * and return a pointer to SP. 411105197Ssam * OUT: positive: a pointer to the entry for security policy leaf matched. 412105197Ssam * NULL: no apropreate SP found, the following value is set to error. 413105197Ssam * 0 : bypass 414105197Ssam * EACCES : discard packet. 415105197Ssam * ENOENT : ipsec_acquire() in progress, maybe. 416105197Ssam * others : error occured. 417105197Ssam */ 418105197Ssamstruct secpolicy * 419186530Sbzipsec_getpolicybyaddr(struct mbuf *m, u_int dir, int flag, int *error) 420105197Ssam{ 421105197Ssam struct secpolicyindex spidx; 422105197Ssam struct secpolicy *sp; 423105197Ssam 424120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 425120585Ssam IPSEC_ASSERT(error != NULL, ("null error")); 426120585Ssam IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND, 427120585Ssam ("invalid direction %u", dir)); 428105197Ssam 429105197Ssam sp = NULL; 430105197Ssam if (key_havesp(dir)) { 431108533Sschweikh /* Make an index to look for a policy. */ 432105197Ssam *error = ipsec_setspidx(m, &spidx, 433105197Ssam (flag & IP_FORWARDING) ? 0 : 1); 434105197Ssam if (*error != 0) { 435120585Ssam DPRINTF(("%s: setpidx failed, dir %u flag %u\n", 436120585Ssam __func__, dir, flag)); 437186531Sbz return (NULL); 438105197Ssam } 439105197Ssam spidx.dir = dir; 440105197Ssam 441105197Ssam sp = KEY_ALLOCSP(&spidx, dir); 442105197Ssam } 443186531Sbz if (sp == NULL) /* No SP found, use system default. */ 444105197Ssam sp = KEY_ALLOCSP_DEFAULT(); 445120585Ssam IPSEC_ASSERT(sp != NULL, ("null SP")); 446186531Sbz return (sp); 447105197Ssam} 448105197Ssam 449105197Ssamstruct secpolicy * 450186530Sbzipsec4_checkpolicy(struct mbuf *m, u_int dir, u_int flag, int *error, 451186530Sbz struct inpcb *inp) 452105197Ssam{ 453105197Ssam struct secpolicy *sp; 454105197Ssam 455105197Ssam *error = 0; 456105197Ssam if (inp == NULL) 457105197Ssam sp = ipsec_getpolicybyaddr(m, dir, flag, error); 458105197Ssam else 459105197Ssam sp = ipsec_getpolicybysock(m, dir, inp, error); 460105197Ssam if (sp == NULL) { 461120585Ssam IPSEC_ASSERT(*error != 0, ("getpolicy failed w/o error")); 462252026Sae IPSECSTAT_INC(ips_out_inval); 463186531Sbz return (NULL); 464105197Ssam } 465120585Ssam IPSEC_ASSERT(*error == 0, ("sp w/ error set to %u", *error)); 466105197Ssam switch (sp->policy) { 467105197Ssam case IPSEC_POLICY_ENTRUST: 468105197Ssam default: 469120585Ssam printf("%s: invalid policy %u\n", __func__, sp->policy); 470186531Sbz /* FALLTHROUGH */ 471105197Ssam case IPSEC_POLICY_DISCARD: 472252026Sae IPSECSTAT_INC(ips_out_polvio); 473186531Sbz *error = -EINVAL; /* Packet is discarded by caller. */ 474105197Ssam break; 475105197Ssam case IPSEC_POLICY_BYPASS: 476105197Ssam case IPSEC_POLICY_NONE: 477105197Ssam KEY_FREESP(&sp); 478186531Sbz sp = NULL; /* NB: force NULL result. */ 479105197Ssam break; 480105197Ssam case IPSEC_POLICY_IPSEC: 481186531Sbz if (sp->req == NULL) /* Acquire a SA. */ 482105197Ssam *error = key_spdacquire(sp); 483105197Ssam break; 484105197Ssam } 485105197Ssam if (*error != 0) { 486105197Ssam KEY_FREESP(&sp); 487105197Ssam sp = NULL; 488105197Ssam } 489186531Sbz return (sp); 490105197Ssam} 491105197Ssam 492105197Ssamstatic int 493188306Sbzipsec_setspidx_inpcb(struct mbuf *m, struct inpcb *inp) 494105197Ssam{ 495105197Ssam int error; 496105197Ssam 497186532Sbz IPSEC_ASSERT(inp != NULL, ("null inp")); 498186532Sbz IPSEC_ASSERT(inp->inp_sp != NULL, ("null inp_sp")); 499186532Sbz IPSEC_ASSERT(inp->inp_sp->sp_out != NULL && inp->inp_sp->sp_in != NULL, 500120585Ssam ("null sp_in || sp_out")); 501105197Ssam 502186532Sbz error = ipsec_setspidx(m, &inp->inp_sp->sp_in->spidx, 1); 503105197Ssam if (error == 0) { 504186532Sbz inp->inp_sp->sp_in->spidx.dir = IPSEC_DIR_INBOUND; 505186532Sbz inp->inp_sp->sp_out->spidx = inp->inp_sp->sp_in->spidx; 506186532Sbz inp->inp_sp->sp_out->spidx.dir = IPSEC_DIR_OUTBOUND; 507105197Ssam } else { 508186532Sbz bzero(&inp->inp_sp->sp_in->spidx, 509186532Sbz sizeof (inp->inp_sp->sp_in->spidx)); 510186532Sbz bzero(&inp->inp_sp->sp_out->spidx, 511186532Sbz sizeof (inp->inp_sp->sp_in->spidx)); 512105197Ssam } 513186531Sbz return (error); 514105197Ssam} 515105197Ssam 516105197Ssam/* 517186531Sbz * Configure security policy index (src/dst/proto/sport/dport) 518105197Ssam * by looking at the content of mbuf. 519186531Sbz * The caller is responsible for error recovery (like clearing up spidx). 520105197Ssam */ 521105197Ssamstatic int 522186530Sbzipsec_setspidx(struct mbuf *m, struct secpolicyindex *spidx, int needport) 523105197Ssam{ 524105197Ssam struct ip *ip = NULL; 525105197Ssam struct ip ipbuf; 526105197Ssam u_int v; 527105197Ssam struct mbuf *n; 528105197Ssam int len; 529105197Ssam int error; 530105197Ssam 531120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 532105197Ssam 533105197Ssam /* 534186531Sbz * Validate m->m_pkthdr.len. We see incorrect length if we 535105197Ssam * mistakenly call this function with inconsistent mbuf chain 536186531Sbz * (like 4.4BSD tcp/udp processing). XXX Should we panic here? 537105197Ssam */ 538105197Ssam len = 0; 539105197Ssam for (n = m; n; n = n->m_next) 540105197Ssam len += n->m_len; 541105197Ssam if (m->m_pkthdr.len != len) { 542105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 543120585Ssam printf("%s: pkthdr len(%d) mismatch (%d), ignored.\n", 544120585Ssam __func__, len, m->m_pkthdr.len)); 545186531Sbz return (EINVAL); 546105197Ssam } 547105197Ssam 548105197Ssam if (m->m_pkthdr.len < sizeof(struct ip)) { 549105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 550120585Ssam printf("%s: pkthdr len(%d) too small (v4), ignored.\n", 551120585Ssam __func__, m->m_pkthdr.len)); 552186531Sbz return (EINVAL); 553105197Ssam } 554105197Ssam 555105197Ssam if (m->m_len >= sizeof(*ip)) 556105197Ssam ip = mtod(m, struct ip *); 557105197Ssam else { 558105197Ssam m_copydata(m, 0, sizeof(ipbuf), (caddr_t)&ipbuf); 559105197Ssam ip = &ipbuf; 560105197Ssam } 561105197Ssam v = ip->ip_v; 562105197Ssam switch (v) { 563105197Ssam case 4: 564105197Ssam error = ipsec4_setspidx_ipaddr(m, spidx); 565105197Ssam if (error) 566186531Sbz return (error); 567105197Ssam ipsec4_get_ulp(m, spidx, needport); 568186531Sbz return (0); 569105197Ssam#ifdef INET6 570105197Ssam case 6: 571105197Ssam if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) { 572105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 573120585Ssam printf("%s: pkthdr len(%d) too small (v6), " 574120585Ssam "ignored\n", __func__, m->m_pkthdr.len)); 575186531Sbz return (EINVAL); 576105197Ssam } 577105197Ssam error = ipsec6_setspidx_ipaddr(m, spidx); 578105197Ssam if (error) 579186531Sbz return (error); 580105197Ssam ipsec6_get_ulp(m, spidx, needport); 581186531Sbz return (0); 582105197Ssam#endif 583105197Ssam default: 584105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 585120585Ssam printf("%s: " "unknown IP version %u, ignored.\n", 586120585Ssam __func__, v)); 587186531Sbz return (EINVAL); 588105197Ssam } 589105197Ssam} 590105197Ssam 591105197Ssamstatic void 592105197Ssamipsec4_get_ulp(struct mbuf *m, struct secpolicyindex *spidx, int needport) 593105197Ssam{ 594105197Ssam u_int8_t nxt; 595105197Ssam int off; 596105197Ssam 597186531Sbz /* Sanity check. */ 598120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 599120585Ssam IPSEC_ASSERT(m->m_pkthdr.len >= sizeof(struct ip),("packet too short")); 600105197Ssam 601206111Seri if (m->m_len >= sizeof (struct ip)) { 602105197Ssam struct ip *ip = mtod(m, struct ip *); 603241913Sglebius if (ip->ip_off & htons(IP_MF | IP_OFFMASK)) 604105197Ssam goto done; 605105197Ssam off = ip->ip_hl << 2; 606105197Ssam nxt = ip->ip_p; 607105197Ssam } else { 608105197Ssam struct ip ih; 609105197Ssam 610105197Ssam m_copydata(m, 0, sizeof (struct ip), (caddr_t) &ih); 611241913Sglebius if (ih.ip_off & htons(IP_MF | IP_OFFMASK)) 612105197Ssam goto done; 613105197Ssam off = ih.ip_hl << 2; 614105197Ssam nxt = ih.ip_p; 615105197Ssam } 616105197Ssam 617105197Ssam while (off < m->m_pkthdr.len) { 618105197Ssam struct ip6_ext ip6e; 619105197Ssam struct tcphdr th; 620105197Ssam struct udphdr uh; 621105197Ssam 622105197Ssam switch (nxt) { 623105197Ssam case IPPROTO_TCP: 624105197Ssam spidx->ul_proto = nxt; 625105197Ssam if (!needport) 626105197Ssam goto done_proto; 627105197Ssam if (off + sizeof(struct tcphdr) > m->m_pkthdr.len) 628105197Ssam goto done; 629105197Ssam m_copydata(m, off, sizeof (th), (caddr_t) &th); 630105197Ssam spidx->src.sin.sin_port = th.th_sport; 631105197Ssam spidx->dst.sin.sin_port = th.th_dport; 632105197Ssam return; 633105197Ssam case IPPROTO_UDP: 634105197Ssam spidx->ul_proto = nxt; 635105197Ssam if (!needport) 636105197Ssam goto done_proto; 637105197Ssam if (off + sizeof(struct udphdr) > m->m_pkthdr.len) 638105197Ssam goto done; 639105197Ssam m_copydata(m, off, sizeof (uh), (caddr_t) &uh); 640105197Ssam spidx->src.sin.sin_port = uh.uh_sport; 641105197Ssam spidx->dst.sin.sin_port = uh.uh_dport; 642105197Ssam return; 643105197Ssam case IPPROTO_AH: 644143323Ssam if (off + sizeof(ip6e) > m->m_pkthdr.len) 645105197Ssam goto done; 646186531Sbz /* XXX Sigh, this works but is totally bogus. */ 647105197Ssam m_copydata(m, off, sizeof(ip6e), (caddr_t) &ip6e); 648105197Ssam off += (ip6e.ip6e_len + 2) << 2; 649105197Ssam nxt = ip6e.ip6e_nxt; 650105197Ssam break; 651105197Ssam case IPPROTO_ICMP: 652105197Ssam default: 653186531Sbz /* XXX Intermediate headers??? */ 654105197Ssam spidx->ul_proto = nxt; 655105197Ssam goto done_proto; 656105197Ssam } 657105197Ssam } 658105197Ssamdone: 659105197Ssam spidx->ul_proto = IPSEC_ULPROTO_ANY; 660105197Ssamdone_proto: 661105197Ssam spidx->src.sin.sin_port = IPSEC_PORT_ANY; 662105197Ssam spidx->dst.sin.sin_port = IPSEC_PORT_ANY; 663105197Ssam} 664105197Ssam 665186531Sbz/* Assumes that m is sane. */ 666105197Ssamstatic int 667105197Ssamipsec4_setspidx_ipaddr(struct mbuf *m, struct secpolicyindex *spidx) 668105197Ssam{ 669105197Ssam static const struct sockaddr_in template = { 670105197Ssam sizeof (struct sockaddr_in), 671105197Ssam AF_INET, 672105197Ssam 0, { 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 } 673105197Ssam }; 674105197Ssam 675105197Ssam spidx->src.sin = template; 676105197Ssam spidx->dst.sin = template; 677105197Ssam 678105197Ssam if (m->m_len < sizeof (struct ip)) { 679105197Ssam m_copydata(m, offsetof(struct ip, ip_src), 680105197Ssam sizeof (struct in_addr), 681105197Ssam (caddr_t) &spidx->src.sin.sin_addr); 682105197Ssam m_copydata(m, offsetof(struct ip, ip_dst), 683105197Ssam sizeof (struct in_addr), 684105197Ssam (caddr_t) &spidx->dst.sin.sin_addr); 685105197Ssam } else { 686105197Ssam struct ip *ip = mtod(m, struct ip *); 687105197Ssam spidx->src.sin.sin_addr = ip->ip_src; 688105197Ssam spidx->dst.sin.sin_addr = ip->ip_dst; 689105197Ssam } 690105197Ssam 691105197Ssam spidx->prefs = sizeof(struct in_addr) << 3; 692105197Ssam spidx->prefd = sizeof(struct in_addr) << 3; 693105197Ssam 694186531Sbz return (0); 695105197Ssam} 696105197Ssam 697105197Ssam#ifdef INET6 698105197Ssamstatic void 699186530Sbzipsec6_get_ulp(struct mbuf *m, struct secpolicyindex *spidx, int needport) 700105197Ssam{ 701105197Ssam int off, nxt; 702105197Ssam struct tcphdr th; 703105197Ssam struct udphdr uh; 704170121Sbz struct icmp6_hdr ih; 705105197Ssam 706186531Sbz /* Sanity check. */ 707105197Ssam if (m == NULL) 708120585Ssam panic("%s: NULL pointer was passed.\n", __func__); 709105197Ssam 710105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 711120585Ssam printf("%s:\n", __func__); kdebug_mbuf(m)); 712105197Ssam 713186531Sbz /* Set default. */ 714105197Ssam spidx->ul_proto = IPSEC_ULPROTO_ANY; 715105197Ssam ((struct sockaddr_in6 *)&spidx->src)->sin6_port = IPSEC_PORT_ANY; 716105197Ssam ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = IPSEC_PORT_ANY; 717105197Ssam 718105197Ssam nxt = -1; 719105197Ssam off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt); 720105197Ssam if (off < 0 || m->m_pkthdr.len < off) 721105197Ssam return; 722105197Ssam 723105197Ssam switch (nxt) { 724105197Ssam case IPPROTO_TCP: 725105197Ssam spidx->ul_proto = nxt; 726105197Ssam if (!needport) 727105197Ssam break; 728105197Ssam if (off + sizeof(struct tcphdr) > m->m_pkthdr.len) 729105197Ssam break; 730105197Ssam m_copydata(m, off, sizeof(th), (caddr_t)&th); 731105197Ssam ((struct sockaddr_in6 *)&spidx->src)->sin6_port = th.th_sport; 732105197Ssam ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = th.th_dport; 733105197Ssam break; 734105197Ssam case IPPROTO_UDP: 735105197Ssam spidx->ul_proto = nxt; 736105197Ssam if (!needport) 737105197Ssam break; 738105197Ssam if (off + sizeof(struct udphdr) > m->m_pkthdr.len) 739105197Ssam break; 740105197Ssam m_copydata(m, off, sizeof(uh), (caddr_t)&uh); 741105197Ssam ((struct sockaddr_in6 *)&spidx->src)->sin6_port = uh.uh_sport; 742105197Ssam ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = uh.uh_dport; 743105197Ssam break; 744105197Ssam case IPPROTO_ICMPV6: 745170121Sbz spidx->ul_proto = nxt; 746170121Sbz if (off + sizeof(struct icmp6_hdr) > m->m_pkthdr.len) 747170121Sbz break; 748170121Sbz m_copydata(m, off, sizeof(ih), (caddr_t)&ih); 749170121Sbz ((struct sockaddr_in6 *)&spidx->src)->sin6_port = 750170121Sbz htons((uint16_t)ih.icmp6_type); 751170121Sbz ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = 752170121Sbz htons((uint16_t)ih.icmp6_code); 753170121Sbz break; 754105197Ssam default: 755186531Sbz /* XXX Intermediate headers??? */ 756105197Ssam spidx->ul_proto = nxt; 757105197Ssam break; 758105197Ssam } 759105197Ssam} 760105197Ssam 761186531Sbz/* Assumes that m is sane. */ 762105197Ssamstatic int 763186530Sbzipsec6_setspidx_ipaddr(struct mbuf *m, struct secpolicyindex *spidx) 764105197Ssam{ 765105197Ssam struct ip6_hdr *ip6 = NULL; 766105197Ssam struct ip6_hdr ip6buf; 767105197Ssam struct sockaddr_in6 *sin6; 768105197Ssam 769105197Ssam if (m->m_len >= sizeof(*ip6)) 770105197Ssam ip6 = mtod(m, struct ip6_hdr *); 771105197Ssam else { 772105197Ssam m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf); 773105197Ssam ip6 = &ip6buf; 774105197Ssam } 775105197Ssam 776105197Ssam sin6 = (struct sockaddr_in6 *)&spidx->src; 777105197Ssam bzero(sin6, sizeof(*sin6)); 778105197Ssam sin6->sin6_family = AF_INET6; 779105197Ssam sin6->sin6_len = sizeof(struct sockaddr_in6); 780105197Ssam bcopy(&ip6->ip6_src, &sin6->sin6_addr, sizeof(ip6->ip6_src)); 781105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { 782105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 783105197Ssam sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]); 784105197Ssam } 785105197Ssam spidx->prefs = sizeof(struct in6_addr) << 3; 786105197Ssam 787105197Ssam sin6 = (struct sockaddr_in6 *)&spidx->dst; 788105197Ssam bzero(sin6, sizeof(*sin6)); 789105197Ssam sin6->sin6_family = AF_INET6; 790105197Ssam sin6->sin6_len = sizeof(struct sockaddr_in6); 791105197Ssam bcopy(&ip6->ip6_dst, &sin6->sin6_addr, sizeof(ip6->ip6_dst)); 792105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { 793105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 794105197Ssam sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]); 795105197Ssam } 796105197Ssam spidx->prefd = sizeof(struct in6_addr) << 3; 797105197Ssam 798186531Sbz return (0); 799105197Ssam} 800105197Ssam#endif 801105197Ssam 802105197Ssamstatic void 803186530Sbzipsec_delpcbpolicy(struct inpcbpolicy *p) 804105197Ssam{ 805186531Sbz 806119643Ssam free(p, M_IPSEC_INPCB); 807105197Ssam} 808105197Ssam 809186531Sbz/* Initialize policy in PCB. */ 810105197Ssamint 811186530Sbzipsec_init_policy(struct socket *so, struct inpcbpolicy **pcb_sp) 812105197Ssam{ 813105197Ssam struct inpcbpolicy *new; 814105197Ssam 815186531Sbz /* Sanity check. */ 816105197Ssam if (so == NULL || pcb_sp == NULL) 817120585Ssam panic("%s: NULL pointer was passed.\n", __func__); 818105197Ssam 819105197Ssam new = (struct inpcbpolicy *) malloc(sizeof(struct inpcbpolicy), 820119643Ssam M_IPSEC_INPCB, M_NOWAIT|M_ZERO); 821105197Ssam if (new == NULL) { 822120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); 823186531Sbz return (ENOBUFS); 824105197Ssam } 825105197Ssam 826120585Ssam new->priv = IPSEC_IS_PRIVILEGED_SO(so); 827105197Ssam 828105197Ssam if ((new->sp_in = KEY_NEWSP()) == NULL) { 829105197Ssam ipsec_delpcbpolicy(new); 830186531Sbz return (ENOBUFS); 831105197Ssam } 832105197Ssam new->sp_in->state = IPSEC_SPSTATE_ALIVE; 833105197Ssam new->sp_in->policy = IPSEC_POLICY_ENTRUST; 834105197Ssam 835105197Ssam if ((new->sp_out = KEY_NEWSP()) == NULL) { 836105197Ssam KEY_FREESP(&new->sp_in); 837105197Ssam ipsec_delpcbpolicy(new); 838186531Sbz return (ENOBUFS); 839105197Ssam } 840105197Ssam new->sp_out->state = IPSEC_SPSTATE_ALIVE; 841105197Ssam new->sp_out->policy = IPSEC_POLICY_ENTRUST; 842105197Ssam 843105197Ssam *pcb_sp = new; 844105197Ssam 845186531Sbz return (0); 846105197Ssam} 847105197Ssam 848186531Sbz/* Copy old IPsec policy into new. */ 849105197Ssamint 850186530Sbzipsec_copy_policy(struct inpcbpolicy *old, struct inpcbpolicy *new) 851105197Ssam{ 852105197Ssam struct secpolicy *sp; 853105197Ssam 854105197Ssam sp = ipsec_deepcopy_policy(old->sp_in); 855105197Ssam if (sp) { 856105197Ssam KEY_FREESP(&new->sp_in); 857105197Ssam new->sp_in = sp; 858105197Ssam } else 859186531Sbz return (ENOBUFS); 860105197Ssam 861105197Ssam sp = ipsec_deepcopy_policy(old->sp_out); 862105197Ssam if (sp) { 863105197Ssam KEY_FREESP(&new->sp_out); 864105197Ssam new->sp_out = sp; 865105197Ssam } else 866186531Sbz return (ENOBUFS); 867105197Ssam 868105197Ssam new->priv = old->priv; 869105197Ssam 870186531Sbz return (0); 871105197Ssam} 872105197Ssam 873119643Ssamstruct ipsecrequest * 874119643Ssamipsec_newisr(void) 875119643Ssam{ 876119643Ssam struct ipsecrequest *p; 877119643Ssam 878119643Ssam p = malloc(sizeof(struct ipsecrequest), M_IPSEC_SR, M_NOWAIT|M_ZERO); 879119643Ssam if (p != NULL) 880120585Ssam IPSECREQUEST_LOCK_INIT(p); 881186531Sbz return (p); 882119643Ssam} 883119643Ssam 884119643Ssamvoid 885119643Ssamipsec_delisr(struct ipsecrequest *p) 886119643Ssam{ 887186531Sbz 888120585Ssam IPSECREQUEST_LOCK_DESTROY(p); 889119643Ssam free(p, M_IPSEC_SR); 890119643Ssam} 891119643Ssam 892186531Sbz/* Deep-copy a policy in PCB. */ 893105197Ssamstatic struct secpolicy * 894186530Sbzipsec_deepcopy_policy(struct secpolicy *src) 895105197Ssam{ 896105197Ssam struct ipsecrequest *newchain = NULL; 897105197Ssam struct ipsecrequest *p; 898105197Ssam struct ipsecrequest **q; 899105197Ssam struct ipsecrequest *r; 900105197Ssam struct secpolicy *dst; 901105197Ssam 902105197Ssam if (src == NULL) 903186531Sbz return (NULL); 904105197Ssam dst = KEY_NEWSP(); 905105197Ssam if (dst == NULL) 906186531Sbz return (NULL); 907105197Ssam 908105197Ssam /* 909186531Sbz * Deep-copy IPsec request chain. This is required since struct 910105197Ssam * ipsecrequest is not reference counted. 911105197Ssam */ 912105197Ssam q = &newchain; 913105197Ssam for (p = src->req; p; p = p->next) { 914119643Ssam *q = ipsec_newisr(); 915105197Ssam if (*q == NULL) 916105197Ssam goto fail; 917105197Ssam (*q)->saidx.proto = p->saidx.proto; 918105197Ssam (*q)->saidx.mode = p->saidx.mode; 919105197Ssam (*q)->level = p->level; 920105197Ssam (*q)->saidx.reqid = p->saidx.reqid; 921105197Ssam 922105197Ssam bcopy(&p->saidx.src, &(*q)->saidx.src, sizeof((*q)->saidx.src)); 923105197Ssam bcopy(&p->saidx.dst, &(*q)->saidx.dst, sizeof((*q)->saidx.dst)); 924105197Ssam 925105197Ssam (*q)->sp = dst; 926105197Ssam 927105197Ssam q = &((*q)->next); 928105197Ssam } 929105197Ssam 930105197Ssam dst->req = newchain; 931105197Ssam dst->state = src->state; 932105197Ssam dst->policy = src->policy; 933186531Sbz /* Do not touch the refcnt fields. */ 934105197Ssam 935186531Sbz return (dst); 936105197Ssam 937105197Ssamfail: 938105197Ssam for (p = newchain; p; p = r) { 939105197Ssam r = p->next; 940119643Ssam ipsec_delisr(p); 941105197Ssam p = NULL; 942105197Ssam } 943299627Sngie KEY_FREESP(&dst); 944186531Sbz return (NULL); 945105197Ssam} 946105197Ssam 947186531Sbz/* Set policy and IPsec request if present. */ 948105197Ssamstatic int 949188306Sbzipsec_set_policy_internal(struct secpolicy **pcb_sp, int optname, 950188306Sbz caddr_t request, size_t len, struct ucred *cred) 951105197Ssam{ 952105197Ssam struct sadb_x_policy *xpl; 953105197Ssam struct secpolicy *newsp = NULL; 954105197Ssam int error; 955105197Ssam 956186531Sbz /* Sanity check. */ 957105197Ssam if (pcb_sp == NULL || *pcb_sp == NULL || request == NULL) 958186531Sbz return (EINVAL); 959105197Ssam if (len < sizeof(*xpl)) 960186531Sbz return (EINVAL); 961105197Ssam xpl = (struct sadb_x_policy *)request; 962105197Ssam 963105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 964120585Ssam printf("%s: passed policy\n", __func__); 965105197Ssam kdebug_sadb_x_policy((struct sadb_ext *)xpl)); 966105197Ssam 967186531Sbz /* Check policy type. */ 968188306Sbz /* ipsec_set_policy_internal() accepts IPSEC, ENTRUST and BYPASS. */ 969105197Ssam if (xpl->sadb_x_policy_type == IPSEC_POLICY_DISCARD 970105197Ssam || xpl->sadb_x_policy_type == IPSEC_POLICY_NONE) 971186531Sbz return (EINVAL); 972105197Ssam 973186531Sbz /* Check privileged socket. */ 974175892Sbz if (cred != NULL && xpl->sadb_x_policy_type == IPSEC_POLICY_BYPASS) { 975175892Sbz error = priv_check_cred(cred, PRIV_NETINET_IPSEC, 0); 976175892Sbz if (error) 977186531Sbz return (EACCES); 978175892Sbz } 979105197Ssam 980186531Sbz /* Allocating new SP entry. */ 981105197Ssam if ((newsp = key_msg2sp(xpl, len, &error)) == NULL) 982186531Sbz return (error); 983105197Ssam 984105197Ssam newsp->state = IPSEC_SPSTATE_ALIVE; 985105197Ssam 986186531Sbz /* Clear old SP and set new SP. */ 987105197Ssam KEY_FREESP(pcb_sp); 988105197Ssam *pcb_sp = newsp; 989105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 990120585Ssam printf("%s: new policy\n", __func__); 991105197Ssam kdebug_secpolicy(newsp)); 992105197Ssam 993186531Sbz return (0); 994105197Ssam} 995105197Ssam 996105197Ssamint 997188306Sbzipsec_set_policy(struct inpcb *inp, int optname, caddr_t request, 998186530Sbz size_t len, struct ucred *cred) 999105197Ssam{ 1000105197Ssam struct sadb_x_policy *xpl; 1001105197Ssam struct secpolicy **pcb_sp; 1002105197Ssam 1003186531Sbz /* Sanity check. */ 1004105197Ssam if (inp == NULL || request == NULL) 1005186531Sbz return (EINVAL); 1006105197Ssam if (len < sizeof(*xpl)) 1007186531Sbz return (EINVAL); 1008105197Ssam xpl = (struct sadb_x_policy *)request; 1009105197Ssam 1010186531Sbz /* Select direction. */ 1011105197Ssam switch (xpl->sadb_x_policy_dir) { 1012105197Ssam case IPSEC_DIR_INBOUND: 1013105197Ssam pcb_sp = &inp->inp_sp->sp_in; 1014105197Ssam break; 1015105197Ssam case IPSEC_DIR_OUTBOUND: 1016105197Ssam pcb_sp = &inp->inp_sp->sp_out; 1017105197Ssam break; 1018105197Ssam default: 1019120585Ssam ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__, 1020105197Ssam xpl->sadb_x_policy_dir)); 1021186531Sbz return (EINVAL); 1022105197Ssam } 1023105197Ssam 1024188306Sbz return (ipsec_set_policy_internal(pcb_sp, optname, request, len, cred)); 1025105197Ssam} 1026105197Ssam 1027105197Ssamint 1028188306Sbzipsec_get_policy(struct inpcb *inp, caddr_t request, size_t len, 1029186530Sbz struct mbuf **mp) 1030105197Ssam{ 1031105197Ssam struct sadb_x_policy *xpl; 1032105197Ssam struct secpolicy *pcb_sp; 1033105197Ssam 1034186531Sbz /* Sanity check. */ 1035105197Ssam if (inp == NULL || request == NULL || mp == NULL) 1036186531Sbz return (EINVAL); 1037120585Ssam IPSEC_ASSERT(inp->inp_sp != NULL, ("null inp_sp")); 1038105197Ssam if (len < sizeof(*xpl)) 1039186531Sbz return (EINVAL); 1040105197Ssam xpl = (struct sadb_x_policy *)request; 1041105197Ssam 1042186531Sbz /* Select direction. */ 1043105197Ssam switch (xpl->sadb_x_policy_dir) { 1044105197Ssam case IPSEC_DIR_INBOUND: 1045105197Ssam pcb_sp = inp->inp_sp->sp_in; 1046105197Ssam break; 1047105197Ssam case IPSEC_DIR_OUTBOUND: 1048105197Ssam pcb_sp = inp->inp_sp->sp_out; 1049105197Ssam break; 1050105197Ssam default: 1051120585Ssam ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__, 1052105197Ssam xpl->sadb_x_policy_dir)); 1053186531Sbz return (EINVAL); 1054105197Ssam } 1055105197Ssam 1056188306Sbz /* Sanity check. Should be an IPSEC_ASSERT. */ 1057188306Sbz if (pcb_sp == NULL) 1058188306Sbz return (EINVAL); 1059188306Sbz 1060188306Sbz *mp = key_sp2msg(pcb_sp); 1061188306Sbz if (!*mp) { 1062188306Sbz ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); 1063188306Sbz return (ENOBUFS); 1064188306Sbz } 1065188306Sbz 1066188306Sbz (*mp)->m_type = MT_DATA; 1067188306Sbz KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 1068188306Sbz printf("%s:\n", __func__); kdebug_mbuf(*mp)); 1069188306Sbz 1070188306Sbz return (0); 1071105197Ssam} 1072105197Ssam 1073186531Sbz/* Delete policy in PCB. */ 1074105197Ssamint 1075186530Sbzipsec_delete_pcbpolicy(struct inpcb *inp) 1076105197Ssam{ 1077120585Ssam IPSEC_ASSERT(inp != NULL, ("null inp")); 1078105197Ssam 1079105197Ssam if (inp->inp_sp == NULL) 1080186531Sbz return (0); 1081105197Ssam 1082105197Ssam if (inp->inp_sp->sp_in != NULL) 1083105197Ssam KEY_FREESP(&inp->inp_sp->sp_in); 1084105197Ssam 1085105197Ssam if (inp->inp_sp->sp_out != NULL) 1086105197Ssam KEY_FREESP(&inp->inp_sp->sp_out); 1087105197Ssam 1088105197Ssam ipsec_delpcbpolicy(inp->inp_sp); 1089105197Ssam inp->inp_sp = NULL; 1090105197Ssam 1091186531Sbz return (0); 1092105197Ssam} 1093105197Ssam 1094105197Ssam/* 1095186531Sbz * Return current level. 1096105197Ssam * Either IPSEC_LEVEL_USE or IPSEC_LEVEL_REQUIRE are always returned. 1097105197Ssam */ 1098105197Ssamu_int 1099186530Sbzipsec_get_reqlevel(struct ipsecrequest *isr) 1100105197Ssam{ 1101105197Ssam u_int level = 0; 1102105197Ssam u_int esp_trans_deflev, esp_net_deflev; 1103105197Ssam u_int ah_trans_deflev, ah_net_deflev; 1104105197Ssam 1105120585Ssam IPSEC_ASSERT(isr != NULL && isr->sp != NULL, ("null argument")); 1106120585Ssam IPSEC_ASSERT(isr->sp->spidx.src.sa.sa_family == isr->sp->spidx.dst.sa.sa_family, 1107120585Ssam ("af family mismatch, src %u, dst %u", 1108105197Ssam isr->sp->spidx.src.sa.sa_family, 1109105197Ssam isr->sp->spidx.dst.sa.sa_family)); 1110105197Ssam 1111186531Sbz/* XXX Note that we have ipseclog() expanded here - code sync issue. */ 1112105197Ssam#define IPSEC_CHECK_DEFAULT(lev) \ 1113105197Ssam (((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE \ 1114105197Ssam && (lev) != IPSEC_LEVEL_UNIQUE) \ 1115181803Sbz ? (V_ipsec_debug \ 1116105197Ssam ? log(LOG_INFO, "fixed system default level " #lev ":%d->%d\n",\ 1117105197Ssam (lev), IPSEC_LEVEL_REQUIRE) \ 1118105197Ssam : 0), \ 1119105197Ssam (lev) = IPSEC_LEVEL_REQUIRE, \ 1120105197Ssam (lev) \ 1121105197Ssam : (lev)) 1122105197Ssam 1123186531Sbz /* Set default level. */ 1124105197Ssam switch (((struct sockaddr *)&isr->sp->spidx.src)->sa_family) { 1125105197Ssam#ifdef INET 1126105197Ssam case AF_INET: 1127181803Sbz esp_trans_deflev = IPSEC_CHECK_DEFAULT(V_ip4_esp_trans_deflev); 1128181803Sbz esp_net_deflev = IPSEC_CHECK_DEFAULT(V_ip4_esp_net_deflev); 1129181803Sbz ah_trans_deflev = IPSEC_CHECK_DEFAULT(V_ip4_ah_trans_deflev); 1130181803Sbz ah_net_deflev = IPSEC_CHECK_DEFAULT(V_ip4_ah_net_deflev); 1131105197Ssam break; 1132105197Ssam#endif 1133105197Ssam#ifdef INET6 1134105197Ssam case AF_INET6: 1135181803Sbz esp_trans_deflev = IPSEC_CHECK_DEFAULT(V_ip6_esp_trans_deflev); 1136181803Sbz esp_net_deflev = IPSEC_CHECK_DEFAULT(V_ip6_esp_net_deflev); 1137181803Sbz ah_trans_deflev = IPSEC_CHECK_DEFAULT(V_ip6_ah_trans_deflev); 1138181803Sbz ah_net_deflev = IPSEC_CHECK_DEFAULT(V_ip6_ah_net_deflev); 1139105197Ssam break; 1140105197Ssam#endif /* INET6 */ 1141105197Ssam default: 1142120585Ssam panic("%s: unknown af %u", 1143120585Ssam __func__, isr->sp->spidx.src.sa.sa_family); 1144105197Ssam } 1145105197Ssam 1146105197Ssam#undef IPSEC_CHECK_DEFAULT 1147105197Ssam 1148186531Sbz /* Set level. */ 1149105197Ssam switch (isr->level) { 1150105197Ssam case IPSEC_LEVEL_DEFAULT: 1151105197Ssam switch (isr->saidx.proto) { 1152105197Ssam case IPPROTO_ESP: 1153105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) 1154105197Ssam level = esp_net_deflev; 1155105197Ssam else 1156105197Ssam level = esp_trans_deflev; 1157105197Ssam break; 1158105197Ssam case IPPROTO_AH: 1159105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) 1160105197Ssam level = ah_net_deflev; 1161105197Ssam else 1162105197Ssam level = ah_trans_deflev; 1163125100Ssam break; 1164105197Ssam case IPPROTO_IPCOMP: 1165105197Ssam /* 1166186531Sbz * We don't really care, as IPcomp document says that 1167186531Sbz * we shouldn't compress small packets. 1168105197Ssam */ 1169105197Ssam level = IPSEC_LEVEL_USE; 1170105197Ssam break; 1171105197Ssam default: 1172120585Ssam panic("%s: Illegal protocol defined %u\n", __func__, 1173105197Ssam isr->saidx.proto); 1174105197Ssam } 1175105197Ssam break; 1176105197Ssam 1177105197Ssam case IPSEC_LEVEL_USE: 1178105197Ssam case IPSEC_LEVEL_REQUIRE: 1179105197Ssam level = isr->level; 1180105197Ssam break; 1181105197Ssam case IPSEC_LEVEL_UNIQUE: 1182105197Ssam level = IPSEC_LEVEL_REQUIRE; 1183105197Ssam break; 1184105197Ssam 1185105197Ssam default: 1186120585Ssam panic("%s: Illegal IPsec level %u\n", __func__, isr->level); 1187105197Ssam } 1188105197Ssam 1189186531Sbz return (level); 1190105197Ssam} 1191105197Ssam 1192105197Ssam/* 1193105197Ssam * Check security policy requirements against the actual 1194105197Ssam * packet contents. Return one if the packet should be 1195105197Ssam * reject as "invalid"; otherwiser return zero to have the 1196105197Ssam * packet treated as "valid". 1197105197Ssam * 1198105197Ssam * OUT: 1199105197Ssam * 0: valid 1200105197Ssam * 1: invalid 1201105197Ssam */ 1202105197Ssamint 1203105197Ssamipsec_in_reject(struct secpolicy *sp, struct mbuf *m) 1204105197Ssam{ 1205105197Ssam struct ipsecrequest *isr; 1206105197Ssam int need_auth; 1207105197Ssam 1208105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 1209120585Ssam printf("%s: using SP\n", __func__); kdebug_secpolicy(sp)); 1210105197Ssam 1211186531Sbz /* Check policy. */ 1212105197Ssam switch (sp->policy) { 1213105197Ssam case IPSEC_POLICY_DISCARD: 1214186531Sbz return (1); 1215105197Ssam case IPSEC_POLICY_BYPASS: 1216105197Ssam case IPSEC_POLICY_NONE: 1217186531Sbz return (0); 1218105197Ssam } 1219105197Ssam 1220120585Ssam IPSEC_ASSERT(sp->policy == IPSEC_POLICY_IPSEC, 1221120585Ssam ("invalid policy %u", sp->policy)); 1222105197Ssam 1223186531Sbz /* XXX Should compare policy against IPsec header history. */ 1224105197Ssam 1225105197Ssam need_auth = 0; 1226105197Ssam for (isr = sp->req; isr != NULL; isr = isr->next) { 1227105197Ssam if (ipsec_get_reqlevel(isr) != IPSEC_LEVEL_REQUIRE) 1228105197Ssam continue; 1229105197Ssam switch (isr->saidx.proto) { 1230105197Ssam case IPPROTO_ESP: 1231105197Ssam if ((m->m_flags & M_DECRYPTED) == 0) { 1232105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 1233120585Ssam printf("%s: ESP m_flags:%x\n", __func__, 1234105197Ssam m->m_flags)); 1235186531Sbz return (1); 1236105197Ssam } 1237105197Ssam 1238105197Ssam if (!need_auth && 1239105197Ssam isr->sav != NULL && 1240105197Ssam isr->sav->tdb_authalgxform != NULL && 1241105197Ssam (m->m_flags & M_AUTHIPDGM) == 0) { 1242105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 1243120585Ssam printf("%s: ESP/AH m_flags:%x\n", __func__, 1244105197Ssam m->m_flags)); 1245186531Sbz return (1); 1246105197Ssam } 1247105197Ssam break; 1248105197Ssam case IPPROTO_AH: 1249105197Ssam need_auth = 1; 1250105197Ssam if ((m->m_flags & M_AUTHIPHDR) == 0) { 1251105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 1252120585Ssam printf("%s: AH m_flags:%x\n", __func__, 1253105197Ssam m->m_flags)); 1254186531Sbz return (1); 1255105197Ssam } 1256105197Ssam break; 1257105197Ssam case IPPROTO_IPCOMP: 1258105197Ssam /* 1259186531Sbz * We don't really care, as IPcomp document 1260105197Ssam * says that we shouldn't compress small 1261186531Sbz * packets. IPComp policy should always be 1262105197Ssam * treated as being in "use" level. 1263105197Ssam */ 1264105197Ssam break; 1265105197Ssam } 1266105197Ssam } 1267186531Sbz return (0); /* Valid. */ 1268105197Ssam} 1269105197Ssam 1270188306Sbzstatic int 1271188306Sbzipsec46_in_reject(struct mbuf *m, struct inpcb *inp) 1272105197Ssam{ 1273105197Ssam struct secpolicy *sp; 1274105197Ssam int error; 1275105197Ssam int result; 1276105197Ssam 1277291355Sgnn if (!key_havesp(IPSEC_DIR_INBOUND)) 1278291355Sgnn return 0; 1279291355Sgnn 1280120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 1281105197Ssam 1282186531Sbz /* 1283186531Sbz * Get SP for this packet. 1284105197Ssam * When we are called from ip_forward(), we call 1285105197Ssam * ipsec_getpolicybyaddr() with IP_FORWARDING flag. 1286105197Ssam */ 1287105197Ssam if (inp == NULL) 1288105197Ssam sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND, IP_FORWARDING, &error); 1289105197Ssam else 1290105197Ssam sp = ipsec_getpolicybysock(m, IPSEC_DIR_INBOUND, inp, &error); 1291105197Ssam 1292105197Ssam if (sp != NULL) { 1293105197Ssam result = ipsec_in_reject(sp, m); 1294105197Ssam KEY_FREESP(&sp); 1295105197Ssam } else { 1296186531Sbz result = 0; /* XXX Should be panic? 1297105197Ssam * -> No, there may be error. */ 1298105197Ssam } 1299186531Sbz return (result); 1300105197Ssam} 1301105197Ssam 1302188306Sbz/* 1303188306Sbz * Check AH/ESP integrity. 1304188306Sbz * This function is called from tcp_input(), udp_input(), 1305188306Sbz * and {ah,esp}4_input for tunnel mode. 1306188306Sbz */ 1307188306Sbzint 1308188306Sbzipsec4_in_reject(struct mbuf *m, struct inpcb *inp) 1309188306Sbz{ 1310188306Sbz int result; 1311188306Sbz 1312188306Sbz result = ipsec46_in_reject(m, inp); 1313188306Sbz if (result) 1314252026Sae IPSECSTAT_INC(ips_in_polvio); 1315188306Sbz 1316188306Sbz return (result); 1317188306Sbz} 1318188306Sbz 1319105197Ssam#ifdef INET6 1320105197Ssam/* 1321105197Ssam * Check AH/ESP integrity. 1322105197Ssam * This function is called from tcp6_input(), udp6_input(), 1323186531Sbz * and {ah,esp}6_input for tunnel mode. 1324105197Ssam */ 1325105197Ssamint 1326186530Sbzipsec6_in_reject(struct mbuf *m, struct inpcb *inp) 1327105197Ssam{ 1328105197Ssam int result; 1329105197Ssam 1330188306Sbz result = ipsec46_in_reject(m, inp); 1331188306Sbz if (result) 1332252026Sae IPSEC6STAT_INC(ips_in_polvio); 1333105197Ssam 1334186531Sbz return (result); 1335105197Ssam} 1336105197Ssam#endif 1337105197Ssam 1338105197Ssam/* 1339186531Sbz * Compute the byte size to be occupied by IPsec header. 1340186531Sbz * In case it is tunnelled, it includes the size of outer IP header. 1341186531Sbz * NOTE: SP passed is freed in this function. 1342105197Ssam */ 1343105197Ssamstatic size_t 1344188306Sbzipsec_hdrsiz_internal(struct secpolicy *sp) 1345105197Ssam{ 1346105197Ssam struct ipsecrequest *isr; 1347186532Sbz size_t size; 1348105197Ssam 1349105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 1350120585Ssam printf("%s: using SP\n", __func__); kdebug_secpolicy(sp)); 1351105197Ssam 1352105197Ssam switch (sp->policy) { 1353105197Ssam case IPSEC_POLICY_DISCARD: 1354105197Ssam case IPSEC_POLICY_BYPASS: 1355105197Ssam case IPSEC_POLICY_NONE: 1356186531Sbz return (0); 1357105197Ssam } 1358105197Ssam 1359120585Ssam IPSEC_ASSERT(sp->policy == IPSEC_POLICY_IPSEC, 1360120585Ssam ("invalid policy %u", sp->policy)); 1361105197Ssam 1362186532Sbz size = 0; 1363105197Ssam for (isr = sp->req; isr != NULL; isr = isr->next) { 1364105197Ssam size_t clen = 0; 1365105197Ssam 1366105197Ssam switch (isr->saidx.proto) { 1367105197Ssam case IPPROTO_ESP: 1368105197Ssam clen = esp_hdrsiz(isr->sav); 1369105197Ssam break; 1370105197Ssam case IPPROTO_AH: 1371105197Ssam clen = ah_hdrsiz(isr->sav); 1372105197Ssam break; 1373105197Ssam case IPPROTO_IPCOMP: 1374105197Ssam clen = sizeof(struct ipcomp); 1375105197Ssam break; 1376105197Ssam } 1377105197Ssam 1378105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { 1379105197Ssam switch (isr->saidx.dst.sa.sa_family) { 1380105197Ssam case AF_INET: 1381105197Ssam clen += sizeof(struct ip); 1382105197Ssam break; 1383105197Ssam#ifdef INET6 1384105197Ssam case AF_INET6: 1385105197Ssam clen += sizeof(struct ip6_hdr); 1386105197Ssam break; 1387105197Ssam#endif 1388105197Ssam default: 1389120585Ssam ipseclog((LOG_ERR, "%s: unknown AF %d in " 1390120585Ssam "IPsec tunnel SA\n", __func__, 1391105197Ssam ((struct sockaddr *)&isr->saidx.dst)->sa_family)); 1392105197Ssam break; 1393105197Ssam } 1394105197Ssam } 1395186532Sbz size += clen; 1396105197Ssam } 1397105197Ssam 1398186532Sbz return (size); 1399105197Ssam} 1400105197Ssam 1401188306Sbz/* 1402188306Sbz * This function is called from ipsec_hdrsiz_tcp(), ip_ipsec_mtu(), 1403188306Sbz * disabled ip6_ipsec_mtu() and ip6_forward(). 1404188306Sbz */ 1405105197Ssamsize_t 1406188306Sbzipsec_hdrsiz(struct mbuf *m, u_int dir, struct inpcb *inp) 1407105197Ssam{ 1408105197Ssam struct secpolicy *sp; 1409105197Ssam int error; 1410105197Ssam size_t size; 1411105197Ssam 1412291355Sgnn if (!key_havesp(dir)) 1413291355Sgnn return 0; 1414291355Sgnn 1415120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 1416105197Ssam 1417186531Sbz /* Get SP for this packet. 1418105197Ssam * When we are called from ip_forward(), we call 1419105197Ssam * ipsec_getpolicybyaddr() with IP_FORWARDING flag. 1420105197Ssam */ 1421105197Ssam if (inp == NULL) 1422105197Ssam sp = ipsec_getpolicybyaddr(m, dir, IP_FORWARDING, &error); 1423105197Ssam else 1424105197Ssam sp = ipsec_getpolicybysock(m, dir, inp, &error); 1425105197Ssam 1426105197Ssam if (sp != NULL) { 1427188306Sbz size = ipsec_hdrsiz_internal(sp); 1428105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 1429120585Ssam printf("%s: size:%lu.\n", __func__, 1430105197Ssam (unsigned long)size)); 1431105197Ssam 1432105197Ssam KEY_FREESP(&sp); 1433105197Ssam } else { 1434186531Sbz size = 0; /* XXX Should be panic? 1435174038Sbz * -> No, we are called w/o knowing if 1436174038Sbz * IPsec processing is needed. */ 1437105197Ssam } 1438186531Sbz return (size); 1439105197Ssam} 1440105197Ssam 1441105197Ssam/* 1442105197Ssam * Check the variable replay window. 1443105197Ssam * ipsec_chkreplay() performs replay check before ICV verification. 1444105197Ssam * ipsec_updatereplay() updates replay bitmap. This must be called after 1445105197Ssam * ICV verification (it also performs replay check, which is usually done 1446105197Ssam * beforehand). 1447105197Ssam * 0 (zero) is returned if packet disallowed, 1 if packet permitted. 1448105197Ssam * 1449186531Sbz * Based on RFC 2401. 1450105197Ssam */ 1451105197Ssamint 1452186530Sbzipsec_chkreplay(u_int32_t seq, struct secasvar *sav) 1453105197Ssam{ 1454105197Ssam const struct secreplay *replay; 1455105197Ssam u_int32_t diff; 1456105197Ssam int fr; 1457186531Sbz u_int32_t wsizeb; /* Constant: bits of window size. */ 1458186531Sbz int frlast; /* Constant: last frame. */ 1459105197Ssam 1460120585Ssam IPSEC_ASSERT(sav != NULL, ("Null SA")); 1461120585Ssam IPSEC_ASSERT(sav->replay != NULL, ("Null replay state")); 1462105197Ssam 1463105197Ssam replay = sav->replay; 1464105197Ssam 1465105197Ssam if (replay->wsize == 0) 1466186531Sbz return (1); /* No need to check replay. */ 1467105197Ssam 1468186531Sbz /* Constant. */ 1469105197Ssam frlast = replay->wsize - 1; 1470105197Ssam wsizeb = replay->wsize << 3; 1471105197Ssam 1472186531Sbz /* Sequence number of 0 is invalid. */ 1473105197Ssam if (seq == 0) 1474186531Sbz return (0); 1475105197Ssam 1476186531Sbz /* First time is always okay. */ 1477105197Ssam if (replay->count == 0) 1478186531Sbz return (1); 1479105197Ssam 1480105197Ssam if (seq > replay->lastseq) { 1481186531Sbz /* Larger sequences are okay. */ 1482186531Sbz return (1); 1483105197Ssam } else { 1484105197Ssam /* seq is equal or less than lastseq. */ 1485105197Ssam diff = replay->lastseq - seq; 1486105197Ssam 1487186531Sbz /* Over range to check, i.e. too old or wrapped. */ 1488105197Ssam if (diff >= wsizeb) 1489186531Sbz return (0); 1490105197Ssam 1491105197Ssam fr = frlast - diff / 8; 1492105197Ssam 1493186531Sbz /* This packet already seen? */ 1494105197Ssam if ((replay->bitmap)[fr] & (1 << (diff % 8))) 1495186531Sbz return (0); 1496105197Ssam 1497186531Sbz /* Out of order but good. */ 1498186531Sbz return (1); 1499105197Ssam } 1500105197Ssam} 1501105197Ssam 1502105197Ssam/* 1503186531Sbz * Check replay counter whether to update or not. 1504105197Ssam * OUT: 0: OK 1505105197Ssam * 1: NG 1506105197Ssam */ 1507105197Ssamint 1508186530Sbzipsec_updatereplay(u_int32_t seq, struct secasvar *sav) 1509105197Ssam{ 1510105197Ssam struct secreplay *replay; 1511105197Ssam u_int32_t diff; 1512105197Ssam int fr; 1513186531Sbz u_int32_t wsizeb; /* Constant: bits of window size. */ 1514186531Sbz int frlast; /* Constant: last frame. */ 1515105197Ssam 1516120585Ssam IPSEC_ASSERT(sav != NULL, ("Null SA")); 1517120585Ssam IPSEC_ASSERT(sav->replay != NULL, ("Null replay state")); 1518105197Ssam 1519105197Ssam replay = sav->replay; 1520105197Ssam 1521105197Ssam if (replay->wsize == 0) 1522186531Sbz goto ok; /* No need to check replay. */ 1523105197Ssam 1524186531Sbz /* Constant. */ 1525105197Ssam frlast = replay->wsize - 1; 1526105197Ssam wsizeb = replay->wsize << 3; 1527105197Ssam 1528186531Sbz /* Sequence number of 0 is invalid. */ 1529105197Ssam if (seq == 0) 1530186531Sbz return (1); 1531105197Ssam 1532186531Sbz /* First time. */ 1533105197Ssam if (replay->count == 0) { 1534105197Ssam replay->lastseq = seq; 1535105197Ssam bzero(replay->bitmap, replay->wsize); 1536105197Ssam (replay->bitmap)[frlast] = 1; 1537105197Ssam goto ok; 1538105197Ssam } 1539105197Ssam 1540105197Ssam if (seq > replay->lastseq) { 1541105197Ssam /* seq is larger than lastseq. */ 1542105197Ssam diff = seq - replay->lastseq; 1543105197Ssam 1544186531Sbz /* New larger sequence number. */ 1545105197Ssam if (diff < wsizeb) { 1546186531Sbz /* In window. */ 1547186531Sbz /* Set bit for this packet. */ 1548105197Ssam vshiftl(replay->bitmap, diff, replay->wsize); 1549105197Ssam (replay->bitmap)[frlast] |= 1; 1550105197Ssam } else { 1551186531Sbz /* This packet has a "way larger". */ 1552105197Ssam bzero(replay->bitmap, replay->wsize); 1553105197Ssam (replay->bitmap)[frlast] = 1; 1554105197Ssam } 1555105197Ssam replay->lastseq = seq; 1556105197Ssam 1557186531Sbz /* Larger is good. */ 1558105197Ssam } else { 1559105197Ssam /* seq is equal or less than lastseq. */ 1560105197Ssam diff = replay->lastseq - seq; 1561105197Ssam 1562186531Sbz /* Over range to check, i.e. too old or wrapped. */ 1563105197Ssam if (diff >= wsizeb) 1564186531Sbz return (1); 1565105197Ssam 1566105197Ssam fr = frlast - diff / 8; 1567105197Ssam 1568186531Sbz /* This packet already seen? */ 1569105197Ssam if ((replay->bitmap)[fr] & (1 << (diff % 8))) 1570186531Sbz return (1); 1571105197Ssam 1572186531Sbz /* Mark as seen. */ 1573105197Ssam (replay->bitmap)[fr] |= (1 << (diff % 8)); 1574105197Ssam 1575186531Sbz /* Out of order but good. */ 1576105197Ssam } 1577105197Ssam 1578105197Ssamok: 1579105197Ssam if (replay->count == ~0) { 1580105197Ssam 1581186531Sbz /* Set overflow flag. */ 1582105197Ssam replay->overflow++; 1583105197Ssam 1584186531Sbz /* Don't increment, no more packets accepted. */ 1585105197Ssam if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) 1586186531Sbz return (1); 1587105197Ssam 1588120585Ssam ipseclog((LOG_WARNING, "%s: replay counter made %d cycle. %s\n", 1589120585Ssam __func__, replay->overflow, ipsec_logsastr(sav))); 1590105197Ssam } 1591105197Ssam 1592105197Ssam replay->count++; 1593105197Ssam 1594186531Sbz return (0); 1595105197Ssam} 1596105197Ssam 1597105197Ssam/* 1598186531Sbz * Shift variable length buffer to left. 1599105197Ssam * IN: bitmap: pointer to the buffer 1600105197Ssam * nbit: the number of to shift. 1601105197Ssam * wsize: buffer size (bytes). 1602105197Ssam */ 1603105197Ssamstatic void 1604186530Sbzvshiftl(unsigned char *bitmap, int nbit, int wsize) 1605105197Ssam{ 1606105197Ssam int s, j, i; 1607105197Ssam unsigned char over; 1608105197Ssam 1609105197Ssam for (j = 0; j < nbit; j += 8) { 1610105197Ssam s = (nbit - j < 8) ? (nbit - j): 8; 1611105197Ssam bitmap[0] <<= s; 1612105197Ssam for (i = 1; i < wsize; i++) { 1613105197Ssam over = (bitmap[i] >> (8 - s)); 1614105197Ssam bitmap[i] <<= s; 1615105197Ssam bitmap[i-1] |= over; 1616105197Ssam } 1617105197Ssam } 1618105197Ssam} 1619105197Ssam 1620193947Sbz#ifdef INET 1621105197Ssam/* Return a printable string for the IPv4 address. */ 1622105197Ssamstatic char * 1623105197Ssaminet_ntoa4(struct in_addr ina) 1624105197Ssam{ 1625105197Ssam static char buf[4][4 * sizeof "123" + 4]; 1626105197Ssam unsigned char *ucp = (unsigned char *) &ina; 1627105197Ssam static int i = 3; 1628105197Ssam 1629186531Sbz /* XXX-BZ Returns static buffer. */ 1630105197Ssam i = (i + 1) % 4; 1631105197Ssam sprintf(buf[i], "%d.%d.%d.%d", ucp[0] & 0xff, ucp[1] & 0xff, 1632105197Ssam ucp[2] & 0xff, ucp[3] & 0xff); 1633105197Ssam return (buf[i]); 1634105197Ssam} 1635193947Sbz#endif 1636105197Ssam 1637105197Ssam/* Return a printable string for the address. */ 1638105197Ssamchar * 1639105197Ssamipsec_address(union sockaddr_union* sa) 1640105197Ssam{ 1641165222Sbz#ifdef INET6 1642165118Sbz char ip6buf[INET6_ADDRSTRLEN]; 1643165118Sbz#endif 1644186531Sbz 1645105197Ssam switch (sa->sa.sa_family) { 1646159237Spjd#ifdef INET 1647105197Ssam case AF_INET: 1648186531Sbz return (inet_ntoa4(sa->sin.sin_addr)); 1649105197Ssam#endif /* INET */ 1650159237Spjd#ifdef INET6 1651105197Ssam case AF_INET6: 1652186531Sbz return (ip6_sprintf(ip6buf, &sa->sin6.sin6_addr)); 1653105197Ssam#endif /* INET6 */ 1654105197Ssam default: 1655186531Sbz return ("(unknown address family)"); 1656105197Ssam } 1657105197Ssam} 1658105197Ssam 1659105197Ssamconst char * 1660186530Sbzipsec_logsastr(struct secasvar *sav) 1661105197Ssam{ 1662105197Ssam static char buf[256]; 1663105197Ssam char *p; 1664105197Ssam struct secasindex *saidx = &sav->sah->saidx; 1665105197Ssam 1666120585Ssam IPSEC_ASSERT(saidx->src.sa.sa_family == saidx->dst.sa.sa_family, 1667120585Ssam ("address family mismatch")); 1668105197Ssam 1669105197Ssam p = buf; 1670105197Ssam snprintf(buf, sizeof(buf), "SA(SPI=%u ", (u_int32_t)ntohl(sav->spi)); 1671105197Ssam while (p && *p) 1672105197Ssam p++; 1673186531Sbz /* NB: only use ipsec_address on one address at a time. */ 1674105197Ssam snprintf(p, sizeof (buf) - (p - buf), "src=%s ", 1675105197Ssam ipsec_address(&saidx->src)); 1676105197Ssam while (p && *p) 1677105197Ssam p++; 1678105197Ssam snprintf(p, sizeof (buf) - (p - buf), "dst=%s)", 1679105197Ssam ipsec_address(&saidx->dst)); 1680105197Ssam 1681186531Sbz return (buf); 1682105197Ssam} 1683105197Ssam 1684105197Ssamvoid 1685186530Sbzipsec_dumpmbuf(struct mbuf *m) 1686105197Ssam{ 1687105197Ssam int totlen; 1688105197Ssam int i; 1689105197Ssam u_char *p; 1690105197Ssam 1691105197Ssam totlen = 0; 1692105197Ssam printf("---\n"); 1693105197Ssam while (m) { 1694105197Ssam p = mtod(m, u_char *); 1695105197Ssam for (i = 0; i < m->m_len; i++) { 1696105197Ssam printf("%02x ", p[i]); 1697105197Ssam totlen++; 1698105197Ssam if (totlen % 16 == 0) 1699105197Ssam printf("\n"); 1700105197Ssam } 1701105197Ssam m = m->m_next; 1702105197Ssam } 1703105197Ssam if (totlen % 16 != 0) 1704105197Ssam printf("\n"); 1705105197Ssam printf("---\n"); 1706105197Ssam} 1707105197Ssam 1708125100Ssamstatic void 1709195837Srwatsonipsec_init(const void *unused __unused) 1710125100Ssam{ 1711186531Sbz 1712181803Sbz SECPOLICY_LOCK_INIT(&V_ip4_def_policy); 1713186531Sbz V_ip4_def_policy.refcnt = 1; /* NB: disallow free. */ 1714125100Ssam} 1715195837SrwatsonVNET_SYSINIT(ipsec_init, SI_SUB_PROTO_DOMAININIT, SI_ORDER_ANY, ipsec_init, 1716195837Srwatson NULL); 1717193731Szec 1718125100Ssam 1719186531Sbz/* XXX This stuff doesn't belong here... */ 1720105197Ssam 1721105197Ssamstatic struct xformsw* xforms = NULL; 1722105197Ssam 1723105197Ssam/* 1724105197Ssam * Register a transform; typically at system startup. 1725105197Ssam */ 1726105197Ssamvoid 1727105197Ssamxform_register(struct xformsw* xsp) 1728105197Ssam{ 1729186531Sbz 1730105197Ssam xsp->xf_next = xforms; 1731105197Ssam xforms = xsp; 1732105197Ssam} 1733105197Ssam 1734105197Ssam/* 1735105197Ssam * Initialize transform support in an sav. 1736105197Ssam */ 1737105197Ssamint 1738105197Ssamxform_init(struct secasvar *sav, int xftype) 1739105197Ssam{ 1740105197Ssam struct xformsw *xsp; 1741105197Ssam 1742186531Sbz if (sav->tdb_xform != NULL) /* Previously initialized. */ 1743186531Sbz return (0); 1744105197Ssam for (xsp = xforms; xsp; xsp = xsp->xf_next) 1745105197Ssam if (xsp->xf_type == xftype) 1746186531Sbz return ((*xsp->xf_init)(sav, xsp)); 1747186531Sbz return (EINVAL); 1748105197Ssam} 1749