torture:smb2: add test for checking sequence number wrap around.
authorGünther Deschner <gd@samba.org>
Thu, 25 Feb 2016 10:15:06 +0000 (11:15 +0100)
committerJeremy Allison <jra@samba.org>
Mon, 21 Mar 2016 23:23:21 +0000 (00:23 +0100)
Guenther

Signed-off-by: Guenther Deschner <gd@samba.org>
Reviewed-by: Michael Adam <obnox@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
selftest/knownfail
source4/torture/smb2/replay.c

index c15d26354f31ff716e53592727791a28f784e80b..0950ef0539f78ecbfd238b17016996a0aa377c18 100644 (file)
 ^samba3.smb2.setinfo.setinfo
 ^samba3.smb2.session.*reauth5 # some special anonymous checks?
 ^samba3.smb2.compound.interim2 # wrong return code (STATUS_CANCELLED)
+^samba3.smb2.replay.channel-sequence
 ^samba3.smb2.replay.replay3
 ^samba3.smb2.replay.replay4
 ^samba3.smb2.lock.*replay
index c32533b1d69a23e802c2c2f39d36ad419e21764b..8388837dde48361df4261b82daae006d47d9e096 100644 (file)
@@ -1428,6 +1428,271 @@ done:
        return ret;
 }
 
+static bool test_channel_sequence_table(struct torture_context *tctx,
+                                       struct smb2_tree *tree,
+                                       bool do_replay,
+                                       uint16_t opcode)
+{
+       NTSTATUS status;
+       TALLOC_CTX *mem_ctx = talloc_new(tctx);
+       struct smb2_handle handle;
+       struct smb2_handle *phandle = NULL;
+       struct smb2_create io;
+       struct GUID create_guid = GUID_random();
+       bool ret = true;
+       const char *fname = BASEDIR "\\channel_sequence.dat";
+       uint16_t csn = 0;
+       uint16_t limit = UINT16_MAX - 0x7fff;
+       int i;
+       struct {
+               uint16_t csn;
+               bool csn_rand_low;
+               bool csn_rand_high;
+               NTSTATUS expected_status;
+       } tests[] = {
+               {
+                       .csn                    = 0,
+                       .expected_status        = NT_STATUS_OK,
+               },{
+                       .csn                    = 0x7fff + 1,
+                       .expected_status        = NT_STATUS_FILE_NOT_AVAILABLE,
+               },{
+                       .csn                    = 0x7fff + 2,
+                       .expected_status        = NT_STATUS_FILE_NOT_AVAILABLE,
+               },{
+                       .csn                    = -1,
+                       .csn_rand_high          = true,
+                       .expected_status        = NT_STATUS_FILE_NOT_AVAILABLE,
+               },{
+                       .csn                    = 0xffff,
+                       .expected_status        = NT_STATUS_FILE_NOT_AVAILABLE,
+               },{
+                       .csn                    = 0x7fff,
+                       .expected_status        = NT_STATUS_OK,
+               },{
+                       .csn                    = 0x7ffe,
+                       .expected_status        = NT_STATUS_FILE_NOT_AVAILABLE,
+               },{
+                       .csn                    = 0,
+                       .expected_status        = NT_STATUS_FILE_NOT_AVAILABLE,
+               },{
+                       .csn                    = -1,
+                       .csn_rand_low           = true,
+                       .expected_status        = NT_STATUS_FILE_NOT_AVAILABLE,
+               },{
+                       .csn                    = 0x7fff + 1,
+                       .expected_status        = NT_STATUS_OK,
+               },{
+                       .csn                    = 0xffff,
+                       .expected_status        = NT_STATUS_OK,
+               },{
+                       .csn                    = 0,
+                       .expected_status        = NT_STATUS_OK,
+               },{
+                       .csn                    = 1,
+                       .expected_status        = NT_STATUS_OK,
+               },{
+                       .csn                    = 0,
+                       .expected_status        = NT_STATUS_FILE_NOT_AVAILABLE,
+               },{
+                       .csn                    = 1,
+                       .expected_status        = NT_STATUS_OK,
+               },{
+                       .csn                    = 0xffff,
+                       .expected_status        = NT_STATUS_FILE_NOT_AVAILABLE,
+               }
+       };
+
+       smb2cli_session_reset_channel_sequence(tree->session->smbXcli, 0);
+
+       csn = smb2cli_session_current_channel_sequence(tree->session->smbXcli);
+       torture_comment(tctx, "Testing create with channel sequence number: 0x%04x\n", csn);
+
+       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.create_guid = create_guid;
+       io.in.timeout = UINT32_MAX;
+
+       torture_assert_ntstatus_ok_goto(tctx,
+               smb2_create(tree, mem_ctx, &io),
+               ret, done, "failed to call smb2_create");
+
+       handle = io.out.file.handle;
+       phandle = &handle;
+
+       for (i=0; i <ARRAY_SIZE(tests); i++) {
+
+               const char *opstr = "";
+               union smb_fileinfo qfinfo;
+
+               csn = tests[i].csn;
+
+               if (tests[i].csn_rand_low) {
+                       csn = rand() % limit;
+               } else if (tests[i].csn_rand_high) {
+                       csn = rand() % limit + 0x7fff;
+               }
+
+               switch (opcode) {
+               case SMB2_OP_WRITE:
+                       opstr = "write";
+                       break;
+               case SMB2_OP_IOCTL:
+                       opstr = "ioctl";
+                       break;
+               case SMB2_OP_SETINFO:
+                       opstr = "setinfo";
+                       break;
+               default:
+                       break;
+               }
+
+               smb2cli_session_reset_channel_sequence(tree->session->smbXcli, csn);
+               csn = smb2cli_session_current_channel_sequence(tree->session->smbXcli);
+
+               torture_comment(tctx, "Testing %s (replay: %s) with CSN 0x%04x, expecting: %s\n",
+                       opstr, do_replay ? "true" : "false", csn,
+                       nt_errstr(tests[i].expected_status));
+
+               if (do_replay) {
+                       smb2cli_session_start_replay(tree->session->smbXcli);
+               }
+
+               switch (opcode) {
+               case SMB2_OP_WRITE: {
+                       DATA_BLOB blob = data_blob_talloc(tctx, NULL, 255);
+
+                       generate_random_buffer(blob.data, blob.length);
+
+                       status = smb2_util_write(tree, handle, blob.data, 0, blob.length);
+                       if (NT_STATUS_IS_OK(status)) {
+                               struct smb2_read rd;
+
+                               rd = (struct smb2_read) {
+                                       .in.file.handle = handle,
+                                       .in.length = blob.length,
+                                       .in.offset = 0
+                               };
+
+                               torture_assert_ntstatus_ok_goto(tctx,
+                                       smb2_read(tree, tree, &rd),
+                                       ret, done, "failed to read after write");
+
+                               torture_assert_data_blob_equal(tctx,
+                                       rd.out.data, blob,
+                                       "read/write mismatch");
+                       }
+                       break;
+               }
+               case SMB2_OP_IOCTL: {
+                       union smb_ioctl ioctl;
+                       ioctl = (union smb_ioctl) {
+                               .smb2.level = RAW_IOCTL_SMB2,
+                               .smb2.in.file.handle = handle,
+                               .smb2.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID,
+                               .smb2.in.max_response_size = 64,
+                               .smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL
+                       };
+                       status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
+                       break;
+               }
+               case SMB2_OP_SETINFO: {
+                       union smb_setfileinfo sfinfo;
+                       ZERO_STRUCT(sfinfo);
+                       sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
+                       sfinfo.generic.in.file.handle = handle;
+                       sfinfo.position_information.in.position = 0x1000;
+                       status = smb2_setinfo_file(tree, &sfinfo);
+                       break;
+               }
+               default:
+                       break;
+               }
+
+               qfinfo = (union smb_fileinfo) {
+                       .generic.level = RAW_SFILEINFO_POSITION_INFORMATION,
+                       .generic.in.file.handle = handle
+               };
+
+               torture_assert_ntstatus_ok_goto(tctx,
+                       smb2_getinfo_file(tree, mem_ctx, &qfinfo),
+                       ret, done, "failed to read after write");
+
+               if (do_replay) {
+                       smb2cli_session_stop_replay(tree->session->smbXcli);
+               }
+
+               torture_assert_ntstatus_equal_goto(tctx,
+                       status, tests[i].expected_status,
+                       ret, done, "got unexpected failure code");
+
+       }
+done:
+       if (phandle != NULL) {
+               smb2_util_close(tree, *phandle);
+       }
+
+       smb2_util_unlink(tree, fname);
+
+       return ret;
+}
+
+static bool test_channel_sequence(struct torture_context *tctx,
+                                 struct smb2_tree *tree)
+{
+       TALLOC_CTX *mem_ctx = talloc_new(tctx);
+       bool ret = true;
+       const char *fname = BASEDIR "\\channel_sequence.dat";
+       struct smb2_transport *transport1 = tree->session->transport;
+       struct smb2_handle handle;
+       uint32_t server_capabilities;
+       uint16_t opcodes[] = { SMB2_OP_WRITE, SMB2_OP_IOCTL, SMB2_OP_SETINFO };
+       int i;
+
+       if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) {
+               torture_skip(tctx, "SMB 3.X Dialect family required for "
+                                  "Replay tests\n");
+       }
+
+       server_capabilities = smb2cli_conn_server_capabilities(
+                                       tree->session->transport->conn);
+       if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) {
+               torture_skip(tctx,
+                            "Server does not support multi-channel.");
+       }
+
+       torture_comment(tctx, "Testing channel sequence numbers\n");
+
+       torture_assert_ntstatus_ok_goto(tctx,
+               torture_smb2_testdir(tree, BASEDIR, &handle),
+               ret, done, "failed to setup test directory");
+
+       smb2_util_close(tree, handle);
+       smb2_util_unlink(tree, fname);
+
+       for (i=0; i <ARRAY_SIZE(opcodes); i++) {
+               torture_assert(tctx,
+                       test_channel_sequence_table(tctx, tree, false, opcodes[i]),
+                       "failed to test CSN without replay flag");
+               torture_assert(tctx,
+                       test_channel_sequence_table(tctx, tree, true, opcodes[i]),
+                       "failed to test CSN with replay flag");
+       }
+
+done:
+
+       smb2_util_unlink(tree, fname);
+       smb2_deltree(tree, BASEDIR);
+
+       talloc_free(tree);
+       talloc_free(mem_ctx);
+
+       return ret;
+}
+
 /**
  * Test Durablity V2 Create Replay Detection on Multi Channel
  */
@@ -1971,6 +2236,7 @@ struct torture_suite *torture_smb2_replay_init(void)
        torture_suite_add_1smb2_test(suite, "replay-dhv2-lease2",  test_replay_dhv2_lease2);
        torture_suite_add_1smb2_test(suite, "replay-dhv2-lease3",  test_replay_dhv2_lease3);
        torture_suite_add_1smb2_test(suite, "replay-dhv2-lease-oplock",  test_replay_dhv2_lease_oplock);
+       torture_suite_add_1smb2_test(suite, "channel-sequence", test_channel_sequence);
        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);