fs/smb/client/cifspdu.h | 1 + fs/smb/client/smb2file.c | 1 - fs/smb/client/smb2glob.h | 1 + fs/smb/client/smb2inode.c | 71 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 72 insertions(+), 2 deletions(-)
Linux SMB client currently is not able to access files for which do not have FILE_READ_ATTRIBUTES permission. For example it is not able to write data into file on SMB server to which has only write access (no read or read attributes access). And applications are not able to get result of stat() syscall on such file. Test case against Windows SMB server: 1) On SMB server prepare file with only GENERIC_WRITE access for Everyone: ACL:S-1-1-0:ALLOWED/0x0/0x40000000 2) On SMB server remove all access for file's parent directory 3) Mount share by Linux SMB client and try to append data to that file: echo test >> /mnt/share/dir/file 4) Try to call: stat /mnt/share/dir/file Without this change the write test fails because Linux SMB client is trying to open SMB path "\dir\file" with GENERIC_WRITE|FILE_READ_ATTRIBUTES. With this change the test pass as Linux SMB client is not opening file with FILE_READ_ATTRIBUTES access anymore. Similarly without this change the stat test always fails as Linux SMB client is trying to read attributes via SMB2_OP_QUERY_INFO. With this change, if SMB2_OP_QUERY_INFO fails then Linux SMB client fallbacks for reading stat attributes via OPEN with MAXIMUM_ALLOWED access (which will pass if there is some permission) and OPEN reply will contain attributes required for stat(). Pali Rohár (2): cifs: Do not issue SMB2 CREATE always with FILE_READ_ATTRIBUTES cifs: Improve stat() to work also without FILE_READ_ATTRIBUTES fs/smb/client/cifspdu.h | 1 + fs/smb/client/smb2file.c | 1 - fs/smb/client/smb2glob.h | 1 + fs/smb/client/smb2inode.c | 71 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 72 insertions(+), 2 deletions(-) -- 2.20.1
Linux SMB client currently is not able to access files for which do not have FILE_READ_ATTRIBUTES permission. For example it is not able to write data into file on SMB server to which has only write access (no read or read attributes access). And applications are not able to get result of stat() syscall on such file. Test case against Windows SMB server: 1) On SMB server prepare file with only GENERIC_WRITE access for Everyone: ACL:S-1-1-0:ALLOWED/0x0/0x40000000 2) On SMB server remove all access for file's parent directory 3) Mount share by Linux SMB client and try to append data to that file: echo test >> /mnt/share/dir/file 4) Try to call: stat /mnt/share/dir/file Without this change the write test fails because Linux SMB client is trying to open SMB path "\dir\file" with GENERIC_WRITE|FILE_READ_ATTRIBUTES. With this change the test pass as Linux SMB client is not opening file with FILE_READ_ATTRIBUTES access anymore. Similarly without this change the stat test always fails as Linux SMB client is trying to read attributes via SMB2_OP_QUERY_INFO. With this change, if SMB2_OP_QUERY_INFO fails then Linux SMB client fallbacks for reading stat attributes via OPEN with MAXIMUM_ALLOWED access (which will pass if there is some permission) and OPEN reply will contain attributes required for stat(). Changes in v2: * At first attempt still try to open with FILE_READ_ATTRIBUTES and fallback to open without FILE_READ_ATTRIBUTES only on -EACCESS. Pali Rohár (2): cifs: Add fallback for SMB2 CREATE without FILE_READ_ATTRIBUTES cifs: Improve stat() to work also without FILE_READ_ATTRIBUTES fs/smb/client/cifspdu.h | 1 + fs/smb/client/smb2file.c | 12 ++++++- fs/smb/client/smb2glob.h | 1 + fs/smb/client/smb2inode.c | 71 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 83 insertions(+), 2 deletions(-) -- 2.20.1
Some operations, like WRITE, does not require FILE_READ_ATTRIBUTES access.
So when FILE_READ_ATTRIBUTES is not explicitly requested for
smb2_open_file() then first try to do SMB2 CREATE with FILE_READ_ATTRIBUTES
access (like it was before) and then fallback to SMB2 CREATE without
FILE_READ_ATTRIBUTES access (less common case).
This change allows to complete WRITE operation to a file when it does not
grant FILE_READ_ATTRIBUTES permission and its parent directory does not
grant READ_DATA permission (parent directory READ_DATA is implicit grant of
child FILE_READ_ATTRIBUTES permission).
Signed-off-by: Pali Rohár <pali@kernel.org>
---
fs/smb/client/smb2file.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c
index e4ae8d2d5bd0..ff255fdc4532 100644
--- a/fs/smb/client/smb2file.c
+++ b/fs/smb/client/smb2file.c
@@ -152,16 +152,25 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32
int err_buftype = CIFS_NO_BUFFER;
struct cifs_fid *fid = oparms->fid;
struct network_resiliency_req nr_ioctl_req;
+ bool retry_without_read_attributes = false;
smb2_path = cifs_convert_path_to_utf16(oparms->path, oparms->cifs_sb);
if (smb2_path == NULL)
return -ENOMEM;
- oparms->desired_access |= FILE_READ_ATTRIBUTES;
+ if (!(oparms->desired_access & FILE_READ_ATTRIBUTES)) {
+ oparms->desired_access |= FILE_READ_ATTRIBUTES;
+ retry_without_read_attributes = true;
+ }
smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH;
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov,
&err_buftype);
+ if (rc == -EACCES && retry_without_read_attributes) {
+ oparms->desired_access &= ~FILE_READ_ATTRIBUTES;
+ rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov,
+ &err_buftype);
+ }
if (rc && data) {
struct smb2_hdr *hdr = err_iov.iov_base;
--
2.20.1
If SMB2_OP_QUERY_INFO (called when POSIX extensions are not used) failed
with STATUS_ACCESS_DENIED then it means that caller does not have
permission to open the path with FILE_READ_ATTRIBUTES access and therefore
cannot issue SMB2_OP_QUERY_INFO command.
This will result in the -EACCES error from stat() sycall.
There is an alternative way how to query limited information about path but
still suitable for stat() syscall. SMB2 OPEN/CREATE operation returns in
its successful response subset of query information.
So try to open the path without FILE_READ_ATTRIBUTES but with
MAXIMUM_ALLOWED access which will grant the maximum possible access to the
file and the response will contain required query information for stat()
syscall.
This will improve smb2_query_path_info() to query also files which do not
grant FILE_READ_ATTRIBUTES access to caller.
Signed-off-by: Pali Rohár <pali@kernel.org>
---
fs/smb/client/cifspdu.h | 1 +
fs/smb/client/smb2file.c | 3 +-
fs/smb/client/smb2glob.h | 1 +
fs/smb/client/smb2inode.c | 71 ++++++++++++++++++++++++++++++++++++++-
4 files changed, 74 insertions(+), 2 deletions(-)
diff --git a/fs/smb/client/cifspdu.h b/fs/smb/client/cifspdu.h
index ee78bb6741d6..d0e7fbc5cacd 100644
--- a/fs/smb/client/cifspdu.h
+++ b/fs/smb/client/cifspdu.h
@@ -217,6 +217,7 @@
/* of an input/output request */
#define SYSTEM_SECURITY 0x01000000 /* The system access control list */
/* can be read and changed */
+#define MAXIMUM_ALLOWED 0x02000000
#define GENERIC_ALL 0x10000000
#define GENERIC_EXECUTE 0x20000000
#define GENERIC_WRITE 0x40000000
diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c
index ff255fdc4532..1476cb824ae4 100644
--- a/fs/smb/client/smb2file.c
+++ b/fs/smb/client/smb2file.c
@@ -158,7 +158,8 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32
if (smb2_path == NULL)
return -ENOMEM;
- if (!(oparms->desired_access & FILE_READ_ATTRIBUTES)) {
+ if (!(oparms->desired_access & FILE_READ_ATTRIBUTES) &&
+ !(oparms->desired_access & MAXIMUM_ALLOWED)) {
oparms->desired_access |= FILE_READ_ATTRIBUTES;
retry_without_read_attributes = true;
}
diff --git a/fs/smb/client/smb2glob.h b/fs/smb/client/smb2glob.h
index 2466e6155136..224495322a05 100644
--- a/fs/smb/client/smb2glob.h
+++ b/fs/smb/client/smb2glob.h
@@ -38,6 +38,7 @@ enum smb2_compound_ops {
SMB2_OP_SET_REPARSE,
SMB2_OP_GET_REPARSE,
SMB2_OP_QUERY_WSL_EA,
+ SMB2_OP_OPEN_QUERY,
};
/* Used when constructing chained read requests. */
diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
index e8bb3f8b53f1..379ac3cbad1f 100644
--- a/fs/smb/client/smb2inode.c
+++ b/fs/smb/client/smb2inode.c
@@ -188,6 +188,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
struct TCP_Server_Info *server;
int num_rqst = 0, i;
int resp_buftype[MAX_COMPOUND];
+ struct smb2_create_rsp *create_rsp = NULL;
struct smb2_query_info_rsp *qi_rsp = NULL;
struct cifs_open_info_data *idata;
struct inode *inode = NULL;
@@ -265,7 +266,13 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
num_rqst++;
rc = 0;
- for (i = 0; i < num_cmds; i++) {
+ i = 0;
+
+ /* Skip the leading explicit OPEN operation */
+ if (num_cmds > 0 && cmds[0] == SMB2_OP_OPEN_QUERY)
+ i++;
+
+ for (; i < num_cmds; i++) {
/* Operation */
switch (cmds[i]) {
case SMB2_OP_QUERY_INFO:
@@ -637,6 +644,26 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
tcon->need_reconnect = true;
}
+ if (rc == 0 && num_cmds > 0 && cmds[0] == SMB2_OP_OPEN_QUERY) {
+ create_rsp = rsp_iov[0].iov_base;
+ idata = in_iov[0].iov_base;
+ idata->fi.CreationTime = create_rsp->CreationTime;
+ idata->fi.LastAccessTime = create_rsp->LastAccessTime;
+ idata->fi.LastWriteTime = create_rsp->LastWriteTime;
+ idata->fi.ChangeTime = create_rsp->ChangeTime;
+ idata->fi.Attributes = create_rsp->FileAttributes;
+ idata->fi.AllocationSize = create_rsp->AllocationSize;
+ idata->fi.EndOfFile = create_rsp->EndofFile;
+ if (le32_to_cpu(idata->fi.NumberOfLinks) == 0)
+ idata->fi.NumberOfLinks = cpu_to_le32(1); /* dummy value */
+ idata->fi.DeletePending = 0;
+ idata->fi.Directory = !!(le32_to_cpu(create_rsp->FileAttributes) & ATTR_DIRECTORY);
+
+ /* smb2_parse_contexts() fills idata->fi.IndexNumber */
+ rc = smb2_parse_contexts(server, &rsp_iov[0], &oparms->fid->epoch,
+ oparms->fid->lease_key, &oplock, &idata->fi, NULL);
+ }
+
for (i = 0; i < num_cmds; i++) {
switch (cmds[i]) {
case SMB2_OP_QUERY_INFO:
@@ -934,6 +961,48 @@ int smb2_query_path_info(const unsigned int xid,
case 0:
rc = parse_create_response(data, cifs_sb, full_path, &out_iov[0]);
break;
+ case -EACCES:
+ /*
+ * If SMB2_OP_QUERY_INFO (called when POSIX extensions are not used) failed with
+ * STATUS_ACCESS_DENIED then it means that caller does not have permission to
+ * open the path with FILE_READ_ATTRIBUTES access and therefore cannot issue
+ * SMB2_OP_QUERY_INFO command.
+ *
+ * There is an alternative way how to query limited information about path but still
+ * suitable for stat() syscall. SMB2 OPEN/CREATE operation returns in its successful
+ * response subset of query information.
+ *
+ * So try to open the path without FILE_READ_ATTRIBUTES but with MAXIMUM_ALLOWED
+ * access which will grant the maximum possible access to the file and the response
+ * will contain required query information for stat() syscall.
+ */
+
+ if (tcon->posix_extensions)
+ break;
+
+ for (i = 0; i < ARRAY_SIZE(out_buftype); i++) {
+ free_rsp_buf(out_buftype[i], out_iov[i].iov_base);
+ out_buftype[i] = 0;
+ out_iov[i].iov_base = NULL;
+ }
+
+ num_cmds = 1;
+ cmds[0] = SMB2_OP_OPEN_QUERY;
+ in_iov[0].iov_base = data;
+ in_iov[0].iov_len = sizeof(*data);
+ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, MAXIMUM_ALLOWED,
+ FILE_OPEN, create_options, ACL_NO_MODE);
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
+ &oparms, in_iov, cmds, num_cmds,
+ cfile, out_iov, out_buftype, NULL);
+
+ hdr = out_iov[0].iov_base;
+ if (!hdr || out_buftype[0] == CIFS_NO_BUFFER)
+ goto out;
+
+ if (!rc)
+ rc = parse_create_response(data, cifs_sb, full_path, &out_iov[0]);
+ break;
case -EOPNOTSUPP:
/*
* BB TODO: When support for special files added to Samba
--
2.20.1
The obvious question to check is whether this would lead to any issues if desired_access is not passed in in oparms in any cases (ie if it ends up 0), and also that this would not hurt any cases where we want to keep the handle cached (deferred close) but don't have sufficient permission for it to be usable by the subsequent operation (e.g. revalidate or stat) On Sat, Oct 5, 2024 at 11:10 AM Pali Rohár <pali@kernel.org> wrote: > > Linux SMB client currently is not able to access files for which do not > have FILE_READ_ATTRIBUTES permission. > > For example it is not able to write data into file on SMB server to > which has only write access (no read or read attributes access). And > applications are not able to get result of stat() syscall on such file. > > Test case against Windows SMB server: > > 1) On SMB server prepare file with only GENERIC_WRITE access for Everyone: > ACL:S-1-1-0:ALLOWED/0x0/0x40000000 > > 2) On SMB server remove all access for file's parent directory > > 3) Mount share by Linux SMB client and try to append data to that file: > echo test >> /mnt/share/dir/file > > 4) Try to call: stat /mnt/share/dir/file > > Without this change the write test fails because Linux SMB client is trying > to open SMB path "\dir\file" with GENERIC_WRITE|FILE_READ_ATTRIBUTES. With > this change the test pass as Linux SMB client is not opening file with > FILE_READ_ATTRIBUTES access anymore. > > Similarly without this change the stat test always fails as Linux SMB > client is trying to read attributes via SMB2_OP_QUERY_INFO. With this > change, if SMB2_OP_QUERY_INFO fails then Linux SMB client fallbacks for > reading stat attributes via OPEN with MAXIMUM_ALLOWED access (which will > pass if there is some permission) and OPEN reply will contain attributes > required for stat(). > > Pali Rohár (2): > cifs: Do not issue SMB2 CREATE always with FILE_READ_ATTRIBUTES > cifs: Improve stat() to work also without FILE_READ_ATTRIBUTES > > fs/smb/client/cifspdu.h | 1 + > fs/smb/client/smb2file.c | 1 - > fs/smb/client/smb2glob.h | 1 + > fs/smb/client/smb2inode.c | 71 ++++++++++++++++++++++++++++++++++++++- > 4 files changed, 72 insertions(+), 2 deletions(-) > > -- > 2.20.1 > > -- Thanks, Steve
On Saturday 05 October 2024 13:32:12 Steve French wrote: > The obvious question to check is whether this would lead to any issues > if desired_access is not passed in in oparms in any cases (ie if it > ends up 0), This is good point. IIRC if zero value is in OPEN/CREATE desired_access request then SMB server returns STATUS_ACCESS_DENIED. So it needs to be checked that desired_access is filled in all usage correctly. > and also that this would not hurt any cases where we want > to keep the handle cached (deferred close) but don't have sufficient > permission for it to be usable by the subsequent operation (e.g. > revalidate or stat) I see, so the code needs to be properly checked or tested that all these conditions are handled. > On Sat, Oct 5, 2024 at 11:10 AM Pali Rohár <pali@kernel.org> wrote: > > > > Linux SMB client currently is not able to access files for which do not > > have FILE_READ_ATTRIBUTES permission. > > > > For example it is not able to write data into file on SMB server to > > which has only write access (no read or read attributes access). And > > applications are not able to get result of stat() syscall on such file. > > > > Test case against Windows SMB server: > > > > 1) On SMB server prepare file with only GENERIC_WRITE access for Everyone: > > ACL:S-1-1-0:ALLOWED/0x0/0x40000000 > > > > 2) On SMB server remove all access for file's parent directory > > > > 3) Mount share by Linux SMB client and try to append data to that file: > > echo test >> /mnt/share/dir/file > > > > 4) Try to call: stat /mnt/share/dir/file > > > > Without this change the write test fails because Linux SMB client is trying > > to open SMB path "\dir\file" with GENERIC_WRITE|FILE_READ_ATTRIBUTES. With > > this change the test pass as Linux SMB client is not opening file with > > FILE_READ_ATTRIBUTES access anymore. > > > > Similarly without this change the stat test always fails as Linux SMB > > client is trying to read attributes via SMB2_OP_QUERY_INFO. With this > > change, if SMB2_OP_QUERY_INFO fails then Linux SMB client fallbacks for > > reading stat attributes via OPEN with MAXIMUM_ALLOWED access (which will > > pass if there is some permission) and OPEN reply will contain attributes > > required for stat(). > > > > Pali Rohár (2): > > cifs: Do not issue SMB2 CREATE always with FILE_READ_ATTRIBUTES > > cifs: Improve stat() to work also without FILE_READ_ATTRIBUTES > > > > fs/smb/client/cifspdu.h | 1 + > > fs/smb/client/smb2file.c | 1 - > > fs/smb/client/smb2glob.h | 1 + > > fs/smb/client/smb2inode.c | 71 ++++++++++++++++++++++++++++++++++++++- > > 4 files changed, 72 insertions(+), 2 deletions(-) > > > > -- > > 2.20.1 > > > > > > > -- > Thanks, > > Steve
On Saturday 05 October 2024 20:44:53 Pali Rohár wrote: > On Saturday 05 October 2024 13:32:12 Steve French wrote: > > The obvious question to check is whether this would lead to any issues > > if desired_access is not passed in in oparms in any cases (ie if it > > ends up 0), > > This is good point. IIRC if zero value is in OPEN/CREATE desired_access > request then SMB server returns STATUS_ACCESS_DENIED. > > So it needs to be checked that desired_access is filled in all usage > correctly. I have done checks and seems that all callers put some non-zero desired access to smb2_open_file() call. So I think this should not be an issue. > > and also that this would not hurt any cases where we want > > to keep the handle cached (deferred close) but don't have sufficient > > permission for it to be usable by the subsequent operation (e.g. > > revalidate or stat) > > I see, so the code needs to be properly checked or tested that all these > conditions are handled. It looks like that when rdwr_for_fscache is used then proper read/write desired access is asked. During testing I have not spotted issues. Also to note, I checked SMB1 code and it already does not automatically add FILE_READ_ATTRIBUTES to desired access during open. Could you schedule this change for some testing? > > On Sat, Oct 5, 2024 at 11:10 AM Pali Rohár <pali@kernel.org> wrote: > > > > > > Linux SMB client currently is not able to access files for which do not > > > have FILE_READ_ATTRIBUTES permission. > > > > > > For example it is not able to write data into file on SMB server to > > > which has only write access (no read or read attributes access). And > > > applications are not able to get result of stat() syscall on such file. > > > > > > Test case against Windows SMB server: > > > > > > 1) On SMB server prepare file with only GENERIC_WRITE access for Everyone: > > > ACL:S-1-1-0:ALLOWED/0x0/0x40000000 > > > > > > 2) On SMB server remove all access for file's parent directory > > > > > > 3) Mount share by Linux SMB client and try to append data to that file: > > > echo test >> /mnt/share/dir/file > > > > > > 4) Try to call: stat /mnt/share/dir/file > > > > > > Without this change the write test fails because Linux SMB client is trying > > > to open SMB path "\dir\file" with GENERIC_WRITE|FILE_READ_ATTRIBUTES. With > > > this change the test pass as Linux SMB client is not opening file with > > > FILE_READ_ATTRIBUTES access anymore. > > > > > > Similarly without this change the stat test always fails as Linux SMB > > > client is trying to read attributes via SMB2_OP_QUERY_INFO. With this > > > change, if SMB2_OP_QUERY_INFO fails then Linux SMB client fallbacks for > > > reading stat attributes via OPEN with MAXIMUM_ALLOWED access (which will > > > pass if there is some permission) and OPEN reply will contain attributes > > > required for stat(). > > > > > > Pali Rohár (2): > > > cifs: Do not issue SMB2 CREATE always with FILE_READ_ATTRIBUTES > > > cifs: Improve stat() to work also without FILE_READ_ATTRIBUTES > > > > > > fs/smb/client/cifspdu.h | 1 + > > > fs/smb/client/smb2file.c | 1 - > > > fs/smb/client/smb2glob.h | 1 + > > > fs/smb/client/smb2inode.c | 71 ++++++++++++++++++++++++++++++++++++++- > > > 4 files changed, 72 insertions(+), 2 deletions(-) > > > > > > -- > > > 2.20.1 > > > > > > > > > > > > -- > > Thanks, > > > > Steve
© 2016 - 2026 Red Hat, Inc.