xref: /freebsd/sys/rpc/rpcsec_tls/rpctls_impl.c (revision 7cc42f6d)
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, struct socket *so, uint64_t *sslp,
360     uint32_t *reterr)
361 {
362 	struct rpctlscd_connect_res res;
363 	struct rpc_callextra ext;
364 	struct timeval utimeout;
365 	enum clnt_stat stat;
366 	CLIENT *cl;
367 	int val;
368 	static bool rpctls_connect_busy = false;
369 
370 	cl = rpctls_connect_client();
371 	if (cl == NULL)
372 		return (RPC_AUTHERROR);
373 
374 	/* First, do the AUTH_TLS NULL RPC. */
375 	memset(&ext, 0, sizeof(ext));
376 	utimeout.tv_sec = 30;
377 	utimeout.tv_usec = 0;
378 	ext.rc_auth = authtls_create();
379 	stat = clnt_call_private(newclient, &ext, NULLPROC, (xdrproc_t)xdr_void,
380 	    NULL, (xdrproc_t)xdr_void, NULL, utimeout);
381 	AUTH_DESTROY(ext.rc_auth);
382 	if (stat == RPC_AUTHERROR)
383 		return (stat);
384 	if (stat != RPC_SUCCESS)
385 		return (RPC_SYSTEMERROR);
386 
387 	/* Serialize the connect upcalls. */
388 	mtx_lock(&rpctls_connect_lock);
389 	while (rpctls_connect_busy)
390 		msleep(&rpctls_connect_busy, &rpctls_connect_lock, PVFS,
391 		    "rtlscn", 0);
392 	rpctls_connect_busy = true;
393 	rpctls_connect_so = so;
394 	rpctls_connect_cl = newclient;
395 	mtx_unlock(&rpctls_connect_lock);
396 
397 	/* Temporarily block reception during the handshake upcall. */
398 	val = 1;
399 	CLNT_CONTROL(newclient, CLSET_BLOCKRCV, &val);
400 
401 	/* Do the connect handshake upcall. */
402 	stat = rpctlscd_connect_1(NULL, &res, cl);
403 	if (stat == RPC_SUCCESS) {
404 		*reterr = res.reterr;
405 		if (res.reterr == 0) {
406 			*sslp++ = res.sec;
407 			*sslp++ = res.usec;
408 			*sslp = res.ssl;
409 		}
410 	} else if (stat == RPC_TIMEDOUT) {
411 		/*
412 		 * Do a shutdown on the socket, since the daemon is probably
413 		 * stuck in SSL_connect() trying to read the socket.
414 		 * Do not soclose() the socket, since the daemon will close()
415 		 * the socket after SSL_connect() returns an error.
416 		 */
417 		soshutdown(so, SHUT_RD);
418 	}
419 	CLNT_RELEASE(cl);
420 
421 	/* Unblock reception. */
422 	val = 0;
423 	CLNT_CONTROL(newclient, CLSET_BLOCKRCV, &val);
424 
425 	/* Once the upcall is done, the daemon is done with the fp and so. */
426 	mtx_lock(&rpctls_connect_lock);
427 	rpctls_connect_so = NULL;
428 	rpctls_connect_cl = NULL;
429 	rpctls_connect_busy = false;
430 	wakeup(&rpctls_connect_busy);
431 	mtx_unlock(&rpctls_connect_lock);
432 
433 	return (stat);
434 }
435 
436 /* Do an upcall to handle an non-application data record using TLS. */
437 enum clnt_stat
438 rpctls_cl_handlerecord(uint64_t sec, uint64_t usec, uint64_t ssl,
439     uint32_t *reterr)
440 {
441 	struct rpctlscd_handlerecord_arg arg;
442 	struct rpctlscd_handlerecord_res res;
443 	enum clnt_stat stat;
444 	CLIENT *cl;
445 
446 	cl = rpctls_connect_client();
447 	if (cl == NULL) {
448 		*reterr = RPCTLSERR_NOSSL;
449 		return (RPC_SUCCESS);
450 	}
451 
452 	/* Do the handlerecord upcall. */
453 	arg.sec = sec;
454 	arg.usec = usec;
455 	arg.ssl = ssl;
456 	stat = rpctlscd_handlerecord_1(&arg, &res, cl);
457 	CLNT_RELEASE(cl);
458 	if (stat == RPC_SUCCESS)
459 		*reterr = res.reterr;
460 	return (stat);
461 }
462 
463 enum clnt_stat
464 rpctls_srv_handlerecord(uint64_t sec, uint64_t usec, uint64_t ssl,
465     uint32_t *reterr)
466 {
467 	struct rpctlssd_handlerecord_arg arg;
468 	struct rpctlssd_handlerecord_res res;
469 	enum clnt_stat stat;
470 	CLIENT *cl;
471 
472 	cl = rpctls_server_client();
473 	if (cl == NULL) {
474 		*reterr = RPCTLSERR_NOSSL;
475 		return (RPC_SUCCESS);
476 	}
477 
478 	/* Do the handlerecord upcall. */
479 	arg.sec = sec;
480 	arg.usec = usec;
481 	arg.ssl = ssl;
482 	stat = rpctlssd_handlerecord_1(&arg, &res, cl);
483 	CLNT_RELEASE(cl);
484 	if (stat == RPC_SUCCESS)
485 		*reterr = res.reterr;
486 	return (stat);
487 }
488 
489 /* Do an upcall to shut down a socket using TLS. */
490 enum clnt_stat
491 rpctls_cl_disconnect(uint64_t sec, uint64_t usec, uint64_t ssl,
492     uint32_t *reterr)
493 {
494 	struct rpctlscd_disconnect_arg arg;
495 	struct rpctlscd_disconnect_res res;
496 	enum clnt_stat stat;
497 	CLIENT *cl;
498 
499 	cl = rpctls_connect_client();
500 	if (cl == NULL) {
501 		*reterr = RPCTLSERR_NOSSL;
502 		return (RPC_SUCCESS);
503 	}
504 
505 	/* Do the disconnect upcall. */
506 	arg.sec = sec;
507 	arg.usec = usec;
508 	arg.ssl = ssl;
509 	stat = rpctlscd_disconnect_1(&arg, &res, cl);
510 	CLNT_RELEASE(cl);
511 	if (stat == RPC_SUCCESS)
512 		*reterr = res.reterr;
513 	return (stat);
514 }
515 
516 enum clnt_stat
517 rpctls_srv_disconnect(uint64_t sec, uint64_t usec, uint64_t ssl,
518     uint32_t *reterr)
519 {
520 	struct rpctlssd_disconnect_arg arg;
521 	struct rpctlssd_disconnect_res res;
522 	enum clnt_stat stat;
523 	CLIENT *cl;
524 
525 	cl = rpctls_server_client();
526 	if (cl == NULL) {
527 		*reterr = RPCTLSERR_NOSSL;
528 		return (RPC_SUCCESS);
529 	}
530 
531 	/* Do the disconnect upcall. */
532 	arg.sec = sec;
533 	arg.usec = usec;
534 	arg.ssl = ssl;
535 	stat = rpctlssd_disconnect_1(&arg, &res, cl);
536 	CLNT_RELEASE(cl);
537 	if (stat == RPC_SUCCESS)
538 		*reterr = res.reterr;
539 	return (stat);
540 }
541 
542 /* Do an upcall for a new server socket using TLS. */
543 static enum clnt_stat
544 rpctls_server(SVCXPRT *xprt, struct socket *so, uint32_t *flags, uint64_t *sslp,
545     uid_t *uid, int *ngrps, gid_t **gids)
546 {
547 	enum clnt_stat stat;
548 	CLIENT *cl;
549 	struct rpctlssd_connect_res res;
550 	gid_t *gidp;
551 	uint32_t *gidv;
552 	int i;
553 	static bool rpctls_server_busy = false;
554 
555 	cl = rpctls_server_client();
556 	if (cl == NULL)
557 		return (RPC_SYSTEMERROR);
558 
559 	/* Serialize the server upcalls. */
560 	mtx_lock(&rpctls_server_lock);
561 	while (rpctls_server_busy)
562 		msleep(&rpctls_server_busy, &rpctls_server_lock, PVFS,
563 		    "rtlssn", 0);
564 	rpctls_server_busy = true;
565 	rpctls_server_so = so;
566 	rpctls_server_xprt = xprt;
567 	mtx_unlock(&rpctls_server_lock);
568 
569 	/* Do the server upcall. */
570 	stat = rpctlssd_connect_1(NULL, &res, cl);
571 	if (stat == RPC_SUCCESS) {
572 		*flags = res.flags;
573 		*sslp++ = res.sec;
574 		*sslp++ = res.usec;
575 		*sslp = res.ssl;
576 		if ((*flags & (RPCTLS_FLAGS_CERTUSER |
577 		    RPCTLS_FLAGS_DISABLED)) == RPCTLS_FLAGS_CERTUSER) {
578 			*ngrps = res.gid.gid_len;
579 			*uid = res.uid;
580 			*gids = gidp = mem_alloc(*ngrps * sizeof(gid_t));
581 			gidv = res.gid.gid_val;
582 			for (i = 0; i < *ngrps; i++)
583 				*gidp++ = *gidv++;
584 		}
585 	} else if (stat == RPC_TIMEDOUT) {
586 		/*
587 		 * Do a shutdown on the socket, since the daemon is probably
588 		 * stuck in SSL_accept() trying to read the socket.
589 		 * Do not soclose() the socket, since the daemon will close()
590 		 * the socket after SSL_accept() returns an error.
591 		 */
592 		soshutdown(so, SHUT_RD);
593 	}
594 	CLNT_RELEASE(cl);
595 
596 	/* Once the upcall is done, the daemon is done with the fp and so. */
597 	mtx_lock(&rpctls_server_lock);
598 	rpctls_server_so = NULL;
599 	rpctls_server_xprt = NULL;
600 	rpctls_server_busy = false;
601 	wakeup(&rpctls_server_busy);
602 	mtx_unlock(&rpctls_server_lock);
603 
604 	return (stat);
605 }
606 
607 /*
608  * Handle the NULL RPC with authentication flavor of AUTH_TLS.
609  * This is a STARTTLS command, so do the upcall to the rpctlssd daemon,
610  * which will do the TLS handshake.
611  */
612 enum auth_stat
613 _svcauth_rpcsec_tls(struct svc_req *rqst, struct rpc_msg *msg)
614 
615 {
616 	bool_t call_stat;
617 	enum clnt_stat stat;
618 	SVCXPRT *xprt;
619 	uint32_t flags;
620 	uint64_t ssl[3];
621 	int ngrps;
622 	uid_t uid;
623 	gid_t *gidp;
624 #ifdef KERN_TLS
625 	u_int maxlen;
626 #endif
627 
628 	/* Initialize reply. */
629 	rqst->rq_verf = rpctls_null_verf;
630 
631 	/* Check client credentials. */
632 	if (rqst->rq_cred.oa_length != 0 ||
633 	    msg->rm_call.cb_verf.oa_length != 0 ||
634 	    msg->rm_call.cb_verf.oa_flavor != AUTH_NULL)
635 		return (AUTH_BADCRED);
636 
637 	if (rqst->rq_proc != NULLPROC)
638 		return (AUTH_REJECTEDCRED);
639 
640 	call_stat = FALSE;
641 #ifdef KERN_TLS
642 	if (rpctls_getinfo(&maxlen, false, true))
643 		call_stat = TRUE;
644 #endif
645 	if (!call_stat)
646 		return (AUTH_REJECTEDCRED);
647 
648 	/*
649 	 * Disable reception for the krpc so that the TLS handshake can
650 	 * be done on the socket in the rpctlssd daemon.
651 	 */
652 	xprt = rqst->rq_xprt;
653 	sx_xlock(&xprt->xp_lock);
654 	xprt->xp_dontrcv = TRUE;
655 	sx_xunlock(&xprt->xp_lock);
656 
657 	/*
658 	 * Send the reply to the NULL RPC with AUTH_TLS, which is the
659 	 * STARTTLS command for Sun RPC.
660 	 */
661 	call_stat = svc_sendreply(rqst, (xdrproc_t)xdr_void, NULL);
662 	if (!call_stat) {
663 		sx_xlock(&xprt->xp_lock);
664 		xprt->xp_dontrcv = FALSE;
665 		sx_xunlock(&xprt->xp_lock);
666 		xprt_active(xprt);	/* Harmless if already active. */
667 		return (AUTH_REJECTEDCRED);
668 	}
669 
670 	/* Do an upcall to do the TLS handshake. */
671 	stat = rpctls_server(xprt, xprt->xp_socket, &flags,
672 	    ssl, &uid, &ngrps, &gidp);
673 
674 	/* Re-enable reception on the socket within the krpc. */
675 	sx_xlock(&xprt->xp_lock);
676 	xprt->xp_dontrcv = FALSE;
677 	if (stat == RPC_SUCCESS) {
678 		xprt->xp_tls = flags;
679 		xprt->xp_sslsec = ssl[0];
680 		xprt->xp_sslusec = ssl[1];
681 		xprt->xp_sslrefno = ssl[2];
682 		if ((flags & (RPCTLS_FLAGS_CERTUSER |
683 		    RPCTLS_FLAGS_DISABLED)) == RPCTLS_FLAGS_CERTUSER) {
684 			xprt->xp_ngrps = ngrps;
685 			xprt->xp_uid = uid;
686 			xprt->xp_gidp = gidp;
687 		}
688 	}
689 	sx_xunlock(&xprt->xp_lock);
690 	xprt_active(xprt);		/* Harmless if already active. */
691 
692 	return (RPCSEC_GSS_NODISPATCH);
693 }
694 
695 /*
696  * Get kern.ipc.tls.enable and kern.ipc.tls.maxlen.
697  */
698 bool
699 rpctls_getinfo(u_int *maxlenp, bool rpctlscd_run, bool rpctlssd_run)
700 {
701 	u_int maxlen;
702 	bool enable;
703 	int error;
704 	size_t siz;
705 
706 	if (PMAP_HAS_DMAP == 0 || !mb_use_ext_pgs)
707 		return (false);
708 	siz = sizeof(enable);
709 	error = kernel_sysctlbyname(curthread, "kern.ipc.tls.enable",
710 	    &enable, &siz, NULL, 0, NULL, 0);
711 	if (error != 0)
712 		return (false);
713 	siz = sizeof(maxlen);
714 	error = kernel_sysctlbyname(curthread, "kern.ipc.tls.maxlen",
715 	    &maxlen, &siz, NULL, 0, NULL, 0);
716 	if (error != 0)
717 		return (false);
718 	if (rpctlscd_run && rpctls_connect_handle == NULL)
719 		return (false);
720 	if (rpctlssd_run && rpctls_server_handle == NULL)
721 		return (false);
722 	*maxlenp = maxlen;
723 	return (enable);
724 }
725 
726