summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNamjae Jeon <linkinjeon@kernel.org>2026-07-05 15:32:06 +0900
committerNamjae Jeon <linkinjeon@kernel.org>2026-08-17 15:00:29 +0900
commit5d47ebb2795d0dab7bf718ae6665ffdaa7bb880c (patch)
tree58c0ed4e36a1b772f52be42c1a61238bccc2b11b
parente5f42cb7577221080e4db0498d71e6db7e67f1a5 (diff)
downloadlinux-5d47ebb2795d0dab7bf718ae6665ffdaa7bb880c.tar.gz
linux-5d47ebb2795d0dab7bf718ae6665ffdaa7bb880c.zip
ksmbd: honor owner rights ACEs in maximal access
The SMB2 create maximal-access context is currently calculated from POSIX mode bits when the client does not request MAXIMUM_ALLOWED. This overwrites the access granted by a stored Windows DACL. Calculate the create-context result with the DACL permission checker. Recognize the S-1-3-4 Owner Rights SID as applying to the object owner and process its allow and deny ACEs in ACL order. When an Owner Rights ACE is present, do not add the owner implicit READ_CONTROL and WRITE_DAC rights. The Owner Rights ACE replaces those implicit grants as required by Windows access-check semantics. Without an Owner Rights ACE, preserve the existing implicit owner grants, including FILE_READ_ATTRIBUTES and DELETE. This fixes smb2.acls.OWNER-RIGHTS and its deny variants without regressing smb2.acls.GENERIC. Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
-rw-r--r--fs/smb/server/smb2pdu.c8
-rw-r--r--fs/smb/server/smbacl.c21
2 files changed, 26 insertions, 3 deletions
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index e79531e120a3..155a0b93ed58 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -3627,6 +3627,14 @@ int smb2_open(struct ksmbd_work *work)
sess->user->uid, false);
if (rc)
goto err_out;
+
+ if (maximal_access_ctxt) {
+ maximal_access = FILE_MAXIMAL_ACCESS_LE;
+ rc = smb_check_perm_dacl(conn, &path, &maximal_access,
+ 0, sess->user->uid, false);
+ if (rc)
+ goto err_out;
+ }
}
if (daccess & FILE_MAXIMAL_ACCESS_LE) {
diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
index 88bf7b97042f..b5db6dcfbaa4 100644
--- a/fs/smb/server/smbacl.c
+++ b/fs/smb/server/smbacl.c
@@ -27,6 +27,9 @@ static const struct smb_sid creator_owner = {
/* security id for everyone/world system group */
static const struct smb_sid creator_group = {
1, 1, {0, 0, 0, 0, 0, 3}, {cpu_to_le32(1)} };
+/* security id for owner rights */
+static const struct smb_sid sid_owner_rights = {
+ 1, 1, {0, 0, 0, 0, 0, 3}, {cpu_to_le32(4)} };
/* security id for everyone/world system group */
static const struct smb_sid sid_everyone = {
@@ -1452,6 +1455,8 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
struct posix_acl_entry *pa_entry;
unsigned int sid_type = SIDOWNER;
unsigned short ace_size;
+ bool is_owner, owner_rights = false;
+ vfsuid_t vfsuid;
ksmbd_debug(SMB, "check permission using windows acl\n");
pntsd_size = ksmbd_vfs_get_sd_xattr(conn, idmap,
@@ -1484,10 +1489,10 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
if (!uid)
sid_type = SIDUNIX_USER;
id_to_sid(uid, sid_type, &sid);
+ vfsuid = i_uid_into_vfsuid(idmap, d_inode(path->dentry));
+ is_owner = uid == from_kuid(&init_user_ns, vfsuid_into_kuid(vfsuid));
if (*pdaccess & FILE_MAXIMAL_ACCESS_LE) {
- access_bits = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES |
- DELETE;
ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
aces_size = acl_size - sizeof(struct smb_acl);
for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) {
@@ -1507,11 +1512,18 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
sizeof(__le32) * ace->sid.num_subauth)
break;
+ if (!compare_sids(&sid_owner_rights, &ace->sid)) {
+ owner_rights = true;
+ if (!is_owner)
+ goto next_ace;
+ }
+
if (ace->flags & INHERIT_ONLY_ACE ||
(compare_sids(&sid, &ace->sid) &&
compare_sids(&sid_unix_NFS_mode, &ace->sid) &&
compare_sids(&sid_everyone, &ace->sid) &&
- compare_sids(&sid_authusers, &ace->sid)))
+ compare_sids(&sid_authusers, &ace->sid) &&
+ compare_sids(&sid_owner_rights, &ace->sid)))
goto next_ace;
switch (ace->type) {
@@ -1527,6 +1539,9 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
next_ace:
ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size));
}
+ if (is_owner && !owner_rights)
+ access_bits |= READ_CONTROL | WRITE_DAC |
+ FILE_READ_ATTRIBUTES | DELETE;
access_bits &= ~denied;
if ((raw_daccess & FILE_GENERIC_EXECUTE_LE) &&
S_ISREG(d_inode(path->dentry)->i_mode) &&