From b51eaaec2699c3f148790059ffb07ee8cc3d777c Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Tue, 15 Aug 2017 21:46:41 +0200 Subject: [PATCH] s4/torture: disconnected durable opens and handle lease MS-SMB2 3.3.4.7 "Object Store Indicates a Lease Break": if a disconnected open keeps the handle lease, the open must be preserved. --- source4/torture/smb2/durable_open.c | 175 ++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/source4/torture/smb2/durable_open.c b/source4/torture/smb2/durable_open.c index 17b3b21..230feb2 100644 --- a/source4/torture/smb2/durable_open.c +++ b/source4/torture/smb2/durable_open.c @@ -2752,12 +2752,184 @@ done: return ret; } +static struct { + const char *held_lease; + const char *broken_to; + bool expect_durable_recon_ok; + + const char *new_lease; + bool new_disp_overwrite; + const char *new_granted; +} contend_results[] = { + /* + * MS-SMB2 3.3.4.7 "Object Store Indicates a Lease Break": if a + * disconnected open keeps the handle lease, the open must be preserved. + */ + {"RH", "RH", true, "R", false, "R"}, + {"RH", "RH", true, "RH", false, "RH"}, + {"RH", "RH", true, "RW", false, "R"}, + {"RH", "RH", true, "RHW", false, "RH"}, + + /* Why does this fail? Ie, why is the handle lease broken?*/ + {"RHW", "RH", false, "R", false, "R"}, + {"RHW", "RH", false, "RH", false, "RH"}, + {"RHW", "RH", false, "RW", false, "RW"}, + {"RHW", "RH", false, "RHW", false, "RHW"}, + + /* + * NTCREATEX_DISP_OVERWRITE will break the handle lease which must cause + * the durable open to be deleted. + */ + {"RH", "R", false, "R", true, "R"}, + {"RH", "R", false, "RH", true, "RH"}, + {"RH", "R", false, "RW", true, "R"}, + {"RH", "R", false, "RHW", true, "RH"}, + + {"RHW", "R", false, "R", true, "R"}, + {"RHW", "R", false, "RH", true, "RH"}, + {"RHW", "R", false, "RW", true, "RW"}, + {"RHW", "R", false, "RHW", true, "RHW"}, +}; + +static bool test_reconnect_contended_lease(struct torture_context *tctx, + struct smb2_tree *_tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_tree *tree = NULL; + const char *fname = "lease_break.dat"; + struct smb2_create io; + struct smb2_lease ls; + struct smb2_handle h1 = {0}; + struct smb2_handle h2 = {0}; + uint32_t caps; + int i; + NTSTATUS status; + bool ret = true; + + caps = smb2cli_conn_server_capabilities(_tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(_tree, fname); + + for (i = 0; i < ARRAY_SIZE(contend_results); i++) { + const char *held = contend_results[i].held_lease; + const char *brokento = contend_results[i].broken_to; + const char *contend = contend_results[i].new_lease; + const char *granted = contend_results[i].new_granted; + uint64_t previous_session_id; + struct smbcli_options options = _tree->session->transport->options; + uint64_t lease1 = random(); + uint64_t lease2 = random(); + bool lease_ok; + + torture_comment(tctx, "test %d: Hold %s, requesting %s, " + "expecting break to %s and grant of %s\n", + i + 1, held, contend, brokento, granted); + + ret = torture_smb2_connection_ext(tctx, 0, &options, &tree); + torture_assert_goto(tctx, ret, ret, done, "torture_smb2_connection_ext failed\n"); + previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); + + /* + * Grab durable open + */ + smb2_lease_create(&io, &ls, false, fname, lease1, smb2_util_lease_state(held)); + io.in.durable_open = true; + status = smb2_create(tree, mem_ctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h1 = io.out.file.handle; + torture_assert_goto(tctx, io.out.oplock_level == SMB2_OPLOCK_LEVEL_LEASE, + ret, done, "Bad oplock level\n"); + lease_ok = io.out.lease_response.lease_state == smb2_util_lease_state(held); + torture_assert_goto(tctx, lease_ok, ret, done, "Bad lease state\n"); + + /* + * Now disconnect + */ + TALLOC_FREE(tree); + + /* + * Contend durable open with second open + */ + smb2_lease_create(&io, &ls, false, fname, lease2, smb2_util_lease_state(contend)); + if (contend_results[i].new_disp_overwrite) { + io.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + } + status = smb2_create(_tree, mem_ctx, &io); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + h2 = io.out.file.handle; + + torture_assert_goto(tctx, io.out.oplock_level == SMB2_OPLOCK_LEVEL_LEASE, + ret, done, "Bad oplock level\n"); + lease_ok = io.out.lease_response.lease_state == smb2_util_lease_state(granted); + torture_assert_goto(tctx, lease_ok, ret, done, "Bad lease state\n"); + + smb2_util_close(_tree, h2); + ZERO_STRUCT(h2); + + /* + * Now reconnect the session and the durable handle + */ + ret = torture_smb2_connection_ext(tctx, previous_session_id, + &options, &tree); + torture_assert_goto(tctx, ret, ret, done, + "torture_smb2_connection_ext failed\n"); + + ZERO_STRUCT(io); + smb2_lease_create(&io, &ls, false, fname, lease1, + smb2_util_lease_state(held)); + io.in.durable_handle = &h1; + status = smb2_create(tree, mem_ctx, &io); + + if (contend_results[i].expect_durable_recon_ok) { + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed\n"); + torture_assert_goto( + tctx, + io.out.oplock_level == SMB2_OPLOCK_LEVEL_LEASE, + ret, done, "Bad oplock level\n"); + lease_ok = io.out.lease_response.lease_state == + smb2_util_lease_state(brokento); + torture_assert_goto(tctx, lease_ok, ret, done, "Bad lease state\n"); + + smb2_util_close(tree, io.out.file.handle); + } else { + torture_assert_ntstatus_equal( + tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, + "Wrong status\n"); + } + + TALLOC_FREE(tree); + + status = smb2_util_unlink(_tree, fname); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink failed\n"); + } + + done: + if (!smb2_util_handle_empty(h1)) { + smb2_util_close(_tree, h1); + } + if (!smb2_util_handle_empty(h2)) { + smb2_util_close(_tree, h2); + } + smb2_util_unlink(_tree, fname); + + talloc_free(mem_ctx); + + return ret; +} struct torture_suite *torture_smb2_durable_open_init(TALLOC_CTX *ctx) { struct torture_suite *suite = torture_suite_create(ctx, "durable-open"); + if(0){ torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock); torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease); torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1); @@ -2787,6 +2959,9 @@ struct torture_suite *torture_smb2_durable_open_init(TALLOC_CTX *ctx) test_durable_open_alloc_size); torture_suite_add_1smb2_test(suite, "read-only", test_durable_open_read_only); +} + torture_suite_add_1smb2_test(suite, "reconnect-contended-lease", + test_reconnect_contended_lease); suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests"); -- 2.9.4