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