From: Stefan Metzmacher Date: Wed, 2 Oct 2019 13:30:53 +0000 (+0200) Subject: s4:torture/smb2: add smb2.lock.replay_smb3_specification test X-Git-Url: http://git.samba.org/?a=commitdiff_plain;ds=sidebyside;h=f2233c27123cd62f4e976a2c10d1ecd949e13dcb;p=metze%2Fsamba%2Fwip.git s4:torture/smb2: add smb2.lock.replay_smb3_specification test This implements a test that checks for the specified behaviour. Signed-off-by: Stefan Metzmacher --- diff --git a/source4/torture/smb2/lock.c b/source4/torture/smb2/lock.c index 586fbd8806ef..45c389c5f0b2 100644 --- a/source4/torture/smb2/lock.c +++ b/source4/torture/smb2/lock.c @@ -3134,6 +3134,222 @@ done: return ret; } +/** + * Test lock replay detection + * + * This test check the SMB 3 behaviour of lock sequence checking, + * which should be implemented for all handles. + * + * Make it clear that this test is supposed to pass a + * server implementing the specification: + * + * [MS-SMB2] 3.3.5.14 Receiving an SMB2 LOCK Request + * + * ... + * + * If the LockSequence value in the SMB2 LOCK Request (section 2.2.26) is not + * zero, and either one of the following conditions is TRUE, the server SHOULD + * verify whether the lock/unlock request with that LockSequence value has + * been successfully processed before: + * * Connection.Dialect is "2.1" and Open.IsResilient is TRUE. + * * Connection.Dialect belongs to the SMB 3.x dialect family.<318> + * + * ... + * + * <318> Section 3.3.5.14: Windows 8, Windows Server 2012, Windows 8.1, and + * Windows Server 2012 R2 do not verify the LockSequence value in the + * SMB2 LOCK Request (section 2.2.26) when both Open.IsResilient and + * Open.IsPersistent are FALSE. + * + * Note <318> also applies to all versions (at least) up to Windows Server 2019. + * + * Hopefully this will be fixed in future Windows versions and they + * will avoid Note <318>. + */ +static bool test_replay_smb3_specification(struct torture_context *torture, + struct smb2_tree *tree) +{ + NTSTATUS status; + bool ret = true; + struct smb2_handle h; + struct smb2_lock lck; + struct smb2_lock_element el; + const char *fname = BASEDIR "\\replay_smb3_specification.txt"; + struct smb2_transport *transport = tree->session->transport; + + if (smbXcli_conn_protocol(transport->conn) < PROTOCOL_SMB3_00) { + torture_skip(torture, "SMB 3.0.0 Dialect family or above \ + required for Lock Replay tests\n"); + } + + status = torture_smb2_testdir(tree, BASEDIR, &h); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, h); + + torture_comment(torture, "Testing Open File:\n"); + status = torture_smb2_testfile(tree, fname, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Setup initial parameters + */ + el = (struct smb2_lock_element) { + .length = 100, + .offset = 100, + }; + lck = (struct smb2_lock) { + .in.locks = &el, + .in.lock_count = 0x0001, + .in.file.handle = h + }; + + torture_comment(torture, + "Testing SMB 3 LockSequence for all Handles\n"); + + /* + * Test with an invalid bucket number (only 1..64 are valid). + * With an invalid number, lock replay detection is not performed. + */ + torture_comment(torture, "Testing Lock (ignored) Replay detection " + "(Bucket No: 0 (invalid)) [ignored]:\n"); + lck.in.lock_sequence = 0x000 + 0x1; + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + torture_comment(torture, "Testing Lock Replay detection " + "(Bucket No: 1):\n"); + + /* + * Obtain Exclusive Lock of length 100 bytes using Bucket Num 1 + * and Bucket Seq 1. + */ + lck.in.lock_sequence = 0x010 + 0x1; + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Server detects Replay of Byte Range locks using the Lock Sequence + * Numbers. And ignores the requests completely. + */ + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* status: still locked */ + + /* + * Server will not grant same Byte Range using a different Bucket Seq + */ + lck.in.lock_sequence = 0x010 + 0x2; + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + torture_comment(torture, "Testing Lock Replay detection " + "(Bucket No: 2):\n"); + + /* + * Server will not grant same Byte Range using a different Bucket Num + */ + lck.in.lock_sequence = 0x020 + 0x1; + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* status: still locked */ + + /* test with invalid bucket when file is locked */ + + torture_comment(torture, "Testing Lock Replay detection " + "(Bucket No: 65 (invalid)) [ignored]:\n"); + + lck.in.lock_sequence = 0x410 + 0x1; + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + /* status: unlocked */ + + /* + * Lock again for the unlock replay test + */ + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(torture, "Testing Lock Replay detection " + "(Bucket No: 64):\n"); + + /* + * Server will not grant same Byte Range using a different Bucket Num + */ + lck.in.lock_sequence = 0x400 + 0x1; + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* + * Test Unlock replay detection + */ + lck.in.lock_sequence = 0x400 + 0x2; + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); /* new seq num ==> unlocked */ + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); /* replay detected ==> ignored */ + + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); /* same seq num ==> ignored */ + CHECK_STATUS(status, NT_STATUS_OK); + + /* verify it's unlocked: */ + lck.in.lock_sequence = 0x400 + 0x3; + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + /* status: not locked */ + +done: + smb2_util_close(tree, h); + smb2_deltree(tree, BASEDIR); + return ret; +} + /** * Test lock interaction between smbd and ctdb with tombstone records. * @@ -3233,6 +3449,8 @@ struct torture_suite *torture_smb2_lock_init(TALLOC_CTX *ctx) torture_suite_add_1smb2_test(suite, "truncate", test_truncate); torture_suite_add_1smb2_test(suite, "replay_broken_windows", test_replay_broken_windows); + torture_suite_add_1smb2_test(suite, "replay_smb3_specification", + test_replay_smb3_specification); torture_suite_add_1smb2_test(suite, "ctdb-delrec-deadlock", test_deadlock); suite->description = talloc_strdup(suite, "SMB2-LOCK tests");