diff options
| author | Namjae Jeon <linkinjeon@kernel.org> | 2026-07-05 15:32:06 +0900 |
|---|---|---|
| committer | Namjae Jeon <linkinjeon@kernel.org> | 2026-08-17 15:00:29 +0900 |
| commit | 5d47ebb2795d0dab7bf718ae6665ffdaa7bb880c (patch) | |
| tree | 58c0ed4e36a1b772f52be42c1a61238bccc2b11b | |
| parent | e5f42cb7577221080e4db0498d71e6db7e67f1a5 (diff) | |
| download | linux-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.c | 8 | ||||
| -rw-r--r-- | fs/smb/server/smbacl.c | 21 |
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) && |
