[PATCH] Version 2 - Fix smbd behavior with SMB2-FLUSH on a directory handle.
Ralph Böhme
slow at samba.org
Fri May 11 07:17:47 UTC 2018
Hi Jeremy,
On Thu, May 10, 2018 at 04:16:42PM -0700, Jeremy Allison wrote:
> Update. After a query from Steve, I've updated
> the test code to check the behavior of Windows
> with a handle on the root directory of a share and a handle
> on a sub-directory within a share.
>
> Turns out they both behave the same, so
> the server-side patch hasn't changed,
> just the test code coverage.
>
> SMB2-FLUSH should succeed on a handle opened
> with FILE_ADD_FILE|FILE_ADD_SUBDIRECTORY
> access.
>
> BUG: https://bugzilla.samba.org/show_bug.cgi?id=13428
>
> Please review and push if happy !
oh, good catch. How did you find this?
Is the access check below really correct? See below.
> if (!CHECK_WRITE(fsp)) {
> - tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
> - return tevent_req_post(req, ev);
> + if (!fsp->is_directory) {
> + tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
> + return tevent_req_post(req, ev);
> + }
> +
> + /*
> + * Directories are not writable in the conventional
> + * sense, but if opened with
> + * FILE_ADD_FILE|FILE_ADD_SUBDIRECTORY
> + * they can be flushed.
> + */
> + if ((fsp->access_mask &
> + (FILE_ADD_FILE|FILE_ADD_SUBDIRECTORY))==0) {
> + tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
> + return tevent_req_post(req, ev);
> + }
if I understand you right, the client must have *both* FILE_ADD_FILE *and*
FILE_ADD_SUBDIRECTORY, so the check should be:
> + if ((fsp->access_mask &
> + (FILE_ADD_FILE|FILE_ADD_SUBDIRECTORY))==(FILE_ADD_FILE|FILE_ADD_SUBDIRECTORY)) {
> + tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
> + return tevent_req_post(req, ev);
> + }
Either way, you'd make me a happy man if you could rewrite the if condition to
something that resembles my attached FIXUP. Thanks! :)
-slow
--
Ralph Boehme, Samba Team https://samba.org/
Samba Developer, SerNet GmbH https://sernet.de/en/samba/
GPG Key Fingerprint: FAE2 C608 8A24 2520 51C5
59E4 AA1E 9B71 2639 9E46
-------------- next part --------------
From bb0f1c706c0911a0f7edb614cc68506843d90816 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Thu, 10 May 2018 10:26:52 -0700
Subject: [PATCH 1/3] s3: smbd: Fix SMB2-FLUSH against directories.
Directories opened with FILE_ADD_FILE|FILE_ADD_SUBDIRECTORY
can be flushed even if they're not writable in the conventional
sense.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=13428
Signed-off-by: Jeremy Allison <jra at samba.org>
---
source3/smbd/smb2_flush.c | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/source3/smbd/smb2_flush.c b/source3/smbd/smb2_flush.c
index d1ab3a09839..f32d9abebc3 100644
--- a/source3/smbd/smb2_flush.c
+++ b/source3/smbd/smb2_flush.c
@@ -23,6 +23,7 @@
#include "smbd/globals.h"
#include "../libcli/smb/smb_common.h"
#include "../lib/util/tevent_ntstatus.h"
+#include "libcli/security/security.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_SMB2
@@ -147,8 +148,22 @@ static struct tevent_req *smbd_smb2_flush_send(TALLOC_CTX *mem_ctx,
}
if (!CHECK_WRITE(fsp)) {
- tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
- return tevent_req_post(req, ev);
+ if (!fsp->is_directory) {
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * Directories are not writable in the conventional
+ * sense, but if opened with
+ * FILE_ADD_FILE|FILE_ADD_SUBDIRECTORY
+ * they can be flushed.
+ */
+ if ((fsp->access_mask &
+ (FILE_ADD_FILE|FILE_ADD_SUBDIRECTORY))==0) {
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return tevent_req_post(req, ev);
+ }
}
if (fsp->fh->fd == -1) {
--
2.13.6
From d3026d35f7541230fc17e9f96e6582f2ba413673 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 11 May 2018 08:56:25 +0200
Subject: [PATCH 2/3] FIXUP: s3: smbd: Fix SMB2-FLUSH against directories
---
source3/smbd/smb2_flush.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/source3/smbd/smb2_flush.c b/source3/smbd/smb2_flush.c
index f32d9abebc3..7e70b7b6c35 100644
--- a/source3/smbd/smb2_flush.c
+++ b/source3/smbd/smb2_flush.c
@@ -148,6 +148,9 @@ static struct tevent_req *smbd_smb2_flush_send(TALLOC_CTX *mem_ctx,
}
if (!CHECK_WRITE(fsp)) {
+ bool flush_ok;
+ int flush_access_mask = FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY;
+
if (!fsp->is_directory) {
tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
return tevent_req_post(req, ev);
@@ -159,8 +162,9 @@ static struct tevent_req *smbd_smb2_flush_send(TALLOC_CTX *mem_ctx,
* FILE_ADD_FILE|FILE_ADD_SUBDIRECTORY
* they can be flushed.
*/
- if ((fsp->access_mask &
- (FILE_ADD_FILE|FILE_ADD_SUBDIRECTORY))==0) {
+ flush_ok = (fsp->access_mask & flush_access_mask)
+ == flush_access_mask;
+ if (!flush_ok) {
tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
return tevent_req_post(req, ev);
}
--
2.13.6
From a70fa12a14afb0eee273decf5c84b8fdc34d51a3 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Thu, 10 May 2018 11:30:24 -0700
Subject: [PATCH 3/3] s3: smbtorture: Add new SMB2-DIR-FSYNC test to show
behavior of FSYNC on directories.
Tests against a directory handle on the root of a share,
and a directory handle on a sub-directory in a share.
Passes against Windows.
Regression test for:
BUG: https://bugzilla.samba.org/show_bug.cgi?id=13428
Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
selftest/knownfail | 1 +
source3/selftest/tests.py | 2 +-
source3/torture/proto.h | 1 +
source3/torture/test_smb2.c | 223 ++++++++++++++++++++++++++++++++++++++++++++
source3/torture/torture.c | 1 +
5 files changed, 227 insertions(+), 1 deletion(-)
diff --git a/selftest/knownfail b/selftest/knownfail
index a2aeed2690d..8c70d6a6172 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -8,6 +8,7 @@
.*driver.add_driver_timestamps # we only can store dates, not timestamps
^samba3.smbtorture_s3.crypt_server\(nt4_dc\).SMB2-SESSION-REAUTH # expected to give ACCESS_DENIED SMB2.1 doesn't have encryption
^samba3.smbtorture_s3.crypt_server\(nt4_dc\).SMB2-SESSION-RECONNECT # expected to give CONNECTION_DISCONNECTED, we need to fix the test
+^samba3.smbtorture_s3.*ad_dc_ntvfs.*SMB2-DIR-FSYNC.*
^samba3.smb2.session enc.reconnect # expected to give CONNECTION_DISCONNECTED, we need to fix the test
^samba3.raw.session enc # expected to give ACCESS_DENIED as SMB1 encryption isn't used
^samba3.smbtorture_s3.crypt_server # expected to give ACCESS_DENIED as SMB1 encryption isn't used
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index c234679b1cd..790144aa24f 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -78,7 +78,7 @@ tests = ["FDPASS", "LOCK1", "LOCK2", "LOCK3", "LOCK4", "LOCK5", "LOCK6", "LOCK7"
"UID-REGRESSION-TEST", "SHORTNAME-TEST",
"CASE-INSENSITIVE-CREATE", "SMB2-BASIC", "NTTRANS-FSCTL", "SMB2-NEGPROT",
"SMB2-SESSION-REAUTH", "SMB2-SESSION-RECONNECT", "SMB2-FTRUNCATE",
- "SMB2-ANONYMOUS",
+ "SMB2-ANONYMOUS", "SMB2-DIR-FSYNC",
"CLEANUP1",
"CLEANUP2",
"CLEANUP4",
diff --git a/source3/torture/proto.h b/source3/torture/proto.h
index 45870c904fb..1634da49315 100644
--- a/source3/torture/proto.h
+++ b/source3/torture/proto.h
@@ -101,6 +101,7 @@ bool run_smb2_tcon_dependence(int dummy);
bool run_smb2_multi_channel(int dummy);
bool run_smb2_session_reauth(int dummy);
bool run_smb2_ftruncate(int dummy);
+bool run_smb2_dir_fsync(int dummy);
bool run_chain3(int dummy);
bool run_local_conv_auth_info(int dummy);
bool run_local_sprintf_append(int dummy);
diff --git a/source3/torture/test_smb2.c b/source3/torture/test_smb2.c
index 897d034f6a9..3b1b951c005 100644
--- a/source3/torture/test_smb2.c
+++ b/source3/torture/test_smb2.c
@@ -2065,3 +2065,226 @@ bool run_smb2_ftruncate(int dummy)
}
return correct;
}
+
+/* Ensure SMB2 flush on directories behaves correctly. */
+
+static bool test_dir_fsync(struct cli_state *cli, const char *path)
+{
+ NTSTATUS status;
+ uint64_t fid_persistent, fid_volatile;
+ uint8_t *dir_data = NULL;
+ uint32_t dir_data_length = 0;
+
+ /* Open directory - no write abilities. */
+ status = smb2cli_create(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, path,
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_DIR_LIST|
+ SEC_DIR_READ_ATTRIBUTE, /* desired_access, */
+ 0, /* file_attributes, */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access, */
+ FILE_OPEN, /* create_disposition, */
+ FILE_SYNCHRONOUS_IO_NONALERT|FILE_DIRECTORY_FILE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_create '%s' (readonly) returned %s\n",
+ path,
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_query_directory(
+ cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
+ 1, 0, 0, fid_persistent, fid_volatile, "*", 0xffff,
+ talloc_tos(), &dir_data, &dir_data_length);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_query_directory returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Open directory no write access. Flush should fail. */
+
+ status = smb2cli_flush(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("smb2cli_flush on a read-only directory returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_close(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, 0, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_close returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* Open directory write-attributes only. Flush should still fail. */
+
+ status = smb2cli_create(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, path,
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_DIR_LIST|
+ SEC_DIR_WRITE_ATTRIBUTE|
+ SEC_DIR_READ_ATTRIBUTE, /* desired_access, */
+ 0, /* file_attributes, */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access, */
+ FILE_OPEN, /* create_disposition, */
+ FILE_SYNCHRONOUS_IO_NONALERT|FILE_DIRECTORY_FILE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_create '%s' (write attr) returned %s\n",
+ path,
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_query_directory(
+ cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
+ 1, 0, 0, fid_persistent, fid_volatile, "*", 0xffff,
+ talloc_tos(), &dir_data, &dir_data_length);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_query_directory returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("smb2cli_flush on a write-attributes directory "
+ "returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_close(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, 0, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_close returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* Open directory with write access. Flush should now succeed. */
+
+ status = smb2cli_create(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, path,
+ SMB2_OPLOCK_LEVEL_NONE, /* oplock_level, */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level, */
+ SEC_STD_SYNCHRONIZE|
+ SEC_DIR_LIST|
+ SEC_DIR_ADD_FILE|
+ SEC_DIR_ADD_SUBDIR|
+ SEC_DIR_WRITE_ATTRIBUTE|
+ SEC_DIR_READ_ATTRIBUTE, /* desired_access, */
+ 0, /* file_attributes, */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access, */
+ FILE_OPEN, /* create_disposition, */
+ FILE_SYNCHRONOUS_IO_NONALERT|FILE_DIRECTORY_FILE, /* create_options, */
+ NULL, /* smb2_create_blobs *blobs */
+ &fid_persistent,
+ &fid_volatile,
+ NULL, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_create '%s' (write access) returned %s\n",
+ path,
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_query_directory(
+ cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
+ 1, 0, 0, fid_persistent, fid_volatile, "*", 0xffff,
+ talloc_tos(), &dir_data, &dir_data_length);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_query_directory returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_flush(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_flush on a directory returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_close(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, 0, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_close returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+bool run_smb2_dir_fsync(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ bool bret = false;
+ const char *dname = "fsync_test_dir";
+
+ printf("Starting SMB2-DIR-FSYNC\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ status = smbXcli_negprot(cli->conn, cli->timeout,
+ PROTOCOL_SMB2_02, PROTOCOL_SMB2_02);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smbXcli_negprot returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_session_setup_creds(cli, torture_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_session_setup returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = cli_tree_connect(cli, share, "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_tree_connect returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ (void)cli_rmdir(cli, dname);
+ status = cli_mkdir(cli, dname);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("cli_mkdir(%s) returned %s\n",
+ dname,
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Test on a subdirectory. */
+ bret = test_dir_fsync(cli, dname);
+ if (bret == false) {
+ (void)cli_rmdir(cli, dname);
+ return false;
+ }
+ (void)cli_rmdir(cli, dname);
+
+ /* Test on the root handle of a share. */
+ bret = test_dir_fsync(cli, "");
+ if (bret == false) {
+ return false;
+ }
+ return true;
+}
diff --git a/source3/torture/torture.c b/source3/torture/torture.c
index 56cc6ff3628..c7ada24ea56 100644
--- a/source3/torture/torture.c
+++ b/source3/torture/torture.c
@@ -11595,6 +11595,7 @@ static struct {
{ "SMB2-MULTI-CHANNEL", run_smb2_multi_channel },
{ "SMB2-SESSION-REAUTH", run_smb2_session_reauth },
{ "SMB2-FTRUNCATE", run_smb2_ftruncate },
+ { "SMB2-DIR-FSYNC", run_smb2_dir_fsync },
{ "CLEANUP1", run_cleanup1 },
{ "CLEANUP2", run_cleanup2 },
{ "CLEANUP3", run_cleanup3 },
--
2.13.6
More information about the samba-technical
mailing list