diff --git a/sys/fs/nfs/nfs_commonkrpc.c b/sys/fs/nfs/nfs_commonkrpc.c index 79c6067c9866..d47d23e30ab3 100644 --- a/sys/fs/nfs/nfs_commonkrpc.c +++ b/sys/fs/nfs/nfs_commonkrpc.c @@ -281,6 +281,8 @@ 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)) + CLNT_CONTROL(client, CLSET_TLS, &one); if (NFSHASSOFT(nmp)) { if (nmp->nm_sotype == SOCK_DGRAM) /* diff --git a/sys/fs/nfs/nfsdport.h b/sys/fs/nfs/nfsdport.h index 0a0a3abef3a3..33b9ba43ba1e 100644 --- a/sys/fs/nfs/nfsdport.h +++ b/sys/fs/nfs/nfsdport.h @@ -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)) diff --git a/sys/fs/nfs/nfsport.h b/sys/fs/nfs/nfsport.h index 912cde95257f..a48736d50a79 100644 --- a/sys/fs/nfs/nfsport.h +++ b/sys/fs/nfs/nfsport.h @@ -1055,6 +1055,7 @@ bool ncl_pager_setsize(struct vnode *vp, u_quad_t *nsizep); #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. diff --git a/sys/fs/nfsclient/nfs_clkrpc.c b/sys/fs/nfsclient/nfs_clkrpc.c index 6bdeaa7f1acb..e52130544b37 100644 --- a/sys/fs/nfsclient/nfs_clkrpc.c +++ b/sys/fs/nfsclient/nfs_clkrpc.c @@ -37,12 +37,14 @@ __FBSDID("$FreeBSD$"); #include "opt_kgssapi.h" +#include "opt_kern_tls.h" #include #include -#include #include +#include +#include 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 { diff --git a/sys/fs/nfsclient/nfs_clvfsops.c b/sys/fs/nfsclient/nfs_clvfsops.c index 3a12af68bfe1..c7e5c2a1c3fa 100644 --- a/sys/fs/nfsclient/nfs_clvfsops.c +++ b/sys/fs/nfsclient/nfs_clvfsops.c @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include "opt_bootp.h" #include "opt_nfsroot.h" +#include "opt_kern_tls.h" #include #include @@ -77,6 +78,8 @@ __FBSDID("$FreeBSD$"); #include #include +#include + FEATURE(nfscl, "NFSv4 client"); extern int nfscl_ticks; @@ -117,7 +120,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); + 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, 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) + int minvers, uint32_t newflag) { struct nfsmount *nmp; struct nfsnode *np; @@ -1396,6 +1403,9 @@ mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam, 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, struct sockaddr *nam, 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, size_t buflen) 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, diff --git a/sys/fs/nfsclient/nfsmount.h b/sys/fs/nfsclient/nfsmount.h index 3b6312fbc87f..063926eceaf5 100644 --- a/sys/fs/nfsclient/nfsmount.h +++ b/sys/fs/nfsclient/nfsmount.h @@ -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 */ @@ -114,6 +115,9 @@ struct nfsmount { #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) \ (&((m)->nm_name[(m)->nm_krbnamelen + (m)->nm_dirpathlen + 2])) diff --git a/sys/fs/nfsserver/nfs_nfsdkrpc.c b/sys/fs/nfsserver/nfs_nfsdkrpc.c index d53d631d3cca..0fa88c6db44a 100644 --- a/sys/fs/nfsserver/nfs_nfsdkrpc.c +++ b/sys/fs/nfsserver/nfs_nfsdkrpc.c @@ -38,11 +38,13 @@ __FBSDID("$FreeBSD$"); #include "opt_inet6.h" #include "opt_kgssapi.h" +#include "opt_kern_tls.h" #include #include #include +#include #include @@ -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); diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c index 179860364a21..07035f283486 100644 --- a/sys/fs/nfsserver/nfs_nfsdport.c +++ b/sys/fs/nfsserver/nfs_nfsdport.c @@ -3283,6 +3283,19 @@ nfsd_fhtovp(struct nfsrv_descript *nd, struct nfsrvfh *nfp, int lktype, nd->nd_repstat = EACCES; } + /* + * 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 @@ -3547,6 +3560,15 @@ nfsvno_v4rootexport(struct nfsrv_descript *nd) 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: NFSEXITCODE(error); return (error); diff --git a/sys/fs/nfsserver/nfs_nfsdserv.c b/sys/fs/nfsserver/nfs_nfsdserv.c index d2fe9e60a0ea..7b9ed0ddd802 100644 --- a/sys/fs/nfsserver/nfs_nfsdserv.c +++ b/sys/fs/nfsserver/nfs_nfsdserv.c @@ -3816,6 +3816,11 @@ nfsrvd_setclientid(struct nfsrv_descript *nd, __unused int isdgram, 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); diff --git a/sys/fs/nfsserver/nfs_nfsdsubs.c b/sys/fs/nfsserver/nfs_nfsdsubs.c index 428751372121..3b3adc3c4977 100644 --- a/sys/fs/nfsserver/nfs_nfsdsubs.c +++ b/sys/fs/nfsserver/nfs_nfsdsubs.c @@ -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); }