From aa0d0e3c91f914464d7b22391d74be9845cf17f9 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 9 Nov 2024 16:00:39 +0100 Subject: [PATCH] smbtorture: verify Windows SEC_FILE_APPEND_DATA behaviour This test proves that Windows will not enforce append behaviour when using SEC_FILE_APPEND_DATA: writing with an offset and length into existing data is not mapped to an append operation. According to https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile Windows internal APIs seemt to support append-IO: If only the FILE_APPEND_DATA and SYNCHRONIZE flags are set, the caller can write only to the end of the file, and any offset information on writes to the file is ignored. However, the file is automatically extended as necessary for this type of write operation. ...but at least over SMB2 it doesn't work. This also demonstrates that Windows behaves as documented in the footnote in MS-SMB2 3.3.5.13 "Receiving an SMB2 WRITE Request": If the range being written to is within the existing file size and Open.GrantedAccess does not include FILE_WRITE_DATA, or if the range being written to extends the file size and Open.GrantedAccess does not include FILE_APPEND_DATA, the server SHOULD<358> fail the request with STATUS_ACCESS_DENIED. MS-SMB2 <358> Section 3.3.5.13: Windows SMB2 servers allow the operation when either FILE_APPEND_DATA or FILE_WRITE_DATA is set in Open.GrantedAccess. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15751 Signed-off-by: Ralph Boehme --- source4/torture/smb2/read_write.c | 130 ++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/source4/torture/smb2/read_write.c b/source4/torture/smb2/read_write.c index 707a49b6b1c..5b8ed4222d2 100644 --- a/source4/torture/smb2/read_write.c +++ b/source4/torture/smb2/read_write.c @@ -347,6 +347,135 @@ done: return ret; } +static bool test_append(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle h = {}; + uint8_t buf[1] = {}; + struct smb2_create c = {}; + union smb_fileinfo finfo = {}; + off_t off; + uint64_t expected_size = 0; + NTSTATUS status; + bool ret = true; + + smb2_util_unlink(tree, FNAME); + + torture_comment(tctx, "Create file with 1 byte\n"); + + status = torture_smb2_testfile(tree, FNAME, &h); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Incorrect status\n"); + + expected_size = 0; + + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Incorrect status\n"); + expected_size += ARRAY_SIZE(buf); + + status = smb2_util_close(tree, h); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Incorrect status\n"); + ZERO_STRUCT(h); + + torture_comment(tctx, "Open file with SEC_FILE_APPEND_DATA" + "|SEC_STD_SYNCHRONIZE\n"); + + c.in.desired_access = SEC_FILE_APPEND_DATA | SEC_STD_SYNCHRONIZE; + c.in.create_disposition = NTCREATEX_DISP_OPEN; + c.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + c.in.fname = FNAME; + + status = smb2_create(tree, tree, &c); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Incorrect status\n"); + + h = c.out.file.handle; + + torture_comment(tctx, "Write one byte at offset 0 and verify " + "written data was not appended but written at " + "requested offset\n"); + + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Incorrect status\n"); + + status = smb2_util_close(tree, h); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Incorrect status\n"); + ZERO_STRUCT(h); + + status = torture_smb2_testfile(tree, FNAME, &h); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Incorrect status\n"); + + finfo.generic.level = RAW_FILEINFO_ALL_INFORMATION; + finfo.generic.in.file.handle = h; + status = smb2_getinfo_file(tree, tree, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Incorrect status\n"); + + torture_assert_u64_equal_goto(tctx, finfo.all_info.out.size, + expected_size, + ret, done, "wrong size"); + + status = smb2_util_close(tree, h); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Incorrect status\n"); + ZERO_STRUCT(h); + + torture_comment(tctx, "Open file in write-data only mode " + "and check if we can append\n"); + + c.in.desired_access = SEC_FILE_WRITE_DATA | SEC_FILE_READ_ATTRIBUTE; + c.in.create_disposition = NTCREATEX_DISP_OPEN; + c.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + c.in.fname = FNAME; + + status = smb2_create(tree, tree, &c); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Incorrect status\n"); + h = c.out.file.handle; + + off = 1000; + + status = smb2_util_write(tree, h, buf, off, ARRAY_SIZE(buf)); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Incorrect status\n"); + + expected_size = off + ARRAY_SIZE(buf); + + finfo.generic.level = RAW_FILEINFO_ALL_INFORMATION; + finfo.generic.in.file.handle = h; + status = smb2_getinfo_file(tree, tree, &finfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Incorrect status\n"); + + torture_assert_u64_equal_goto(tctx, finfo.all_info.out.size, + expected_size, + ret, done, "wrong size"); + + status = smb2_util_close(tree, h); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "Incorrect status\n"); + ZERO_STRUCT(h); + +done: + if (!smb2_util_handle_empty(h)) { + smb2_util_close(tree, h); + } + smb2_util_unlink(tree, FNAME); + return ret; +} + + struct torture_suite *torture_smb2_readwrite_init(TALLOC_CTX *ctx) { struct torture_suite *suite = torture_suite_create(ctx, "rw"); @@ -354,6 +483,7 @@ struct torture_suite *torture_smb2_readwrite_init(TALLOC_CTX *ctx) torture_suite_add_2smb2_test(suite, "rw1", run_smb2_readwritetest); torture_suite_add_2smb2_test(suite, "rw2", run_smb2_wrap_readwritetest); torture_suite_add_1smb2_test(suite, "invalid", test_rw_invalid); + torture_suite_add_1smb2_test(suite, "append", test_append); suite->description = talloc_strdup(suite, "SMB2 Samba4 Read/Write"); -- 2.47.3