From 37da5c2a431ee1a906347d3166dc633349412e9c Mon Sep 17 00:00:00 2001 From: Anubhav Rakshit Date: Thu, 30 Oct 2014 13:20:57 +0530 Subject: [PATCH] torture:smb2: Add test replay6 to verify Error Codes for DurableHandleReqV2 replay Pair-Programmed-With: Stefan Metzmacher Pair-Programmed-With: Guenther Deschner Pair-Programmed-With: Michael Adam Signed-off-by: Anubhav Rakshit Signed-off-by: Stefan Metzmacher Signed-off-by: Guenther Deschner Signed-off-by: Michael Adam Reviewed-by: Jeremy Allison (cherry picked from commit e095a61c4b36e71b03d8afc724da09c91603a29b) BUG: https://bugzilla.samba.org/show_bug.cgi?id=11809 --- source4/torture/smb2/replay.c | 211 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 209 insertions(+), 2 deletions(-) diff --git a/source4/torture/smb2/replay.c b/source4/torture/smb2/replay.c index 8388837dde4..91bb56800fe 100644 --- a/source4/torture/smb2/replay.c +++ b/source4/torture/smb2/replay.c @@ -94,7 +94,7 @@ #define BASEDIR "replaytestdir" -static struct { +struct break_info { struct torture_context *tctx; struct smb2_handle handle; uint8_t level; @@ -102,7 +102,16 @@ static struct { int count; int failures; NTSTATUS failure_status; -} break_info; +}; + +static struct break_info break_info; + +static void torture_reset_break_info(struct torture_context *tctx, + struct break_info *r) +{ + ZERO_STRUCTP(r); + r->tctx = tctx; +} static void torture_oplock_ack_callback(struct smb2_request *req) { @@ -163,6 +172,61 @@ static bool torture_oplock_ack_handler(struct smb2_transport *transport, } /** + * Timer handler function notifies the registering function that time is up + */ +static void timeout_cb(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + bool *timesup = (bool *)private_data; + *timesup = true; + return; +} + +/** + * Wait a short period of time to receive a single oplock break request + */ +static void torture_wait_for_oplock_break(struct torture_context *tctx) +{ + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + struct tevent_timer *te = NULL; + struct timeval ne; + bool timesup = false; + int old_count = break_info.count; + + /* Wait .1 seconds for an oplock break */ + ne = tevent_timeval_current_ofs(0, 100000); + + te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, ×up); + if (te == NULL) { + torture_comment(tctx, "Failed to wait for an oplock break. " + "test results may not be accurate."); + goto done; + } + + while (!timesup && break_info.count < old_count + 1) { + if (tevent_loop_once(tctx->ev) != 0) { + torture_comment(tctx, "Failed to wait for an oplock " + "break. test results may not be " + "accurate."); + goto done; + } + } + +done: + /* + * We don't know if the timed event fired and was freed, we received + * our oplock break, or some other event triggered the loop. Thus, + * we create a tmp_ctx to be able to safely free/remove the timed + * event in all 3 cases. + */ + talloc_free(tmp_ctx); + + return; +} + +/** * Test what happens when SMB2_FLAGS_REPLAY_OPERATION is enabled for various * commands. We want to verify if the server returns an error code or not. */ @@ -2221,6 +2285,148 @@ done: return ret; } + +/** + * Test Error Codes when a DurableHandleReqV2 with matching CreateGuid is + * re-sent with or without SMB2_FLAGS_REPLAY_OPERATION + */ +static bool test_replay6(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle _h; + struct smb2_handle *h = NULL; + struct smb2_create io, ref1; + union smb_fileinfo qfinfo; + struct GUID create_guid = GUID_random(); + bool ret = true; + const char *fname = BASEDIR "\\replay6.dat"; + struct smb2_transport *transport = tree->session->transport; + + if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, "SMB 3.X Dialect family required for " + "replay tests\n"); + } + + torture_reset_break_info(tctx, &break_info); + tree->session->transport->oplock.handler = torture_oplock_ack_handler; + tree->session->transport->oplock.private_data = tree; + + torture_comment(tctx, "Error Codes for DurableHandleReqV2 Replay\n"); + smb2_util_unlink(tree, fname); + status = torture_smb2_testdir(tree, BASEDIR, &_h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, _h); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + torture_reset_break_info(tctx, &break_info); + + smb2_oplock_create_share(&io, fname, + smb2_util_share_access("RWD"), + smb2_util_oplock_level("b")); + io.in.durable_open = false; + io.in.durable_open_v2 = true; + io.in.persistent_open = false; + io.in.create_guid = create_guid; + io.in.timeout = UINT32_MAX; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + ref1 = io; + _h = io.out.file.handle; + h = &_h; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); + CHECK_VAL(io.out.durable_open, false); + CHECK_VAL(io.out.durable_open_v2, true); + + io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + smb2cli_session_start_replay(tree->session->smbXcli); + status = smb2_create(tree, mem_ctx, &io); + smb2cli_session_stop_replay(tree->session->smbXcli); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATE_OUT(&io, &ref1); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + torture_reset_break_info(tctx, &break_info); + + qfinfo = (union smb_fileinfo) { + .generic.level = RAW_SFILEINFO_POSITION_INFORMATION, + .generic.in.file.handle = *h + }; + torture_comment(tctx, "Trying getinfo\n"); + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(qfinfo.position_information.out.position, 0); + + smb2cli_session_start_replay(tree->session->smbXcli); + status = smb2_create(tree, mem_ctx, &io); + smb2cli_session_stop_replay(tree->session->smbXcli); + CHECK_STATUS(status, NT_STATUS_OK); + torture_assert_u64_not_equal_goto(tctx, + io.out.file.handle.data[0], + ref1.out.file.handle.data[0], + ret, done, "data 0"); + torture_assert_u64_not_equal_goto(tctx, + io.out.file.handle.data[1], + ref1.out.file.handle.data[1], + ret, done, "data 1"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, smb2_util_oplock_level("s")); + torture_reset_break_info(tctx, &break_info); + + /* + * Resend the matching Durable V2 Create without + * SMB2_FLAGS_REPLAY_OPERATION. This triggers an oplock break and still + * gets NT_STATUS_DUPLICATE_OBJECTID + */ + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_DUPLICATE_OBJECTID); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + torture_reset_break_info(tctx, &break_info); + + /* + * According to MS-SMB2 3.3.5.9.10 if Durable V2 Create is replayed and + * FileAttributes or CreateDisposition do not match the earlier Create + * request the Server fails request with + * NT_STATUS_INVALID_PARAMETER. But through this test we see that server + * does not really care about changed FileAttributes or + * CreateDisposition. + */ + io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.in.create_disposition = NTCREATEX_DISP_OPEN; + smb2cli_session_start_replay(tree->session->smbXcli); + status = smb2_create(tree, mem_ctx, &io); + smb2cli_session_stop_replay(tree->session->smbXcli); + CHECK_STATUS(status, NT_STATUS_OK); + torture_assert_u64_not_equal_goto(tctx, + io.out.file.handle.data[0], + ref1.out.file.handle.data[0], + ret, done, "data 0"); + torture_assert_u64_not_equal_goto(tctx, + io.out.file.handle.data[1], + ref1.out.file.handle.data[1], + ret, done, "data 1"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + +done: + if (h != NULL) { + smb2_util_close(tree, *h); + } + + smb2_util_unlink(tree, fname); + smb2_deltree(tree, BASEDIR); + + talloc_free(tree); + talloc_free(mem_ctx); + + return ret; +} + struct torture_suite *torture_smb2_replay_init(void) { struct torture_suite *suite = @@ -2240,6 +2446,7 @@ struct torture_suite *torture_smb2_replay_init(void) torture_suite_add_1smb2_test(suite, "replay3", test_replay3); torture_suite_add_1smb2_test(suite, "replay4", test_replay4); torture_suite_add_1smb2_test(suite, "replay5", test_replay5); + torture_suite_add_1smb2_test(suite, "replay6", test_replay6); suite->description = talloc_strdup(suite, "SMB2 REPLAY tests"); -- 2.11.4.GIT