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