Re: [PATCH RFC 5/5] NFSD: Handle new xprtsec= export option
From: Jeff Layton <jlayton@kernel.org>
Date: 2023-03-21 15:10:40
On Tue, 2023-03-21 at 14:05 +0000, Chuck Lever III wrote:
quoted
On Mar 21, 2023, at 7:50 AM, Jeff Layton [off-list ref] wrote: On Mon, 2023-03-20 at 10:24 -0400, Chuck Lever wrote:quoted
From: Chuck Lever <chuck.lever@oracle.com> Enable administrators to require clients to use transport layer security when accessing particular exports.quoted
Signed-off-by: Chuck Lever <chuck.lever@oracle.com> --- fs/nfsd/export.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++--- fs/nfsd/export.h | 11 +++++++++++ 2 files changed, 61 insertions(+), 3 deletions(-)diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 668c7527b17e..171ebc21bf07 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c@@ -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,31 @@ static inline intsecinfo_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 > 3) + return -EINVAL;Might want to make a note that the limit of 3 here is arbitrary, and that it might need to be lifted in the future (if/when we grow other xprtsec options).Well I can easily add a symbolic constant for that too. I missed this one in the final clean-up before posting. The bigger question is whether the new downcall parameter is sensible. If there's a nicer way for mountd to get this information to the kernel, I'm open to suggestion.
I don't know of one. Export options seem fine here, since that's how we control all sorts of options in the nfs server.
quoted
quoted
+ + exp->ex_xprtsec_modes = 0; + for (i = 0; i < listsize; i++) { + err = get_uint(mesg, &mode); + if (err) + return err; + mode--; + if (mode > 2) + return -EINVAL; + /* Ad hoc */ + exp->ex_xprtsec_modes |= 1 << mode; + } + return 0; +} + static inline int nfsd_uuid_parse(char **mesg, char *buf, unsigned char **puuid) {@@ -608,6 +632,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 = -EINVAL;@@ -650,6 +675,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@@ -663,6 +690,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@@ -824,6 +852,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)@@ -1035,9 +1064,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;@@ -1062,6 +1108,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; }diff --git a/fs/nfsd/export.h b/fs/nfsd/export.h index d03f7f6a8642..61e1e8383c3d 100644 --- a/fs/nfsd/export.h +++ b/fs/nfsd/export.h@@ -77,8 +77,19 @@ struct svc_export {struct cache_detail *cd; struct rcu_head ex_rcu; struct export_stats ex_stats; + unsigned long ex_xprtsec_modes; }; +enum { + NFSEXP_XPRTSEC_NONE = 0x01, + NFSEXP_XPRTSEC_TLS = 0x02, + NFSEXP_XPRTSEC_MTLS = 0x04, +}; + +#define NFSEXP_XPRTSEC_ALL (NFSEXP_XPRTSEC_NONE | \ + NFSEXP_XPRTSEC_TLS | \ + NFSEXP_XPRTSEC_MTLS) + /* an "export key" (expkey) maps a filehandlefragement to an * svc_export for a given client. There can be several per export, * for the different fsid types.-- Jeff Layton [off-list ref]-- Chuck Lever
-- Jeff Layton [off-list ref]