diff options
-rw-r--r-- | source4/torture/smb2/config.mk | 1 | ||||
-rw-r--r-- | source4/torture/smb2/lease.c | 296 | ||||
-rw-r--r-- | source4/torture/smb2/smb2.c | 1 |
3 files changed, 298 insertions, 0 deletions
diff --git a/source4/torture/smb2/config.mk b/source4/torture/smb2/config.mk index 91ba9fcf0c..70b1743929 100644 --- a/source4/torture/smb2/config.mk +++ b/source4/torture/smb2/config.mk @@ -22,6 +22,7 @@ TORTURE_SMB2_OBJ_FILES = $(addprefix $(torturesrcdir)/smb2/, \ smb2.o \ durable_open.o \ oplocks.o \ + lease.o \ create.o \ read.o) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c new file mode 100644 index 0000000000..e67517324b --- /dev/null +++ b/source4/torture/smb2/lease.c @@ -0,0 +1,296 @@ +/* + Unix SMB/CIFS implementation. + + test suite for SMB2 leases + + Copyright (C) Zachary Loafman 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "librpc/gen_ndr/security.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \ + __location__, #v, (int)v, (int)correct); \ + ret = false; \ + }} while (0) + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \ + nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +static void smb2_lease_create(struct smb2_create *io, struct smb2_lease *ls, + bool dir, const char *name, uint64_t leasekey, + uint32_t leasestate) +{ + ZERO_STRUCT(*io); + io->in.security_flags = 0x00; + io->in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; + io->in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + io->in.create_flags = 0x00000000; + io->in.reserved = 0x00000000; + io->in.desired_access = SEC_RIGHTS_FILE_ALL; + io->in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io->in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + io->in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io->in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | + NTCREATEX_OPTIONS_ASYNC_ALERT | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | + 0x00200000; + io->in.fname = name; + + if (dir) { + io->in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io->in.share_access &= ~NTCREATEX_SHARE_ACCESS_DELETE; + io->in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io->in.create_disposition = NTCREATEX_DISP_CREATE; + } + + ZERO_STRUCT(*ls); + ls->lease_key[0] = leasekey; + ls->lease_key[1] = ~leasekey; + ls->lease_state = leasestate; + io->in.lease_request = ls; +} + +#define CHECK_CREATED(__io, __created, __attribute) \ + do { \ + if (__created) { \ + CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_CREATED); \ + } else { \ + CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_EXISTED); \ + } \ + CHECK_VAL((__io)->out.alloc_size, 0); \ + CHECK_VAL((__io)->out.size, 0); \ + CHECK_VAL((__io)->out.file_attr, (__attribute)); \ + CHECK_VAL((__io)->out.reserved2, 0); \ + } while(0) + +#define CHECK_LEASE(__io, __state, __oplevel, __key) \ + do { \ + if (__oplevel) { \ + CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \ + CHECK_VAL((__io)->out.lease_response.lease_key[0], (__key)); \ + CHECK_VAL((__io)->out.lease_response.lease_key[1], ~(__key)); \ + CHECK_VAL((__io)->out.lease_response.lease_state, (__state)); \ + } else { \ + CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \ + CHECK_VAL((__io)->out.lease_response.lease_key[0], 0); \ + CHECK_VAL((__io)->out.lease_response.lease_key[1], 0); \ + CHECK_VAL((__io)->out.lease_response.lease_state, 0); \ + } \ + \ + CHECK_VAL((__io)->out.lease_response.lease_flags, 0); \ + CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \ + } while(0) \ + +static const uint64_t LEASE1 = 0xBADC0FFEE0DDF00Dull; +static const uint64_t LEASE2 = 0xDEADBEEFFEEDBEADull; +static const uint64_t LEASE3 = 0xDAD0FFEDD00DF00Dull; + +#define NRESULTS 8 +static const int request_results[NRESULTS][2] = { + { SMB2_LEASE_NONE, SMB2_LEASE_NONE }, + { SMB2_LEASE_READ, SMB2_LEASE_READ }, + { SMB2_LEASE_HANDLE, SMB2_LEASE_NONE, }, + { SMB2_LEASE_WRITE, SMB2_LEASE_NONE }, + { SMB2_LEASE_READ|SMB2_LEASE_HANDLE, + SMB2_LEASE_READ|SMB2_LEASE_HANDLE }, + { SMB2_LEASE_READ|SMB2_LEASE_WRITE, + SMB2_LEASE_READ|SMB2_LEASE_WRITE }, + { SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE, SMB2_LEASE_NONE }, + { SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE, + SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE }, +}; + +static bool test_lease_request(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls; + struct smb2_handle h1, h2; + NTSTATUS status; + const char *fname = "lease.dat"; + const char *fname2 = "lease2.dat"; + const char *sname = "lease.dat:stream"; + const char *dname = "lease.dir"; + bool ret = true; + int i; + + smb2_util_unlink(tree, fname); + smb2_util_unlink(tree, fname2); + smb2_util_rmdir(tree, dname); + + /* Win7 is happy to grant RHW leases on files. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, + SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.out.file.handle; + CHECK_CREATED(&io, true, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE, + true, LEASE1); + + /* But will reject leases on directories. */ + smb2_lease_create(&io, &ls, true, dname, LEASE2, + SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, true, FILE_ATTRIBUTE_DIRECTORY); + CHECK_LEASE(&io, SMB2_LEASE_NONE, false, 0); + smb2_util_close(tree, io.out.file.handle); + + /* Also rejects multiple files leased under the same key. */ + smb2_lease_create(&io, &ls, true, fname2, LEASE1, + SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + /* And grants leases on streams (with separate leasekey). */ + smb2_lease_create(&io, &ls, false, sname, LEASE2, + SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE); + status = smb2_create(tree, mem_ctx, &io); + h2 = io.out.file.handle; + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, true, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE, + true, LEASE2); + smb2_util_close(tree, h2); + + smb2_util_close(tree, h1); + + /* Now see what combos are actually granted. */ + for (i = 0; i < NRESULTS; i++) { + torture_comment(tctx, "Testing lease type %x, expecting %x\n", + request_results[i][0], request_results[i][1]); + smb2_lease_create(&io, &ls, false, fname, LEASE1, + request_results[i][0]); + status = smb2_create(tree, mem_ctx, &io); + h2 = io.out.file.handle; + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, false, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, request_results[i][1], true, LEASE1); + smb2_util_close(tree, io.out.file.handle); + } + + done: + smb2_util_close(tree, h1); + smb2_util_close(tree, h2); + + smb2_util_unlink(tree, fname); + smb2_util_unlink(tree, fname2); + smb2_util_rmdir(tree, dname); + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_lease_upgrade(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls; + struct smb2_handle h, hnew; + NTSTATUS status; + const char *fname = "lease.dat"; + bool ret = true; + + smb2_util_unlink(tree, fname); + + /* Grab a RH lease. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, + SMB2_LEASE_READ|SMB2_LEASE_HANDLE); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, true, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, + SMB2_LEASE_READ|SMB2_LEASE_HANDLE, true, LEASE1); + h = io.out.file.handle; + + /* Upgrades (sidegrades?) to RW leave us with an RH. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, + SMB2_LEASE_READ|SMB2_LEASE_WRITE); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, false, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, + SMB2_LEASE_READ|SMB2_LEASE_HANDLE, true, LEASE1); + hnew = io.out.file.handle; + + smb2_util_close(tree, hnew); + + /* Upgrade to RHW lease. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, + SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, false, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE, + true, LEASE1); + hnew = io.out.file.handle; + + smb2_util_close(tree, h); + h = hnew; + + /* Attempt to downgrade - original lease state is maintained. */ + smb2_lease_create(&io, &ls, false, fname, LEASE1, + SMB2_LEASE_READ|SMB2_LEASE_HANDLE); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, false, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE, + true, LEASE1); + hnew = io.out.file.handle; + + smb2_util_close(tree, hnew); + + done: + smb2_util_close(tree, h); + smb2_util_close(tree, hnew); + + smb2_util_unlink(tree, fname); + + talloc_free(mem_ctx); + + return ret; +} + +struct torture_suite *torture_smb2_lease_init(void) +{ + struct torture_suite *suite = + torture_suite_create(talloc_autofree_context(), "LEASE"); + + torture_suite_add_1smb2_test(suite, "REQUEST", test_lease_request); + torture_suite_add_1smb2_test(suite, "UPGRADE", test_lease_upgrade); + + suite->description = talloc_strdup(suite, "SMB2-LEASE tests"); + + return suite; +} diff --git a/source4/torture/smb2/smb2.c b/source4/torture/smb2/smb2.c index 3c9522e771..64456aaed5 100644 --- a/source4/torture/smb2/smb2.c +++ b/source4/torture/smb2/smb2.c @@ -141,6 +141,7 @@ NTSTATUS torture_smb2_init(void) torture_suite_add_simple_test(suite, "NOTIFY", torture_smb2_notify); torture_suite_add_suite(suite, torture_smb2_durable_open_init()); torture_suite_add_1smb2_test(suite, "OPLOCK-BATCH1", torture_smb2_oplock_batch1); + torture_suite_add_suite(suite, torture_smb2_lease_init()); suite->description = talloc_strdup(suite, "SMB2-specific tests"); |