[PATCHSET] smb2 durable handle torture tests and server fixes

Michael Adam obnox at samba.org
Fri Oct 4 05:40:58 MDT 2013


Hi Jeremy,

thanks for the review!

On 2013-10-03 at 18:34 -0700, Jeremy Allison wrote:
> On Thu, Sep 26, 2013 at 08:49:44AM +0200, Michael Adam wrote:
> > 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:
> 
> OK, I've gone through these carefully. There are
> some instances of using :
> 
> talloc_free(tree);
> tree = NULL;
> 
> which would probably be better served by
> TALLOC_FREE(tree);

I fixed all cases I added in the patches.
There are more in the code which I did not fix.
Those would be additional patches.

> But the place I'd like to see a little
> cleanup is in the patches at the end that
> do :
> 
> ...
> 
> Now I've looked at your logic here on decrementing
> num_extra_blobs, which is unsigned, and I think
> it's right.
> 
> Problem is it looks a little dodgy and hard to
> follow. I'd like to see a comment around the
> 
> num_extra_blobs = in_context_blobs.num_blobs - 1;
> 
> saying something like
> 
> "If the dhnc pointer is set we can guarentee that
> in_context_blobs.num_blobs is at least 1 */
> 
> and then something similar around the decrement
> of num_extra_blobs protected by the "if (dhnq)"
> check.
> 
> We really want to make it clear that uint32_t num_extra_blobs
> can never wrap lower (higher) than zero.

You are absolutely right. This was clumsy.
I rewrote it to be much more obvious hopefully:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 	if (dhnc) {
+		uint32_t num_blobs_allowed;
+
 		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) {
-			/*
-			 * DHNC should be the only one.
-			 * TODO: This is only true for the oplock case!
-			 * For leases, lease request is required additionally!
-			 */
+
+		/*
+		 * According to MS-SMB2: 3.3.5.9.7, "Handling the
+		 * SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context",
+		 * we should ignore an additional dhnq blob, but fail
+		 * the request (with status OBJECT_NAME_NOT_FOUND) if
+		 * any other extra create blob has been provided.
+		 *
+		 * (Note that the cases of an additional dh2q or dh2c blob
+		 *  which require a different error code, have been treated
+		 *  above.)
+		 *
+		 * TODO:
+		 * This is only true for the oplock case:
+		 * For leases, lease request is required additionally.
+		 */
+
+		if (dhnq) {
+			num_blobs_allowed = 2;
+		} else {
+			num_blobs_allowed = 1;
+		}
+
+		if (in_context_blobs.num_blobs != num_blobs_allowed) {
 			tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
 			return tevent_req_post(req, ev);
 		}
 	}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I hope you agree this is much clearer.

I also adapoted the dh2c case to look similar in an additional test.
One patch has meanwhile been pushed to master by Metze:
c3a5fecdc1ff0320f4979fa21aa636aacaac8abe
"smbd:smb2: fix error code when the header says the request is signed but we don't have a session"

Just that you don't wonder why these are still 22 patches despite the
additional one... :-)

For convenience, these patches can also be retrieved out of my
"master-smb2" branch.

Thanks for further comments or push..

Cheers - Michael

-------------- next part --------------
From 35cc2e87ffa2d2a459f6c2f777a774a1f7ae74fb 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 e8a5827e9cb56860950b8832129ed0138d79a198 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 e8724b48c57720059c7c5505e9e0ab63437592eb 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 4259338e096973e73d1d14e2b49d5cb3f27fedc7 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 5f1574311c1b0ee83962a654317cdfea1d2c91fc 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 1ecf8da492a974e3a47da558ee64d90c382198a1 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 d73a3d3b31b743ecf0200aebc2f28693817f42e8 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 2ea6ea753115084a2e8d8b14424bf7eb60b927b0 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 |  238 +++++++++++++++++++++++++++++++++++
 1 file changed, 238 insertions(+)

diff --git a/source4/torture/smb2/durable_open.c b/source4/torture/smb2/durable_open.c
index 3bc012e..56ecc65 100644
--- a/source4/torture/smb2/durable_open.c
+++ b/source4/torture/smb2/durable_open.c
@@ -482,6 +482,243 @@ 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);
+
+	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);
+
+	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 +2127,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 7e6133b4e7cd859a30e48f68f0ec2b1b94545509 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 |  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 3d15ddf..216097d 100644
--- a/source4/torture/smb2/durable_v2_open.c
+++ b/source4/torture/smb2/durable_v2_open.c
@@ -585,6 +585,252 @@ 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);
+
+	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);
+
+	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 +1109,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 5962b579c61c947184738bf09ea6c32349c732d8 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 d8f4db6e3820c88bec2a9247824bbd8355fb859a 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 |  245 ++++++++++++++++++++++++++++++++
 1 file changed, 245 insertions(+)

diff --git a/source4/torture/smb2/durable_v2_open.c b/source4/torture/smb2/durable_v2_open.c
index 216097d..1d85339 100644
--- a/source4/torture/smb2/durable_v2_open.c
+++ b/source4/torture/smb2/durable_v2_open.c
@@ -831,6 +831,250 @@ 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);
+
+	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);
+
+	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,
@@ -1110,6 +1354,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 2a44400b98c5628bd22e96aa4d50559b8d4ab14d 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 |  238 +++++++++++++++++++++++++++++++++++
 1 file changed, 238 insertions(+)

diff --git a/source4/torture/smb2/durable_open.c b/source4/torture/smb2/durable_open.c
index 56ecc65..42cf302 100644
--- a/source4/torture/smb2/durable_open.c
+++ b/source4/torture/smb2/durable_open.c
@@ -719,6 +719,243 @@ 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);
+
+	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);
+
+	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)
@@ -2128,6 +2365,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 43edb7786f0b2e90f1c578ccfdc0cf717e06641e 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 41abc38c61ce89b52204ab99501fe37a127cfdcd 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 14/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 6f14b2f8e9c22119037e5c01637315b1140238fd 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 15/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 1d85339..9a33f87 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 5690ecef0f5121d8963b000b7f92d8f11fea20cf 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 16/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 42cf302..a931f6f 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 e19b8dc21169f146eed1a8b41ff5b30c8e9fb42b 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 17/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 9a33f87..3942b15 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.
@@ -1389,6 +1496,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 74b2c2f4e5a3cfa59ddebe2be3ed4fbc34ffbe99 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 18/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 3942b15..f4c9b85 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)
@@ -1501,6 +1591,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 7f22b36b8302229fcac23da3b1f7378a354d287e 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 19/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 f4c9b85..c1429b1 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
@@ -1592,6 +1666,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 8477a11463ccca3ad5d7ce72b63211d3f4a7d01e 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 20/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 03d13e8428d07703e544a80444dd489896763418 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 21/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 |   32 ++++++++++++++++++++++++++------
 1 file changed, 26 insertions(+), 6 deletions(-)

diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index 79ba146..99a5b38 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -479,16 +479,36 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 	}
 
 	if (dhnc) {
+		uint32_t num_blobs_allowed;
+
 		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) {
-			/*
-			 * DHNC should be the only one.
-			 * TODO: This is only true for the oplock case!
-			 * For leases, lease request is required additionally!
-			 */
+
+		/*
+		 * According to MS-SMB2: 3.3.5.9.7, "Handling the
+		 * SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context",
+		 * we should ignore an additional dhnq blob, but fail
+		 * the request (with status OBJECT_NAME_NOT_FOUND) if
+		 * any other extra create blob has been provided.
+		 *
+		 * (Note that the cases of an additional dh2q or dh2c blob
+		 *  which require a different error code, have been treated
+		 *  above.)
+		 *
+		 * TODO:
+		 * This is only true for the oplock case:
+		 * For leases, lease request is required additionally.
+		 */
+
+		if (dhnq) {
+			num_blobs_allowed = 2;
+		} else {
+			num_blobs_allowed = 1;
+		}
+
+		if (in_context_blobs.num_blobs != num_blobs_allowed) {
 			tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
 			return tevent_req_post(req, ev);
 		}
-- 
1.7.9.5


From 05f46139581182a6cf02fbc2de028e93d0e8cefa Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Fri, 4 Oct 2013 12:39:57 +0200
Subject: [PATCH 22/22] smbd:smb2: clarify and comment code treating dh2c blob
 check.

This makes the code that checks for extra create blobs in the
case of the dh2c blob look very similar to the corresponding
(slightly mode complex) code for the dhnc blob.

With this preparation it will be easier and more obvious how
to add proper treatment of the lease request blobs when leases
get implemented.

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

diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index 99a5b38..140c81b 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -515,16 +515,32 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 	}
 
 	if (dh2c) {
+		uint32_t num_blobs_allowed;
+
 		if (dh2c->data.length != 36) {
 			tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
 			return tevent_req_post(req, ev);
 		}
-		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!
-			 */
+
+		/*
+		 * According to MS-SMB2: 3.3.5.9.12, "Handling the
+		 * SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 Create Context",
+		 * we should fail the request with status
+		 * OBJECT_NAME_NOT_FOUND if any other create blob has been
+		 * provided.
+		 *
+		 * (Note that the cases of an additional dhnq, dhnc or dh2q
+		 *  blob which require a different error code, have been
+		 *  treated above.)
+		 *
+		 * TODO:
+		 * This is only true for the oplock case:
+		 * For leases, lease request is required additionally!
+		 */
+
+		num_blobs_allowed = 1;
+
+		if (in_context_blobs.num_blobs != num_blobs_allowed) {
 			tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
 			return tevent_req_post(req, ev);
 		}
-- 
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/20131004/6aecbc0f/attachment-0001.pgp>


More information about the samba-technical mailing list