diff options
| author | Namjae Jeon <linkinjeon@kernel.org> | 2026-07-05 15:43:29 +0900 |
|---|---|---|
| committer | Steve French <stfrench@microsoft.com> | 2026-07-26 17:44:24 -0500 |
| commit | bc070163574f90f28fc5bd5a00aab0c005211a3f (patch) | |
| tree | f274f2a7181c0e6283fd961b545f8cfd931bbc18 /fs/smb | |
| parent | a9852ec31ac25ccb6d725ee441393f500ebe46fd (diff) | |
| download | linux-next-bc070163574f90f28fc5bd5a00aab0c005211a3f.tar.gz linux-next-bc070163574f90f28fc5bd5a00aab0c005211a3f.zip | |
ksmbd: reject delete-on-close for read-only files
DELETE_ON_CLOSE is currently accepted for files carrying the read-only
DOS attribute. The server consequently creates or opens the file and
marks it for deletion instead of returning STATUS_CANNOT_DELETE.
Reject creation of a new read-only file with DELETE_ON_CLOSE. For an
existing file, load the stored DOS attributes before accepting the
create option. Also reject FileDispositionInformation when the opened
file has the read-only attribute.
Preserve the explicit STATUS_CANNOT_DELETE value while unwinding the
CREATE request.
This fixes smb2.delete-on-close-perms.READONLY.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
Diffstat (limited to 'fs/smb')
| -rw-r--r-- | fs/smb/server/smb2pdu.c | 35 |
1 files changed, 32 insertions, 3 deletions
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index badf48ce5579..9a3b4a759d77 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3540,6 +3540,8 @@ int smb2_open(struct ksmbd_work *work) file_present = true; if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { + struct xattr_dos_attrib da; + /* * If file exists with under flags, return access * denied error. @@ -3553,6 +3555,16 @@ int smb2_open(struct ksmbd_work *work) if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { ksmbd_debug(SMB, "User does not have write permission\n"); + rc = -EACCES; + goto err_out; + } + + if (test_share_config_flag(tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS) && + ksmbd_vfs_get_dos_attrib_xattr(mnt_idmap(path.mnt), + path.dentry, &da) > 0 && + da.attr & FILE_ATTRIBUTE_READONLY) { + rsp->hdr.Status = STATUS_CANNOT_DELETE; rc = -EACCES; goto err_out; } @@ -3570,6 +3582,13 @@ int smb2_open(struct ksmbd_work *work) rc = 0; } + if (!file_present && req->CreateOptions & FILE_DELETE_ON_CLOSE_LE && + req->FileAttributes & FILE_ATTRIBUTE_READONLY_LE) { + rsp->hdr.Status = STATUS_CANNOT_DELETE; + rc = -EACCES; + goto err_out; + } + /* * An explicit ::$DATA suffix names the unnamed data stream and is * canonicalized to a NULL stream name (base file), but the request @@ -4210,7 +4229,8 @@ err_out2: rsp->hdr.Status = STATUS_INVALID_PARAMETER; else if (rc == -EOPNOTSUPP) rsp->hdr.Status = STATUS_NOT_SUPPORTED; - else if (rc == -EACCES || rc == -ESTALE || rc == -EXDEV) { + else if ((rc == -EACCES || rc == -ESTALE || rc == -EXDEV) && + !rsp->hdr.Status) { if (req->DesiredAccess & FILE_ACCESS_SYSTEM_SECURITY_LE) rsp->hdr.Status = STATUS_PRIVILEGE_NOT_HELD; else @@ -6959,6 +6979,9 @@ static int set_file_disposition_info(struct ksmbd_work *work, return -EACCES; } + if (fp->f_ci->m_fattr & FILE_ATTRIBUTE_READONLY_LE) + return -EACCES; + inode = file_inode(fp->filp); if (file_info->DeletePending) { if (ksmbd_has_stream_without_delete_share(fp)) @@ -7229,8 +7252,14 @@ int smb2_set_info(struct ksmbd_work *work) return 0; err_out: - if (rc == -EACCES || rc == -EPERM || rc == -EXDEV) - rsp->hdr.Status = STATUS_ACCESS_DENIED; + if (rc == -EACCES || rc == -EPERM || rc == -EXDEV) { + if (fp && req->InfoType == SMB2_O_INFO_FILE && + req->FileInfoClass == FILE_DISPOSITION_INFORMATION && + fp->f_ci->m_fattr & FILE_ATTRIBUTE_READONLY_LE) + rsp->hdr.Status = STATUS_CANNOT_DELETE; + else + rsp->hdr.Status = STATUS_ACCESS_DENIED; + } else if (rc == -EINVAL) rsp->hdr.Status = STATUS_INVALID_PARAMETER; else if (rc == -EMSGSIZE) |
