[Patches] Network session expired at the wrong moment (bug #13197)

Stefan Metzmacher metze at samba.org
Thu Dec 21 14:48:48 UTC 2017


Hi,

here're patches to allow some SMB2 operations to be processed
even if the session is already expired.

They fix https://bugzilla.samba.org/show_bug.cgi?id=13197

The test (smb2.session.expire2) I wrote shows that
Windows (at least 2012R2) allows more than
[MS-SMB2] 3.3.5.2.9 Verifying the Session specifies.

I have a customer capture were an SMB2 close gets
NETWORK_SESSION_EXPIRED, which isn't replayed.
This causes a SHARING_VIOLATION deadlock with the
same client, as it immediately tries to reopen the
same file with different options.

Please review and push:-)

Thanks!
metze
-------------- next part --------------
From a1e533fef6f5e5da2d8eeae196df45980b7cd698 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 21 Dec 2017 12:53:02 +0100
Subject: [PATCH 1/3] s4:torture: add smb2.session.expire2 test

This demonstrates the interaction of NT_STATUS_NETWORK_SESSION_EXPIRED
and various SMB2 opcodes.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=13197

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 selftest/knownfail.d/session.expire2 |   1 +
 source4/libcli/smb2/keepalive.c      |   7 +-
 source4/torture/smb2/session.c       | 362 +++++++++++++++++++++++++++++++++++
 3 files changed, 368 insertions(+), 2 deletions(-)
 create mode 100644 selftest/knownfail.d/session.expire2

diff --git a/selftest/knownfail.d/session.expire2 b/selftest/knownfail.d/session.expire2
new file mode 100644
index 0000000..998ccbd
--- /dev/null
+++ b/selftest/knownfail.d/session.expire2
@@ -0,0 +1 @@
+^samba3.smb2.session.*krb5.expire2
diff --git a/source4/libcli/smb2/keepalive.c b/source4/libcli/smb2/keepalive.c
index 402b063..71004aa14 100644
--- a/source4/libcli/smb2/keepalive.c
+++ b/source4/libcli/smb2/keepalive.c
@@ -26,7 +26,8 @@
 /*
   send a keepalive request
 */
-struct smb2_request *smb2_keepalive_send(struct smb2_transport *transport)
+struct smb2_request *smb2_keepalive_send(struct smb2_transport *transport,
+					 struct smb2_session *session)
 {
 	struct smb2_request *req;
 
@@ -35,6 +36,8 @@ struct smb2_request *smb2_keepalive_send(struct smb2_transport *transport)
 
 	SSVAL(req->out.body, 0x02, 0);
 
+	req->session = session;
+
 	smb2_transport_send(req);
 
 	return req;
@@ -60,6 +63,6 @@ NTSTATUS smb2_keepalive_recv(struct smb2_request *req)
 */
 NTSTATUS smb2_keepalive(struct smb2_transport *transport)
 {
-	struct smb2_request *req = smb2_keepalive_send(transport);
+	struct smb2_request *req = smb2_keepalive_send(transport, NULL);
 	return smb2_keepalive_recv(req);
 }
diff --git a/source4/torture/smb2/session.c b/source4/torture/smb2/session.c
index fca0552..22073ed 100644
--- a/source4/torture/smb2/session.c
+++ b/source4/torture/smb2/session.c
@@ -31,6 +31,7 @@
 #include "libcli/security/security.h"
 #include "libcli/resolve/resolve.h"
 #include "lib/param/param.h"
+#include "lib/util/tevent_ntstatus.h"
 
 #define CHECK_CREATED(tctx, __io, __created, __attribute)			\
 	do {									\
@@ -1167,6 +1168,366 @@ done:
 	return ret;
 }
 
+static bool test_session_expire2(struct torture_context *tctx)
+{
+	NTSTATUS status;
+	bool ret = false;
+	struct smbcli_options options;
+	const char *host = torture_setting_string(tctx, "host", NULL);
+	const char *share = torture_setting_string(tctx, "share", NULL);
+	struct cli_credentials *credentials = popt_get_cmdline_credentials();
+	struct smb2_tree *tree = NULL;
+	const char *unc = NULL;
+	struct smb2_tree *tree2 = NULL;
+	struct tevent_req *subreq = NULL;
+	uint32_t timeout_msec;
+	enum credentials_use_kerberos use_kerberos;
+	uint32_t caps;
+	char fname[256];
+	struct smb2_handle dh;
+	struct smb2_handle dh2;
+	struct smb2_handle _h1;
+	struct smb2_handle *h1 = NULL;
+	struct smb2_create io1;
+	union smb_fileinfo qfinfo;
+	union smb_setfileinfo sfinfo;
+	struct smb2_flush flsh;
+	struct smb2_read rd;
+	const uint8_t wd = 0;
+	struct smb2_lock lck;
+	struct smb2_lock_element el;
+	struct smb2_ioctl ctl;
+	struct smb2_break oack;
+	struct smb2_lease_break_ack lack;
+	struct smb2_find fnd;
+	union smb_search_data *d = NULL;
+	unsigned int count;
+	struct smb2_request *req = NULL;
+	struct smb2_notify ntf1;
+	struct smb2_notify ntf2;
+
+	use_kerberos = cli_credentials_get_kerberos_state(credentials);
+	if (use_kerberos != CRED_MUST_USE_KERBEROS) {
+		torture_warning(tctx, "smb2.session.expire2 requires -k yes!");
+		torture_skip(tctx, "smb2.session.expire2 requires -k yes!");
+	}
+
+	torture_assert_int_equal(tctx, use_kerberos, CRED_MUST_USE_KERBEROS,
+				 "please use -k yes");
+
+	lpcfg_set_option(tctx->lp_ctx, "gensec_gssapi:requested_life_time=4");
+
+	lpcfg_smbcli_options(tctx->lp_ctx, &options);
+
+	unc = talloc_asprintf(tctx, "\\\\%s\\%s", host, share);
+	torture_assert(tctx, unc != NULL, "talloc_asprintf");
+
+	status = smb2_connect(tctx,
+			      host,
+			      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)
+			      );
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_connect failed");
+
+	caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
+
+	/* Add some random component to the file name. */
+	snprintf(fname, sizeof(fname), "session_expire2_%s.dat",
+		 generate_random_str(tctx, 8));
+
+	smb2_util_unlink(tree, fname);
+
+	status = smb2_util_roothandle(tree, &dh);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_util_roothandle failed");
+
+	smb2_oplock_create_share(&io1, fname,
+				 smb2_util_share_access(""),
+				 smb2_util_oplock_level("b"));
+	io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
+
+	status = smb2_create(tree, tctx, &io1);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_create failed");
+	_h1 = io1.out.file.handle;
+	h1 = &_h1;
+	CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	torture_assert_int_equal(tctx, io1.out.oplock_level,
+					smb2_util_oplock_level("b"),
+					"oplock_level incorrect");
+
+	/* get the security descriptor */
+
+	ZERO_STRUCT(qfinfo);
+
+	qfinfo.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION;
+	qfinfo.access_information.in.file.handle = _h1;
+
+	torture_comment(tctx, "query info => OK\n");
+
+	ZERO_STRUCT(qfinfo.access_information.out);
+	status = smb2_getinfo_file(tree, tctx, &qfinfo);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_getinfo_file failed");
+
+	torture_comment(tctx, "lock => OK\n");
+	ZERO_STRUCT(lck);
+	lck.in.locks		= ⪙
+	lck.in.lock_count	= 0x0001;
+	lck.in.lock_sequence	= 0x00000000;
+	lck.in.file.handle	= *h1;
+	ZERO_STRUCT(el);
+	el.flags		= SMB2_LOCK_FLAG_EXCLUSIVE |
+				  SMB2_LOCK_FLAG_FAIL_IMMEDIATELY;
+	el.offset		= 0x0000000000000000;
+	el.length		= 0x0000000000000001;
+	status = smb2_lock(tree, &lck);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_lock lock failed");
+
+	torture_comment(tctx, "1st notify => PENDING\n");
+	ZERO_STRUCT(ntf1);
+	ntf1.in.file.handle	= dh;
+	ntf1.in.recursive	= 0x0000;
+	ntf1.in.buffer_size	= 128;
+	ntf1.in.completion_filter= FILE_NOTIFY_CHANGE_ATTRIBUTES;
+	ntf1.in.unknown		= 0x00000000;
+	req = smb2_notify_send(tree, &ntf1);
+
+	while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) {
+		if (tevent_loop_once(tctx->ev) != 0) {
+			break;
+		}
+	}
+
+	torture_assert_goto(tctx, req->state <= SMB2_REQUEST_RECV, ret, done,
+			    "smb2_notify finished");
+
+	torture_comment(tctx, "sleep 10 seconds\n");
+	smb_msleep(10*1000);
+
+	torture_comment(tctx, "query info => EXPIRED\n");
+	ZERO_STRUCT(qfinfo.access_information.out);
+	status = smb2_getinfo_file(tree, tctx, &qfinfo);
+	torture_assert_ntstatus_equal_goto(tctx, status,
+				NT_STATUS_NETWORK_SESSION_EXPIRED,
+				ret, done, "smb2_getinfo_file "
+				"returned unexpected status");
+
+
+	torture_comment(tctx, "set info => EXPIRED\n");
+	ZERO_STRUCT(sfinfo);
+	sfinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
+	sfinfo.end_of_file_info.in.file.handle = *h1;
+	sfinfo.end_of_file_info.in.size = 1;
+	status = smb2_setinfo_file(tree, &sfinfo);
+	torture_assert_ntstatus_equal_goto(tctx, status,
+				NT_STATUS_NETWORK_SESSION_EXPIRED,
+				ret, done, "smb2_setinfo_file "
+				"returned unexpected status");
+
+	torture_comment(tctx, "flush => EXPIRED\n");
+	ZERO_STRUCT(flsh);
+	flsh.in.file.handle = *h1;
+	status = smb2_flush(tree, &flsh);
+	torture_assert_ntstatus_equal_goto(tctx, status,
+				NT_STATUS_NETWORK_SESSION_EXPIRED,
+				ret, done, "smb2_flush "
+				"returned unexpected status");
+
+	torture_comment(tctx, "read => EXPIRED\n");
+	ZERO_STRUCT(rd);
+	rd.in.file.handle = *h1;
+	rd.in.length      = 5;
+	rd.in.offset      = 0;
+	status = smb2_read(tree, tctx, &rd);
+	torture_assert_ntstatus_equal_goto(tctx, status,
+				NT_STATUS_NETWORK_SESSION_EXPIRED,
+				ret, done, "smb2_read "
+				"returned unexpected status");
+
+	torture_comment(tctx, "write => EXPIRED\n");
+	status = smb2_util_write(tree, *h1, &wd, 0, 1);
+	torture_assert_ntstatus_equal_goto(tctx, status,
+				NT_STATUS_NETWORK_SESSION_EXPIRED,
+				ret, done, "smb2_util_write "
+				"returned unexpected status");
+
+	torture_comment(tctx, "ioctl => EXPIRED\n");
+	ZERO_STRUCT(ctl);
+	ctl.in.file.handle = *h1;
+	ctl.in.function = FSCTL_SRV_ENUM_SNAPS;
+	ctl.in.max_response_size = 16;
+	ctl.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
+	status = smb2_ioctl(tree, tctx, &ctl);
+	torture_assert_ntstatus_equal_goto(tctx, status,
+				NT_STATUS_NETWORK_SESSION_EXPIRED,
+				ret, done, "smb2_ioctl "
+				"returned unexpected status");
+
+	torture_comment(tctx, "oplock ack => EXPIRED\n");
+	ZERO_STRUCT(oack);
+	oack.in.file.handle = *h1;
+	status = smb2_break(tree, &oack);
+	torture_assert_ntstatus_equal_goto(tctx, status,
+				NT_STATUS_NETWORK_SESSION_EXPIRED,
+				ret, done, "smb2_break "
+				"returned unexpected status");
+
+	if (caps & SMB2_CAP_LEASING) {
+		torture_comment(tctx, "lease ack => EXPIRED\n");
+		ZERO_STRUCT(lack);
+		lack.in.lease.lease_version = 1;
+		lack.in.lease.lease_key.data[0] = 1;
+		lack.in.lease.lease_key.data[0] = 2;
+		status = smb2_lease_break_ack(tree, &lack);
+		torture_assert_ntstatus_equal_goto(tctx, status,
+					NT_STATUS_NETWORK_SESSION_EXPIRED,
+					ret, done, "smb2_break "
+					"returned unexpected status");
+	}
+
+	torture_comment(tctx, "query directory => EXPIRED\n");
+	ZERO_STRUCT(fnd);
+	fnd.in.file.handle	= dh;
+	fnd.in.pattern		= "*";
+	fnd.in.continue_flags	= SMB2_CONTINUE_FLAG_SINGLE;
+	fnd.in.max_response_size= 0x100;
+	fnd.in.level		= SMB2_FIND_BOTH_DIRECTORY_INFO;
+	status = smb2_find_level(tree, tree, &fnd, &count, &d);
+	torture_assert_ntstatus_equal_goto(tctx, status,
+				NT_STATUS_NETWORK_SESSION_EXPIRED,
+				ret, done, "smb2_find_level "
+				"returned unexpected status");
+
+	torture_comment(tctx, "1st notify => CANCEL\n");
+	smb2_cancel(req);
+
+	torture_comment(tctx, "2nd notify => EXPIRED\n");
+	ZERO_STRUCT(ntf2);
+	ntf2.in.file.handle	= dh;
+	ntf2.in.recursive	= 0x0000;
+	ntf2.in.buffer_size	= 128;
+	ntf2.in.completion_filter= FILE_NOTIFY_CHANGE_ATTRIBUTES;
+	ntf2.in.unknown		= 0x00000000;
+	status = smb2_notify(tree, tctx, &ntf2);
+	torture_assert_ntstatus_equal_goto(tctx, status,
+				NT_STATUS_NETWORK_SESSION_EXPIRED,
+				ret, done, "smb2_notify "
+				"returned unexpected status");
+
+	torture_assert_goto(tctx, req->state > SMB2_REQUEST_RECV, ret, done,
+			    "smb2_notify (1st) not finished");
+
+	status = smb2_notify_recv(req, tctx, &ntf1);
+	torture_assert_ntstatus_equal_goto(tctx, status,
+				NT_STATUS_CANCELLED,
+				ret, done, "smb2_notify cancelled"
+				"returned unexpected status");
+
+	torture_comment(tctx, "tcon => EXPIRED\n");
+	tree2 = smb2_tree_init(tree->session, tctx, false);
+	torture_assert(tctx, tree2 != NULL, "smb2_tree_init");
+	timeout_msec = tree->session->transport->options.request_timeout * 1000;
+	subreq = smb2cli_tcon_send(tree2, tctx->ev,
+				   tree2->session->transport->conn,
+				   timeout_msec,
+				   tree2->session->smbXcli,
+				   tree2->smbXcli,
+				   0, /* flags */
+				   unc);
+	torture_assert(tctx, subreq != NULL, "smb2cli_tcon_send");
+	torture_assert(tctx,
+		       tevent_req_poll_ntstatus(subreq, tctx->ev, &status),
+		       "tevent_req_poll_ntstatus");
+	status = smb2cli_tcon_recv(subreq);
+	TALLOC_FREE(subreq);
+	torture_assert_ntstatus_equal_goto(tctx, status,
+				NT_STATUS_NETWORK_SESSION_EXPIRED,
+				ret, done, "smb2cli_tcon"
+				"returned unexpected status");
+
+	torture_comment(tctx, "create => EXPIRED\n");
+	status = smb2_util_roothandle(tree, &dh2);
+	torture_assert_ntstatus_equal_goto(tctx, status,
+				NT_STATUS_NETWORK_SESSION_EXPIRED,
+				ret, done, "smb2_util_roothandle"
+				"returned unexpected status");
+
+	torture_comment(tctx, "tdis => EXPIRED\n");
+	status = smb2_tdis(tree);
+	torture_assert_ntstatus_equal_goto(tctx, status,
+				NT_STATUS_NETWORK_SESSION_EXPIRED,
+				ret, done, "smb2cli_tdis"
+				"returned unexpected status");
+
+	/*
+	 * (Un)Lock, Close and Logoff are still possible
+	 */
+
+	torture_comment(tctx, "1st unlock => OK\n");
+	el.flags		= SMB2_LOCK_FLAG_UNLOCK;
+	status = smb2_lock(tree, &lck);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_lock unlock failed");
+
+	torture_comment(tctx, "2nd unlock => RANGE_NOT_LOCKED\n");
+	status = smb2_lock(tree, &lck);
+	torture_assert_ntstatus_equal_goto(tctx, status,
+				NT_STATUS_RANGE_NOT_LOCKED,
+				ret, done, "smb2_lock 2nd unlock"
+				"returned unexpected status");
+
+	torture_comment(tctx, "lock => EXPIRED\n");
+	el.flags		= SMB2_LOCK_FLAG_EXCLUSIVE |
+				  SMB2_LOCK_FLAG_FAIL_IMMEDIATELY;
+	status = smb2_lock(tree, &lck);
+	torture_assert_ntstatus_equal_goto(tctx, status,
+				NT_STATUS_NETWORK_SESSION_EXPIRED,
+				ret, done, "smb2_util_roothandle"
+				"returned unexpected status");
+
+	torture_comment(tctx, "close => OK\n");
+	status = smb2_util_close(tree, *h1);
+	h1 = NULL;
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_close failed");
+
+	torture_comment(tctx, "echo without session => OK\n");
+	status = smb2_keepalive(tree->session->transport);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_keepalive without session failed");
+
+	torture_comment(tctx, "echo with session => OK\n");
+	req = smb2_keepalive_send(tree->session->transport, tree->session);
+	status = smb2_keepalive_recv(req);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_keepalive with session failed");
+
+	torture_comment(tctx, "logoff => OK\n");
+	status = smb2_logoff(tree->session);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_logoff failed");
+
+	ret = true;
+done:
+	if (h1 != NULL) {
+		smb2_util_close(tree, *h1);
+	}
+
+	talloc_free(tree);
+	lpcfg_set_option(tctx->lp_ctx, "gensec_gssapi:requested_life_time=0");
+	return ret;
+}
+
 bool test_session_bind1(struct torture_context *tctx, struct smb2_tree *tree1)
 {
 	const char *host = torture_setting_string(tctx, "host", NULL);
@@ -1321,6 +1682,7 @@ struct torture_suite *torture_smb2_session_init(TALLOC_CTX *ctx)
 	torture_suite_add_1smb2_test(suite, "reauth5", test_session_reauth5);
 	torture_suite_add_1smb2_test(suite, "reauth6", test_session_reauth6);
 	torture_suite_add_simple_test(suite, "expire1", test_session_expire1);
+	torture_suite_add_simple_test(suite, "expire2", test_session_expire2);
 	torture_suite_add_1smb2_test(suite, "bind1", test_session_bind1);
 
 	suite->description = talloc_strdup(suite, "SMB2-SESSION tests");
-- 
1.9.1


From 1a02e02b7fc8e3a99eba9e89801551d5607e4c63 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 21 Dec 2017 14:47:06 +0100
Subject: [PATCH 2/3] s3:smbd: return the correct error for cancelled SMB2
 notifies on expired sessions

BUG: https://bugzilla.samba.org/show_bug.cgi?id=13197

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/notify.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/source3/smbd/notify.c b/source3/smbd/notify.c
index f64185d..add59089 100644
--- a/source3/smbd/notify.c
+++ b/source3/smbd/notify.c
@@ -391,12 +391,21 @@ static void smbd_notify_cancel_by_map(struct notify_mid_map *map)
 	NTSTATUS notify_status = NT_STATUS_CANCELLED;
 
 	if (smb2req != NULL) {
+		NTSTATUS sstatus;
+
 		if (smb2req->session == NULL) {
-			notify_status = STATUS_NOTIFY_CLEANUP;
-		} else if (!NT_STATUS_IS_OK(smb2req->session->status)) {
-			notify_status = STATUS_NOTIFY_CLEANUP;
+			sstatus = NT_STATUS_USER_SESSION_DELETED;
+		} else {
+			sstatus = smb2req->session->status;
 		}
-		if (smb2req->tcon == NULL) {
+
+		if (NT_STATUS_EQUAL(sstatus, NT_STATUS_NETWORK_SESSION_EXPIRED)) {
+			sstatus = NT_STATUS_OK;
+		}
+
+		if (!NT_STATUS_IS_OK(sstatus)) {
+			notify_status = STATUS_NOTIFY_CLEANUP;
+		} else if (smb2req->tcon == NULL) {
 			notify_status = STATUS_NOTIFY_CLEANUP;
 		} else if (!NT_STATUS_IS_OK(smb2req->tcon->status)) {
 			notify_status = STATUS_NOTIFY_CLEANUP;
-- 
1.9.1


From 94c4c63082dae90b6959fe7e7ccfd3c59575dc11 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 20 Dec 2017 14:05:54 +0100
Subject: [PATCH 3/3] s3:smb2_server: allow logoff, close, unlock, cancel and
 echo on expired sessions

Windows client at least doesn't have code to replay
a SMB2 Close after getting NETWORK_SESSION_EXPIRED,
which locks out a the client and generates an endless
loop around NT_STATUS_SHARING_VIOLATION.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=13197

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 selftest/knownfail.d/session.expire2 |  1 -
 source3/smbd/smb2_lock.c             | 17 +++++++++++++++++
 source3/smbd/smb2_server.c           | 19 +++++++++++++++++++
 3 files changed, 36 insertions(+), 1 deletion(-)
 delete mode 100644 selftest/knownfail.d/session.expire2

diff --git a/selftest/knownfail.d/session.expire2 b/selftest/knownfail.d/session.expire2
deleted file mode 100644
index 998ccbd..0000000
--- a/selftest/knownfail.d/session.expire2
+++ /dev/null
@@ -1 +0,0 @@
-^samba3.smb2.session.*krb5.expire2
diff --git a/source3/smbd/smb2_lock.c b/source3/smbd/smb2_lock.c
index 2fcd359..45b833c 100644
--- a/source3/smbd/smb2_lock.c
+++ b/source3/smbd/smb2_lock.c
@@ -98,6 +98,23 @@ NTSTATUS smbd_smb2_request_process_lock(struct smbd_smb2_request *req)
 	in_locks[l].flags	= IVAL(lock_buffer, 0x10);
 	/* 0x14 - 4 reserved bytes */
 
+	status = req->session->status;
+	if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)) {
+		/*
+		 * We need to catch NT_STATUS_NETWORK_SESSION_EXPIRED
+		 * for lock requests only.
+		 *
+		 * Unlock requests still need to be processed!
+		 *
+		 * This means smbd_smb2_request_check_session()
+		 * can't handle the difference and always
+		 * allows SMB2_OP_LOCK.
+		 */
+		if (in_locks[0].flags != SMB2_LOCK_FLAG_UNLOCK) {
+			return smbd_smb2_request_error(req, status);
+		}
+	}
+
 	lock_buffer = SMBD_SMB2_IN_DYN_PTR(req);
 
 	for (l=1; l < in_lock_count; l++) {
diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c
index 68a024f..5290c05 100644
--- a/source3/smbd/smb2_server.c
+++ b/source3/smbd/smb2_server.c
@@ -1902,6 +1902,25 @@ static NTSTATUS smbd_smb2_request_check_session(struct smbd_smb2_request *req)
 		case SMB2_OP_SESSSETUP:
 			status = NT_STATUS_OK;
 			break;
+		case SMB2_OP_LOGOFF:
+		case SMB2_OP_CLOSE:
+		case SMB2_OP_LOCK:
+		case SMB2_OP_CANCEL:
+		case SMB2_OP_KEEPALIVE:
+			/*
+			 * [MS-SMB2] 3.3.5.2.9 Verifying the Session
+			 * specifies that LOGOFF, CLOSE and (UN)LOCK
+			 * should always be processed even on expired sessions.
+			 *
+			 * Also see the logic in
+			 * smbd_smb2_request_process_lock().
+			 *
+			 * The smb2.session.expire2 test shows that
+			 * CANCEL and KEEPALIVE/ECHO should also
+			 * be processed.
+			 */
+			status = NT_STATUS_OK;
+			break;
 		default:
 			break;
 		}
-- 
1.9.1

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: OpenPGP digital signature
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20171221/f5840da5/signature.sig>


More information about the samba-technical mailing list