summaryrefslogtreecommitdiff
path: root/fs/smb
diff options
context:
space:
mode:
authorNamjae Jeon <linkinjeon@kernel.org>2026-07-05 15:43:29 +0900
committerSteve French <stfrench@microsoft.com>2026-07-26 17:44:24 -0500
commitbc070163574f90f28fc5bd5a00aab0c005211a3f (patch)
treef274f2a7181c0e6283fd961b545f8cfd931bbc18 /fs/smb
parenta9852ec31ac25ccb6d725ee441393f500ebe46fd (diff)
downloadlinux-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.c35
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)