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