Date: Thu, 27 Aug 2020 23:57:31 +0000 (UTC) From: Rick Macklem <rmacklem@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r364896 - in head/sys/fs: nfs nfsclient nfsserver Message-ID: <202008272357.07RNvVRf042127@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: rmacklem Date: Thu Aug 27 23:57:30 2020 New Revision: 364896 URL: https://svnweb.freebsd.org/changeset/base/364896 Log: Add flags to enable NFS over TLS to the NFS client and server. An Internet Draft titled "Towards Remote Procedure Call Encryption By Default" (soon to be an RFC I think) describes how Sun RPC is to use TLS with NFS as a specific application case. Various commits prepared the NFS code to use KERN_TLS, mainly enabling use of ext_pgs mbufs for large RPC messages. r364475 added TLS support to the kernel RPC. This commit (which is the final one for kernel changes required to do NFS over TLS) adds support for three export flags: MNT_EXTLS - Requires a TLS connection. MNT_EXTLSCERT - Requires a TLS connection where the client presents a valid X.509 certificate during TLS handshake. MNT_EXTLSCERTUSER - Requires a TLS connection where the client presents a valid X.509 certificate with "user@domain" in the otherName field of the SubjectAltName during TLS handshake. Without these export options, clients are permitted, but not required, to use TLS. For the client, a new nmount(2) option called "tls" makes the client do a STARTTLS Null RPC and TLS handshake for all TCP connections used for the mount. The CLSET_TLS client control option is used to indicate to the kernel RPC that this should be done. Unless the above export flags or "tls" option is used, semantics should not change for the NFS client nor server. For NFS over TLS to work, the userspace daemons rpctlscd(8) { for client } or rpctlssd(8) daemon { for server } must be running. Modified: head/sys/fs/nfs/nfs_commonkrpc.c head/sys/fs/nfs/nfsdport.h head/sys/fs/nfs/nfsport.h head/sys/fs/nfsclient/nfs_clkrpc.c head/sys/fs/nfsclient/nfs_clvfsops.c head/sys/fs/nfsclient/nfsmount.h head/sys/fs/nfsserver/nfs_nfsdkrpc.c head/sys/fs/nfsserver/nfs_nfsdport.c head/sys/fs/nfsserver/nfs_nfsdserv.c head/sys/fs/nfsserver/nfs_nfsdsubs.c Modified: head/sys/fs/nfs/nfs_commonkrpc.c ============================================================================== --- head/sys/fs/nfs/nfs_commonkrpc.c Thu Aug 27 22:14:58 2020 (r364895) +++ head/sys/fs/nfs/nfs_commonkrpc.c Thu Aug 27 23:57:30 2020 (r364896) @@ -281,6 +281,8 @@ newnfs_connect(struct nfsmount *nmp, struct nfssockreq CLNT_CONTROL(client, CLSET_INTERRUPTIBLE, &one); if ((nmp->nm_flag & NFSMNT_RESVPORT)) CLNT_CONTROL(client, CLSET_PRIVPORT, &one); + if (NFSHASTLS(nmp)) + CLNT_CONTROL(client, CLSET_TLS, &one); if (NFSHASSOFT(nmp)) { if (nmp->nm_sotype == SOCK_DGRAM) /* Modified: head/sys/fs/nfs/nfsdport.h ============================================================================== --- head/sys/fs/nfs/nfsdport.h Thu Aug 27 22:14:58 2020 (r364895) +++ head/sys/fs/nfs/nfsdport.h Thu Aug 27 23:57:30 2020 (r364896) @@ -81,6 +81,9 @@ struct nfsexstuff { #define NFSVNO_EXPORTANON(e) ((e)->nes_exflag & MNT_EXPORTANON) #define NFSVNO_EXSTRICTACCESS(e) ((e)->nes_exflag & MNT_EXSTRICTACCESS) #define NFSVNO_EXV4ONLY(e) ((e)->nes_exflag & MNT_EXV4ONLY) +#define NFSVNO_EXTLS(e) ((e)->nes_exflag & MNT_EXTLS) +#define NFSVNO_EXTLSCERT(e) ((e)->nes_exflag & MNT_EXTLSCERT) +#define NFSVNO_EXTLSCERTUSER(e) ((e)->nes_exflag & MNT_EXTLSCERTUSER) #define NFSVNO_SETEXRDONLY(e) ((e)->nes_exflag = (MNT_EXPORTED|MNT_EXRDONLY)) Modified: head/sys/fs/nfs/nfsport.h ============================================================================== --- head/sys/fs/nfs/nfsport.h Thu Aug 27 22:14:58 2020 (r364895) +++ head/sys/fs/nfs/nfsport.h Thu Aug 27 23:57:30 2020 (r364896) @@ -1055,6 +1055,7 @@ bool ncl_pager_setsize(struct vnode *vp, u_quad_t *nsi #define NFSHASOPENMODE(n) ((n)->nm_state & NFSSTA_OPENMODE) #define NFSHASONEOPENOWN(n) (((n)->nm_flag & NFSMNT_ONEOPENOWN) != 0 && \ (n)->nm_minorvers > 0) +#define NFSHASTLS(n) (((n)->nm_newflag & NFSMNT_TLS) != 0) /* * Set boottime. Modified: head/sys/fs/nfsclient/nfs_clkrpc.c ============================================================================== --- head/sys/fs/nfsclient/nfs_clkrpc.c Thu Aug 27 22:14:58 2020 (r364895) +++ head/sys/fs/nfsclient/nfs_clkrpc.c Thu Aug 27 23:57:30 2020 (r364896) @@ -37,12 +37,14 @@ __FBSDID("$FreeBSD$"); #include "opt_kgssapi.h" +#include "opt_kern_tls.h" #include <fs/nfs/nfsport.h> #include <rpc/rpc.h> -#include <rpc/rpcsec_gss.h> #include <rpc/replay.h> +#include <rpc/rpcsec_gss.h> +#include <rpc/rpcsec_tls.h> NFSDLOCKMUTEX; @@ -67,6 +69,9 @@ nfscb_program(struct svc_req *rqst, SVCXPRT *xprt) { struct nfsrv_descript nd; int cacherep, credflavor; +#ifdef KERN_TLS + u_int maxlen; +#endif memset(&nd, 0, sizeof(nd)); if (rqst->rq_proc != NFSPROC_NULL && @@ -107,6 +112,13 @@ nfscb_program(struct svc_req *rqst, SVCXPRT *xprt) #ifdef MAC mac_cred_associate_nfsd(nd.nd_cred); #endif +#endif +#ifdef KERN_TLS + if ((xprt->xp_tls & RPCTLS_FLAGS_HANDSHAKE) != 0 && + rpctls_getinfo(&maxlen, false, false)) { + nd.nd_flag |= ND_EXTPG; + nd.nd_maxextsiz = maxlen; + } #endif cacherep = nfs_cbproc(&nd, rqst->rq_xid); } else { Modified: head/sys/fs/nfsclient/nfs_clvfsops.c ============================================================================== --- head/sys/fs/nfsclient/nfs_clvfsops.c Thu Aug 27 22:14:58 2020 (r364895) +++ head/sys/fs/nfsclient/nfs_clvfsops.c Thu Aug 27 23:57:30 2020 (r364896) @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include "opt_bootp.h" #include "opt_nfsroot.h" +#include "opt_kern_tls.h" #include <sys/param.h> #include <sys/systm.h> @@ -77,6 +78,8 @@ __FBSDID("$FreeBSD$"); #include <fs/nfsclient/nfs.h> #include <nfs/nfsdiskless.h> +#include <rpc/rpcsec_tls.h> + FEATURE(nfscl, "NFSv4 client"); extern int nfscl_ticks; @@ -117,7 +120,7 @@ static void nfs_decode_args(struct mount *mp, struct n 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); + struct thread *, int, int, int, uint32_t); static void nfs_getnlminfo(struct vnode *, uint8_t *, size_t *, struct sockaddr_storage *, int *, off_t *, struct timeval *); @@ -544,7 +547,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) { + NFS_DEFAULT_NEGNAMETIMEO, 0, 0)) != 0) { printf("nfs_mountroot: mount %s on /: %d\n", path, error); return (error); } @@ -746,7 +749,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", + "pnfs", "wcommitsize", "oneopenown", "tls", NULL }; /* @@ -897,9 +900,11 @@ nfs_mount(struct mount *mp) int dirlen, has_nfs_args_opt, has_nfs_from_opt, krbnamelen, srvkrbnamelen; size_t hstlen; + uint32_t newflag; has_nfs_args_opt = 0; has_nfs_from_opt = 0; + newflag = 0; hst = malloc(MNAMELEN, M_TEMP, M_WAITOK); if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) { error = EINVAL; @@ -983,6 +988,8 @@ nfs_mount(struct mount *mp) args.flags |= NFSMNT_PNFS; if (vfs_getopt(mp->mnt_optnew, "oneopenown", NULL, NULL) == 0) args.flags |= NFSMNT_ONEOPENOWN; + if (vfs_getopt(mp->mnt_optnew, "tls", NULL, NULL) == 0) + newflag |= NFSMNT_TLS; if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) { if (opt == NULL) { vfs_mount_error(mp, "illegal readdirsize"); @@ -1337,7 +1344,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); + nametimeo, negnametimeo, minvers, newflag); out: if (!error) { MNT_ILOCK(mp); @@ -1386,7 +1393,7 @@ mountnfs(struct nfs_args *argp, struct mount *mp, stru 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) + int minvers, uint32_t newflag) { struct nfsmount *nmp; struct nfsnode *np; @@ -1396,6 +1403,9 @@ mountnfs(struct nfs_args *argp, struct mount *mp, stru struct nfsclds *dsp, *tdsp; uint32_t lease; static u_int64_t clval = 0; +#ifdef KERN_TLS + u_int maxlen; +#endif NFSCL_DEBUG(3, "in mnt\n"); clp = NULL; @@ -1405,9 +1415,22 @@ mountnfs(struct nfs_args *argp, struct mount *mp, stru free(nam, M_SONAME); return (0); } else { + /* NFS-over-TLS requires that rpctls be functioning. */ + if ((newflag & NFSMNT_TLS) != 0) { + error = EINVAL; +#ifdef KERN_TLS + if (rpctls_getinfo(&maxlen, true, false)) + error = 0; +#endif + if (error != 0) { + free(nam, M_SONAME); + return (error); + } + } nmp = malloc(sizeof (struct nfsmount) + krbnamelen + dirlen + srvkrbnamelen + 2, M_NEWNFSMNT, M_WAITOK | M_ZERO); + nmp->nm_newflag = newflag; TAILQ_INIT(&nmp->nm_bufq); TAILQ_INIT(&nmp->nm_sess); if (clval == 0) @@ -2011,6 +2034,8 @@ void nfscl_retopts(struct nfsmount *nmp, char *buffer, nfscl_printopt(nmp, nmp->nm_sotype != SOCK_STREAM, ",udp", &buf, &blen); nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_RESVPORT) != 0, ",resvport", &buf, &blen); + nfscl_printopt(nmp, (nmp->nm_newflag & NFSMNT_TLS) != 0, ",tls", &buf, + &blen); nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NOCONN) != 0, ",noconn", &buf, &blen); nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_SOFT) == 0, ",hard", &buf, Modified: head/sys/fs/nfsclient/nfsmount.h ============================================================================== --- head/sys/fs/nfsclient/nfsmount.h Thu Aug 27 22:14:58 2020 (r364895) +++ head/sys/fs/nfsclient/nfsmount.h Thu Aug 27 23:57:30 2020 (r364896) @@ -47,6 +47,7 @@ struct nfsmount { struct nfsmount_common nm_com; /* Common fields for nlm */ uint32_t nm_privflag; /* Private flags */ + uint32_t nm_newflag; /* New mount flags */ int nm_numgrps; /* Max. size of groupslist */ u_char nm_fh[NFSX_FHMAX]; /* File handle of root dir */ int nm_fhsize; /* Size of root file handle */ @@ -113,6 +114,9 @@ struct nfsmount { #define NFSMNTP_NOXATTR 0x00000080 #define NFSMNTP_NOADVISE 0x00000100 #define NFSMNTP_NOALLOCATE 0x00000200 + +/* New mount flags only used by the kernel via nmount(2). */ +#define NFSMNT_TLS 0x00000001 #define NFSMNT_DIRPATH(m) (&((m)->nm_name[(m)->nm_krbnamelen + 1])) #define NFSMNT_SRVKRBNAME(m) \ Modified: head/sys/fs/nfsserver/nfs_nfsdkrpc.c ============================================================================== --- head/sys/fs/nfsserver/nfs_nfsdkrpc.c Thu Aug 27 22:14:58 2020 (r364895) +++ head/sys/fs/nfsserver/nfs_nfsdkrpc.c Thu Aug 27 23:57:30 2020 (r364896) @@ -38,11 +38,13 @@ __FBSDID("$FreeBSD$"); #include "opt_inet6.h" #include "opt_kgssapi.h" +#include "opt_kern_tls.h" #include <fs/nfs/nfsport.h> #include <rpc/rpc.h> #include <rpc/rpcsec_gss.h> +#include <rpc/rpcsec_tls.h> #include <fs/nfsserver/nfs_fha_new.h> @@ -120,6 +122,9 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt) struct nfsrv_descript nd; struct nfsrvcache *rp = NULL; int cacherep, credflavor; +#ifdef KERN_TLS + u_int maxlen; +#endif memset(&nd, 0, sizeof(nd)); if (rqst->rq_vers == NFS_VER2) { @@ -234,6 +239,14 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt) goto out; } + if ((xprt->xp_tls & RPCTLS_FLAGS_HANDSHAKE) != 0) { + nd.nd_flag |= ND_TLS; + if ((xprt->xp_tls & RPCTLS_FLAGS_VERIFIED) != 0) + nd.nd_flag |= ND_TLSCERT; + if ((xprt->xp_tls & RPCTLS_FLAGS_CERTUSER) != 0) + nd.nd_flag |= ND_TLSCERTUSER; + } + nd.nd_maxextsiz = 16384; #ifdef MAC mac_cred_associate_nfsd(nd.nd_cred); #endif @@ -268,6 +281,11 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt) } } +#ifdef KERN_TLS + if ((xprt->xp_tls & RPCTLS_FLAGS_HANDSHAKE) != 0 && + rpctls_getinfo(&maxlen, false, false)) + nd.nd_maxextsiz = maxlen; +#endif cacherep = nfs_proc(&nd, rqst->rq_xid, xprt, &rp); NFSLOCKV4ROOTMUTEX(); nfsv4_relref(&nfsd_suspend_lock); Modified: head/sys/fs/nfsserver/nfs_nfsdport.c ============================================================================== --- head/sys/fs/nfsserver/nfs_nfsdport.c Thu Aug 27 22:14:58 2020 (r364895) +++ head/sys/fs/nfsserver/nfs_nfsdport.c Thu Aug 27 23:57:30 2020 (r364896) @@ -3284,6 +3284,19 @@ nfsd_fhtovp(struct nfsrv_descript *nd, struct nfsrvfh } /* + * If TLS is required by the export, check the flags in nd_flag. + */ + if (nd->nd_repstat == 0 && ((NFSVNO_EXTLS(exp) && + (nd->nd_flag & ND_TLS) == 0) || + (NFSVNO_EXTLSCERT(exp) && + (nd->nd_flag & ND_TLSCERT) == 0) || + (NFSVNO_EXTLSCERTUSER(exp) && + (nd->nd_flag & ND_TLSCERTUSER) == 0))) { + vput(*vpp); + nd->nd_repstat = NFSERR_ACCES; + } + + /* * Personally, I've never seen any point in requiring a * reserved port#, since only in the rare case where the * clients are all boxes with secure system privileges, @@ -3545,6 +3558,15 @@ nfsvno_v4rootexport(struct nfsrv_descript *nd) nd->nd_flag |= ND_EXGSSINTEGRITY; else if (secflavors[i] == RPCSEC_GSS_KRB5P) nd->nd_flag |= ND_EXGSSPRIVACY; + } + + /* And set ND_EXxx flags for TLS. */ + if ((exflags & MNT_EXTLS) != 0) { + nd->nd_flag |= ND_EXTLS; + if ((exflags & MNT_EXTLSCERT) != 0) + nd->nd_flag |= ND_EXTLSCERT; + if ((exflags & MNT_EXTLSCERTUSER) != 0) + nd->nd_flag |= ND_EXTLSCERTUSER; } out: Modified: head/sys/fs/nfsserver/nfs_nfsdserv.c ============================================================================== --- head/sys/fs/nfsserver/nfs_nfsdserv.c Thu Aug 27 22:14:58 2020 (r364895) +++ head/sys/fs/nfsserver/nfs_nfsdserv.c Thu Aug 27 23:57:30 2020 (r364896) @@ -3816,6 +3816,11 @@ nfsrvd_setclientid(struct nfsrv_descript *nd, __unused clp->lc_uid = nd->nd_cred->cr_uid; clp->lc_gid = nd->nd_cred->cr_gid; } + + /* If the client is using TLS, do so for the callback connection. */ + if (nd->nd_flag & ND_TLS) + clp->lc_flags |= LCL_TLSCB; + NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); clp->lc_program = fxdr_unsigned(u_int32_t, *tl); error = nfsrv_getclientipaddr(nd, clp); Modified: head/sys/fs/nfsserver/nfs_nfsdsubs.c ============================================================================== --- head/sys/fs/nfsserver/nfs_nfsdsubs.c Thu Aug 27 22:14:58 2020 (r364895) +++ head/sys/fs/nfsserver/nfs_nfsdsubs.c Thu Aug 27 23:57:30 2020 (r364896) @@ -2114,15 +2114,28 @@ nfsd_checkrootexp(struct nfsrv_descript *nd) { if ((nd->nd_flag & (ND_GSS | ND_EXAUTHSYS)) == ND_EXAUTHSYS) - return (0); + goto checktls; if ((nd->nd_flag & (ND_GSSINTEGRITY | ND_EXGSSINTEGRITY)) == (ND_GSSINTEGRITY | ND_EXGSSINTEGRITY)) - return (0); + goto checktls; if ((nd->nd_flag & (ND_GSSPRIVACY | ND_EXGSSPRIVACY)) == (ND_GSSPRIVACY | ND_EXGSSPRIVACY)) - return (0); + goto checktls; if ((nd->nd_flag & (ND_GSS | ND_GSSINTEGRITY | ND_GSSPRIVACY | ND_EXGSS)) == (ND_GSS | ND_EXGSS)) + goto checktls; + return (1); +checktls: + if ((nd->nd_flag & ND_EXTLS) == 0) + return (0); + if ((nd->nd_flag & (ND_TLSCERTUSER | ND_EXTLSCERTUSER)) == + (ND_TLSCERTUSER | ND_EXTLSCERTUSER)) + return (0); + if ((nd->nd_flag & (ND_TLSCERT | ND_EXTLSCERT | ND_EXTLSCERTUSER)) == + (ND_TLSCERT | ND_EXTLSCERT)) + return (0); + if ((nd->nd_flag & (ND_TLS | ND_EXTLSCERTUSER | ND_EXTLSCERT)) == + ND_TLS) return (0); return (1); }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202008272357.07RNvVRf042127>