xref: /freebsd/sys/rpc/rpcsec_tls/rpctls_impl.c (revision 26ee0593920946646882a14997d15e16b1bec772)
1ab0c29afSRick Macklem /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3ab0c29afSRick Macklem  *
4ab0c29afSRick Macklem  * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
5ab0c29afSRick Macklem  * Authors: Doug Rabson <dfr@rabson.org>
6ab0c29afSRick Macklem  * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
7ab0c29afSRick Macklem  *
8ab0c29afSRick Macklem  * Redistribution and use in source and binary forms, with or without
9ab0c29afSRick Macklem  * modification, are permitted provided that the following conditions
10ab0c29afSRick Macklem  * are met:
11ab0c29afSRick Macklem  * 1. Redistributions of source code must retain the above copyright
12ab0c29afSRick Macklem  *    notice, this list of conditions and the following disclaimer.
13ab0c29afSRick Macklem  * 2. Redistributions in binary form must reproduce the above copyright
14ab0c29afSRick Macklem  *    notice, this list of conditions and the following disclaimer in the
15ab0c29afSRick Macklem  *    documentation and/or other materials provided with the distribution.
16ab0c29afSRick Macklem  *
17ab0c29afSRick Macklem  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18ab0c29afSRick Macklem  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19ab0c29afSRick Macklem  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20ab0c29afSRick Macklem  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21ab0c29afSRick Macklem  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22ab0c29afSRick Macklem  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23ab0c29afSRick Macklem  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24ab0c29afSRick Macklem  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25ab0c29afSRick Macklem  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26ab0c29afSRick Macklem  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27ab0c29afSRick Macklem  * SUCH DAMAGE.
28ab0c29afSRick Macklem  */
29ab0c29afSRick Macklem 
30ab0c29afSRick Macklem /* Modified from the kernel GSSAPI code for RPC-over-TLS. */
31ab0c29afSRick Macklem 
32ab0c29afSRick Macklem #include <sys/cdefs.h>
33ab0c29afSRick Macklem #include "opt_kern_tls.h"
34ab0c29afSRick Macklem 
35ab0c29afSRick Macklem #include <sys/param.h>
36ab0c29afSRick Macklem #include <sys/capsicum.h>
37ab0c29afSRick Macklem #include <sys/file.h>
38ab0c29afSRick Macklem #include <sys/filedesc.h>
396444662aSRick Macklem #include <sys/jail.h>
40ab0c29afSRick Macklem #include <sys/kernel.h>
41ab0c29afSRick Macklem #include <sys/lock.h>
42ab0c29afSRick Macklem #include <sys/malloc.h>
43ab0c29afSRick Macklem #include <sys/mbuf.h>
44ab0c29afSRick Macklem #include <sys/mutex.h>
45ab0c29afSRick Macklem #include <sys/priv.h>
46ab0c29afSRick Macklem #include <sys/proc.h>
47ab0c29afSRick Macklem #include <sys/socketvar.h>
48ab0c29afSRick Macklem #include <sys/syscall.h>
49ab0c29afSRick Macklem #include <sys/syscallsubr.h>
50ab0c29afSRick Macklem #include <sys/sysent.h>
51ab0c29afSRick Macklem #include <sys/sysproto.h>
52a3a6dc24SGleb Smirnoff #include <sys/tree.h>
53ab0c29afSRick Macklem 
546444662aSRick Macklem #include <net/vnet.h>
556444662aSRick Macklem 
56ab0c29afSRick Macklem #include <rpc/rpc.h>
57ab0c29afSRick Macklem #include <rpc/rpc_com.h>
5856a96c51SGleb Smirnoff #include <rpc/krpc.h>
59ab0c29afSRick Macklem #include <rpc/rpcsec_tls.h>
60ab0c29afSRick Macklem 
61ab0c29afSRick Macklem #include <vm/vm.h>
62ab0c29afSRick Macklem #include <vm/pmap.h>
63ab0c29afSRick Macklem #include <vm/vm_param.h>
64ab0c29afSRick Macklem 
65ab0c29afSRick Macklem #include "rpctlscd.h"
66ab0c29afSRick Macklem #include "rpctlssd.h"
67ab0c29afSRick Macklem 
68ab0c29afSRick Macklem /*
69ab0c29afSRick Macklem  * Syscall hooks
70ab0c29afSRick Macklem  */
71ab0c29afSRick Macklem static struct syscall_helper_data rpctls_syscalls[] = {
72ab0c29afSRick Macklem 	SYSCALL_INIT_HELPER(rpctls_syscall),
73ab0c29afSRick Macklem 	SYSCALL_INIT_LAST
74ab0c29afSRick Macklem };
75ab0c29afSRick Macklem 
76ab0c29afSRick Macklem static struct opaque_auth rpctls_null_verf;
77ab0c29afSRick Macklem 
781a878807SRick Macklem KRPC_VNET_DECLARE(uint64_t, svc_vc_tls_handshake_success);
791a878807SRick Macklem KRPC_VNET_DECLARE(uint64_t, svc_vc_tls_handshake_failed);
801a878807SRick Macklem 
8196104caaSGleb Smirnoff static CLIENT *rpctls_connect_handle;
8296104caaSGleb Smirnoff static CLIENT *rpctls_server_handle;
836444662aSRick Macklem 
84a3a6dc24SGleb Smirnoff struct upsock {
85a3a6dc24SGleb Smirnoff 	RB_ENTRY(upsock) tree;
86a3a6dc24SGleb Smirnoff 	struct socket *so;
8742eec520SGleb Smirnoff 	union {
88a3a6dc24SGleb Smirnoff 		CLIENT *cl;
8942eec520SGleb Smirnoff 		SVCXPRT *xp;
9042eec520SGleb Smirnoff 	};
91765ad4f0SGleb Smirnoff 	bool server;
92a3a6dc24SGleb Smirnoff };
93a3a6dc24SGleb Smirnoff 
94a3a6dc24SGleb Smirnoff static RB_HEAD(upsock_t, upsock) upcall_sockets;
95a3a6dc24SGleb Smirnoff static intptr_t
upsock_compare(const struct upsock * a,const struct upsock * b)96a3a6dc24SGleb Smirnoff upsock_compare(const struct upsock *a, const struct upsock *b)
97a3a6dc24SGleb Smirnoff {
98a3a6dc24SGleb Smirnoff 	return ((intptr_t)((uintptr_t)a->so/2 - (uintptr_t)b->so/2));
99a3a6dc24SGleb Smirnoff }
100a3a6dc24SGleb Smirnoff RB_GENERATE_STATIC(upsock_t, upsock, tree, upsock_compare);
10142eec520SGleb Smirnoff static struct mtx rpctls_lock;
102a3a6dc24SGleb Smirnoff 
103af805255SGleb Smirnoff static enum clnt_stat	rpctls_server(SVCXPRT *xprt, uint32_t *flags,
104af805255SGleb Smirnoff     uid_t *uid, int *ngrps, gid_t **gids);
105ab0c29afSRick Macklem 
10642eec520SGleb Smirnoff static CLIENT *
rpctls_client_nl_create(const char * group,const rpcprog_t program,const rpcvers_t version)10742eec520SGleb Smirnoff rpctls_client_nl_create(const char *group, const rpcprog_t program,
10842eec520SGleb Smirnoff     const rpcvers_t version)
1096444662aSRick Macklem {
110a3a6dc24SGleb Smirnoff 	CLIENT *cl;
1116444662aSRick Macklem 
11242eec520SGleb Smirnoff 	cl = client_nl_create(group, program, version);
113a3a6dc24SGleb Smirnoff 	KASSERT(cl, ("%s: netlink client already exist", __func__));
114a3a6dc24SGleb Smirnoff 	/*
115a3a6dc24SGleb Smirnoff 	 * Set the try_count to 1 so that no retries of the RPC occur.  Since
116a3a6dc24SGleb Smirnoff 	 * it is an upcall to a local daemon, requests should not be lost and
117a3a6dc24SGleb Smirnoff 	 * doing one of these RPCs multiple times is not correct.  If the
118a3a6dc24SGleb Smirnoff 	 * server is not working correctly, the daemon can get stuck in
119a3a6dc24SGleb Smirnoff 	 * SSL_connect() trying to read data from the socket during the upcall.
120a3a6dc24SGleb Smirnoff 	 * Set a timeout (currently 15sec) and assume the daemon is hung when
121a3a6dc24SGleb Smirnoff 	 *  the timeout occurs.
122a3a6dc24SGleb Smirnoff 	 */
123a3a6dc24SGleb Smirnoff 	clnt_control(cl, CLSET_RETRIES, &(int){1});
124a3a6dc24SGleb Smirnoff 	clnt_control(cl, CLSET_TIMEOUT, &(struct timeval){.tv_sec = 15});
12542eec520SGleb Smirnoff 	clnt_control(cl, CLSET_WAITCHAN, __DECONST(char *, group));
12642eec520SGleb Smirnoff 
12742eec520SGleb Smirnoff 	return (cl);
12842eec520SGleb Smirnoff }
12942eec520SGleb Smirnoff 
130ab0c29afSRick Macklem int
rpctls_init(void)131ab0c29afSRick Macklem rpctls_init(void)
132ab0c29afSRick Macklem {
1336444662aSRick Macklem 	int error;
134ab0c29afSRick Macklem 
135ab0c29afSRick Macklem 	error = syscall_helper_register(rpctls_syscalls, SY_THR_STATIC_KLD);
136ab0c29afSRick Macklem 	if (error != 0) {
137ab0c29afSRick Macklem 		printf("rpctls_init: cannot register syscall\n");
138ab0c29afSRick Macklem 		return (error);
139ab0c29afSRick Macklem 	}
14042eec520SGleb Smirnoff 	mtx_init(&rpctls_lock, "rpctls lock", NULL, MTX_DEF);
141ab0c29afSRick Macklem 	rpctls_null_verf.oa_flavor = AUTH_NULL;
142ab0c29afSRick Macklem 	rpctls_null_verf.oa_base = RPCTLS_START_STRING;
143ab0c29afSRick Macklem 	rpctls_null_verf.oa_length = strlen(RPCTLS_START_STRING);
14496104caaSGleb Smirnoff 	rpctls_connect_handle = rpctls_client_nl_create("tlsclnt",
14596104caaSGleb Smirnoff 	    RPCTLSCD, RPCTLSCDVERS);
14696104caaSGleb Smirnoff 	rpctls_server_handle = rpctls_client_nl_create("tlsserv",
14796104caaSGleb Smirnoff 	    RPCTLSSD, RPCTLSSDVERS);
148ab0c29afSRick Macklem 	return (0);
149ab0c29afSRick Macklem }
150ab0c29afSRick Macklem 
151ab0c29afSRick Macklem int
sys_rpctls_syscall(struct thread * td,struct rpctls_syscall_args * uap)152ab0c29afSRick Macklem sys_rpctls_syscall(struct thread *td, struct rpctls_syscall_args *uap)
153ab0c29afSRick Macklem {
154ab0c29afSRick Macklem 	struct file *fp;
155765ad4f0SGleb Smirnoff 	struct upsock *upsp, ups;
15642eec520SGleb Smirnoff 	int fd = -1, error;
157ab0c29afSRick Macklem 
158ab0c29afSRick Macklem 	error = priv_check(td, PRIV_NFS_DAEMON);
159ab0c29afSRick Macklem 	if (error != 0)
160ab0c29afSRick Macklem 		return (error);
161ab0c29afSRick Macklem 
1626444662aSRick Macklem 	KRPC_CURVNET_SET(KRPC_TD_TO_VNET(td));
16342eec520SGleb Smirnoff 	mtx_lock(&rpctls_lock);
164765ad4f0SGleb Smirnoff 	upsp = RB_FIND(upsock_t, &upcall_sockets,
165a3a6dc24SGleb Smirnoff 	    &(struct upsock){
166765ad4f0SGleb Smirnoff 	    .so = __DECONST(struct socket *, uap->socookie) });
167765ad4f0SGleb Smirnoff 	if (__predict_true(upsp != NULL)) {
168765ad4f0SGleb Smirnoff 		RB_REMOVE(upsock_t, &upcall_sockets, upsp);
169ab0c29afSRick Macklem 		/*
170765ad4f0SGleb Smirnoff 		 * The upsp points to stack of NFS mounting thread.  Even
171765ad4f0SGleb Smirnoff 		 * though we removed it from the tree, we still don't own it.
172765ad4f0SGleb Smirnoff 		 * Make a copy before releasing the lock.  The mounting thread
173765ad4f0SGleb Smirnoff 		 * may timeout the RPC and unroll its stack.
174ab0c29afSRick Macklem 		 */
175765ad4f0SGleb Smirnoff 		ups = *upsp;
176765ad4f0SGleb Smirnoff 	}
177765ad4f0SGleb Smirnoff 	mtx_unlock(&rpctls_lock);
178765ad4f0SGleb Smirnoff 	if (upsp == NULL) {
179765ad4f0SGleb Smirnoff 		KRPC_CURVNET_RESTORE();
180765ad4f0SGleb Smirnoff 		printf("%s: socket lookup failed\n", __func__);
181765ad4f0SGleb Smirnoff 		return (EPERM);
182765ad4f0SGleb Smirnoff 	}
183765ad4f0SGleb Smirnoff 	if ((error = falloc(td, &fp, &fd, 0)) != 0) {
184*26ee0593SRick Macklem 		/*
185*26ee0593SRick Macklem 		 * The socket will not be acquired by the daemon,
186*26ee0593SRick Macklem 		 * but has been removed from the upcall socket RB.
187*26ee0593SRick Macklem 		 * As such, it needs to be closed here.
188*26ee0593SRick Macklem 		 */
189*26ee0593SRick Macklem 		soclose(ups.so);
190765ad4f0SGleb Smirnoff 		KRPC_CURVNET_RESTORE();
191765ad4f0SGleb Smirnoff 		return (error);
192765ad4f0SGleb Smirnoff 	}
193765ad4f0SGleb Smirnoff 	soref(ups.so);
194765ad4f0SGleb Smirnoff 	if (ups.server) {
195ab0c29afSRick Macklem 		/*
196ab0c29afSRick Macklem 		 * Once this file descriptor is associated
197ab0c29afSRick Macklem 		 * with the socket, it cannot be closed by
198ab0c29afSRick Macklem 		 * the server side krpc code (svc_vc.c).
199ab0c29afSRick Macklem 		 */
200765ad4f0SGleb Smirnoff 		sx_xlock(&ups.xp->xp_lock);
201765ad4f0SGleb Smirnoff 		ups.xp->xp_tls = RPCTLS_FLAGS_HANDSHFAIL;
202765ad4f0SGleb Smirnoff 		sx_xunlock(&ups.xp->xp_lock);
203765ad4f0SGleb Smirnoff 	} else {
204765ad4f0SGleb Smirnoff 		/*
205765ad4f0SGleb Smirnoff 		 * Initialize TLS state so that clnt_vc_destroy() will
206765ad4f0SGleb Smirnoff 		 * not close the socket and will leave that for the
207765ad4f0SGleb Smirnoff 		 * daemon to do.
208765ad4f0SGleb Smirnoff 		 */
209765ad4f0SGleb Smirnoff 		CLNT_CONTROL(ups.cl, CLSET_TLS, &(int){RPCTLS_INHANDSHAKE});
21042eec520SGleb Smirnoff 	}
211765ad4f0SGleb Smirnoff 	finit(fp, FREAD | FWRITE, DTYPE_SOCKET, ups.so, &socketops);
212ab0c29afSRick Macklem 	fdrop(fp, td);	/* Drop fp reference. */
213ab0c29afSRick Macklem 	td->td_retval[0] = fd;
2146444662aSRick Macklem 	KRPC_CURVNET_RESTORE();
215ab0c29afSRick Macklem 
216ab0c29afSRick Macklem 	return (error);
217ab0c29afSRick Macklem }
218ab0c29afSRick Macklem 
21975a884f4SGleb Smirnoff /* Error handling for both client and server failed RPC upcalls. */
22075a884f4SGleb Smirnoff static void
rpctls_rpc_failed(struct upsock * ups,struct socket * so)22175a884f4SGleb Smirnoff rpctls_rpc_failed(struct upsock *ups, struct socket *so)
22275a884f4SGleb Smirnoff {
22375a884f4SGleb Smirnoff 
22475a884f4SGleb Smirnoff 	mtx_lock(&rpctls_lock);
22575a884f4SGleb Smirnoff 	if (RB_FIND(upsock_t, &upcall_sockets, ups)) {
22675a884f4SGleb Smirnoff 		struct upsock *removed __diagused;
22775a884f4SGleb Smirnoff 
22875a884f4SGleb Smirnoff 		removed = RB_REMOVE(upsock_t, &upcall_sockets, ups);
22975a884f4SGleb Smirnoff 		mtx_unlock(&rpctls_lock);
23075a884f4SGleb Smirnoff 		MPASS(removed == ups);
23175a884f4SGleb Smirnoff 		/*
232*26ee0593SRick Macklem 		 * Since the socket was still in the RB tree when
233*26ee0593SRick Macklem 		 * this function was called, the daemon will not
234*26ee0593SRick Macklem 		 * close it.  As such, it needs to be closed here.
23575a884f4SGleb Smirnoff 		 */
236*26ee0593SRick Macklem 		soclose(so);
23775a884f4SGleb Smirnoff 	} else {
23875a884f4SGleb Smirnoff 		/*
23975a884f4SGleb Smirnoff 		 * The daemon has taken the socket from the tree, but
24075a884f4SGleb Smirnoff 		 * failed to do the handshake.
24175a884f4SGleb Smirnoff 		 */
24275a884f4SGleb Smirnoff 		mtx_unlock(&rpctls_lock);
24375a884f4SGleb Smirnoff 	}
24475a884f4SGleb Smirnoff }
24575a884f4SGleb Smirnoff 
246ab0c29afSRick Macklem /* Do an upcall for a new socket connect using TLS. */
247ab0c29afSRick Macklem enum clnt_stat
rpctls_connect(CLIENT * newclient,char * certname,struct socket * so,uint32_t * reterr)248665b1365SRick Macklem rpctls_connect(CLIENT *newclient, char *certname, struct socket *so,
24956a96c51SGleb Smirnoff     uint32_t *reterr)
250ab0c29afSRick Macklem {
251665b1365SRick Macklem 	struct rpctlscd_connect_arg arg;
252ab0c29afSRick Macklem 	struct rpctlscd_connect_res res;
253ab0c29afSRick Macklem 	struct rpc_callextra ext;
254ab0c29afSRick Macklem 	enum clnt_stat stat;
255a3a6dc24SGleb Smirnoff 	struct upsock ups = {
256a3a6dc24SGleb Smirnoff 		.so = so,
257a3a6dc24SGleb Smirnoff 		.cl = newclient,
258765ad4f0SGleb Smirnoff 		.server = false,
259a3a6dc24SGleb Smirnoff 	};
260ab0c29afSRick Macklem 
261ab0c29afSRick Macklem 	/* First, do the AUTH_TLS NULL RPC. */
262ab0c29afSRick Macklem 	memset(&ext, 0, sizeof(ext));
263ab0c29afSRick Macklem 	ext.rc_auth = authtls_create();
264ab0c29afSRick Macklem 	stat = clnt_call_private(newclient, &ext, NULLPROC, (xdrproc_t)xdr_void,
26556a96c51SGleb Smirnoff 	    NULL, (xdrproc_t)xdr_void, NULL, (struct timeval){ .tv_sec = 30 });
266ab0c29afSRick Macklem 	AUTH_DESTROY(ext.rc_auth);
267ab0c29afSRick Macklem 	if (stat == RPC_AUTHERROR)
268ab0c29afSRick Macklem 		return (stat);
269ab0c29afSRick Macklem 	if (stat != RPC_SUCCESS)
270ab0c29afSRick Macklem 		return (RPC_SYSTEMERROR);
271ab0c29afSRick Macklem 
27242eec520SGleb Smirnoff 	mtx_lock(&rpctls_lock);
273a3a6dc24SGleb Smirnoff 	RB_INSERT(upsock_t, &upcall_sockets, &ups);
27442eec520SGleb Smirnoff 	mtx_unlock(&rpctls_lock);
275ab0c29afSRick Macklem 
276ab0c29afSRick Macklem 	/* Temporarily block reception during the handshake upcall. */
27756a96c51SGleb Smirnoff 	CLNT_CONTROL(newclient, CLSET_BLOCKRCV, &(int){1});
278ab0c29afSRick Macklem 
279ab0c29afSRick Macklem 	/* Do the connect handshake upcall. */
280665b1365SRick Macklem 	if (certname != NULL) {
281665b1365SRick Macklem 		arg.certname.certname_len = strlen(certname);
282665b1365SRick Macklem 		arg.certname.certname_val = certname;
283665b1365SRick Macklem 	} else
284665b1365SRick Macklem 		arg.certname.certname_len = 0;
28556a96c51SGleb Smirnoff 	arg.socookie = (uint64_t)so;
28696104caaSGleb Smirnoff 	stat = rpctlscd_connect_2(&arg, &res, rpctls_connect_handle);
28775a884f4SGleb Smirnoff 	if (stat == RPC_SUCCESS)
288ab0c29afSRick Macklem 		*reterr = res.reterr;
28975a884f4SGleb Smirnoff 	else
29075a884f4SGleb Smirnoff 		rpctls_rpc_failed(&ups, so);
291ab0c29afSRick Macklem 
292ab0c29afSRick Macklem 	/* Unblock reception. */
29356a96c51SGleb Smirnoff 	CLNT_CONTROL(newclient, CLSET_BLOCKRCV, &(int){0});
294ab0c29afSRick Macklem 
29575a884f4SGleb Smirnoff #ifdef INVARIANTS
29675a884f4SGleb Smirnoff 	mtx_lock(&rpctls_lock);
29775a884f4SGleb Smirnoff 	MPASS((RB_FIND(upsock_t, &upcall_sockets, &ups) == NULL));
29875a884f4SGleb Smirnoff 	mtx_unlock(&rpctls_lock);
29975a884f4SGleb Smirnoff #endif
30075a884f4SGleb Smirnoff 
301ab0c29afSRick Macklem 	return (stat);
302ab0c29afSRick Macklem }
303ab0c29afSRick Macklem 
304ab0c29afSRick Macklem /* Do an upcall to handle an non-application data record using TLS. */
305ab0c29afSRick Macklem enum clnt_stat
rpctls_cl_handlerecord(void * socookie,uint32_t * reterr)30656a96c51SGleb Smirnoff rpctls_cl_handlerecord(void *socookie, uint32_t *reterr)
307ab0c29afSRick Macklem {
308ab0c29afSRick Macklem 	struct rpctlscd_handlerecord_arg arg;
309ab0c29afSRick Macklem 	struct rpctlscd_handlerecord_res res;
310ab0c29afSRick Macklem 	enum clnt_stat stat;
311ab0c29afSRick Macklem 
312ab0c29afSRick Macklem 	/* Do the handlerecord upcall. */
31356a96c51SGleb Smirnoff 	arg.socookie = (uint64_t)socookie;
31496104caaSGleb Smirnoff 	stat = rpctlscd_handlerecord_2(&arg, &res, rpctls_connect_handle);
315ab0c29afSRick Macklem 	if (stat == RPC_SUCCESS)
316ab0c29afSRick Macklem 		*reterr = res.reterr;
317ab0c29afSRick Macklem 	return (stat);
318ab0c29afSRick Macklem }
319ab0c29afSRick Macklem 
320ab0c29afSRick Macklem enum clnt_stat
rpctls_srv_handlerecord(void * socookie,uint32_t * reterr)321af805255SGleb Smirnoff rpctls_srv_handlerecord(void *socookie, uint32_t *reterr)
322ab0c29afSRick Macklem {
323ab0c29afSRick Macklem 	struct rpctlssd_handlerecord_arg arg;
324ab0c29afSRick Macklem 	struct rpctlssd_handlerecord_res res;
325ab0c29afSRick Macklem 	enum clnt_stat stat;
326ab0c29afSRick Macklem 
327ab0c29afSRick Macklem 	/* Do the handlerecord upcall. */
328af805255SGleb Smirnoff 	arg.socookie = (uint64_t)socookie;
32996104caaSGleb Smirnoff 	stat = rpctlssd_handlerecord_2(&arg, &res, rpctls_server_handle);
330ab0c29afSRick Macklem 	if (stat == RPC_SUCCESS)
331ab0c29afSRick Macklem 		*reterr = res.reterr;
332ab0c29afSRick Macklem 	return (stat);
333ab0c29afSRick Macklem }
334ab0c29afSRick Macklem 
335ab0c29afSRick Macklem /* Do an upcall to shut down a socket using TLS. */
336ab0c29afSRick Macklem enum clnt_stat
rpctls_cl_disconnect(void * socookie,uint32_t * reterr)33756a96c51SGleb Smirnoff rpctls_cl_disconnect(void *socookie, uint32_t *reterr)
338ab0c29afSRick Macklem {
339ab0c29afSRick Macklem 	struct rpctlscd_disconnect_arg arg;
340ab0c29afSRick Macklem 	struct rpctlscd_disconnect_res res;
341ab0c29afSRick Macklem 	enum clnt_stat stat;
342ab0c29afSRick Macklem 
343ab0c29afSRick Macklem 	/* Do the disconnect upcall. */
34456a96c51SGleb Smirnoff 	arg.socookie = (uint64_t)socookie;
34596104caaSGleb Smirnoff 	stat = rpctlscd_disconnect_2(&arg, &res, rpctls_connect_handle);
346ab0c29afSRick Macklem 	if (stat == RPC_SUCCESS)
347ab0c29afSRick Macklem 		*reterr = res.reterr;
348ab0c29afSRick Macklem 	return (stat);
349ab0c29afSRick Macklem }
350ab0c29afSRick Macklem 
351ab0c29afSRick Macklem enum clnt_stat
rpctls_srv_disconnect(void * socookie,uint32_t * reterr)352af805255SGleb Smirnoff rpctls_srv_disconnect(void *socookie, uint32_t *reterr)
353ab0c29afSRick Macklem {
354ab0c29afSRick Macklem 	struct rpctlssd_disconnect_arg arg;
355ab0c29afSRick Macklem 	struct rpctlssd_disconnect_res res;
356ab0c29afSRick Macklem 	enum clnt_stat stat;
357ab0c29afSRick Macklem 
358ab0c29afSRick Macklem 	/* Do the disconnect upcall. */
359af805255SGleb Smirnoff 	arg.socookie = (uint64_t)socookie;
36096104caaSGleb Smirnoff 	stat = rpctlssd_disconnect_2(&arg, &res, rpctls_server_handle);
361ab0c29afSRick Macklem 	if (stat == RPC_SUCCESS)
362ab0c29afSRick Macklem 		*reterr = res.reterr;
363ab0c29afSRick Macklem 	return (stat);
364ab0c29afSRick Macklem }
365ab0c29afSRick Macklem 
366ab0c29afSRick Macklem /* Do an upcall for a new server socket using TLS. */
367ab0c29afSRick Macklem static enum clnt_stat
rpctls_server(SVCXPRT * xprt,uint32_t * flags,uid_t * uid,int * ngrps,gid_t ** gids)368af805255SGleb Smirnoff rpctls_server(SVCXPRT *xprt, uint32_t *flags, uid_t *uid, int *ngrps,
369af805255SGleb Smirnoff     gid_t **gids)
370ab0c29afSRick Macklem {
371ab0c29afSRick Macklem 	enum clnt_stat stat;
37242eec520SGleb Smirnoff 	struct upsock ups = {
373af805255SGleb Smirnoff 		.so = xprt->xp_socket,
37442eec520SGleb Smirnoff 		.xp = xprt,
375765ad4f0SGleb Smirnoff 		.server = true,
37642eec520SGleb Smirnoff 	};
37742eec520SGleb Smirnoff 	struct rpctlssd_connect_arg arg;
378ab0c29afSRick Macklem 	struct rpctlssd_connect_res res;
379ab0c29afSRick Macklem 	gid_t *gidp;
380ab0c29afSRick Macklem 	uint32_t *gidv;
38142eec520SGleb Smirnoff 	int i;
382ab0c29afSRick Macklem 
38342eec520SGleb Smirnoff 	mtx_lock(&rpctls_lock);
38442eec520SGleb Smirnoff 	RB_INSERT(upsock_t, &upcall_sockets, &ups);
38542eec520SGleb Smirnoff 	mtx_unlock(&rpctls_lock);
386ab0c29afSRick Macklem 
387ab0c29afSRick Macklem 	/* Do the server upcall. */
388f6dc363fSRick Macklem 	res.gid.gid_val = NULL;
389af805255SGleb Smirnoff 	arg.socookie = (uint64_t)xprt->xp_socket;
39096104caaSGleb Smirnoff 	stat = rpctlssd_connect_2(&arg, &res, rpctls_server_handle);
391ab0c29afSRick Macklem 	if (stat == RPC_SUCCESS) {
392ab0c29afSRick Macklem 		*flags = res.flags;
393ab0c29afSRick Macklem 		if ((*flags & (RPCTLS_FLAGS_CERTUSER |
394ab0c29afSRick Macklem 		    RPCTLS_FLAGS_DISABLED)) == RPCTLS_FLAGS_CERTUSER) {
395ab0c29afSRick Macklem 			*ngrps = res.gid.gid_len;
396ab0c29afSRick Macklem 			*uid = res.uid;
397ab0c29afSRick Macklem 			*gids = gidp = mem_alloc(*ngrps * sizeof(gid_t));
398ab0c29afSRick Macklem 			gidv = res.gid.gid_val;
399ab0c29afSRick Macklem 			for (i = 0; i < *ngrps; i++)
400ab0c29afSRick Macklem 				*gidp++ = *gidv++;
401ab0c29afSRick Macklem 		}
40275a884f4SGleb Smirnoff 	} else
40375a884f4SGleb Smirnoff 		rpctls_rpc_failed(&ups, xprt->xp_socket);
40442eec520SGleb Smirnoff 
405f6dc363fSRick Macklem 	mem_free(res.gid.gid_val, 0);
406ab0c29afSRick Macklem 
40775a884f4SGleb Smirnoff #ifdef INVARIANTS
40875a884f4SGleb Smirnoff 	mtx_lock(&rpctls_lock);
40975a884f4SGleb Smirnoff 	MPASS((RB_FIND(upsock_t, &upcall_sockets, &ups) == NULL));
41075a884f4SGleb Smirnoff 	mtx_unlock(&rpctls_lock);
41175a884f4SGleb Smirnoff #endif
41275a884f4SGleb Smirnoff 
413ab0c29afSRick Macklem 	return (stat);
414ab0c29afSRick Macklem }
415ab0c29afSRick Macklem 
416ab0c29afSRick Macklem /*
417ab0c29afSRick Macklem  * Handle the NULL RPC with authentication flavor of AUTH_TLS.
418ab0c29afSRick Macklem  * This is a STARTTLS command, so do the upcall to the rpctlssd daemon,
419ab0c29afSRick Macklem  * which will do the TLS handshake.
420ab0c29afSRick Macklem  */
421ab0c29afSRick Macklem enum auth_stat
_svcauth_rpcsec_tls(struct svc_req * rqst,struct rpc_msg * msg)422ab0c29afSRick Macklem _svcauth_rpcsec_tls(struct svc_req *rqst, struct rpc_msg *msg)
423ab0c29afSRick Macklem 
424ab0c29afSRick Macklem {
425ab0c29afSRick Macklem 	bool_t call_stat;
426ab0c29afSRick Macklem 	enum clnt_stat stat;
427ab0c29afSRick Macklem 	SVCXPRT *xprt;
428ab0c29afSRick Macklem 	uint32_t flags;
429af805255SGleb Smirnoff 	int ngrps;
430ab0c29afSRick Macklem 	uid_t uid;
431ab0c29afSRick Macklem 	gid_t *gidp;
432ab0c29afSRick Macklem #ifdef KERN_TLS
433ab0c29afSRick Macklem 	u_int maxlen;
434ab0c29afSRick Macklem #endif
435ab0c29afSRick Macklem 
4361a878807SRick Macklem 	KRPC_CURVNET_SET_QUIET(KRPC_TD_TO_VNET(curthread));
4371a878807SRick Macklem 	KRPC_VNET(svc_vc_tls_handshake_failed)++;
438ab0c29afSRick Macklem 	/* Initialize reply. */
439ab0c29afSRick Macklem 	rqst->rq_verf = rpctls_null_verf;
440ab0c29afSRick Macklem 
441ab0c29afSRick Macklem 	/* Check client credentials. */
442ab0c29afSRick Macklem 	if (rqst->rq_cred.oa_length != 0 ||
443ab0c29afSRick Macklem 	    msg->rm_call.cb_verf.oa_length != 0 ||
4441a878807SRick Macklem 	    msg->rm_call.cb_verf.oa_flavor != AUTH_NULL) {
4451a878807SRick Macklem 		KRPC_CURVNET_RESTORE();
446ab0c29afSRick Macklem 		return (AUTH_BADCRED);
4471a878807SRick Macklem 	}
448ab0c29afSRick Macklem 
4491a878807SRick Macklem 	if (rqst->rq_proc != NULLPROC) {
4501a878807SRick Macklem 		KRPC_CURVNET_RESTORE();
451ab0c29afSRick Macklem 		return (AUTH_REJECTEDCRED);
4521a878807SRick Macklem 	}
453ab0c29afSRick Macklem 
454ab0c29afSRick Macklem 	call_stat = FALSE;
455ab0c29afSRick Macklem #ifdef KERN_TLS
456ab0c29afSRick Macklem 	if (rpctls_getinfo(&maxlen, false, true))
457ab0c29afSRick Macklem 		call_stat = TRUE;
458ab0c29afSRick Macklem #endif
4591a878807SRick Macklem 	if (!call_stat) {
4601a878807SRick Macklem 		KRPC_CURVNET_RESTORE();
461ab0c29afSRick Macklem 		return (AUTH_REJECTEDCRED);
4621a878807SRick Macklem 	}
463ab0c29afSRick Macklem 
464ab0c29afSRick Macklem 	/*
465ab0c29afSRick Macklem 	 * Disable reception for the krpc so that the TLS handshake can
466ab0c29afSRick Macklem 	 * be done on the socket in the rpctlssd daemon.
467ab0c29afSRick Macklem 	 */
468ab0c29afSRick Macklem 	xprt = rqst->rq_xprt;
469ab0c29afSRick Macklem 	sx_xlock(&xprt->xp_lock);
470ab0c29afSRick Macklem 	xprt->xp_dontrcv = TRUE;
471ab0c29afSRick Macklem 	sx_xunlock(&xprt->xp_lock);
472ab0c29afSRick Macklem 
473ab0c29afSRick Macklem 	/*
474ab0c29afSRick Macklem 	 * Send the reply to the NULL RPC with AUTH_TLS, which is the
475ab0c29afSRick Macklem 	 * STARTTLS command for Sun RPC.
476ab0c29afSRick Macklem 	 */
477ab0c29afSRick Macklem 	call_stat = svc_sendreply(rqst, (xdrproc_t)xdr_void, NULL);
478ab0c29afSRick Macklem 	if (!call_stat) {
479ab0c29afSRick Macklem 		sx_xlock(&xprt->xp_lock);
480ab0c29afSRick Macklem 		xprt->xp_dontrcv = FALSE;
481ab0c29afSRick Macklem 		sx_xunlock(&xprt->xp_lock);
482ab0c29afSRick Macklem 		xprt_active(xprt);	/* Harmless if already active. */
4831a878807SRick Macklem 		KRPC_CURVNET_RESTORE();
484ab0c29afSRick Macklem 		return (AUTH_REJECTEDCRED);
485ab0c29afSRick Macklem 	}
486ab0c29afSRick Macklem 
487ab0c29afSRick Macklem 	/* Do an upcall to do the TLS handshake. */
488af805255SGleb Smirnoff 	stat = rpctls_server(xprt, &flags, &uid, &ngrps, &gidp);
489ab0c29afSRick Macklem 
490ab0c29afSRick Macklem 	/* Re-enable reception on the socket within the krpc. */
491ab0c29afSRick Macklem 	sx_xlock(&xprt->xp_lock);
492ab0c29afSRick Macklem 	xprt->xp_dontrcv = FALSE;
493ab0c29afSRick Macklem 	if (stat == RPC_SUCCESS) {
494ab0c29afSRick Macklem 		xprt->xp_tls = flags;
495ab0c29afSRick Macklem 		if ((flags & (RPCTLS_FLAGS_CERTUSER |
496ab0c29afSRick Macklem 		    RPCTLS_FLAGS_DISABLED)) == RPCTLS_FLAGS_CERTUSER) {
497ab0c29afSRick Macklem 			xprt->xp_ngrps = ngrps;
498ab0c29afSRick Macklem 			xprt->xp_uid = uid;
499ab0c29afSRick Macklem 			xprt->xp_gidp = gidp;
500ab0c29afSRick Macklem 		}
5011a878807SRick Macklem 		KRPC_VNET(svc_vc_tls_handshake_failed)--;
5021a878807SRick Macklem 		KRPC_VNET(svc_vc_tls_handshake_success)++;
503ab0c29afSRick Macklem 	}
504ab0c29afSRick Macklem 	sx_xunlock(&xprt->xp_lock);
505ab0c29afSRick Macklem 	xprt_active(xprt);		/* Harmless if already active. */
5061a878807SRick Macklem 	KRPC_CURVNET_RESTORE();
507ab0c29afSRick Macklem 
508ab0c29afSRick Macklem 	return (RPCSEC_GSS_NODISPATCH);
509ab0c29afSRick Macklem }
510ab0c29afSRick Macklem 
511ab0c29afSRick Macklem /*
512ab0c29afSRick Macklem  * Get kern.ipc.tls.enable and kern.ipc.tls.maxlen.
513ab0c29afSRick Macklem  */
514ab0c29afSRick Macklem bool
rpctls_getinfo(u_int * maxlenp,bool rpctlscd_run,bool rpctlssd_run)515ab0c29afSRick Macklem rpctls_getinfo(u_int *maxlenp, bool rpctlscd_run, bool rpctlssd_run)
516ab0c29afSRick Macklem {
517ab0c29afSRick Macklem 	u_int maxlen;
518ab0c29afSRick Macklem 	bool enable;
519ab0c29afSRick Macklem 	int error;
520ab0c29afSRick Macklem 	size_t siz;
521ab0c29afSRick Macklem 
522fcaa890cSMark Johnston 	if (!mb_use_ext_pgs)
523ab0c29afSRick Macklem 		return (false);
524ab0c29afSRick Macklem 	siz = sizeof(enable);
525ab0c29afSRick Macklem 	error = kernel_sysctlbyname(curthread, "kern.ipc.tls.enable",
526ab0c29afSRick Macklem 	    &enable, &siz, NULL, 0, NULL, 0);
527ab0c29afSRick Macklem 	if (error != 0)
528ab0c29afSRick Macklem 		return (false);
529ab0c29afSRick Macklem 	siz = sizeof(maxlen);
530ab0c29afSRick Macklem 	error = kernel_sysctlbyname(curthread, "kern.ipc.tls.maxlen",
531ab0c29afSRick Macklem 	    &maxlen, &siz, NULL, 0, NULL, 0);
532ab0c29afSRick Macklem 	if (error != 0)
533ab0c29afSRick Macklem 		return (false);
534ab0c29afSRick Macklem 	*maxlenp = maxlen;
535ab0c29afSRick Macklem 	return (enable);
536ab0c29afSRick Macklem }
537