[PATCHSET] smb2 durable handle torture tests and server fixes

Michael Adam obnox at samba.org
Thu Sep 26 00:49:44 MDT 2013


Hi,

attached find a patchset for master that extends and fixes
a couple of smb2 tests (durable handles). The server is
fixed as needed to pass these.

This is the result of running ms protocol tests and studying
the docs. Also the lease-versions of durable tests now
pass against windows... (*cough*).

A couple of the first patches have already been reviewed by
Metze.

Patches are also found in my master-smb2 branch:

http://gitweb.samba.org/?p=obnox/samba/samba-obnox.git;a=shortlog;h=refs/heads/master-smb2

review / comments / push welcome!

Cheers - Michael

-------------- next part --------------
From 796970af4c800b039629b63d2f18e0c4f6fe9f14 Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Mon, 23 Sep 2013 22:55:55 +0200
Subject: [PATCH 01/22] s4:torture:smb2: fix a comment in the
 durable-open.lock-oplock test

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source4/torture/smb2/durable_open.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source4/torture/smb2/durable_open.c b/source4/torture/smb2/durable_open.c
index 69bc5ed..77c23c1 100644
--- a/source4/torture/smb2/durable_open.c
+++ b/source4/torture/smb2/durable_open.c
@@ -1225,7 +1225,7 @@ static bool test_durable_open_lock_oplock(struct torture_context *tctx,
 	/* Clean slate */
 	smb2_util_unlink(tree, fname);
 
-	/* Create with lease */
+	/* Create with oplock */
 
 	smb2_oplock_create_share(&io, fname,
 				 smb2_util_share_access(""),
-- 
1.7.9.5


From e6124f82159b38e56a9b033559ae9e4ac9df0527 Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Wed, 25 Sep 2013 06:21:38 +0200
Subject: [PATCH 02/22] s4:torture:spoolss: use smb2_connect() instead of
 smb2_connet_ext()

in print_test_smbd: we don't need to pass the persistent file id.

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source4/torture/rpc/spoolss.c |   23 +++++++++++------------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/source4/torture/rpc/spoolss.c b/source4/torture/rpc/spoolss.c
index 4c84bc0..f2a44c9 100644
--- a/source4/torture/rpc/spoolss.c
+++ b/source4/torture/rpc/spoolss.c
@@ -8173,18 +8173,17 @@ static bool test_print_test_smbd(struct torture_context *tctx,
 	torture_comment(tctx, "Testing smbd job spooling\n");
 	lpcfg_smbcli_options(tctx->lp_ctx, &options);
 
-	status = smb2_connect_ext(mem_ctx,
-				  torture_setting_string(tctx, "host", NULL),
-				  lpcfg_smb_ports(tctx->lp_ctx),
-				  share,
-				  lpcfg_resolve_context(tctx->lp_ctx),
-				  credentials,
-				  0,
-				  &tree,
-				  tctx->ev,
-				  &options,
-				  lpcfg_socket_options(tctx->lp_ctx),
-				  lpcfg_gensec_settings(tctx, tctx->lp_ctx));
+	status = smb2_connect(mem_ctx,
+			      torture_setting_string(tctx, "host", NULL),
+			      lpcfg_smb_ports(tctx->lp_ctx),
+			      share,
+			      lpcfg_resolve_context(tctx->lp_ctx),
+			      credentials,
+			      &tree,
+			      tctx->ev,
+			      &options,
+			      lpcfg_socket_options(tctx->lp_ctx),
+			      lpcfg_gensec_settings(tctx, tctx->lp_ctx));
 	if (!NT_STATUS_IS_OK(status)) {
 		printf("Failed to connect to SMB2 printer %s - %s\n",
 		       share, nt_errstr(status));
-- 
1.7.9.5


From 8b5c7789c4fc39c482ea814b264484d9b76eb652 Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Wed, 25 Sep 2013 06:46:47 +0200
Subject: [PATCH 03/22] s4:libcli:smb2: add the client_guid to the
 smbcli_options

and initialize it in lpcfg_smbcli_options() instead of
in smb2_transport_init() as previously.

Having the client guid in the smbcli_options will allow
us to control them from callers later.

Signed-off-by: Michael Adam <obnox at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source4/libcli/raw/libcliraw.h  |    1 +
 source4/libcli/smb2/transport.c |    5 +----
 source4/param/loadparm.c        |    2 ++
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/source4/libcli/raw/libcliraw.h b/source4/libcli/raw/libcliraw.h
index a246ade..d2ff90d 100644
--- a/source4/libcli/raw/libcliraw.h
+++ b/source4/libcli/raw/libcliraw.h
@@ -100,6 +100,7 @@ struct smbcli_options {
 	uint16_t max_mux;
 	int request_timeout;
 	enum smb_signing_setting signing;
+	struct GUID client_guid;
 };
 
 /* this is the context for the client transport layer */
diff --git a/source4/libcli/smb2/transport.c b/source4/libcli/smb2/transport.c
index 2ad16a9..380c9cd 100644
--- a/source4/libcli/smb2/transport.c
+++ b/source4/libcli/smb2/transport.c
@@ -48,7 +48,6 @@ struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock,
 					   struct smbcli_options *options)
 {
 	struct smb2_transport *transport;
-	struct GUID client_guid;
 	uint32_t smb2_capabilities = 0;
 
 	transport = talloc_zero(parent_ctx, struct smb2_transport);
@@ -60,8 +59,6 @@ struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock,
 	TALLOC_FREE(sock->event.fde);
 	TALLOC_FREE(sock->event.te);
 
-	client_guid = GUID_random();
-
 	/* TODO: hand this in via the options? */
 	smb2_capabilities = SMB2_CAP_ALL;
 
@@ -70,7 +67,7 @@ struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock,
 					      sock->hostname,
 					      options->signing,
 					      0, /* smb1_capabilities */
-					      &client_guid,
+					      &options->client_guid,
 					      smb2_capabilities);
 	if (transport->conn == NULL) {
 		talloc_free(transport);
diff --git a/source4/param/loadparm.c b/source4/param/loadparm.c
index 3fbf852..900db7d 100644
--- a/source4/param/loadparm.c
+++ b/source4/param/loadparm.c
@@ -29,6 +29,7 @@
 #include "includes.h"
 #include "lib/param/param.h"
 #include "libcli/raw/libcliraw.h"
+#include "librpc/ndr/libndr.h"
 
 void lpcfg_smbcli_options(struct loadparm_context *lp_ctx,
 			 struct smbcli_options *options)
@@ -43,6 +44,7 @@ void lpcfg_smbcli_options(struct loadparm_context *lp_ctx,
 	options->unicode = lpcfg_unicode(lp_ctx);
 	options->use_oplocks = true;
 	options->use_level2_oplocks = true;
+	options->client_guid = GUID_random();
 }
 
 void lpcfg_smbcli_session_options(struct loadparm_context *lp_ctx,
-- 
1.7.9.5


From 660f9b0aa012ab8acd037a5a57375c68c7c7e35d Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Wed, 25 Sep 2013 06:57:23 +0200
Subject: [PATCH 04/22] s4:libcli:smb2: add the smb2_capabilities to the
 smbcli_options

and initialize them in lpcfg_smbcli_options() instead of
in smb2_transport_init() as previously.

This will allow us to control them from callers later.

Signed-off-by: Michael Adam <obnox at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source4/libcli/raw/libcliraw.h  |    1 +
 source4/libcli/smb2/transport.c |    5 +----
 source4/param/loadparm.c        |    1 +
 3 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/source4/libcli/raw/libcliraw.h b/source4/libcli/raw/libcliraw.h
index d2ff90d..95e6943 100644
--- a/source4/libcli/raw/libcliraw.h
+++ b/source4/libcli/raw/libcliraw.h
@@ -100,6 +100,7 @@ struct smbcli_options {
 	uint16_t max_mux;
 	int request_timeout;
 	enum smb_signing_setting signing;
+	uint32_t smb2_capabilities;
 	struct GUID client_guid;
 };
 
diff --git a/source4/libcli/smb2/transport.c b/source4/libcli/smb2/transport.c
index 380c9cd..b4a6c94 100644
--- a/source4/libcli/smb2/transport.c
+++ b/source4/libcli/smb2/transport.c
@@ -59,16 +59,13 @@ struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock,
 	TALLOC_FREE(sock->event.fde);
 	TALLOC_FREE(sock->event.te);
 
-	/* TODO: hand this in via the options? */
-	smb2_capabilities = SMB2_CAP_ALL;
-
 	transport->conn = smbXcli_conn_create(transport,
 					      sock->sock->fd,
 					      sock->hostname,
 					      options->signing,
 					      0, /* smb1_capabilities */
 					      &options->client_guid,
-					      smb2_capabilities);
+					      options->smb2_capabilities);
 	if (transport->conn == NULL) {
 		talloc_free(transport);
 		return NULL;
diff --git a/source4/param/loadparm.c b/source4/param/loadparm.c
index 900db7d..5f4f418 100644
--- a/source4/param/loadparm.c
+++ b/source4/param/loadparm.c
@@ -44,6 +44,7 @@ void lpcfg_smbcli_options(struct loadparm_context *lp_ctx,
 	options->unicode = lpcfg_unicode(lp_ctx);
 	options->use_oplocks = true;
 	options->use_level2_oplocks = true;
+	options->smb2_capabilities = SMB2_CAP_ALL;
 	options->client_guid = GUID_random();
 }
 
-- 
1.7.9.5


From 554a3f2b9202a58583a6ed9036a550d5931b92d3 Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Wed, 25 Sep 2013 07:16:39 +0200
Subject: [PATCH 05/22] s4:libcli:smb2: make smbcli_options argument to
 smb2_connect_(send|ext) const

Signed-off-by: Michael Adam <obnox at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source4/libcli/smb2/connect.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source4/libcli/smb2/connect.c b/source4/libcli/smb2/connect.c
index 5cdf258..18977d8 100644
--- a/source4/libcli/smb2/connect.c
+++ b/source4/libcli/smb2/connect.c
@@ -64,7 +64,7 @@ struct tevent_req *smb2_connect_send(TALLOC_CTX *mem_ctx,
 				     struct resolve_context *resolve_ctx,
 				     struct cli_credentials *credentials,
 				     uint64_t previous_session_id,
-				     struct smbcli_options *options,
+				     const struct smbcli_options *options,
 				     const char *socket_options,
 				     struct gensec_settings *gensec_settings)
 {
@@ -284,7 +284,7 @@ NTSTATUS smb2_connect_ext(TALLOC_CTX *mem_ctx,
 			  uint64_t previous_session_id,
 			  struct smb2_tree **tree,
 			  struct tevent_context *ev,
-			  struct smbcli_options *options,
+			  const struct smbcli_options *options,
 			  const char *socket_options,
 			  struct gensec_settings *gensec_settings)
 {
-- 
1.7.9.5


From 8c166209ff4f307941543d8ebeadb15bca133b9d Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Wed, 25 Sep 2013 07:20:30 +0200
Subject: [PATCH 06/22] s4:torture:smb2: add smbcli_options argument to
 torture_smb2_connection_ext()

Signed-off-by: Michael Adam <obnox at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source4/torture/smb2/durable_open.c |   43 +++++++++++++++++++++++++++++------
 source4/torture/smb2/session.c      |    5 +++-
 source4/torture/smb2/util.c         |   11 +++++----
 3 files changed, 46 insertions(+), 13 deletions(-)

diff --git a/source4/torture/smb2/durable_open.c b/source4/torture/smb2/durable_open.c
index 77c23c1..f4822f6 100644
--- a/source4/torture/smb2/durable_open.c
+++ b/source4/torture/smb2/durable_open.c
@@ -497,6 +497,9 @@ static bool test_durable_open_reopen2a(struct torture_context *tctx,
 	struct smb2_create io1, io2;
 	uint64_t previous_session_id;
 	bool ret = true;
+	struct smbcli_options options;
+
+	options = tree->session->transport->options;
 
 	/* Choose a random name in case the state is left a little funky. */
 	snprintf(fname, 256, "durable_open_reopen2_%s.dat",
@@ -522,7 +525,9 @@ static bool test_durable_open_reopen2a(struct torture_context *tctx,
 	talloc_free(tree);
 	tree = NULL;
 
-	if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
+	if (!torture_smb2_connection_ext(tctx, previous_session_id,
+					 &options, &tree))
+	{
 		torture_warning(tctx, "couldn't reconnect, bailing\n");
 		ret = false;
 		goto done;
@@ -816,6 +821,9 @@ static bool test_durable_open_delete_on_close2(struct torture_context *tctx,
 	uint8_t b = 0;
 	uint64_t previous_session_id;
 	uint64_t alloc_size_step;
+	struct smbcli_options options;
+
+	options = tree->session->transport->options;
 
 	/* Choose a random name in case the state is left a little funky. */
 	snprintf(fname, 256, "durable_open_delete_on_close2_%s.dat",
@@ -845,7 +853,9 @@ static bool test_durable_open_delete_on_close2(struct torture_context *tctx,
 	/* disconnect, leaving the durable handle in place */
 	TALLOC_FREE(tree);
 
-	if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
+	if (!torture_smb2_connection_ext(tctx, previous_session_id,
+					 &options, &tree))
+	{
 		torture_warning(tctx, "could not reconnect, bailing\n");
 		ret = false;
 		goto done;
@@ -929,6 +939,9 @@ static bool test_durable_open_file_position(struct torture_context *tctx,
 	bool ret = true;
 	uint64_t pos;
 	uint64_t previous_session_id;
+	struct smbcli_options options;
+
+	options = tree->session->transport->options;
 
 	smb2_util_unlink(tree, fname);
 
@@ -978,7 +991,9 @@ static bool test_durable_open_file_position(struct torture_context *tctx,
 	tree = NULL;
 
 	/* do a session reconnect */
-	if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
+	if (!torture_smb2_connection_ext(tctx, previous_session_id,
+					 &options, &tree))
+	{
 		torture_warning(tctx, "couldn't reconnect, bailing\n");
 		ret = false;
 		goto done;
@@ -1580,6 +1595,9 @@ static bool test_durable_open_alloc_size(struct torture_context *tctx,
 	uint64_t alloc_size_step;
 	uint64_t initial_alloc_size = 0x100;
 	const uint8_t *b = NULL;
+	struct smbcli_options options;
+
+	options = tree->session->transport->options;
 
 	/* Choose a random name in case the state is left a little funky. */
 	snprintf(fname, 256, "durable_open_alloc_size_%s.dat",
@@ -1614,7 +1632,9 @@ static bool test_durable_open_alloc_size(struct torture_context *tctx,
 	talloc_free(tree);
 	tree = NULL;
 
-	if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
+	if (!torture_smb2_connection_ext(tctx, previous_session_id,
+					 &options, &tree))
+	{
 		torture_warning(tctx, "couldn't reconnect, bailing\n");
 		ret = false;
 		goto done;
@@ -1643,7 +1663,9 @@ static bool test_durable_open_alloc_size(struct torture_context *tctx,
 	talloc_free(tree);
 	tree = NULL;
 
-	if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
+	if (!torture_smb2_connection_ext(tctx, previous_session_id,
+					 &options, &tree))
+	{
 		torture_warning(tctx, "couldn't reconnect, bailing\n");
 		ret = false;
 		goto done;
@@ -1671,7 +1693,9 @@ static bool test_durable_open_alloc_size(struct torture_context *tctx,
 	talloc_free(tree);
 	tree = NULL;
 
-	if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
+	if (!torture_smb2_connection_ext(tctx, previous_session_id,
+					 &options, &tree))
+	{
 		torture_warning(tctx, "couldn't reconnect, bailing\n");
 		ret = false;
 		goto done;
@@ -1720,6 +1744,9 @@ static bool test_durable_open_read_only(struct torture_context *tctx,
 	uint64_t previous_session_id;
 	const uint8_t b = 0;
 	uint64_t alloc_size = 0;
+	struct smbcli_options options;
+
+	options = tree->session->transport->options;
 
 	/* Choose a random name in case the state is left a little funky. */
 	snprintf(fname, 256, "durable_open_initial_alloc_%s.dat",
@@ -1751,7 +1778,9 @@ static bool test_durable_open_read_only(struct torture_context *tctx,
 	talloc_free(tree);
 	tree = NULL;
 
-	if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
+	if (!torture_smb2_connection_ext(tctx, previous_session_id,
+					 &options, &tree))
+	{
 		torture_warning(tctx, "couldn't reconnect, bailing\n");
 		ret = false;
 		goto done;
diff --git a/source4/torture/smb2/session.c b/source4/torture/smb2/session.c
index 3d72433..d076bb6 100644
--- a/source4/torture/smb2/session.c
+++ b/source4/torture/smb2/session.c
@@ -94,7 +94,10 @@ bool test_session_reconnect1(struct torture_context *tctx, struct smb2_tree *tre
 	/* disconnect, reconnect and then do durable reopen */
 	previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
 
-	if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree2)) {
+	if (!torture_smb2_connection_ext(tctx, previous_session_id,
+					 &tree->session->transport->options,
+					 &tree2))
+	{
 		torture_warning(tctx, "session reconnect failed\n");
 		ret = false;
 		goto done;
diff --git a/source4/torture/smb2/util.c b/source4/torture/smb2/util.c
index 4ffcfea..f23708d 100644
--- a/source4/torture/smb2/util.c
+++ b/source4/torture/smb2/util.c
@@ -347,15 +347,13 @@ bool torture_smb2_session_setup(struct torture_context *tctx,
 */
 bool torture_smb2_connection_ext(struct torture_context *tctx,
 				 uint64_t previous_session_id,
+				 const struct smbcli_options *options,
 				 struct smb2_tree **tree)
 {
 	NTSTATUS status;
 	const char *host = torture_setting_string(tctx, "host", NULL);
 	const char *share = torture_setting_string(tctx, "share", NULL);
 	struct cli_credentials *credentials = cmdline_credentials;
-	struct smbcli_options options;
-
-	lpcfg_smbcli_options(tctx->lp_ctx, &options);
 
 	status = smb2_connect_ext(tctx,
 				  host,
@@ -366,7 +364,7 @@ bool torture_smb2_connection_ext(struct torture_context *tctx,
 				  previous_session_id,
 				  tree,
 				  tctx->ev,
-				  &options,
+				  options,
 				  lpcfg_socket_options(tctx->lp_ctx),
 				  lpcfg_gensec_settings(tctx, tctx->lp_ctx)
 				  );
@@ -381,8 +379,11 @@ bool torture_smb2_connection_ext(struct torture_context *tctx,
 bool torture_smb2_connection(struct torture_context *tctx, struct smb2_tree **tree)
 {
 	bool ret;
+	struct smbcli_options options;
+
+	lpcfg_smbcli_options(tctx->lp_ctx, &options);
 
-	ret = torture_smb2_connection_ext(tctx, 0, tree);
+	ret = torture_smb2_connection_ext(tctx, 0, &options, tree);
 
 	return ret;
 }
-- 
1.7.9.5


From 42a31a27d18fb4aeb02c4a3eed5ccd6cf6539419 Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Wed, 25 Sep 2013 01:25:49 +0200
Subject: [PATCH 07/22] s4:torture:smb2: fix durable-open lease tests to pass
 against windows.

Fix is: reconnect with same client-guid as on the first connection.

Signed-off-by: Michael Adam <obnox at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source4/torture/smb2/durable_open.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/source4/torture/smb2/durable_open.c b/source4/torture/smb2/durable_open.c
index f4822f6..3bc012e 100644
--- a/source4/torture/smb2/durable_open.c
+++ b/source4/torture/smb2/durable_open.c
@@ -1316,6 +1316,9 @@ static bool test_durable_open_lock_lease(struct torture_context *tctx,
 	bool ret = true;
 	uint64_t lease;
 	uint32_t caps;
+	struct smbcli_options options;
+
+	options = tree->session->transport->options;
 
 	caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
 	if (!(caps & SMB2_CAP_LEASING)) {
@@ -1367,7 +1370,7 @@ static bool test_durable_open_lock_lease(struct torture_context *tctx,
 	talloc_free(tree);
 	tree = NULL;
 
-	if (!torture_smb2_connection(tctx, &tree)) {
+	if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
 		torture_warning(tctx, "couldn't reconnect, bailing\n");
 		ret = false;
 		goto done;
@@ -1416,6 +1419,9 @@ static bool test_durable_open_open2_lease(struct torture_context *tctx,
 	bool ret = true;
 	uint64_t lease;
 	uint32_t caps;
+	struct smbcli_options options;
+
+	options = tree1->session->transport->options;
 
 	caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
 	if (!(caps & SMB2_CAP_LEASING)) {
@@ -1465,7 +1471,7 @@ static bool test_durable_open_open2_lease(struct torture_context *tctx,
 	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
 
 	/* Reconnect */
-	if (!torture_smb2_connection(tctx, &tree1)) {
+	if (!torture_smb2_connection_ext(tctx, 0, &options, &tree1)) {
 		torture_warning(tctx, "couldn't reconnect, bailing\n");
 		ret = false;
 		goto done;
-- 
1.7.9.5


From 050a6ad2afc09f393bf03ce9da47bb9a985eda4f Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Tue, 24 Sep 2013 23:08:24 +0200
Subject: [PATCH 08/22] s4:torture:smb2: add durable-open.reopen2-lease test

lease-variant of the reopen2 test

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source4/torture/smb2/durable_open.c |  240 +++++++++++++++++++++++++++++++++++
 1 file changed, 240 insertions(+)

diff --git a/source4/torture/smb2/durable_open.c b/source4/torture/smb2/durable_open.c
index 3bc012e..3232575 100644
--- a/source4/torture/smb2/durable_open.c
+++ b/source4/torture/smb2/durable_open.c
@@ -482,6 +482,245 @@ done:
 }
 
 /**
+ * lease variant of reopen2
+ * basic test for doing a durable open
+ * tcp disconnect, reconnect, do a durable reopen (succeeds)
+ */
+static bool test_durable_open_reopen2_lease(struct torture_context *tctx,
+					    struct smb2_tree *tree)
+{
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	char fname[256];
+	struct smb2_handle _h;
+	struct smb2_handle *h = NULL;
+	struct smb2_create io;
+	struct smb2_lease ls;
+	uint64_t lease_key;
+	bool ret = true;
+	struct smbcli_options options;
+	uint32_t caps;
+
+	caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
+	if (!(caps & SMB2_CAP_LEASING)) {
+		torture_skip(tctx, "leases are not supported");
+	}
+
+	options = tree->session->transport->options;
+
+	/* Choose a random name in case the state is left a little funky. */
+	snprintf(fname, 256, "durable_open_reopen2_%s.dat",
+		 generate_random_str(tctx, 8));
+
+	smb2_util_unlink(tree, fname);
+
+	lease_key = random();
+	smb2_lease_create(&io, &ls, false /* dir */, fname, lease_key,
+			  smb2_util_lease_state("RWH"));
+	io.in.durable_open = true;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	_h = io.out.file.handle;
+	h = &_h;
+	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+
+	CHECK_VAL(io.out.durable_open, true);
+	CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
+	CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
+	CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
+	CHECK_VAL(io.out.lease_response.lease_state,
+		  smb2_util_lease_state("RWH"));
+	CHECK_VAL(io.out.lease_response.lease_flags, 0);
+	CHECK_VAL(io.out.lease_response.lease_duration, 0);
+
+	/* disconnect, reconnect and then do durable reopen */
+	talloc_free(tree);
+	tree = NULL;
+
+	if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
+		torture_warning(tctx, "couldn't reconnect, bailing\n");
+		ret = false;
+		goto done;
+	}
+
+
+	/* a few failure tests: */
+
+	/*
+	 * several attempts without lease attached:
+	 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
+	 * irrespective of file name provided
+	 */
+
+	ZERO_STRUCT(io);
+	io.in.fname = "";
+	io.in.durable_handle = h;
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+	ZERO_STRUCT(io);
+	io.in.fname = "__non_existing_fname__";
+	io.in.durable_handle = h;
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_handle = h;
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+	/*
+	 * attempt with lease provided, but
+	 * with a changed lease key. => fails
+	 */
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_open = false;
+	io.in.durable_handle = h;
+	io.in.lease_request = &ls;
+	io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
+	/* a wrong lease key lets the request fail */
+	ls.lease_key.data[0]++;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+	/* restore the correct lease key */
+	ls.lease_key.data[0]--;
+
+	/*
+	 * this last failing attempt is almost correct:
+	 * only problem is: we use the wrong filename...
+	 * Note that this gives INVALID_PARAMETER.
+	 * This is different from oplocks!
+	 */
+	ZERO_STRUCT(io);
+	io.in.fname = "__non_existing_fname__";
+	io.in.durable_open = false;
+	io.in.durable_handle = h;
+	io.in.lease_request = &ls;
+	io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+	/*
+	 * Now for a succeeding reconnect:
+	 */
+
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_open = false;
+	io.in.durable_handle = h;
+	io.in.lease_request = &ls;
+	io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
+
+	/* the requested lease state is irrelevant */
+	ls.lease_state = smb2_util_lease_state("");
+
+	h = NULL;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.durable_open, false);
+	CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
+	CHECK_VAL(io.out.persistent_open, false);
+	CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
+	CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
+	CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
+	CHECK_VAL(io.out.lease_response.lease_state,
+		  smb2_util_lease_state("RWH"));
+	CHECK_VAL(io.out.lease_response.lease_flags, 0);
+	CHECK_VAL(io.out.lease_response.lease_duration, 0);
+	_h = io.out.file.handle;
+	h = &_h;
+
+	/* disconnect one more time */
+	talloc_free(tree);
+	tree = NULL;
+
+	if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
+		torture_warning(tctx, "couldn't reconnect, bailing\n");
+		ret = false;
+		goto done;
+	}
+
+	/*
+	 * demonstrate that various parameters are ignored
+	 * in the reconnect
+	 */
+
+	ZERO_STRUCT(io);
+	/*
+	 * These are completely ignored by the server
+	 */
+	io.in.security_flags = 0x78;
+	io.in.oplock_level = 0x78;
+	io.in.impersonation_level = 0x12345678;
+	io.in.create_flags = 0x12345678;
+	io.in.reserved = 0x12345678;
+	io.in.desired_access = 0x12345678;
+	io.in.file_attributes = 0x12345678;
+	io.in.share_access = 0x12345678;
+	io.in.create_disposition = 0x12345678;
+	io.in.create_options = 0x12345678;
+
+	/*
+	 * only these are checked:
+	 * - io.in.fname
+	 * - io.in.durable_handle,
+	 * - io.in.lease_request->lease_key
+	 */
+
+	io.in.fname = fname;
+	io.in.durable_open_v2 = false;
+	io.in.durable_handle_v2 = h;
+	io.in.lease_request = &ls;
+
+	/* the requested lease state is irrelevant */
+	ls.lease_state = smb2_util_lease_state("");
+
+	h = NULL;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.durable_open, false);
+	CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
+	CHECK_VAL(io.out.persistent_open, false);
+	CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
+	CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
+	CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
+	CHECK_VAL(io.out.lease_response.lease_state,
+		  smb2_util_lease_state("RWH"));
+	CHECK_VAL(io.out.lease_response.lease_flags, 0);
+	CHECK_VAL(io.out.lease_response.lease_duration, 0);
+
+	_h = io.out.file.handle;
+	h = &_h;
+
+done:
+	if (tree != NULL) {
+		if (h != NULL) {
+			smb2_util_close(tree, *h);
+		}
+
+		smb2_util_unlink(tree, fname);
+
+		talloc_free(tree);
+	}
+
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
+/**
  * basic test for doing a durable open
  * tcp disconnect, reconnect with a session reconnect and
  * do a durable reopen (succeeds)
@@ -1890,6 +2129,7 @@ struct torture_suite *torture_smb2_durable_open_init(void)
 	torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease);
 	torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
 	torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
+	torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_open_reopen2_lease);
 	torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
 	torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
 	torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
-- 
1.7.9.5


From 744a4ac04a04401cee3d672d12eafa28bc569c9d Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Tue, 24 Sep 2013 23:20:39 +0200
Subject: [PATCH 09/22] s4:torture:smb2: add durable-v2-open.reopen2-lease

lease v1 variant of the reopen2 test

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source4/torture/smb2/durable_v2_open.c |  249 ++++++++++++++++++++++++++++++++
 1 file changed, 249 insertions(+)

diff --git a/source4/torture/smb2/durable_v2_open.c b/source4/torture/smb2/durable_v2_open.c
index 3d15ddf..584b787 100644
--- a/source4/torture/smb2/durable_v2_open.c
+++ b/source4/torture/smb2/durable_v2_open.c
@@ -585,6 +585,254 @@ done:
 }
 
 /**
+ * lease variant of reopen2
+ * basic test for doing a durable open
+ * tcp disconnect, reconnect, do a durable reopen (succeeds)
+ */
+bool test_durable_v2_open_reopen2_lease(struct torture_context *tctx,
+					struct smb2_tree *tree)
+{
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	char fname[256];
+	struct smb2_handle _h;
+	struct smb2_handle *h = NULL;
+	struct smb2_create io;
+	struct GUID create_guid = GUID_random();
+	struct smb2_lease ls;
+	uint64_t lease_key;
+	bool ret = true;
+	struct smbcli_options options;
+	uint32_t caps;
+
+	caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
+	if (!(caps & SMB2_CAP_LEASING)) {
+		torture_skip(tctx, "leases are not supported");
+	}
+
+	options = tree->session->transport->options;
+
+	/* Choose a random name in case the state is left a little funky. */
+	snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
+		 generate_random_str(tctx, 8));
+
+	smb2_util_unlink(tree, fname);
+
+	lease_key = random();
+	smb2_lease_create(&io, &ls, false /* dir */, fname,
+			  lease_key, smb2_util_lease_state("RWH"));
+	io.in.durable_open = false;
+	io.in.durable_open_v2 = true;
+	io.in.persistent_open = false;
+	io.in.create_guid = create_guid;
+	io.in.timeout = UINT32_MAX;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	_h = io.out.file.handle;
+	h = &_h;
+	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.durable_open, false);
+	CHECK_VAL(io.out.durable_open_v2, true);
+	CHECK_VAL(io.out.persistent_open, false);
+	CHECK_VAL(io.out.timeout, io.in.timeout);
+	CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
+	CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
+	CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
+	CHECK_VAL(io.out.lease_response.lease_state,
+		  smb2_util_lease_state("RWH"));
+	CHECK_VAL(io.out.lease_response.lease_flags, 0);
+	CHECK_VAL(io.out.lease_response.lease_duration, 0);
+
+	/* disconnect, reconnect and then do durable reopen */
+	talloc_free(tree);
+	tree = NULL;
+
+	if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
+		torture_warning(tctx, "couldn't reconnect, bailing\n");
+		ret = false;
+		goto done;
+	}
+
+	/* a few failure tests: */
+
+	/*
+	 * several attempts without lease attached:
+	 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
+	 * irrespective of file name provided
+	 */
+
+	ZERO_STRUCT(io);
+	io.in.fname = "";
+	io.in.durable_handle_v2 = h;
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+	ZERO_STRUCT(io);
+	io.in.fname = "__non_existing_fname__";
+	io.in.durable_handle_v2 = h;
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_handle_v2 = h;
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+	/*
+	 * attempt with lease provided, but
+	 * with a changed lease key. => fails
+	 */
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_open_v2 = false;
+	io.in.durable_handle_v2 = h;
+	io.in.create_guid = create_guid;
+	io.in.lease_request = &ls;
+	io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
+	/* a wrong lease key lets the request fail */
+	ls.lease_key.data[0]++;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+	/* restore the correct lease key */
+	ls.lease_key.data[0]--;
+
+	/*
+	 * this last failing attempt is almost correct:
+	 * only problem is: we use the wrong filename...
+	 * Note that this gives INVALID_PARAMETER.
+	 * This is different from oplocks!
+	 */
+	ZERO_STRUCT(io);
+	io.in.fname = "__non_existing_fname__";
+	io.in.durable_open_v2 = false;
+	io.in.durable_handle_v2 = h;
+	io.in.create_guid = create_guid;
+	io.in.lease_request = &ls;
+	io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+	/*
+	 * Now for a succeeding reconnect:
+	 */
+
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_open_v2 = false;
+	io.in.durable_handle_v2 = h;
+	io.in.create_guid = create_guid;
+	io.in.lease_request = &ls;
+	io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
+
+	/* the requested lease state is irrelevant */
+	ls.lease_state = smb2_util_lease_state("");
+
+	h = NULL;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.durable_open, false);
+	CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
+	CHECK_VAL(io.out.persistent_open, false);
+	CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
+	CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
+	CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
+	CHECK_VAL(io.out.lease_response.lease_state,
+		  smb2_util_lease_state("RWH"));
+	CHECK_VAL(io.out.lease_response.lease_flags, 0);
+	CHECK_VAL(io.out.lease_response.lease_duration, 0);
+	_h = io.out.file.handle;
+	h = &_h;
+
+	/* disconnect one more time */
+	talloc_free(tree);
+	tree = NULL;
+
+	if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
+		torture_warning(tctx, "couldn't reconnect, bailing\n");
+		ret = false;
+		goto done;
+	}
+
+	/*
+	 * demonstrate that various parameters are ignored
+	 * in the reconnect
+	 */
+
+	ZERO_STRUCT(io);
+	/*
+	 * These are completely ignored by the server
+	 */
+	io.in.security_flags = 0x78;
+	io.in.oplock_level = 0x78;
+	io.in.impersonation_level = 0x12345678;
+	io.in.create_flags = 0x12345678;
+	io.in.reserved = 0x12345678;
+	io.in.desired_access = 0x12345678;
+	io.in.file_attributes = 0x12345678;
+	io.in.share_access = 0x12345678;
+	io.in.create_disposition = 0x12345678;
+	io.in.create_options = 0x12345678;
+
+	/*
+	 * only these are checked:
+	 * - io.in.fname
+	 * - io.in.durable_handle_v2,
+	 * - io.in.create_guid
+	 * - io.in.lease_request->lease_key
+	 */
+
+	io.in.fname = fname;
+	io.in.durable_open_v2 = false;
+	io.in.durable_handle_v2 = h;
+	io.in.create_guid = create_guid;
+	io.in.lease_request = &ls;
+
+	/* the requested lease state is irrelevant */
+	ls.lease_state = smb2_util_lease_state("");
+
+	h = NULL;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.durable_open, false);
+	CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
+	CHECK_VAL(io.out.persistent_open, false);
+	CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
+	CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
+	CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
+	CHECK_VAL(io.out.lease_response.lease_state,
+		  smb2_util_lease_state("RWH"));
+	CHECK_VAL(io.out.lease_response.lease_flags, 0);
+	CHECK_VAL(io.out.lease_response.lease_duration, 0);
+
+	_h = io.out.file.handle;
+	h = &_h;
+
+done:
+	if (h != NULL) {
+		smb2_util_close(tree, *h);
+	}
+
+	smb2_util_unlink(tree, fname);
+
+	talloc_free(tree);
+
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
+/**
  * Test durable request / reconnect with AppInstanceId
  */
 bool test_durable_v2_open_app_instance(struct torture_context *tctx,
@@ -863,6 +1111,7 @@ struct torture_suite *torture_smb2_durable_v2_open_init(void)
 	torture_suite_add_1smb2_test(suite, "open-lease", test_durable_v2_open_lease);
 	torture_suite_add_1smb2_test(suite, "reopen1", test_durable_v2_open_reopen1);
 	torture_suite_add_1smb2_test(suite, "reopen2", test_durable_v2_open_reopen2);
+	torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_v2_open_reopen2_lease);
 	torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance);
 	torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock);
 	torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease);
-- 
1.7.9.5


From 4fb8a781bb39cb67cafd5b70b155eb1035e73236 Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Tue, 24 Sep 2013 23:09:18 +0200
Subject: [PATCH 10/22] s4:torture:smb2: add smb2_lease_v2_create() wrapper to
 smb2_lease_v2_create_share()

that sets share all. similar to smb2_lease_create()

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source4/torture/smb2/util.c |   16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/source4/torture/smb2/util.c b/source4/torture/smb2/util.c
index f23708d..97761d7 100644
--- a/source4/torture/smb2/util.c
+++ b/source4/torture/smb2/util.c
@@ -758,6 +758,22 @@ void smb2_lease_v2_create_share(struct smb2_create *io,
 	}
 }
 
+void smb2_lease_v2_create(struct smb2_create *io,
+			  struct smb2_lease *ls,
+			  bool dir,
+			  const char *name,
+			  uint64_t leasekey,
+			  const uint64_t *parentleasekey,
+			  uint32_t leasestate,
+			  uint16_t lease_epoch)
+{
+	smb2_lease_v2_create_share(io, ls, dir, name,
+				   smb2_util_share_access("RWD"),
+				   leasekey, parentleasekey,
+				   leasestate, lease_epoch);
+}
+
+
 void smb2_oplock_create_share(struct smb2_create *io, const char *name,
 			      uint32_t share_access, uint8_t oplock)
 {
-- 
1.7.9.5


From f79cadf11e3b7edfddd893c55249a77508f3dea8 Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Tue, 24 Sep 2013 23:21:37 +0200
Subject: [PATCH 11/22] s4:torture:smb2: add durable-v2-open.reopen2-lease-v2

lease v2 variant of the reopen2 test.
Test various success and failure cases.

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source4/torture/smb2/durable_v2_open.c |  247 ++++++++++++++++++++++++++++++++
 1 file changed, 247 insertions(+)

diff --git a/source4/torture/smb2/durable_v2_open.c b/source4/torture/smb2/durable_v2_open.c
index 584b787..2be9ce8 100644
--- a/source4/torture/smb2/durable_v2_open.c
+++ b/source4/torture/smb2/durable_v2_open.c
@@ -833,6 +833,252 @@ done:
 }
 
 /**
+ * lease_v2 variant of reopen2
+ * basic test for doing a durable open
+ * tcp disconnect, reconnect, do a durable reopen (succeeds)
+ */
+bool test_durable_v2_open_reopen2_lease_v2(struct torture_context *tctx,
+					   struct smb2_tree *tree)
+{
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	char fname[256];
+	struct smb2_handle _h;
+	struct smb2_handle *h = NULL;
+	struct smb2_create io;
+	struct GUID create_guid = GUID_random();
+	struct smb2_lease ls;
+	uint64_t lease_key;
+	bool ret = true;
+	struct smbcli_options options;
+	uint32_t caps;
+
+	caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
+	if (!(caps & SMB2_CAP_LEASING)) {
+		torture_skip(tctx, "leases are not supported");
+	}
+
+	options = tree->session->transport->options;
+
+	/* Choose a random name in case the state is left a little funky. */
+	snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
+		 generate_random_str(tctx, 8));
+
+	smb2_util_unlink(tree, fname);
+
+	lease_key = random();
+	smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
+			     lease_key, 0, /* parent lease key */
+			     smb2_util_lease_state("RWH"), 0 /* lease epoch */);
+	io.in.durable_open = false;
+	io.in.durable_open_v2 = true;
+	io.in.persistent_open = false;
+	io.in.create_guid = create_guid;
+	io.in.timeout = UINT32_MAX;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	_h = io.out.file.handle;
+	h = &_h;
+	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.durable_open, false);
+	CHECK_VAL(io.out.durable_open_v2, true);
+	CHECK_VAL(io.out.persistent_open, false);
+	CHECK_VAL(io.out.timeout, io.in.timeout);
+	CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
+	CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
+	CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
+
+	/* disconnect, reconnect and then do durable reopen */
+	talloc_free(tree);
+	tree = NULL;
+
+	if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
+		torture_warning(tctx, "couldn't reconnect, bailing\n");
+		ret = false;
+		goto done;
+	}
+
+	/* a few failure tests: */
+
+	/*
+	 * several attempts without lease attached:
+	 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
+	 * irrespective of file name provided
+	 */
+
+	ZERO_STRUCT(io);
+	io.in.fname = "";
+	io.in.durable_handle_v2 = h;
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+	ZERO_STRUCT(io);
+	io.in.fname = "__non_existing_fname__";
+	io.in.durable_handle_v2 = h;
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_handle_v2 = h;
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+	/*
+	 * attempt with lease provided, but
+	 * with a changed lease key. => fails
+	 */
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_open_v2 = false;
+	io.in.durable_handle_v2 = h;
+	io.in.create_guid = create_guid;
+	io.in.lease_request_v2 = &ls;
+	io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
+	/* a wrong lease key lets the request fail */
+	ls.lease_key.data[0]++;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+	/* restore the correct lease key */
+	ls.lease_key.data[0]--;
+
+
+	/*
+	 * this last failing attempt is almost correct:
+	 * only problem is: we use the wrong filename...
+	 * Note that this gives INVALID_PARAMETER.
+	 * This is different from oplocks!
+	 */
+	ZERO_STRUCT(io);
+	io.in.fname = "__non_existing_fname__";
+	io.in.durable_open_v2 = false;
+	io.in.durable_handle_v2 = h;
+	io.in.create_guid = create_guid;
+	io.in.lease_request_v2 = &ls;
+	io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+	/*
+	 * Now for a succeeding reconnect:
+	 */
+
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_open_v2 = false;
+	io.in.durable_handle_v2 = h;
+	io.in.create_guid = create_guid;
+	io.in.lease_request_v2 = &ls;
+	io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
+
+	/* the requested lease state is irrelevant */
+	ls.lease_state = smb2_util_lease_state("");
+
+	h = NULL;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.durable_open, false);
+	CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
+	CHECK_VAL(io.out.persistent_open, false);
+	CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
+	CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
+	CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
+	CHECK_VAL(io.out.lease_response_v2.lease_state,
+		  smb2_util_lease_state("RWH"));
+	CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
+	CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
+	_h = io.out.file.handle;
+	h = &_h;
+
+	/* disconnect one more time */
+	talloc_free(tree);
+	tree = NULL;
+
+	if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
+		torture_warning(tctx, "couldn't reconnect, bailing\n");
+		ret = false;
+		goto done;
+	}
+
+	/*
+	 * demonstrate that various parameters are ignored
+	 * in the reconnect
+	 */
+
+	ZERO_STRUCT(io);
+	/*
+	 * These are completely ignored by the server
+	 */
+	io.in.security_flags = 0x78;
+	io.in.oplock_level = 0x78;
+	io.in.impersonation_level = 0x12345678;
+	io.in.create_flags = 0x12345678;
+	io.in.reserved = 0x12345678;
+	io.in.desired_access = 0x12345678;
+	io.in.file_attributes = 0x12345678;
+	io.in.share_access = 0x12345678;
+	io.in.create_disposition = 0x12345678;
+	io.in.create_options = 0x12345678;
+	io.in.fname = "__non_existing_fname__";
+
+	/*
+	 * only these are checked:
+	 * - io.in.fname
+	 * - io.in.durable_handle_v2,
+	 * - io.in.create_guid
+	 * - io.in.lease_request_v2->lease_key
+	 */
+
+	io.in.fname = fname;
+	io.in.durable_open_v2 = false;
+	io.in.durable_handle_v2 = h;
+	io.in.create_guid = create_guid;
+	io.in.lease_request_v2 = &ls;
+
+	/* the requested lease state is irrelevant */
+	ls.lease_state = smb2_util_lease_state("");
+
+	h = NULL;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.durable_open, false);
+	CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
+	CHECK_VAL(io.out.persistent_open, false);
+	CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
+	CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
+	CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
+	CHECK_VAL(io.out.lease_response_v2.lease_state,
+		  smb2_util_lease_state("RWH"));
+	CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
+	CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
+
+	_h = io.out.file.handle;
+	h = &_h;
+
+done:
+	if (h != NULL) {
+		smb2_util_close(tree, *h);
+	}
+
+	smb2_util_unlink(tree, fname);
+
+	talloc_free(tree);
+
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
+/**
  * Test durable request / reconnect with AppInstanceId
  */
 bool test_durable_v2_open_app_instance(struct torture_context *tctx,
@@ -1112,6 +1358,7 @@ struct torture_suite *torture_smb2_durable_v2_open_init(void)
 	torture_suite_add_1smb2_test(suite, "reopen1", test_durable_v2_open_reopen1);
 	torture_suite_add_1smb2_test(suite, "reopen2", test_durable_v2_open_reopen2);
 	torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_v2_open_reopen2_lease);
+	torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_v2_open_reopen2_lease_v2);
 	torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance);
 	torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock);
 	torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease);
-- 
1.7.9.5


From f81883b4f1b30fbe242b649647f48692607b980e Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Wed, 25 Sep 2013 18:31:10 +0200
Subject: [PATCH 12/22] s4:torture:smb2: add a durable-open.reopen-lease-v2
 test

like durable-open.reopen2-lease but with v2 lease requets

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source4/torture/smb2/durable_open.c |  241 +++++++++++++++++++++++++++++++++++
 1 file changed, 241 insertions(+)

diff --git a/source4/torture/smb2/durable_open.c b/source4/torture/smb2/durable_open.c
index 3232575..230a9ce 100644
--- a/source4/torture/smb2/durable_open.c
+++ b/source4/torture/smb2/durable_open.c
@@ -721,6 +721,246 @@ done:
 }
 
 /**
+ * lease v2 variant of reopen2
+ * basic test for doing a durable open
+ * tcp disconnect, reconnect, do a durable reopen (succeeds)
+ */
+static bool test_durable_open_reopen2_lease_v2(struct torture_context *tctx,
+					       struct smb2_tree *tree)
+{
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	char fname[256];
+	struct smb2_handle _h;
+	struct smb2_handle *h = NULL;
+	struct smb2_create io;
+	struct smb2_lease ls;
+	uint64_t lease_key;
+	bool ret = true;
+	struct smbcli_options options;
+	uint32_t caps;
+
+	caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
+	if (!(caps & SMB2_CAP_LEASING)) {
+		torture_skip(tctx, "leases are not supported");
+	}
+
+	options = tree->session->transport->options;
+
+	/* Choose a random name in case the state is left a little funky. */
+	snprintf(fname, 256, "durable_open_reopen2_%s.dat",
+		 generate_random_str(tctx, 8));
+
+	smb2_util_unlink(tree, fname);
+
+	lease_key = random();
+	smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
+			     lease_key, 0, /* parent lease key */
+			     smb2_util_lease_state("RWH"), 0 /* lease epoch */);
+	io.in.durable_open = true;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	_h = io.out.file.handle;
+	h = &_h;
+	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+
+	CHECK_VAL(io.out.durable_open, true);
+	CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
+	CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
+	CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
+	CHECK_VAL(io.out.lease_response_v2.lease_state,
+		  smb2_util_lease_state("RWH"));
+	CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
+	CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
+
+	/* disconnect, reconnect and then do durable reopen */
+	talloc_free(tree);
+	tree = NULL;
+
+	if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
+		torture_warning(tctx, "couldn't reconnect, bailing\n");
+		ret = false;
+		goto done;
+	}
+
+
+	/* a few failure tests: */
+
+	/*
+	 * several attempts without lease attached:
+	 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
+	 * irrespective of file name provided
+	 */
+
+	ZERO_STRUCT(io);
+	io.in.fname = "";
+	io.in.durable_handle = h;
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+	ZERO_STRUCT(io);
+	io.in.fname = "__non_existing_fname__";
+	io.in.durable_handle = h;
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_handle = h;
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+	/*
+	 * attempt with lease provided, but
+	 * with a changed lease key. => fails
+	 */
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_open = false;
+	io.in.durable_handle = h;
+	io.in.lease_request_v2 = &ls;
+	io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
+	/* a wrong lease key lets the request fail */
+	ls.lease_key.data[0]++;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+	/* restore the correct lease key */
+	ls.lease_key.data[0]--;
+
+	/*
+	 * this last failing attempt is almost correct:
+	 * only problem is: we use the wrong filename...
+	 * Note that this gives INVALID_PARAMETER.
+	 * This is different from oplocks!
+	 */
+	ZERO_STRUCT(io);
+	io.in.fname = "__non_existing_fname__";
+	io.in.durable_open = false;
+	io.in.durable_handle = h;
+	io.in.lease_request_v2 = &ls;
+	io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+	/*
+	 * Now for a succeeding reconnect:
+	 */
+
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_open = false;
+	io.in.durable_handle = h;
+	io.in.lease_request_v2 = &ls;
+	io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
+
+	/* the requested lease state is irrelevant */
+	ls.lease_state = smb2_util_lease_state("");
+
+	h = NULL;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.durable_open, false);
+	CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
+	CHECK_VAL(io.out.persistent_open, false);
+	CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
+	CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
+	CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
+	CHECK_VAL(io.out.lease_response_v2.lease_state,
+		  smb2_util_lease_state("RWH"));
+	CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
+	CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
+	_h = io.out.file.handle;
+	h = &_h;
+
+	/* disconnect one more time */
+	talloc_free(tree);
+	tree = NULL;
+
+	if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
+		torture_warning(tctx, "couldn't reconnect, bailing\n");
+		ret = false;
+		goto done;
+	}
+
+	/*
+	 * demonstrate that various parameters are ignored
+	 * in the reconnect
+	 */
+
+	ZERO_STRUCT(io);
+	/*
+	 * These are completely ignored by the server
+	 */
+	io.in.security_flags = 0x78;
+	io.in.oplock_level = 0x78;
+	io.in.impersonation_level = 0x12345678;
+	io.in.create_flags = 0x12345678;
+	io.in.reserved = 0x12345678;
+	io.in.desired_access = 0x12345678;
+	io.in.file_attributes = 0x12345678;
+	io.in.share_access = 0x12345678;
+	io.in.create_disposition = 0x12345678;
+	io.in.create_options = 0x12345678;
+
+	/*
+	 * only these are checked:
+	 * - io.in.fname
+	 * - io.in.durable_handle,
+	 * - io.in.lease_request->lease_key
+	 */
+
+	io.in.fname = fname;
+	io.in.durable_open_v2 = false;
+	io.in.durable_handle_v2 = h;
+	io.in.lease_request_v2 = &ls;
+
+	/* the requested lease state is irrelevant */
+	ls.lease_state = smb2_util_lease_state("");
+
+	h = NULL;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.durable_open, false);
+	CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
+	CHECK_VAL(io.out.persistent_open, false);
+	CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
+	CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
+	CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
+	CHECK_VAL(io.out.lease_response_v2.lease_state,
+		  smb2_util_lease_state("RWH"));
+	CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
+	CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
+
+	_h = io.out.file.handle;
+	h = &_h;
+
+done:
+	if (tree != NULL) {
+		if (h != NULL) {
+			smb2_util_close(tree, *h);
+		}
+
+		smb2_util_unlink(tree, fname);
+
+		talloc_free(tree);
+	}
+
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
+/**
  * basic test for doing a durable open
  * tcp disconnect, reconnect with a session reconnect and
  * do a durable reopen (succeeds)
@@ -2130,6 +2370,7 @@ struct torture_suite *torture_smb2_durable_open_init(void)
 	torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
 	torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
 	torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_open_reopen2_lease);
+	torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_open_reopen2_lease_v2);
 	torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
 	torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
 	torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
-- 
1.7.9.5


From 60fa34e3a6ba9df0d0565733bf3f0652a94c35c8 Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Wed, 25 Sep 2013 20:40:11 +0200
Subject: [PATCH 13/22] smbd:smb2_create: add comment about validity of check
 reconnect blob being only one

With leases this will not be true any more.

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source3/smbd/smb2_create.c |    4 ++++
 1 file changed, 4 insertions(+)

diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index 4f2edfc..93cccf8 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -470,6 +470,8 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 		if (in_context_blobs.num_blobs != 1) {
 			/*
 			 * DHNC should be the only one.
+			 * TODO: This is only true for the oplock case!
+			 * For leases, lease request is required additionally!
 			 */
 			tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
 			return tevent_req_post(req, ev);
@@ -486,6 +488,8 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 		if (in_context_blobs.num_blobs != 1) {
 			/*
 			 * DH2C should be the only one.
+			 * TODO: This is only true for the oplock case!
+			 * For leases, lease request is required additionally!
 			 */
 			tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
 			return tevent_req_post(req, ev);
-- 
1.7.9.5


From adb779d7486ca4654e9d317ecaa8176f6cd37ef3 Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Wed, 25 Sep 2013 23:18:56 +0200
Subject: [PATCH 14/22] smbd:smb2: fix error code when the header says the
 request is signed but we don't have a sesseion

I.e. when the request is a session setup.
We replied with ACCESS_DENIED, but windows expects USER_SESSION_DELETED

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source3/smbd/smb2_server.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c
index cf5e37e..1bebee1 100644
--- a/source3/smbd/smb2_server.c
+++ b/source3/smbd/smb2_server.c
@@ -1998,7 +1998,7 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
 
 		if (x == NULL) {
 			return smbd_smb2_request_error(
-				req, NT_STATUS_ACCESS_DENIED);
+				req, NT_STATUS_USER_SESSION_DELETED);
 		}
 
 		signing_key = x->global->channels[0].signing_key;
-- 
1.7.9.5


From f85202502742a052405191ee601fd4f1276bbf40 Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Wed, 25 Sep 2013 23:20:18 +0200
Subject: [PATCH 15/22] smbd:smb2: successfully answer a DHnC request when the
 initial create was DH2Q

I.e. the durable reconnect attempt is v1 while the original create was durable
v2 including the create guid.

Implement this by skipping the create_guid verification when
the reconnect request is v1.

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source3/smbd/globals.h      |    2 +-
 source3/smbd/smb2_create.c  |   11 +++++++----
 source3/smbd/smbXsrv_open.c |   13 +++++++++++--
 3 files changed, 19 insertions(+), 7 deletions(-)

diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h
index 9ea5e25..6beee59 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -457,7 +457,7 @@ NTSTATUS smb2srv_open_lookup(struct smbXsrv_connection *conn,
 NTSTATUS smb2srv_open_recreate(struct smbXsrv_connection *conn,
 			       struct auth_session_info *session_info,
 			       uint64_t persistent_id,
-			       struct GUID create_guid,
+			       const struct GUID *create_guid,
 			       NTTIME now,
 			       struct smbXsrv_open **_open);
 struct smbXsrv_open_global0;
diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index 93cccf8..fb9b56e 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -550,7 +550,8 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 		uint64_t allocation_size = 0;
 		struct smb2_create_blob *twrp = NULL;
 		struct smb2_create_blob *qfid = NULL;
-		struct GUID create_guid = GUID_zero();
+		struct GUID _create_guid = GUID_zero();
+		struct GUID *create_guid = NULL;
 		bool update_open = false;
 		bool durable_requested = false;
 		uint32_t durable_timeout_msec = 0;
@@ -668,10 +669,11 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 			create_guid_blob = data_blob_const(p + 16, 16);
 
 			status = GUID_from_ndr_blob(&create_guid_blob,
-						    &create_guid);
+						    &_create_guid);
 			if (tevent_req_nterror(req, status)) {
 				return tevent_req_post(req, ev);
 			}
+			create_guid = &_create_guid;
 			/*
 			 * we need to store the create_guid later
 			 */
@@ -706,10 +708,11 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 			create_guid_blob = data_blob_const(p + 16, 16);
 
 			status = GUID_from_ndr_blob(&create_guid_blob,
-						    &create_guid);
+						    &_create_guid);
 			if (tevent_req_nterror(req, status)) {
 				return tevent_req_post(req, ev);
 			}
+			create_guid = &_create_guid;
 
 			do_durable_reconnect = true;
 		}
@@ -940,7 +943,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 		}
 
 		if (update_open) {
-			op->global->create_guid = create_guid;
+			op->global->create_guid = _create_guid;
 
 			status = smbXsrv_open_update(op);
 			DEBUG(10, ("smb2_create_send: smbXsrv_open_update "
diff --git a/source3/smbd/smbXsrv_open.c b/source3/smbd/smbXsrv_open.c
index 27dd50c..542df00 100644
--- a/source3/smbd/smbXsrv_open.c
+++ b/source3/smbd/smbXsrv_open.c
@@ -1168,7 +1168,7 @@ NTSTATUS smb2srv_open_lookup(struct smbXsrv_connection *conn,
 NTSTATUS smb2srv_open_recreate(struct smbXsrv_connection *conn,
 			       struct auth_session_info *session_info,
 			       uint64_t persistent_id,
-			       struct GUID create_guid,
+			       const struct GUID *create_guid,
 			       NTTIME now,
 			       struct smbXsrv_open **_open)
 {
@@ -1181,6 +1181,7 @@ NTSTATUS smb2srv_open_recreate(struct smbXsrv_connection *conn,
 	uint64_t global_zeros = persistent_id & 0xFFFFFFFF00000000LLU;
 	NTSTATUS status;
 	struct security_token *current_token = NULL;
+	struct GUID zero_guid;
 
 	if (session_info == NULL) {
 		return NT_STATUS_INVALID_HANDLE;
@@ -1207,7 +1208,15 @@ NTSTATUS smb2srv_open_recreate(struct smbXsrv_connection *conn,
 		return status;
 	}
 
-	if (!GUID_equal(&op->global->create_guid, &create_guid)) {
+	/*
+	 * If the provided create_guid is NULL, this means that
+	 * the reconnect request was a v1 request. In that case
+	 * we should skipt the create GUID verification, since
+	 * it is valid to v1-reconnect a v2-opened handle.
+	 */
+	if ((create_guid != NULL) &&
+	    !GUID_equal(&op->global->create_guid, create_guid))
+	{
 		TALLOC_FREE(op);
 		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
 	}
-- 
1.7.9.5


From d4129cbd2592adaf9a4b5faf0677cef651679bd9 Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Thu, 26 Sep 2013 05:12:02 +0200
Subject: [PATCH 16/22] s4:torture:smb2: extend the durable-v2-open.reopen2
 test

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source4/torture/smb2/durable_v2_open.c |   48 +++++++++++++++++++++++++++++---
 1 file changed, 44 insertions(+), 4 deletions(-)

diff --git a/source4/torture/smb2/durable_v2_open.c b/source4/torture/smb2/durable_v2_open.c
index 2be9ce8..0b49300 100644
--- a/source4/torture/smb2/durable_v2_open.c
+++ b/source4/torture/smb2/durable_v2_open.c
@@ -479,6 +479,7 @@ bool test_durable_v2_open_reopen2(struct torture_context *tctx,
 	struct smb2_handle *h = NULL;
 	struct smb2_create io;
 	struct GUID create_guid = GUID_random();
+	struct GUID create_guid_invalid = GUID_random();
 	bool ret = true;
 
 	/* Choose a random name in case the state is left a little funky. */
@@ -507,9 +508,8 @@ bool test_durable_v2_open_reopen2(struct torture_context *tctx,
 	CHECK_VAL(io.out.persistent_open, false);
 	CHECK_VAL(io.out.timeout, io.in.timeout);
 
-	/* disconnect, reconnect and then do durable reopen */
-	talloc_free(tree);
-	tree = NULL;
+	/* disconnect, leaving the durable open */
+	TALLOC_FREE(tree);
 
 	if (!torture_smb2_connection(tctx, &tree)) {
 		torture_warning(tctx, "couldn't reconnect, bailing\n");
@@ -517,6 +517,10 @@ bool test_durable_v2_open_reopen2(struct torture_context *tctx,
 		goto done;
 	}
 
+	/*
+	 * first a few failure cases
+	 */
+
 	ZERO_STRUCT(io);
 	io.in.fname = "";
 	io.in.durable_handle_v2 = h;
@@ -535,10 +539,46 @@ bool test_durable_v2_open_reopen2(struct torture_context *tctx,
 	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
 
+	/* a non-zero but non-matching create_guid does not change it: */
 	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_handle_v2 = h;
+	io.in.create_guid = create_guid_invalid;
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
 	/*
-	 * These are completely ignored by the server
+	 * now success:
+	 * The important difference is that the create_guid is provided.
 	 */
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_open_v2 = false;
+	io.in.durable_handle_v2 = h;
+	io.in.create_guid = create_guid;
+	h = NULL;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.durable_open, false);
+	CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
+	CHECK_VAL(io.out.persistent_open, false);
+	CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
+	_h = io.out.file.handle;
+	h = &_h;
+
+	/* disconnect one more time */
+	TALLOC_FREE(tree);
+
+	if (!torture_smb2_connection(tctx, &tree)) {
+		torture_warning(tctx, "couldn't reconnect, bailing\n");
+		ret = false;
+		goto done;
+	}
+
+	ZERO_STRUCT(io);
+	/* These are completely ignored by the server */
 	io.in.security_flags = 0x78;
 	io.in.oplock_level = 0x78;
 	io.in.impersonation_level = 0x12345678;
-- 
1.7.9.5


From f3b84b807f7744c86b23940543a1543fa83e4a36 Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Thu, 26 Sep 2013 05:35:19 +0200
Subject: [PATCH 17/22] s4:torture:smb2: extend the durable-open.reopen2 test

Add tests for:
- filename and many other things don't matter in reconnect
- additionally specified DHnQ request blob is ignored.

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source4/torture/smb2/durable_open.c |  101 ++++++++++++++++++++++++++++-------
 1 file changed, 83 insertions(+), 18 deletions(-)

diff --git a/source4/torture/smb2/durable_open.c b/source4/torture/smb2/durable_open.c
index 230a9ce..c4b1c88 100644
--- a/source4/torture/smb2/durable_open.c
+++ b/source4/torture/smb2/durable_open.c
@@ -420,7 +420,7 @@ static bool test_durable_open_reopen2(struct torture_context *tctx,
 	char fname[256];
 	struct smb2_handle _h;
 	struct smb2_handle *h = NULL;
-	struct smb2_create io1, io2;
+	struct smb2_create io;
 	bool ret = true;
 
 	/* Choose a random name in case the state is left a little funky. */
@@ -429,22 +429,21 @@ static bool test_durable_open_reopen2(struct torture_context *tctx,
 
 	smb2_util_unlink(tree, fname);
 
-	smb2_oplock_create_share(&io1, fname,
+	smb2_oplock_create_share(&io, fname,
 				 smb2_util_share_access(""),
 				 smb2_util_oplock_level("b"));
-	io1.in.durable_open = true;
+	io.in.durable_open = true;
 
-	status = smb2_create(tree, mem_ctx, &io1);
+	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OK);
-	_h = io1.out.file.handle;
+	_h = io.out.file.handle;
 	h = &_h;
-	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
-	CHECK_VAL(io1.out.durable_open, true);
-	CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
+	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.durable_open, true);
+	CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
 
-	/* disconnect, reconnect and then do durable reopen */
-	talloc_free(tree);
-	tree = NULL;
+	/* disconnect, leaving the durable in place */
+	TALLOC_FREE(tree);
 
 	if (!torture_smb2_connection(tctx, &tree)) {
 		torture_warning(tctx, "couldn't reconnect, bailing\n");
@@ -452,17 +451,83 @@ static bool test_durable_open_reopen2(struct torture_context *tctx,
 		goto done;
 	}
 
-	ZERO_STRUCT(io2);
+	ZERO_STRUCT(io);
 	/* the path name is ignored by the server */
-	io2.in.fname = "__non_existing_fname__";
-	io2.in.durable_handle = h;
+	io.in.fname = fname;
+	io.in.durable_handle = h; /* durable v1 reconnect request */
 	h = NULL;
 
-	status = smb2_create(tree, mem_ctx, &io2);
+	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OK);
-	CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
-	CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
-	_h = io2.out.file.handle;
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
+	_h = io.out.file.handle;
+	h = &_h;
+
+	/* disconnect again, leaving the durable in place */
+	TALLOC_FREE(tree);
+
+	if (!torture_smb2_connection(tctx, &tree)) {
+		torture_warning(tctx, "couldn't reconnect, bailing\n");
+		ret = false;
+		goto done;
+	}
+
+	/*
+	 * show that the filename and many other fields
+	 * are ignored. only the reconnect request blob
+	 * is important.
+	 */
+	ZERO_STRUCT(io);
+	/* the path name is ignored by the server */
+	io.in.security_flags = 0x78;
+	io.in.oplock_level = 0x78;
+	io.in.impersonation_level = 0x12345678;
+	io.in.create_flags = 0x12345678;
+	io.in.reserved = 0x12345678;
+	io.in.desired_access = 0x12345678;
+	io.in.file_attributes = 0x12345678;
+	io.in.share_access = 0x12345678;
+	io.in.create_disposition = 0x12345678;
+	io.in.create_options = 0x12345678;
+	io.in.fname = "__non_existing_fname__";
+	io.in.durable_handle = h; /* durable v1 reconnect request */
+	h = NULL;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
+	_h = io.out.file.handle;
+	h = &_h;
+
+	/* disconnect, leaving the durable in place */
+	TALLOC_FREE(tree);
+
+	if (!torture_smb2_connection(tctx, &tree)) {
+		torture_warning(tctx, "couldn't reconnect, bailing\n");
+		ret = false;
+		goto done;
+	}
+
+	/*
+	 * show that an additionally specified durable v1 request
+	 * is ignored by the server.
+	 * See MS-SMB2, 3.3.5.9.7
+	 * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context
+	 */
+	ZERO_STRUCT(io);
+	/* the path name is ignored by the server */
+	io.in.fname = fname;
+	io.in.durable_handle = h;  /* durable v1 reconnect request */
+	io.in.durable_open = true; /* durable v1 handle request */
+	h = NULL;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
+	_h = io.out.file.handle;
 	h = &_h;
 
 done:
-- 
1.7.9.5


From 0badbaae54a5dad1a9eee66b35f312b44edc2589 Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Thu, 26 Sep 2013 05:47:47 +0200
Subject: [PATCH 18/22] s4:torture:smb2: add durable-v2-open.create-blob

test various combinations of durable create and reconnect
request blobs, according to
MS-SMB2, 3.3.5.9.12:
"Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 Create Context"

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source4/torture/smb2/durable_v2_open.c |  108 ++++++++++++++++++++++++++++++++
 1 file changed, 108 insertions(+)

diff --git a/source4/torture/smb2/durable_v2_open.c b/source4/torture/smb2/durable_v2_open.c
index 0b49300..6f9d01e 100644
--- a/source4/torture/smb2/durable_v2_open.c
+++ b/source4/torture/smb2/durable_v2_open.c
@@ -83,6 +83,113 @@ static bool torture_oplock_handler(struct smb2_transport *transport,
 }
 
 /**
+ * testing various create blob combinations.
+ */
+bool test_durable_v2_open_create_blob(struct torture_context *tctx,
+				      struct smb2_tree *tree)
+{
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	char fname[256];
+	struct smb2_handle _h;
+	struct smb2_handle *h = NULL;
+	struct smb2_create io;
+	struct GUID create_guid = GUID_random();
+	bool ret = true;
+	struct smbcli_options options;
+
+	options = tree->session->transport->options;
+
+	/* Choose a random name in case the state is left a little funky. */
+	snprintf(fname, 256, "durable_v2_open_create_blob_%s.dat",
+		 generate_random_str(tctx, 8));
+
+	smb2_util_unlink(tree, fname);
+
+	smb2_oplock_create_share(&io, fname,
+				 smb2_util_share_access(""),
+				 smb2_util_oplock_level("b"));
+	io.in.durable_open = false;
+	io.in.durable_open_v2 = true;
+	io.in.persistent_open = false;
+	io.in.create_guid = create_guid;
+	io.in.timeout = UINT32_MAX;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	_h = io.out.file.handle;
+	h = &_h;
+	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
+	CHECK_VAL(io.out.durable_open, false);
+	CHECK_VAL(io.out.durable_open_v2, true);
+	CHECK_VAL(io.out.persistent_open, false);
+	CHECK_VAL(io.out.timeout, io.in.timeout);
+
+	/* disconnect */
+	TALLOC_FREE(tree);
+
+	/* create a new session (same client_guid) */
+	if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
+		torture_warning(tctx, "couldn't reconnect, bailing\n");
+		ret = false;
+		goto done;
+	}
+
+	/*
+	 * check invalid combinations of durable handle
+	 * request and reconnect blobs
+	 * See MS-SMB2: 3.3.5.9.12
+	 * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 Create Context
+	 */
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_handle_v2 = h; /* durable v2 reconnect request */
+	io.in.durable_open = true;   /* durable v1 handle request */
+	io.in.create_guid = create_guid;
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_handle = h;     /* durable v1 reconnect request */
+	io.in.durable_open_v2 = true; /* durable v2 handle request */
+	io.in.create_guid = create_guid;
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_handle = h;    /* durable v1 reconnect request */
+	io.in.durable_handle_v2 = h; /* durable v2 reconnect request */
+	io.in.create_guid = create_guid;
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_handle_v2 = h;  /* durable v2 reconnect request */
+	io.in.durable_open_v2 = true; /* durable v2 handle request */
+	io.in.create_guid = create_guid;
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+done:
+	if (h != NULL) {
+		smb2_util_close(tree, *h);
+	}
+
+	smb2_util_unlink(tree, fname);
+
+	talloc_free(tree);
+
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
+
+/**
  * basic durable_open test.
  * durable state should only be granted when requested
  * along with a batch oplock or a handle lease.
@@ -1393,6 +1500,7 @@ struct torture_suite *torture_smb2_durable_v2_open_init(void)
 	struct torture_suite *suite =
 	    torture_suite_create(talloc_autofree_context(), "durable-v2-open");
 
+	torture_suite_add_1smb2_test(suite, "create-blob", test_durable_v2_open_create_blob);
 	torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_v2_open_oplock);
 	torture_suite_add_1smb2_test(suite, "open-lease", test_durable_v2_open_lease);
 	torture_suite_add_1smb2_test(suite, "reopen1", test_durable_v2_open_reopen1);
-- 
1.7.9.5


From 4faa5d814ba6f6ba643239b2003d7acb2724eeff Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Thu, 26 Sep 2013 06:06:01 +0200
Subject: [PATCH 19/22] s4:torture:smb2: add durable-v2-open.reopen2b

- connect with durable v2
- reconnect with durable v1
=> succeeds

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source4/torture/smb2/durable_v2_open.c |   91 ++++++++++++++++++++++++++++++++
 1 file changed, 91 insertions(+)

diff --git a/source4/torture/smb2/durable_v2_open.c b/source4/torture/smb2/durable_v2_open.c
index 6f9d01e..6ead8e1 100644
--- a/source4/torture/smb2/durable_v2_open.c
+++ b/source4/torture/smb2/durable_v2_open.c
@@ -732,6 +732,96 @@ done:
 }
 
 /**
+ * durable reconnect test:
+ * connect with v2, reconnect with v1
+ */
+bool test_durable_v2_open_reopen2b(struct torture_context *tctx,
+				   struct smb2_tree *tree)
+{
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	char fname[256];
+	struct smb2_handle _h;
+	struct smb2_handle *h = NULL;
+	struct smb2_create io;
+	struct GUID create_guid = GUID_random();
+	bool ret = true;
+	struct smbcli_options options;
+
+	options = tree->session->transport->options;
+
+	/* Choose a random name in case the state is left a little funky. */
+	snprintf(fname, 256, "durable_v2_open_reopen2b_%s.dat",
+		 generate_random_str(tctx, 8));
+
+	smb2_util_unlink(tree, fname);
+
+	smb2_oplock_create_share(&io, fname,
+				 smb2_util_share_access(""),
+				 smb2_util_oplock_level("b"));
+	io.in.durable_open = false;
+	io.in.durable_open_v2 = true;
+	io.in.persistent_open = false;
+	io.in.create_guid = create_guid;
+	io.in.timeout = UINT32_MAX;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	_h = io.out.file.handle;
+	h = &_h;
+	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
+	CHECK_VAL(io.out.durable_open, false);
+	CHECK_VAL(io.out.durable_open_v2, true);
+	CHECK_VAL(io.out.persistent_open, false);
+	CHECK_VAL(io.out.timeout, io.in.timeout);
+
+	/* disconnect, leaving the durable open */
+	TALLOC_FREE(tree);
+
+	if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
+		torture_warning(tctx, "couldn't reconnect, bailing\n");
+		ret = false;
+		goto done;
+	}
+
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_handle_v2 = h;     /* durable v2 reconnect */
+	io.in.create_guid = GUID_zero(); /* but zero create GUID */
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_handle = h; /* durable v1 (!) reconnect */
+	h = NULL;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.durable_open, false);
+	CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
+	CHECK_VAL(io.out.persistent_open, false);
+	CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
+	_h = io.out.file.handle;
+	h = &_h;
+
+done:
+	if (h != NULL) {
+		smb2_util_close(tree, *h);
+	}
+
+	smb2_util_unlink(tree, fname);
+
+	talloc_free(tree);
+
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
+/**
  * lease variant of reopen2
  * basic test for doing a durable open
  * tcp disconnect, reconnect, do a durable reopen (succeeds)
@@ -1505,6 +1595,7 @@ struct torture_suite *torture_smb2_durable_v2_open_init(void)
 	torture_suite_add_1smb2_test(suite, "open-lease", test_durable_v2_open_lease);
 	torture_suite_add_1smb2_test(suite, "reopen1", test_durable_v2_open_reopen1);
 	torture_suite_add_1smb2_test(suite, "reopen2", test_durable_v2_open_reopen2);
+	torture_suite_add_1smb2_test(suite, "reopen2b", test_durable_v2_open_reopen2b);
 	torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_v2_open_reopen2_lease);
 	torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_v2_open_reopen2_lease_v2);
 	torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance);
-- 
1.7.9.5


From d32053cfb9a94fc0cfbc0478cebf1e4c183df85b Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Thu, 26 Sep 2013 07:00:33 +0200
Subject: [PATCH 20/22] s4:torture:smb2: add durable-v2-open.reopen2c

- create durable with v1 request
- reconnect with v2 reconnect request
==> fails

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source4/torture/smb2/durable_v2_open.c |   75 ++++++++++++++++++++++++++++++++
 1 file changed, 75 insertions(+)

diff --git a/source4/torture/smb2/durable_v2_open.c b/source4/torture/smb2/durable_v2_open.c
index 6ead8e1..36c9962 100644
--- a/source4/torture/smb2/durable_v2_open.c
+++ b/source4/torture/smb2/durable_v2_open.c
@@ -820,6 +820,80 @@ done:
 
 	return ret;
 }
+/**
+ * durable reconnect test:
+ * connect with v1, reconnect with v2 : fails (no client_guid...)
+ */
+bool test_durable_v2_open_reopen2c(struct torture_context *tctx,
+				   struct smb2_tree *tree)
+{
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	char fname[256];
+	struct smb2_handle _h;
+	struct smb2_handle *h = NULL;
+	struct smb2_create io;
+	struct GUID create_guid = GUID_random();
+	bool ret = true;
+	struct smbcli_options options;
+
+	options = tree->session->transport->options;
+
+	/* Choose a random name in case the state is left a little funky. */
+	snprintf(fname, 256, "durable_v2_open_reopen2c_%s.dat",
+		 generate_random_str(tctx, 8));
+
+	smb2_util_unlink(tree, fname);
+
+	smb2_oplock_create_share(&io, fname,
+				 smb2_util_share_access(""),
+				 smb2_util_oplock_level("b"));
+	io.in.durable_open = true;
+	io.in.durable_open_v2 = false;
+	io.in.persistent_open = false;
+	io.in.create_guid = create_guid;
+	io.in.timeout = UINT32_MAX;
+
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	_h = io.out.file.handle;
+	h = &_h;
+	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
+	CHECK_VAL(io.out.durable_open, true);
+	CHECK_VAL(io.out.durable_open_v2, false);
+	CHECK_VAL(io.out.persistent_open, false);
+	CHECK_VAL(io.out.timeout, 0);
+
+	/* disconnect, leaving the durable open */
+	TALLOC_FREE(tree);
+
+	if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
+		torture_warning(tctx, "couldn't reconnect, bailing\n");
+		ret = false;
+		goto done;
+	}
+
+	ZERO_STRUCT(io);
+	io.in.fname = fname;
+	io.in.durable_handle_v2 = h;     /* durable v2 reconnect */
+	io.in.create_guid = create_guid;
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+done:
+	if (h != NULL) {
+		smb2_util_close(tree, *h);
+	}
+
+	smb2_util_unlink(tree, fname);
+
+	talloc_free(tree);
+
+	talloc_free(mem_ctx);
+
+	return ret;
+}
 
 /**
  * lease variant of reopen2
@@ -1596,6 +1670,7 @@ struct torture_suite *torture_smb2_durable_v2_open_init(void)
 	torture_suite_add_1smb2_test(suite, "reopen1", test_durable_v2_open_reopen1);
 	torture_suite_add_1smb2_test(suite, "reopen2", test_durable_v2_open_reopen2);
 	torture_suite_add_1smb2_test(suite, "reopen2b", test_durable_v2_open_reopen2b);
+	torture_suite_add_1smb2_test(suite, "reopen2c", test_durable_v2_open_reopen2c);
 	torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_v2_open_reopen2_lease);
 	torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_v2_open_reopen2_lease_v2);
 	torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance);
-- 
1.7.9.5


From 5d5c9f76806054539f83b0afd27952bc1d728227 Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Wed, 25 Sep 2013 20:39:17 +0200
Subject: [PATCH 21/22] smbd:smb2_create: fix return code for durable handle
 create blob combinations

According to MS-SMB2:

3.3.5.9.7 Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context
3.3.5.9.12 Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 Create Context

and verified by test results.

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source3/smbd/smb2_create.c |   24 ++++++++++++++++--------
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index fb9b56e..79ba146 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -421,6 +421,8 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 	int requested_oplock_level;
 	struct smb2_create_blob *dhnc = NULL;
 	struct smb2_create_blob *dh2c = NULL;
+	struct smb2_create_blob *dhnq = NULL;
+	struct smb2_create_blob *dh2q = NULL;
 	struct smbXsrv_open *op = NULL;
 
 	ZERO_STRUCT(out_context_blobs);
@@ -459,8 +461,22 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 			in_name ));
 	}
 
+	dhnq = smb2_create_blob_find(&in_context_blobs,
+				     SMB2_CREATE_TAG_DHNQ);
 	dhnc = smb2_create_blob_find(&in_context_blobs,
 				     SMB2_CREATE_TAG_DHNC);
+	dh2q = smb2_create_blob_find(&in_context_blobs,
+				     SMB2_CREATE_TAG_DH2Q);
+	dh2c = smb2_create_blob_find(&in_context_blobs,
+				     SMB2_CREATE_TAG_DH2C);
+
+	if ((dhnc && dh2c) || (dhnc && dh2q) || (dh2c && dhnq) ||
+	    (dh2q && dh2c))
+	{
+		/* not both are allowed at the same time */
+		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+		return tevent_req_post(req, ev);
+	}
 
 	if (dhnc) {
 		if (dhnc->data.length != 16) {
@@ -478,8 +494,6 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 		}
 	}
 
-	dh2c = smb2_create_blob_find(&in_context_blobs,
-				     SMB2_CREATE_TAG_DH2C);
 	if (dh2c) {
 		if (dh2c->data.length != 36) {
 			tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
@@ -545,7 +559,6 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 		NTTIME max_access_time = 0;
 		struct smb2_create_blob *secd = NULL;
 		struct security_descriptor *sec_desc = NULL;
-		struct smb2_create_blob *dhnq = NULL;
 		struct smb2_create_blob *alsi = NULL;
 		uint64_t allocation_size = 0;
 		struct smb2_create_blob *twrp = NULL;
@@ -556,7 +569,6 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 		bool durable_requested = false;
 		uint32_t durable_timeout_msec = 0;
 		bool do_durable_reconnect = false;
-		struct smb2_create_blob *dh2q = NULL;
 		uint64_t persistent_id = 0;
 
 		exta = smb2_create_blob_find(&in_context_blobs,
@@ -565,16 +577,12 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 					     SMB2_CREATE_TAG_MXAC);
 		secd = smb2_create_blob_find(&in_context_blobs,
 					     SMB2_CREATE_TAG_SECD);
-		dhnq = smb2_create_blob_find(&in_context_blobs,
-					     SMB2_CREATE_TAG_DHNQ);
 		alsi = smb2_create_blob_find(&in_context_blobs,
 					     SMB2_CREATE_TAG_ALSI);
 		twrp = smb2_create_blob_find(&in_context_blobs,
 					     SMB2_CREATE_TAG_TWRP);
 		qfid = smb2_create_blob_find(&in_context_blobs,
 					     SMB2_CREATE_TAG_QFID);
-		dh2q = smb2_create_blob_find(&in_context_blobs,
-					     SMB2_CREATE_TAG_DH2Q);
 
 		fname = talloc_strdup(state, in_name);
 		if (tevent_req_nomem(fname, req)) {
-- 
1.7.9.5


From 795eb0b1794d0599a730f27237bc18724fa0be2a Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Thu, 26 Sep 2013 07:48:42 +0200
Subject: [PATCH 22/22] smbd:smb2: ignore an dhnq blob along with a dhnc in
 create

This is according to MS-SMB2, 3.3.5.9.7
"Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context"

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source3/smbd/smb2_create.c |   14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index 79ba146..3e73814 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -479,11 +479,23 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 	}
 
 	if (dhnc) {
+		uint32_t num_extra_blobs;
 		if (dhnc->data.length != 16) {
 			tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
 			return tevent_req_post(req, ev);
 		}
-		if (in_context_blobs.num_blobs != 1) {
+
+		num_extra_blobs = in_context_blobs.num_blobs - 1;
+		if (dhnq) {
+			/*
+			 * Note: We ignore additional dhnq blob according
+			 * to MS-SMB2: 3.3.5.9.7 Handling the
+			 * SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context
+			 */
+			num_extra_blobs--;
+		}
+
+		if (num_extra_blobs > 0) {
 			/*
 			 * DHNC should be the only one.
 			 * TODO: This is only true for the oplock case!
-- 
1.7.9.5

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 215 bytes
Desc: Digital signature
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20130926/3f740506/attachment-0001.pgp>


More information about the samba-technical mailing list