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