From: VeroFess <coderzeng@gmail.com>
To: cel@kernel.org, jlayton@kernel.org, neil@brown.name,
okorniev@redhat.com, Dai.Ngo@oracle.com, tom@talpey.com
Cc: linux-nfs@vger.kernel.org
Subject: [PATCH v3] nfsd: enforce export security for NFSv4 filehandles and stateids
Date: Thu, 29 Oct 2026 13:14:30 +0800 [thread overview]
Message-ID: <20261029051430.96326-1-coderzeng@gmail.com> (raw)
In-Reply-To: <20261029013648.12015-1-coderzeng@gmail.com>
An AUTH_SYS NFSv4.0 client can read or write a file on a sec=krb5p
export using another client's valid OPEN stateid by inserting
SAVEFH between PUTFH and READ or WRITE.
PUTFH resolves the handle with NFSD_MAY_BYPASS_GSS, expecting
a later security check. SAVEFH carries OP_HANDLES_WRONGSEC even though
it only saves the handle, so PUTFH skips NFS4ERR_WRONGSEC. Stateid I/O
can then reuse a cached nfsd_file.
The original report used these export options:
rw,sync,no_subtree_check,fsid=0,sec=krb5p,secure,root_squash
A Kerberos privacy NFSv4.0 client held an OPEN stateid on that export.
With the same stateid, an AUTH_SYS request was rejected by PUTFH, READ,
but SAVEFH between those operations allowed READ and WRITE. The Kerberos
client subsequently read the changed contents.
SAVEFH leaves CURRENT_FH in place. When deciding whether PUTFH must
report NFS4ERR_WRONGSEC, look past SAVEFH to the first operation that
uses or replaces CURRENT_FH. If another PUTFH replaces it, leave the
earlier PUTFH unchecked, as required by RFC 8881 Section 2.6.3.1.1.
Local COPY and CLONE consume SAVED_FH as the source. Check the
saved export with check_nfsd_access() before using its stateid:
a cached open stateid can otherwise bypass the sec= policy. Map
NFS4ERR_WRONGSEC to NFS4ERR_ACCESS at COPY or CLONE, since neither
operation permits NFS4ERR_WRONGSEC.
An inter-server COPY carries a foreign SAVED_FH. The source server
validates it; the destination must not apply its own export policy
to that handle. The destination PUTFH still checks its local export.
A bind-mounted regular file can have a stricter export than its parent.
LOOKUP crosses into that export, but nfsd_cross_mnt() checks the
parent export. Check the selected export in nfsd_lookup() before
composing a filehandle, and in READDIR before encoding child attributes.
When rdattr_error is not requested, return NFS4ERR_ACCESS for a flavor
mismatch; NFS4ERR_WRONGSEC is not a READDIR status. Keep the child-export
check out of nfsd_cross_mnt(): SECINFO uses it to discover the child's
supported security flavors.
SECINFO and SECINFO_NO_NAME report export security policies. RFC 8881
Section 2.6.3.1.1.5 forbids NFS4ERR_WRONGSEC from either operation.
SECINFO_NO_NAME(PARENT) needs to look up the parent without enforcing
the parent's policy; ordinary LOOKUPP still checks the selected export.
The starting directory's fh_verify(..., NFSD_MAY_EXEC) call also
rejects SECINFO and SECINFO_NO_NAME(PARENT) when that export does not
allow the request's flavor. When SECINFO crosses a mount, it can hit
another check on the source export.
The fsid=0 lookup uses the request's auth domains to find an export;
it does not itself enforce the export's sec= policy.
Pass NFSD_MAY_SECINFO to fh_verify() so it skips the starting export's
flavor and xprtsec policy checks. A SECINFO mount crossing also skips
the source export's policy check. The directory type, secure-port, and
execute-permission checks still apply. Ordinary LOOKUP and LOOKUPP
continue to enforce export security.
The LOOKUP check controls whether a client obtains a new child handle.
It does not cover a handle and OPEN stateid retained while the export's
sec= policy changes. The cached nfsd_file branch of nfs4_check_file()
calls nfsd_permission() without checking the current export's flavor.
Check that export before reusing the file for stateid I/O.
COPY_NOTIFY validates a stateid without acquiring an nfsd_file, so it
misses that check. After an export was changed to sec=krb5p, an
AUTH_SYS COPY_NOTIFY using an existing stateid still issued a copy
stateid although AUTH_SYS READ was denied. In the test, a separate
user-space GSS client used that stateid to read the file. Check the
current export before issuing it and return NFS4ERR_ACCESS on a
flavor mismatch.
Fixes: 68d93184352f ("nfsd4: fix wrongsec handling for PUTFH + op cases")
Fixes: 624322f1adc5 ("NFSD add COPY_NOTIFY operation")
Fixes: f0d6ad5a2f67 ("nfsd: move check_nfsd_access() call into nfsd_cross_mnt()")
Fixes: 29a78a3ed7fc ("nfsd4: make fh_verify responsibility of nfsd_lookup_dentry caller")
Fixes: 04f4ad16b231 ("nfsd4: implement secinfo_no_name")
Assisted-by: LLM
Signed-off-by: VeroFess <coderzeng@gmail.com>
---
Changes since v2:
SECINFO_NO_NAME(PARENT) uses the parent lookup path. The selected-export
check added in v2 made it return NFS4ERR_WRONGSEC when the parent export
rejected the RPC flavor. Use nfsd_lookup_dentry() for this query;
leave nfsd_lookup() and its selected-export check in place for LOOKUPP.
Also allow SECINFO and SECINFO_NO_NAME(PARENT) to query a strict
starting export. SECINFO mount crossing skips the source export's
policy check. Directory traversal permission remains enforced.
Changes since v1:
Check cached stateid I/O and COPY_NOTIFY against the current export.
Check the selected export after LOOKUP and READDIR mount crossing, and
map READDIR's operation-level NFS4ERR_WRONGSEC to NFS4ERR_ACCESS when
rdattr_error was not requested. Restore the RFC comments, drop a
redundant assignment, and add the missing Fixes tags. Regenerate the
diff after v1's whitespace damage.
Tests and review notes:
SECINFO_NO_NAME(PARENT) was tested on v4.1 and v4.2 with a retained
child-directory handle after the parent export's policy changed. It
returned the parent's AUTH_SYS flavor before v2, NFS4ERR_WRONGSEC in
v2, and the AUTH_SYS flavor in v3. With a strict starting export,
AUTH_SYS SECINFO_NO_NAME(PARENT) also returned the parent's flavors.
SECINFO returned the named file's or crossed-mount export's flavors
on v4.0, v4.1 and v4.2. LOOKUP and LOOKUPP still returned
NFS4ERR_WRONGSEC. Without directory execute permission, the queries
returned NFS4ERR_ACCESS.
SECINFO can distinguish existing from nonexistent names while
negotiating security flavors. This is consistent with the protocol's
security negotiation semantics.
The original SAVEFH READ/WRITE tests returned NFS4ERR_WRONGSEC at
PUTFH. SAVEFH alone, a replacement PUTFH and
SECINFO_NO_NAME(CURRENT_FH) remained allowed; RESTOREFH checked the
restored handle.
A regular-file export with stricter security than its parent was
also tested. The v4.0 test used another client's stateid; v4.1 and
v4.2 changed credentials within one session. The fixed LOOKUP
returned NFS4ERR_WRONGSEC on all three versions. GSS LOOKUP,
AUTH_SYS SECINFO, and access to a public sibling still succeeded.
The Linux client uses SECINFO to retry LOOKUP with an allowed flavor
after NFS4ERR_WRONGSEC. Cross-client stateids were not tested on
v4.1 or v4.2.
In earlier v3 tests, READDIR with rdattr_error reported the strict
child's NFS4ERR_WRONGSEC per entry and kept the public entry; with
size but no rdattr_error it returned NFS4ERR_ACCESS. Name-only
READDIR succeeded on v4.0, v4.1 and v4.2. Before the fix, COPY into
the strict child transferred 4096 bytes; the LOOKUP check now stops
that destination from being selected under AUTH_SYS. CLONE reached
ext4 but returned NFS4ERR_NOTSUPP, so no clone completed in that test.
In an intermediate build without the LOOKUP check, nfs4_check_file()
denied stateid READ/WRITE, READ_PLUS, SEEK, ALLOCATE,
DEALLOCATE and COPY. GETATTR, ACCESS and OPEN already returned
NFS4ERR_WRONGSEC. Source review found fh_verify() in SETATTR,
READLINK, VERIFY/NVERIFY and xattrs; COMMIT acquires an nfsd_file.
LINK/RENAME saved-handle probes returned NFS4ERR_WRONGSEC.
Protected saved-source COPY/CLONE returned NFS4ERR_ACCESS; authorized
local COPY/CLONE and a foreign-filehandle inter-server COPY completed.
Local-only COPY with a foreign handle returned NFS4ERR_STALE.
Before the COPY_NOTIFY fix, a policy change left AUTH_SYS READ denied
while COPY_NOTIFY still issued a copy stateid usable by a GSS client.
Stock NFSD-to-NFSD COPY from the strict source returned
NFS4ERR_OFFLOAD_DENIED with zero bytes copied; the permissive-source
control completed. The v2 guard denied a retained-handle COPY_NOTIFY
before issuing a stateid. Source review and repeated denials found no
notification allocation or retained stateid reference on that path.
Earlier W=1 object builds and the revised x86_64 KASAN bzImage
completed. The targeted guest logged no KASAN fault or NFSD warning.
pNFS, other architectures and the full pynfs suite were not tested.
base-commit: bcd97658ca9be5a691eff14065a8f2dcfef6bbaf
fs/nfsd/nfs4proc.c | 80 +++++++++++++++++++++++++++++++++++----------
fs/nfsd/nfs4state.c | 8 ++++-
fs/nfsd/nfs4xdr.c | 8 ++++-
fs/nfsd/nfsfh.c | 15 +++++----
fs/nfsd/trace.h | 3 +-
fs/nfsd/vfs.c | 32 +++++++++++++-----
fs/nfsd/vfs.h | 4 ++-
7 files changed, 114 insertions(+), 36 deletions(-)
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index a50f9dcb3..d0eebd3cd 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1079,7 +1079,31 @@ nfsd4_link(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return status;
}
-static __be32 nfsd4_do_lookupp(struct svc_rqst *rqstp, struct svc_fh *fh)
+/* SECINFO_NO_NAME(PARENT) must be able to report the parent's flavors. */
+static __be32
+nfsd4_secinfo_lookupp(struct svc_rqst *rqstp, struct svc_fh *fh)
+{
+ struct svc_export *exp;
+ struct dentry *dentry;
+ __be32 status;
+
+ status = fh_verify(rqstp, fh, S_IFDIR,
+ NFSD_MAY_EXEC | NFSD_MAY_SECINFO);
+ if (status)
+ return status;
+ status = nfsd_lookup_dentry(rqstp, fh, "..", 2, &exp, &dentry, true);
+ if (status)
+ return status;
+ status = fh_compose(fh, exp, dentry, fh);
+ if (!status && d_really_is_negative(dentry))
+ status = nfserr_noent;
+ dput(dentry);
+ exp_put(exp);
+ return status;
+}
+
+static __be32 nfsd4_do_lookupp(struct svc_rqst *rqstp, struct svc_fh *fh,
+ bool for_secinfo)
{
struct svc_fh tmp_fh;
__be32 ret;
@@ -1093,6 +1117,8 @@ static __be32 nfsd4_do_lookupp(struct svc_rqst *rqstp, struct svc_fh *fh)
return nfserr_noent;
}
fh_put(&tmp_fh);
+ if (for_secinfo)
+ return nfsd4_secinfo_lookupp(rqstp, fh);
return nfsd_lookup(rqstp, fh, "..", 2, fh);
}
@@ -1100,7 +1126,7 @@ static __be32
nfsd4_lookupp(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
union nfsd4_op_u *u)
{
- return nfsd4_do_lookupp(rqstp, &cstate->current_fh);
+ return nfsd4_do_lookupp(rqstp, &cstate->current_fh, false);
}
static __be32
@@ -1252,12 +1278,13 @@ nfsd4_secinfo(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct dentry *dentry;
__be32 err;
- err = fh_verify(rqstp, &cstate->current_fh, S_IFDIR, NFSD_MAY_EXEC);
+ err = fh_verify(rqstp, &cstate->current_fh, S_IFDIR,
+ NFSD_MAY_EXEC | NFSD_MAY_SECINFO);
if (err)
return err;
err = nfsd_lookup_dentry(rqstp, &cstate->current_fh,
secinfo->si_name, secinfo->si_namelen,
- &exp, &dentry);
+ &exp, &dentry, true);
if (err)
return err;
if (d_really_is_negative(dentry)) {
@@ -1282,7 +1309,7 @@ nfsd4_secinfo_no_name(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstat
case NFS4_SECINFO_STYLE4_CURRENT_FH:
break;
case NFS4_SECINFO_STYLE4_PARENT:
- err = nfsd4_do_lookupp(rqstp, &cstate->current_fh);
+ err = nfsd4_do_lookupp(rqstp, &cstate->current_fh, true);
if (err)
return err;
break;
@@ -1491,6 +1518,11 @@ nfsd4_verify_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (!cstate->save_fh.fh_dentry)
return nfserr_nofilehandle;
+ /* A cached open stateid does not authorize the saved source export. */
+ status = check_nfsd_access(cstate->save_fh.fh_export, rqstp);
+ if (status)
+ return nfserr_acces;
+
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->save_fh,
src_stateid, RD_STATE, src, NULL);
if (status)
@@ -2532,6 +2564,12 @@ nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (!stid)
return nfserr_bad_stateid;
+ status = check_nfsd_access(cstate->current_fh.fh_export, rqstp);
+ if (status) {
+ status = nfserr_acces;
+ goto out;
+ }
+
cn->cpn_lease_time.tv_sec = nn->nfsd4_lease;
cn->cpn_lease_time.tv_nsec = 0;
@@ -3237,32 +3275,38 @@ static bool need_wrongsec_check(struct svc_rqst *rqstp)
struct nfsd4_compoundres *resp = rqstp->rq_resp;
struct nfsd4_compoundargs *argp = rqstp->rq_argp;
struct nfsd4_op *this = &argp->ops[resp->opcnt - 1];
- struct nfsd4_op *next = &argp->ops[resp->opcnt];
+ struct nfsd4_op *next;
const struct nfsd4_operation *thisd = OPDESC(this);
const struct nfsd4_operation *nextd;
+ unsigned int i;
- /*
- * Most ops check wronsec on our own; only the putfh-like ops
- * have special rules.
- */
+ /* Only put-filehandle operations need a deferred WRONGSEC check. */
if (!(thisd->op_flags & OP_IS_PUTFH_LIKE))
return false;
+
/*
- * rfc 5661 2.6.3.1.1.6: don't bother erroring out a
- * put-filehandle operation if we're not going to use the
- * result:
+ * RFC 8881 2.6.3.1.1.1: ignore SAVEFH when choosing the operation
+ * that determines whether the put-filehandle operation checks WRONGSEC.
*/
- if (argp->opcnt == resp->opcnt)
+ for (i = resp->opcnt; i < argp->opcnt; i++) {
+ next = &argp->ops[i];
+ if (next->opnum != OP_SAVEFH)
+ break;
+ }
+
+ /* RFC 8881 2.6.3.1.1.6: the filehandle is not used. */
+ if (i == argp->opcnt)
return false;
+
if (next->opnum == OP_ILLEGAL)
return false;
nextd = OPDESC(next);
/*
- * Rest of 2.6.3.1.1: certain operations will return WRONGSEC
- * errors themselves as necessary; others should check for them
- * now:
+ * RFC 8881 2.6.3.1.1.2: a later put-filehandle operation replaces
+ * this one. Other marked operations check WRONGSEC themselves.
*/
- return !(nextd->op_flags & OP_HANDLES_WRONGSEC);
+ return !(nextd->op_flags &
+ (OP_HANDLES_WRONGSEC | OP_IS_PUTFH_LIKE));
}
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index f2b52ba90..3a12aeb56 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -6967,7 +6967,7 @@ nfsd4_verify_deleg_dentry(struct nfsd4_open *open, struct nfs4_file *fp,
err = nfsd_lookup_dentry(open->op_rqstp, parent,
open->op_fname, open->op_fnamelen,
- &exp, &child);
+ &exp, &child, false);
if (err)
return -ESTALE;
@@ -8486,6 +8486,12 @@ nfs4_check_file(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfs4_stid *s,
nf = nfs4_find_file(s, flags);
if (nf) {
+ /* A cached file still needs the current export security check. */
+ status = check_nfsd_access(fhp->fh_export, rqstp);
+ if (status) {
+ nfsd_file_put(nf);
+ return nfserr_acces;
+ }
status = nfsd_permission(&rqstp->rq_cred,
fhp->fh_export, fhp->fh_dentry,
acc | NFSD_MAY_OWNER_OVERRIDE);
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 4d9834446..daf2ad77d 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -4677,6 +4677,9 @@ nfsd4_encode_entry4_fattr(struct nfsd4_readdir *cd, const char *name,
* At least, add a comment here to explain....
*/
nfserr = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp);
+ if (nfserr)
+ goto out_put;
+ nfserr = check_nfsd_access(exp, cd->rd_rqstp);
if (nfserr)
goto out_put;
crossed = true;
@@ -4775,8 +4778,11 @@ nfsd4_encode_entry4(void *ccdv, const char *name, int namlen,
* then in accordance with the spec, we fail the
* entire READDIR operation(!)
*/
- if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR))
+ if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR)) {
+ if (nfserr == nfserr_wrongsec)
+ nfserr = nfserr_acces;
goto fail;
+ }
if (nfsd4_encode_entry4_rdattr_error(xdr, nfserr)) {
nfserr = nfserr_toosmall;
goto fail;
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index f1ba5a14b..2627c344a 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -451,10 +451,11 @@ __fh_verify(struct svc_rqst *rqstp,
goto out;
/*
- * NLM is allowed to bypass the xprtsec policy check because lockd
- * doesn't support xprtsec.
+ * SECINFO queries the export's policy, so neither the RPC flavor nor
+ * the transport need match it. Keep the inode permission check below.
+ * NLM also bypasses xprtsec because lockd does not support it.
*/
- if (!(access & NFSD_MAY_NLM)) {
+ if (!(access & (NFSD_MAY_NLM | NFSD_MAY_SECINFO))) {
error = check_xprtsec_policy(exp, rqstp);
if (error)
goto out;
@@ -471,9 +472,11 @@ __fh_verify(struct svc_rqst *rqstp,
&& exp->ex_path.dentry == dentry)
may_bypass_gss = true;
- error = check_security_flavor(exp, rqstp, may_bypass_gss);
- if (error)
- goto out;
+ if (!(access & NFSD_MAY_SECINFO)) {
+ error = check_security_flavor(exp, rqstp, may_bypass_gss);
+ if (error)
+ goto out;
+ }
svc_xprt_set_valid(rqstp->rq_xprt);
diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h
index 951af645d..a7d9f9d7b 100644
--- a/fs/nfsd/trace.h
+++ b/fs/nfsd/trace.h
@@ -142,7 +142,8 @@ DEFINE_NFSD_DYNTHREAD_EVENT(trylock_fail);
{ NFSD_MAY_BYPASS_GSS, "BYPASS_GSS" }, \
{ NFSD_MAY_READ_IF_EXEC, "READ_IF_EXEC" }, \
{ NFSD_MAY_64BIT_COOKIE, "64BIT_COOKIE" }, \
- { NFSD_MAY_LOCALIO, "LOCALIO" })
+ { NFSD_MAY_LOCALIO, "LOCALIO" }, \
+ { NFSD_MAY_SECINFO, "SECINFO" })
TRACE_EVENT(nfsd_compound,
TP_PROTO(
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index f59a55f89..63ebde0fd 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -124,14 +124,14 @@ nfserrno(int errno)
}
/*
- * Called from nfsd_lookup and encode_dirent. Check if we have crossed
+ * Called from lookup, SECINFO, and encode_dirent. Check if we have crossed
* a mount point.
* Returns an nfs error leaving *dpp and *expp unchanged,
* or nfs_ok having possibly changed *dpp and *expp
*/
-__be32
-nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
- struct svc_export **expp)
+static __be32
+nfsd_cross_mnt_common(struct svc_rqst *rqstp, struct dentry **dpp,
+ struct svc_export **expp, bool for_secinfo)
{
struct svc_export *exp = *expp, *exp2 = NULL;
struct dentry *dentry = *dpp;
@@ -169,7 +169,9 @@ 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 = check_nfsd_access(exp, rqstp);
+ /* SECINFO needs the target's flavors even if the source differs. */
+ if (!for_secinfo)
+ nfserr = check_nfsd_access(exp, rqstp);
if (nfserr == nfs_ok) {
/* successfully crossed mount point */
/*
@@ -193,6 +195,13 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
return nfserrno(err);
}
+__be32
+nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
+ struct svc_export **expp)
+{
+ return nfsd_cross_mnt_common(rqstp, dpp, expp, false);
+}
+
static void follow_to_parent(struct path *path)
{
struct dentry *dp;
@@ -255,7 +264,8 @@ int nfsd_mountpoint(struct dentry *dentry, struct svc_export *exp)
__be32
nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp,
const char *name, unsigned int len,
- struct svc_export **exp_ret, struct dentry **dentry_ret)
+ struct svc_export **exp_ret, struct dentry **dentry_ret,
+ bool for_secinfo)
{
struct svc_export *exp;
struct dentry *dparent;
@@ -288,7 +298,8 @@ nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (IS_ERR(dentry))
goto out_nfserr;
if (nfsd_mountpoint(dentry, exp)) {
- __be32 nfserr = nfsd_cross_mnt(rqstp, &dentry, &exp);
+ __be32 nfserr = nfsd_cross_mnt_common(rqstp,
+ &dentry, &exp, for_secinfo);
if (nfserr) {
dput(dentry);
@@ -337,9 +348,13 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_EXEC);
if (err)
return err;
- err = nfsd_lookup_dentry(rqstp, fhp, name, len, &exp, &dentry);
+ err = nfsd_lookup_dentry(rqstp, fhp, name, len, &exp, &dentry,
+ false);
if (err)
return err;
+ err = check_nfsd_access(exp, rqstp);
+ if (err)
+ goto out;
/*
* Note: we compose the file handle now, but as the
* dentry may be negative, it may need to be updated.
@@ -348,6 +363,7 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
if (!err && d_really_is_negative(dentry))
err = nfserr_noent;
+out:
dput(dentry);
exp_put(exp);
return err;
diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h
index 1f05c83a3..e7724628f 100644
--- a/fs/nfsd/vfs.h
+++ b/fs/nfsd/vfs.h
@@ -33,6 +33,7 @@
#define NFSD_MAY_64BIT_COOKIE 0x1000 /* 64 bit readdir cookies for >= NFSv3 */
#define NFSD_MAY_LOCALIO 0x2000 /* for tracing, reflects when localio used */
+#define NFSD_MAY_SECINFO 0x4000 /* query export security policy */
#define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE)
#define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC)
@@ -133,7 +134,8 @@ __be32 nfsd_lookup(struct svc_rqst *, struct svc_fh *,
const char *, unsigned int, struct svc_fh *);
__be32 nfsd_lookup_dentry(struct svc_rqst *, struct svc_fh *,
const char *, unsigned int,
- struct svc_export **, struct dentry **);
+ struct svc_export **, struct dentry **,
+ bool for_secinfo);
__be32 nfsd_setattr(struct svc_rqst *, struct svc_fh *,
struct nfsd_attrs *, const struct timespec64 *);
int nfsd_mountpoint(struct dentry *, struct svc_export *);
--
2.43.0
next prev parent reply other threads:[~2026-10-29 5:14 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <SY9P300MB153090355AAD017CD7216634A6602@SY9P300MB1530.AUSP300.PROD.OUTLOOK.COM>
[not found] ` <IA1PR12MB662781C8C09627A5CDEBF39DF4602@IA1PR12MB6627.namprd12.prod.outlook.com>
2026-10-27 15:41 ` [PATCH] nfsd: prevent SAVEFH from bypassing export security Chuck Lever
[not found] ` <IA1PR12MB662788BBD70D4064432FD117F4602@IA1PR12MB6627.namprd12.prod.outlook.com>
2026-10-28 14:09 ` Chuck Lever
2026-10-28 13:57 ` Jeff Layton
2026-10-29 1:36 ` [PATCH v2] nfsd: enforce export security for NFSv4 filehandles and stateids VeroFess
2026-10-29 5:14 ` VeroFess [this message]
2026-10-29 6:39 ` [PATCH v4] " VeroFess
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=20261029051430.96326-1-coderzeng@gmail.com \
--to=coderzeng@gmail.com \
--cc=Dai.Ngo@oracle.com \
--cc=cel@kernel.org \
--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 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.