xref: /freebsd/sys/rpc/rpcsec_tls/rpctls_impl.c (revision 1323ec57)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 __FBSDID("$FreeBSD$");
34 
35 #include "opt_kern_tls.h"
36 
37 #include <sys/param.h>
38 #include <sys/capsicum.h>
39 #include <sys/file.h>
40 #include <sys/filedesc.h>
41 #include <sys/kernel.h>
42 #include <sys/lock.h>
43 #include <sys/malloc.h>
44 #include <sys/mbuf.h>
45 #include <sys/mutex.h>
46 #include <sys/priv.h>
47 #include <sys/proc.h>
48 #include <sys/socketvar.h>
49 #include <sys/syscall.h>
50 #include <sys/syscallsubr.h>
51 #include <sys/sysent.h>
52 #include <sys/sysproto.h>
53 
54 #include <rpc/rpc.h>
55 #include <rpc/rpc_com.h>
56 #include <rpc/rpcsec_tls.h>
57 
58 #include <vm/vm.h>
59 #include <vm/pmap.h>
60 #include <vm/vm_param.h>
61 
62 #include "rpctlscd.h"
63 #include "rpctlssd.h"
64 
65 /*
66  * Syscall hooks
67  */
68 static struct syscall_helper_data rpctls_syscalls[] = {
69 	SYSCALL_INIT_HELPER(rpctls_syscall),
70 	SYSCALL_INIT_LAST
71 };
72 
73 static CLIENT		*rpctls_connect_handle;
74 static struct mtx	rpctls_connect_lock;
75 static struct socket	*rpctls_connect_so = NULL;
76 static CLIENT		*rpctls_connect_cl = NULL;
77 static CLIENT		*rpctls_server_handle;
78 static struct mtx	rpctls_server_lock;
79 static struct socket	*rpctls_server_so = NULL;
80 static SVCXPRT		*rpctls_server_xprt = NULL;
81 static struct opaque_auth rpctls_null_verf;
82 
83 static CLIENT		*rpctls_connect_client(void);
84 static CLIENT		*rpctls_server_client(void);
85 static enum clnt_stat	rpctls_server(SVCXPRT *xprt, struct socket *so,
86 			    uint32_t *flags, uint64_t *sslp,
87 			    uid_t *uid, int *ngrps, gid_t **gids);
88 
89 int
90 rpctls_init(void)
91 {
92 	int error;
93 
94 	error = syscall_helper_register(rpctls_syscalls, SY_THR_STATIC_KLD);
95 	if (error != 0) {
96 		printf("rpctls_init: cannot register syscall\n");
97 		return (error);
98 	}
99 	mtx_init(&rpctls_connect_lock, "rpctls_connect_lock", NULL,
100 	    MTX_DEF);
101 	mtx_init(&rpctls_server_lock, "rpctls_server_lock", NULL,
102 	    MTX_DEF);
103 	rpctls_null_verf.oa_flavor = AUTH_NULL;
104 	rpctls_null_verf.oa_base = RPCTLS_START_STRING;
105 	rpctls_null_verf.oa_length = strlen(RPCTLS_START_STRING);
106 	return (0);
107 }
108 
109 int
110 sys_rpctls_syscall(struct thread *td, struct rpctls_syscall_args *uap)
111 {
112         struct sockaddr_un sun;
113         struct netconfig *nconf;
114 	struct file *fp;
115 	struct socket *so;
116 	SVCXPRT *xprt;
117 	char path[MAXPATHLEN];
118 	int fd = -1, error, try_count;
119 	CLIENT *cl, *oldcl, *concl;
120 	uint64_t ssl[3];
121 	struct timeval timeo;
122 #ifdef KERN_TLS
123 	u_int maxlen;
124 #endif
125 
126 	error = priv_check(td, PRIV_NFS_DAEMON);
127 	if (error != 0)
128 		return (error);
129 
130 	switch (uap->op) {
131 	case RPCTLS_SYSC_CLSETPATH:
132 		error = copyinstr(uap->path, path, sizeof(path), NULL);
133 		if (error == 0) {
134 			error = ENXIO;
135 #ifdef KERN_TLS
136 			if (rpctls_getinfo(&maxlen, false, false))
137 				error = 0;
138 #endif
139 		}
140 		if (error == 0 && (strlen(path) + 1 > sizeof(sun.sun_path) ||
141 		    strlen(path) == 0))
142 			error = EINVAL;
143 
144 		cl = NULL;
145 		if (error == 0) {
146 			sun.sun_family = AF_LOCAL;
147 			strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
148 			sun.sun_len = SUN_LEN(&sun);
149 
150 			nconf = getnetconfigent("local");
151 			cl = clnt_reconnect_create(nconf,
152 			    (struct sockaddr *)&sun, RPCTLSCD, RPCTLSCDVERS,
153 			    RPC_MAXDATASIZE, RPC_MAXDATASIZE);
154 			/*
155 			 * The number of retries defaults to INT_MAX, which
156 			 * effectively means an infinite, uninterruptable loop.
157 			 * Set the try_count to 1 so that no retries of the
158 			 * RPC occur.  Since it is an upcall to a local daemon,
159 			 * requests should not be lost and doing one of these
160 			 * RPCs multiple times is not correct.
161 			 * If the server is not working correctly, the
162 			 * daemon can get stuck in SSL_connect() trying
163 			 * to read data from the socket during the upcall.
164 			 * Set a timeout (currently 15sec) and assume the
165 			 * daemon is hung when the timeout occurs.
166 			 */
167 			if (cl != NULL) {
168 				try_count = 1;
169 				CLNT_CONTROL(cl, CLSET_RETRIES, &try_count);
170 				timeo.tv_sec = 15;
171 				timeo.tv_usec = 0;
172 				CLNT_CONTROL(cl, CLSET_TIMEOUT, &timeo);
173 			} else
174 				error = EINVAL;
175 		}
176 
177 		mtx_lock(&rpctls_connect_lock);
178 		oldcl = rpctls_connect_handle;
179 		rpctls_connect_handle = cl;
180 		mtx_unlock(&rpctls_connect_lock);
181 
182 		if (oldcl != NULL) {
183 			CLNT_CLOSE(oldcl);
184 			CLNT_RELEASE(oldcl);
185 		}
186 		break;
187 	case RPCTLS_SYSC_SRVSETPATH:
188 		error = copyinstr(uap->path, path, sizeof(path), NULL);
189 		if (error == 0) {
190 			error = ENXIO;
191 #ifdef KERN_TLS
192 			if (rpctls_getinfo(&maxlen, false, false))
193 				error = 0;
194 #endif
195 		}
196 		if (error == 0 && (strlen(path) + 1 > sizeof(sun.sun_path) ||
197 		    strlen(path) == 0))
198 			error = EINVAL;
199 
200 		cl = NULL;
201 		if (error == 0) {
202 			sun.sun_family = AF_LOCAL;
203 			strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
204 			sun.sun_len = SUN_LEN(&sun);
205 
206 			nconf = getnetconfigent("local");
207 			cl = clnt_reconnect_create(nconf,
208 			    (struct sockaddr *)&sun, RPCTLSSD, RPCTLSSDVERS,
209 			    RPC_MAXDATASIZE, RPC_MAXDATASIZE);
210 			/*
211 			 * The number of retries defaults to INT_MAX, which
212 			 * effectively means an infinite, uninterruptable loop.
213 			 * Set the try_count to 1 so that no retries of the
214 			 * RPC occur.  Since it is an upcall to a local daemon,
215 			 * requests should not be lost and doing one of these
216 			 * RPCs multiple times is not correct.
217 			 * Set a timeout (currently 15sec) and assume that
218 			 * the daemon is hung if a timeout occurs.
219 			 */
220 			if (cl != NULL) {
221 				try_count = 1;
222 				CLNT_CONTROL(cl, CLSET_RETRIES, &try_count);
223 				timeo.tv_sec = 15;
224 				timeo.tv_usec = 0;
225 				CLNT_CONTROL(cl, CLSET_TIMEOUT, &timeo);
226 			} else
227 				error = EINVAL;
228 		}
229 
230 		mtx_lock(&rpctls_server_lock);
231 		oldcl = rpctls_server_handle;
232 		rpctls_server_handle = cl;
233 		mtx_unlock(&rpctls_server_lock);
234 
235 		if (oldcl != NULL) {
236 			CLNT_CLOSE(oldcl);
237 			CLNT_RELEASE(oldcl);
238 		}
239 		break;
240 	case RPCTLS_SYSC_CLSHUTDOWN:
241 		mtx_lock(&rpctls_connect_lock);
242 		oldcl = rpctls_connect_handle;
243 		rpctls_connect_handle = NULL;
244 		mtx_unlock(&rpctls_connect_lock);
245 
246 		if (oldcl != NULL) {
247 			CLNT_CLOSE(oldcl);
248 			CLNT_RELEASE(oldcl);
249 		}
250 		break;
251 	case RPCTLS_SYSC_SRVSHUTDOWN:
252 		mtx_lock(&rpctls_server_lock);
253 		oldcl = rpctls_server_handle;
254 		rpctls_server_handle = NULL;
255 		mtx_unlock(&rpctls_server_lock);
256 
257 		if (oldcl != NULL) {
258 			CLNT_CLOSE(oldcl);
259 			CLNT_RELEASE(oldcl);
260 		}
261 		break;
262 	case RPCTLS_SYSC_CLSOCKET:
263 		mtx_lock(&rpctls_connect_lock);
264 		so = rpctls_connect_so;
265 		rpctls_connect_so = NULL;
266 		concl = rpctls_connect_cl;
267 		rpctls_connect_cl = NULL;
268 		mtx_unlock(&rpctls_connect_lock);
269 		if (so != NULL) {
270 			error = falloc(td, &fp, &fd, 0);
271 			if (error == 0) {
272 				/*
273 				 * Set ssl refno so that clnt_vc_destroy() will
274 				 * not close the socket and will leave that for
275 				 * the daemon to do.
276 				 */
277 				soref(so);
278 				ssl[0] = ssl[1] = 0;
279 				ssl[2] = RPCTLS_REFNO_HANDSHAKE;
280 				CLNT_CONTROL(concl, CLSET_TLS, ssl);
281 				finit(fp, FREAD | FWRITE, DTYPE_SOCKET, so,
282 				    &socketops);
283 				fdrop(fp, td);	/* Drop fp reference. */
284 				td->td_retval[0] = fd;
285 			}
286 		} else
287 			error = EPERM;
288 		break;
289 	case RPCTLS_SYSC_SRVSOCKET:
290 		mtx_lock(&rpctls_server_lock);
291 		so = rpctls_server_so;
292 		rpctls_server_so = NULL;
293 		xprt = rpctls_server_xprt;
294 		rpctls_server_xprt = NULL;
295 		mtx_unlock(&rpctls_server_lock);
296 		if (so != NULL) {
297 			error = falloc(td, &fp, &fd, 0);
298 			if (error == 0) {
299 				/*
300 				 * Once this file descriptor is associated
301 				 * with the socket, it cannot be closed by
302 				 * the server side krpc code (svc_vc.c).
303 				 */
304 				soref(so);
305 				sx_xlock(&xprt->xp_lock);
306 				xprt->xp_tls = RPCTLS_FLAGS_HANDSHFAIL;
307 				sx_xunlock(&xprt->xp_lock);
308 				finit(fp, FREAD | FWRITE, DTYPE_SOCKET, so,
309 				    &socketops);
310 				fdrop(fp, td);	/* Drop fp reference. */
311 				td->td_retval[0] = fd;
312 			}
313 		} else
314 			error = EPERM;
315 		break;
316 	default:
317 		error = EINVAL;
318 	}
319 
320 	return (error);
321 }
322 
323 /*
324  * Acquire the rpctls_connect_handle and return it with a reference count,
325  * if it is available.
326  */
327 static CLIENT *
328 rpctls_connect_client(void)
329 {
330 	CLIENT *cl;
331 
332 	mtx_lock(&rpctls_connect_lock);
333 	cl = rpctls_connect_handle;
334 	if (cl != NULL)
335 		CLNT_ACQUIRE(cl);
336 	mtx_unlock(&rpctls_connect_lock);
337 	return (cl);
338 }
339 
340 /*
341  * Acquire the rpctls_server_handle and return it with a reference count,
342  * if it is available.
343  */
344 static CLIENT *
345 rpctls_server_client(void)
346 {
347 	CLIENT *cl;
348 
349 	mtx_lock(&rpctls_server_lock);
350 	cl = rpctls_server_handle;
351 	if (cl != NULL)
352 		CLNT_ACQUIRE(cl);
353 	mtx_unlock(&rpctls_server_lock);
354 	return (cl);
355 }
356 
357 /* Do an upcall for a new socket connect using TLS. */
358 enum clnt_stat
359 rpctls_connect(CLIENT *newclient, char *certname, struct socket *so,
360     uint64_t *sslp, uint32_t *reterr)
361 {
362 	struct rpctlscd_connect_arg arg;
363 	struct rpctlscd_connect_res res;
364 	struct rpc_callextra ext;
365 	struct timeval utimeout;
366 	enum clnt_stat stat;
367 	CLIENT *cl;
368 	int val;
369 	static bool rpctls_connect_busy = false;
370 
371 	cl = rpctls_connect_client();
372 	if (cl == NULL)
373 		return (RPC_AUTHERROR);
374 
375 	/* First, do the AUTH_TLS NULL RPC. */
376 	memset(&ext, 0, sizeof(ext));
377 	utimeout.tv_sec = 30;
378 	utimeout.tv_usec = 0;
379 	ext.rc_auth = authtls_create();
380 	stat = clnt_call_private(newclient, &ext, NULLPROC, (xdrproc_t)xdr_void,
381 	    NULL, (xdrproc_t)xdr_void, NULL, utimeout);
382 	AUTH_DESTROY(ext.rc_auth);
383 	if (stat == RPC_AUTHERROR)
384 		return (stat);
385 	if (stat != RPC_SUCCESS)
386 		return (RPC_SYSTEMERROR);
387 
388 	/* Serialize the connect upcalls. */
389 	mtx_lock(&rpctls_connect_lock);
390 	while (rpctls_connect_busy)
391 		msleep(&rpctls_connect_busy, &rpctls_connect_lock, PVFS,
392 		    "rtlscn", 0);
393 	rpctls_connect_busy = true;
394 	rpctls_connect_so = so;
395 	rpctls_connect_cl = newclient;
396 	mtx_unlock(&rpctls_connect_lock);
397 
398 	/* Temporarily block reception during the handshake upcall. */
399 	val = 1;
400 	CLNT_CONTROL(newclient, CLSET_BLOCKRCV, &val);
401 
402 	/* Do the connect handshake upcall. */
403 	if (certname != NULL) {
404 		arg.certname.certname_len = strlen(certname);
405 		arg.certname.certname_val = certname;
406 	} else
407 		arg.certname.certname_len = 0;
408 	stat = rpctlscd_connect_1(&arg, &res, cl);
409 	if (stat == RPC_SUCCESS) {
410 		*reterr = res.reterr;
411 		if (res.reterr == 0) {
412 			*sslp++ = res.sec;
413 			*sslp++ = res.usec;
414 			*sslp = res.ssl;
415 		}
416 	} else if (stat == RPC_TIMEDOUT) {
417 		/*
418 		 * Do a shutdown on the socket, since the daemon is probably
419 		 * stuck in SSL_connect() trying to read the socket.
420 		 * Do not soclose() the socket, since the daemon will close()
421 		 * the socket after SSL_connect() returns an error.
422 		 */
423 		soshutdown(so, SHUT_RD);
424 	}
425 	CLNT_RELEASE(cl);
426 
427 	/* Unblock reception. */
428 	val = 0;
429 	CLNT_CONTROL(newclient, CLSET_BLOCKRCV, &val);
430 
431 	/* Once the upcall is done, the daemon is done with the fp and so. */
432 	mtx_lock(&rpctls_connect_lock);
433 	rpctls_connect_so = NULL;
434 	rpctls_connect_cl = NULL;
435 	rpctls_connect_busy = false;
436 	wakeup(&rpctls_connect_busy);
437 	mtx_unlock(&rpctls_connect_lock);
438 
439 	return (stat);
440 }
441 
442 /* Do an upcall to handle an non-application data record using TLS. */
443 enum clnt_stat
444 rpctls_cl_handlerecord(uint64_t sec, uint64_t usec, uint64_t ssl,
445     uint32_t *reterr)
446 {
447 	struct rpctlscd_handlerecord_arg arg;
448 	struct rpctlscd_handlerecord_res res;
449 	enum clnt_stat stat;
450 	CLIENT *cl;
451 
452 	cl = rpctls_connect_client();
453 	if (cl == NULL) {
454 		*reterr = RPCTLSERR_NOSSL;
455 		return (RPC_SUCCESS);
456 	}
457 
458 	/* Do the handlerecord upcall. */
459 	arg.sec = sec;
460 	arg.usec = usec;
461 	arg.ssl = ssl;
462 	stat = rpctlscd_handlerecord_1(&arg, &res, cl);
463 	CLNT_RELEASE(cl);
464 	if (stat == RPC_SUCCESS)
465 		*reterr = res.reterr;
466 	return (stat);
467 }
468 
469 enum clnt_stat
470 rpctls_srv_handlerecord(uint64_t sec, uint64_t usec, uint64_t ssl,
471     uint32_t *reterr)
472 {
473 	struct rpctlssd_handlerecord_arg arg;
474 	struct rpctlssd_handlerecord_res res;
475 	enum clnt_stat stat;
476 	CLIENT *cl;
477 
478 	cl = rpctls_server_client();
479 	if (cl == NULL) {
480 		*reterr = RPCTLSERR_NOSSL;
481 		return (RPC_SUCCESS);
482 	}
483 
484 	/* Do the handlerecord upcall. */
485 	arg.sec = sec;
486 	arg.usec = usec;
487 	arg.ssl = ssl;
488 	stat = rpctlssd_handlerecord_1(&arg, &res, cl);
489 	CLNT_RELEASE(cl);
490 	if (stat == RPC_SUCCESS)
491 		*reterr = res.reterr;
492 	return (stat);
493 }
494 
495 /* Do an upcall to shut down a socket using TLS. */
496 enum clnt_stat
497 rpctls_cl_disconnect(uint64_t sec, uint64_t usec, uint64_t ssl,
498     uint32_t *reterr)
499 {
500 	struct rpctlscd_disconnect_arg arg;
501 	struct rpctlscd_disconnect_res res;
502 	enum clnt_stat stat;
503 	CLIENT *cl;
504 
505 	cl = rpctls_connect_client();
506 	if (cl == NULL) {
507 		*reterr = RPCTLSERR_NOSSL;
508 		return (RPC_SUCCESS);
509 	}
510 
511 	/* Do the disconnect upcall. */
512 	arg.sec = sec;
513 	arg.usec = usec;
514 	arg.ssl = ssl;
515 	stat = rpctlscd_disconnect_1(&arg, &res, cl);
516 	CLNT_RELEASE(cl);
517 	if (stat == RPC_SUCCESS)
518 		*reterr = res.reterr;
519 	return (stat);
520 }
521 
522 enum clnt_stat
523 rpctls_srv_disconnect(uint64_t sec, uint64_t usec, uint64_t ssl,
524     uint32_t *reterr)
525 {
526 	struct rpctlssd_disconnect_arg arg;
527 	struct rpctlssd_disconnect_res res;
528 	enum clnt_stat stat;
529 	CLIENT *cl;
530 
531 	cl = rpctls_server_client();
532 	if (cl == NULL) {
533 		*reterr = RPCTLSERR_NOSSL;
534 		return (RPC_SUCCESS);
535 	}
536 
537 	/* Do the disconnect upcall. */
538 	arg.sec = sec;
539 	arg.usec = usec;
540 	arg.ssl = ssl;
541 	stat = rpctlssd_disconnect_1(&arg, &res, cl);
542 	CLNT_RELEASE(cl);
543 	if (stat == RPC_SUCCESS)
544 		*reterr = res.reterr;
545 	return (stat);
546 }
547 
548 /* Do an upcall for a new server socket using TLS. */
549 static enum clnt_stat
550 rpctls_server(SVCXPRT *xprt, struct socket *so, uint32_t *flags, uint64_t *sslp,
551     uid_t *uid, int *ngrps, gid_t **gids)
552 {
553 	enum clnt_stat stat;
554 	CLIENT *cl;
555 	struct rpctlssd_connect_res res;
556 	gid_t *gidp;
557 	uint32_t *gidv;
558 	int i;
559 	static bool rpctls_server_busy = false;
560 
561 	cl = rpctls_server_client();
562 	if (cl == NULL)
563 		return (RPC_SYSTEMERROR);
564 
565 	/* Serialize the server upcalls. */
566 	mtx_lock(&rpctls_server_lock);
567 	while (rpctls_server_busy)
568 		msleep(&rpctls_server_busy, &rpctls_server_lock, PVFS,
569 		    "rtlssn", 0);
570 	rpctls_server_busy = true;
571 	rpctls_server_so = so;
572 	rpctls_server_xprt = xprt;
573 	mtx_unlock(&rpctls_server_lock);
574 
575 	/* Do the server upcall. */
576 	res.gid.gid_val = NULL;
577 	stat = rpctlssd_connect_1(NULL, &res, cl);
578 	if (stat == RPC_SUCCESS) {
579 		*flags = res.flags;
580 		*sslp++ = res.sec;
581 		*sslp++ = res.usec;
582 		*sslp = res.ssl;
583 		if ((*flags & (RPCTLS_FLAGS_CERTUSER |
584 		    RPCTLS_FLAGS_DISABLED)) == RPCTLS_FLAGS_CERTUSER) {
585 			*ngrps = res.gid.gid_len;
586 			*uid = res.uid;
587 			*gids = gidp = mem_alloc(*ngrps * sizeof(gid_t));
588 			gidv = res.gid.gid_val;
589 			for (i = 0; i < *ngrps; i++)
590 				*gidp++ = *gidv++;
591 		}
592 	} else if (stat == RPC_TIMEDOUT) {
593 		/*
594 		 * Do a shutdown on the socket, since the daemon is probably
595 		 * stuck in SSL_accept() trying to read the socket.
596 		 * Do not soclose() the socket, since the daemon will close()
597 		 * the socket after SSL_accept() returns an error.
598 		 */
599 		soshutdown(so, SHUT_RD);
600 	}
601 	CLNT_RELEASE(cl);
602 	mem_free(res.gid.gid_val, 0);
603 
604 	/* Once the upcall is done, the daemon is done with the fp and so. */
605 	mtx_lock(&rpctls_server_lock);
606 	rpctls_server_so = NULL;
607 	rpctls_server_xprt = NULL;
608 	rpctls_server_busy = false;
609 	wakeup(&rpctls_server_busy);
610 	mtx_unlock(&rpctls_server_lock);
611 
612 	return (stat);
613 }
614 
615 /*
616  * Handle the NULL RPC with authentication flavor of AUTH_TLS.
617  * This is a STARTTLS command, so do the upcall to the rpctlssd daemon,
618  * which will do the TLS handshake.
619  */
620 enum auth_stat
621 _svcauth_rpcsec_tls(struct svc_req *rqst, struct rpc_msg *msg)
622 
623 {
624 	bool_t call_stat;
625 	enum clnt_stat stat;
626 	SVCXPRT *xprt;
627 	uint32_t flags;
628 	uint64_t ssl[3];
629 	int ngrps;
630 	uid_t uid;
631 	gid_t *gidp;
632 #ifdef KERN_TLS
633 	u_int maxlen;
634 #endif
635 
636 	/* Initialize reply. */
637 	rqst->rq_verf = rpctls_null_verf;
638 
639 	/* Check client credentials. */
640 	if (rqst->rq_cred.oa_length != 0 ||
641 	    msg->rm_call.cb_verf.oa_length != 0 ||
642 	    msg->rm_call.cb_verf.oa_flavor != AUTH_NULL)
643 		return (AUTH_BADCRED);
644 
645 	if (rqst->rq_proc != NULLPROC)
646 		return (AUTH_REJECTEDCRED);
647 
648 	call_stat = FALSE;
649 #ifdef KERN_TLS
650 	if (rpctls_getinfo(&maxlen, false, true))
651 		call_stat = TRUE;
652 #endif
653 	if (!call_stat)
654 		return (AUTH_REJECTEDCRED);
655 
656 	/*
657 	 * Disable reception for the krpc so that the TLS handshake can
658 	 * be done on the socket in the rpctlssd daemon.
659 	 */
660 	xprt = rqst->rq_xprt;
661 	sx_xlock(&xprt->xp_lock);
662 	xprt->xp_dontrcv = TRUE;
663 	sx_xunlock(&xprt->xp_lock);
664 
665 	/*
666 	 * Send the reply to the NULL RPC with AUTH_TLS, which is the
667 	 * STARTTLS command for Sun RPC.
668 	 */
669 	call_stat = svc_sendreply(rqst, (xdrproc_t)xdr_void, NULL);
670 	if (!call_stat) {
671 		sx_xlock(&xprt->xp_lock);
672 		xprt->xp_dontrcv = FALSE;
673 		sx_xunlock(&xprt->xp_lock);
674 		xprt_active(xprt);	/* Harmless if already active. */
675 		return (AUTH_REJECTEDCRED);
676 	}
677 
678 	/* Do an upcall to do the TLS handshake. */
679 	stat = rpctls_server(xprt, xprt->xp_socket, &flags,
680 	    ssl, &uid, &ngrps, &gidp);
681 
682 	/* Re-enable reception on the socket within the krpc. */
683 	sx_xlock(&xprt->xp_lock);
684 	xprt->xp_dontrcv = FALSE;
685 	if (stat == RPC_SUCCESS) {
686 		xprt->xp_tls = flags;
687 		xprt->xp_sslsec = ssl[0];
688 		xprt->xp_sslusec = ssl[1];
689 		xprt->xp_sslrefno = ssl[2];
690 		if ((flags & (RPCTLS_FLAGS_CERTUSER |
691 		    RPCTLS_FLAGS_DISABLED)) == RPCTLS_FLAGS_CERTUSER) {
692 			xprt->xp_ngrps = ngrps;
693 			xprt->xp_uid = uid;
694 			xprt->xp_gidp = gidp;
695 		}
696 	}
697 	sx_xunlock(&xprt->xp_lock);
698 	xprt_active(xprt);		/* Harmless if already active. */
699 
700 	return (RPCSEC_GSS_NODISPATCH);
701 }
702 
703 /*
704  * Get kern.ipc.tls.enable and kern.ipc.tls.maxlen.
705  */
706 bool
707 rpctls_getinfo(u_int *maxlenp, bool rpctlscd_run, bool rpctlssd_run)
708 {
709 	u_int maxlen;
710 	bool enable;
711 	int error;
712 	size_t siz;
713 
714 	if (!mb_use_ext_pgs)
715 		return (false);
716 	siz = sizeof(enable);
717 	error = kernel_sysctlbyname(curthread, "kern.ipc.tls.enable",
718 	    &enable, &siz, NULL, 0, NULL, 0);
719 	if (error != 0)
720 		return (false);
721 	siz = sizeof(maxlen);
722 	error = kernel_sysctlbyname(curthread, "kern.ipc.tls.maxlen",
723 	    &maxlen, &siz, NULL, 0, NULL, 0);
724 	if (error != 0)
725 		return (false);
726 	if (rpctlscd_run && rpctls_connect_handle == NULL)
727 		return (false);
728 	if (rpctlssd_run && rpctls_server_handle == NULL)
729 		return (false);
730 	*maxlenp = maxlen;
731 	return (enable);
732 }
733 
734