nfsd: Use an NFSv4 ACL for the delegation ACE if available

Without this patch, the ACE in a NFSv4 delegation reply is
generated from the file's user mode bits.  This is correct
in most situations, but not if the file has certain NFSv4 ACLs.

This patch uses the @OWNER ACE in the NFSv4 ACL if it comes
before any deny ACE and returns a "nil access" ACE if a
deny preceeds the @OWNER.

This change affects few NFSv4 clients, since most clients
ignore the delegation access ACE and it only affects cases
where the NFSv4 server is issuing delegations.

Fixes:	8e2a90ac80 ("nfscommon: Factor out conversion of ae_perm to NFSv4 ACE flags")
This commit is contained in:
Rick Macklem 2025-06-29 09:09:23 -07:00
parent afa70490ee
commit 0d51adee30

View file

@ -64,6 +64,7 @@ extern u_long sb_max_adj;
extern int nfsrv_pnfsatime;
extern int nfsrv_maxpnfsmirror;
extern uint32_t nfs_srvmaxio;
extern int nfsrv_issuedelegs;
static int nfs_async = 0;
SYSCTL_DECL(_vfs_nfsd);
@ -2866,6 +2867,8 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
NFSACL_T *aclp = NULL;
struct thread *p = curthread;
bool done_namei;
__enum_uint8_decl(wdelegace) { USENONE, USEMODE, USENFSV4ACL }
delegace;
#ifdef NFS4_ACL_EXTATTR_NAME
aclp = acl_alloc(M_WAITOK);
@ -2873,6 +2876,7 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
#endif
NFSZERO_ATTRBIT(&attrbits);
done_namei = false;
delegace = USEMODE;
named.ni_cnd.cn_nameiop = 0;
NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
i = fxdr_unsigned(int, *(tl + 5));
@ -3214,6 +3218,25 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
if (!nd->nd_repstat)
nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
if (nd->nd_repstat == 0 && aclp != NULL && nfsrv_issuedelegs != 0 &&
(dp->v_mount->mnt_flag & MNT_NFS4ACLS) != 0) {
if (aclp->acl_cnt == 0 && create == NFSV4OPEN_NOCREATE) {
int retacl;
/* We do not yet have an ACL, so try and get one. */
retacl = VOP_GETACL(vp, ACL_TYPE_NFS4, aclp,
nd->nd_cred, p);
if (retacl != 0 && retacl != ENOATTR &&
retacl != EOPNOTSUPP && retacl != EINVAL)
delegace = USENONE;
else if (retacl == 0 && aclp->acl_cnt > 0)
delegace = USENFSV4ACL;
} else if (aclp->acl_cnt > 0 && create == NFSV4OPEN_CREATE) {
delegace = USENFSV4ACL;
}
}
/*
* Do the open locking/delegation stuff.
*/
@ -3306,18 +3329,56 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
*tl++ = txdr_unsigned(NFSV4OPEN_LIMITSIZE);
txdr_hyper(nva.na_size, tl);
}
NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV4ACE_ALLOWEDTYPE);
*tl++ = txdr_unsigned(0x0);
acemask = NFSV4ACE_ALLFILESMASK;
if (nva.na_mode & S_IRUSR)
acemask |= NFSV4ACE_READMASK;
if (nva.na_mode & S_IWUSR)
acemask |= NFSV4ACE_WRITEMASK;
if (nva.na_mode & S_IXUSR)
acemask |= NFSV4ACE_EXECUTEMASK;
*tl = txdr_unsigned(acemask);
(void) nfsm_strtom(nd, "OWNER@", 6);
/* Set up the write delegation ACE. */
NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
if (delegace == USENFSV4ACL) {
int j;
for (j = 0; j < aclp->acl_cnt; j++) {
if (aclp->acl_entry[j].ae_tag ==
ACL_USER_OBJ ||
aclp->acl_entry[j].ae_entry_type !=
ACL_ENTRY_TYPE_ALLOW)
break;
}
if (j < aclp->acl_cnt &&
aclp->acl_entry[j].ae_tag ==
ACL_USER_OBJ &&
aclp->acl_entry[j].ae_entry_type ==
ACL_ENTRY_TYPE_ALLOW) {
/* Use this ACE. */
*tl++ = txdr_unsigned(
NFSV4ACE_ALLOWEDTYPE);
*tl++ = txdr_unsigned(0x0);
*tl = txdr_unsigned(
nfs_aceperm(
aclp->acl_entry[j].ae_perm));
(void)nfsm_strtom(nd, "OWNER@", 6);
} else
delegace = USENONE;
}
if (delegace == USENONE) {
/* Don't allow anything. */
*tl++ = 0x0;
*tl++ = 0x0;
*tl = 0x0;
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
*tl = 0;
} else if (delegace == USEMODE) {
/* Build from mode. */
*tl++ = txdr_unsigned(NFSV4ACE_ALLOWEDTYPE);
*tl++ = txdr_unsigned(0x0);
acemask = NFSV4ACE_ALLFILESMASK;
if (nva.na_mode & S_IRUSR)
acemask |= NFSV4ACE_READMASK;
if (nva.na_mode & S_IWUSR)
acemask |= NFSV4ACE_WRITEMASK;
if (nva.na_mode & S_IXUSR)
acemask |= NFSV4ACE_EXECUTEMASK;
*tl = txdr_unsigned(acemask);
(void)nfsm_strtom(nd, "OWNER@", 6);
}
}
*vpp = vp;
} else if (vp) {