Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 23 Dec 2020 21:43:32 GMT
From:      Rick Macklem <rmacklem@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 665b1365fe8e - Add a new "tlscertname" NFS mount option.
Message-ID:  <202012232143.0BNLhWTG037012@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by rmacklem:

URL: https://cgit.FreeBSD.org/src/commit/?id=665b1365fe8e24d618d63b0d57b0b4ad39e97824

commit 665b1365fe8e24d618d63b0d57b0b4ad39e97824
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2020-12-21 23:14:53 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2020-12-23 21:42:55 +0000

    Add a new "tlscertname" NFS mount option.
    
    When using NFS-over-TLS, an NFS client can optionally provide an X.509
    certificate to the server during the TLS handshake.  For some situations,
    such as different NFS servers or different certificates being mapped
    to different user credentials on the NFS server, there may be a need
    for different mounts to provide different certificates.
    
    This new mount option called "tlscertname" may be used to specify a
    non-default certificate be provided.  This alernate certificate will
    be stored in /etc/rpc.tlsclntd in a file with a name based on what is
    provided by this mount option.
---
 sys/fs/nfs/nfs_commonkrpc.c      |  6 +++++-
 sys/fs/nfsclient/nfs_clvfsops.c  | 34 ++++++++++++++++++++++++++++------
 sys/fs/nfsclient/nfsmount.h      |  1 +
 sys/rpc/clnt.h                   |  1 +
 sys/rpc/clnt_rc.c                | 20 +++++++++++++++++++-
 sys/rpc/krpc.h                   |  1 +
 sys/rpc/rpcsec_tls.h             |  4 ++--
 sys/rpc/rpcsec_tls/rpctls_impl.c | 12 +++++++++---
 sys/rpc/rpcsec_tls/rpctlscd.x    |  6 +++++-
 9 files changed, 71 insertions(+), 14 deletions(-)

diff --git a/sys/fs/nfs/nfs_commonkrpc.c b/sys/fs/nfs/nfs_commonkrpc.c
index 19896a59718d..daf8082fa1c3 100644
--- a/sys/fs/nfs/nfs_commonkrpc.c
+++ b/sys/fs/nfs/nfs_commonkrpc.c
@@ -281,8 +281,12 @@ newnfs_connect(struct nfsmount *nmp, struct nfssockreq *nrp,
 			CLNT_CONTROL(client, CLSET_INTERRUPTIBLE, &one);
 		if ((nmp->nm_flag & NFSMNT_RESVPORT))
 			CLNT_CONTROL(client, CLSET_PRIVPORT, &one);
-		if (NFSHASTLS(nmp))
+		if (NFSHASTLS(nmp)) {
 			CLNT_CONTROL(client, CLSET_TLS, &one);
+			if (nmp->nm_tlscertname != NULL)
+				CLNT_CONTROL(client, CLSET_TLSCERTNAME,
+				    nmp->nm_tlscertname);
+		}
 		if (NFSHASSOFT(nmp)) {
 			if (nmp->nm_sotype == SOCK_DGRAM)
 				/*
diff --git a/sys/fs/nfsclient/nfs_clvfsops.c b/sys/fs/nfsclient/nfs_clvfsops.c
index 8b059ef1be9f..365b1c387cc2 100644
--- a/sys/fs/nfsclient/nfs_clvfsops.c
+++ b/sys/fs/nfsclient/nfs_clvfsops.c
@@ -119,7 +119,7 @@ static void	nfs_decode_args(struct mount *mp, struct nfsmount *nmp,
 static int	mountnfs(struct nfs_args *, struct mount *,
 		    struct sockaddr *, char *, u_char *, int, u_char *, int,
 		    u_char *, int, struct vnode **, struct ucred *,
-		    struct thread *, int, int, int, uint32_t);
+		    struct thread *, int, int, int, uint32_t, char *);
 static void	nfs_getnlminfo(struct vnode *, uint8_t *, size_t *,
 		    struct sockaddr_storage *, int *, off_t *,
 		    struct timeval *);
@@ -545,7 +545,7 @@ nfs_mountdiskless(char *path,
 	nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK);
 	if ((error = mountnfs(args, mp, nam, path, NULL, 0, dirpath, dirlen,
 	    NULL, 0, vpp, td->td_ucred, td, NFS_DEFAULT_NAMETIMEO, 
-	    NFS_DEFAULT_NEGNAMETIMEO, 0, 0)) != 0) {
+	    NFS_DEFAULT_NEGNAMETIMEO, 0, 0, NULL)) != 0) {
 		printf("nfs_mountroot: mount %s on /: %d\n", path, error);
 		return (error);
 	}
@@ -747,7 +747,7 @@ static const char *nfs_opts[] = { "from", "nfs_args",
     "resvport", "readahead", "hostname", "timeo", "timeout", "addr", "fh",
     "nfsv3", "sec", "principal", "nfsv4", "gssname", "allgssname", "dirpath",
     "minorversion", "nametimeo", "negnametimeo", "nocto", "noncontigwr",
-    "pnfs", "wcommitsize", "oneopenown", "tls",
+    "pnfs", "wcommitsize", "oneopenown", "tls", "tlscertname",
     NULL };
 
 /*
@@ -891,7 +891,7 @@ nfs_mount(struct mount *mp)
 	struct thread *td;
 	char *hst;
 	u_char nfh[NFSX_FHMAX], krbname[100], dirpath[100], srvkrbname[100];
-	char *cp, *opt, *name, *secname;
+	char *cp, *opt, *name, *secname, *tlscertname;
 	int nametimeo = NFS_DEFAULT_NAMETIMEO;
 	int negnametimeo = NFS_DEFAULT_NEGNAMETIMEO;
 	int minvers = 0;
@@ -903,6 +903,7 @@ nfs_mount(struct mount *mp)
 	has_nfs_args_opt = 0;
 	has_nfs_from_opt = 0;
 	newflag = 0;
+	tlscertname = NULL;
 	hst = malloc(MNAMELEN, M_TEMP, M_WAITOK);
 	if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) {
 		error = EINVAL;
@@ -988,6 +989,22 @@ nfs_mount(struct mount *mp)
 		args.flags |= NFSMNT_ONEOPENOWN;
 	if (vfs_getopt(mp->mnt_optnew, "tls", NULL, NULL) == 0)
 		newflag |= NFSMNT_TLS;
+	if (vfs_getopt(mp->mnt_optnew, "tlscertname", (void **)&opt, &len) ==
+	    0) {
+		/*
+		 * tlscertname with "key.pem" appended to it forms a file
+		 * name.  As such, the maximum allowable strlen(tlscertname) is
+		 * NAME_MAX - 7. However, "len" includes the nul termination
+		 * byte so it can be up to NAME_MAX - 6.
+		 */
+		if (opt == NULL || len <= 1 || len > NAME_MAX - 6) {
+			vfs_mount_error(mp, "invalid tlscertname");
+			error = EINVAL;
+			goto out;
+		}
+		tlscertname = malloc(len, M_NEWNFSMNT, M_WAITOK);
+		strlcpy(tlscertname, opt, len);
+	}
 	if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) {
 		if (opt == NULL) { 
 			vfs_mount_error(mp, "illegal readdirsize");
@@ -1342,7 +1359,7 @@ nfs_mount(struct mount *mp)
 	args.fh = nfh;
 	error = mountnfs(&args, mp, nam, hst, krbname, krbnamelen, dirpath,
 	    dirlen, srvkrbname, srvkrbnamelen, &vp, td->td_ucred, td,
-	    nametimeo, negnametimeo, minvers, newflag);
+	    nametimeo, negnametimeo, minvers, newflag, tlscertname);
 out:
 	if (!error) {
 		MNT_ILOCK(mp);
@@ -1390,7 +1407,7 @@ mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
     char *hst, u_char *krbname, int krbnamelen, u_char *dirpath, int dirlen,
     u_char *srvkrbname, int srvkrbnamelen, struct vnode **vpp,
     struct ucred *cred, struct thread *td, int nametimeo, int negnametimeo,
-    int minvers, uint32_t newflag)
+    int minvers, uint32_t newflag, char *tlscertname)
 {
 	struct nfsmount *nmp;
 	struct nfsnode *np;
@@ -1410,6 +1427,7 @@ mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
 		nmp = VFSTONFS(mp);
 		printf("%s: MNT_UPDATE is no longer handled here\n", __func__);
 		free(nam, M_SONAME);
+		free(tlscertname, M_NEWNFSMNT);
 		return (0);
 	} else {
 		/* NFS-over-TLS requires that rpctls be functioning. */
@@ -1423,12 +1441,14 @@ mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
 #endif
 			if (error != 0) {
 				free(nam, M_SONAME);
+				free(tlscertname, M_NEWNFSMNT);
 				return (error);
 			}
 		}
 		nmp = malloc(sizeof (struct nfsmount) +
 		    krbnamelen + dirlen + srvkrbnamelen + 2,
 		    M_NEWNFSMNT, M_WAITOK | M_ZERO);
+		nmp->nm_tlscertname = tlscertname;
 		nmp->nm_newflag = newflag;
 		TAILQ_INIT(&nmp->nm_bufq);
 		TAILQ_INIT(&nmp->nm_sess);
@@ -1681,6 +1701,7 @@ bad:
 			newnfs_disconnect(dsp->nfsclds_sockp);
 		nfscl_freenfsclds(dsp);
 	}
+	free(nmp->nm_tlscertname, M_NEWNFSMNT);
 	free(nmp, M_NEWNFSMNT);
 	free(nam, M_SONAME);
 	return (error);
@@ -1776,6 +1797,7 @@ nfs_unmount(struct mount *mp, int mntflags)
 			newnfs_disconnect(dsp->nfsclds_sockp);
 		nfscl_freenfsclds(dsp);
 	}
+	free(nmp->nm_tlscertname, M_NEWNFSMNT);
 	free(nmp, M_NEWNFSMNT);
 out:
 	return (error);
diff --git a/sys/fs/nfsclient/nfsmount.h b/sys/fs/nfsclient/nfsmount.h
index 063926eceaf5..57adcd8f2fca 100644
--- a/sys/fs/nfsclient/nfsmount.h
+++ b/sys/fs/nfsclient/nfsmount.h
@@ -76,6 +76,7 @@ struct	nfsmount {
 	/* Newnfs additions */
 	TAILQ_HEAD(, nfsclds) nm_sess;	/* Session(s) for NFSv4.1. */
 	struct	nfsclclient *nm_clp;
+	char	*nm_tlscertname;	/* TLS certificate file name */
 	uid_t	nm_uid;			/* Uid for SetClientID etc. */
 	u_int64_t nm_clval;		/* identifies which clientid */
 	u_int64_t nm_fsid[2];		/* NFSv4 fsid */
diff --git a/sys/rpc/clnt.h b/sys/rpc/clnt.h
index 23c92103edff..f4cc78b1c3b6 100644
--- a/sys/rpc/clnt.h
+++ b/sys/rpc/clnt.h
@@ -359,6 +359,7 @@ enum clnt_stat clnt_call_private(CLIENT *, struct rpc_callextra *, rpcproc_t,
 #define CLSET_BACKCHANNEL	29	/* set backchannel for socket */
 #define	CLSET_TLS		30	/* set TLS for socket */
 #define	CLSET_BLOCKRCV		31	/* Temporarily block reception */
+#define	CLSET_TLSCERTNAME	32	/* TLS certificate file name */
 #endif
 
 
diff --git a/sys/rpc/clnt_rc.c b/sys/rpc/clnt_rc.c
index 730001723e94..8c204989d0ea 100644
--- a/sys/rpc/clnt_rc.c
+++ b/sys/rpc/clnt_rc.c
@@ -110,6 +110,7 @@ clnt_reconnect_create(
 	rc->rc_ucred = crdup(curthread->td_ucred);
 	rc->rc_client = NULL;
 	rc->rc_tls = false;
+	rc->rc_tlscertname = NULL;
 
 	cl->cl_refs = 1;
 	cl->cl_ops = &clnt_reconnect_ops;
@@ -198,7 +199,8 @@ clnt_reconnect_connect(CLIENT *cl)
 		    (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);
+			stat = rpctls_connect(newclient, rc->rc_tlscertname, so,
+			    ssl, &reterr);
 			if (stat != RPC_SUCCESS || reterr != RPCTLSERR_OK) {
 				if (stat == RPC_SUCCESS)
 					stat = RPC_FAILED;
@@ -405,6 +407,7 @@ clnt_reconnect_control(CLIENT *cl, u_int request, void *info)
 {
 	struct rc_data *rc = (struct rc_data *)cl->cl_private;
 	SVCXPRT *xprt;
+	size_t slen;
 
 	if (info == NULL) {
 		return (FALSE);
@@ -496,6 +499,20 @@ clnt_reconnect_control(CLIENT *cl, u_int request, void *info)
 		rc->rc_tls = true;
 		break;
 
+	case CLSET_TLSCERTNAME:
+		slen = strlen(info) + 1;
+		/*
+		 * tlscertname with "key.pem" appended to it forms a file
+		 * name.  As such, the maximum allowable strlen(info) is
+		 * NAME_MAX - 7. However, "slen" includes the nul termination
+		 * byte so it can be up to NAME_MAX - 6.
+		 */
+		if (slen <= 1 || slen > NAME_MAX - 6)
+			return (FALSE);
+		rc->rc_tlscertname = mem_alloc(slen);
+		strlcpy(rc->rc_tlscertname, info, slen);
+		break;
+
 	default:
 		return (FALSE);
 	}
@@ -543,6 +560,7 @@ clnt_reconnect_destroy(CLIENT *cl)
 	}
 	crfree(rc->rc_ucred);
 	mtx_destroy(&rc->rc_lock);
+	mem_free(rc->rc_tlscertname, 0);	/* 0 ok, since arg. ignored. */
 	mem_free(rc, sizeof(*rc));
 	mem_free(cl, sizeof (CLIENT));
 }
diff --git a/sys/rpc/krpc.h b/sys/rpc/krpc.h
index 53d46deddf65..77facdcf16cc 100644
--- a/sys/rpc/krpc.h
+++ b/sys/rpc/krpc.h
@@ -80,6 +80,7 @@ struct rc_data {
 	struct rpc_err		rc_err;
 	void			*rc_backchannel;
 	bool			rc_tls; /* Enable TLS on connection */
+	char			*rc_tlscertname;
 };
 
 /* Bits for ct_rcvstate. */
diff --git a/sys/rpc/rpcsec_tls.h b/sys/rpc/rpcsec_tls.h
index a33feff17c06..49a7e71b7514 100644
--- a/sys/rpc/rpcsec_tls.h
+++ b/sys/rpc/rpcsec_tls.h
@@ -58,8 +58,8 @@ int	rpctls_syscall(int, const char *);
 
 #ifdef _KERNEL
 /* Functions that perform upcalls to the rpctlsd daemon. */
-enum clnt_stat	rpctls_connect(CLIENT *newclient, struct socket *so,
-		    uint64_t *sslp, uint32_t *reterr);
+enum clnt_stat	rpctls_connect(CLIENT *newclient, char *certname,
+		    struct socket *so, uint64_t *sslp, uint32_t *reterr);
 enum clnt_stat	rpctls_cl_handlerecord(uint64_t sec, uint64_t usec,
 		    uint64_t ssl, uint32_t *reterr);
 enum clnt_stat	rpctls_srv_handlerecord(uint64_t sec, uint64_t usec,
diff --git a/sys/rpc/rpcsec_tls/rpctls_impl.c b/sys/rpc/rpcsec_tls/rpctls_impl.c
index c5f3ce5d46a6..638f27eaf350 100644
--- a/sys/rpc/rpcsec_tls/rpctls_impl.c
+++ b/sys/rpc/rpcsec_tls/rpctls_impl.c
@@ -356,9 +356,10 @@ rpctls_server_client(void)
 
 /* 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)
+rpctls_connect(CLIENT *newclient, char *certname, struct socket *so,
+    uint64_t *sslp, uint32_t *reterr)
 {
+	struct rpctlscd_connect_arg arg;
 	struct rpctlscd_connect_res res;
 	struct rpc_callextra ext;
 	struct timeval utimeout;
@@ -399,7 +400,12 @@ rpctls_connect(CLIENT *newclient, struct socket *so, uint64_t *sslp,
 	CLNT_CONTROL(newclient, CLSET_BLOCKRCV, &val);
 
 	/* Do the connect handshake upcall. */
-	stat = rpctlscd_connect_1(NULL, &res, cl);
+	if (certname != NULL) {
+		arg.certname.certname_len = strlen(certname);
+		arg.certname.certname_val = certname;
+	} else
+		arg.certname.certname_len = 0;
+	stat = rpctlscd_connect_1(&arg, &res, cl);
 	if (stat == RPC_SUCCESS) {
 		*reterr = res.reterr;
 		if (res.reterr == 0) {
diff --git a/sys/rpc/rpcsec_tls/rpctlscd.x b/sys/rpc/rpcsec_tls/rpctlscd.x
index d5c4d61a43a8..1ae53d7b8d17 100644
--- a/sys/rpc/rpcsec_tls/rpctlscd.x
+++ b/sys/rpc/rpcsec_tls/rpctlscd.x
@@ -29,6 +29,10 @@
 
 /* $FreeBSD$ */
 
+struct rpctlscd_connect_arg {
+	char certname<>;
+};
+
 struct rpctlscd_connect_res {
 	uint32_t reterr;
 	uint64_t sec;
@@ -61,7 +65,7 @@ program RPCTLSCD {
 		void RPCTLSCD_NULL(void) = 0;
 
 		rpctlscd_connect_res
-		RPCTLSCD_CONNECT(void) = 1;
+		RPCTLSCD_CONNECT(rpctlscd_connect_arg) = 1;
 
 		rpctlscd_handlerecord_res
 		RPCTLSCD_HANDLERECORD(rpctlscd_handlerecord_arg) = 2;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202012232143.0BNLhWTG037012>