NFSD: Handle new xprtsec= export option
authorChuck Lever <chuck.lever@oracle.com>
Thu, 20 Apr 2023 17:56:31 +0000 (13:56 -0400)
committerChuck Lever <chuck.lever@oracle.com>
Thu, 27 Apr 2023 22:49:24 +0000 (18:49 -0400)
Enable administrators to require clients to use transport layer
security when accessing particular exports.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
fs/nfsd/export.c
fs/nfsd/export.h
include/uapi/linux/nfsd/export.h

index 6da74aebe1fb045789f2bf686bf4cecaa2a4b251..ae85257b423849ed937e093737c242f6d43d682f 100644 (file)
@@ -439,7 +439,6 @@ static int check_export(struct path *path, int *flags, unsigned char *uuid)
                return -EINVAL;
        }
        return 0;
-
 }
 
 #ifdef CONFIG_NFSD_V4
@@ -546,6 +545,29 @@ static inline int
 secinfo_parse(char **mesg, char *buf, struct svc_export *exp) { return 0; }
 #endif
 
+static int xprtsec_parse(char **mesg, char *buf, struct svc_export *exp)
+{
+       unsigned int i, mode, listsize;
+       int err;
+
+       err = get_uint(mesg, &listsize);
+       if (err)
+               return err;
+       if (listsize > NFSEXP_XPRTSEC_NUM)
+               return -EINVAL;
+
+       exp->ex_xprtsec_modes = 0;
+       for (i = 0; i < listsize; i++) {
+               err = get_uint(mesg, &mode);
+               if (err)
+                       return err;
+               if (mode > NFSEXP_XPRTSEC_MTLS)
+                       return -EINVAL;
+               exp->ex_xprtsec_modes |= mode;
+       }
+       return 0;
+}
+
 static inline int
 nfsd_uuid_parse(char **mesg, char *buf, unsigned char **puuid)
 {
@@ -608,6 +630,7 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
        exp.ex_client = dom;
        exp.cd = cd;
        exp.ex_devid_map = NULL;
+       exp.ex_xprtsec_modes = NFSEXP_XPRTSEC_ALL;
 
        /* expiry */
        err = get_expiry(&mesg, &exp.h.expiry_time);
@@ -649,6 +672,8 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
                                err = nfsd_uuid_parse(&mesg, buf, &exp.ex_uuid);
                        else if (strcmp(buf, "secinfo") == 0)
                                err = secinfo_parse(&mesg, buf, &exp);
+                       else if (strcmp(buf, "xprtsec") == 0)
+                               err = xprtsec_parse(&mesg, buf, &exp);
                        else
                                /* quietly ignore unknown words and anything
                                 * following. Newer user-space can try to set
@@ -662,6 +687,7 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
                err = check_export(&exp.ex_path, &exp.ex_flags, exp.ex_uuid);
                if (err)
                        goto out4;
+
                /*
                 * No point caching this if it would immediately expire.
                 * Also, this protects exportfs's dummy export from the
@@ -823,6 +849,7 @@ static void export_update(struct cache_head *cnew, struct cache_head *citem)
        for (i = 0; i < MAX_SECINFO_LIST; i++) {
                new->ex_flavors[i] = item->ex_flavors[i];
        }
+       new->ex_xprtsec_modes = item->ex_xprtsec_modes;
 }
 
 static struct cache_head *svc_export_alloc(void)
@@ -1034,9 +1061,26 @@ static struct svc_export *exp_find(struct cache_detail *cd,
 
 __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp)
 {
-       struct exp_flavor_info *f;
-       struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors;
+       struct exp_flavor_info *f, *end = exp->ex_flavors + exp->ex_nflavors;
+       struct svc_xprt *xprt = rqstp->rq_xprt;
+
+       if (exp->ex_xprtsec_modes & NFSEXP_XPRTSEC_NONE) {
+               if (!test_bit(XPT_TLS_SESSION, &xprt->xpt_flags))
+                       goto ok;
+       }
+       if (exp->ex_xprtsec_modes & NFSEXP_XPRTSEC_TLS) {
+               if (test_bit(XPT_TLS_SESSION, &xprt->xpt_flags) &&
+                   !test_bit(XPT_PEER_AUTH, &xprt->xpt_flags))
+                       goto ok;
+       }
+       if (exp->ex_xprtsec_modes & NFSEXP_XPRTSEC_MTLS) {
+               if (test_bit(XPT_TLS_SESSION, &xprt->xpt_flags) &&
+                   test_bit(XPT_PEER_AUTH, &xprt->xpt_flags))
+                       goto ok;
+       }
+       goto denied;
 
+ok:
        /* legacy gss-only clients are always OK: */
        if (exp->ex_client == rqstp->rq_gssclient)
                return 0;
@@ -1061,6 +1105,7 @@ __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp)
        if (nfsd4_spo_must_allow(rqstp))
                return 0;
 
+denied:
        return rqstp->rq_vers < 4 ? nfserr_acces : nfserr_wrongsec;
 }
 
index d03f7f6a8642d354f03d33306b2344996f57375f..2df8ae25aad30285a1ad27111f09cfeafbde5da2 100644 (file)
@@ -77,6 +77,7 @@ struct svc_export {
        struct cache_detail     *cd;
        struct rcu_head         ex_rcu;
        struct export_stats     ex_stats;
+       unsigned long           ex_xprtsec_modes;
 };
 
 /* an "export key" (expkey) maps a filehandlefragement to an
index 2124ba904779d3d4c713eb3a743bc8eb621eee94..a73ca3703abbc2e1d759fc7729fd0545ebd0cc44 100644 (file)
                                        | NFSEXP_ALLSQUASH \
                                        | NFSEXP_INSECURE_PORT)
 
+/*
+ * Transport layer security policies that are permitted to access
+ * an export
+ */
+#define NFSEXP_XPRTSEC_NONE    0x0001
+#define NFSEXP_XPRTSEC_TLS     0x0002
+#define NFSEXP_XPRTSEC_MTLS    0x0004
+
+#define NFSEXP_XPRTSEC_NUM     (3)
+
+#define NFSEXP_XPRTSEC_ALL     (NFSEXP_XPRTSEC_NONE | \
+                                NFSEXP_XPRTSEC_TLS | \
+                                NFSEXP_XPRTSEC_MTLS)
 
 #endif /* _UAPINFSD_EXPORT_H */