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);
 }
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to