The branch, master has been updated via 12a8ed07b43ed8dc27db396a23cd8657e419101e (commit) via 92f3686c962d67a5feab00b9e8abdb87ddb832a6 (commit) from 0b890ae19a6e9dad9bdff248ada3fdee9416ab96 (commit)
http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 12a8ed07b43ed8dc27db396a23cd8657e419101e Author: Zach Loafman <zachary.loaf...@isilon.com> Date: Fri Mar 27 19:14:01 2009 -0700 s4:torture/smb2: Add torture tests for leases. commit 92f3686c962d67a5feab00b9e8abdb87ddb832a6 Author: Zach Loafman <zachary.loaf...@isilon.com> Date: Fri Mar 27 19:12:43 2009 -0700 s4:smb2: Add rudimentary SMB2.1 lease support to libcli Add the structures, constants, and marshalling for SMB2.1 leases. ----------------------------------------------------------------------- Summary of changes: source4/libcli/raw/interfaces.h | 12 ++ source4/libcli/smb2/create.c | 30 ++++ source4/libcli/smb2/smb2.h | 8 + source4/torture/smb2/config.mk | 1 + source4/torture/smb2/lease.c | 296 +++++++++++++++++++++++++++++++++++++++ source4/torture/smb2/smb2.c | 1 + 6 files changed, 348 insertions(+), 0 deletions(-) create mode 100644 source4/torture/smb2/lease.c Changeset truncated at 500 lines: diff --git a/source4/libcli/raw/interfaces.h b/source4/libcli/raw/interfaces.h index a0584c0..bd93fa1 100644 --- a/source4/libcli/raw/interfaces.h +++ b/source4/libcli/raw/interfaces.h @@ -53,6 +53,16 @@ struct smb2_handle { uint64_t data[2]; }; +/* + SMB2 lease structure (per MS-SMB2 2.2.13) +*/ +struct smb2_lease { + uint64_t lease_key[2]; + uint32_t lease_state; + uint32_t lease_flags; /* should be 0 */ + uint64_t lease_duration; /* should be 0 */ +}; + struct ntvfs_handle; /* @@ -1633,6 +1643,7 @@ union smb_open { bool query_maximal_access; NTTIME timewarp; bool query_on_disk_id; + struct smb2_lease *lease_request; /* and any additional blobs the caller wants */ struct smb2_create_blobs { @@ -1666,6 +1677,7 @@ union smb_open { /* optional return values matching tagged values in the call */ uint32_t maximal_access; uint8_t on_disk_id[32]; + struct smb2_lease lease_response; /* tagged blobs in the reply */ struct smb2_create_blobs blobs; diff --git a/source4/libcli/smb2/create.c b/source4/libcli/smb2/create.c index 8a40e56..344be60 100644 --- a/source4/libcli/smb2/create.c +++ b/source4/libcli/smb2/create.c @@ -312,6 +312,23 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create } } + if (io->in.lease_request) { + uint8_t data[32]; + + memcpy(&data[0], io->in.lease_request->lease_key, 16); + SIVAL(data, 16, io->in.lease_request->lease_state); + SIVAL(data, 20, io->in.lease_request->lease_flags); + SBVAL(data, 24, io->in.lease_request->lease_duration); + + status = smb2_create_blob_add(req, &blobs, + SMB2_CREATE_TAG_RQLS, + data_blob_const(data, 32)); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + } + /* and any custom blobs */ for (i=0;i<io->in.blobs.num_blobs;i++) { status = smb2_create_blob_add(req, &blobs, @@ -402,6 +419,19 @@ NTSTATUS smb2_create_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct } memcpy(io->out.on_disk_id, io->out.blobs.blobs[i].data.data, 32); } + if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_RQLS) == 0) { + uint8_t *data; + if (io->out.blobs.blobs[i].data.length != 32) { + smb2_request_destroy(req); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + data = io->out.blobs.blobs[i].data.data; + memcpy(io->out.lease_response.lease_key, data, 16); + io->out.lease_response.lease_state = IVAL(data, 16); + io->out.lease_response.lease_flags = IVAL(data, 20); + io->out.lease_response.lease_duration = BVAL(data, 24); + } } data_blob_free(&blob); diff --git a/source4/libcli/smb2/smb2.h b/source4/libcli/smb2/smb2.h index 7c07c84..fd961ce 100644 --- a/source4/libcli/smb2/smb2.h +++ b/source4/libcli/smb2/smb2.h @@ -263,6 +263,13 @@ struct smb2_request { #define SMB2_OPLOCK_LEVEL_II 0x01 #define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 #define SMB2_OPLOCK_LEVEL_BATCH 0x09 +#define SMB2_OPLOCK_LEVEL_LEASE 0xFF + +/* SMB2 lease bits */ +#define SMB2_LEASE_NONE 0x00 +#define SMB2_LEASE_READ 0x01 +#define SMB2_LEASE_HANDLE 0x02 +#define SMB2_LEASE_WRITE 0x04 /* SMB2 impersonation levels */ #define SMB2_IMPERSONATION_ANONYMOUS 0x00 @@ -279,6 +286,7 @@ struct smb2_request { #define SMB2_CREATE_TAG_ALSI "AlSi" #define SMB2_CREATE_TAG_TWRP "TWrp" #define SMB2_CREATE_TAG_QFID "QFid" +#define SMB2_CREATE_TAG_RQLS "RqLs" /* SMB2 Create ignore some more create_options */ #define SMB2_CREATE_OPTIONS_NOT_SUPPORTED_MASK (NTCREATEX_OPTIONS_TREE_CONNECTION | \ diff --git a/source4/torture/smb2/config.mk b/source4/torture/smb2/config.mk index 91ba9fc..70b1743 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 0000000..e675173 --- /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 3c9522e..64456aa 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"); -- Samba Shared Repository