1*fcf59617SAndrey V. Elsukov /*- 2*fcf59617SAndrey V. Elsukov * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org> 3*fcf59617SAndrey V. Elsukov * All rights reserved. 4*fcf59617SAndrey V. Elsukov * 5*fcf59617SAndrey V. Elsukov * Redistribution and use in source and binary forms, with or without 6*fcf59617SAndrey V. Elsukov * modification, are permitted provided that the following conditions 7*fcf59617SAndrey V. Elsukov * are met: 8*fcf59617SAndrey V. Elsukov * 9*fcf59617SAndrey V. Elsukov * 1. Redistributions of source code must retain the above copyright 10*fcf59617SAndrey V. Elsukov * notice, this list of conditions and the following disclaimer. 11*fcf59617SAndrey V. Elsukov * 2. Redistributions in binary form must reproduce the above copyright 12*fcf59617SAndrey V. Elsukov * notice, this list of conditions and the following disclaimer in the 13*fcf59617SAndrey V. Elsukov * documentation and/or other materials provided with the distribution. 14*fcf59617SAndrey V. Elsukov * 15*fcf59617SAndrey V. Elsukov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16*fcf59617SAndrey V. Elsukov * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17*fcf59617SAndrey V. Elsukov * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18*fcf59617SAndrey V. Elsukov * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19*fcf59617SAndrey V. Elsukov * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20*fcf59617SAndrey V. Elsukov * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21*fcf59617SAndrey V. Elsukov * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22*fcf59617SAndrey V. Elsukov * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23*fcf59617SAndrey V. Elsukov * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24*fcf59617SAndrey V. Elsukov * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25*fcf59617SAndrey V. Elsukov */ 26*fcf59617SAndrey V. Elsukov 27*fcf59617SAndrey V. Elsukov #include <sys/cdefs.h> 28*fcf59617SAndrey V. Elsukov __FBSDID("$FreeBSD$"); 29*fcf59617SAndrey V. Elsukov 30*fcf59617SAndrey V. Elsukov #include "opt_inet.h" 31*fcf59617SAndrey V. Elsukov #include "opt_inet6.h" 32*fcf59617SAndrey V. Elsukov #include "opt_ipsec.h" 33*fcf59617SAndrey V. Elsukov 34*fcf59617SAndrey V. Elsukov #include <sys/param.h> 35*fcf59617SAndrey V. Elsukov #include <sys/systm.h> 36*fcf59617SAndrey V. Elsukov #include <sys/kernel.h> 37*fcf59617SAndrey V. Elsukov #include <sys/lock.h> 38*fcf59617SAndrey V. Elsukov #include <sys/malloc.h> 39*fcf59617SAndrey V. Elsukov #include <sys/mbuf.h> 40*fcf59617SAndrey V. Elsukov #include <sys/priv.h> 41*fcf59617SAndrey V. Elsukov #include <sys/socket.h> 42*fcf59617SAndrey V. Elsukov #include <sys/sockopt.h> 43*fcf59617SAndrey V. Elsukov #include <sys/syslog.h> 44*fcf59617SAndrey V. Elsukov #include <sys/proc.h> 45*fcf59617SAndrey V. Elsukov 46*fcf59617SAndrey V. Elsukov #include <netinet/in.h> 47*fcf59617SAndrey V. Elsukov #include <netinet/in_pcb.h> 48*fcf59617SAndrey V. Elsukov 49*fcf59617SAndrey V. Elsukov #include <netipsec/ipsec.h> 50*fcf59617SAndrey V. Elsukov #include <netipsec/ipsec6.h> 51*fcf59617SAndrey V. Elsukov #include <netipsec/ipsec_support.h> 52*fcf59617SAndrey V. Elsukov #include <netipsec/key.h> 53*fcf59617SAndrey V. Elsukov #include <netipsec/key_debug.h> 54*fcf59617SAndrey V. Elsukov 55*fcf59617SAndrey V. Elsukov MALLOC_DEFINE(M_IPSEC_INPCB, "inpcbpolicy", "inpcb-resident ipsec policy"); 56*fcf59617SAndrey V. Elsukov 57*fcf59617SAndrey V. Elsukov static void 58*fcf59617SAndrey V. Elsukov ipsec_setsockaddrs_inpcb(struct inpcb *inp, union sockaddr_union *src, 59*fcf59617SAndrey V. Elsukov union sockaddr_union *dst, u_int dir) 60*fcf59617SAndrey V. Elsukov { 61*fcf59617SAndrey V. Elsukov 62*fcf59617SAndrey V. Elsukov #ifdef INET6 63*fcf59617SAndrey V. Elsukov if (inp->inp_vflag & INP_IPV6) { 64*fcf59617SAndrey V. Elsukov struct sockaddr_in6 *sin6; 65*fcf59617SAndrey V. Elsukov 66*fcf59617SAndrey V. Elsukov bzero(&src->sin6, sizeof(src->sin6)); 67*fcf59617SAndrey V. Elsukov bzero(&dst->sin6, sizeof(dst->sin6)); 68*fcf59617SAndrey V. Elsukov src->sin6.sin6_family = AF_INET6; 69*fcf59617SAndrey V. Elsukov src->sin6.sin6_len = sizeof(struct sockaddr_in6); 70*fcf59617SAndrey V. Elsukov dst->sin6.sin6_family = AF_INET6; 71*fcf59617SAndrey V. Elsukov dst->sin6.sin6_len = sizeof(struct sockaddr_in6); 72*fcf59617SAndrey V. Elsukov 73*fcf59617SAndrey V. Elsukov if (dir == IPSEC_DIR_OUTBOUND) 74*fcf59617SAndrey V. Elsukov sin6 = &src->sin6; 75*fcf59617SAndrey V. Elsukov else 76*fcf59617SAndrey V. Elsukov sin6 = &dst->sin6; 77*fcf59617SAndrey V. Elsukov sin6->sin6_addr = inp->in6p_laddr; 78*fcf59617SAndrey V. Elsukov sin6->sin6_port = inp->inp_lport; 79*fcf59617SAndrey V. Elsukov if (IN6_IS_SCOPE_LINKLOCAL(&inp->in6p_laddr)) { 80*fcf59617SAndrey V. Elsukov /* XXXAE: use in6p_zoneid */ 81*fcf59617SAndrey V. Elsukov sin6->sin6_addr.s6_addr16[1] = 0; 82*fcf59617SAndrey V. Elsukov sin6->sin6_scope_id = ntohs( 83*fcf59617SAndrey V. Elsukov inp->in6p_laddr.s6_addr16[1]); 84*fcf59617SAndrey V. Elsukov } 85*fcf59617SAndrey V. Elsukov 86*fcf59617SAndrey V. Elsukov if (dir == IPSEC_DIR_OUTBOUND) 87*fcf59617SAndrey V. Elsukov sin6 = &dst->sin6; 88*fcf59617SAndrey V. Elsukov else 89*fcf59617SAndrey V. Elsukov sin6 = &src->sin6; 90*fcf59617SAndrey V. Elsukov sin6->sin6_addr = inp->in6p_faddr; 91*fcf59617SAndrey V. Elsukov sin6->sin6_port = inp->inp_fport; 92*fcf59617SAndrey V. Elsukov if (IN6_IS_SCOPE_LINKLOCAL(&inp->in6p_faddr)) { 93*fcf59617SAndrey V. Elsukov /* XXXAE: use in6p_zoneid */ 94*fcf59617SAndrey V. Elsukov sin6->sin6_addr.s6_addr16[1] = 0; 95*fcf59617SAndrey V. Elsukov sin6->sin6_scope_id = ntohs( 96*fcf59617SAndrey V. Elsukov inp->in6p_faddr.s6_addr16[1]); 97*fcf59617SAndrey V. Elsukov } 98*fcf59617SAndrey V. Elsukov } 99*fcf59617SAndrey V. Elsukov #endif 100*fcf59617SAndrey V. Elsukov #ifdef INET 101*fcf59617SAndrey V. Elsukov if (inp->inp_vflag & INP_IPV4) { 102*fcf59617SAndrey V. Elsukov struct sockaddr_in *sin; 103*fcf59617SAndrey V. Elsukov 104*fcf59617SAndrey V. Elsukov bzero(&src->sin, sizeof(src->sin)); 105*fcf59617SAndrey V. Elsukov bzero(&dst->sin, sizeof(dst->sin)); 106*fcf59617SAndrey V. Elsukov src->sin.sin_family = AF_INET; 107*fcf59617SAndrey V. Elsukov src->sin.sin_len = sizeof(struct sockaddr_in); 108*fcf59617SAndrey V. Elsukov dst->sin.sin_family = AF_INET; 109*fcf59617SAndrey V. Elsukov dst->sin.sin_len = sizeof(struct sockaddr_in); 110*fcf59617SAndrey V. Elsukov 111*fcf59617SAndrey V. Elsukov if (dir == IPSEC_DIR_OUTBOUND) 112*fcf59617SAndrey V. Elsukov sin = &src->sin; 113*fcf59617SAndrey V. Elsukov else 114*fcf59617SAndrey V. Elsukov sin = &dst->sin; 115*fcf59617SAndrey V. Elsukov sin->sin_addr = inp->inp_laddr; 116*fcf59617SAndrey V. Elsukov sin->sin_port = inp->inp_lport; 117*fcf59617SAndrey V. Elsukov 118*fcf59617SAndrey V. Elsukov if (dir == IPSEC_DIR_OUTBOUND) 119*fcf59617SAndrey V. Elsukov sin = &dst->sin; 120*fcf59617SAndrey V. Elsukov else 121*fcf59617SAndrey V. Elsukov sin = &src->sin; 122*fcf59617SAndrey V. Elsukov sin->sin_addr = inp->inp_faddr; 123*fcf59617SAndrey V. Elsukov sin->sin_port = inp->inp_fport; 124*fcf59617SAndrey V. Elsukov } 125*fcf59617SAndrey V. Elsukov #endif 126*fcf59617SAndrey V. Elsukov } 127*fcf59617SAndrey V. Elsukov 128*fcf59617SAndrey V. Elsukov void 129*fcf59617SAndrey V. Elsukov ipsec_setspidx_inpcb(struct inpcb *inp, struct secpolicyindex *spidx, 130*fcf59617SAndrey V. Elsukov u_int dir) 131*fcf59617SAndrey V. Elsukov { 132*fcf59617SAndrey V. Elsukov 133*fcf59617SAndrey V. Elsukov ipsec_setsockaddrs_inpcb(inp, &spidx->src, &spidx->dst, dir); 134*fcf59617SAndrey V. Elsukov #ifdef INET6 135*fcf59617SAndrey V. Elsukov if (inp->inp_vflag & INP_IPV6) { 136*fcf59617SAndrey V. Elsukov spidx->prefs = sizeof(struct in6_addr) << 3; 137*fcf59617SAndrey V. Elsukov spidx->prefd = sizeof(struct in6_addr) << 3; 138*fcf59617SAndrey V. Elsukov } 139*fcf59617SAndrey V. Elsukov #endif 140*fcf59617SAndrey V. Elsukov #ifdef INET 141*fcf59617SAndrey V. Elsukov if (inp->inp_vflag & INP_IPV4) { 142*fcf59617SAndrey V. Elsukov spidx->prefs = sizeof(struct in_addr) << 3; 143*fcf59617SAndrey V. Elsukov spidx->prefd = sizeof(struct in_addr) << 3; 144*fcf59617SAndrey V. Elsukov } 145*fcf59617SAndrey V. Elsukov #endif 146*fcf59617SAndrey V. Elsukov spidx->ul_proto = IPPROTO_TCP; /* XXX: currently only TCP uses this */ 147*fcf59617SAndrey V. Elsukov spidx->dir = dir; 148*fcf59617SAndrey V. Elsukov KEYDBG(IPSEC_DUMP, 149*fcf59617SAndrey V. Elsukov printf("%s: ", __func__); kdebug_secpolicyindex(spidx, NULL)); 150*fcf59617SAndrey V. Elsukov } 151*fcf59617SAndrey V. Elsukov 152*fcf59617SAndrey V. Elsukov /* Initialize PCB policy. */ 153*fcf59617SAndrey V. Elsukov int 154*fcf59617SAndrey V. Elsukov ipsec_init_pcbpolicy(struct inpcb *inp) 155*fcf59617SAndrey V. Elsukov { 156*fcf59617SAndrey V. Elsukov 157*fcf59617SAndrey V. Elsukov IPSEC_ASSERT(inp != NULL, ("null inp")); 158*fcf59617SAndrey V. Elsukov IPSEC_ASSERT(inp->inp_sp == NULL, ("inp_sp already initialized")); 159*fcf59617SAndrey V. Elsukov 160*fcf59617SAndrey V. Elsukov inp->inp_sp = malloc(sizeof(struct inpcbpolicy), M_IPSEC_INPCB, 161*fcf59617SAndrey V. Elsukov M_NOWAIT | M_ZERO); 162*fcf59617SAndrey V. Elsukov if (inp->inp_sp == NULL) 163*fcf59617SAndrey V. Elsukov return (ENOBUFS); 164*fcf59617SAndrey V. Elsukov return (0); 165*fcf59617SAndrey V. Elsukov } 166*fcf59617SAndrey V. Elsukov 167*fcf59617SAndrey V. Elsukov /* Delete PCB policy. */ 168*fcf59617SAndrey V. Elsukov int 169*fcf59617SAndrey V. Elsukov ipsec_delete_pcbpolicy(struct inpcb *inp) 170*fcf59617SAndrey V. Elsukov { 171*fcf59617SAndrey V. Elsukov 172*fcf59617SAndrey V. Elsukov if (inp->inp_sp == NULL) 173*fcf59617SAndrey V. Elsukov return (0); 174*fcf59617SAndrey V. Elsukov 175*fcf59617SAndrey V. Elsukov if (inp->inp_sp->flags & INP_INBOUND_POLICY) 176*fcf59617SAndrey V. Elsukov key_freesp(&inp->inp_sp->sp_in); 177*fcf59617SAndrey V. Elsukov 178*fcf59617SAndrey V. Elsukov if (inp->inp_sp->flags & INP_OUTBOUND_POLICY) 179*fcf59617SAndrey V. Elsukov key_freesp(&inp->inp_sp->sp_out); 180*fcf59617SAndrey V. Elsukov 181*fcf59617SAndrey V. Elsukov free(inp->inp_sp, M_IPSEC_INPCB); 182*fcf59617SAndrey V. Elsukov inp->inp_sp = NULL; 183*fcf59617SAndrey V. Elsukov return (0); 184*fcf59617SAndrey V. Elsukov } 185*fcf59617SAndrey V. Elsukov 186*fcf59617SAndrey V. Elsukov /* Deep-copy a policy in PCB. */ 187*fcf59617SAndrey V. Elsukov static struct secpolicy * 188*fcf59617SAndrey V. Elsukov ipsec_deepcopy_pcbpolicy(struct secpolicy *src) 189*fcf59617SAndrey V. Elsukov { 190*fcf59617SAndrey V. Elsukov struct secpolicy *dst; 191*fcf59617SAndrey V. Elsukov int i; 192*fcf59617SAndrey V. Elsukov 193*fcf59617SAndrey V. Elsukov if (src == NULL) 194*fcf59617SAndrey V. Elsukov return (NULL); 195*fcf59617SAndrey V. Elsukov 196*fcf59617SAndrey V. Elsukov IPSEC_ASSERT(src->state == IPSEC_SPSTATE_PCB, ("SP isn't PCB")); 197*fcf59617SAndrey V. Elsukov 198*fcf59617SAndrey V. Elsukov dst = key_newsp(); 199*fcf59617SAndrey V. Elsukov if (dst == NULL) 200*fcf59617SAndrey V. Elsukov return (NULL); 201*fcf59617SAndrey V. Elsukov 202*fcf59617SAndrey V. Elsukov /* spidx is not copied here */ 203*fcf59617SAndrey V. Elsukov dst->policy = src->policy; 204*fcf59617SAndrey V. Elsukov dst->state = src->state; 205*fcf59617SAndrey V. Elsukov dst->priority = src->priority; 206*fcf59617SAndrey V. Elsukov /* Do not touch the refcnt field. */ 207*fcf59617SAndrey V. Elsukov 208*fcf59617SAndrey V. Elsukov /* Copy IPsec request chain. */ 209*fcf59617SAndrey V. Elsukov for (i = 0; i < src->tcount; i++) { 210*fcf59617SAndrey V. Elsukov dst->req[i] = ipsec_newisr(); 211*fcf59617SAndrey V. Elsukov if (dst->req[i] == NULL) { 212*fcf59617SAndrey V. Elsukov key_freesp(&dst); 213*fcf59617SAndrey V. Elsukov return (NULL); 214*fcf59617SAndrey V. Elsukov } 215*fcf59617SAndrey V. Elsukov bcopy(src->req[i], dst->req[i], sizeof(struct ipsecrequest)); 216*fcf59617SAndrey V. Elsukov dst->tcount++; 217*fcf59617SAndrey V. Elsukov } 218*fcf59617SAndrey V. Elsukov KEYDBG(IPSEC_DUMP, 219*fcf59617SAndrey V. Elsukov printf("%s: copied SP(%p) -> SP(%p)\n", __func__, src, dst); 220*fcf59617SAndrey V. Elsukov kdebug_secpolicy(dst)); 221*fcf59617SAndrey V. Elsukov return (dst); 222*fcf59617SAndrey V. Elsukov } 223*fcf59617SAndrey V. Elsukov 224*fcf59617SAndrey V. Elsukov /* 225*fcf59617SAndrey V. Elsukov * Copy IPsec policy from old INPCB into new. 226*fcf59617SAndrey V. Elsukov * It is expected that new INPCB has not configured policies. 227*fcf59617SAndrey V. Elsukov */ 228*fcf59617SAndrey V. Elsukov int 229*fcf59617SAndrey V. Elsukov ipsec_copy_pcbpolicy(struct inpcb *old, struct inpcb *new) 230*fcf59617SAndrey V. Elsukov { 231*fcf59617SAndrey V. Elsukov struct secpolicy *sp; 232*fcf59617SAndrey V. Elsukov 233*fcf59617SAndrey V. Elsukov /* 234*fcf59617SAndrey V. Elsukov * old->inp_sp can be NULL if PCB was created when an IPsec 235*fcf59617SAndrey V. Elsukov * support was unavailable. This is not an error, we don't have 236*fcf59617SAndrey V. Elsukov * policies in this PCB, so nothing to copy. 237*fcf59617SAndrey V. Elsukov */ 238*fcf59617SAndrey V. Elsukov if (old->inp_sp == NULL) 239*fcf59617SAndrey V. Elsukov return (0); 240*fcf59617SAndrey V. Elsukov 241*fcf59617SAndrey V. Elsukov IPSEC_ASSERT(new->inp_sp != NULL, ("new inp_sp is NULL")); 242*fcf59617SAndrey V. Elsukov IPSEC_ASSERT((new->inp_sp->flags & ( 243*fcf59617SAndrey V. Elsukov INP_INBOUND_POLICY | INP_OUTBOUND_POLICY)) == 0, 244*fcf59617SAndrey V. Elsukov ("new PCB already has configured policies")); 245*fcf59617SAndrey V. Elsukov INP_WLOCK_ASSERT(new); 246*fcf59617SAndrey V. Elsukov INP_LOCK_ASSERT(old); 247*fcf59617SAndrey V. Elsukov 248*fcf59617SAndrey V. Elsukov if (old->inp_sp->flags & INP_INBOUND_POLICY) { 249*fcf59617SAndrey V. Elsukov sp = ipsec_deepcopy_pcbpolicy(old->inp_sp->sp_in); 250*fcf59617SAndrey V. Elsukov if (sp == NULL) 251*fcf59617SAndrey V. Elsukov return (ENOBUFS); 252*fcf59617SAndrey V. Elsukov ipsec_setspidx_inpcb(new, &sp->spidx, IPSEC_DIR_INBOUND); 253*fcf59617SAndrey V. Elsukov new->inp_sp->sp_in = sp; 254*fcf59617SAndrey V. Elsukov new->inp_sp->flags |= INP_INBOUND_POLICY; 255*fcf59617SAndrey V. Elsukov } 256*fcf59617SAndrey V. Elsukov if (old->inp_sp->flags & INP_OUTBOUND_POLICY) { 257*fcf59617SAndrey V. Elsukov sp = ipsec_deepcopy_pcbpolicy(old->inp_sp->sp_out); 258*fcf59617SAndrey V. Elsukov if (sp == NULL) 259*fcf59617SAndrey V. Elsukov return (ENOBUFS); 260*fcf59617SAndrey V. Elsukov ipsec_setspidx_inpcb(new, &sp->spidx, IPSEC_DIR_OUTBOUND); 261*fcf59617SAndrey V. Elsukov new->inp_sp->sp_out = sp; 262*fcf59617SAndrey V. Elsukov new->inp_sp->flags |= INP_OUTBOUND_POLICY; 263*fcf59617SAndrey V. Elsukov } 264*fcf59617SAndrey V. Elsukov return (0); 265*fcf59617SAndrey V. Elsukov } 266*fcf59617SAndrey V. Elsukov 267*fcf59617SAndrey V. Elsukov static int 268*fcf59617SAndrey V. Elsukov ipsec_set_pcbpolicy(struct inpcb *inp, struct ucred *cred, 269*fcf59617SAndrey V. Elsukov void *request, size_t len) 270*fcf59617SAndrey V. Elsukov { 271*fcf59617SAndrey V. Elsukov struct sadb_x_policy *xpl; 272*fcf59617SAndrey V. Elsukov struct secpolicy **spp, *newsp; 273*fcf59617SAndrey V. Elsukov int error, flags; 274*fcf59617SAndrey V. Elsukov 275*fcf59617SAndrey V. Elsukov xpl = (struct sadb_x_policy *)request; 276*fcf59617SAndrey V. Elsukov /* Select direction. */ 277*fcf59617SAndrey V. Elsukov switch (xpl->sadb_x_policy_dir) { 278*fcf59617SAndrey V. Elsukov case IPSEC_DIR_INBOUND: 279*fcf59617SAndrey V. Elsukov case IPSEC_DIR_OUTBOUND: 280*fcf59617SAndrey V. Elsukov break; 281*fcf59617SAndrey V. Elsukov default: 282*fcf59617SAndrey V. Elsukov ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__, 283*fcf59617SAndrey V. Elsukov xpl->sadb_x_policy_dir)); 284*fcf59617SAndrey V. Elsukov return (EINVAL); 285*fcf59617SAndrey V. Elsukov } 286*fcf59617SAndrey V. Elsukov /* 287*fcf59617SAndrey V. Elsukov * Privileged sockets are allowed to set own security policy 288*fcf59617SAndrey V. Elsukov * and configure IPsec bypass. Unprivileged sockets only can 289*fcf59617SAndrey V. Elsukov * have ENTRUST policy. 290*fcf59617SAndrey V. Elsukov */ 291*fcf59617SAndrey V. Elsukov switch (xpl->sadb_x_policy_type) { 292*fcf59617SAndrey V. Elsukov case IPSEC_POLICY_IPSEC: 293*fcf59617SAndrey V. Elsukov case IPSEC_POLICY_BYPASS: 294*fcf59617SAndrey V. Elsukov if (cred != NULL && 295*fcf59617SAndrey V. Elsukov priv_check_cred(cred, PRIV_NETINET_IPSEC, 0) != 0) 296*fcf59617SAndrey V. Elsukov return (EACCES); 297*fcf59617SAndrey V. Elsukov /* Allocate new SP entry. */ 298*fcf59617SAndrey V. Elsukov newsp = key_msg2sp(xpl, len, &error); 299*fcf59617SAndrey V. Elsukov if (newsp == NULL) 300*fcf59617SAndrey V. Elsukov return (error); 301*fcf59617SAndrey V. Elsukov newsp->state = IPSEC_SPSTATE_PCB; 302*fcf59617SAndrey V. Elsukov newsp->spidx.ul_proto = IPSEC_ULPROTO_ANY; 303*fcf59617SAndrey V. Elsukov #ifdef INET 304*fcf59617SAndrey V. Elsukov if (inp->inp_vflag & INP_IPV4) { 305*fcf59617SAndrey V. Elsukov newsp->spidx.src.sin.sin_family = 306*fcf59617SAndrey V. Elsukov newsp->spidx.dst.sin.sin_family = AF_INET; 307*fcf59617SAndrey V. Elsukov newsp->spidx.src.sin.sin_len = 308*fcf59617SAndrey V. Elsukov newsp->spidx.dst.sin.sin_len = 309*fcf59617SAndrey V. Elsukov sizeof(struct sockaddr_in); 310*fcf59617SAndrey V. Elsukov } 311*fcf59617SAndrey V. Elsukov #endif 312*fcf59617SAndrey V. Elsukov #ifdef INET6 313*fcf59617SAndrey V. Elsukov if (inp->inp_vflag & INP_IPV6) { 314*fcf59617SAndrey V. Elsukov newsp->spidx.src.sin6.sin6_family = 315*fcf59617SAndrey V. Elsukov newsp->spidx.dst.sin6.sin6_family = AF_INET6; 316*fcf59617SAndrey V. Elsukov newsp->spidx.src.sin6.sin6_len = 317*fcf59617SAndrey V. Elsukov newsp->spidx.dst.sin6.sin6_len = 318*fcf59617SAndrey V. Elsukov sizeof(struct sockaddr_in6); 319*fcf59617SAndrey V. Elsukov } 320*fcf59617SAndrey V. Elsukov #endif 321*fcf59617SAndrey V. Elsukov break; 322*fcf59617SAndrey V. Elsukov case IPSEC_POLICY_ENTRUST: 323*fcf59617SAndrey V. Elsukov /* We just use NULL pointer for ENTRUST policy */ 324*fcf59617SAndrey V. Elsukov newsp = NULL; 325*fcf59617SAndrey V. Elsukov break; 326*fcf59617SAndrey V. Elsukov default: 327*fcf59617SAndrey V. Elsukov /* Other security policy types aren't allowed for PCB */ 328*fcf59617SAndrey V. Elsukov return (EINVAL); 329*fcf59617SAndrey V. Elsukov } 330*fcf59617SAndrey V. Elsukov 331*fcf59617SAndrey V. Elsukov INP_WLOCK(inp); 332*fcf59617SAndrey V. Elsukov if (xpl->sadb_x_policy_dir == IPSEC_DIR_INBOUND) { 333*fcf59617SAndrey V. Elsukov spp = &inp->inp_sp->sp_in; 334*fcf59617SAndrey V. Elsukov flags = INP_INBOUND_POLICY; 335*fcf59617SAndrey V. Elsukov } else { 336*fcf59617SAndrey V. Elsukov spp = &inp->inp_sp->sp_out; 337*fcf59617SAndrey V. Elsukov flags = INP_OUTBOUND_POLICY; 338*fcf59617SAndrey V. Elsukov } 339*fcf59617SAndrey V. Elsukov /* Clear old SP and set new SP. */ 340*fcf59617SAndrey V. Elsukov if (*spp != NULL) 341*fcf59617SAndrey V. Elsukov key_freesp(spp); 342*fcf59617SAndrey V. Elsukov *spp = newsp; 343*fcf59617SAndrey V. Elsukov KEYDBG(IPSEC_DUMP, 344*fcf59617SAndrey V. Elsukov printf("%s: new SP(%p)\n", __func__, newsp)); 345*fcf59617SAndrey V. Elsukov if (newsp == NULL) 346*fcf59617SAndrey V. Elsukov inp->inp_sp->flags &= ~flags; 347*fcf59617SAndrey V. Elsukov else { 348*fcf59617SAndrey V. Elsukov inp->inp_sp->flags |= flags; 349*fcf59617SAndrey V. Elsukov KEYDBG(IPSEC_DUMP, kdebug_secpolicy(newsp)); 350*fcf59617SAndrey V. Elsukov } 351*fcf59617SAndrey V. Elsukov INP_WUNLOCK(inp); 352*fcf59617SAndrey V. Elsukov return (0); 353*fcf59617SAndrey V. Elsukov } 354*fcf59617SAndrey V. Elsukov 355*fcf59617SAndrey V. Elsukov static int 356*fcf59617SAndrey V. Elsukov ipsec_get_pcbpolicy(struct inpcb *inp, void *request, size_t *len) 357*fcf59617SAndrey V. Elsukov { 358*fcf59617SAndrey V. Elsukov struct sadb_x_policy *xpl; 359*fcf59617SAndrey V. Elsukov struct secpolicy *sp; 360*fcf59617SAndrey V. Elsukov int error, flags; 361*fcf59617SAndrey V. Elsukov 362*fcf59617SAndrey V. Elsukov xpl = (struct sadb_x_policy *)request; 363*fcf59617SAndrey V. Elsukov 364*fcf59617SAndrey V. Elsukov INP_RLOCK(inp); 365*fcf59617SAndrey V. Elsukov flags = inp->inp_sp->flags; 366*fcf59617SAndrey V. Elsukov /* Select direction. */ 367*fcf59617SAndrey V. Elsukov switch (xpl->sadb_x_policy_dir) { 368*fcf59617SAndrey V. Elsukov case IPSEC_DIR_INBOUND: 369*fcf59617SAndrey V. Elsukov sp = inp->inp_sp->sp_in; 370*fcf59617SAndrey V. Elsukov flags &= INP_INBOUND_POLICY; 371*fcf59617SAndrey V. Elsukov break; 372*fcf59617SAndrey V. Elsukov case IPSEC_DIR_OUTBOUND: 373*fcf59617SAndrey V. Elsukov sp = inp->inp_sp->sp_out; 374*fcf59617SAndrey V. Elsukov flags &= INP_OUTBOUND_POLICY; 375*fcf59617SAndrey V. Elsukov break; 376*fcf59617SAndrey V. Elsukov default: 377*fcf59617SAndrey V. Elsukov INP_RUNLOCK(inp); 378*fcf59617SAndrey V. Elsukov ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__, 379*fcf59617SAndrey V. Elsukov xpl->sadb_x_policy_dir)); 380*fcf59617SAndrey V. Elsukov return (EINVAL); 381*fcf59617SAndrey V. Elsukov } 382*fcf59617SAndrey V. Elsukov 383*fcf59617SAndrey V. Elsukov if (flags == 0) { 384*fcf59617SAndrey V. Elsukov /* Return ENTRUST policy */ 385*fcf59617SAndrey V. Elsukov INP_RUNLOCK(inp); 386*fcf59617SAndrey V. Elsukov xpl->sadb_x_policy_exttype = SADB_X_EXT_POLICY; 387*fcf59617SAndrey V. Elsukov xpl->sadb_x_policy_type = IPSEC_POLICY_ENTRUST; 388*fcf59617SAndrey V. Elsukov xpl->sadb_x_policy_id = 0; 389*fcf59617SAndrey V. Elsukov xpl->sadb_x_policy_priority = 0; 390*fcf59617SAndrey V. Elsukov xpl->sadb_x_policy_len = PFKEY_UNIT64(sizeof(*xpl)); 391*fcf59617SAndrey V. Elsukov *len = sizeof(*xpl); 392*fcf59617SAndrey V. Elsukov return (0); 393*fcf59617SAndrey V. Elsukov } 394*fcf59617SAndrey V. Elsukov 395*fcf59617SAndrey V. Elsukov IPSEC_ASSERT(sp != NULL, 396*fcf59617SAndrey V. Elsukov ("sp is NULL, but flags is 0x%04x", inp->inp_sp->flags)); 397*fcf59617SAndrey V. Elsukov 398*fcf59617SAndrey V. Elsukov key_addref(sp); 399*fcf59617SAndrey V. Elsukov INP_RUNLOCK(inp); 400*fcf59617SAndrey V. Elsukov error = key_sp2msg(sp, request, len); 401*fcf59617SAndrey V. Elsukov key_freesp(&sp); 402*fcf59617SAndrey V. Elsukov if (error == EINVAL) 403*fcf59617SAndrey V. Elsukov return (error); 404*fcf59617SAndrey V. Elsukov /* 405*fcf59617SAndrey V. Elsukov * We return "success", but user should check *len. 406*fcf59617SAndrey V. Elsukov * *len will be set to size of valid data and 407*fcf59617SAndrey V. Elsukov * sadb_x_policy_len will contain needed size. 408*fcf59617SAndrey V. Elsukov */ 409*fcf59617SAndrey V. Elsukov return (0); 410*fcf59617SAndrey V. Elsukov } 411*fcf59617SAndrey V. Elsukov 412*fcf59617SAndrey V. Elsukov /* Handle socket option control request for PCB */ 413*fcf59617SAndrey V. Elsukov static int 414*fcf59617SAndrey V. Elsukov ipsec_control_pcbpolicy(struct inpcb *inp, struct sockopt *sopt) 415*fcf59617SAndrey V. Elsukov { 416*fcf59617SAndrey V. Elsukov void *optdata; 417*fcf59617SAndrey V. Elsukov size_t optlen; 418*fcf59617SAndrey V. Elsukov int error; 419*fcf59617SAndrey V. Elsukov 420*fcf59617SAndrey V. Elsukov if (inp->inp_sp == NULL) 421*fcf59617SAndrey V. Elsukov return (ENOPROTOOPT); 422*fcf59617SAndrey V. Elsukov 423*fcf59617SAndrey V. Elsukov /* Limit maximum request size to PAGE_SIZE */ 424*fcf59617SAndrey V. Elsukov optlen = sopt->sopt_valsize; 425*fcf59617SAndrey V. Elsukov if (optlen < sizeof(struct sadb_x_policy) || optlen > PAGE_SIZE) 426*fcf59617SAndrey V. Elsukov return (EINVAL); 427*fcf59617SAndrey V. Elsukov 428*fcf59617SAndrey V. Elsukov optdata = malloc(optlen, M_TEMP, sopt->sopt_td ? M_WAITOK: M_NOWAIT); 429*fcf59617SAndrey V. Elsukov if (optdata == NULL) 430*fcf59617SAndrey V. Elsukov return (ENOBUFS); 431*fcf59617SAndrey V. Elsukov /* 432*fcf59617SAndrey V. Elsukov * We need a hint from the user, what policy is requested - input 433*fcf59617SAndrey V. Elsukov * or output? User should specify it in the buffer, even for 434*fcf59617SAndrey V. Elsukov * setsockopt(). 435*fcf59617SAndrey V. Elsukov */ 436*fcf59617SAndrey V. Elsukov error = sooptcopyin(sopt, optdata, optlen, optlen); 437*fcf59617SAndrey V. Elsukov if (error == 0) { 438*fcf59617SAndrey V. Elsukov if (sopt->sopt_dir == SOPT_SET) 439*fcf59617SAndrey V. Elsukov error = ipsec_set_pcbpolicy(inp, 440*fcf59617SAndrey V. Elsukov sopt->sopt_td ? sopt->sopt_td->td_ucred: NULL, 441*fcf59617SAndrey V. Elsukov optdata, optlen); 442*fcf59617SAndrey V. Elsukov else { 443*fcf59617SAndrey V. Elsukov error = ipsec_get_pcbpolicy(inp, optdata, &optlen); 444*fcf59617SAndrey V. Elsukov if (error == 0) 445*fcf59617SAndrey V. Elsukov error = sooptcopyout(sopt, optdata, optlen); 446*fcf59617SAndrey V. Elsukov } 447*fcf59617SAndrey V. Elsukov } 448*fcf59617SAndrey V. Elsukov free(optdata, M_TEMP); 449*fcf59617SAndrey V. Elsukov return (error); 450*fcf59617SAndrey V. Elsukov } 451*fcf59617SAndrey V. Elsukov 452*fcf59617SAndrey V. Elsukov #ifdef INET 453*fcf59617SAndrey V. Elsukov /* 454*fcf59617SAndrey V. Elsukov * IPSEC_PCBCTL() method implementation for IPv4. 455*fcf59617SAndrey V. Elsukov */ 456*fcf59617SAndrey V. Elsukov int 457*fcf59617SAndrey V. Elsukov ipsec4_pcbctl(struct inpcb *inp, struct sockopt *sopt) 458*fcf59617SAndrey V. Elsukov { 459*fcf59617SAndrey V. Elsukov 460*fcf59617SAndrey V. Elsukov if (sopt->sopt_name != IP_IPSEC_POLICY) 461*fcf59617SAndrey V. Elsukov return (ENOPROTOOPT); 462*fcf59617SAndrey V. Elsukov return (ipsec_control_pcbpolicy(inp, sopt)); 463*fcf59617SAndrey V. Elsukov } 464*fcf59617SAndrey V. Elsukov #endif 465*fcf59617SAndrey V. Elsukov 466*fcf59617SAndrey V. Elsukov #ifdef INET6 467*fcf59617SAndrey V. Elsukov /* 468*fcf59617SAndrey V. Elsukov * IPSEC_PCBCTL() method implementation for IPv6. 469*fcf59617SAndrey V. Elsukov */ 470*fcf59617SAndrey V. Elsukov int 471*fcf59617SAndrey V. Elsukov ipsec6_pcbctl(struct inpcb *inp, struct sockopt *sopt) 472*fcf59617SAndrey V. Elsukov { 473*fcf59617SAndrey V. Elsukov 474*fcf59617SAndrey V. Elsukov if (sopt->sopt_name != IPV6_IPSEC_POLICY) 475*fcf59617SAndrey V. Elsukov return (ENOPROTOOPT); 476*fcf59617SAndrey V. Elsukov return (ipsec_control_pcbpolicy(inp, sopt)); 477*fcf59617SAndrey V. Elsukov } 478*fcf59617SAndrey V. Elsukov #endif 479*fcf59617SAndrey V. Elsukov 480