From: VeroFess <coderzeng@gmail.com>
To: linux-nfs@vger.kernel.org
Cc: cel@kernel.org, jlayton@kernel.org
Subject: [PATCH 1/3] NFSD: Check the secure-port policy when entering an export
Date: Tue, 3 Nov 2026 09:32:35 +0800 [thread overview]
Message-ID: <20261103013237.1174538-2-coderzeng@gmail.com> (raw)
In-Reply-To: <20261103013237.1174538-1-coderzeng@gmail.com>
LOOKUP checks the parent filehandle before crossing a mountpoint.
The crossing checks the entered export's flavor and transport policy,
but not its secure-port setting. An AUTH_SYS request from an
unreserved port can therefore enter a secure export by LOOKUP,
although PUTFH of the same filehandle returns NFS4ERR_PERM. The
requester must already hold an OPEN stateid for the file for the
demonstrated READ and CLONE paths; the reproducer obtains it from a
reserved port before sending the requests from an unreserved port.
Check the port whenever check_nfsd_access() revalidates an export.
Use the same port helper as fh_verify(), including SECINFO's flavor
bypass path. Map a port denial to NFS4ERR_ACCESS in SECINFO and
SECINFO_NO_NAME, as required for a denied name lookup.
The omission is old; it dates back at least to Linux 2.6.12.
Maintained LTS branches with this crossing path also need the check.
Signed-off-by: VeroFess <coderzeng@gmail.com>
---
The combined fix tree was tested in an x86-64 KASAN guest. High-port
AUTH_SYS LOOKUP returned NFS4ERR_PERM, named SECINFO returned
NFS4ERR_ACCESS, and the CLONE destination was unchanged. Low-port COPY
and CLONE and high-port GSS privacy access succeeded. This patch also
built alone with W=1.
fs/nfsd/export.c | 8 ++++++--
fs/nfsd/nfs4proc.c | 4 +++-
fs/nfsd/nfsfh.c | 26 ++++++++++++++++++++++----
fs/nfsd/nfsfh.h | 2 ++
fs/nfsd/vfs.c | 21 +++++++++++++++------
5 files changed, 48 insertions(+), 13 deletions(-)
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index f0dd9fb..3bdc6b8 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -2077,13 +2077,17 @@ __be32 check_security_flavor(struct svc_export *exp, struct svc_rqst *rqstp,
* @rqstp: svc_rqst attempting to access @exp.
*
* Return values:
- * %nfs_ok if access is granted, or
- * %nfserr_wrongsec if access is denied
+ * %nfs_ok if access is granted, %nfserr_perm if the
+ * originating port is not allowed, or %nfserr_wrongsec
+ * if the transport or flavor is not allowed.
*/
__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp)
{
__be32 status;
+ status = nfsd_check_port(rqstp, &rqstp->rq_cred, exp);
+ if (status != nfs_ok)
+ return status;
status = check_xprtsec_policy(exp, rqstp);
if (status != nfs_ok)
return status;
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 2c65302..43c2aa9 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1086,7 +1086,9 @@ nfsd4_link(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
*/
static __be32 nfsd4_secinfo_status(__be32 status)
{
- return status == nfserr_wrongsec ? nfserr_acces : status;
+ if (status == nfserr_wrongsec || status == nfserr_perm)
+ return nfserr_acces;
+ return status;
}
static __be32 nfsd4_do_lookupp(struct svc_rqst *rqstp, struct svc_fh *fh,
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index 0939146..4b5280e 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -102,11 +102,17 @@ static bool nfsd_originating_port_ok(struct svc_rqst *rqstp,
return test_bit(RQ_SECURE, &rqstp->rq_flags);
}
-static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
- struct svc_cred *cred,
- struct svc_export *exp)
+/**
+ * nfsd_check_port - check an export's secure-port requirement
+ * @rqstp: request being checked, or %NULL for a local caller
+ * @cred: credentials used for export option checks
+ * @exp: export being checked
+ *
+ * Return: %nfs_ok if allowed, %nfserr_perm otherwise.
+ */
+__be32 nfsd_check_port(struct svc_rqst *rqstp,
+ struct svc_cred *cred, struct svc_export *exp)
{
- /* Check if the request originated from a secure port. */
if (rqstp && !nfsd_originating_port_ok(rqstp, cred, exp)) {
if (IS_ENABLED(CONFIG_SUNRPC_DEBUG)) {
char buf[RPC_MAX_ADDRBUFLEN];
@@ -116,6 +122,18 @@ static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
}
return nfserr_perm;
}
+ return nfs_ok;
+}
+
+static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
+ struct svc_cred *cred,
+ struct svc_export *exp)
+{
+ __be32 status;
+
+ status = nfsd_check_port(rqstp, cred, exp);
+ if (status)
+ return status;
/* Set user creds for this exportpoint */
return nfserrno(nfsd_setuser(cred, exp));
diff --git a/fs/nfsd/nfsfh.h b/fs/nfsd/nfsfh.h
index aa3ff30..90b6429 100644
--- a/fs/nfsd/nfsfh.h
+++ b/fs/nfsd/nfsfh.h
@@ -226,6 +226,8 @@ extern char * SVCFH_fmt(struct svc_fh *fhp);
/*
* Function prototypes
*/
+__be32 nfsd_check_port(struct svc_rqst *rqstp,
+ struct svc_cred *cred, struct svc_export *exp);
__be32 fh_verify(struct svc_rqst *, struct svc_fh *, umode_t, int);
__be32 fh_verify_local(struct net *, struct svc_cred *, struct auth_domain *,
struct svc_fh *, umode_t, int);
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 269dc49..2775911 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -123,6 +123,19 @@ nfserrno(int errno)
return nfserr_io;
}
+static __be32 nfsd_check_crossing_access(struct svc_rqst *rqstp,
+ struct svc_export *exp, int access)
+{
+ __be32 status;
+
+ if (!(access & NFSD_MAY_BYPASS_FLAVOR))
+ return check_nfsd_access(exp, rqstp);
+ status = nfsd_check_port(rqstp, &rqstp->rq_cred, exp);
+ if (status)
+ return status;
+ return check_xprtsec_policy(exp, rqstp);
+}
+
/*
* Check if we have crossed a mount point.
* Returns an nfs error leaving *dpp and *expp unchanged,
@@ -168,9 +181,7 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
err = 0;
} else if (nfsd_v4client(rqstp) ||
(exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2)) {
- nfserr = (access & NFSD_MAY_BYPASS_FLAVOR) ?
- check_xprtsec_policy(exp2, rqstp) :
- check_nfsd_access(exp2, rqstp);
+ nfserr = nfsd_check_crossing_access(rqstp, exp2, access);
if (nfserr == nfs_ok) {
/* successfully crossed mount point */
/*
@@ -222,9 +233,7 @@ static __be32 nfsd_lookup_parent(struct svc_rqst *rqstp, struct dentry *dparent,
} else if (IS_ERR(exp2)) {
nfserr = nfserrno(PTR_ERR(exp2));
} else {
- nfserr = (access & NFSD_MAY_BYPASS_FLAVOR) ?
- check_xprtsec_policy(exp2, rqstp) :
- check_nfsd_access(exp2, rqstp);
+ nfserr = nfsd_check_crossing_access(rqstp, exp2, access);
if (nfserr != nfs_ok) {
exp_put(exp2);
} else {
--
2.43.0
next prev parent reply other threads:[~2026-11-03 1:32 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-03 1:32 [PATCH 0/3] NFSD: Apply all export checks when a compound changes exports VeroFess
2026-11-03 1:32 ` VeroFess [this message]
2026-11-03 1:32 ` [PATCH 2/3] NFSD: Apply the entered export's credentials after LOOKUP and RESTOREFH VeroFess
2026-11-03 1:32 ` [PATCH 3/3] NFSD: Verify COPY and CLONE source and destination filehandles VeroFess
2026-11-03 14:06 ` [PATCH 0/3] NFSD: Apply all export checks when a compound changes exports Chuck Lever
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=20261103013237.1174538-2-coderzeng@gmail.com \
--to=coderzeng@gmail.com \
--cc=cel@kernel.org \
--cc=jlayton@kernel.org \
--cc=linux-nfs@vger.kernel.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.