From: Scott Mayhew <smayhew@redhat.com>
To: Chuck Lever <chuck.lever@oracle.com>
Cc: jlayton@kernel.org, neil@brown.name, okorniev@redhat.com,
Dai.Ngo@oracle.com, tom@talpey.com, linux-nfs@vger.kernel.org
Subject: Re: [PATCH] nfsd: decouple the xprtsec policy check from check_nfsd_access()
Date: Tue, 5 Aug 2025 10:32:08 -0400 [thread overview]
Message-ID: <aJIV6I5MNYOU1YQC@aion> (raw)
In-Reply-To: <3cd2b16e-d264-48e0-ba20-0c666d88d39c@oracle.com>
On Fri, 01 Aug 2025, Chuck Lever wrote:
> On 7/31/25 5:14 PM, Scott Mayhew wrote:
> > A while back I had reported that an NFSv3 client could successfully
> > mount using '-o xprtsec=none' an export that had been exported with
> > 'xprtsec=tls:mtls'. By "successfully" I mean that the mount command
> > would succeed and the mount would show up in /proc/mounts. Attempting
> > to do anything futher with the mount would be met with NFS3ERR_ACCES.
>
> I agree, though it doesn't compromise access to file data, that's not
> the most desirable behavior.
>
> Can you find your report on lore, and a Link: to it here in the patch
> description?
Will do.
>
>
> > This was fixed (albeit accidentally) by bb4f07f2409c ("nfsd: Fix
> > NFSD_MAY_BYPASS_GSS and NFSD_MAY_BYPASS_GSS_ON_ROOT") and was
> > subsequently re-broken by 0813c5f01249 ("nfsd: fix access checking for
> > NLM under XPRTSEC policies").
> >
> > Transport Layer Security isn't an RPC security flavor or pseudo-flavor,
> > so they shouldn't be conflated when determining whether the access
> > checks can be bypassed.
>
> Fixes: 9280c5774314 ("NFSD: Handle new xprtsec= export option")
>
>
> > Signed-off-by: Scott Mayhew <smayhew@redhat.com>
> > ---
> > fs/nfsd/export.c | 60 ++++++++++++++++++++++++++++++++++++----------
> > fs/nfsd/export.h | 1 +
> > fs/nfsd/nfs4proc.c | 6 ++++-
> > fs/nfsd/nfs4xdr.c | 3 +++
> > fs/nfsd/nfsfh.c | 8 +++++++
> > fs/nfsd/vfs.c | 3 +++
> > 6 files changed, 67 insertions(+), 14 deletions(-)
> >
> > diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
> > index cadfc2bae60e..bc54b01bb516 100644
> > --- a/fs/nfsd/export.c
> > +++ b/fs/nfsd/export.c
> > @@ -1082,19 +1082,27 @@ static struct svc_export *exp_find(struct cache_detail *cd,
> > }
> >
> > /**
> > - * check_nfsd_access - check if access to export is allowed.
> > + * check_xprtsec_policy - check if access to export is permitted by the
> > + * xprtsec policy
> > * @exp: svc_export that is being accessed.
> > * @rqstp: svc_rqst attempting to access @exp (will be NULL for LOCALIO).
> > - * @may_bypass_gss: reduce strictness of authorization check
> > + *
> > + * This logic should not be combined with check_nfsd_access, as the rules
> > + * for bypassing GSS are not the same as for bypassing the xprtsec policy
> > + * check:
> > + * - NFSv3 FSINFO and GETATTR can bypass the GSS for the root dentry,
> > + * but that doesn't mean they can bypass the xprtsec poolicy check
> > + * - NLM can bypass the GSS check on exports exported with the
> > + * NFSEXP_NOAUTHNLM flag
> > + * - NLM can always bypass the xprtsec policy check since TLS isn't
> > + * implemented for the sidecar protocols
> > *
> > * Return values:
> > * %nfs_ok if access is granted, or
> > - * %nfserr_wrongsec if access is denied
> > + * %nfserr_acces or %nfserr_wrongsec if access is denied
> > */
> > -__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp,
> > - bool may_bypass_gss)
> > +__be32 check_xprtsec_policy(struct svc_export *exp, struct svc_rqst *rqstp)
> > {
> > - struct exp_flavor_info *f, *end = exp->ex_flavors + exp->ex_nflavors;
> > struct svc_xprt *xprt;
> >
> > /*
> > @@ -1110,22 +1118,49 @@ __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp,
> >
> > if (exp->ex_xprtsec_modes & NFSEXP_XPRTSEC_NONE) {
> > if (!test_bit(XPT_TLS_SESSION, &xprt->xpt_flags))
> > - goto ok;
> > + return nfs_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;
> > + return nfs_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;
> > + return nfs_ok;
> > }
> > - if (!may_bypass_gss)
> > - goto denied;
> >
> > -ok:
> > + return rqstp->rq_vers < 4 ? nfserr_acces : nfserr_wrongsec;
>
> We recently spilled a lot of electrons trying to get these version
> checks out of the generic security checking paths. For one thing, this
> particular check is valid only for the NFS program.
>
> Returning nfserr_wrongsec unconditionally, as check_nfsd_access now
> does, should be sufficient.
Okay.
>
>
> > +}
> > +
> > +/**
> > + * check_nfsd_access - check if access to export is allowed.
> > + * @exp: svc_export that is being accessed.
> > + * @rqstp: svc_rqst attempting to access @exp (will be NULL for LOCALIO).
> > + * @may_bypass_gss: reduce strictness of authorization check
> > + *
> > + * Return values:
> > + * %nfs_ok if access is granted, or
> > + * %nfserr_wrongsec if access is denied
> > + */
> > +__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp,
> > + bool may_bypass_gss)
> > +{
> > + struct exp_flavor_info *f, *end = exp->ex_flavors + exp->ex_nflavors;
> > + struct svc_xprt *xprt;
> > +
> > + /*
> > + * If rqstp is NULL, this is a LOCALIO request which will only
> > + * ever use a filehandle/credential pair for which access has
> > + * been affirmed (by ACCESS or OPEN NFS requests) over the
> > + * wire. So there is no need for further checks here.
> > + */
> > + if (!rqstp)
> > + return nfs_ok;
>
> Is this true of all of check_nfsd_access's callers, or only of
> __fh_verify ?
>
Looking at the commit where this check was added, and looking at the
other callers, it looks like this is only true of __fh_verify().
I'm splitting up check_nfsd_access() into two helpers has you suggested,
having __fh_verify() call the helpers directly while having the other
callers continue to use check_nfsd_access().
Should I add an argument to the helpers indicate when they have been
called directly? Something like 'bool maybe_localio', which can
then be incorporated into the above check, e.g.
if (!rqstp) {
if (maybe_localio) {
return nfs_ok;
} else {
WARN_ON_ONCE(1);
return nfserr_wrongsec;
}
}
>
> > +
> > + xprt = rqstp->rq_xprt;
> > +
> > /* legacy gss-only clients are always OK: */
> > if (exp->ex_client == rqstp->rq_gssclient)
> > return nfs_ok;
> > @@ -1167,7 +1202,6 @@ __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp,
> > }
> > }
> >
> > -denied:
> > return nfserr_wrongsec;
> > }
> >
> > diff --git a/fs/nfsd/export.h b/fs/nfsd/export.h
> > index b9c0adb3ce09..c5a45f4b72be 100644
> > --- a/fs/nfsd/export.h
> > +++ b/fs/nfsd/export.h
> > @@ -101,6 +101,7 @@ struct svc_expkey {
> >
> > struct svc_cred;
> > int nfsexp_flags(struct svc_cred *cred, struct svc_export *exp);
> > +__be32 check_xprtsec_policy(struct svc_export *exp, struct svc_rqst *rqstp);
> > __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp,
> > bool may_bypass_gss);
> >
> > diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
> > index 71b428efcbb5..71e9a170f7bf 100644
> > --- a/fs/nfsd/nfs4proc.c
> > +++ b/fs/nfsd/nfs4proc.c
> > @@ -2902,8 +2902,12 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
> > clear_current_stateid(cstate);
> >
> > if (current_fh->fh_export &&
> > - need_wrongsec_check(rqstp))
> > + need_wrongsec_check(rqstp)) {
> > + op->status = check_xprtsec_policy(current_fh->fh_export, rqstp);
> > + if (op->status)
> > + goto encode_op;
> > op->status = check_nfsd_access(current_fh->fh_export, rqstp, false);
> > + }
> > }
> > encode_op:
> > if (op->status == nfserr_replay_me) {
> > diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> > index ea91bad4eee2..48d55c13c918 100644
> > --- a/fs/nfsd/nfs4xdr.c
> > +++ b/fs/nfsd/nfs4xdr.c
> > @@ -3859,6 +3859,9 @@ nfsd4_encode_entry4_fattr(struct nfsd4_readdir *cd, const char *name,
> > nfserr = nfserrno(err);
> > goto out_put;
> > }
> > + nfserr = check_xprtsec_policy(exp, cd->rd_rqstp);
> > + if (nfserr)
> > + goto out_put;
> > nfserr = check_nfsd_access(exp, cd->rd_rqstp, false);
> > if (nfserr)
> > goto out_put;
> > diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
> > index 74cf1f4de174..1ffc33662582 100644
> > --- a/fs/nfsd/nfsfh.c
> > +++ b/fs/nfsd/nfsfh.c
> > @@ -364,6 +364,14 @@ __fh_verify(struct svc_rqst *rqstp,
> > if (error)
> > goto out;
> >
> > + if (access & NFSD_MAY_NLM)
> > + /* NLM is allowed to bypass the xprtssec policy check */
> /* because lockd currently does not support xprtsec */> + goto out;
> Every check_xprtsec_policy / check_nfsd_access call site now has two
> function calls, resulting in duplicate code.
>
> Why not leave check_nfsd_access() in place, but replace it's guts with
> two helpers, and then call the two helpers directly here in __fh_verify?
I'll create the two helpers as you suggest.
I'll still need to check the access flags for NFSD_MAY_NLM before
calling the xprtsec helper though (I'll make sure I do it in the right
place this time).
-Scott
>
>
> > +
> > + error = check_xprtsec_policy(exp, rqstp);
> > + if (error)
> > + goto out;
> > +
> > if ((access & NFSD_MAY_NLM) && (exp->ex_flags & NFSEXP_NOAUTHNLM))
> > /* NLM is allowed to fully bypass authentication */
> > goto out;
> > diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
> > index 98ab55ba3ced..1b66aff1d750 100644
> > --- a/fs/nfsd/vfs.c
> > +++ b/fs/nfsd/vfs.c
> > @@ -323,6 +323,9 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
> > err = nfsd_lookup_dentry(rqstp, fhp, name, len, &exp, &dentry);
> > if (err)
> > return err;
> > + err = check_xprtsec_policy(exp, rqstp);
> > + if (err)
> > + goto out;
> > err = check_nfsd_access(exp, rqstp, false);
> > if (err)
> > goto out;
>
>
> --
> Chuck Lever
>
next prev parent reply other threads:[~2025-08-05 14:32 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-07-31 21:14 [PATCH] nfsd: decouple the xprtsec policy check from check_nfsd_access() Scott Mayhew
2025-07-31 21:49 ` Scott Mayhew
2025-08-01 1:53 ` NeilBrown
2025-08-01 10:23 ` kernel test robot
2025-08-01 13:00 ` Jeff Layton
2025-08-01 13:24 ` Chuck Lever
2025-08-05 14:32 ` Scott Mayhew [this message]
2025-08-05 14:36 ` Chuck Lever
2025-08-05 14:51 ` Scott Mayhew
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aJIV6I5MNYOU1YQC@aion \
--to=smayhew@redhat.com \
--cc=Dai.Ngo@oracle.com \
--cc=chuck.lever@oracle.com \
--cc=jlayton@kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=neil@brown.name \
--cc=okorniev@redhat.com \
--cc=tom@talpey.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox