Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/rpc/rpcsec_tls/rpctls_impl.c
39483 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2008 Isilon Inc http://www.isilon.com/
5
* Authors: Doug Rabson <[email protected]>
6
* Developed with Red Inc: Alfred Perlstein <[email protected]>
7
*
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions
10
* are met:
11
* 1. Redistributions of source code must retain the above copyright
12
* notice, this list of conditions and the following disclaimer.
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in the
15
* documentation and/or other materials provided with the distribution.
16
*
17
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
* SUCH DAMAGE.
28
*/
29
30
/* Modified from the kernel GSSAPI code for RPC-over-TLS. */
31
32
#include <sys/cdefs.h>
33
#include "opt_kern_tls.h"
34
35
#include <sys/param.h>
36
#include <sys/capsicum.h>
37
#include <sys/file.h>
38
#include <sys/filedesc.h>
39
#include <sys/jail.h>
40
#include <sys/kernel.h>
41
#include <sys/lock.h>
42
#include <sys/malloc.h>
43
#include <sys/mbuf.h>
44
#include <sys/mutex.h>
45
#include <sys/priv.h>
46
#include <sys/proc.h>
47
#include <sys/socketvar.h>
48
#include <sys/syscall.h>
49
#include <sys/syscallsubr.h>
50
#include <sys/sysent.h>
51
#include <sys/sysproto.h>
52
#include <sys/tree.h>
53
54
#include <net/vnet.h>
55
56
#include <rpc/rpc.h>
57
#include <rpc/rpc_com.h>
58
#include <rpc/krpc.h>
59
#include <rpc/rpcsec_tls.h>
60
61
#include <vm/vm.h>
62
#include <vm/pmap.h>
63
#include <vm/vm_param.h>
64
65
#include "rpctlscd.h"
66
#include "rpctlssd.h"
67
68
/*
69
* Syscall hooks
70
*/
71
static struct syscall_helper_data rpctls_syscalls[] = {
72
SYSCALL_INIT_HELPER(rpctls_syscall),
73
SYSCALL_INIT_LAST
74
};
75
76
static struct opaque_auth rpctls_null_verf;
77
78
KRPC_VNET_DECLARE(uint64_t, svc_vc_tls_handshake_success);
79
KRPC_VNET_DECLARE(uint64_t, svc_vc_tls_handshake_failed);
80
81
static CLIENT *rpctls_connect_handle;
82
static CLIENT *rpctls_server_handle;
83
84
struct upsock {
85
RB_ENTRY(upsock) tree;
86
struct socket *so;
87
union {
88
CLIENT *cl;
89
SVCXPRT *xp;
90
};
91
bool server;
92
};
93
94
static RB_HEAD(upsock_t, upsock) upcall_sockets;
95
static intptr_t
96
upsock_compare(const struct upsock *a, const struct upsock *b)
97
{
98
return ((intptr_t)((uintptr_t)a->so/2 - (uintptr_t)b->so/2));
99
}
100
RB_GENERATE_STATIC(upsock_t, upsock, tree, upsock_compare);
101
static struct mtx rpctls_lock;
102
103
static enum clnt_stat rpctls_server(SVCXPRT *xprt, uint32_t *flags,
104
uid_t *uid, int *ngrps, gid_t **gids);
105
106
static CLIENT *
107
rpctls_client_nl_create(const char *group, const rpcprog_t program,
108
const rpcvers_t version)
109
{
110
CLIENT *cl;
111
112
cl = client_nl_create(group, program, version);
113
KASSERT(cl, ("%s: netlink client already exist", __func__));
114
/*
115
* Set the try_count to 1 so that no retries of the RPC occur. Since
116
* it is an upcall to a local daemon, requests should not be lost and
117
* doing one of these RPCs multiple times is not correct. If the
118
* server is not working correctly, the daemon can get stuck in
119
* SSL_connect() trying to read data from the socket during the upcall.
120
* Set a timeout (currently 15sec) and assume the daemon is hung when
121
* the timeout occurs.
122
*/
123
clnt_control(cl, CLSET_RETRIES, &(int){1});
124
clnt_control(cl, CLSET_TIMEOUT, &(struct timeval){.tv_sec = 15});
125
clnt_control(cl, CLSET_WAITCHAN, __DECONST(char *, group));
126
127
return (cl);
128
}
129
130
int
131
rpctls_init(void)
132
{
133
int error;
134
135
error = syscall_helper_register(rpctls_syscalls, SY_THR_STATIC_KLD);
136
if (error != 0) {
137
printf("rpctls_init: cannot register syscall\n");
138
return (error);
139
}
140
mtx_init(&rpctls_lock, "rpctls lock", NULL, MTX_DEF);
141
rpctls_null_verf.oa_flavor = AUTH_NULL;
142
rpctls_null_verf.oa_base = RPCTLS_START_STRING;
143
rpctls_null_verf.oa_length = strlen(RPCTLS_START_STRING);
144
rpctls_connect_handle = rpctls_client_nl_create("tlsclnt",
145
RPCTLSCD, RPCTLSCDVERS);
146
rpctls_server_handle = rpctls_client_nl_create("tlsserv",
147
RPCTLSSD, RPCTLSSDVERS);
148
return (0);
149
}
150
151
int
152
sys_rpctls_syscall(struct thread *td, struct rpctls_syscall_args *uap)
153
{
154
struct file *fp;
155
struct upsock *upsp, ups;
156
int fd = -1, error;
157
158
error = priv_check(td, PRIV_NFS_DAEMON);
159
if (error != 0)
160
return (error);
161
162
KRPC_CURVNET_SET(KRPC_TD_TO_VNET(td));
163
mtx_lock(&rpctls_lock);
164
upsp = RB_FIND(upsock_t, &upcall_sockets,
165
&(struct upsock){
166
.so = __DECONST(struct socket *, uap->socookie) });
167
if (__predict_true(upsp != NULL)) {
168
RB_REMOVE(upsock_t, &upcall_sockets, upsp);
169
/*
170
* The upsp points to stack of NFS mounting thread. Even
171
* though we removed it from the tree, we still don't own it.
172
* Make a copy before releasing the lock. The mounting thread
173
* may timeout the RPC and unroll its stack.
174
*/
175
ups = *upsp;
176
}
177
mtx_unlock(&rpctls_lock);
178
if (upsp == NULL) {
179
KRPC_CURVNET_RESTORE();
180
printf("%s: socket lookup failed\n", __func__);
181
return (EPERM);
182
}
183
if ((error = falloc(td, &fp, &fd, 0)) != 0) {
184
/*
185
* The socket will not be acquired by the daemon,
186
* but has been removed from the upcall socket RB.
187
* As such, it needs to be closed here.
188
*/
189
soclose(ups.so);
190
KRPC_CURVNET_RESTORE();
191
return (error);
192
}
193
soref(ups.so);
194
if (ups.server) {
195
/*
196
* Once this file descriptor is associated
197
* with the socket, it cannot be closed by
198
* the server side krpc code (svc_vc.c).
199
*/
200
sx_xlock(&ups.xp->xp_lock);
201
ups.xp->xp_tls = RPCTLS_FLAGS_HANDSHFAIL;
202
sx_xunlock(&ups.xp->xp_lock);
203
} else {
204
/*
205
* Initialize TLS state so that clnt_vc_destroy() will
206
* not close the socket and will leave that for the
207
* daemon to do.
208
*/
209
CLNT_CONTROL(ups.cl, CLSET_TLS, &(int){RPCTLS_INHANDSHAKE});
210
}
211
finit(fp, FREAD | FWRITE, DTYPE_SOCKET, ups.so, &socketops);
212
fdrop(fp, td); /* Drop fp reference. */
213
td->td_retval[0] = fd;
214
KRPC_CURVNET_RESTORE();
215
216
return (error);
217
}
218
219
/* Error handling for both client and server failed RPC upcalls. */
220
static void
221
rpctls_rpc_failed(struct upsock *ups, struct socket *so)
222
{
223
224
mtx_lock(&rpctls_lock);
225
if (RB_FIND(upsock_t, &upcall_sockets, ups)) {
226
struct upsock *removed __diagused;
227
228
removed = RB_REMOVE(upsock_t, &upcall_sockets, ups);
229
mtx_unlock(&rpctls_lock);
230
MPASS(removed == ups);
231
/*
232
* Since the socket was still in the RB tree when
233
* this function was called, the daemon will not
234
* close it. As such, it needs to be closed here.
235
*/
236
soclose(so);
237
} else {
238
/*
239
* The daemon has taken the socket from the tree, but
240
* failed to do the handshake.
241
*/
242
mtx_unlock(&rpctls_lock);
243
/*
244
* Do a shutdown on the socket, since the daemon is
245
* probably stuck in SSL_accept() or SSL_connect() trying to
246
* read the socket. Do not soclose() the socket, since the
247
* daemon will close() the socket after SSL_accept()
248
* returns an error.
249
*/
250
soshutdown(so, SHUT_RD);
251
}
252
}
253
254
/* Do an upcall for a new socket connect using TLS. */
255
enum clnt_stat
256
rpctls_connect(CLIENT *newclient, char *certname, struct socket *so,
257
uint32_t *reterr)
258
{
259
struct rpctlscd_connect_arg arg;
260
struct rpctlscd_connect_res res;
261
struct rpc_callextra ext;
262
enum clnt_stat stat;
263
struct upsock ups = {
264
.so = so,
265
.cl = newclient,
266
.server = false,
267
};
268
269
/* First, do the AUTH_TLS NULL RPC. */
270
memset(&ext, 0, sizeof(ext));
271
ext.rc_auth = authtls_create();
272
stat = clnt_call_private(newclient, &ext, NULLPROC, (xdrproc_t)xdr_void,
273
NULL, (xdrproc_t)xdr_void, NULL, (struct timeval){ .tv_sec = 30 });
274
AUTH_DESTROY(ext.rc_auth);
275
if (stat == RPC_AUTHERROR)
276
return (stat);
277
if (stat != RPC_SUCCESS)
278
return (RPC_SYSTEMERROR);
279
280
mtx_lock(&rpctls_lock);
281
RB_INSERT(upsock_t, &upcall_sockets, &ups);
282
mtx_unlock(&rpctls_lock);
283
284
/* Temporarily block reception during the handshake upcall. */
285
CLNT_CONTROL(newclient, CLSET_BLOCKRCV, &(int){1});
286
287
/* Do the connect handshake upcall. */
288
if (certname != NULL) {
289
arg.certname.certname_len = strlen(certname);
290
arg.certname.certname_val = certname;
291
} else
292
arg.certname.certname_len = 0;
293
arg.socookie = (uint64_t)so;
294
stat = rpctlscd_connect_2(&arg, &res, rpctls_connect_handle);
295
if (stat == RPC_SUCCESS)
296
*reterr = res.reterr;
297
else
298
rpctls_rpc_failed(&ups, so);
299
300
/* Unblock reception. */
301
CLNT_CONTROL(newclient, CLSET_BLOCKRCV, &(int){0});
302
303
#ifdef INVARIANTS
304
mtx_lock(&rpctls_lock);
305
MPASS((RB_FIND(upsock_t, &upcall_sockets, &ups) == NULL));
306
mtx_unlock(&rpctls_lock);
307
#endif
308
309
return (stat);
310
}
311
312
/* Do an upcall to handle an non-application data record using TLS. */
313
enum clnt_stat
314
rpctls_cl_handlerecord(void *socookie, uint32_t *reterr)
315
{
316
struct rpctlscd_handlerecord_arg arg;
317
struct rpctlscd_handlerecord_res res;
318
enum clnt_stat stat;
319
320
/* Do the handlerecord upcall. */
321
arg.socookie = (uint64_t)socookie;
322
stat = rpctlscd_handlerecord_2(&arg, &res, rpctls_connect_handle);
323
if (stat == RPC_SUCCESS)
324
*reterr = res.reterr;
325
return (stat);
326
}
327
328
enum clnt_stat
329
rpctls_srv_handlerecord(void *socookie, uint32_t *reterr)
330
{
331
struct rpctlssd_handlerecord_arg arg;
332
struct rpctlssd_handlerecord_res res;
333
enum clnt_stat stat;
334
335
/* Do the handlerecord upcall. */
336
arg.socookie = (uint64_t)socookie;
337
stat = rpctlssd_handlerecord_2(&arg, &res, rpctls_server_handle);
338
if (stat == RPC_SUCCESS)
339
*reterr = res.reterr;
340
return (stat);
341
}
342
343
/* Do an upcall to shut down a socket using TLS. */
344
enum clnt_stat
345
rpctls_cl_disconnect(void *socookie, uint32_t *reterr)
346
{
347
struct rpctlscd_disconnect_arg arg;
348
struct rpctlscd_disconnect_res res;
349
enum clnt_stat stat;
350
351
/* Do the disconnect upcall. */
352
arg.socookie = (uint64_t)socookie;
353
stat = rpctlscd_disconnect_2(&arg, &res, rpctls_connect_handle);
354
if (stat == RPC_SUCCESS)
355
*reterr = res.reterr;
356
return (stat);
357
}
358
359
enum clnt_stat
360
rpctls_srv_disconnect(void *socookie, uint32_t *reterr)
361
{
362
struct rpctlssd_disconnect_arg arg;
363
struct rpctlssd_disconnect_res res;
364
enum clnt_stat stat;
365
366
/* Do the disconnect upcall. */
367
arg.socookie = (uint64_t)socookie;
368
stat = rpctlssd_disconnect_2(&arg, &res, rpctls_server_handle);
369
if (stat == RPC_SUCCESS)
370
*reterr = res.reterr;
371
return (stat);
372
}
373
374
/* Do an upcall for a new server socket using TLS. */
375
static enum clnt_stat
376
rpctls_server(SVCXPRT *xprt, uint32_t *flags, uid_t *uid, int *ngrps,
377
gid_t **gids)
378
{
379
enum clnt_stat stat;
380
struct upsock ups = {
381
.so = xprt->xp_socket,
382
.xp = xprt,
383
.server = true,
384
};
385
struct rpctlssd_connect_arg arg;
386
struct rpctlssd_connect_res res;
387
gid_t *gidp;
388
uint32_t *gidv;
389
int i;
390
391
mtx_lock(&rpctls_lock);
392
RB_INSERT(upsock_t, &upcall_sockets, &ups);
393
mtx_unlock(&rpctls_lock);
394
395
/* Do the server upcall. */
396
res.gid.gid_val = NULL;
397
arg.socookie = (uint64_t)xprt->xp_socket;
398
stat = rpctlssd_connect_2(&arg, &res, rpctls_server_handle);
399
if (stat == RPC_SUCCESS) {
400
*flags = res.flags;
401
if ((*flags & (RPCTLS_FLAGS_CERTUSER |
402
RPCTLS_FLAGS_DISABLED)) == RPCTLS_FLAGS_CERTUSER) {
403
*ngrps = res.gid.gid_len;
404
*uid = res.uid;
405
*gids = gidp = mem_alloc(*ngrps * sizeof(gid_t));
406
gidv = res.gid.gid_val;
407
for (i = 0; i < *ngrps; i++)
408
*gidp++ = *gidv++;
409
}
410
} else
411
rpctls_rpc_failed(&ups, xprt->xp_socket);
412
413
mem_free(res.gid.gid_val, 0);
414
415
#ifdef INVARIANTS
416
mtx_lock(&rpctls_lock);
417
MPASS((RB_FIND(upsock_t, &upcall_sockets, &ups) == NULL));
418
mtx_unlock(&rpctls_lock);
419
#endif
420
421
return (stat);
422
}
423
424
/*
425
* Handle the NULL RPC with authentication flavor of AUTH_TLS.
426
* This is a STARTTLS command, so do the upcall to the rpctlssd daemon,
427
* which will do the TLS handshake.
428
*/
429
enum auth_stat
430
_svcauth_rpcsec_tls(struct svc_req *rqst, struct rpc_msg *msg)
431
432
{
433
bool_t call_stat;
434
enum clnt_stat stat;
435
SVCXPRT *xprt;
436
uint32_t flags;
437
int ngrps;
438
uid_t uid;
439
gid_t *gidp;
440
#ifdef KERN_TLS
441
u_int maxlen;
442
#endif
443
444
KRPC_CURVNET_SET_QUIET(KRPC_TD_TO_VNET(curthread));
445
KRPC_VNET(svc_vc_tls_handshake_failed)++;
446
/* Initialize reply. */
447
rqst->rq_verf = rpctls_null_verf;
448
449
/* Check client credentials. */
450
if (rqst->rq_cred.oa_length != 0 ||
451
msg->rm_call.cb_verf.oa_length != 0 ||
452
msg->rm_call.cb_verf.oa_flavor != AUTH_NULL) {
453
KRPC_CURVNET_RESTORE();
454
return (AUTH_BADCRED);
455
}
456
457
if (rqst->rq_proc != NULLPROC) {
458
KRPC_CURVNET_RESTORE();
459
return (AUTH_REJECTEDCRED);
460
}
461
462
call_stat = FALSE;
463
#ifdef KERN_TLS
464
if (rpctls_getinfo(&maxlen, false, true))
465
call_stat = TRUE;
466
#endif
467
if (!call_stat) {
468
KRPC_CURVNET_RESTORE();
469
return (AUTH_REJECTEDCRED);
470
}
471
472
/*
473
* Disable reception for the krpc so that the TLS handshake can
474
* be done on the socket in the rpctlssd daemon.
475
*/
476
xprt = rqst->rq_xprt;
477
sx_xlock(&xprt->xp_lock);
478
xprt->xp_dontrcv = TRUE;
479
sx_xunlock(&xprt->xp_lock);
480
481
/*
482
* Send the reply to the NULL RPC with AUTH_TLS, which is the
483
* STARTTLS command for Sun RPC.
484
*/
485
call_stat = svc_sendreply(rqst, (xdrproc_t)xdr_void, NULL);
486
if (!call_stat) {
487
sx_xlock(&xprt->xp_lock);
488
xprt->xp_dontrcv = FALSE;
489
sx_xunlock(&xprt->xp_lock);
490
xprt_active(xprt); /* Harmless if already active. */
491
KRPC_CURVNET_RESTORE();
492
return (AUTH_REJECTEDCRED);
493
}
494
495
/* Do an upcall to do the TLS handshake. */
496
stat = rpctls_server(xprt, &flags, &uid, &ngrps, &gidp);
497
498
/* Re-enable reception on the socket within the krpc. */
499
sx_xlock(&xprt->xp_lock);
500
xprt->xp_dontrcv = FALSE;
501
if (stat == RPC_SUCCESS) {
502
xprt->xp_tls = flags;
503
if ((flags & (RPCTLS_FLAGS_CERTUSER |
504
RPCTLS_FLAGS_DISABLED)) == RPCTLS_FLAGS_CERTUSER) {
505
xprt->xp_ngrps = ngrps;
506
xprt->xp_uid = uid;
507
xprt->xp_gidp = gidp;
508
}
509
KRPC_VNET(svc_vc_tls_handshake_failed)--;
510
KRPC_VNET(svc_vc_tls_handshake_success)++;
511
}
512
sx_xunlock(&xprt->xp_lock);
513
xprt_active(xprt); /* Harmless if already active. */
514
KRPC_CURVNET_RESTORE();
515
516
return (RPCSEC_GSS_NODISPATCH);
517
}
518
519
/*
520
* Get kern.ipc.tls.enable and kern.ipc.tls.maxlen.
521
*/
522
bool
523
rpctls_getinfo(u_int *maxlenp, bool rpctlscd_run, bool rpctlssd_run)
524
{
525
u_int maxlen;
526
bool enable;
527
int error;
528
size_t siz;
529
530
if (!mb_use_ext_pgs)
531
return (false);
532
siz = sizeof(enable);
533
error = kernel_sysctlbyname(curthread, "kern.ipc.tls.enable",
534
&enable, &siz, NULL, 0, NULL, 0);
535
if (error != 0)
536
return (false);
537
siz = sizeof(maxlen);
538
error = kernel_sysctlbyname(curthread, "kern.ipc.tls.maxlen",
539
&maxlen, &siz, NULL, 0, NULL, 0);
540
if (error != 0)
541
return (false);
542
*maxlenp = maxlen;
543
return (enable);
544
}
545
546