Author: rmacklem Date: Sat Aug 22 03:57:55 2020 New Revision: 364475 URL: https://svnweb.freebsd.org/changeset/base/364475
Log: Add TLS support to the kernel RPC. An internet draft titled "Towards Remote Procedure Call Encryption By Default" describes how TLS is to be used for Sun RPC, with NFS as an intended use case. This patch adds client and server support for this to the kernel RPC, using KERN_TLS and upcalls to daemons for the handshake, peer reset and other non-application data record cases. The upcalls to the daemons use three fields to uniquely identify the TCP connection. They are the time.tv_sec, time.tv_usec of the connection establshment, plus a 64bit sequence number. The time fields avoid problems with re-use of the sequence number after a daemon restart. For the server side, once a Null RPC with AUTH_TLS is received, kernel reception on the socket is blocked and an upcall to the rpctlssd(8) daemon is done to perform the TLS handshake. Upon completion, the completion status of the handshake is stored in xp_tls as flag bits and the reply to the Null RPC is sent. For the client, if CLSET_TLS has been set, a new TCP connection will send the Null RPC with AUTH_TLS to initiate the handshake. The client kernel RPC code will then block kernel I/O on the socket and do an upcall to the rpctlscd(8) daemon to perform the handshake. If the upcall is successful, ct_rcvstate will be maintained to indicate if/when an upcall is being done. If non-application data records are received, the code does an upcall to the appropriate daemon, which will do a SSL_read() of 0 length to handle the record(s). When the socket is being shut down, upcalls are done to the daemons, so that they can perform SSL_shutdown() calls to perform the "peer reset". The rpctlssd(8) and rpctlscd(8) daemons require a patched version of the openssl library and, as such, will not be committed to head at this time. Although the changes done by this patch are fairly numerous, there should be no semantics change to the kernel RPC at this time. A future commit to the NFS code will optionally enable use of TLS for NFS. Added: head/sys/rpc/rpcsec_tls/ head/sys/rpc/rpcsec_tls/auth_tls.c (contents, props changed) head/sys/rpc/rpcsec_tls/rpctls_impl.c (contents, props changed) head/sys/rpc/rpcsec_tls/rpctlscd.x (contents, props changed) head/sys/rpc/rpcsec_tls/rpctlssd.x (contents, props changed) Modified: head/sys/conf/files head/sys/modules/krpc/Makefile head/sys/rpc/auth.h head/sys/rpc/clnt_bck.c head/sys/rpc/clnt_rc.c head/sys/rpc/clnt_vc.c head/sys/rpc/krpc.h head/sys/rpc/rpc_generic.c head/sys/rpc/rpcsec_tls.h head/sys/rpc/svc.h head/sys/rpc/svc_auth.c head/sys/rpc/svc_vc.c Modified: head/sys/conf/files ============================================================================== --- head/sys/conf/files Sat Aug 22 01:10:59 2020 (r364474) +++ head/sys/conf/files Sat Aug 22 03:57:55 2020 (r364475) @@ -4868,6 +4868,41 @@ rpc/svc_auth_unix.c optional krpc | nfslockd | nfscl rpc/svc_dg.c optional krpc | nfslockd | nfscl | nfsd rpc/svc_generic.c optional krpc | nfslockd | nfscl | nfsd rpc/svc_vc.c optional krpc | nfslockd | nfscl | nfsd +# +# Kernel RPC-over-TLS +# +rpctlscd.h optional krpc | nfslockd | nfscl | nfsd \ + dependency "$S/rpc/rpcsec_tls/rpctlscd.x" \ + compile-with "RPCGEN_CPP='${CPP}' rpcgen -hM $S/rpc/rpcsec_tls/rpctlscd.x | grep -v pthread.h > rpctlscd.h" \ + no-obj no-implicit-rule before-depend local \ + clean "rpctlscd.h" +rpctlscd_xdr.c optional krpc | nfslockd | nfscl | nfsd \ + dependency "$S/rpc/rpcsec_tls/rpctlscd.x rpctlscd.h" \ + compile-with "RPCGEN_CPP='${CPP}' rpcgen -c $S/rpc/rpcsec_tls/rpctlscd.x -o rpctlscd_xdr.c" no-ctfconvert \ + no-implicit-rule before-depend local \ + clean "rpctlscd_xdr.c" +rpctlscd_clnt.c optional krpc | nfslockd | nfscl | nfsd \ + dependency "$S/rpc/rpcsec_tls/rpctlscd.x rpctlscd.h" \ + compile-with "RPCGEN_CPP='${CPP}' rpcgen -lM $S/rpc/rpcsec_tls/rpctlscd.x | grep -v string.h > rpctlscd_clnt.c" no-ctfconvert \ + no-implicit-rule before-depend local \ + clean "rpctlscd_clnt.c" +rpctlssd.h optional krpc | nfslockd | nfscl | nfsd \ + dependency "$S/rpc/rpcsec_tls/rpctlssd.x" \ + compile-with "RPCGEN_CPP='${CPP}' rpcgen -hM $S/rpc/rpcsec_tls/rpctlssd.x | grep -v pthread.h > rpctlssd.h" \ + no-obj no-implicit-rule before-depend local \ + clean "rpctlssd.h" +rpctlssd_xdr.c optional krpc | nfslockd | nfscl | nfsd \ + dependency "$S/rpc/rpcsec_tls/rpctlssd.x rpctlssd.h" \ + compile-with "RPCGEN_CPP='${CPP}' rpcgen -c $S/rpc/rpcsec_tls/rpctlssd.x -o rpctlssd_xdr.c" no-ctfconvert \ + no-implicit-rule before-depend local \ + clean "rpctlssd_xdr.c" +rpctlssd_clnt.c optional krpc | nfslockd | nfscl | nfsd \ + dependency "$S/rpc/rpcsec_tls/rpctlssd.x rpctlssd.h" \ + compile-with "RPCGEN_CPP='${CPP}' rpcgen -lM $S/rpc/rpcsec_tls/rpctlssd.x | grep -v string.h > rpctlssd_clnt.c" no-ctfconvert \ + no-implicit-rule before-depend local \ + clean "rpctlssd_clnt.c" +rpc/rpcsec_tls/rpctls_impl.c optional krpc | nfslockd | nfscl | nfsd +rpc/rpcsec_tls/auth_tls.c optional krpc | nfslockd | nfscl | nfsd rpc/rpcsec_gss/rpcsec_gss.c optional krpc kgssapi | nfslockd kgssapi | nfscl kgssapi | nfsd kgssapi rpc/rpcsec_gss/rpcsec_gss_conf.c optional krpc kgssapi | nfslockd kgssapi | nfscl kgssapi | nfsd kgssapi rpc/rpcsec_gss/rpcsec_gss_misc.c optional krpc kgssapi | nfslockd kgssapi | nfscl kgssapi | nfsd kgssapi Modified: head/sys/modules/krpc/Makefile ============================================================================== --- head/sys/modules/krpc/Makefile Sat Aug 22 01:10:59 2020 (r364474) +++ head/sys/modules/krpc/Makefile Sat Aug 22 03:57:55 2020 (r364475) @@ -1,6 +1,6 @@ # $FreeBSD$ -.PATH: ${SRCTOP}/sys/rpc +.PATH: ${SRCTOP}/sys/rpc ${SRCTOP}/sys/rpc/rpcsec_tls KMOD= krpc SRCS= auth_none.c \ auth_unix.c \ @@ -21,8 +21,38 @@ SRCS= auth_none.c \ svc_auth_unix.c \ svc_dg.c \ svc_generic.c \ - svc_vc.c \ + svc_vc.c -SRCS+= opt_inet6.h +SRCS+= rpctls_impl.c auth_tls.c + +SRCS+= opt_inet6.h opt_kern_tls.h + +SRCS+= rpctlscd.h rpctlscd_xdr.c rpctlscd_clnt.c +CLEANFILES= rpctlscd.h rpctlscd_xdr.c rpctlscd_clnt.c + +S= ${SRCTOP}/sys + +rpctlscd.h: $S/rpc/rpcsec_tls/rpctlscd.x + RPCGEN_CPP=${CPP:Q} rpcgen -hM $S/rpc/rpcsec_tls/rpctlscd.x | grep -v pthread.h > rpctlscd.h + +rpctlscd_xdr.c: $S/rpc/rpcsec_tls/rpctlscd.x + RPCGEN_CPP=${CPP:Q} rpcgen -c $S/rpc/rpcsec_tls/rpctlscd.x -o rpctlscd_xdr.c + +rpctlscd_clnt.c: $S/rpc/rpcsec_tls/rpctlscd.x + RPCGEN_CPP=${CPP:Q} rpcgen -lM $S/rpc/rpcsec_tls/rpctlscd.x | grep -v string.h > rpctlscd_clnt.c + +SRCS+= rpctlssd.h rpctlssd_xdr.c rpctlssd_clnt.c +CLEANFILES= rpctlssd.h rpctlssd_xdr.c rpctlssd_clnt.c + +S= ${SRCTOP}/sys + +rpctlssd.h: $S/rpc/rpcsec_tls/rpctlssd.x + RPCGEN_CPP=${CPP:Q} rpcgen -hM $S/rpc/rpcsec_tls/rpctlssd.x | grep -v pthread.h > rpctlssd.h + +rpctlssd_xdr.c: $S/rpc/rpcsec_tls/rpctlssd.x + RPCGEN_CPP=${CPP:Q} rpcgen -c $S/rpc/rpcsec_tls/rpctlssd.x -o rpctlssd_xdr.c + +rpctlssd_clnt.c: $S/rpc/rpcsec_tls/rpctlssd.x + RPCGEN_CPP=${CPP:Q} rpcgen -lM $S/rpc/rpcsec_tls/rpctlssd.x | grep -v string.h > rpctlssd_clnt.c .include <bsd.kmod.mk> Modified: head/sys/rpc/auth.h ============================================================================== --- head/sys/rpc/auth.h Sat Aug 22 01:10:59 2020 (r364474) +++ head/sys/rpc/auth.h Sat Aug 22 03:57:55 2020 (r364475) @@ -150,6 +150,7 @@ enum auth_stat { */ RPCSEC_GSS_CREDPROBLEM = 13, RPCSEC_GSS_CTXPROBLEM = 14, + /* Also used by RPCSEC_TLS for the same purpose */ RPCSEC_GSS_NODISPATCH = 0x8000000 }; @@ -249,6 +250,7 @@ extern AUTH *authunix_create(char *, u_int, u_int, int extern AUTH *authunix_create_default(void); /* takes no parameters */ #endif extern AUTH *authnone_create(void); /* takes no parameters */ +extern AUTH *authtls_create(void); /* takes no parameters */ __END_DECLS /* * DES style authentication @@ -344,6 +346,7 @@ struct rpc_msg; enum auth_stat _svcauth_null (struct svc_req *, struct rpc_msg *); enum auth_stat _svcauth_short (struct svc_req *, struct rpc_msg *); enum auth_stat _svcauth_unix (struct svc_req *, struct rpc_msg *); +enum auth_stat _svcauth_rpcsec_tls (struct svc_req *, struct rpc_msg *); __END_DECLS #define AUTH_NONE 0 /* no authentication */ @@ -355,6 +358,7 @@ __END_DECLS #define AUTH_DES AUTH_DH /* for backward compatibility */ #define AUTH_KERB 4 /* kerberos style */ #define RPCSEC_GSS 6 /* RPCSEC_GSS */ +#define AUTH_TLS 7 /* Initiate RPC-over-TLS */ /* * Pseudo auth flavors for RPCSEC_GSS. Modified: head/sys/rpc/clnt_bck.c ============================================================================== --- head/sys/rpc/clnt_bck.c Sat Aug 22 01:10:59 2020 (r364474) +++ head/sys/rpc/clnt_bck.c Sat Aug 22 03:57:55 2020 (r364475) @@ -61,8 +61,11 @@ __FBSDID("$FreeBSD$"); * connection provided by the client to the server. */ +#include "opt_kern_tls.h" + #include <sys/param.h> #include <sys/systm.h> +#include <sys/ktls.h> #include <sys/lock.h> #include <sys/malloc.h> #include <sys/mbuf.h> @@ -84,6 +87,7 @@ __FBSDID("$FreeBSD$"); #include <rpc/rpc.h> #include <rpc/rpc_com.h> #include <rpc/krpc.h> +#include <rpc/rpcsec_tls.h> struct cmessage { struct cmsghdr cmsg; @@ -203,7 +207,10 @@ clnt_bck_call( uint32_t xid; struct mbuf *mreq = NULL, *results; struct ct_request *cr; - int error; + int error, maxextsiz; +#ifdef KERN_TLS + u_int maxlen; +#endif cr = malloc(sizeof(struct ct_request), M_RPC, M_WAITOK); @@ -296,6 +303,19 @@ call_again: TAILQ_INSERT_TAIL(&ct->ct_pending, cr, cr_link); mtx_unlock(&ct->ct_lock); + /* For RPC-over-TLS, copy mrep to a chain of ext_pgs. */ + if ((xprt->xp_tls & RPCTLS_FLAGS_HANDSHAKE) != 0) { + /* + * Copy the mbuf chain to a chain of + * ext_pgs mbuf(s) as required by KERN_TLS. + */ + maxextsiz = TLS_MAX_MSG_SIZE_V10_2; +#ifdef KERN_TLS + if (rpctls_getinfo(&maxlen, false, false)) + maxextsiz = min(maxextsiz, maxlen); +#endif + mreq = _rpc_copym_into_ext_pgs(mreq, maxextsiz); + } /* * sosend consumes mreq. */ Modified: head/sys/rpc/clnt_rc.c ============================================================================== --- head/sys/rpc/clnt_rc.c Sat Aug 22 01:10:59 2020 (r364474) +++ head/sys/rpc/clnt_rc.c Sat Aug 22 03:57:55 2020 (r364475) @@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include <rpc/rpc.h> #include <rpc/rpc_com.h> #include <rpc/krpc.h> +#include <rpc/rpcsec_tls.h> static enum clnt_stat clnt_reconnect_call(CLIENT *, struct rpc_callextra *, rpcproc_t, struct mbuf *, struct mbuf **, struct timeval); @@ -108,6 +109,7 @@ clnt_reconnect_create( rc->rc_closed = FALSE; rc->rc_ucred = crdup(curthread->td_ucred); rc->rc_client = NULL; + rc->rc_tls = false; cl->cl_refs = 1; cl->cl_ops = &clnt_reconnect_ops; @@ -129,6 +131,8 @@ clnt_reconnect_connect(CLIENT *cl) int one = 1; struct ucred *oldcred; CLIENT *newclient = NULL; + uint64_t ssl[3]; + uint32_t reterr; mtx_lock(&rc->rc_lock); while (rc->rc_connecting) { @@ -193,6 +197,20 @@ clnt_reconnect_connect(CLIENT *cl) newclient = clnt_vc_create(so, (struct sockaddr *) &rc->rc_addr, rc->rc_prog, rc->rc_vers, rc->rc_sendsz, rc->rc_recvsz, rc->rc_intr); + if (rc->rc_tls && newclient != NULL) { + stat = rpctls_connect(newclient, so, ssl, &reterr); + if (stat != RPC_SUCCESS || reterr != RPCTLSERR_OK) { + if (stat == RPC_SUCCESS) + stat = RPC_FAILED; + stat = rpc_createerr.cf_stat = stat; + rpc_createerr.cf_error.re_errno = 0; + CLNT_CLOSE(newclient); + CLNT_RELEASE(newclient); + newclient = NULL; + td->td_ucred = oldcred; + goto out; + } + } } td->td_ucred = oldcred; @@ -209,6 +227,8 @@ clnt_reconnect_connect(CLIENT *cl) CLNT_CONTROL(newclient, CLSET_RETRY_TIMEOUT, &rc->rc_retry); CLNT_CONTROL(newclient, CLSET_WAITCHAN, rc->rc_waitchan); CLNT_CONTROL(newclient, CLSET_INTERRUPTIBLE, &rc->rc_intr); + if (rc->rc_tls) + CLNT_CONTROL(newclient, CLSET_TLS, ssl); if (rc->rc_backchannel != NULL) CLNT_CONTROL(newclient, CLSET_BACKCHANNEL, rc->rc_backchannel); stat = RPC_SUCCESS; @@ -470,6 +490,10 @@ clnt_reconnect_control(CLIENT *cl, u_int request, void xprt = (SVCXPRT *)info; xprt_register(xprt); rc->rc_backchannel = info; + break; + + case CLSET_TLS: + rc->rc_tls = true; break; default: Modified: head/sys/rpc/clnt_vc.c ============================================================================== --- head/sys/rpc/clnt_vc.c Sat Aug 22 01:10:59 2020 (r364474) +++ head/sys/rpc/clnt_vc.c Sat Aug 22 03:57:55 2020 (r364475) @@ -57,9 +57,13 @@ __FBSDID("$FreeBSD$"); * Now go hang yourself. */ +#include "opt_kern_tls.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> +#include <sys/kthread.h> +#include <sys/ktls.h> #include <sys/lock.h> #include <sys/malloc.h> #include <sys/mbuf.h> @@ -81,6 +85,7 @@ __FBSDID("$FreeBSD$"); #include <rpc/rpc.h> #include <rpc/rpc_com.h> #include <rpc/krpc.h> +#include <rpc/rpcsec_tls.h> struct cmessage { struct cmsghdr cmsg; @@ -97,6 +102,7 @@ static void clnt_vc_close(CLIENT *); static void clnt_vc_destroy(CLIENT *); static bool_t time_not_ok(struct timeval *); static int clnt_vc_soupcall(struct socket *so, void *arg, int waitflag); +static void clnt_vc_dotlsupcall(void *data); static struct clnt_ops clnt_vc_ops = { .cl_call = clnt_vc_call, @@ -154,6 +160,7 @@ clnt_vc_create( ct->ct_closing = FALSE; ct->ct_closed = FALSE; ct->ct_upcallrefs = 0; + ct->ct_rcvstate = RPCRCVSTATE_NORMAL; if ((so->so_state & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0) { error = soconnect(so, raddr, curthread); @@ -272,6 +279,7 @@ clnt_vc_create( ct->ct_raw = NULL; ct->ct_record = NULL; ct->ct_record_resid = 0; + ct->ct_sslrefno = 0; TAILQ_INIT(&ct->ct_pending); return (cl); @@ -304,7 +312,10 @@ clnt_vc_call( uint32_t xid; struct mbuf *mreq = NULL, *results; struct ct_request *cr; - int error, trycnt; + int error, maxextsiz, trycnt; +#ifdef KERN_TLS + u_int maxlen; +#endif cr = malloc(sizeof(struct ct_request), M_RPC, M_WAITOK); @@ -407,9 +418,27 @@ call_again: stat = RPC_CANTRECV; goto out; } + + /* For TLS, wait for an upcall to be done, as required. */ + while ((ct->ct_rcvstate & (RPCRCVSTATE_NORMAL | + RPCRCVSTATE_NONAPPDATA)) == 0) + msleep(&ct->ct_rcvstate, &ct->ct_lock, 0, "rpcrcvst", hz); + TAILQ_INSERT_TAIL(&ct->ct_pending, cr, cr_link); mtx_unlock(&ct->ct_lock); + if (ct->ct_sslrefno != 0) { + /* + * Copy the mbuf chain to a chain of ext_pgs mbuf(s) + * as required by KERN_TLS. + */ + maxextsiz = TLS_MAX_MSG_SIZE_V10_2; +#ifdef KERN_TLS + if (rpctls_getinfo(&maxlen, false, false)) + maxextsiz = min(maxextsiz, maxlen); +#endif + mreq = _rpc_copym_into_ext_pgs(mreq, maxextsiz); + } /* * sosend consumes mreq. */ @@ -615,6 +644,9 @@ clnt_vc_control(CLIENT *cl, u_int request, void *info) struct ct_data *ct = (struct ct_data *)cl->cl_private; void *infop = info; SVCXPRT *xprt; + uint64_t *p; + int error; + static u_int thrdnum = 0; mtx_lock(&ct->ct_lock); @@ -730,10 +762,38 @@ clnt_vc_control(CLIENT *cl, u_int request, void *info) xprt = (SVCXPRT *)info; if (ct->ct_backchannelxprt == NULL) { xprt->xp_p2 = ct; + if (ct->ct_sslrefno != 0) + xprt->xp_tls = RPCTLS_FLAGS_HANDSHAKE; ct->ct_backchannelxprt = xprt; } break; + case CLSET_TLS: + p = (uint64_t *)info; + ct->ct_sslsec = *p++; + ct->ct_sslusec = *p++; + ct->ct_sslrefno = *p; + if (ct->ct_sslrefno != RPCTLS_REFNO_HANDSHAKE) { + mtx_unlock(&ct->ct_lock); + /* Start the kthread that handles upcalls. */ + error = kthread_add(clnt_vc_dotlsupcall, ct, + NULL, NULL, 0, 0, "krpctls%u", thrdnum++); + if (error != 0) + panic("Can't add KRPC thread error %d", error); + } else + mtx_unlock(&ct->ct_lock); + return (TRUE); + + case CLSET_BLOCKRCV: + if (*(int *) info) { + ct->ct_rcvstate &= ~RPCRCVSTATE_NORMAL; + ct->ct_rcvstate |= RPCRCVSTATE_TLSHANDSHAKE; + } else { + ct->ct_rcvstate &= ~RPCRCVSTATE_TLSHANDSHAKE; + ct->ct_rcvstate |= RPCRCVSTATE_NORMAL; + } + break; + default: mtx_unlock(&ct->ct_lock); return (FALSE); @@ -769,8 +829,10 @@ clnt_vc_close(CLIENT *cl) mtx_unlock(&ct->ct_lock); SOCKBUF_LOCK(&ct->ct_socket->so_rcv); - soupcall_clear(ct->ct_socket, SO_RCV); - clnt_vc_upcallsdone(ct); + if (ct->ct_socket->so_rcv.sb_upcall != NULL) { + soupcall_clear(ct->ct_socket, SO_RCV); + clnt_vc_upcallsdone(ct); + } SOCKBUF_UNLOCK(&ct->ct_socket->so_rcv); /* @@ -790,6 +852,7 @@ clnt_vc_close(CLIENT *cl) ct->ct_closing = FALSE; ct->ct_closed = TRUE; + wakeup(&ct->ct_sslrefno); mtx_unlock(&ct->ct_lock); wakeup(ct); } @@ -800,6 +863,8 @@ clnt_vc_destroy(CLIENT *cl) struct ct_data *ct = (struct ct_data *) cl->cl_private; struct socket *so = NULL; SVCXPRT *xprt; + enum clnt_stat stat; + uint32_t reterr; clnt_vc_close(cl); @@ -820,12 +885,40 @@ clnt_vc_destroy(CLIENT *cl) } } + /* Wait for the upcall kthread to terminate. */ + while ((ct->ct_rcvstate & RPCRCVSTATE_UPCALLTHREAD) != 0) + msleep(&ct->ct_sslrefno, &ct->ct_lock, 0, + "clntvccl", hz); mtx_unlock(&ct->ct_lock); mtx_destroy(&ct->ct_lock); if (so) { - soshutdown(so, SHUT_WR); - soclose(so); + if (ct->ct_sslrefno != 0) { + /* + * If the TLS handshake is in progress, the upcall + * will fail, but the socket should be closed by the + * daemon, since the connect upcall has just failed. + */ + if (ct->ct_sslrefno != RPCTLS_REFNO_HANDSHAKE) { + /* + * If the upcall fails, the socket has + * probably been closed via the rpctlscd + * daemon having crashed or been + * restarted, so ignore return stat. + */ + stat = rpctls_cl_disconnect(ct->ct_sslsec, + ct->ct_sslusec, ct->ct_sslrefno, + &reterr); + } + /* Must sorele() to get rid of reference. */ + CURVNET_SET(so->so_vnet); + SOCK_LOCK(so); + sorele(so); + CURVNET_RESTORE(); + } else { + soshutdown(so, SHUT_WR); + soclose(so); + } } m_freem(ct->ct_record); m_freem(ct->ct_raw); @@ -853,15 +946,34 @@ clnt_vc_soupcall(struct socket *so, void *arg, int wai { struct ct_data *ct = (struct ct_data *) arg; struct uio uio; - struct mbuf *m, *m2; + struct mbuf *m, *m2, **ctrlp; struct ct_request *cr; int error, rcvflag, foundreq; uint32_t xid_plus_direction[2], header; SVCXPRT *xprt; struct cf_conn *cd; u_int rawlen; + struct cmsghdr *cmsg; + struct tls_get_record tgr; /* + * RPC-over-TLS needs to block reception during + * upcalls since the upcall will be doing I/O on + * the socket via openssl library calls. + */ + mtx_lock(&ct->ct_lock); + if ((ct->ct_rcvstate & (RPCRCVSTATE_NORMAL | + RPCRCVSTATE_NONAPPDATA)) == 0) { + /* Mark that a socket upcall needs to be done. */ + if ((ct->ct_rcvstate & (RPCRCVSTATE_UPCALLNEEDED | + RPCRCVSTATE_UPCALLINPROG)) != 0) + ct->ct_rcvstate |= RPCRCVSTATE_SOUPCALLNEEDED; + mtx_unlock(&ct->ct_lock); + return (SU_OK); + } + mtx_unlock(&ct->ct_lock); + + /* * If another thread is already here, it must be in * soreceive(), so just return to avoid races with it. * ct_upcallrefs is protected by the SOCKBUF_LOCK(), @@ -881,8 +993,14 @@ clnt_vc_soupcall(struct socket *so, void *arg, int wai uio.uio_td = curthread; m2 = m = NULL; rcvflag = MSG_DONTWAIT | MSG_SOCALLBCK; + if (ct->ct_sslrefno != 0 && (ct->ct_rcvstate & + RPCRCVSTATE_NORMAL) != 0) { + rcvflag |= MSG_TLSAPPDATA; + ctrlp = NULL; + } else + ctrlp = &m2; SOCKBUF_UNLOCK(&so->so_rcv); - error = soreceive(so, NULL, &uio, &m, NULL, &rcvflag); + error = soreceive(so, NULL, &uio, &m, ctrlp, &rcvflag); SOCKBUF_LOCK(&so->so_rcv); if (error == EWOULDBLOCK) { @@ -905,9 +1023,55 @@ clnt_vc_soupcall(struct socket *so, void *arg, int wai */ error = ECONNRESET; } + + /* + * A return of ENXIO indicates that there is a + * non-application data record at the head of the + * socket's receive queue, for TLS connections. + * This record needs to be handled in userland + * via an SSL_read() call, so do an upcall to the daemon. + */ + if (ct->ct_sslrefno != 0 && error == ENXIO) { + /* Disable reception, marking an upcall needed. */ + mtx_lock(&ct->ct_lock); + ct->ct_rcvstate |= RPCRCVSTATE_UPCALLNEEDED; + /* + * If an upcall in needed, wake up the kthread + * that runs clnt_vc_dotlsupcall(). + */ + wakeup(&ct->ct_sslrefno); + mtx_unlock(&ct->ct_lock); + break; + } if (error != 0) break; + /* Process any record header(s). */ + if (m2 != NULL) { + cmsg = mtod(m2, struct cmsghdr *); + if (cmsg->cmsg_type == TLS_GET_RECORD && + cmsg->cmsg_len == CMSG_LEN(sizeof(tgr))) { + memcpy(&tgr, CMSG_DATA(cmsg), sizeof(tgr)); + /* + * This should have been handled by + * setting RPCRCVSTATE_UPCALLNEEDED in + * ct_rcvstate but if not, all we can do + * is toss it away. + */ + if (tgr.tls_type != TLS_RLTYPE_APP) { + m_freem(m); + m_free(m2); + mtx_lock(&ct->ct_lock); + ct->ct_rcvstate &= + ~RPCRCVSTATE_NONAPPDATA; + ct->ct_rcvstate |= RPCRCVSTATE_NORMAL; + mtx_unlock(&ct->ct_lock); + continue; + } + } + m_free(m2); + } + if (ct->ct_raw != NULL) m_last(ct->ct_raw)->m_next = m; else @@ -1108,4 +1272,51 @@ clnt_vc_upcallsdone(struct ct_data *ct) while (ct->ct_upcallrefs > 0) (void) msleep(&ct->ct_upcallrefs, SOCKBUF_MTX(&ct->ct_socket->so_rcv), 0, "rpcvcup", 0); +} + +/* + * Do a TLS upcall to the rpctlscd daemon, as required. + * This function runs as a kthread. + */ +static void +clnt_vc_dotlsupcall(void *data) +{ + struct ct_data *ct = (struct ct_data *)data; + enum clnt_stat ret; + uint32_t reterr; + + mtx_lock(&ct->ct_lock); + ct->ct_rcvstate |= RPCRCVSTATE_UPCALLTHREAD; + while (!ct->ct_closed) { + if ((ct->ct_rcvstate & RPCRCVSTATE_UPCALLNEEDED) != 0) { + ct->ct_rcvstate &= ~RPCRCVSTATE_UPCALLNEEDED; + ct->ct_rcvstate |= RPCRCVSTATE_UPCALLINPROG; + if (ct->ct_sslrefno != 0 && ct->ct_sslrefno != + RPCTLS_REFNO_HANDSHAKE) { + mtx_unlock(&ct->ct_lock); + ret = rpctls_cl_handlerecord(ct->ct_sslsec, + ct->ct_sslusec, ct->ct_sslrefno, &reterr); + mtx_lock(&ct->ct_lock); + } + ct->ct_rcvstate &= ~RPCRCVSTATE_UPCALLINPROG; + if (ret == RPC_SUCCESS && reterr == RPCTLSERR_OK) + ct->ct_rcvstate |= RPCRCVSTATE_NORMAL; + else + ct->ct_rcvstate |= RPCRCVSTATE_NONAPPDATA; + wakeup(&ct->ct_rcvstate); + } + if ((ct->ct_rcvstate & RPCRCVSTATE_SOUPCALLNEEDED) != 0) { + ct->ct_rcvstate &= ~RPCRCVSTATE_SOUPCALLNEEDED; + mtx_unlock(&ct->ct_lock); + SOCKBUF_LOCK(&ct->ct_socket->so_rcv); + clnt_vc_soupcall(ct->ct_socket, ct, M_NOWAIT); + SOCKBUF_UNLOCK(&ct->ct_socket->so_rcv); + mtx_lock(&ct->ct_lock); + } + msleep(&ct->ct_sslrefno, &ct->ct_lock, 0, "clntvcdu", hz); + } + ct->ct_rcvstate &= ~RPCRCVSTATE_UPCALLTHREAD; + wakeup(&ct->ct_sslrefno); + mtx_unlock(&ct->ct_lock); + kthread_exit(); } Modified: head/sys/rpc/krpc.h ============================================================================== --- head/sys/rpc/krpc.h Sat Aug 22 01:10:59 2020 (r364474) +++ head/sys/rpc/krpc.h Sat Aug 22 03:57:55 2020 (r364475) @@ -42,6 +42,7 @@ void clnt_bck_svccall(void *, struct mbuf *, uint32_t); enum clnt_stat clnt_bck_call(CLIENT *, struct rpc_callextra *, rpcproc_t, struct mbuf *, struct mbuf **, struct timeval, SVCXPRT *); +struct mbuf *_rpc_copym_into_ext_pgs(struct mbuf *, int); /* * A pending RPC request which awaits a reply. Requests which have @@ -78,8 +79,18 @@ struct rc_data { CLIENT* rc_client; /* underlying RPC client */ struct rpc_err rc_err; void *rc_backchannel; + bool rc_tls; /* Enable TLS on connection */ }; +/* Bits for ct_rcvstate. */ +#define RPCRCVSTATE_NORMAL 0x01 /* Normal reception. */ +#define RPCRCVSTATE_NONAPPDATA 0x02 /* Reception of a non-application record. */ +#define RPCRCVSTATE_TLSHANDSHAKE 0x04 /* Reception blocked for TLS handshake. */ +#define RPCRCVSTATE_UPCALLNEEDED 0x08 /* Upcall to rpctlscd needed. */ +#define RPCRCVSTATE_UPCALLINPROG 0x10 /* Upcall to rpctlscd in progress. */ +#define RPCRCVSTATE_SOUPCALLNEEDED 0x20 /* Socket upcall needed. */ +#define RPCRCVSTATE_UPCALLTHREAD 0x40 /* Upcall kthread running. */ + struct ct_data { struct mtx ct_lock; int ct_threads; /* number of threads in clnt_vc_call */ @@ -101,6 +112,10 @@ struct ct_data { struct ct_request_list ct_pending; int ct_upcallrefs; /* Ref cnt of upcalls in prog. */ SVCXPRT *ct_backchannelxprt; /* xprt for backchannel */ + uint64_t ct_sslsec; /* RPC-over-TLS connection. */ + uint64_t ct_sslusec; + uint64_t ct_sslrefno; + uint32_t ct_rcvstate; /* Handle receiving for TLS upcalls */ struct mbuf *ct_raw; /* Raw mbufs recv'd */ }; Modified: head/sys/rpc/rpc_generic.c ============================================================================== --- head/sys/rpc/rpc_generic.c Sat Aug 22 01:10:59 2020 (r364474) +++ head/sys/rpc/rpc_generic.c Sat Aug 22 03:57:55 2020 (r364475) @@ -62,9 +62,15 @@ __FBSDID("$FreeBSD$"); #include <rpc/rpc.h> #include <rpc/nettype.h> #include <rpc/rpcsec_gss.h> +#include <rpc/rpcsec_tls.h> #include <rpc/rpc_com.h> +#include <rpc/krpc.h> +#include <vm/vm.h> +#include <vm/pmap.h> +#include <vm/vm_param.h> + extern u_long sb_max_adj; /* not defined in socketvar.h */ /* Provide an entry point hook for the rpcsec_gss module. */ @@ -865,13 +871,115 @@ out: } /* + * Make sure an mbuf list is made up entirely of ext_pgs mbufs. + * This is needed for sosend() when KERN_TLS is being used. + * (There might also be a performance improvement for certain + * network interfaces that handle ext_pgs mbufs efficiently.) + * It expects at least one non-ext_pgs mbuf followed by zero + * or more ext_pgs mbufs. It does not handle the case where + * non-ext_pgs mbuf(s) follow ext_pgs ones. + * It also performs sanity checks on the resultant list. + * The "mp" argument list is consumed. + * The "maxextsiz" argument is the upper bound on the data + * size for each mbuf (usually 16K for KERN_TLS). + */ +struct mbuf * +_rpc_copym_into_ext_pgs(struct mbuf *mp, int maxextsiz) +{ + struct mbuf *m, *m2, *m3, *mhead; + int tlen; + + KASSERT((mp->m_flags & (M_EXT | M_EXTPG)) != + (M_EXT | M_EXTPG), ("_rpc_copym_into_ext_pgs:" + " first mbuf is an ext_pgs")); + /* + * Find the last non-ext_pgs mbuf and the total + * length of the non-ext_pgs mbuf(s). + * The first mbuf must always be a non-ext_pgs + * mbuf. + */ + tlen = mp->m_len; + m2 = mp; + for (m = mp->m_next; m != NULL; m = m->m_next) { + if ((m->m_flags & M_EXTPG) != 0) + break; + tlen += m->m_len; + m2 = m; + } + + /* + * Copy the non-ext_pgs mbuf(s) into an ext_pgs + * mbuf list. + */ + m2->m_next = NULL; + mhead = mb_mapped_to_unmapped(mp, tlen, maxextsiz, + M_WAITOK, &m2); + + /* + * Link the ext_pgs list onto the newly copied + * list and free up the non-ext_pgs mbuf(s). + */ + m2->m_next = m; + m_freem(mp); + + /* + * Sanity check the resultant mbuf list. Check for and + * remove any 0 length mbufs in the list, since the + * KERN_TLS code does not expect any 0 length mbuf(s) + * in the list. + */ + m3 = NULL; + m2 = mhead; + tlen = 0; + while (m2 != NULL) { + KASSERT(m2->m_len >= 0, ("_rpc_copym_into_ext_pgs:" + " negative m_len")); + KASSERT((m2->m_flags & (M_EXT | M_EXTPG)) == + (M_EXT | M_EXTPG), ("_rpc_copym_into_ext_pgs:" + " non-nomap mbuf in list")); + if (m2->m_len == 0) { + if (m3 != NULL) + m3->m_next = m2->m_next; + else + m = m2->m_next; + m2->m_next = NULL; + m_free(m2); + if (m3 != NULL) + m2 = m3->m_next; + else + m2 = m; + } else { + MBUF_EXT_PGS_ASSERT_SANITY(m2); + m3 = m2; + tlen += m2->m_len; + m2 = m2->m_next; + } + } + return (mhead); +} + +/* * Kernel module glue */ static int krpc_modevent(module_t mod, int type, void *data) { + int error = 0; - return (0); + switch (type) { + case MOD_LOAD: + error = rpctls_init(); + break; + case MOD_UNLOAD: + /* + * Cannot be unloaded, since the rpctlssd or rpctlscd daemons + * might be performing a rpctls syscall. + */ + /* FALLTHROUGH */ + default: + error = EOPNOTSUPP; + } + return (error); } static moduledata_t krpc_mod = { "krpc", Modified: head/sys/rpc/rpcsec_tls.h ============================================================================== --- head/sys/rpc/rpcsec_tls.h Sat Aug 22 01:10:59 2020 (r364474) +++ head/sys/rpc/rpcsec_tls.h Sat Aug 22 03:57:55 2020 (r364475) @@ -48,6 +48,7 @@ int rpctls_syscall(int, const char *); #define RPCTLS_FLAGS_VERIFIED 0x08 #define RPCTLS_FLAGS_DISABLED 0x10 #define RPCTLS_FLAGS_CERTUSER 0x20 +#define RPCTLS_FLAGS_HANDSHFAIL 0x40 /* Error return values for upcall rpcs. */ #define RPCTLSERR_OK 0 @@ -72,10 +73,14 @@ enum clnt_stat rpctls_srv_disconnect(uint64_t sec, uin int rpctls_init(void); /* Get TLS information function. */ -bool rpctls_getinfo(u_int *maxlen); +bool rpctls_getinfo(u_int *maxlen, bool rpctlscd_run, + bool rpctlssd_run); /* String for AUTH_TLS reply verifier. */ #define RPCTLS_START_STRING "STARTTLS" + +/* ssl refno value to indicate TLS handshake being done. */ +#define RPCTLS_REFNO_HANDSHAKE 0xFFFFFFFFFFFFFFFFULL #endif /* _KERNEL */ Added: head/sys/rpc/rpcsec_tls/auth_tls.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/rpc/rpcsec_tls/auth_tls.c Sat Aug 22 03:57:55 2020 (r364475) @@ -0,0 +1,169 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2009, Sun Microsystems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of Sun Microsystems, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * auth_none.c + * Creates a client authentication handle for passing "null" + * credentials and verifiers to remote systems. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +/* + * Modified from auth_none.c to expect a reply verifier of "STARTTLS" + * for the RPC-over-TLS STARTTLS command. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> + +#include <rpc/types.h> +#include <rpc/xdr.h> +#include <rpc/auth.h> +#include <rpc/clnt.h> +#include <rpc/rpcsec_tls.h> + +#define MAX_MARSHAL_SIZE 20 + +/* + * Authenticator operations routines + */ + +static bool_t authtls_marshal (AUTH *, uint32_t, XDR *, struct mbuf *); +static void authtls_verf (AUTH *); +static bool_t authtls_validate (AUTH *, uint32_t, struct opaque_auth *, + struct mbuf **); +static bool_t authtls_refresh (AUTH *, void *); +static void authtls_destroy (AUTH *); + +static struct auth_ops authtls_ops = { + .ah_nextverf = authtls_verf, + .ah_marshal = authtls_marshal, + .ah_validate = authtls_validate, + .ah_refresh = authtls_refresh, + .ah_destroy = authtls_destroy, +}; + +struct authtls_private { + AUTH no_client; + char mclient[MAX_MARSHAL_SIZE]; + u_int mcnt; +}; + +static struct authtls_private authtls_private; +static struct opaque_auth _tls_null_auth; + +static void +authtls_init(void *dummy) +{ + struct authtls_private *ap = &authtls_private; + XDR xdrs; + + _tls_null_auth.oa_flavor = AUTH_TLS; + _tls_null_auth.oa_base = NULL; + _tls_null_auth.oa_length = 0; + ap->no_client.ah_cred = _tls_null_auth; + ap->no_client.ah_verf = _null_auth; + ap->no_client.ah_ops = &authtls_ops; + xdrmem_create(&xdrs, ap->mclient, MAX_MARSHAL_SIZE, XDR_ENCODE); + xdr_opaque_auth(&xdrs, &ap->no_client.ah_cred); + xdr_opaque_auth(&xdrs, &ap->no_client.ah_verf); + ap->mcnt = XDR_GETPOS(&xdrs); + XDR_DESTROY(&xdrs); +} +SYSINIT(authtls_init, SI_SUB_KMEM, SI_ORDER_ANY, authtls_init, NULL); + +AUTH * +authtls_create(void) +{ + struct authtls_private *ap = &authtls_private; + + return (&ap->no_client); +} + +/*ARGSUSED*/ +static bool_t +authtls_marshal(AUTH *client, uint32_t xid, XDR *xdrs, struct mbuf *args) +{ + struct authtls_private *ap = &authtls_private; + + KASSERT(xdrs != NULL, ("authtls_marshal: xdrs is null")); + + if (!XDR_PUTBYTES(xdrs, ap->mclient, ap->mcnt)) + return (FALSE); + + xdrmbuf_append(xdrs, args); + + return (TRUE); +} + +/* All these unused parameters are required to keep ANSI-C from grumbling */ +/*ARGSUSED*/ +static void +authtls_verf(AUTH *client) +{ +} + +/*ARGSUSED*/ +static bool_t +authtls_validate(AUTH *client, uint32_t xid, struct opaque_auth *opaque, + struct mbuf **mrepp) +{ + size_t strsiz; + + strsiz = strlen(RPCTLS_START_STRING); + /* The verifier must be the string RPCTLS_START_STRING. */ + if (opaque != NULL && + (opaque->oa_length != strsiz || memcmp(opaque->oa_base, + RPCTLS_START_STRING, strsiz) != 0)) + return (FALSE); + return (TRUE); +} + +/*ARGSUSED*/ +static bool_t +authtls_refresh(AUTH *client, void *dummy) +{ + + return (FALSE); +} + +/*ARGSUSED*/ +static void +authtls_destroy(AUTH *client) +{ +} Added: head/sys/rpc/rpcsec_tls/rpctls_impl.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/rpc/rpcsec_tls/rpctls_impl.c Sat Aug 22 03:57:55 2020 (r364475) @@ -0,0 +1,727 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * *** DIFF OUTPUT TRUNCATED AT 1000 LINES *** _______________________________________________ svn-src-head@freebsd.org mailing list https://lists.freebsd.org/mailman/listinfo/svn-src-head To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"