From 9f7ecbe19398516bb17a4545fb417472ae827047 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Tue, 5 Jun 2018 18:04:12 +0000 Subject: [PATCH 1/4] version 0.2.16 --- pike/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pike/__init__.py b/pike/__init__.py index 1fff2898..ee019c27 100644 --- a/pike/__init__.py +++ b/pike/__init__.py @@ -13,5 +13,5 @@ 'test', 'transport', ] -__version_info__ = (0, 2, 15) +__version_info__ = (0, 2, 16) __version__ = "{0}.{1}.{2}".format(*__version_info__) From ff1dc352883df4ec60b9c9fd78df8e620ba2a5c0 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Tue, 5 Jun 2018 18:04:28 +0000 Subject: [PATCH 2/4] add `dialect_revision` property to connection --- pike/model.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pike/model.py b/pike/model.py index b5a3ece0..c0ea940a 100644 --- a/pike/model.py +++ b/pike/model.py @@ -570,6 +570,12 @@ def negotiate_response(self, value): self._pre_auth_integrity_hash + value.parent.parent.buf[4:]) + @property + def dialect_revision(self): + return (self.negotiate_response.dialect_revision + if self.negotiate_response is not None + else 0x0) + def next_mid_range(self, length): """ multicredit requests must reserve 1 message id per credit charged. From 16fd19f7fcc77d115f30b97707fa2029dae8ff8a Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Wed, 6 Jun 2018 01:23:39 +0000 Subject: [PATCH 3/4] ErrorResponse: never decode the "extra" byte, it is ignored anyway Smb2.decode will handle advancing the cursor to the next command or end of buffer, so there is no point in decoding the extra byte as specified in [MS-SMB2] 2.2.2. Additionally since some versions of Windows no longer write the "extra" byte when error_count and byte_count is zero, this can lead to BufferOverflow exception. --- pike/smb2.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pike/smb2.py b/pike/smb2.py index 4dda142f..0cd59b7e 100644 --- a/pike/smb2.py +++ b/pike/smb2.py @@ -383,9 +383,6 @@ def _decode(self, cur): with cur.bounded(cur, end): self.error_data = ctx(self, self.byte_count) self.error_data.decode(cur) - else: - # Ignore ErrorData - cur += self.byte_count if self.byte_count else 1 class ErrorId(core.ValueEnum): SMB2_ERROR_ID_DEFAULT = 0x0 From cb3a12062f4e475746a0b8e3c476b2db6d9e8d61 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Wed, 6 Jun 2018 01:28:09 +0000 Subject: [PATCH 4/4] add additional pike.test.compound test cases ensure that not decoding the "extra" byte doesn't break chained command handling when a command in the middle of the chain returns an error. --- pike/test/compound.py | 124 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 120 insertions(+), 4 deletions(-) diff --git a/pike/test/compound.py b/pike/test/compound.py index 471dc6bb..e560ff78 100644 --- a/pike/test/compound.py +++ b/pike/test/compound.py @@ -35,11 +35,30 @@ # import pike.model +import pike.ntstatus import pike.smb2 import pike.test import random import array +def adopt(new_parent, obj): + """ + obj is adopted by new_parent + """ + new_parent.append(obj) + obj.parent = new_parent + if isinstance(obj, pike.smb2.Smb2): + obj.flags |= pike.smb2.SMB2_FLAGS_RELATED_OPERATIONS + +class RelatedOpen(pike.model.Tree): + """ + Shim to insert the RELATED_FID into the request + """ + def __init__(self, tree=None): + self.tree_id = tree.tree_id + self.file_id = pike.smb2.RELATED_FID + self.encrypt_data = tree.encrypt_data if tree is not None else False + class CompoundTest(pike.test.PikeTest): # Compounded create/close of the same file, with maximal access request @@ -52,15 +71,112 @@ def test_create_close(self): smb_req2 = chan.request(nb_req, obj=tree) create_req = pike.smb2.CreateRequest(smb_req1) close_req = pike.smb2.CloseRequest(smb_req2) - + create_req.name = 'hello.txt' create_req.desired_access = pike.smb2.GENERIC_READ | pike.smb2.GENERIC_WRITE create_req.file_attributes = pike.smb2.FILE_ATTRIBUTE_NORMAL create_req.create_disposition = pike.smb2.FILE_OPEN_IF - + max_req = pike.smb2.MaximalAccessRequest(create_req) - + close_req.file_id = pike.smb2.RELATED_FID smb_req2.flags |= pike.smb2.SMB2_FLAGS_RELATED_OPERATIONS - + chan.connection.transceive(nb_req) + + # Compound create/query/close + def test_create_query_close(self): + chan, tree = self.tree_connect() + + create_req = chan.create_request( + tree, + "create_query_close", + access=(pike.smb2.GENERIC_READ | + pike.smb2.GENERIC_WRITE | + pike.smb2.DELETE), + options=pike.smb2.FILE_DELETE_ON_CLOSE) + nb_req = create_req.parent.parent + query_req = chan.query_file_info_request(RelatedOpen(tree)) + adopt(nb_req, query_req.parent) + + close_req = chan.close_request(RelatedOpen(tree)) + adopt(nb_req, close_req.parent) + + (create_res, + query_res, + close_res) = chan.connection.transceive(nb_req) + + compare_attributes = [ + "creation_time", + "change_time", + "last_access_time", + "last_write_time", + "file_attributes"] + for attr in compare_attributes: + self.assertEqual(getattr(create_res[0], attr), + getattr(query_res[0][0], attr)) + + # Compound create/write/close & create/read/close + def test_create_write_close(self): + filename = "create_write_close" + buf = "compounded write" + + chan, tree = self.tree_connect() + + create_req1 = chan.create_request( + tree, + filename, + access=pike.smb2.GENERIC_WRITE) + nb_req = create_req1.parent.parent + write_req = chan.write_request(RelatedOpen(tree), 0, buf) + adopt(nb_req, write_req.parent) + + close_req = chan.close_request(RelatedOpen(tree)) + adopt(nb_req, close_req.parent) + + (create_res1, + write_res, + close_res) = chan.connection.transceive(nb_req) + + create_req2 = chan.create_request( + tree, + filename, + access=(pike.smb2.GENERIC_READ | + pike.smb2.DELETE), + options=pike.smb2.FILE_DELETE_ON_CLOSE) + nb_req = create_req2.parent.parent + read_req = chan.read_request(RelatedOpen(tree), 1024, 0) + adopt(nb_req, read_req.parent) + + close_req = chan.close_request(RelatedOpen(tree)) + adopt(nb_req, close_req.parent) + + (create_res2, + read_res, + close_res) = chan.connection.transceive(nb_req) + + self.assertEqual(buf, read_res[0].data.tostring()) + + # Compound create/write/close with insufficient access + def test_create_write_close_access_denied(self): + filename = "create_write_close_access_denied" + + chan, tree = self.tree_connect() + + create_req1 = chan.create_request( + tree, + filename, + access=(pike.smb2.GENERIC_READ | + pike.smb2.DELETE), + options=pike.smb2.FILE_DELETE_ON_CLOSE) + nb_req = create_req1.parent.parent + write_req = chan.write_request(RelatedOpen(tree), 0, "Expect fail") + adopt(nb_req, write_req.parent) + + close_req = chan.close_request(RelatedOpen(tree)) + adopt(nb_req, close_req.parent) + + with self.assert_error(pike.ntstatus.STATUS_ACCESS_DENIED): + (create_res1, + write_res, + close_res) = chan.connection.transceive(nb_req)