xref: /freebsd/sys/netipsec/ipsec_pcb.c (revision ef2a572bf6bdcac97ef29ce631d2f50f938e1ec8)
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