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