xref: /freebsd/sys/rpc/rpcsec_tls/rpctls_impl.c (revision 26ee0593920946646882a14997d15e16b1bec772)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
5  * Authors: Doug Rabson <dfr@rabson.org>
6  * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
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
upsock_compare(const struct upsock * a,const struct upsock * b)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 *
rpctls_client_nl_create(const char * group,const rpcprog_t program,const rpcvers_t version)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
rpctls_init(void)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
sys_rpctls_syscall(struct thread * td,struct rpctls_syscall_args * uap)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
rpctls_rpc_failed(struct upsock * ups,struct socket * so)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 }
245 
246 /* Do an upcall for a new socket connect using TLS. */
247 enum clnt_stat
rpctls_connect(CLIENT * newclient,char * certname,struct socket * so,uint32_t * reterr)248 rpctls_connect(CLIENT *newclient, char *certname, struct socket *so,
249     uint32_t *reterr)
250 {
251 	struct rpctlscd_connect_arg arg;
252 	struct rpctlscd_connect_res res;
253 	struct rpc_callextra ext;
254 	enum clnt_stat stat;
255 	struct upsock ups = {
256 		.so = so,
257 		.cl = newclient,
258 		.server = false,
259 	};
260 
261 	/* First, do the AUTH_TLS NULL RPC. */
262 	memset(&ext, 0, sizeof(ext));
263 	ext.rc_auth = authtls_create();
264 	stat = clnt_call_private(newclient, &ext, NULLPROC, (xdrproc_t)xdr_void,
265 	    NULL, (xdrproc_t)xdr_void, NULL, (struct timeval){ .tv_sec = 30 });
266 	AUTH_DESTROY(ext.rc_auth);
267 	if (stat == RPC_AUTHERROR)
268 		return (stat);
269 	if (stat != RPC_SUCCESS)
270 		return (RPC_SYSTEMERROR);
271 
272 	mtx_lock(&rpctls_lock);
273 	RB_INSERT(upsock_t, &upcall_sockets, &ups);
274 	mtx_unlock(&rpctls_lock);
275 
276 	/* Temporarily block reception during the handshake upcall. */
277 	CLNT_CONTROL(newclient, CLSET_BLOCKRCV, &(int){1});
278 
279 	/* Do the connect handshake upcall. */
280 	if (certname != NULL) {
281 		arg.certname.certname_len = strlen(certname);
282 		arg.certname.certname_val = certname;
283 	} else
284 		arg.certname.certname_len = 0;
285 	arg.socookie = (uint64_t)so;
286 	stat = rpctlscd_connect_2(&arg, &res, rpctls_connect_handle);
287 	if (stat == RPC_SUCCESS)
288 		*reterr = res.reterr;
289 	else
290 		rpctls_rpc_failed(&ups, so);
291 
292 	/* Unblock reception. */
293 	CLNT_CONTROL(newclient, CLSET_BLOCKRCV, &(int){0});
294 
295 #ifdef INVARIANTS
296 	mtx_lock(&rpctls_lock);
297 	MPASS((RB_FIND(upsock_t, &upcall_sockets, &ups) == NULL));
298 	mtx_unlock(&rpctls_lock);
299 #endif
300 
301 	return (stat);
302 }
303 
304 /* Do an upcall to handle an non-application data record using TLS. */
305 enum clnt_stat
rpctls_cl_handlerecord(void * socookie,uint32_t * reterr)306 rpctls_cl_handlerecord(void *socookie, uint32_t *reterr)
307 {
308 	struct rpctlscd_handlerecord_arg arg;
309 	struct rpctlscd_handlerecord_res res;
310 	enum clnt_stat stat;
311 
312 	/* Do the handlerecord upcall. */
313 	arg.socookie = (uint64_t)socookie;
314 	stat = rpctlscd_handlerecord_2(&arg, &res, rpctls_connect_handle);
315 	if (stat == RPC_SUCCESS)
316 		*reterr = res.reterr;
317 	return (stat);
318 }
319 
320 enum clnt_stat
rpctls_srv_handlerecord(void * socookie,uint32_t * reterr)321 rpctls_srv_handlerecord(void *socookie, uint32_t *reterr)
322 {
323 	struct rpctlssd_handlerecord_arg arg;
324 	struct rpctlssd_handlerecord_res res;
325 	enum clnt_stat stat;
326 
327 	/* Do the handlerecord upcall. */
328 	arg.socookie = (uint64_t)socookie;
329 	stat = rpctlssd_handlerecord_2(&arg, &res, rpctls_server_handle);
330 	if (stat == RPC_SUCCESS)
331 		*reterr = res.reterr;
332 	return (stat);
333 }
334 
335 /* Do an upcall to shut down a socket using TLS. */
336 enum clnt_stat
rpctls_cl_disconnect(void * socookie,uint32_t * reterr)337 rpctls_cl_disconnect(void *socookie, uint32_t *reterr)
338 {
339 	struct rpctlscd_disconnect_arg arg;
340 	struct rpctlscd_disconnect_res res;
341 	enum clnt_stat stat;
342 
343 	/* Do the disconnect upcall. */
344 	arg.socookie = (uint64_t)socookie;
345 	stat = rpctlscd_disconnect_2(&arg, &res, rpctls_connect_handle);
346 	if (stat == RPC_SUCCESS)
347 		*reterr = res.reterr;
348 	return (stat);
349 }
350 
351 enum clnt_stat
rpctls_srv_disconnect(void * socookie,uint32_t * reterr)352 rpctls_srv_disconnect(void *socookie, uint32_t *reterr)
353 {
354 	struct rpctlssd_disconnect_arg arg;
355 	struct rpctlssd_disconnect_res res;
356 	enum clnt_stat stat;
357 
358 	/* Do the disconnect upcall. */
359 	arg.socookie = (uint64_t)socookie;
360 	stat = rpctlssd_disconnect_2(&arg, &res, rpctls_server_handle);
361 	if (stat == RPC_SUCCESS)
362 		*reterr = res.reterr;
363 	return (stat);
364 }
365 
366 /* Do an upcall for a new server socket using TLS. */
367 static enum clnt_stat
rpctls_server(SVCXPRT * xprt,uint32_t * flags,uid_t * uid,int * ngrps,gid_t ** gids)368 rpctls_server(SVCXPRT *xprt, uint32_t *flags, uid_t *uid, int *ngrps,
369     gid_t **gids)
370 {
371 	enum clnt_stat stat;
372 	struct upsock ups = {
373 		.so = xprt->xp_socket,
374 		.xp = xprt,
375 		.server = true,
376 	};
377 	struct rpctlssd_connect_arg arg;
378 	struct rpctlssd_connect_res res;
379 	gid_t *gidp;
380 	uint32_t *gidv;
381 	int i;
382 
383 	mtx_lock(&rpctls_lock);
384 	RB_INSERT(upsock_t, &upcall_sockets, &ups);
385 	mtx_unlock(&rpctls_lock);
386 
387 	/* Do the server upcall. */
388 	res.gid.gid_val = NULL;
389 	arg.socookie = (uint64_t)xprt->xp_socket;
390 	stat = rpctlssd_connect_2(&arg, &res, rpctls_server_handle);
391 	if (stat == RPC_SUCCESS) {
392 		*flags = res.flags;
393 		if ((*flags & (RPCTLS_FLAGS_CERTUSER |
394 		    RPCTLS_FLAGS_DISABLED)) == RPCTLS_FLAGS_CERTUSER) {
395 			*ngrps = res.gid.gid_len;
396 			*uid = res.uid;
397 			*gids = gidp = mem_alloc(*ngrps * sizeof(gid_t));
398 			gidv = res.gid.gid_val;
399 			for (i = 0; i < *ngrps; i++)
400 				*gidp++ = *gidv++;
401 		}
402 	} else
403 		rpctls_rpc_failed(&ups, xprt->xp_socket);
404 
405 	mem_free(res.gid.gid_val, 0);
406 
407 #ifdef INVARIANTS
408 	mtx_lock(&rpctls_lock);
409 	MPASS((RB_FIND(upsock_t, &upcall_sockets, &ups) == NULL));
410 	mtx_unlock(&rpctls_lock);
411 #endif
412 
413 	return (stat);
414 }
415 
416 /*
417  * Handle the NULL RPC with authentication flavor of AUTH_TLS.
418  * This is a STARTTLS command, so do the upcall to the rpctlssd daemon,
419  * which will do the TLS handshake.
420  */
421 enum auth_stat
_svcauth_rpcsec_tls(struct svc_req * rqst,struct rpc_msg * msg)422 _svcauth_rpcsec_tls(struct svc_req *rqst, struct rpc_msg *msg)
423 
424 {
425 	bool_t call_stat;
426 	enum clnt_stat stat;
427 	SVCXPRT *xprt;
428 	uint32_t flags;
429 	int ngrps;
430 	uid_t uid;
431 	gid_t *gidp;
432 #ifdef KERN_TLS
433 	u_int maxlen;
434 #endif
435 
436 	KRPC_CURVNET_SET_QUIET(KRPC_TD_TO_VNET(curthread));
437 	KRPC_VNET(svc_vc_tls_handshake_failed)++;
438 	/* Initialize reply. */
439 	rqst->rq_verf = rpctls_null_verf;
440 
441 	/* Check client credentials. */
442 	if (rqst->rq_cred.oa_length != 0 ||
443 	    msg->rm_call.cb_verf.oa_length != 0 ||
444 	    msg->rm_call.cb_verf.oa_flavor != AUTH_NULL) {
445 		KRPC_CURVNET_RESTORE();
446 		return (AUTH_BADCRED);
447 	}
448 
449 	if (rqst->rq_proc != NULLPROC) {
450 		KRPC_CURVNET_RESTORE();
451 		return (AUTH_REJECTEDCRED);
452 	}
453 
454 	call_stat = FALSE;
455 #ifdef KERN_TLS
456 	if (rpctls_getinfo(&maxlen, false, true))
457 		call_stat = TRUE;
458 #endif
459 	if (!call_stat) {
460 		KRPC_CURVNET_RESTORE();
461 		return (AUTH_REJECTEDCRED);
462 	}
463 
464 	/*
465 	 * Disable reception for the krpc so that the TLS handshake can
466 	 * be done on the socket in the rpctlssd daemon.
467 	 */
468 	xprt = rqst->rq_xprt;
469 	sx_xlock(&xprt->xp_lock);
470 	xprt->xp_dontrcv = TRUE;
471 	sx_xunlock(&xprt->xp_lock);
472 
473 	/*
474 	 * Send the reply to the NULL RPC with AUTH_TLS, which is the
475 	 * STARTTLS command for Sun RPC.
476 	 */
477 	call_stat = svc_sendreply(rqst, (xdrproc_t)xdr_void, NULL);
478 	if (!call_stat) {
479 		sx_xlock(&xprt->xp_lock);
480 		xprt->xp_dontrcv = FALSE;
481 		sx_xunlock(&xprt->xp_lock);
482 		xprt_active(xprt);	/* Harmless if already active. */
483 		KRPC_CURVNET_RESTORE();
484 		return (AUTH_REJECTEDCRED);
485 	}
486 
487 	/* Do an upcall to do the TLS handshake. */
488 	stat = rpctls_server(xprt, &flags, &uid, &ngrps, &gidp);
489 
490 	/* Re-enable reception on the socket within the krpc. */
491 	sx_xlock(&xprt->xp_lock);
492 	xprt->xp_dontrcv = FALSE;
493 	if (stat == RPC_SUCCESS) {
494 		xprt->xp_tls = flags;
495 		if ((flags & (RPCTLS_FLAGS_CERTUSER |
496 		    RPCTLS_FLAGS_DISABLED)) == RPCTLS_FLAGS_CERTUSER) {
497 			xprt->xp_ngrps = ngrps;
498 			xprt->xp_uid = uid;
499 			xprt->xp_gidp = gidp;
500 		}
501 		KRPC_VNET(svc_vc_tls_handshake_failed)--;
502 		KRPC_VNET(svc_vc_tls_handshake_success)++;
503 	}
504 	sx_xunlock(&xprt->xp_lock);
505 	xprt_active(xprt);		/* Harmless if already active. */
506 	KRPC_CURVNET_RESTORE();
507 
508 	return (RPCSEC_GSS_NODISPATCH);
509 }
510 
511 /*
512  * Get kern.ipc.tls.enable and kern.ipc.tls.maxlen.
513  */
514 bool
rpctls_getinfo(u_int * maxlenp,bool rpctlscd_run,bool rpctlssd_run)515 rpctls_getinfo(u_int *maxlenp, bool rpctlscd_run, bool rpctlssd_run)
516 {
517 	u_int maxlen;
518 	bool enable;
519 	int error;
520 	size_t siz;
521 
522 	if (!mb_use_ext_pgs)
523 		return (false);
524 	siz = sizeof(enable);
525 	error = kernel_sysctlbyname(curthread, "kern.ipc.tls.enable",
526 	    &enable, &siz, NULL, 0, NULL, 0);
527 	if (error != 0)
528 		return (false);
529 	siz = sizeof(maxlen);
530 	error = kernel_sysctlbyname(curthread, "kern.ipc.tls.maxlen",
531 	    &maxlen, &siz, NULL, 0, NULL, 0);
532 	if (error != 0)
533 		return (false);
534 	*maxlenp = maxlen;
535 	return (enable);
536 }
537