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> 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 105207369Sbz 106195699Srwatson/* NB: name changed so netstat doesn't use it. */ 107195699SrwatsonVNET_DEFINE(struct ipsecstat, ipsec4stat); 108195699SrwatsonVNET_DEFINE(int, ip4_ah_offsetmask) = 0; /* maybe IP_DF? */ 109195699Srwatson/* DF bit on encap. 0: clear 1: set 2: copy */ 110195699SrwatsonVNET_DEFINE(int, ip4_ipsec_dfbit) = 0; 111195699SrwatsonVNET_DEFINE(int, ip4_esp_trans_deflev) = IPSEC_LEVEL_USE; 112195699SrwatsonVNET_DEFINE(int, ip4_esp_net_deflev) = IPSEC_LEVEL_USE; 113195699SrwatsonVNET_DEFINE(int, ip4_ah_trans_deflev) = IPSEC_LEVEL_USE; 114195699SrwatsonVNET_DEFINE(int, ip4_ah_net_deflev) = IPSEC_LEVEL_USE; 115195699SrwatsonVNET_DEFINE(struct secpolicy, ip4_def_policy); 116195699Srwatson/* ECN ignore(-1)/forbidden(0)/allowed(1) */ 117195699SrwatsonVNET_DEFINE(int, ip4_ipsec_ecn) = 0; 118195699SrwatsonVNET_DEFINE(int, ip4_esp_randpad) = -1; 119185895Szec 120105197Ssam/* 121105197Ssam * Crypto support requirements: 122105197Ssam * 123105197Ssam * 1 require hardware support 124105197Ssam * -1 require software support 125105197Ssam * 0 take anything 126105197Ssam */ 127195699SrwatsonVNET_DEFINE(int, crypto_support) = CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE; 128105197Ssam 129214565SbzFEATURE(ipsec, "Internet Protocol Security (IPsec)"); 130214565Sbz#ifdef IPSEC_NAT_T 131214565SbzFEATURE(ipsec_natt, "UDP Encapsulation of IPsec ESP Packets ('NAT-T')"); 132214565Sbz#endif 133214565Sbz 134105197SsamSYSCTL_DECL(_net_inet_ipsec); 135105197Ssam 136105197Ssam/* net.inet.ipsec */ 137195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEF_POLICY, def_policy, 138195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_def_policy).policy, 0, 139183550Szec "IPsec default policy."); 140195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev, 141195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_esp_trans_deflev), 0, 142183550Szec "Default ESP transport mode level"); 143195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_NETLEV, esp_net_deflev, 144195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_esp_net_deflev), 0, 145183550Szec "Default ESP tunnel mode level."); 146195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_TRANSLEV, ah_trans_deflev, 147195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_ah_trans_deflev), 0, 148183550Szec "AH transfer mode default level."); 149195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev, 150195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_ah_net_deflev), 0, 151183550Szec "AH tunnel mode default level."); 152195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_AH_CLEARTOS, ah_cleartos, 153195699Srwatson CTLFLAG_RW, &VNET_NAME(ah_cleartos), 0, 154183550Szec "If set clear type-of-service field when doing AH computation."); 155195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_AH_OFFSETMASK, ah_offsetmask, 156195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_ah_offsetmask), 0, 157183550Szec "If not set clear offset field mask when doing AH computation."); 158195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DFBIT, dfbit, 159195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_ipsec_dfbit), 0, 160183550Szec "Do not fragment bit on encap."); 161195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_ECN, ecn, 162195699Srwatson CTLFLAG_RW, &VNET_NAME(ip4_ipsec_ecn), 0, 163183550Szec "Explicit Congestion Notification handling."); 164195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEBUG, debug, 165195699Srwatson CTLFLAG_RW, &VNET_NAME(ipsec_debug), 0, 166183550Szec "Enable IPsec debugging output when set."); 167195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, OID_AUTO, crypto_support, 168195699Srwatson CTLFLAG_RW, &VNET_NAME(crypto_support), 0, 169183550Szec "Crypto driver selection."); 170195699SrwatsonSYSCTL_VNET_STRUCT(_net_inet_ipsec, OID_AUTO, ipsecstats, 171195699Srwatson CTLFLAG_RD, &VNET_NAME(ipsec4stat), ipsecstat, 172183550Szec "IPsec IPv4 statistics."); 173105197Ssam 174157634Spjd#ifdef REGRESSION 175157613Spjd/* 176157613Spjd * When set to 1, IPsec will send packets with the same sequence number. 177157613Spjd * This allows to verify if the other side has proper replay attacks detection. 178157613Spjd */ 179195699SrwatsonVNET_DEFINE(int, ipsec_replay) = 0; 180195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, OID_AUTO, test_replay, 181195699Srwatson CTLFLAG_RW, &VNET_NAME(ipsec_replay), 0, 182195699Srwatson "Emulate replay attack"); 183157613Spjd/* 184157613Spjd * When set 1, IPsec will send packets with corrupted HMAC. 185157613Spjd * This allows to verify if the other side properly detects modified packets. 186157613Spjd */ 187195699SrwatsonVNET_DEFINE(int, ipsec_integrity) = 0; 188195699SrwatsonSYSCTL_VNET_INT(_net_inet_ipsec, OID_AUTO, test_integrity, 189195699Srwatson CTLFLAG_RW, &VNET_NAME(ipsec_integrity), 0, 190195699Srwatson "Emulate man-in-the-middle attack"); 191157634Spjd#endif 192157613Spjd 193171133Sgnn#ifdef INET6 194195699SrwatsonVNET_DEFINE(struct ipsecstat, ipsec6stat); 195195699SrwatsonVNET_DEFINE(int, ip6_esp_trans_deflev) = IPSEC_LEVEL_USE; 196195699SrwatsonVNET_DEFINE(int, ip6_esp_net_deflev) = IPSEC_LEVEL_USE; 197195699SrwatsonVNET_DEFINE(int, ip6_ah_trans_deflev) = IPSEC_LEVEL_USE; 198195699SrwatsonVNET_DEFINE(int, ip6_ah_net_deflev) = IPSEC_LEVEL_USE; 199195699SrwatsonVNET_DEFINE(int, ip6_ipsec_ecn) = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */ 200105197Ssam 201105197SsamSYSCTL_DECL(_net_inet6_ipsec6); 202105197Ssam 203105197Ssam/* net.inet6.ipsec6 */ 204105197Ssam#ifdef COMPAT_KAME 205105197SsamSYSCTL_OID(_net_inet6_ipsec6, IPSECCTL_STATS, stats, CTLFLAG_RD, 206180820Strhodes 0, 0, compat_ipsecstats_sysctl, "S", "IPsec IPv6 statistics."); 207105197Ssam#endif /* COMPAT_KAME */ 208195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEF_POLICY, def_policy, CTLFLAG_RW, 209195699Srwatson &VNET_NAME(ip4_def_policy).policy, 0, 210183550Szec "IPsec default policy."); 211195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_TRANSLEV, 212195699Srwatson esp_trans_deflev, CTLFLAG_RW, &VNET_NAME(ip6_esp_trans_deflev), 0, 213183550Szec "Default ESP transport mode level."); 214195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_NETLEV, 215195699Srwatson esp_net_deflev, CTLFLAG_RW, &VNET_NAME(ip6_esp_net_deflev), 0, 216183550Szec "Default ESP tunnel mode level."); 217195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_TRANSLEV, 218195699Srwatson ah_trans_deflev, CTLFLAG_RW, &VNET_NAME(ip6_ah_trans_deflev), 0, 219183550Szec "AH transfer mode default level."); 220195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_NETLEV, 221195699Srwatson ah_net_deflev, CTLFLAG_RW, &VNET_NAME(ip6_ah_net_deflev), 0, 222183550Szec "AH tunnel mode default level."); 223195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_ECN, 224195699Srwatson ecn, CTLFLAG_RW, &VNET_NAME(ip6_ipsec_ecn), 0, 225183550Szec "Explicit Congestion Notification handling."); 226195699SrwatsonSYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEBUG, debug, CTLFLAG_RW, 227195699Srwatson &VNET_NAME(ipsec_debug), 0, 228183550Szec "Enable IPsec debugging output when set."); 229195699SrwatsonSYSCTL_VNET_STRUCT(_net_inet6_ipsec6, IPSECCTL_STATS, 230195699Srwatson ipsecstats, CTLFLAG_RD, &VNET_NAME(ipsec6stat), ipsecstat, 231183550Szec "IPsec IPv6 statistics."); 232105197Ssam#endif /* INET6 */ 233105197Ssam 234188306Sbzstatic int ipsec_setspidx_inpcb __P((struct mbuf *, struct inpcb *)); 235105197Ssamstatic int ipsec_setspidx __P((struct mbuf *, struct secpolicyindex *, int)); 236105197Ssamstatic void ipsec4_get_ulp __P((struct mbuf *m, struct secpolicyindex *, int)); 237105197Ssamstatic int ipsec4_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); 238105197Ssam#ifdef INET6 239105197Ssamstatic void ipsec6_get_ulp __P((struct mbuf *m, struct secpolicyindex *, int)); 240105197Ssamstatic int ipsec6_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); 241105197Ssam#endif 242105197Ssamstatic void ipsec_delpcbpolicy __P((struct inpcbpolicy *)); 243105197Ssamstatic struct secpolicy *ipsec_deepcopy_policy __P((struct secpolicy *src)); 244105197Ssamstatic void vshiftl __P((unsigned char *, int, int)); 245105197Ssam 246119643SsamMALLOC_DEFINE(M_IPSEC_INPCB, "inpcbpolicy", "inpcb-resident ipsec policy"); 247119643Ssam 248105197Ssam/* 249105197Ssam * Return a held reference to the default SP. 250105197Ssam */ 251105197Ssamstatic struct secpolicy * 252105197Ssamkey_allocsp_default(const char* where, int tag) 253105197Ssam{ 254105197Ssam struct secpolicy *sp; 255105197Ssam 256105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 257105197Ssam printf("DP key_allocsp_default from %s:%u\n", where, tag)); 258105197Ssam 259181803Sbz sp = &V_ip4_def_policy; 260105197Ssam if (sp->policy != IPSEC_POLICY_DISCARD && 261105197Ssam sp->policy != IPSEC_POLICY_NONE) { 262105197Ssam ipseclog((LOG_INFO, "fixed system default policy: %d->%d\n", 263105197Ssam sp->policy, IPSEC_POLICY_NONE)); 264105197Ssam sp->policy = IPSEC_POLICY_NONE; 265105197Ssam } 266135947Ssam key_addref(sp); 267105197Ssam 268105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 269105197Ssam printf("DP key_allocsp_default returns SP:%p (%u)\n", 270105197Ssam sp, sp->refcnt)); 271186531Sbz return (sp); 272105197Ssam} 273105197Ssam#define KEY_ALLOCSP_DEFAULT() \ 274105197Ssam key_allocsp_default(__FILE__, __LINE__) 275105197Ssam 276105197Ssam/* 277105197Ssam * For OUTBOUND packet having a socket. Searching SPD for packet, 278105197Ssam * and return a pointer to SP. 279105197Ssam * OUT: NULL: no apropreate SP found, the following value is set to error. 280105197Ssam * 0 : bypass 281105197Ssam * EACCES : discard packet. 282105197Ssam * ENOENT : ipsec_acquire() in progress, maybe. 283105197Ssam * others : error occured. 284105197Ssam * others: a pointer to SP 285105197Ssam * 286105197Ssam * NOTE: IPv6 mapped adddress concern is implemented here. 287105197Ssam */ 288105197Ssamstruct secpolicy * 289105197Ssamipsec_getpolicy(struct tdb_ident *tdbi, u_int dir) 290105197Ssam{ 291105197Ssam struct secpolicy *sp; 292105197Ssam 293120585Ssam IPSEC_ASSERT(tdbi != NULL, ("null tdbi")); 294120585Ssam IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND, 295120585Ssam ("invalid direction %u", dir)); 296105197Ssam 297105197Ssam sp = KEY_ALLOCSP2(tdbi->spi, &tdbi->dst, tdbi->proto, dir); 298105197Ssam if (sp == NULL) /*XXX????*/ 299105197Ssam sp = KEY_ALLOCSP_DEFAULT(); 300120585Ssam IPSEC_ASSERT(sp != NULL, ("null SP")); 301186531Sbz return (sp); 302105197Ssam} 303105197Ssam 304105197Ssam/* 305105197Ssam * For OUTBOUND packet having a socket. Searching SPD for packet, 306105197Ssam * and return a pointer to SP. 307105197Ssam * OUT: NULL: no apropreate SP found, the following value is set to error. 308105197Ssam * 0 : bypass 309105197Ssam * EACCES : discard packet. 310105197Ssam * ENOENT : ipsec_acquire() in progress, maybe. 311105197Ssam * others : error occured. 312105197Ssam * others: a pointer to SP 313105197Ssam * 314105197Ssam * NOTE: IPv6 mapped adddress concern is implemented here. 315105197Ssam */ 316186508Sbzstatic struct secpolicy * 317186508Sbzipsec_getpolicybysock(struct mbuf *m, u_int dir, struct inpcb *inp, int *error) 318105197Ssam{ 319188306Sbz struct inpcbpolicy *pcbsp; 320186531Sbz struct secpolicy *currsp = NULL; /* Policy on socket. */ 321105197Ssam struct secpolicy *sp; 322105197Ssam 323120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 324120585Ssam IPSEC_ASSERT(inp != NULL, ("null inpcb")); 325120585Ssam IPSEC_ASSERT(error != NULL, ("null error")); 326120585Ssam IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND, 327120585Ssam ("invalid direction %u", dir)); 328105197Ssam 329186531Sbz /* Set spidx in pcb. */ 330188306Sbz *error = ipsec_setspidx_inpcb(m, inp); 331105197Ssam if (*error) 332186531Sbz return (NULL); 333105197Ssam 334188306Sbz pcbsp = inp->inp_sp; 335120585Ssam IPSEC_ASSERT(pcbsp != NULL, ("null pcbsp")); 336105197Ssam switch (dir) { 337105197Ssam case IPSEC_DIR_INBOUND: 338105197Ssam currsp = pcbsp->sp_in; 339105197Ssam break; 340105197Ssam case IPSEC_DIR_OUTBOUND: 341105197Ssam currsp = pcbsp->sp_out; 342105197Ssam break; 343105197Ssam } 344120585Ssam IPSEC_ASSERT(currsp != NULL, ("null currsp")); 345105197Ssam 346186531Sbz if (pcbsp->priv) { /* When privilieged socket. */ 347105197Ssam switch (currsp->policy) { 348105197Ssam case IPSEC_POLICY_BYPASS: 349105197Ssam case IPSEC_POLICY_IPSEC: 350135947Ssam key_addref(currsp); 351105197Ssam sp = currsp; 352105197Ssam break; 353105197Ssam 354105197Ssam case IPSEC_POLICY_ENTRUST: 355186531Sbz /* Look for a policy in SPD. */ 356105197Ssam sp = KEY_ALLOCSP(&currsp->spidx, dir); 357186531Sbz if (sp == NULL) /* No SP found. */ 358105197Ssam sp = KEY_ALLOCSP_DEFAULT(); 359105197Ssam break; 360105197Ssam 361105197Ssam default: 362120585Ssam ipseclog((LOG_ERR, "%s: Invalid policy for PCB %d\n", 363120585Ssam __func__, currsp->policy)); 364105197Ssam *error = EINVAL; 365186531Sbz return (NULL); 366105197Ssam } 367186531Sbz } else { /* Unpriv, SPD has policy. */ 368105197Ssam sp = KEY_ALLOCSP(&currsp->spidx, dir); 369186531Sbz if (sp == NULL) { /* No SP found. */ 370105197Ssam switch (currsp->policy) { 371105197Ssam case IPSEC_POLICY_BYPASS: 372120585Ssam ipseclog((LOG_ERR, "%s: Illegal policy for " 373120585Ssam "non-priviliged defined %d\n", 374120585Ssam __func__, currsp->policy)); 375105197Ssam *error = EINVAL; 376186531Sbz return (NULL); 377105197Ssam 378105197Ssam case IPSEC_POLICY_ENTRUST: 379105197Ssam sp = KEY_ALLOCSP_DEFAULT(); 380105197Ssam break; 381105197Ssam 382105197Ssam case IPSEC_POLICY_IPSEC: 383135947Ssam key_addref(currsp); 384105197Ssam sp = currsp; 385105197Ssam break; 386105197Ssam 387105197Ssam default: 388120585Ssam ipseclog((LOG_ERR, "%s: Invalid policy for " 389120585Ssam "PCB %d\n", __func__, currsp->policy)); 390105197Ssam *error = EINVAL; 391186531Sbz return (NULL); 392105197Ssam } 393105197Ssam } 394105197Ssam } 395120585Ssam IPSEC_ASSERT(sp != NULL, 396120585Ssam ("null SP (priv %u policy %u", pcbsp->priv, currsp->policy)); 397105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 398120585Ssam printf("DP %s (priv %u policy %u) allocate SP:%p (refcnt %u)\n", 399120585Ssam __func__, pcbsp->priv, currsp->policy, sp, sp->refcnt)); 400186531Sbz return (sp); 401105197Ssam} 402105197Ssam 403105197Ssam/* 404105197Ssam * For FORWADING packet or OUTBOUND without a socket. Searching SPD for packet, 405105197Ssam * and return a pointer to SP. 406105197Ssam * OUT: positive: a pointer to the entry for security policy leaf matched. 407105197Ssam * NULL: no apropreate SP found, the following value is set to error. 408105197Ssam * 0 : bypass 409105197Ssam * EACCES : discard packet. 410105197Ssam * ENOENT : ipsec_acquire() in progress, maybe. 411105197Ssam * others : error occured. 412105197Ssam */ 413105197Ssamstruct secpolicy * 414186530Sbzipsec_getpolicybyaddr(struct mbuf *m, u_int dir, int flag, int *error) 415105197Ssam{ 416105197Ssam struct secpolicyindex spidx; 417105197Ssam struct secpolicy *sp; 418105197Ssam 419120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 420120585Ssam IPSEC_ASSERT(error != NULL, ("null error")); 421120585Ssam IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND, 422120585Ssam ("invalid direction %u", dir)); 423105197Ssam 424105197Ssam sp = NULL; 425105197Ssam if (key_havesp(dir)) { 426108533Sschweikh /* Make an index to look for a policy. */ 427105197Ssam *error = ipsec_setspidx(m, &spidx, 428105197Ssam (flag & IP_FORWARDING) ? 0 : 1); 429105197Ssam if (*error != 0) { 430120585Ssam DPRINTF(("%s: setpidx failed, dir %u flag %u\n", 431120585Ssam __func__, dir, flag)); 432186531Sbz return (NULL); 433105197Ssam } 434105197Ssam spidx.dir = dir; 435105197Ssam 436105197Ssam sp = KEY_ALLOCSP(&spidx, dir); 437105197Ssam } 438186531Sbz if (sp == NULL) /* No SP found, use system default. */ 439105197Ssam sp = KEY_ALLOCSP_DEFAULT(); 440120585Ssam IPSEC_ASSERT(sp != NULL, ("null SP")); 441186531Sbz return (sp); 442105197Ssam} 443105197Ssam 444105197Ssamstruct secpolicy * 445186530Sbzipsec4_checkpolicy(struct mbuf *m, u_int dir, u_int flag, int *error, 446186530Sbz struct inpcb *inp) 447105197Ssam{ 448105197Ssam struct secpolicy *sp; 449105197Ssam 450105197Ssam *error = 0; 451105197Ssam if (inp == NULL) 452105197Ssam sp = ipsec_getpolicybyaddr(m, dir, flag, error); 453105197Ssam else 454105197Ssam sp = ipsec_getpolicybysock(m, dir, inp, error); 455105197Ssam if (sp == NULL) { 456120585Ssam IPSEC_ASSERT(*error != 0, ("getpolicy failed w/o error")); 457252692Sae IPSECSTAT_INC(ips_out_inval); 458186531Sbz return (NULL); 459105197Ssam } 460120585Ssam IPSEC_ASSERT(*error == 0, ("sp w/ error set to %u", *error)); 461105197Ssam switch (sp->policy) { 462105197Ssam case IPSEC_POLICY_ENTRUST: 463105197Ssam default: 464120585Ssam printf("%s: invalid policy %u\n", __func__, sp->policy); 465186531Sbz /* FALLTHROUGH */ 466105197Ssam case IPSEC_POLICY_DISCARD: 467252692Sae IPSECSTAT_INC(ips_out_polvio); 468186531Sbz *error = -EINVAL; /* Packet is discarded by caller. */ 469105197Ssam break; 470105197Ssam case IPSEC_POLICY_BYPASS: 471105197Ssam case IPSEC_POLICY_NONE: 472105197Ssam KEY_FREESP(&sp); 473186531Sbz sp = NULL; /* NB: force NULL result. */ 474105197Ssam break; 475105197Ssam case IPSEC_POLICY_IPSEC: 476186531Sbz if (sp->req == NULL) /* Acquire a SA. */ 477105197Ssam *error = key_spdacquire(sp); 478105197Ssam break; 479105197Ssam } 480105197Ssam if (*error != 0) { 481105197Ssam KEY_FREESP(&sp); 482105197Ssam sp = NULL; 483105197Ssam } 484186531Sbz return (sp); 485105197Ssam} 486105197Ssam 487105197Ssamstatic int 488188306Sbzipsec_setspidx_inpcb(struct mbuf *m, struct inpcb *inp) 489105197Ssam{ 490105197Ssam int error; 491105197Ssam 492186532Sbz IPSEC_ASSERT(inp != NULL, ("null inp")); 493186532Sbz IPSEC_ASSERT(inp->inp_sp != NULL, ("null inp_sp")); 494186532Sbz IPSEC_ASSERT(inp->inp_sp->sp_out != NULL && inp->inp_sp->sp_in != NULL, 495120585Ssam ("null sp_in || sp_out")); 496105197Ssam 497186532Sbz error = ipsec_setspidx(m, &inp->inp_sp->sp_in->spidx, 1); 498105197Ssam if (error == 0) { 499186532Sbz inp->inp_sp->sp_in->spidx.dir = IPSEC_DIR_INBOUND; 500186532Sbz inp->inp_sp->sp_out->spidx = inp->inp_sp->sp_in->spidx; 501186532Sbz inp->inp_sp->sp_out->spidx.dir = IPSEC_DIR_OUTBOUND; 502105197Ssam } else { 503186532Sbz bzero(&inp->inp_sp->sp_in->spidx, 504186532Sbz sizeof (inp->inp_sp->sp_in->spidx)); 505186532Sbz bzero(&inp->inp_sp->sp_out->spidx, 506186532Sbz sizeof (inp->inp_sp->sp_in->spidx)); 507105197Ssam } 508186531Sbz return (error); 509105197Ssam} 510105197Ssam 511105197Ssam/* 512186531Sbz * Configure security policy index (src/dst/proto/sport/dport) 513105197Ssam * by looking at the content of mbuf. 514186531Sbz * The caller is responsible for error recovery (like clearing up spidx). 515105197Ssam */ 516105197Ssamstatic int 517186530Sbzipsec_setspidx(struct mbuf *m, struct secpolicyindex *spidx, int needport) 518105197Ssam{ 519105197Ssam struct ip *ip = NULL; 520105197Ssam struct ip ipbuf; 521105197Ssam u_int v; 522105197Ssam struct mbuf *n; 523105197Ssam int len; 524105197Ssam int error; 525105197Ssam 526120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 527105197Ssam 528105197Ssam /* 529186531Sbz * Validate m->m_pkthdr.len. We see incorrect length if we 530105197Ssam * mistakenly call this function with inconsistent mbuf chain 531186531Sbz * (like 4.4BSD tcp/udp processing). XXX Should we panic here? 532105197Ssam */ 533105197Ssam len = 0; 534105197Ssam for (n = m; n; n = n->m_next) 535105197Ssam len += n->m_len; 536105197Ssam if (m->m_pkthdr.len != len) { 537105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 538120585Ssam printf("%s: pkthdr len(%d) mismatch (%d), ignored.\n", 539120585Ssam __func__, len, m->m_pkthdr.len)); 540186531Sbz return (EINVAL); 541105197Ssam } 542105197Ssam 543105197Ssam if (m->m_pkthdr.len < sizeof(struct ip)) { 544105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 545120585Ssam printf("%s: pkthdr len(%d) too small (v4), ignored.\n", 546120585Ssam __func__, m->m_pkthdr.len)); 547186531Sbz return (EINVAL); 548105197Ssam } 549105197Ssam 550105197Ssam if (m->m_len >= sizeof(*ip)) 551105197Ssam ip = mtod(m, struct ip *); 552105197Ssam else { 553105197Ssam m_copydata(m, 0, sizeof(ipbuf), (caddr_t)&ipbuf); 554105197Ssam ip = &ipbuf; 555105197Ssam } 556105197Ssam v = ip->ip_v; 557105197Ssam switch (v) { 558105197Ssam case 4: 559105197Ssam error = ipsec4_setspidx_ipaddr(m, spidx); 560105197Ssam if (error) 561186531Sbz return (error); 562105197Ssam ipsec4_get_ulp(m, spidx, needport); 563186531Sbz return (0); 564105197Ssam#ifdef INET6 565105197Ssam case 6: 566105197Ssam if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) { 567105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 568120585Ssam printf("%s: pkthdr len(%d) too small (v6), " 569120585Ssam "ignored\n", __func__, m->m_pkthdr.len)); 570186531Sbz return (EINVAL); 571105197Ssam } 572105197Ssam error = ipsec6_setspidx_ipaddr(m, spidx); 573105197Ssam if (error) 574186531Sbz return (error); 575105197Ssam ipsec6_get_ulp(m, spidx, needport); 576186531Sbz return (0); 577105197Ssam#endif 578105197Ssam default: 579105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 580120585Ssam printf("%s: " "unknown IP version %u, ignored.\n", 581120585Ssam __func__, v)); 582186531Sbz return (EINVAL); 583105197Ssam } 584105197Ssam} 585105197Ssam 586105197Ssamstatic void 587105197Ssamipsec4_get_ulp(struct mbuf *m, struct secpolicyindex *spidx, int needport) 588105197Ssam{ 589105197Ssam u_int8_t nxt; 590105197Ssam int off; 591105197Ssam 592186531Sbz /* Sanity check. */ 593120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 594120585Ssam IPSEC_ASSERT(m->m_pkthdr.len >= sizeof(struct ip),("packet too short")); 595105197Ssam 596186531Sbz /* NB: ip_input() flips it into host endian. XXX Need more checking. */ 597206111Seri if (m->m_len >= sizeof (struct ip)) { 598105197Ssam struct ip *ip = mtod(m, struct ip *); 599105197Ssam if (ip->ip_off & (IP_MF | IP_OFFMASK)) 600105197Ssam goto done; 601105197Ssam off = ip->ip_hl << 2; 602105197Ssam nxt = ip->ip_p; 603105197Ssam } else { 604105197Ssam struct ip ih; 605105197Ssam 606105197Ssam m_copydata(m, 0, sizeof (struct ip), (caddr_t) &ih); 607105197Ssam if (ih.ip_off & (IP_MF | IP_OFFMASK)) 608105197Ssam goto done; 609105197Ssam off = ih.ip_hl << 2; 610105197Ssam nxt = ih.ip_p; 611105197Ssam } 612105197Ssam 613105197Ssam while (off < m->m_pkthdr.len) { 614105197Ssam struct ip6_ext ip6e; 615105197Ssam struct tcphdr th; 616105197Ssam struct udphdr uh; 617105197Ssam 618105197Ssam switch (nxt) { 619105197Ssam case IPPROTO_TCP: 620105197Ssam spidx->ul_proto = nxt; 621105197Ssam if (!needport) 622105197Ssam goto done_proto; 623105197Ssam if (off + sizeof(struct tcphdr) > m->m_pkthdr.len) 624105197Ssam goto done; 625105197Ssam m_copydata(m, off, sizeof (th), (caddr_t) &th); 626105197Ssam spidx->src.sin.sin_port = th.th_sport; 627105197Ssam spidx->dst.sin.sin_port = th.th_dport; 628105197Ssam return; 629105197Ssam case IPPROTO_UDP: 630105197Ssam spidx->ul_proto = nxt; 631105197Ssam if (!needport) 632105197Ssam goto done_proto; 633105197Ssam if (off + sizeof(struct udphdr) > m->m_pkthdr.len) 634105197Ssam goto done; 635105197Ssam m_copydata(m, off, sizeof (uh), (caddr_t) &uh); 636105197Ssam spidx->src.sin.sin_port = uh.uh_sport; 637105197Ssam spidx->dst.sin.sin_port = uh.uh_dport; 638105197Ssam return; 639105197Ssam case IPPROTO_AH: 640143323Ssam if (off + sizeof(ip6e) > m->m_pkthdr.len) 641105197Ssam goto done; 642186531Sbz /* XXX Sigh, this works but is totally bogus. */ 643105197Ssam m_copydata(m, off, sizeof(ip6e), (caddr_t) &ip6e); 644105197Ssam off += (ip6e.ip6e_len + 2) << 2; 645105197Ssam nxt = ip6e.ip6e_nxt; 646105197Ssam break; 647105197Ssam case IPPROTO_ICMP: 648105197Ssam default: 649186531Sbz /* XXX Intermediate headers??? */ 650105197Ssam spidx->ul_proto = nxt; 651105197Ssam goto done_proto; 652105197Ssam } 653105197Ssam } 654105197Ssamdone: 655105197Ssam spidx->ul_proto = IPSEC_ULPROTO_ANY; 656105197Ssamdone_proto: 657105197Ssam spidx->src.sin.sin_port = IPSEC_PORT_ANY; 658105197Ssam spidx->dst.sin.sin_port = IPSEC_PORT_ANY; 659105197Ssam} 660105197Ssam 661186531Sbz/* Assumes that m is sane. */ 662105197Ssamstatic int 663105197Ssamipsec4_setspidx_ipaddr(struct mbuf *m, struct secpolicyindex *spidx) 664105197Ssam{ 665105197Ssam static const struct sockaddr_in template = { 666105197Ssam sizeof (struct sockaddr_in), 667105197Ssam AF_INET, 668105197Ssam 0, { 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 } 669105197Ssam }; 670105197Ssam 671105197Ssam spidx->src.sin = template; 672105197Ssam spidx->dst.sin = template; 673105197Ssam 674105197Ssam if (m->m_len < sizeof (struct ip)) { 675105197Ssam m_copydata(m, offsetof(struct ip, ip_src), 676105197Ssam sizeof (struct in_addr), 677105197Ssam (caddr_t) &spidx->src.sin.sin_addr); 678105197Ssam m_copydata(m, offsetof(struct ip, ip_dst), 679105197Ssam sizeof (struct in_addr), 680105197Ssam (caddr_t) &spidx->dst.sin.sin_addr); 681105197Ssam } else { 682105197Ssam struct ip *ip = mtod(m, struct ip *); 683105197Ssam spidx->src.sin.sin_addr = ip->ip_src; 684105197Ssam spidx->dst.sin.sin_addr = ip->ip_dst; 685105197Ssam } 686105197Ssam 687105197Ssam spidx->prefs = sizeof(struct in_addr) << 3; 688105197Ssam spidx->prefd = sizeof(struct in_addr) << 3; 689105197Ssam 690186531Sbz return (0); 691105197Ssam} 692105197Ssam 693105197Ssam#ifdef INET6 694105197Ssamstatic void 695186530Sbzipsec6_get_ulp(struct mbuf *m, struct secpolicyindex *spidx, int needport) 696105197Ssam{ 697105197Ssam int off, nxt; 698105197Ssam struct tcphdr th; 699105197Ssam struct udphdr uh; 700170121Sbz struct icmp6_hdr ih; 701105197Ssam 702186531Sbz /* Sanity check. */ 703105197Ssam if (m == NULL) 704120585Ssam panic("%s: NULL pointer was passed.\n", __func__); 705105197Ssam 706105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 707120585Ssam printf("%s:\n", __func__); kdebug_mbuf(m)); 708105197Ssam 709186531Sbz /* Set default. */ 710105197Ssam spidx->ul_proto = IPSEC_ULPROTO_ANY; 711105197Ssam ((struct sockaddr_in6 *)&spidx->src)->sin6_port = IPSEC_PORT_ANY; 712105197Ssam ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = IPSEC_PORT_ANY; 713105197Ssam 714105197Ssam nxt = -1; 715105197Ssam off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt); 716105197Ssam if (off < 0 || m->m_pkthdr.len < off) 717105197Ssam return; 718105197Ssam 719105197Ssam switch (nxt) { 720105197Ssam case IPPROTO_TCP: 721105197Ssam spidx->ul_proto = nxt; 722105197Ssam if (!needport) 723105197Ssam break; 724105197Ssam if (off + sizeof(struct tcphdr) > m->m_pkthdr.len) 725105197Ssam break; 726105197Ssam m_copydata(m, off, sizeof(th), (caddr_t)&th); 727105197Ssam ((struct sockaddr_in6 *)&spidx->src)->sin6_port = th.th_sport; 728105197Ssam ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = th.th_dport; 729105197Ssam break; 730105197Ssam case IPPROTO_UDP: 731105197Ssam spidx->ul_proto = nxt; 732105197Ssam if (!needport) 733105197Ssam break; 734105197Ssam if (off + sizeof(struct udphdr) > m->m_pkthdr.len) 735105197Ssam break; 736105197Ssam m_copydata(m, off, sizeof(uh), (caddr_t)&uh); 737105197Ssam ((struct sockaddr_in6 *)&spidx->src)->sin6_port = uh.uh_sport; 738105197Ssam ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = uh.uh_dport; 739105197Ssam break; 740105197Ssam case IPPROTO_ICMPV6: 741170121Sbz spidx->ul_proto = nxt; 742170121Sbz if (off + sizeof(struct icmp6_hdr) > m->m_pkthdr.len) 743170121Sbz break; 744170121Sbz m_copydata(m, off, sizeof(ih), (caddr_t)&ih); 745170121Sbz ((struct sockaddr_in6 *)&spidx->src)->sin6_port = 746170121Sbz htons((uint16_t)ih.icmp6_type); 747170121Sbz ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = 748170121Sbz htons((uint16_t)ih.icmp6_code); 749170121Sbz break; 750105197Ssam default: 751186531Sbz /* XXX Intermediate headers??? */ 752105197Ssam spidx->ul_proto = nxt; 753105197Ssam break; 754105197Ssam } 755105197Ssam} 756105197Ssam 757186531Sbz/* Assumes that m is sane. */ 758105197Ssamstatic int 759186530Sbzipsec6_setspidx_ipaddr(struct mbuf *m, struct secpolicyindex *spidx) 760105197Ssam{ 761105197Ssam struct ip6_hdr *ip6 = NULL; 762105197Ssam struct ip6_hdr ip6buf; 763105197Ssam struct sockaddr_in6 *sin6; 764105197Ssam 765105197Ssam if (m->m_len >= sizeof(*ip6)) 766105197Ssam ip6 = mtod(m, struct ip6_hdr *); 767105197Ssam else { 768105197Ssam m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf); 769105197Ssam ip6 = &ip6buf; 770105197Ssam } 771105197Ssam 772105197Ssam sin6 = (struct sockaddr_in6 *)&spidx->src; 773105197Ssam bzero(sin6, sizeof(*sin6)); 774105197Ssam sin6->sin6_family = AF_INET6; 775105197Ssam sin6->sin6_len = sizeof(struct sockaddr_in6); 776105197Ssam bcopy(&ip6->ip6_src, &sin6->sin6_addr, sizeof(ip6->ip6_src)); 777105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { 778105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 779105197Ssam sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]); 780105197Ssam } 781105197Ssam spidx->prefs = sizeof(struct in6_addr) << 3; 782105197Ssam 783105197Ssam sin6 = (struct sockaddr_in6 *)&spidx->dst; 784105197Ssam bzero(sin6, sizeof(*sin6)); 785105197Ssam sin6->sin6_family = AF_INET6; 786105197Ssam sin6->sin6_len = sizeof(struct sockaddr_in6); 787105197Ssam bcopy(&ip6->ip6_dst, &sin6->sin6_addr, sizeof(ip6->ip6_dst)); 788105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { 789105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 790105197Ssam sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]); 791105197Ssam } 792105197Ssam spidx->prefd = sizeof(struct in6_addr) << 3; 793105197Ssam 794186531Sbz return (0); 795105197Ssam} 796105197Ssam#endif 797105197Ssam 798105197Ssamstatic void 799186530Sbzipsec_delpcbpolicy(struct inpcbpolicy *p) 800105197Ssam{ 801186531Sbz 802119643Ssam free(p, M_IPSEC_INPCB); 803105197Ssam} 804105197Ssam 805186531Sbz/* Initialize policy in PCB. */ 806105197Ssamint 807186530Sbzipsec_init_policy(struct socket *so, struct inpcbpolicy **pcb_sp) 808105197Ssam{ 809105197Ssam struct inpcbpolicy *new; 810105197Ssam 811186531Sbz /* Sanity check. */ 812105197Ssam if (so == NULL || pcb_sp == NULL) 813120585Ssam panic("%s: NULL pointer was passed.\n", __func__); 814105197Ssam 815105197Ssam new = (struct inpcbpolicy *) malloc(sizeof(struct inpcbpolicy), 816119643Ssam M_IPSEC_INPCB, M_NOWAIT|M_ZERO); 817105197Ssam if (new == NULL) { 818120585Ssam ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); 819186531Sbz return (ENOBUFS); 820105197Ssam } 821105197Ssam 822120585Ssam new->priv = IPSEC_IS_PRIVILEGED_SO(so); 823105197Ssam 824105197Ssam if ((new->sp_in = KEY_NEWSP()) == NULL) { 825105197Ssam ipsec_delpcbpolicy(new); 826186531Sbz return (ENOBUFS); 827105197Ssam } 828105197Ssam new->sp_in->state = IPSEC_SPSTATE_ALIVE; 829105197Ssam new->sp_in->policy = IPSEC_POLICY_ENTRUST; 830105197Ssam 831105197Ssam if ((new->sp_out = KEY_NEWSP()) == NULL) { 832105197Ssam KEY_FREESP(&new->sp_in); 833105197Ssam ipsec_delpcbpolicy(new); 834186531Sbz return (ENOBUFS); 835105197Ssam } 836105197Ssam new->sp_out->state = IPSEC_SPSTATE_ALIVE; 837105197Ssam new->sp_out->policy = IPSEC_POLICY_ENTRUST; 838105197Ssam 839105197Ssam *pcb_sp = new; 840105197Ssam 841186531Sbz return (0); 842105197Ssam} 843105197Ssam 844186531Sbz/* Copy old IPsec policy into new. */ 845105197Ssamint 846186530Sbzipsec_copy_policy(struct inpcbpolicy *old, struct inpcbpolicy *new) 847105197Ssam{ 848105197Ssam struct secpolicy *sp; 849105197Ssam 850105197Ssam sp = ipsec_deepcopy_policy(old->sp_in); 851105197Ssam if (sp) { 852105197Ssam KEY_FREESP(&new->sp_in); 853105197Ssam new->sp_in = sp; 854105197Ssam } else 855186531Sbz return (ENOBUFS); 856105197Ssam 857105197Ssam sp = ipsec_deepcopy_policy(old->sp_out); 858105197Ssam if (sp) { 859105197Ssam KEY_FREESP(&new->sp_out); 860105197Ssam new->sp_out = sp; 861105197Ssam } else 862186531Sbz return (ENOBUFS); 863105197Ssam 864105197Ssam new->priv = old->priv; 865105197Ssam 866186531Sbz return (0); 867105197Ssam} 868105197Ssam 869119643Ssamstruct ipsecrequest * 870119643Ssamipsec_newisr(void) 871119643Ssam{ 872119643Ssam struct ipsecrequest *p; 873119643Ssam 874119643Ssam p = malloc(sizeof(struct ipsecrequest), M_IPSEC_SR, M_NOWAIT|M_ZERO); 875119643Ssam if (p != NULL) 876120585Ssam IPSECREQUEST_LOCK_INIT(p); 877186531Sbz return (p); 878119643Ssam} 879119643Ssam 880119643Ssamvoid 881119643Ssamipsec_delisr(struct ipsecrequest *p) 882119643Ssam{ 883186531Sbz 884120585Ssam IPSECREQUEST_LOCK_DESTROY(p); 885119643Ssam free(p, M_IPSEC_SR); 886119643Ssam} 887119643Ssam 888186531Sbz/* Deep-copy a policy in PCB. */ 889105197Ssamstatic struct secpolicy * 890186530Sbzipsec_deepcopy_policy(struct secpolicy *src) 891105197Ssam{ 892105197Ssam struct ipsecrequest *newchain = NULL; 893105197Ssam struct ipsecrequest *p; 894105197Ssam struct ipsecrequest **q; 895105197Ssam struct ipsecrequest *r; 896105197Ssam struct secpolicy *dst; 897105197Ssam 898105197Ssam if (src == NULL) 899186531Sbz return (NULL); 900105197Ssam dst = KEY_NEWSP(); 901105197Ssam if (dst == NULL) 902186531Sbz return (NULL); 903105197Ssam 904105197Ssam /* 905186531Sbz * Deep-copy IPsec request chain. This is required since struct 906105197Ssam * ipsecrequest is not reference counted. 907105197Ssam */ 908105197Ssam q = &newchain; 909105197Ssam for (p = src->req; p; p = p->next) { 910119643Ssam *q = ipsec_newisr(); 911105197Ssam if (*q == NULL) 912105197Ssam goto fail; 913105197Ssam (*q)->saidx.proto = p->saidx.proto; 914105197Ssam (*q)->saidx.mode = p->saidx.mode; 915105197Ssam (*q)->level = p->level; 916105197Ssam (*q)->saidx.reqid = p->saidx.reqid; 917105197Ssam 918105197Ssam bcopy(&p->saidx.src, &(*q)->saidx.src, sizeof((*q)->saidx.src)); 919105197Ssam bcopy(&p->saidx.dst, &(*q)->saidx.dst, sizeof((*q)->saidx.dst)); 920105197Ssam 921105197Ssam (*q)->sp = dst; 922105197Ssam 923105197Ssam q = &((*q)->next); 924105197Ssam } 925105197Ssam 926105197Ssam dst->req = newchain; 927105197Ssam dst->state = src->state; 928105197Ssam dst->policy = src->policy; 929186531Sbz /* Do not touch the refcnt fields. */ 930105197Ssam 931186531Sbz return (dst); 932105197Ssam 933105197Ssamfail: 934105197Ssam for (p = newchain; p; p = r) { 935105197Ssam r = p->next; 936119643Ssam ipsec_delisr(p); 937105197Ssam p = NULL; 938105197Ssam } 939186531Sbz return (NULL); 940105197Ssam} 941105197Ssam 942186531Sbz/* Set policy and IPsec request if present. */ 943105197Ssamstatic int 944188306Sbzipsec_set_policy_internal(struct secpolicy **pcb_sp, int optname, 945188306Sbz caddr_t request, size_t len, struct ucred *cred) 946105197Ssam{ 947105197Ssam struct sadb_x_policy *xpl; 948105197Ssam struct secpolicy *newsp = NULL; 949105197Ssam int error; 950105197Ssam 951186531Sbz /* Sanity check. */ 952105197Ssam if (pcb_sp == NULL || *pcb_sp == NULL || request == NULL) 953186531Sbz return (EINVAL); 954105197Ssam if (len < sizeof(*xpl)) 955186531Sbz return (EINVAL); 956105197Ssam xpl = (struct sadb_x_policy *)request; 957105197Ssam 958105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 959120585Ssam printf("%s: passed policy\n", __func__); 960105197Ssam kdebug_sadb_x_policy((struct sadb_ext *)xpl)); 961105197Ssam 962186531Sbz /* Check policy type. */ 963188306Sbz /* ipsec_set_policy_internal() accepts IPSEC, ENTRUST and BYPASS. */ 964105197Ssam if (xpl->sadb_x_policy_type == IPSEC_POLICY_DISCARD 965105197Ssam || xpl->sadb_x_policy_type == IPSEC_POLICY_NONE) 966186531Sbz return (EINVAL); 967105197Ssam 968186531Sbz /* Check privileged socket. */ 969175892Sbz if (cred != NULL && xpl->sadb_x_policy_type == IPSEC_POLICY_BYPASS) { 970175892Sbz error = priv_check_cred(cred, PRIV_NETINET_IPSEC, 0); 971175892Sbz if (error) 972186531Sbz return (EACCES); 973175892Sbz } 974105197Ssam 975186531Sbz /* Allocating new SP entry. */ 976105197Ssam if ((newsp = key_msg2sp(xpl, len, &error)) == NULL) 977186531Sbz return (error); 978105197Ssam 979105197Ssam newsp->state = IPSEC_SPSTATE_ALIVE; 980105197Ssam 981186531Sbz /* Clear old SP and set new SP. */ 982105197Ssam KEY_FREESP(pcb_sp); 983105197Ssam *pcb_sp = newsp; 984105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 985120585Ssam printf("%s: new policy\n", __func__); 986105197Ssam kdebug_secpolicy(newsp)); 987105197Ssam 988186531Sbz return (0); 989105197Ssam} 990105197Ssam 991105197Ssamint 992188306Sbzipsec_set_policy(struct inpcb *inp, int optname, caddr_t request, 993186530Sbz size_t len, struct ucred *cred) 994105197Ssam{ 995105197Ssam struct sadb_x_policy *xpl; 996105197Ssam struct secpolicy **pcb_sp; 997105197Ssam 998186531Sbz /* Sanity check. */ 999105197Ssam if (inp == NULL || request == NULL) 1000186531Sbz return (EINVAL); 1001105197Ssam if (len < sizeof(*xpl)) 1002186531Sbz return (EINVAL); 1003105197Ssam xpl = (struct sadb_x_policy *)request; 1004105197Ssam 1005186531Sbz /* Select direction. */ 1006105197Ssam switch (xpl->sadb_x_policy_dir) { 1007105197Ssam case IPSEC_DIR_INBOUND: 1008105197Ssam pcb_sp = &inp->inp_sp->sp_in; 1009105197Ssam break; 1010105197Ssam case IPSEC_DIR_OUTBOUND: 1011105197Ssam pcb_sp = &inp->inp_sp->sp_out; 1012105197Ssam break; 1013105197Ssam default: 1014120585Ssam ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__, 1015105197Ssam xpl->sadb_x_policy_dir)); 1016186531Sbz return (EINVAL); 1017105197Ssam } 1018105197Ssam 1019188306Sbz return (ipsec_set_policy_internal(pcb_sp, optname, request, len, cred)); 1020105197Ssam} 1021105197Ssam 1022105197Ssamint 1023188306Sbzipsec_get_policy(struct inpcb *inp, caddr_t request, size_t len, 1024186530Sbz struct mbuf **mp) 1025105197Ssam{ 1026105197Ssam struct sadb_x_policy *xpl; 1027105197Ssam struct secpolicy *pcb_sp; 1028105197Ssam 1029186531Sbz /* Sanity check. */ 1030105197Ssam if (inp == NULL || request == NULL || mp == NULL) 1031186531Sbz return (EINVAL); 1032120585Ssam IPSEC_ASSERT(inp->inp_sp != NULL, ("null inp_sp")); 1033105197Ssam if (len < sizeof(*xpl)) 1034186531Sbz return (EINVAL); 1035105197Ssam xpl = (struct sadb_x_policy *)request; 1036105197Ssam 1037186531Sbz /* Select direction. */ 1038105197Ssam switch (xpl->sadb_x_policy_dir) { 1039105197Ssam case IPSEC_DIR_INBOUND: 1040105197Ssam pcb_sp = inp->inp_sp->sp_in; 1041105197Ssam break; 1042105197Ssam case IPSEC_DIR_OUTBOUND: 1043105197Ssam pcb_sp = inp->inp_sp->sp_out; 1044105197Ssam break; 1045105197Ssam default: 1046120585Ssam ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__, 1047105197Ssam xpl->sadb_x_policy_dir)); 1048186531Sbz return (EINVAL); 1049105197Ssam } 1050105197Ssam 1051188306Sbz /* Sanity check. Should be an IPSEC_ASSERT. */ 1052188306Sbz if (pcb_sp == NULL) 1053188306Sbz return (EINVAL); 1054188306Sbz 1055188306Sbz *mp = key_sp2msg(pcb_sp); 1056188306Sbz if (!*mp) { 1057188306Sbz ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__)); 1058188306Sbz return (ENOBUFS); 1059188306Sbz } 1060188306Sbz 1061188306Sbz (*mp)->m_type = MT_DATA; 1062188306Sbz KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 1063188306Sbz printf("%s:\n", __func__); kdebug_mbuf(*mp)); 1064188306Sbz 1065188306Sbz return (0); 1066105197Ssam} 1067105197Ssam 1068186531Sbz/* Delete policy in PCB. */ 1069105197Ssamint 1070186530Sbzipsec_delete_pcbpolicy(struct inpcb *inp) 1071105197Ssam{ 1072120585Ssam IPSEC_ASSERT(inp != NULL, ("null inp")); 1073105197Ssam 1074105197Ssam if (inp->inp_sp == NULL) 1075186531Sbz return (0); 1076105197Ssam 1077105197Ssam if (inp->inp_sp->sp_in != NULL) 1078105197Ssam KEY_FREESP(&inp->inp_sp->sp_in); 1079105197Ssam 1080105197Ssam if (inp->inp_sp->sp_out != NULL) 1081105197Ssam KEY_FREESP(&inp->inp_sp->sp_out); 1082105197Ssam 1083105197Ssam ipsec_delpcbpolicy(inp->inp_sp); 1084105197Ssam inp->inp_sp = NULL; 1085105197Ssam 1086186531Sbz return (0); 1087105197Ssam} 1088105197Ssam 1089105197Ssam/* 1090186531Sbz * Return current level. 1091105197Ssam * Either IPSEC_LEVEL_USE or IPSEC_LEVEL_REQUIRE are always returned. 1092105197Ssam */ 1093105197Ssamu_int 1094186530Sbzipsec_get_reqlevel(struct ipsecrequest *isr) 1095105197Ssam{ 1096105197Ssam u_int level = 0; 1097105197Ssam u_int esp_trans_deflev, esp_net_deflev; 1098105197Ssam u_int ah_trans_deflev, ah_net_deflev; 1099105197Ssam 1100120585Ssam IPSEC_ASSERT(isr != NULL && isr->sp != NULL, ("null argument")); 1101120585Ssam IPSEC_ASSERT(isr->sp->spidx.src.sa.sa_family == isr->sp->spidx.dst.sa.sa_family, 1102120585Ssam ("af family mismatch, src %u, dst %u", 1103105197Ssam isr->sp->spidx.src.sa.sa_family, 1104105197Ssam isr->sp->spidx.dst.sa.sa_family)); 1105105197Ssam 1106186531Sbz/* XXX Note that we have ipseclog() expanded here - code sync issue. */ 1107105197Ssam#define IPSEC_CHECK_DEFAULT(lev) \ 1108105197Ssam (((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE \ 1109105197Ssam && (lev) != IPSEC_LEVEL_UNIQUE) \ 1110181803Sbz ? (V_ipsec_debug \ 1111105197Ssam ? log(LOG_INFO, "fixed system default level " #lev ":%d->%d\n",\ 1112105197Ssam (lev), IPSEC_LEVEL_REQUIRE) \ 1113105197Ssam : 0), \ 1114105197Ssam (lev) = IPSEC_LEVEL_REQUIRE, \ 1115105197Ssam (lev) \ 1116105197Ssam : (lev)) 1117105197Ssam 1118186531Sbz /* Set default level. */ 1119105197Ssam switch (((struct sockaddr *)&isr->sp->spidx.src)->sa_family) { 1120105197Ssam#ifdef INET 1121105197Ssam case AF_INET: 1122181803Sbz esp_trans_deflev = IPSEC_CHECK_DEFAULT(V_ip4_esp_trans_deflev); 1123181803Sbz esp_net_deflev = IPSEC_CHECK_DEFAULT(V_ip4_esp_net_deflev); 1124181803Sbz ah_trans_deflev = IPSEC_CHECK_DEFAULT(V_ip4_ah_trans_deflev); 1125181803Sbz ah_net_deflev = IPSEC_CHECK_DEFAULT(V_ip4_ah_net_deflev); 1126105197Ssam break; 1127105197Ssam#endif 1128105197Ssam#ifdef INET6 1129105197Ssam case AF_INET6: 1130181803Sbz esp_trans_deflev = IPSEC_CHECK_DEFAULT(V_ip6_esp_trans_deflev); 1131181803Sbz esp_net_deflev = IPSEC_CHECK_DEFAULT(V_ip6_esp_net_deflev); 1132181803Sbz ah_trans_deflev = IPSEC_CHECK_DEFAULT(V_ip6_ah_trans_deflev); 1133181803Sbz ah_net_deflev = IPSEC_CHECK_DEFAULT(V_ip6_ah_net_deflev); 1134105197Ssam break; 1135105197Ssam#endif /* INET6 */ 1136105197Ssam default: 1137120585Ssam panic("%s: unknown af %u", 1138120585Ssam __func__, isr->sp->spidx.src.sa.sa_family); 1139105197Ssam } 1140105197Ssam 1141105197Ssam#undef IPSEC_CHECK_DEFAULT 1142105197Ssam 1143186531Sbz /* Set level. */ 1144105197Ssam switch (isr->level) { 1145105197Ssam case IPSEC_LEVEL_DEFAULT: 1146105197Ssam switch (isr->saidx.proto) { 1147105197Ssam case IPPROTO_ESP: 1148105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) 1149105197Ssam level = esp_net_deflev; 1150105197Ssam else 1151105197Ssam level = esp_trans_deflev; 1152105197Ssam break; 1153105197Ssam case IPPROTO_AH: 1154105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) 1155105197Ssam level = ah_net_deflev; 1156105197Ssam else 1157105197Ssam level = ah_trans_deflev; 1158125100Ssam break; 1159105197Ssam case IPPROTO_IPCOMP: 1160105197Ssam /* 1161186531Sbz * We don't really care, as IPcomp document says that 1162186531Sbz * we shouldn't compress small packets. 1163105197Ssam */ 1164105197Ssam level = IPSEC_LEVEL_USE; 1165105197Ssam break; 1166105197Ssam default: 1167120585Ssam panic("%s: Illegal protocol defined %u\n", __func__, 1168105197Ssam isr->saidx.proto); 1169105197Ssam } 1170105197Ssam break; 1171105197Ssam 1172105197Ssam case IPSEC_LEVEL_USE: 1173105197Ssam case IPSEC_LEVEL_REQUIRE: 1174105197Ssam level = isr->level; 1175105197Ssam break; 1176105197Ssam case IPSEC_LEVEL_UNIQUE: 1177105197Ssam level = IPSEC_LEVEL_REQUIRE; 1178105197Ssam break; 1179105197Ssam 1180105197Ssam default: 1181120585Ssam panic("%s: Illegal IPsec level %u\n", __func__, isr->level); 1182105197Ssam } 1183105197Ssam 1184186531Sbz return (level); 1185105197Ssam} 1186105197Ssam 1187105197Ssam/* 1188105197Ssam * Check security policy requirements against the actual 1189105197Ssam * packet contents. Return one if the packet should be 1190105197Ssam * reject as "invalid"; otherwiser return zero to have the 1191105197Ssam * packet treated as "valid". 1192105197Ssam * 1193105197Ssam * OUT: 1194105197Ssam * 0: valid 1195105197Ssam * 1: invalid 1196105197Ssam */ 1197105197Ssamint 1198105197Ssamipsec_in_reject(struct secpolicy *sp, struct mbuf *m) 1199105197Ssam{ 1200105197Ssam struct ipsecrequest *isr; 1201105197Ssam int need_auth; 1202105197Ssam 1203105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 1204120585Ssam printf("%s: using SP\n", __func__); kdebug_secpolicy(sp)); 1205105197Ssam 1206186531Sbz /* Check policy. */ 1207105197Ssam switch (sp->policy) { 1208105197Ssam case IPSEC_POLICY_DISCARD: 1209186531Sbz return (1); 1210105197Ssam case IPSEC_POLICY_BYPASS: 1211105197Ssam case IPSEC_POLICY_NONE: 1212186531Sbz return (0); 1213105197Ssam } 1214105197Ssam 1215120585Ssam IPSEC_ASSERT(sp->policy == IPSEC_POLICY_IPSEC, 1216120585Ssam ("invalid policy %u", sp->policy)); 1217105197Ssam 1218186531Sbz /* XXX Should compare policy against IPsec header history. */ 1219105197Ssam 1220105197Ssam need_auth = 0; 1221105197Ssam for (isr = sp->req; isr != NULL; isr = isr->next) { 1222105197Ssam if (ipsec_get_reqlevel(isr) != IPSEC_LEVEL_REQUIRE) 1223105197Ssam continue; 1224105197Ssam switch (isr->saidx.proto) { 1225105197Ssam case IPPROTO_ESP: 1226105197Ssam if ((m->m_flags & M_DECRYPTED) == 0) { 1227105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 1228120585Ssam printf("%s: ESP m_flags:%x\n", __func__, 1229105197Ssam m->m_flags)); 1230186531Sbz return (1); 1231105197Ssam } 1232105197Ssam 1233105197Ssam if (!need_auth && 1234105197Ssam isr->sav != NULL && 1235105197Ssam isr->sav->tdb_authalgxform != NULL && 1236105197Ssam (m->m_flags & M_AUTHIPDGM) == 0) { 1237105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 1238120585Ssam printf("%s: ESP/AH m_flags:%x\n", __func__, 1239105197Ssam m->m_flags)); 1240186531Sbz return (1); 1241105197Ssam } 1242105197Ssam break; 1243105197Ssam case IPPROTO_AH: 1244105197Ssam need_auth = 1; 1245105197Ssam if ((m->m_flags & M_AUTHIPHDR) == 0) { 1246105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 1247120585Ssam printf("%s: AH m_flags:%x\n", __func__, 1248105197Ssam m->m_flags)); 1249186531Sbz return (1); 1250105197Ssam } 1251105197Ssam break; 1252105197Ssam case IPPROTO_IPCOMP: 1253105197Ssam /* 1254186531Sbz * We don't really care, as IPcomp document 1255105197Ssam * says that we shouldn't compress small 1256186531Sbz * packets. IPComp policy should always be 1257105197Ssam * treated as being in "use" level. 1258105197Ssam */ 1259105197Ssam break; 1260105197Ssam } 1261105197Ssam } 1262186531Sbz return (0); /* Valid. */ 1263105197Ssam} 1264105197Ssam 1265188306Sbzstatic int 1266188306Sbzipsec46_in_reject(struct mbuf *m, struct inpcb *inp) 1267105197Ssam{ 1268105197Ssam struct secpolicy *sp; 1269105197Ssam int error; 1270105197Ssam int result; 1271105197Ssam 1272120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 1273105197Ssam 1274186531Sbz /* 1275186531Sbz * Get SP for this packet. 1276105197Ssam * When we are called from ip_forward(), we call 1277105197Ssam * ipsec_getpolicybyaddr() with IP_FORWARDING flag. 1278105197Ssam */ 1279105197Ssam if (inp == NULL) 1280105197Ssam sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND, IP_FORWARDING, &error); 1281105197Ssam else 1282105197Ssam sp = ipsec_getpolicybysock(m, IPSEC_DIR_INBOUND, inp, &error); 1283105197Ssam 1284105197Ssam if (sp != NULL) { 1285105197Ssam result = ipsec_in_reject(sp, m); 1286105197Ssam KEY_FREESP(&sp); 1287105197Ssam } else { 1288186531Sbz result = 0; /* XXX Should be panic? 1289105197Ssam * -> No, there may be error. */ 1290105197Ssam } 1291186531Sbz return (result); 1292105197Ssam} 1293105197Ssam 1294188306Sbz/* 1295188306Sbz * Check AH/ESP integrity. 1296188306Sbz * This function is called from tcp_input(), udp_input(), 1297188306Sbz * and {ah,esp}4_input for tunnel mode. 1298188306Sbz */ 1299188306Sbzint 1300188306Sbzipsec4_in_reject(struct mbuf *m, struct inpcb *inp) 1301188306Sbz{ 1302188306Sbz int result; 1303188306Sbz 1304188306Sbz result = ipsec46_in_reject(m, inp); 1305188306Sbz if (result) 1306252692Sae IPSECSTAT_INC(ips_in_polvio); 1307188306Sbz 1308188306Sbz return (result); 1309188306Sbz} 1310188306Sbz 1311105197Ssam#ifdef INET6 1312105197Ssam/* 1313105197Ssam * Check AH/ESP integrity. 1314105197Ssam * This function is called from tcp6_input(), udp6_input(), 1315186531Sbz * and {ah,esp}6_input for tunnel mode. 1316105197Ssam */ 1317105197Ssamint 1318186530Sbzipsec6_in_reject(struct mbuf *m, struct inpcb *inp) 1319105197Ssam{ 1320105197Ssam int result; 1321105197Ssam 1322188306Sbz result = ipsec46_in_reject(m, inp); 1323188306Sbz if (result) 1324252692Sae IPSEC6STAT_INC(ips_in_polvio); 1325105197Ssam 1326186531Sbz return (result); 1327105197Ssam} 1328105197Ssam#endif 1329105197Ssam 1330105197Ssam/* 1331186531Sbz * Compute the byte size to be occupied by IPsec header. 1332186531Sbz * In case it is tunnelled, it includes the size of outer IP header. 1333186531Sbz * NOTE: SP passed is freed in this function. 1334105197Ssam */ 1335105197Ssamstatic size_t 1336188306Sbzipsec_hdrsiz_internal(struct secpolicy *sp) 1337105197Ssam{ 1338105197Ssam struct ipsecrequest *isr; 1339186532Sbz size_t size; 1340105197Ssam 1341105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 1342120585Ssam printf("%s: using SP\n", __func__); kdebug_secpolicy(sp)); 1343105197Ssam 1344105197Ssam switch (sp->policy) { 1345105197Ssam case IPSEC_POLICY_DISCARD: 1346105197Ssam case IPSEC_POLICY_BYPASS: 1347105197Ssam case IPSEC_POLICY_NONE: 1348186531Sbz return (0); 1349105197Ssam } 1350105197Ssam 1351120585Ssam IPSEC_ASSERT(sp->policy == IPSEC_POLICY_IPSEC, 1352120585Ssam ("invalid policy %u", sp->policy)); 1353105197Ssam 1354186532Sbz size = 0; 1355105197Ssam for (isr = sp->req; isr != NULL; isr = isr->next) { 1356105197Ssam size_t clen = 0; 1357105197Ssam 1358105197Ssam switch (isr->saidx.proto) { 1359105197Ssam case IPPROTO_ESP: 1360105197Ssam clen = esp_hdrsiz(isr->sav); 1361105197Ssam break; 1362105197Ssam case IPPROTO_AH: 1363105197Ssam clen = ah_hdrsiz(isr->sav); 1364105197Ssam break; 1365105197Ssam case IPPROTO_IPCOMP: 1366105197Ssam clen = sizeof(struct ipcomp); 1367105197Ssam break; 1368105197Ssam } 1369105197Ssam 1370105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { 1371105197Ssam switch (isr->saidx.dst.sa.sa_family) { 1372105197Ssam case AF_INET: 1373105197Ssam clen += sizeof(struct ip); 1374105197Ssam break; 1375105197Ssam#ifdef INET6 1376105197Ssam case AF_INET6: 1377105197Ssam clen += sizeof(struct ip6_hdr); 1378105197Ssam break; 1379105197Ssam#endif 1380105197Ssam default: 1381120585Ssam ipseclog((LOG_ERR, "%s: unknown AF %d in " 1382120585Ssam "IPsec tunnel SA\n", __func__, 1383105197Ssam ((struct sockaddr *)&isr->saidx.dst)->sa_family)); 1384105197Ssam break; 1385105197Ssam } 1386105197Ssam } 1387186532Sbz size += clen; 1388105197Ssam } 1389105197Ssam 1390186532Sbz return (size); 1391105197Ssam} 1392105197Ssam 1393188306Sbz/* 1394188306Sbz * This function is called from ipsec_hdrsiz_tcp(), ip_ipsec_mtu(), 1395188306Sbz * disabled ip6_ipsec_mtu() and ip6_forward(). 1396188306Sbz */ 1397105197Ssamsize_t 1398188306Sbzipsec_hdrsiz(struct mbuf *m, u_int dir, struct inpcb *inp) 1399105197Ssam{ 1400105197Ssam struct secpolicy *sp; 1401105197Ssam int error; 1402105197Ssam size_t size; 1403105197Ssam 1404120585Ssam IPSEC_ASSERT(m != NULL, ("null mbuf")); 1405105197Ssam 1406186531Sbz /* Get SP for this packet. 1407105197Ssam * When we are called from ip_forward(), we call 1408105197Ssam * ipsec_getpolicybyaddr() with IP_FORWARDING flag. 1409105197Ssam */ 1410105197Ssam if (inp == NULL) 1411105197Ssam sp = ipsec_getpolicybyaddr(m, dir, IP_FORWARDING, &error); 1412105197Ssam else 1413105197Ssam sp = ipsec_getpolicybysock(m, dir, inp, &error); 1414105197Ssam 1415105197Ssam if (sp != NULL) { 1416188306Sbz size = ipsec_hdrsiz_internal(sp); 1417105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 1418120585Ssam printf("%s: size:%lu.\n", __func__, 1419105197Ssam (unsigned long)size)); 1420105197Ssam 1421105197Ssam KEY_FREESP(&sp); 1422105197Ssam } else { 1423186531Sbz size = 0; /* XXX Should be panic? 1424174038Sbz * -> No, we are called w/o knowing if 1425174038Sbz * IPsec processing is needed. */ 1426105197Ssam } 1427186531Sbz return (size); 1428105197Ssam} 1429105197Ssam 1430105197Ssam/* 1431105197Ssam * Check the variable replay window. 1432105197Ssam * ipsec_chkreplay() performs replay check before ICV verification. 1433105197Ssam * ipsec_updatereplay() updates replay bitmap. This must be called after 1434105197Ssam * ICV verification (it also performs replay check, which is usually done 1435105197Ssam * beforehand). 1436105197Ssam * 0 (zero) is returned if packet disallowed, 1 if packet permitted. 1437105197Ssam * 1438186531Sbz * Based on RFC 2401. 1439105197Ssam */ 1440105197Ssamint 1441186530Sbzipsec_chkreplay(u_int32_t seq, struct secasvar *sav) 1442105197Ssam{ 1443105197Ssam const struct secreplay *replay; 1444105197Ssam u_int32_t diff; 1445105197Ssam int fr; 1446186531Sbz u_int32_t wsizeb; /* Constant: bits of window size. */ 1447186531Sbz int frlast; /* Constant: last frame. */ 1448105197Ssam 1449120585Ssam IPSEC_ASSERT(sav != NULL, ("Null SA")); 1450120585Ssam IPSEC_ASSERT(sav->replay != NULL, ("Null replay state")); 1451105197Ssam 1452105197Ssam replay = sav->replay; 1453105197Ssam 1454105197Ssam if (replay->wsize == 0) 1455186531Sbz return (1); /* No need to check replay. */ 1456105197Ssam 1457186531Sbz /* Constant. */ 1458105197Ssam frlast = replay->wsize - 1; 1459105197Ssam wsizeb = replay->wsize << 3; 1460105197Ssam 1461186531Sbz /* Sequence number of 0 is invalid. */ 1462105197Ssam if (seq == 0) 1463186531Sbz return (0); 1464105197Ssam 1465186531Sbz /* First time is always okay. */ 1466105197Ssam if (replay->count == 0) 1467186531Sbz return (1); 1468105197Ssam 1469105197Ssam if (seq > replay->lastseq) { 1470186531Sbz /* Larger sequences are okay. */ 1471186531Sbz return (1); 1472105197Ssam } else { 1473105197Ssam /* seq is equal or less than lastseq. */ 1474105197Ssam diff = replay->lastseq - seq; 1475105197Ssam 1476186531Sbz /* Over range to check, i.e. too old or wrapped. */ 1477105197Ssam if (diff >= wsizeb) 1478186531Sbz return (0); 1479105197Ssam 1480105197Ssam fr = frlast - diff / 8; 1481105197Ssam 1482186531Sbz /* This packet already seen? */ 1483105197Ssam if ((replay->bitmap)[fr] & (1 << (diff % 8))) 1484186531Sbz return (0); 1485105197Ssam 1486186531Sbz /* Out of order but good. */ 1487186531Sbz return (1); 1488105197Ssam } 1489105197Ssam} 1490105197Ssam 1491105197Ssam/* 1492186531Sbz * Check replay counter whether to update or not. 1493105197Ssam * OUT: 0: OK 1494105197Ssam * 1: NG 1495105197Ssam */ 1496105197Ssamint 1497186530Sbzipsec_updatereplay(u_int32_t seq, struct secasvar *sav) 1498105197Ssam{ 1499105197Ssam struct secreplay *replay; 1500105197Ssam u_int32_t diff; 1501105197Ssam int fr; 1502186531Sbz u_int32_t wsizeb; /* Constant: bits of window size. */ 1503186531Sbz int frlast; /* Constant: last frame. */ 1504105197Ssam 1505120585Ssam IPSEC_ASSERT(sav != NULL, ("Null SA")); 1506120585Ssam IPSEC_ASSERT(sav->replay != NULL, ("Null replay state")); 1507105197Ssam 1508105197Ssam replay = sav->replay; 1509105197Ssam 1510105197Ssam if (replay->wsize == 0) 1511186531Sbz goto ok; /* No need to check replay. */ 1512105197Ssam 1513186531Sbz /* Constant. */ 1514105197Ssam frlast = replay->wsize - 1; 1515105197Ssam wsizeb = replay->wsize << 3; 1516105197Ssam 1517186531Sbz /* Sequence number of 0 is invalid. */ 1518105197Ssam if (seq == 0) 1519186531Sbz return (1); 1520105197Ssam 1521186531Sbz /* First time. */ 1522105197Ssam if (replay->count == 0) { 1523105197Ssam replay->lastseq = seq; 1524105197Ssam bzero(replay->bitmap, replay->wsize); 1525105197Ssam (replay->bitmap)[frlast] = 1; 1526105197Ssam goto ok; 1527105197Ssam } 1528105197Ssam 1529105197Ssam if (seq > replay->lastseq) { 1530105197Ssam /* seq is larger than lastseq. */ 1531105197Ssam diff = seq - replay->lastseq; 1532105197Ssam 1533186531Sbz /* New larger sequence number. */ 1534105197Ssam if (diff < wsizeb) { 1535186531Sbz /* In window. */ 1536186531Sbz /* Set bit for this packet. */ 1537105197Ssam vshiftl(replay->bitmap, diff, replay->wsize); 1538105197Ssam (replay->bitmap)[frlast] |= 1; 1539105197Ssam } else { 1540186531Sbz /* This packet has a "way larger". */ 1541105197Ssam bzero(replay->bitmap, replay->wsize); 1542105197Ssam (replay->bitmap)[frlast] = 1; 1543105197Ssam } 1544105197Ssam replay->lastseq = seq; 1545105197Ssam 1546186531Sbz /* Larger is good. */ 1547105197Ssam } else { 1548105197Ssam /* seq is equal or less than lastseq. */ 1549105197Ssam diff = replay->lastseq - seq; 1550105197Ssam 1551186531Sbz /* Over range to check, i.e. too old or wrapped. */ 1552105197Ssam if (diff >= wsizeb) 1553186531Sbz return (1); 1554105197Ssam 1555105197Ssam fr = frlast - diff / 8; 1556105197Ssam 1557186531Sbz /* This packet already seen? */ 1558105197Ssam if ((replay->bitmap)[fr] & (1 << (diff % 8))) 1559186531Sbz return (1); 1560105197Ssam 1561186531Sbz /* Mark as seen. */ 1562105197Ssam (replay->bitmap)[fr] |= (1 << (diff % 8)); 1563105197Ssam 1564186531Sbz /* Out of order but good. */ 1565105197Ssam } 1566105197Ssam 1567105197Ssamok: 1568105197Ssam if (replay->count == ~0) { 1569105197Ssam 1570186531Sbz /* Set overflow flag. */ 1571105197Ssam replay->overflow++; 1572105197Ssam 1573186531Sbz /* Don't increment, no more packets accepted. */ 1574105197Ssam if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) 1575186531Sbz return (1); 1576105197Ssam 1577120585Ssam ipseclog((LOG_WARNING, "%s: replay counter made %d cycle. %s\n", 1578120585Ssam __func__, replay->overflow, ipsec_logsastr(sav))); 1579105197Ssam } 1580105197Ssam 1581105197Ssam replay->count++; 1582105197Ssam 1583186531Sbz return (0); 1584105197Ssam} 1585105197Ssam 1586105197Ssam/* 1587186531Sbz * Shift variable length buffer to left. 1588105197Ssam * IN: bitmap: pointer to the buffer 1589105197Ssam * nbit: the number of to shift. 1590105197Ssam * wsize: buffer size (bytes). 1591105197Ssam */ 1592105197Ssamstatic void 1593186530Sbzvshiftl(unsigned char *bitmap, int nbit, int wsize) 1594105197Ssam{ 1595105197Ssam int s, j, i; 1596105197Ssam unsigned char over; 1597105197Ssam 1598105197Ssam for (j = 0; j < nbit; j += 8) { 1599105197Ssam s = (nbit - j < 8) ? (nbit - j): 8; 1600105197Ssam bitmap[0] <<= s; 1601105197Ssam for (i = 1; i < wsize; i++) { 1602105197Ssam over = (bitmap[i] >> (8 - s)); 1603105197Ssam bitmap[i] <<= s; 1604105197Ssam bitmap[i-1] |= over; 1605105197Ssam } 1606105197Ssam } 1607105197Ssam} 1608105197Ssam 1609193947Sbz#ifdef INET 1610105197Ssam/* Return a printable string for the IPv4 address. */ 1611105197Ssamstatic char * 1612105197Ssaminet_ntoa4(struct in_addr ina) 1613105197Ssam{ 1614105197Ssam static char buf[4][4 * sizeof "123" + 4]; 1615105197Ssam unsigned char *ucp = (unsigned char *) &ina; 1616105197Ssam static int i = 3; 1617105197Ssam 1618186531Sbz /* XXX-BZ Returns static buffer. */ 1619105197Ssam i = (i + 1) % 4; 1620105197Ssam sprintf(buf[i], "%d.%d.%d.%d", ucp[0] & 0xff, ucp[1] & 0xff, 1621105197Ssam ucp[2] & 0xff, ucp[3] & 0xff); 1622105197Ssam return (buf[i]); 1623105197Ssam} 1624193947Sbz#endif 1625105197Ssam 1626105197Ssam/* Return a printable string for the address. */ 1627105197Ssamchar * 1628105197Ssamipsec_address(union sockaddr_union* sa) 1629105197Ssam{ 1630165222Sbz#ifdef INET6 1631165118Sbz char ip6buf[INET6_ADDRSTRLEN]; 1632165118Sbz#endif 1633186531Sbz 1634105197Ssam switch (sa->sa.sa_family) { 1635159237Spjd#ifdef INET 1636105197Ssam case AF_INET: 1637186531Sbz return (inet_ntoa4(sa->sin.sin_addr)); 1638105197Ssam#endif /* INET */ 1639159237Spjd#ifdef INET6 1640105197Ssam case AF_INET6: 1641186531Sbz return (ip6_sprintf(ip6buf, &sa->sin6.sin6_addr)); 1642105197Ssam#endif /* INET6 */ 1643105197Ssam default: 1644186531Sbz return ("(unknown address family)"); 1645105197Ssam } 1646105197Ssam} 1647105197Ssam 1648105197Ssamconst char * 1649186530Sbzipsec_logsastr(struct secasvar *sav) 1650105197Ssam{ 1651105197Ssam static char buf[256]; 1652105197Ssam char *p; 1653105197Ssam struct secasindex *saidx = &sav->sah->saidx; 1654105197Ssam 1655120585Ssam IPSEC_ASSERT(saidx->src.sa.sa_family == saidx->dst.sa.sa_family, 1656120585Ssam ("address family mismatch")); 1657105197Ssam 1658105197Ssam p = buf; 1659105197Ssam snprintf(buf, sizeof(buf), "SA(SPI=%u ", (u_int32_t)ntohl(sav->spi)); 1660105197Ssam while (p && *p) 1661105197Ssam p++; 1662186531Sbz /* NB: only use ipsec_address on one address at a time. */ 1663105197Ssam snprintf(p, sizeof (buf) - (p - buf), "src=%s ", 1664105197Ssam ipsec_address(&saidx->src)); 1665105197Ssam while (p && *p) 1666105197Ssam p++; 1667105197Ssam snprintf(p, sizeof (buf) - (p - buf), "dst=%s)", 1668105197Ssam ipsec_address(&saidx->dst)); 1669105197Ssam 1670186531Sbz return (buf); 1671105197Ssam} 1672105197Ssam 1673105197Ssamvoid 1674186530Sbzipsec_dumpmbuf(struct mbuf *m) 1675105197Ssam{ 1676105197Ssam int totlen; 1677105197Ssam int i; 1678105197Ssam u_char *p; 1679105197Ssam 1680105197Ssam totlen = 0; 1681105197Ssam printf("---\n"); 1682105197Ssam while (m) { 1683105197Ssam p = mtod(m, u_char *); 1684105197Ssam for (i = 0; i < m->m_len; i++) { 1685105197Ssam printf("%02x ", p[i]); 1686105197Ssam totlen++; 1687105197Ssam if (totlen % 16 == 0) 1688105197Ssam printf("\n"); 1689105197Ssam } 1690105197Ssam m = m->m_next; 1691105197Ssam } 1692105197Ssam if (totlen % 16 != 0) 1693105197Ssam printf("\n"); 1694105197Ssam printf("---\n"); 1695105197Ssam} 1696105197Ssam 1697125100Ssamstatic void 1698195837Srwatsonipsec_init(const void *unused __unused) 1699125100Ssam{ 1700186531Sbz 1701181803Sbz SECPOLICY_LOCK_INIT(&V_ip4_def_policy); 1702186531Sbz V_ip4_def_policy.refcnt = 1; /* NB: disallow free. */ 1703125100Ssam} 1704195837SrwatsonVNET_SYSINIT(ipsec_init, SI_SUB_PROTO_DOMAININIT, SI_ORDER_ANY, ipsec_init, 1705195837Srwatson NULL); 1706193731Szec 1707125100Ssam 1708186531Sbz/* XXX This stuff doesn't belong here... */ 1709105197Ssam 1710105197Ssamstatic struct xformsw* xforms = NULL; 1711105197Ssam 1712105197Ssam/* 1713105197Ssam * Register a transform; typically at system startup. 1714105197Ssam */ 1715105197Ssamvoid 1716105197Ssamxform_register(struct xformsw* xsp) 1717105197Ssam{ 1718186531Sbz 1719105197Ssam xsp->xf_next = xforms; 1720105197Ssam xforms = xsp; 1721105197Ssam} 1722105197Ssam 1723105197Ssam/* 1724105197Ssam * Initialize transform support in an sav. 1725105197Ssam */ 1726105197Ssamint 1727105197Ssamxform_init(struct secasvar *sav, int xftype) 1728105197Ssam{ 1729105197Ssam struct xformsw *xsp; 1730105197Ssam 1731186531Sbz if (sav->tdb_xform != NULL) /* Previously initialized. */ 1732186531Sbz return (0); 1733105197Ssam for (xsp = xforms; xsp; xsp = xsp->xf_next) 1734105197Ssam if (xsp->xf_type == xftype) 1735186531Sbz return ((*xsp->xf_init)(sav, xsp)); 1736186531Sbz return (EINVAL); 1737105197Ssam} 1738