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.
This commit is contained in:
Rick Macklem 2020-08-22 03:57:55 +00:00
parent 530134d291
commit ab0c29af05
16 changed files with 1685 additions and 23 deletions

View File

@ -4868,6 +4868,41 @@ rpc/svc_auth_unix.c optional krpc | nfslockd | nfscl | nfsd
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

View File

@ -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>

View File

@ -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, u_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.

View File

@ -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 @@ clnt_bck_call(
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.
*/

View File

@ -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;
@ -472,6 +492,10 @@ clnt_reconnect_control(CLIENT *cl, u_int request, void *info)
rc->rc_backchannel = info;
break;
case CLSET_TLS:
rc->rc_tls = true;
break;
default:
return (FALSE);
}

View File

@ -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 @@ clnt_vc_call(
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,13 +946,32 @@ clnt_vc_soupcall(struct socket *so, void *arg, int waitflag)
{
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
@ -881,8 +993,14 @@ clnt_vc_soupcall(struct socket *so, void *arg, int waitflag)
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 waitflag)
*/
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
@ -1109,3 +1273,50 @@ clnt_vc_upcallsdone(struct ct_data *ct)
(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();
}

View File

@ -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 */
};

View File

@ -62,8 +62,14 @@ __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 */
@ -864,14 +870,116 @@ bindresvport(struct socket *so, struct sockaddr *sa)
return (error);
}
/*
* 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",

View File

@ -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,11 +73,15 @@ enum clnt_stat rpctls_srv_disconnect(uint64_t sec, uint64_t usec,
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 */
#endif /* _RPC_RPCSEC_TLS_H_ */

View File

@ -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)
{
}

View File

@ -0,0 +1,727 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2008 Isilon Inc http://www.isilon.com/
* Authors: Doug Rabson <dfr@rabson.org>
* Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*/
/* Modified from the kernel GSSAPI code for RPC-over-TLS. */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_kern_tls.h"
#include <sys/param.h>
#include <sys/capsicum.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/mutex.h>
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/socketvar.h>
#include <sys/syscall.h>
#include <sys/syscallsubr.h>
#include <sys/sysent.h>
#include <sys/sysproto.h>
#include <rpc/rpc.h>
#include <rpc/rpc_com.h>
#include <rpc/rpcsec_tls.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <vm/vm_param.h>
#include "rpctlscd.h"
#include "rpctlssd.h"
extern struct fileops badfileops;
/*
* Syscall hooks
*/
static struct syscall_helper_data rpctls_syscalls[] = {
SYSCALL_INIT_HELPER(rpctls_syscall),
SYSCALL_INIT_LAST
};
static CLIENT *rpctls_connect_handle;
static struct mtx rpctls_connect_lock;
static struct socket *rpctls_connect_so = NULL;
static CLIENT *rpctls_connect_cl = NULL;
static CLIENT *rpctls_server_handle;
static struct mtx rpctls_server_lock;
static struct socket *rpctls_server_so = NULL;
static SVCXPRT *rpctls_server_xprt = NULL;
static struct opaque_auth rpctls_null_verf;
static CLIENT *rpctls_connect_client(void);
static CLIENT *rpctls_server_client(void);
static enum clnt_stat rpctls_server(SVCXPRT *xprt, struct socket *so,
uint32_t *flags, uint64_t *sslp,
uid_t *uid, int *ngrps, gid_t **gids);
int
rpctls_init(void)
{
int error;
error = syscall_helper_register(rpctls_syscalls, SY_THR_STATIC_KLD);
if (error != 0) {
printf("rpctls_init: cannot register syscall\n");
return (error);
}
mtx_init(&rpctls_connect_lock, "rpctls_connect_lock", NULL,
MTX_DEF);
mtx_init(&rpctls_server_lock, "rpctls_server_lock", NULL,
MTX_DEF);
rpctls_null_verf.oa_flavor = AUTH_NULL;
rpctls_null_verf.oa_base = RPCTLS_START_STRING;
rpctls_null_verf.oa_length = strlen(RPCTLS_START_STRING);
return (0);
}
int
sys_rpctls_syscall(struct thread *td, struct rpctls_syscall_args *uap)
{
struct sockaddr_un sun;
struct netconfig *nconf;
struct file *fp;
struct socket *so;
SVCXPRT *xprt;
char path[MAXPATHLEN];
int fd = -1, error, try_count;
CLIENT *cl, *oldcl, *concl;
uint64_t ssl[3];
struct timeval timeo;
#ifdef KERN_TLS
u_int maxlen;
#endif
error = priv_check(td, PRIV_NFS_DAEMON);
if (error != 0)
return (error);
switch (uap->op) {
case RPCTLS_SYSC_CLSETPATH:
error = copyinstr(uap->path, path, sizeof(path), NULL);
if (error == 0) {
error = ENXIO;
#ifdef KERN_TLS
if (rpctls_getinfo(&maxlen, false, false))
error = 0;
#endif
}
if (error == 0 && (strlen(path) + 1 > sizeof(sun.sun_path) ||
strlen(path) == 0))
error = EINVAL;
cl = NULL;
if (error == 0) {
sun.sun_family = AF_LOCAL;
strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
sun.sun_len = SUN_LEN(&sun);
nconf = getnetconfigent("local");
cl = clnt_reconnect_create(nconf,
(struct sockaddr *)&sun, RPCTLSCD, RPCTLSCDVERS,
RPC_MAXDATASIZE, RPC_MAXDATASIZE);
/*
* The number of retries defaults to INT_MAX, which
* effectively means an infinite, uninterruptable loop.
* Set the try_count to 1 so that no retries of the
* RPC occur. Since it is an upcall to a local daemon,
* requests should not be lost and doing one of these
* RPCs multiple times is not correct.
* If the server is not working correctly, the
* daemon can get stuck in SSL_connect() trying
* to read data from the socket during the upcall.
* Set a timeout (currently 15sec) and assume the
* daemon is hung when the timeout occurs.
*/
if (cl != NULL) {
try_count = 1;
CLNT_CONTROL(cl, CLSET_RETRIES, &try_count);
timeo.tv_sec = 15;
timeo.tv_usec = 0;
CLNT_CONTROL(cl, CLSET_TIMEOUT, &timeo);
} else
error = EINVAL;
}
mtx_lock(&rpctls_connect_lock);
oldcl = rpctls_connect_handle;
rpctls_connect_handle = cl;
mtx_unlock(&rpctls_connect_lock);
if (oldcl != NULL) {
CLNT_CLOSE(oldcl);
CLNT_RELEASE(oldcl);
}
break;
case RPCTLS_SYSC_SRVSETPATH:
error = copyinstr(uap->path, path, sizeof(path), NULL);
if (error == 0) {
error = ENXIO;
#ifdef KERN_TLS
if (rpctls_getinfo(&maxlen, false, false))
error = 0;
#endif
}
if (error == 0 && (strlen(path) + 1 > sizeof(sun.sun_path) ||
strlen(path) == 0))
error = EINVAL;
cl = NULL;
if (error == 0) {
sun.sun_family = AF_LOCAL;
strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
sun.sun_len = SUN_LEN(&sun);
nconf = getnetconfigent("local");
cl = clnt_reconnect_create(nconf,
(struct sockaddr *)&sun, RPCTLSSD, RPCTLSSDVERS,
RPC_MAXDATASIZE, RPC_MAXDATASIZE);
/*
* The number of retries defaults to INT_MAX, which
* effectively means an infinite, uninterruptable loop.
* Set the try_count to 1 so that no retries of the
* RPC occur. Since it is an upcall to a local daemon,
* requests should not be lost and doing one of these
* RPCs multiple times is not correct.
* Set a timeout (currently 15sec) and assume that
* the daemon is hung if a timeout occurs.
*/
if (cl != NULL) {
try_count = 1;
CLNT_CONTROL(cl, CLSET_RETRIES, &try_count);
timeo.tv_sec = 15;
timeo.tv_usec = 0;
CLNT_CONTROL(cl, CLSET_TIMEOUT, &timeo);
} else
error = EINVAL;
}
mtx_lock(&rpctls_server_lock);
oldcl = rpctls_server_handle;
rpctls_server_handle = cl;
mtx_unlock(&rpctls_server_lock);
if (oldcl != NULL) {
CLNT_CLOSE(oldcl);
CLNT_RELEASE(oldcl);
}
break;
case RPCTLS_SYSC_CLSHUTDOWN:
mtx_lock(&rpctls_connect_lock);
oldcl = rpctls_connect_handle;
rpctls_connect_handle = NULL;
mtx_unlock(&rpctls_connect_lock);
if (oldcl != NULL) {
CLNT_CLOSE(oldcl);
CLNT_RELEASE(oldcl);
}
break;
case RPCTLS_SYSC_SRVSHUTDOWN:
mtx_lock(&rpctls_server_lock);
oldcl = rpctls_server_handle;
rpctls_server_handle = NULL;
mtx_unlock(&rpctls_server_lock);
if (oldcl != NULL) {
CLNT_CLOSE(oldcl);
CLNT_RELEASE(oldcl);
}
break;
case RPCTLS_SYSC_CLSOCKET:
mtx_lock(&rpctls_connect_lock);
so = rpctls_connect_so;
rpctls_connect_so = NULL;
concl = rpctls_connect_cl;
rpctls_connect_cl = NULL;
mtx_unlock(&rpctls_connect_lock);
if (so != NULL) {
error = falloc(td, &fp, &fd, 0);
if (error == 0) {
/*
* Set ssl refno so that clnt_vc_destroy() will
* not close the socket and will leave that for
* the daemon to do.
*/
soref(so);
ssl[0] = ssl[1] = 0;
ssl[2] = RPCTLS_REFNO_HANDSHAKE;
CLNT_CONTROL(concl, CLSET_TLS, ssl);
finit(fp, FREAD | FWRITE, DTYPE_SOCKET, so,
&socketops);
fdrop(fp, td); /* Drop fp reference. */
td->td_retval[0] = fd;
}
} else
error = EPERM;
break;
case RPCTLS_SYSC_SRVSOCKET:
mtx_lock(&rpctls_server_lock);
so = rpctls_server_so;
rpctls_server_so = NULL;
xprt = rpctls_server_xprt;
rpctls_server_xprt = NULL;
mtx_unlock(&rpctls_server_lock);
if (so != NULL) {
error = falloc(td, &fp, &fd, 0);
if (error == 0) {
/*
* Once this file descriptor is associated
* with the socket, it cannot be closed by
* the server side krpc code (svc_vc.c).
*/
soref(so);
sx_xlock(&xprt->xp_lock);
xprt->xp_tls = RPCTLS_FLAGS_HANDSHFAIL;
sx_xunlock(&xprt->xp_lock);
finit(fp, FREAD | FWRITE, DTYPE_SOCKET, so,
&socketops);
fdrop(fp, td); /* Drop fp reference. */
td->td_retval[0] = fd;
}
} else
error = EPERM;
break;
default:
error = EINVAL;
}
return (error);
}
/*
* Acquire the rpctls_connect_handle and return it with a reference count,
* if it is available.
*/
static CLIENT *
rpctls_connect_client(void)
{
CLIENT *cl;
mtx_lock(&rpctls_connect_lock);
cl = rpctls_connect_handle;
if (cl != NULL)
CLNT_ACQUIRE(cl);
mtx_unlock(&rpctls_connect_lock);
return (cl);
}
/*
* Acquire the rpctls_server_handle and return it with a reference count,
* if it is available.
*/
static CLIENT *
rpctls_server_client(void)
{
CLIENT *cl;
mtx_lock(&rpctls_server_lock);
cl = rpctls_server_handle;
if (cl != NULL)
CLNT_ACQUIRE(cl);
mtx_unlock(&rpctls_server_lock);
return (cl);
}
/* Do an upcall for a new socket connect using TLS. */
enum clnt_stat
rpctls_connect(CLIENT *newclient, struct socket *so, uint64_t *sslp,
uint32_t *reterr)
{
struct rpctlscd_connect_res res;
struct rpc_callextra ext;
struct timeval utimeout;
enum clnt_stat stat;
CLIENT *cl;
int val;
static bool rpctls_connect_busy = false;
cl = rpctls_connect_client();
if (cl == NULL)
return (RPC_AUTHERROR);
/* First, do the AUTH_TLS NULL RPC. */
memset(&ext, 0, sizeof(ext));
utimeout.tv_sec = 30;
utimeout.tv_usec = 0;
ext.rc_auth = authtls_create();
stat = clnt_call_private(newclient, &ext, NULLPROC, (xdrproc_t)xdr_void,
NULL, (xdrproc_t)xdr_void, NULL, utimeout);
AUTH_DESTROY(ext.rc_auth);
if (stat == RPC_AUTHERROR)
return (stat);
if (stat != RPC_SUCCESS)
return (RPC_SYSTEMERROR);
/* Serialize the connect upcalls. */
mtx_lock(&rpctls_connect_lock);
while (rpctls_connect_busy)
msleep(&rpctls_connect_busy, &rpctls_connect_lock, PVFS,
"rtlscn", 0);
rpctls_connect_busy = true;
rpctls_connect_so = so;
rpctls_connect_cl = newclient;
mtx_unlock(&rpctls_connect_lock);
/* Temporarily block reception during the handshake upcall. */
val = 1;
CLNT_CONTROL(newclient, CLSET_BLOCKRCV, &val);
/* Do the connect handshake upcall. */
stat = rpctlscd_connect_1(NULL, &res, cl);
if (stat == RPC_SUCCESS) {
*reterr = res.reterr;
if (res.reterr == 0) {
*sslp++ = res.sec;
*sslp++ = res.usec;
*sslp = res.ssl;
}
} else if (stat == RPC_TIMEDOUT) {
/*
* Do a shutdown on the socket, since the daemon is probably
* stuck in SSL_connect() trying to read the socket.
* Do not soclose() the socket, since the daemon will close()
* the socket after SSL_connect() returns an error.
*/
soshutdown(so, SHUT_RD);
}
CLNT_RELEASE(cl);
/* Unblock reception. */
val = 0;
CLNT_CONTROL(newclient, CLSET_BLOCKRCV, &val);
/* Once the upcall is done, the daemon is done with the fp and so. */
mtx_lock(&rpctls_connect_lock);
rpctls_connect_so = NULL;
rpctls_connect_cl = NULL;
rpctls_connect_busy = false;
wakeup(&rpctls_connect_busy);
mtx_unlock(&rpctls_connect_lock);
return (stat);
}
/* Do an upcall to handle an non-application data record using TLS. */
enum clnt_stat
rpctls_cl_handlerecord(uint64_t sec, uint64_t usec, uint64_t ssl,
uint32_t *reterr)
{
struct rpctlscd_handlerecord_arg arg;
struct rpctlscd_handlerecord_res res;
enum clnt_stat stat;
CLIENT *cl;
cl = rpctls_connect_client();
if (cl == NULL) {
*reterr = RPCTLSERR_NOSSL;
return (RPC_SUCCESS);
}
/* Do the handlerecord upcall. */
arg.sec = sec;
arg.usec = usec;
arg.ssl = ssl;
stat = rpctlscd_handlerecord_1(&arg, &res, cl);
CLNT_RELEASE(cl);
if (stat == RPC_SUCCESS)
*reterr = res.reterr;
return (stat);
}
enum clnt_stat
rpctls_srv_handlerecord(uint64_t sec, uint64_t usec, uint64_t ssl,
uint32_t *reterr)
{
struct rpctlssd_handlerecord_arg arg;
struct rpctlssd_handlerecord_res res;
enum clnt_stat stat;
CLIENT *cl;
cl = rpctls_server_client();
if (cl == NULL) {
*reterr = RPCTLSERR_NOSSL;
return (RPC_SUCCESS);
}
/* Do the handlerecord upcall. */
arg.sec = sec;
arg.usec = usec;
arg.ssl = ssl;
stat = rpctlssd_handlerecord_1(&arg, &res, cl);
CLNT_RELEASE(cl);
if (stat == RPC_SUCCESS)
*reterr = res.reterr;
return (stat);
}
/* Do an upcall to shut down a socket using TLS. */
enum clnt_stat
rpctls_cl_disconnect(uint64_t sec, uint64_t usec, uint64_t ssl,
uint32_t *reterr)
{
struct rpctlscd_disconnect_arg arg;
struct rpctlscd_disconnect_res res;
enum clnt_stat stat;
CLIENT *cl;
cl = rpctls_connect_client();
if (cl == NULL) {
*reterr = RPCTLSERR_NOSSL;
return (RPC_SUCCESS);
}
/* Do the disconnect upcall. */
arg.sec = sec;
arg.usec = usec;
arg.ssl = ssl;
stat = rpctlscd_disconnect_1(&arg, &res, cl);
CLNT_RELEASE(cl);
if (stat == RPC_SUCCESS)
*reterr = res.reterr;
return (stat);
}
enum clnt_stat
rpctls_srv_disconnect(uint64_t sec, uint64_t usec, uint64_t ssl,
uint32_t *reterr)
{
struct rpctlssd_disconnect_arg arg;
struct rpctlssd_disconnect_res res;
enum clnt_stat stat;
CLIENT *cl;
cl = rpctls_server_client();
if (cl == NULL) {
*reterr = RPCTLSERR_NOSSL;
return (RPC_SUCCESS);
}
/* Do the disconnect upcall. */
arg.sec = sec;
arg.usec = usec;
arg.ssl = ssl;
stat = rpctlssd_disconnect_1(&arg, &res, cl);
CLNT_RELEASE(cl);
if (stat == RPC_SUCCESS)
*reterr = res.reterr;
return (stat);
}
/* Do an upcall for a new server socket using TLS. */
static enum clnt_stat
rpctls_server(SVCXPRT *xprt, struct socket *so, uint32_t *flags, uint64_t *sslp,
uid_t *uid, int *ngrps, gid_t **gids)
{
enum clnt_stat stat;
CLIENT *cl;
struct rpctlssd_connect_res res;
gid_t *gidp;
uint32_t *gidv;
int i;
static bool rpctls_server_busy = false;
cl = rpctls_server_client();
if (cl == NULL)
return (RPC_SYSTEMERROR);
/* Serialize the server upcalls. */
mtx_lock(&rpctls_server_lock);
while (rpctls_server_busy)
msleep(&rpctls_server_busy, &rpctls_server_lock, PVFS,
"rtlssn", 0);
rpctls_server_busy = true;
rpctls_server_so = so;
rpctls_server_xprt = xprt;
mtx_unlock(&rpctls_server_lock);
/* Do the server upcall. */
stat = rpctlssd_connect_1(NULL, &res, cl);
if (stat == RPC_SUCCESS) {
*flags = res.flags;
*sslp++ = res.sec;
*sslp++ = res.usec;
*sslp = res.ssl;
if ((*flags & (RPCTLS_FLAGS_CERTUSER |
RPCTLS_FLAGS_DISABLED)) == RPCTLS_FLAGS_CERTUSER) {
*ngrps = res.gid.gid_len;
*uid = res.uid;
*gids = gidp = mem_alloc(*ngrps * sizeof(gid_t));
gidv = res.gid.gid_val;
for (i = 0; i < *ngrps; i++)
*gidp++ = *gidv++;
}
} else if (stat == RPC_TIMEDOUT) {
/*
* Do a shutdown on the socket, since the daemon is probably
* stuck in SSL_accept() trying to read the socket.
* Do not soclose() the socket, since the daemon will close()
* the socket after SSL_accept() returns an error.
*/
soshutdown(so, SHUT_RD);
}
CLNT_RELEASE(cl);
/* Once the upcall is done, the daemon is done with the fp and so. */
mtx_lock(&rpctls_server_lock);
rpctls_server_so = NULL;
rpctls_server_xprt = NULL;
rpctls_server_busy = false;
wakeup(&rpctls_server_busy);
mtx_unlock(&rpctls_server_lock);
return (stat);
}
/*
* Handle the NULL RPC with authentication flavor of AUTH_TLS.
* This is a STARTTLS command, so do the upcall to the rpctlssd daemon,
* which will do the TLS handshake.
*/
enum auth_stat
_svcauth_rpcsec_tls(struct svc_req *rqst, struct rpc_msg *msg)
{
bool_t call_stat;
enum clnt_stat stat;
SVCXPRT *xprt;
uint32_t flags;
uint64_t ssl[3];
int ngrps;
uid_t uid;
gid_t *gidp;
#ifdef KERN_TLS
u_int maxlen;
#endif
/* Initialize reply. */
rqst->rq_verf = rpctls_null_verf;
/* Check client credentials. */
if (rqst->rq_cred.oa_length != 0 ||
msg->rm_call.cb_verf.oa_length != 0 ||
msg->rm_call.cb_verf.oa_flavor != AUTH_NULL)
return (AUTH_BADCRED);
if (rqst->rq_proc != NULLPROC)
return (AUTH_REJECTEDCRED);
call_stat = FALSE;
#ifdef KERN_TLS
if (rpctls_getinfo(&maxlen, false, true))
call_stat = TRUE;
#endif
if (!call_stat)
return (AUTH_REJECTEDCRED);
/*
* Disable reception for the krpc so that the TLS handshake can
* be done on the socket in the rpctlssd daemon.
*/
xprt = rqst->rq_xprt;
sx_xlock(&xprt->xp_lock);
xprt->xp_dontrcv = TRUE;
sx_xunlock(&xprt->xp_lock);
/*
* Send the reply to the NULL RPC with AUTH_TLS, which is the
* STARTTLS command for Sun RPC.
*/
call_stat = svc_sendreply(rqst, (xdrproc_t)xdr_void, NULL);
if (!call_stat) {
sx_xlock(&xprt->xp_lock);
xprt->xp_dontrcv = FALSE;
sx_xunlock(&xprt->xp_lock);
xprt_active(xprt); /* Harmless if already active. */
return (AUTH_REJECTEDCRED);
}
/* Do an upcall to do the TLS handshake. */
stat = rpctls_server(xprt, xprt->xp_socket, &flags,
ssl, &uid, &ngrps, &gidp);
/* Re-enable reception on the socket within the krpc. */
sx_xlock(&xprt->xp_lock);
xprt->xp_dontrcv = FALSE;
if (stat == RPC_SUCCESS) {
xprt->xp_tls = flags;
xprt->xp_sslsec = ssl[0];
xprt->xp_sslusec = ssl[1];
xprt->xp_sslrefno = ssl[2];
if ((flags & (RPCTLS_FLAGS_CERTUSER |
RPCTLS_FLAGS_DISABLED)) == RPCTLS_FLAGS_CERTUSER) {
xprt->xp_ngrps = ngrps;
xprt->xp_uid = uid;
xprt->xp_gidp = gidp;
}
}
sx_xunlock(&xprt->xp_lock);
xprt_active(xprt); /* Harmless if already active. */
return (RPCSEC_GSS_NODISPATCH);
}
/*
* Get kern.ipc.tls.enable and kern.ipc.tls.maxlen.
*/
bool
rpctls_getinfo(u_int *maxlenp, bool rpctlscd_run, bool rpctlssd_run)
{
u_int maxlen;
bool enable;
int error;
size_t siz;
if (PMAP_HAS_DMAP == 0 || !mb_use_ext_pgs)
return (false);
siz = sizeof(enable);
error = kernel_sysctlbyname(curthread, "kern.ipc.tls.enable",
&enable, &siz, NULL, 0, NULL, 0);
if (error != 0)
return (false);
siz = sizeof(maxlen);
error = kernel_sysctlbyname(curthread, "kern.ipc.tls.maxlen",
&maxlen, &siz, NULL, 0, NULL, 0);
if (error != 0)
return (false);
if (rpctlscd_run && rpctls_connect_handle == NULL)
return (false);
if (rpctlssd_run && rpctls_server_handle == NULL)
return (false);
*maxlenp = maxlen;
return (enable);
}

View File

@ -0,0 +1,72 @@
/*-
* Copyright (c) 2008 Isilon Inc http://www.isilon.com/
* Authors: Doug Rabson <dfr@rabson.org>
* Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*/
/* Modified from gssd.x for the client side of RPC-over-TLS. */
/* $FreeBSD$ */
struct rpctlscd_connect_res {
uint32_t reterr;
uint64_t sec;
uint64_t usec;
uint64_t ssl;
};
struct rpctlscd_handlerecord_arg {
uint64_t sec;
uint64_t usec;
uint64_t ssl;
};
struct rpctlscd_handlerecord_res {
uint32_t reterr;
};
struct rpctlscd_disconnect_arg {
uint64_t sec;
uint64_t usec;
uint64_t ssl;
};
struct rpctlscd_disconnect_res {
uint32_t reterr;
};
program RPCTLSCD {
version RPCTLSCDVERS {
void RPCTLSCD_NULL(void) = 0;
rpctlscd_connect_res
RPCTLSCD_CONNECT(void) = 1;
rpctlscd_handlerecord_res
RPCTLSCD_HANDLERECORD(rpctlscd_handlerecord_arg) = 2;
rpctlscd_disconnect_res
RPCTLSCD_DISCONNECT(rpctlscd_disconnect_arg) = 3;
} = 1;
} = 0x40677374;

View File

@ -0,0 +1,74 @@
/*-
* Copyright (c) 2008 Isilon Inc http://www.isilon.com/
* Authors: Doug Rabson <dfr@rabson.org>
* Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*/
/* Modified from gssd.x for the server side of RPC-over-TLS. */
/* $FreeBSD$ */
struct rpctlssd_connect_res {
uint32_t flags;
uint64_t sec;
uint64_t usec;
uint64_t ssl;
uint32_t uid;
uint32_t gid<>;
};
struct rpctlssd_handlerecord_arg {
uint64_t sec;
uint64_t usec;
uint64_t ssl;
};
struct rpctlssd_handlerecord_res {
uint32_t reterr;
};
struct rpctlssd_disconnect_arg {
uint64_t sec;
uint64_t usec;
uint64_t ssl;
};
struct rpctlssd_disconnect_res {
uint32_t reterr;
};
program RPCTLSSD {
version RPCTLSSDVERS {
void RPCTLSSD_NULL(void) = 0;
rpctlssd_connect_res
RPCTLSSD_CONNECT(void) = 1;
rpctlssd_handlerecord_res
RPCTLSSD_HANDLERECORD(rpctlssd_handlerecord_arg) = 2;
rpctlssd_disconnect_res
RPCTLSSD_DISCONNECT(rpctlssd_disconnect_arg) = 3;
} = 1;
} = 0x40677375;

View File

@ -175,6 +175,14 @@ typedef struct __rpc_svcxprt {
int xp_upcallset; /* socket upcall is set up */
uint32_t xp_snd_cnt; /* # of bytes to send to socket */
uint32_t xp_snt_cnt; /* # of bytes sent to socket */
bool_t xp_dontrcv; /* Do not receive on the socket */
uint32_t xp_tls; /* RPC-over-TLS on socket */
uint64_t xp_sslsec; /* Userland SSL * */
uint64_t xp_sslusec;
uint64_t xp_sslrefno;
int xp_ngrps; /* Cred. from TLS cert. */
uid_t xp_uid;
gid_t *xp_gidp;
#else
int xp_fd;
u_short xp_port; /* associated port number */

View File

@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$");
#include <sys/ucred.h>
#include <rpc/rpc.h>
#include <rpc/rpcsec_tls.h>
static enum auth_stat (*_svcauth_rpcsec_gss)(struct svc_req *,
struct rpc_msg *) = NULL;
@ -94,16 +95,25 @@ _authenticate(struct svc_req *rqst, struct rpc_msg *msg)
dummy = _svcauth_null(rqst, msg);
return (dummy);
case AUTH_SYS:
if ((rqst->rq_xprt->xp_tls & RPCTLS_FLAGS_DISABLED) != 0)
return (AUTH_REJECTEDCRED);
dummy = _svcauth_unix(rqst, msg);
return (dummy);
case AUTH_SHORT:
if ((rqst->rq_xprt->xp_tls & RPCTLS_FLAGS_DISABLED) != 0)
return (AUTH_REJECTEDCRED);
dummy = _svcauth_short(rqst, msg);
return (dummy);
case RPCSEC_GSS:
if ((rqst->rq_xprt->xp_tls & RPCTLS_FLAGS_DISABLED) != 0)
return (AUTH_REJECTEDCRED);
if (!_svcauth_rpcsec_gss)
return (AUTH_REJECTEDCRED);
dummy = _svcauth_rpcsec_gss(rqst, msg);
return (dummy);
case AUTH_TLS:
dummy = _svcauth_rpcsec_tls(rqst, msg);
return (dummy);
default:
break;
}
@ -169,11 +179,30 @@ svc_getcred(struct svc_req *rqst, struct ucred **crp, int *flavorp)
struct ucred *cr = NULL;
int flavor;
struct xucred *xcr;
SVCXPRT *xprt = rqst->rq_xprt;
flavor = rqst->rq_cred.oa_flavor;
if (flavorp)
*flavorp = flavor;
/*
* If there are credentials acquired via a TLS
* certificate for this TCP connection, use those
* instead of what is in the RPC header.
*/
if ((xprt->xp_tls & (RPCTLS_FLAGS_CERTUSER |
RPCTLS_FLAGS_DISABLED)) == RPCTLS_FLAGS_CERTUSER &&
flavor == AUTH_UNIX) {
cr = crget();
cr->cr_uid = cr->cr_ruid = cr->cr_svuid = xprt->xp_uid;
crsetgroups(cr, xprt->xp_ngrps, xprt->xp_gidp);
cr->cr_rgid = cr->cr_svgid = xprt->xp_gidp[0];
cr->cr_prison = &prison0;
prison_hold(cr->cr_prison);
*crp = cr;
return (TRUE);
}
switch (flavor) {
case AUTH_UNIX:
xcr = (struct xucred *) rqst->rq_clntcred;

View File

@ -45,10 +45,13 @@ __FBSDID("$FreeBSD$");
* and a record/tcp stream.
*/
#include "opt_kern_tls.h"
#include <sys/param.h>
#include <sys/limits.h>
#include <sys/lock.h>
#include <sys/kernel.h>
#include <sys/ktls.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/mutex.h>
@ -66,6 +69,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/tcp.h>
#include <rpc/rpc.h>
#include <rpc/rpcsec_tls.h>
#include <rpc/krpc.h>
#include <rpc/rpc_com.h>
@ -447,9 +451,31 @@ svc_vc_rendezvous_stat(SVCXPRT *xprt)
static void
svc_vc_destroy_common(SVCXPRT *xprt)
{
enum clnt_stat stat;
uint32_t reterr;
if (xprt->xp_socket)
(void)soclose(xprt->xp_socket);
if (xprt->xp_socket) {
if ((xprt->xp_tls & (RPCTLS_FLAGS_HANDSHAKE |
RPCTLS_FLAGS_HANDSHFAIL)) != 0) {
if ((xprt->xp_tls & RPCTLS_FLAGS_HANDSHAKE) != 0) {
/*
* If the upcall fails, the socket has
* probably been closed via the rpctlssd
* daemon having crashed or been
* restarted, so just ignore returned stat.
*/
stat = rpctls_srv_disconnect(xprt->xp_sslsec,
xprt->xp_sslusec, xprt->xp_sslrefno,
&reterr);
}
/* Must sorele() to get rid of reference. */
CURVNET_SET(xprt->xp_socket->so_vnet);
SOCK_LOCK(xprt->xp_socket);
sorele(xprt->xp_socket);
CURVNET_RESTORE();
} else
(void)soclose(xprt->xp_socket);
}
if (xprt->xp_netid)
(void) mem_free(xprt->xp_netid, strlen(xprt->xp_netid) + 1);
@ -478,7 +504,8 @@ svc_vc_destroy(SVCXPRT *xprt)
SOCKBUF_LOCK(&xprt->xp_socket->so_rcv);
if (xprt->xp_upcallset) {
xprt->xp_upcallset = 0;
soupcall_clear(xprt->xp_socket, SO_RCV);
if (xprt->xp_socket->so_rcv.sb_upcall != NULL)
soupcall_clear(xprt->xp_socket, SO_RCV);
}
SOCKBUF_UNLOCK(&xprt->xp_socket->so_rcv);
@ -656,11 +683,14 @@ svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg,
{
struct cf_conn *cd = (struct cf_conn *) xprt->xp_p1;
struct uio uio;
struct mbuf *m;
struct mbuf *m, *ctrl;
struct socket* so = xprt->xp_socket;
XDR xdrs;
int error, rcvflag;
uint32_t xid_plus_direction[2];
uint32_t reterr, xid_plus_direction[2];
struct cmsghdr *cmsg;
struct tls_get_record tgr;
enum clnt_stat ret;
/*
* Serialise access to the socket and our own record parsing
@ -731,6 +761,19 @@ svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg,
return (TRUE);
}
/*
* If receiving is disabled so that a TLS handshake can be
* done by the rpctlssd daemon, return FALSE here.
*/
rcvflag = MSG_DONTWAIT;
if ((xprt->xp_tls & RPCTLS_FLAGS_HANDSHAKE) != 0)
rcvflag |= MSG_TLSAPPDATA;
tryagain:
if (xprt->xp_dontrcv) {
sx_xunlock(&xprt->xp_lock);
return (FALSE);
}
/*
* The socket upcall calls xprt_active() which will eventually
* cause the server to call us here. We attempt to
@ -741,9 +784,8 @@ svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg,
*/
uio.uio_resid = 1000000000;
uio.uio_td = curthread;
m = NULL;
rcvflag = MSG_DONTWAIT;
error = soreceive(so, NULL, &uio, &m, NULL, &rcvflag);
ctrl = m = NULL;
error = soreceive(so, NULL, &uio, &m, &ctrl, &rcvflag);
if (error == EWOULDBLOCK) {
/*
@ -761,6 +803,36 @@ svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg,
return (FALSE);
}
/*
* 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 ((xprt->xp_tls & RPCTLS_FLAGS_HANDSHAKE) != 0 &&
error == ENXIO) {
/* Disable reception. */
xprt->xp_dontrcv = TRUE;
sx_xunlock(&xprt->xp_lock);
ret = rpctls_srv_handlerecord(xprt->xp_sslsec,
xprt->xp_sslusec, xprt->xp_sslrefno,
&reterr);
sx_xlock(&xprt->xp_lock);
xprt->xp_dontrcv = FALSE;
if (ret != RPC_SUCCESS || reterr != RPCTLSERR_OK) {
/*
* All we can do is soreceive() it and
* then toss it.
*/
rcvflag = MSG_DONTWAIT;
goto tryagain;
}
sx_xunlock(&xprt->xp_lock);
xprt_active(xprt); /* Harmless if already active. */
return (FALSE);
}
if (error) {
SOCKBUF_LOCK(&so->so_rcv);
if (xprt->xp_upcallset) {
@ -784,6 +856,28 @@ svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg,
return (FALSE);
}
/* Process any record header(s). */
if (ctrl != NULL) {
cmsg = mtod(ctrl, 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
* the rpctls_svc_handlerecord()
* upcall. If not, all we can do is
* toss it away.
*/
if (tgr.tls_type != TLS_RLTYPE_APP) {
m_freem(m);
m_free(ctrl);
rcvflag = MSG_DONTWAIT | MSG_TLSAPPDATA;
goto tryagain;
}
}
m_free(ctrl);
}
if (cd->mpending)
m_last(cd->mpending)->m_next = m;
else
@ -836,7 +930,10 @@ svc_vc_reply(SVCXPRT *xprt, struct rpc_msg *msg,
XDR xdrs;
struct mbuf *mrep;
bool_t stat = TRUE;
int error, len;
int error, len, maxextsiz;
#ifdef KERN_TLS
u_int maxlen;
#endif
/*
* Leave space for record mark.
@ -866,7 +963,24 @@ svc_vc_reply(SVCXPRT *xprt, struct rpc_msg *msg,
len = mrep->m_pkthdr.len;
*mtod(mrep, uint32_t *) =
htonl(0x80000000 | (len - sizeof(uint32_t)));
/* 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
mrep = _rpc_copym_into_ext_pgs(mrep, maxextsiz);
}
atomic_add_32(&xprt->xp_snd_cnt, len);
/*
* sosend consumes mreq.
*/
error = sosend(xprt->xp_socket, NULL, NULL, mrep, NULL,
0, curthread);
if (!error) {
@ -893,7 +1007,10 @@ svc_vc_backchannel_reply(SVCXPRT *xprt, struct rpc_msg *msg,
XDR xdrs;
struct mbuf *mrep;
bool_t stat = TRUE;
int error;
int error, maxextsiz;
#ifdef KERN_TLS
u_int maxlen;
#endif
/*
* Leave space for record mark.
@ -923,6 +1040,20 @@ svc_vc_backchannel_reply(SVCXPRT *xprt, struct rpc_msg *msg,
*mtod(mrep, uint32_t *) =
htonl(0x80000000 | (mrep->m_pkthdr.len
- sizeof(uint32_t)));
/* 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
mrep = _rpc_copym_into_ext_pgs(mrep, maxextsiz);
}
sx_xlock(&xprt->xp_lock);
ct = (struct ct_data *)xprt->xp_p2;
if (ct != NULL)