1# -*- coding: utf-8 -*- 2# Copyright: (c) 2019, Jordan Borean (@jborean93) <jborean93@gmail.com> 3# MIT License (see LICENSE or https://opensource.org/licenses/MIT) 4 5import logging 6 7from collections import ( 8 OrderedDict, 9) 10 11from smbprotocol import ( 12 Dialects, 13 MAX_PAYLOAD_SIZE, 14) 15 16from smbprotocol.create_contexts import ( 17 SMB2CreateContextRequest, 18) 19 20from smbprotocol.exceptions import ( 21 FileClosed, 22 SMBException, 23 SMBUnsupportedFeature, 24) 25 26from smbprotocol.file_info import ( 27 FileBothDirectoryInformation, 28 FileDirectoryInformation, 29 FileFullDirectoryInformation, 30 FileFullEaInformation, 31 FileIdBothDirectoryInformation, 32 FileIdFullDirectoryInformation, 33 FileNamesInformation, 34 FileStreamInformation, 35 InfoType, 36 37 # While this shouldn't ever be removed, we need to keep this imported so we stay backwards compat. These were 38 # originally defined here. 39 FileAttributes, 40 FileInformationClass, 41) 42 43from smbprotocol.header import ( 44 Commands, 45) 46 47from smbprotocol.structure import ( 48 BytesField, 49 DateTimeField, 50 EnumField, 51 FlagField, 52 IntField, 53 ListField, 54 Structure, 55 StructureField, 56) 57 58log = logging.getLogger(__name__) 59 60 61class RequestedOplockLevel(object): 62 """ 63 [MS-SMB2] v53.0 2017-09-15 64 65 2.2.31 SMB2 CREATE Request RequestedOplockLevel 66 The requested oplock level used when creating/accessing a file. 67 """ 68 SMB2_OPLOCK_LEVEL_NONE = 0x00 69 SMB2_OPLOCK_LEVEL_II = 0x01 70 SMB2_OPLOCK_LEVEL_EXCLUSIVE = 0x08 71 SMB2_OPLOCK_LEVEL_BATCH = 0x09 72 SMB2_OPLOCK_LEVEL_LEASE = 0xFF 73 74 75class ImpersonationLevel(object): 76 """ 77 [MS-SMB2] v53.0 2017-09-15 78 79 2.2.31 SMB2 CREATE Request ImpersonationLevel 80 The impersonation level requested by the application in a create request. 81 """ 82 Anonymous = 0x0 83 Identification = 0x1 84 Impersonation = 0x2 85 Delegate = 0x3 86 87 88class ShareAccess(object): 89 """ 90 [MS-SMB2] v53.0 2017-09-15 91 92 2.2.31 SMB2 CREATE Request ShareAccess 93 The sharing mode for the open 94 """ 95 FILE_SHARE_READ = 0x1 96 FILE_SHARE_WRITE = 0x2 97 FILE_SHARE_DELETE = 0x4 98 99 100class CreateDisposition(object): 101 """ 102 [MS-SMB2] v53.0 2017-09-15 103 104 2.2.31 SMB2 CREATE Request CreateDisposition 105 Defines the action the server must take if the file that is specific 106 already exists. 107 """ 108 FILE_SUPERSEDE = 0x0 109 FILE_OPEN = 0x1 110 FILE_CREATE = 0x2 111 FILE_OPEN_IF = 0x3 112 FILE_OVERWRITE = 0x4 113 FILE_OVERWRITE_IF = 0x5 114 115 116class CreateOptions(object): 117 """ 118 [MS-SMB2] v53.0 2017-09-15 119 120 2.2.31 SMB2 CREATE Request CreateOptions 121 Specifies the options to be applied when creating or opening the file 122 """ 123 FILE_DIRECTORY_FILE = 0x00000001 124 FILE_WRITE_THROUGH = 0x00000002 125 FILE_SEQUENTIAL_ONLY = 0x00000004 126 FILE_NO_INTERMEDIATE_BUFFERING = 0x00000008 127 FILE_SYNCHRONOUS_IO_ALERT = 0x00000010 128 FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020 129 FILE_NON_DIRECTORY_FILE = 0x00000040 130 FILE_COMPLETE_IF_OPLOCKED = 0x00000100 131 FILE_NO_EA_KNOWLEDGE = 0x00000200 132 FILE_RANDOM_ACCESS = 0x00000800 133 FILE_DELETE_ON_CLOSE = 0x00001000 134 FILE_OPEN_BY_FILE_ID = 0x00002000 135 FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000 136 FILE_NO_COMPRESSION = 0x00008000 137 FILE_OPEN_REMOTE_INSTANCE = 0x00000400 138 FILE_OPEN_REQUIRING_OPLOCK = 0x00010000 139 FILE_DISALLOW_EXCLUSIVE = 0x00020000 140 FILE_RESERVE_OPFILTER = 0x00100000 141 FILE_OPEN_REPARSE_POINT = 0x00200000 142 FILE_OPEN_NO_RECALL = 0x00400000 143 FILE_OPEN_FOR_FREE_SPACE_QUERY = 0x00800000 144 145 146class FilePipePrinterAccessMask(object): 147 """ 148 [MS-SMB2] v53.0 2017-09-15 149 150 2.2.13.1.1 File_Pipe_Printer_Access_Mask 151 Access Mask flag values to be used when accessing a file, pipe, or printer 152 """ 153 FILE_READ_DATA = 0x00000001 154 FILE_WRITE_DATA = 0x00000002 155 FILE_APPEND_DATA = 0x00000004 156 FILE_READ_EA = 0x00000008 157 FILE_WRITE_EA = 0x00000010 158 FILE_DELETE_CHILD = 0x00000040 159 FILE_EXECUTE = 0x00000020 160 FILE_READ_ATTRIBUTES = 0x00000080 161 FILE_WRITE_ATTRIBUTES = 0x00000100 162 DELETE = 0x00010000 163 READ_CONTROL = 0x00020000 164 WRITE_DAC = 0x00040000 165 WRITE_OWNER = 0x00080000 166 SYNCHRONIZE = 0x00100000 167 ACCESS_SYSTEM_SECURITY = 0x01000000 168 MAXIMUM_ALLOWED = 0x02000000 169 GENERIC_ALL = 0x10000000 170 GENERIC_EXECUTE = 0x20000000 171 GENERIC_WRITE = 0x40000000 172 GENERIC_READ = 0x80000000 173 174 175class DirectoryAccessMask(object): 176 """ 177 [MS-SMB2] v53.0 2017-09-15 178 179 2.2.13.1.2 Directory_Access_Mask 180 Access Mask flag values to be used when accessing a directory 181 """ 182 FILE_LIST_DIRECTORY = 0x00000001 183 FILE_ADD_FILE = 0x00000002 184 FILE_ADD_SUBDIRECTORY = 0x00000004 185 FILE_READ_EA = 0x00000008 186 FILE_WRITE_EA = 0x00000010 187 FILE_TRAVERSE = 0x00000020 188 FILE_DELETE_CHILD = 0x00000040 189 FILE_READ_ATTRIBUTES = 0x00000080 190 FILE_WRITE_ATTRIBUTES = 0x00000100 191 DELETE = 0x00010000 192 READ_CONTROL = 0x00020000 193 WRITE_DAC = 0x00040000 194 WRITE_OWNER = 0x00080000 195 SYNCHRONIZE = 0x00100000 196 ACCESS_SYSTEM_SECURITY = 0x01000000 197 MAXIMUM_ALLOWED = 0x02000000 198 GENERIC_ALL = 0x10000000 199 GENERIC_EXECUTE = 0x20000000 200 GENERIC_WRITE = 0x40000000 201 GENERIC_READ = 0x80000000 202 203 204class FileFlags(object): 205 """ 206 [MS-SMB2] v53.0 2017-09-15 207 208 2.2.14 SMB2 CREATE Response Flags 209 Flag that details info about the file that was opened. 210 """ 211 SMB2_CREATE_FLAG_REPARSEPOINT = 0x1 212 213 214class CreateAction(object): 215 """ 216 [MS-SMB2] v53.0 2017-09-15 217 218 2.2.14 SMB2 CREATE Response Flags 219 The action taken in establishing the open. 220 """ 221 FILE_SUPERSEDED = 0x0 222 FILE_OPENED = 0x1 223 FILE_CREATED = 0x2 224 FILE_OVERWRITTEN = 0x3 225 226 227class CloseFlags(object): 228 """ 229 [MS-SMB2] v53.0 2017-09-15 230 231 2.2.15 SMB2 CLOSE Request Flags 232 Flags to indicate how to process the operation 233 """ 234 SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB = 0x01 235 236 237class ReadFlags(object): 238 """ 239 [MS-SMB2] v53.0 2017-09-15 240 241 2.2.19 SMB2 READ Request Flags 242 Read flags for SMB 3.0.2 and newer dialects 243 """ 244 SMB2_READFLAG_READ_UNBUFFERED = 0x01 245 246 247class ReadWriteChannel(object): 248 """ 249 [MS-SMB2] v53.0 2017-09-15 250 251 2.2.19/21 SMB2 READ/Write Request Channel 252 Channel information for an SMB2 READ Request message 253 """ 254 SMB2_CHANNEL_NONE = 0x0 255 SMB2_CHANNEL_RDMA_V1 = 0x1 256 SMB2_CHANNEL_RDMA_V1_INVALIDATE = 0x2 257 258 259class WriteFlags(object): 260 """ 261 [MS-SMB2] v53.0 2017-09-15 262 263 2.2.21 SMB2 WRITE Request Flags 264 Flags to indicate how to process the operation 265 """ 266 SMB2_WRITEFLAG_WRITE_THROUGH = 0x00000001 267 SMB2_WRITEFLAG_WRITE_UNBUFFERED = 0x00000002 268 269 270class QueryDirectoryFlags(object): 271 """ 272 [MS-SMB2] v53.0 2017-09-15 273 274 2.2.33 SMB2 QUERY_DIRECTORY Request Flags 275 Indicates how the query directory operation MUST be processed. 276 """ 277 SMB2_RESTART_SCANS = 0x01 278 SMB2_RETURN_SINGLE_ENTRY = 0x02 279 SMB2_INDEX_SPECIFIED = 0x04 280 SMB2_REOPEN = 0x10 281 282 283class QueryInfoFlags(object): 284 """ 285 [MS-SMB2] 2.2.37 SMB2 QUERY_INFO Request - Flags 286 https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/d623b2f7-a5cd-4639-8cc9-71fa7d9f9ba9 287 288 Flags to set on a QUERY_INFO request. 289 """ 290 SL_RESTART_SCAN = 0x00000001 291 SL_RETURN_SINGLE_ENTRY = 0x00000002 292 SL_INDEX_SPECIFIED = 0x00000004 293 294 295class InfoAdditionalInformation(object): 296 """ 297 [MS-SMB2] 2.2.39 SMB2 SET_INFO Request - AdditionalInformation 298 https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/ee9614c4-be54-4a3c-98f1-769a7032a0e4 299 300 If security information is being set, this value must contains one or more of the following flags. 301 """ 302 OWNER_SECURTIY_INFORMATION = 0x00000001 303 GROUP_SECURITY_INFORMATION = 0x00000002 304 DACL_SECURITY_INFORMATION = 0x00000004 305 SACL_SECURITY_INFORMATION = 0x00000008 306 LABEL_SECURITY_INFORMATION = 0x00000010 307 ATTRIBUTE_SECURITY_INFORMATION = 0x00000020 308 SCOPE_SECURITY_INFORMATION = 0x00000040 309 BACKUP_SECURITY_INFORMATION = 0x00010000 310 311 312class SMB2CreateRequest(Structure): 313 """ 314 [MS-SMB2] v53.0 2017-09-15 315 316 2.2.13 SMB2 CREATE Request 317 The SMB2 Create Request packet is sent by a client to request either 318 creation of or access to a file. 319 """ 320 COMMAND = Commands.SMB2_CREATE 321 322 def __init__(self): 323 # pep 80 char issues force me to define this here 324 create_con_req = SMB2CreateContextRequest 325 self.fields = OrderedDict([ 326 ('structure_size', IntField( 327 size=2, 328 default=57, 329 )), 330 ('security_flags', IntField(size=1)), 331 ('requested_oplock_level', EnumField( 332 size=1, 333 enum_type=RequestedOplockLevel 334 )), 335 ('impersonation_level', EnumField( 336 size=4, 337 enum_type=ImpersonationLevel 338 )), 339 ('smb_create_flags', IntField(size=8)), 340 ('reserved', IntField(size=8)), 341 ('desired_access', IntField(size=4)), 342 ('file_attributes', IntField(size=4)), 343 ('share_access', FlagField( 344 size=4, 345 flag_type=ShareAccess 346 )), 347 ('create_disposition', EnumField( 348 size=4, 349 enum_type=CreateDisposition 350 )), 351 ('create_options', FlagField( 352 size=4, 353 flag_type=CreateOptions 354 )), 355 ('name_offset', IntField( 356 size=2, 357 default=120 # (header size 64) + (structure size 56) 358 )), 359 ('name_length', IntField( 360 size=2, 361 default=lambda s: self._name_length(s) 362 )), 363 ('create_contexts_offset', IntField( 364 size=4, 365 default=lambda s: self._create_contexts_offset(s) 366 )), 367 ('create_contexts_length', IntField( 368 size=4, 369 default=lambda s: len(s['buffer_contexts']) 370 )), 371 # Technically these are all under buffer but we split it to make 372 # things easier 373 ('buffer_path', BytesField( 374 size=lambda s: self._buffer_path_size(s), 375 )), 376 ('padding', BytesField( 377 size=lambda s: self._padding_size(s), 378 default=lambda s: b"\x00" * self._padding_size(s) 379 )), 380 ('buffer_contexts', ListField( 381 size=lambda s: s['create_contexts_length'].get_value(), 382 list_type=StructureField( 383 structure_type=create_con_req 384 ), 385 unpack_func=lambda s, d: self._buffer_context_list(s, d) 386 )) 387 ]) 388 super(SMB2CreateRequest, self).__init__() 389 390 def _name_length(self, structure): 391 buffer_path = structure['buffer_path'].get_value() 392 return len(buffer_path) if buffer_path != b"\x00\x00" else 0 393 394 def _create_contexts_offset(self, structure): 395 if len(structure['buffer_contexts']) == 0: 396 return 0 397 else: 398 return structure['name_offset'].get_value() + \ 399 len(structure['padding']) + len(structure['buffer_path']) 400 401 def _buffer_path_size(self, structure): 402 name_length = structure['name_length'].get_value() 403 return name_length if name_length != 0 else 2 404 405 def _padding_size(self, structure): 406 # no padding is needed if there are no contexts 407 if structure['create_contexts_length'].get_value() == 0: 408 return 0 409 410 mod = structure['name_length'].get_value() % 8 411 return 0 if mod == 0 else 8 - mod 412 413 def _buffer_context_list(self, structure, data): 414 context_list = [] 415 last_context = data == b"" 416 while not last_context: 417 create_context = SMB2CreateContextRequest() 418 data = create_context.unpack(data) 419 context_list.append(create_context) 420 last_context = create_context['next'].get_value() == 0 421 422 return context_list 423 424 425class SMB2CreateResponse(Structure): 426 """ 427 [MS-SMB2] v53.0 2017-09-15 428 429 2.2.14 SMB2 CREATE Response 430 The SMB2 Create Response packet is sent by the server to an SMB2 CREATE 431 Request. 432 """ 433 COMMAND = Commands.SMB2_CREATE 434 435 def __init__(self): 436 create_con_req = SMB2CreateContextRequest 437 self.fields = OrderedDict([ 438 ('structure_size', IntField( 439 size=2, 440 default=89 441 )), 442 ('oplock_level', EnumField( 443 size=1, 444 enum_type=RequestedOplockLevel 445 )), 446 ('flag', FlagField( 447 size=1, 448 flag_type=FileFlags 449 )), 450 ('create_action', EnumField( 451 size=4, 452 enum_type=CreateAction 453 )), 454 ('creation_time', DateTimeField(size=8)), 455 ('last_access_time', DateTimeField(size=8)), 456 ('last_write_time', DateTimeField(size=8)), 457 ('change_time', DateTimeField(size=8)), 458 ('allocation_size', IntField(size=8)), 459 ('end_of_file', IntField(size=8)), 460 ('file_attributes', FlagField( 461 size=4, 462 flag_type=FileAttributes, 463 flag_strict=False, 464 )), 465 ('reserved2', IntField(size=4)), 466 ('file_id', BytesField(size=16)), 467 ('create_contexts_offset', IntField( 468 size=4, 469 default=lambda s: self._create_contexts_offset(s) 470 )), 471 ('create_contexts_length', IntField( 472 size=4, 473 default=lambda s: len(s['buffer']) 474 )), 475 ('buffer', ListField( 476 size=lambda s: s['create_contexts_length'].get_value(), 477 list_type=StructureField( 478 structure_type=create_con_req 479 ), 480 unpack_func=lambda s, d: self._buffer_context_list(s, d) 481 )) 482 ]) 483 super(SMB2CreateResponse, self).__init__() 484 485 def _create_contexts_offset(self, structure): 486 if len(structure['buffer']) == 0: 487 return 0 488 else: 489 return 152 490 491 def _buffer_context_list(self, structure, data): 492 context_list = [] 493 last_context = data == b"" 494 while not last_context: 495 create_context = SMB2CreateContextRequest() 496 data = create_context.unpack(data) 497 context_list.append(create_context) 498 # Manually make sure the final padding is present 499 create_context['padding2'] = b"\x00" * create_context._padding2_size(create_context) 500 last_context = create_context['next'].get_value() == 0 501 502 return context_list 503 504 505class SMB2CloseRequest(Structure): 506 """ 507 [MS-SMB2] v53.0 2017-09-15 508 509 2.2.15 SMB2 CLOSE Request 510 Used by the client to close an instance of a file 511 """ 512 COMMAND = Commands.SMB2_CLOSE 513 514 def __init__(self): 515 self.fields = OrderedDict([ 516 ('structure_size', IntField( 517 size=2, 518 default=24 519 )), 520 ('flags', FlagField( 521 size=2, 522 flag_type=CloseFlags 523 )), 524 ('reserved', IntField(size=4)), 525 ('file_id', BytesField(size=16)), 526 ]) 527 super(SMB2CloseRequest, self).__init__() 528 529 530class SMB2CloseResponse(Structure): 531 """ 532 [MS-SMB2] v53.0 2017-09-15 533 534 2.2.16 SMB2 CLOSE Response 535 The response of a SMB2 CLOSE Request 536 """ 537 COMMAND = Commands.SMB2_CLOSE 538 539 def __init__(self): 540 self.fields = OrderedDict([ 541 ('structure_size', IntField( 542 size=2, 543 default=60 544 )), 545 ('flags', FlagField( 546 size=2, 547 flag_type=CloseFlags 548 )), 549 ('reserved', IntField(size=4)), 550 ('creation_time', DateTimeField()), 551 ('last_access_time', DateTimeField()), 552 ('last_write_time', DateTimeField()), 553 ('change_time', DateTimeField()), 554 ('allocation_size', IntField(size=8)), 555 ('end_of_file', IntField(size=8)), 556 ('file_attributes', FlagField( 557 size=4, 558 flag_type=FileAttributes, 559 flag_strict=False, 560 )) 561 ]) 562 super(SMB2CloseResponse, self).__init__() 563 564 565class SMB2FlushRequest(Structure): 566 """ 567 [MS-SMB2] v53.0 2017-09-15 568 569 2.2.17 SMB2 FLUSH Request 570 Flush all cached file information for a specified open of a file to the 571 persistent store that backs the file. 572 """ 573 COMMAND = Commands.SMB2_FLUSH 574 575 def __init__(self): 576 self.fields = OrderedDict([ 577 ('structure_size', IntField( 578 size=2, 579 default=24 580 )), 581 ('reserved1', IntField(size=2)), 582 ('reserved2', IntField(size=4)), 583 ('file_id', BytesField(size=16)), 584 ]) 585 super(SMB2FlushRequest, self).__init__() 586 587 588class SMB2FlushResponse(Structure): 589 """ 590 [MS-SMB2] v53.0 2017-09-15 591 592 2.2.18 SMB2 FLUSH Response 593 SMB2 FLUSH Response packet sent by the server. 594 """ 595 COMMAND = Commands.SMB2_FLUSH 596 597 def __init__(self): 598 self.fields = OrderedDict([ 599 ('structure_size', IntField( 600 size=2, 601 default=4 602 )), 603 ('reserved', IntField(size=2)) 604 ]) 605 super(SMB2FlushResponse, self).__init__() 606 607 608class SMB2ReadRequest(Structure): 609 """ 610 [MS-SMB2] v53.0 2017-09-15 611 612 2.2.19 SMB2 READ Request 613 The request is used to run a read operation on the file specified. 614 """ 615 COMMAND = Commands.SMB2_READ 616 617 def __init__(self): 618 self.fields = OrderedDict([ 619 ('structure_size', IntField( 620 size=2, 621 default=49 622 )), 623 ('padding', IntField(size=1)), 624 ('flags', FlagField( 625 size=1, 626 flag_type=ReadFlags 627 )), 628 ('length', IntField( 629 size=4 630 )), 631 ('offset', IntField( 632 size=8 633 )), 634 ('file_id', BytesField(size=16)), 635 ('minimum_count', IntField( 636 size=4 637 )), 638 ('channel', FlagField( 639 size=4, 640 flag_type=ReadWriteChannel 641 )), 642 ('remaining_bytes', IntField(size=4)), 643 ('read_channel_info_offset', IntField( 644 size=2, 645 default=lambda s: self._get_read_channel_info_offset(s) 646 )), 647 ('read_channel_info_length', IntField( 648 size=2, 649 default=lambda s: self._get_read_channel_info_length(s) 650 )), 651 ('buffer', BytesField( 652 size=lambda s: self._get_buffer_length(s), 653 default=b"\x00" 654 )) 655 ]) 656 super(SMB2ReadRequest, self).__init__() 657 658 def _get_read_channel_info_offset(self, structure): 659 if structure['channel'].get_value() == 0: 660 return 0 661 else: 662 return 64 + structure['structure_size'].get_value() - 1 663 664 def _get_read_channel_info_length(self, structure): 665 if structure['channel'].get_value() == 0: 666 return 0 667 else: 668 return len(structure['buffer'].get_value()) 669 670 def _get_buffer_length(self, structure): 671 # buffer should contain 1 byte of \x00 and not be empty 672 if structure['channel'].get_value() == 0: 673 return 1 674 else: 675 return structure['read_channel_info_length'].get_value() 676 677 678class SMB2ReadResponse(Structure): 679 """ 680 [MS-SMB2] v53.0 2017-09-15 681 682 2.2.20 SMB2 READ Response 683 Response to an SMB2 READ Request. 684 """ 685 COMMAND = Commands.SMB2_READ 686 687 def __init__(self): 688 self.fields = OrderedDict([ 689 ('structure_size', IntField( 690 size=2, 691 default=17 692 )), 693 ('data_offset', IntField(size=1)), 694 ('reserved', IntField(size=1)), 695 ('data_length', IntField( 696 size=4, 697 default=lambda s: len(s['buffer']) 698 )), 699 ('data_remaining', IntField(size=4)), 700 ('reserved2', IntField(size=4)), 701 ('buffer', BytesField( 702 size=lambda s: s['data_length'].get_value() 703 )) 704 ]) 705 super(SMB2ReadResponse, self).__init__() 706 707 708class SMB2WriteRequest(Structure): 709 """ 710 [MS-SMB2] v53.0 2017-09-15 711 712 2.2.21 SMB2 WRITE Request 713 A write packet to sent to an open file or named pipe on the server 714 """ 715 COMMAND = Commands.SMB2_WRITE 716 717 def __init__(self): 718 self.fields = OrderedDict([ 719 ('structure_size', IntField( 720 size=2, 721 default=49 722 )), 723 ('data_offset', IntField( # offset to the buffer field 724 size=2, 725 default=0x70 # seems to be hardcoded to this value 726 )), 727 ('length', IntField( 728 size=4, 729 default=lambda s: len(s['buffer']) 730 )), 731 ('offset', IntField(size=8)), # the offset in the file of the data 732 ('file_id', BytesField(size=16)), 733 ('channel', FlagField( 734 size=4, 735 flag_type=ReadWriteChannel 736 )), 737 ('remaining_bytes', IntField(size=4)), 738 ('write_channel_info_offset', IntField( 739 size=2, 740 default=lambda s: self._get_write_channel_info_offset(s) 741 )), 742 ('write_channel_info_length', IntField( 743 size=2, 744 default=lambda s: len(s['buffer_channel_info']) 745 )), 746 ('flags', FlagField( 747 size=4, 748 flag_type=WriteFlags 749 )), 750 ('buffer', BytesField( 751 size=lambda s: s['length'].get_value() 752 )), 753 ('buffer_channel_info', BytesField( 754 size=lambda s: s['write_channel_info_length'].get_value() 755 )) 756 ]) 757 super(SMB2WriteRequest, self).__init__() 758 759 def _get_write_channel_info_offset(self, structure): 760 if len(structure['buffer_channel_info']) == 0: 761 return 0 762 else: 763 header_size = 64 764 packet_size = structure['structure_size'].get_value() - 1 765 buffer_size = len(structure['buffer']) 766 return header_size + packet_size + buffer_size 767 768 769class SMB2WriteResponse(Structure): 770 """ 771 [MS-SMB2] v53.0 2017-09-15 772 773 2.2.22 SMB2 WRITE Response 774 The response to the SMB2 WRITE Request sent by the server 775 """ 776 COMMAND = Commands.SMB2_WRITE 777 778 def __init__(self): 779 self.fields = OrderedDict([ 780 ('structure_size', IntField( 781 size=2, 782 default=17 783 )), 784 ('reserved', IntField(size=2)), 785 ('count', IntField(size=4)), 786 ('remaining', IntField(size=4)), 787 ('write_channel_info_offset', IntField(size=2)), 788 ('write_channel_info_length', IntField(size=2)) 789 ]) 790 super(SMB2WriteResponse, self).__init__() 791 792 793class SMB2QueryDirectoryRequest(Structure): 794 """ 795 [MS-SMB2] v53.0 2017-09-15 796 797 2.2.33 QUERY_DIRECTORY Request 798 Used by the client to obtain a directory enumeration on a directory open. 799 """ 800 COMMAND = Commands.SMB2_QUERY_DIRECTORY 801 802 def __init__(self): 803 self.fields = OrderedDict([ 804 ('structure_size', IntField( 805 size=2, 806 default=33 807 )), 808 ('file_information_class', EnumField( 809 size=1, 810 enum_type=FileInformationClass 811 )), 812 ('flags', FlagField( 813 size=1, 814 flag_type=QueryDirectoryFlags 815 )), 816 ('file_index', IntField(size=4)), 817 ('file_id', BytesField(size=16)), 818 ('file_name_offset', IntField( 819 size=2, 820 default=lambda s: 0 if len(s['buffer']) == 0 else 96 821 )), 822 ('file_name_length', IntField( 823 size=2, 824 default=lambda s: len(s['buffer']) 825 )), 826 ('output_buffer_length', IntField(size=4)), 827 # UTF-16-LE encoded search pattern 828 ('buffer', BytesField( 829 size=lambda s: s['file_name_length'].get_value() 830 )) 831 ]) 832 super(SMB2QueryDirectoryRequest, self).__init__() 833 834 @staticmethod 835 def unpack_response(file_information_class, buffer): 836 """ 837 Pass in the buffer value from the response object to unpack it and 838 return a list of query response structures for the request. 839 840 :param file_information_class: The info class that represents the 841 buffer. 842 :param buffer: The raw bytes value of the SMB2QueryDirectoryResponse 843 buffer field. 844 :return: List of query_info.* structures based on the 845 FileInformationClass used in the initial query request. 846 """ 847 resp_structure = { 848 FileInformationClass.FILE_DIRECTORY_INFORMATION: FileDirectoryInformation, 849 FileInformationClass.FILE_NAMES_INFORMATION: FileNamesInformation, 850 FileInformationClass.FILE_BOTH_DIRECTORY_INFORMATION: FileBothDirectoryInformation, 851 FileInformationClass.FILE_ID_BOTH_DIRECTORY_INFORMATION: FileIdBothDirectoryInformation, 852 FileInformationClass.FILE_FULL_DIRECTORY_INFORMATION: FileFullDirectoryInformation, 853 FileInformationClass.FILE_ID_FULL_DIRECTORY_INFORMATION: FileIdFullDirectoryInformation, 854 }[file_information_class] 855 query_results = [] 856 857 current_offset = 0 858 is_next = True 859 while is_next: 860 result = resp_structure() 861 result.unpack(buffer[current_offset:]) 862 query_results.append(result) 863 current_offset += result['next_entry_offset'].get_value() 864 is_next = result['next_entry_offset'].get_value() != 0 865 866 return query_results 867 868 869class SMB2QueryDirectoryResponse(Structure): 870 """ 871 [MS-SMB2] v53.0 2017-09-15 872 873 2.2.34 SMB2 QUERY_DIRECTORY Response 874 Response to an SMB2 QUERY_DIRECTORY Request. 875 """ 876 877 COMMAND = Commands.SMB2_QUERY_DIRECTORY 878 879 def __init__(self): 880 self.fields = OrderedDict([ 881 ('structure_size', IntField( 882 size=2, 883 default=9 884 )), 885 ('output_buffer_offset', IntField( 886 size=2, 887 default=72 888 )), 889 ('output_buffer_length', IntField( 890 size=4, 891 default=lambda s: len(s['buffer']) 892 )), 893 # this structure varies based on the requested information class 894 ('buffer', BytesField( 895 size=lambda s: s['output_buffer_length'].get_value() 896 )) 897 ]) 898 super(SMB2QueryDirectoryResponse, self).__init__() 899 900 901class SMB2QueryInfoRequest(Structure): 902 """ 903 [MS-SMB2] 2.2.37 SMB2 QUERY_INFO Request 904 https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/d623b2f7-a5cd-4639-8cc9-71fa7d9f9ba9 905 906 Sent by a client to request information on a file, named pipe, or underlying volume. 907 """ 908 COMMAND = Commands.SMB2_QUERY_INFO 909 910 def __init__(self): 911 self.fields = OrderedDict([ 912 ('structure_size', IntField( 913 size=2, 914 default=41, 915 )), 916 ('info_type', EnumField( 917 size=1, 918 enum_type=InfoType, 919 )), 920 ('file_info_class', EnumField( 921 size=1, 922 enum_type=FileInformationClass, 923 default=FileInformationClass.FILE_NONE 924 )), 925 ('output_buffer_length', IntField( 926 size=4, 927 default=lambda s: 0, 928 )), 929 ('input_buffer_offset', IntField( 930 size=2, 931 default=lambda s: 0 if s['input_buffer_length'].get_value() == 0 else 104, 932 )), 933 ('reserved', IntField(size=2)), 934 ('input_buffer_length', IntField( 935 size=4, 936 default=lambda s: len(s['buffer']), 937 )), 938 ('additional_information', IntField(size=4)), 939 ('flags', FlagField( 940 size=4, 941 flag_type=QueryInfoFlags, 942 )), 943 ('file_id', BytesField(size=16)), 944 ('buffer', BytesField( 945 size=lambda s: s['input_buffer_length'].get_value(), 946 )), 947 ]) 948 super(SMB2QueryInfoRequest, self).__init__() 949 950 951class SMB2QueryInfoResponse(Structure): 952 """ 953 [MS-SMB2] 2.2.38 SMB2 QUERY_INFO Response 954 https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/3b1b3598-a898-44ca-bfac-2dcae065247f 955 956 Sent by the server in response to an SMB2QueryInfoRequest. 957 """ 958 COMMAND = Commands.SMB2_QUERY_INFO 959 960 def __init__(self): 961 self.fields = OrderedDict([ 962 ('structure_size', IntField( 963 size=2, 964 default=9, 965 )), 966 ('output_buffer_offset', IntField( 967 size=2, 968 default=72, 969 )), 970 ('output_buffer_length', IntField( 971 size=4, 972 default=lambda s: len(s['buffer']), 973 )), 974 ('buffer', BytesField( 975 size=lambda s: s['output_buffer_length'].get_value(), 976 )), 977 ]) 978 super(SMB2QueryInfoResponse, self).__init__() 979 980 def parse_buffer(self, file_info_type): 981 buffer = self['buffer'].get_value() 982 983 def unpack_list(buffer, byte_boundary): 984 info_list = [] 985 while buffer: 986 entry = file_info_type() 987 buffer = entry.unpack(buffer) 988 989 padded_size = len(entry) % byte_boundary 990 buffer_offset = (byte_boundary - padded_size) if padded_size else 0 991 buffer = buffer[buffer_offset:] 992 993 info_list.append(entry) 994 995 return info_list 996 997 file_obj = file_info_type() 998 if isinstance(file_obj, FileFullEaInformation): 999 return unpack_list(buffer, 4) 1000 elif isinstance(file_obj, FileStreamInformation): 1001 return unpack_list(buffer, 8) 1002 else: 1003 file_obj.unpack(buffer) 1004 return file_obj 1005 1006 1007class SMB2SetInfoRequest(Structure): 1008 """ 1009 [MS-SMB2] 2.2.39 SMB2 SET_INFO Request 1010 https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/ee9614c4-be54-4a3c-98f1-769a7032a0e4 1011 1012 Sent by a client to set information on a file or underlying object store. 1013 """ 1014 COMMAND = Commands.SMB2_SET_INFO 1015 1016 def __init__(self): 1017 self.fields = OrderedDict([ 1018 ('structure_size', IntField( 1019 size=2, 1020 default=33, 1021 )), 1022 ('info_type', EnumField( 1023 size=1, 1024 enum_type=InfoType, 1025 )), 1026 ('file_info_class', EnumField( 1027 size=1, 1028 enum_type=FileInformationClass, 1029 default=FileInformationClass.FILE_NONE 1030 )), 1031 ('buffer_length', IntField( 1032 size=4, 1033 default=lambda s: len(s['buffer']), 1034 )), 1035 ('buffer_offset', IntField( 1036 size=2, 1037 default=96 1038 )), 1039 ('reserved', IntField(size=2)), 1040 ('additional_information', FlagField( 1041 size=4, 1042 flag_type=InfoAdditionalInformation, 1043 )), 1044 ('file_id', BytesField(size=16)), 1045 ('buffer', BytesField( 1046 size=lambda s: s['buffer_length'].get_value() 1047 )) 1048 ]) 1049 super(SMB2SetInfoRequest, self).__init__() 1050 1051 1052class SMB2SetInfoResponse(Structure): 1053 """ 1054 [MS-SMB2] 2.2.40 SMB2 SET_INFO Response 1055 https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/c4318eb4-bdab-49b7-9352-abd7005c7f19 1056 1057 Sent by the server in response to an SMB2SetInfoRequest to notify the client that its request has been successfully 1058 processed. 1059 """ 1060 COMMAND = Commands.SMB2_SET_INFO 1061 1062 def __init__(self): 1063 self.fields = OrderedDict([ 1064 ('structure_size', IntField( 1065 size=2, 1066 default=2 1067 )), 1068 ]) 1069 super(SMB2SetInfoResponse, self).__init__() 1070 1071 1072class Open(object): 1073 1074 def __init__(self, tree, name): 1075 """ 1076 [MS-SMB2] v53.0 2017-09-15 1077 1078 3.2.1.6 Per Application Open of a File 1079 Attributes per each open of a file. A file can be a File, Pipe, 1080 Directory, or Printer 1081 1082 :param tree: The Tree (share) the file is located in. 1083 :param name: The name of the file, excluding the share path. 1084 """ 1085 # properties available based on the file itself 1086 self._connected = False 1087 self.create_action = None 1088 self.creation_time = None 1089 self.last_access_time = None 1090 self.last_write_time = None 1091 self.change_time = None 1092 self.allocation_size = None 1093 self.end_of_file = None 1094 self.file_attributes = None 1095 1096 # properties used privately 1097 # set to { 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF } to allow message 1098 # compounding with the open as the first message, once opened this 1099 # will be overwritten by the open response 1100 self.file_id = b"\xff" * 16 1101 self.tree_connect = tree 1102 self.connection = tree.session.connection 1103 self.oplock_level = None 1104 self.durable = None 1105 self.file_name = name 1106 self.resilient_handle = None 1107 self.last_disconnect_time = None 1108 self.resilient_timeout = None 1109 1110 # an array of entries used to maintain information about outstanding 1111 # lock and unlock operations performed on resilient Opens. Contains 1112 # sequence_number - 4-bit integer modulo 16 1113 # free - boolean value where False is no outstanding requests 1114 self.operation_buckets = [] 1115 1116 # SMB 3.x+ 1117 self.durable_timeout = None 1118 1119 # Table of outstanding requests, lookup by Request.cancel_id, 1120 # message_id 1121 self.outstanding_requests = {} 1122 1123 self.create_guid = None 1124 self.is_persistent = None 1125 self.desired_access = None 1126 self.share_mode = None 1127 self.create_options = None 1128 self.file_attributes = None 1129 self.create_disposition = None 1130 1131 @property 1132 def connected(self): 1133 return self._connected 1134 1135 def create(self, impersonation_level, desired_access, file_attributes, 1136 share_access, create_disposition, create_options, 1137 create_contexts=None, 1138 oplock_level=RequestedOplockLevel.SMB2_OPLOCK_LEVEL_NONE, 1139 send=True): 1140 """ 1141 This will open the file based on the input parameters supplied. Any 1142 file open should also be called with Open.close() when it is finished. 1143 1144 More details on how each option affects the open process can be found 1145 here https://msdn.microsoft.com/en-us/library/cc246502.aspx. 1146 1147 Supports out of band send function, call this function with send=False 1148 to return a tuple of (SMB2CreateRequest, receive_func) instead of 1149 sending the the request and waiting for the response. The receive_func 1150 can be used to get the response from the server by passing in the 1151 Request that was used to sent it out of band. 1152 1153 :param impersonation_level: (ImpersonationLevel) The type of 1154 impersonation level that is issuing the create request. 1155 :param desired_access: The level of access that is required of the 1156 open. FilePipePrinterAccessMask or DirectoryAccessMask should be 1157 used depending on the type of file being opened. 1158 :param file_attributes: (FileAttributes) attributes to set on the file 1159 being opened, this usually is for opens that creates a file. 1160 :param share_access: (ShareAccess) Specifies the sharing mode for the 1161 open. 1162 :param create_disposition: (CreateDisposition) Defines the action the 1163 server MUST take if the file already exists. 1164 :param create_options: (CreateOptions) Specifies the options to be 1165 applied when creating or opening the file. 1166 :param create_contexts: (List<SMB2CreateContextRequest>) List of 1167 context request values to be applied to the create. 1168 1169 Create Contexts are used to encode additional flags and attributes when 1170 opening files. More details on create context request values can be 1171 found here https://msdn.microsoft.com/en-us/library/cc246504.aspx. 1172 1173 :param oplock_level: The requested oplock level of the request. 1174 :param send: Whether to send the request in the same call or return the 1175 message to the caller and the unpack function 1176 1177 :return: List of context response values or None if there are no 1178 context response values. If the context response value is not known 1179 to smbprotocol then the list value would be raw bytes otherwise 1180 it is a Structure defined in create_contexts.py 1181 """ 1182 create = SMB2CreateRequest() 1183 create['requested_oplock_level'] = oplock_level 1184 create['impersonation_level'] = impersonation_level 1185 create['desired_access'] = desired_access 1186 create['file_attributes'] = file_attributes 1187 create['share_access'] = share_access 1188 create['create_disposition'] = create_disposition 1189 create['create_options'] = create_options 1190 if self.file_name == "": 1191 create['buffer_path'] = b"\x00\x00" 1192 else: 1193 create['buffer_path'] = self.file_name.encode('utf-16-le') 1194 if create_contexts: 1195 create['buffer_contexts'] = SMB2CreateContextRequest.pack_multiple(create_contexts) 1196 1197 if self.connection.dialect >= Dialects.SMB_3_0_0: 1198 self.desired_access = desired_access 1199 self.share_mode = share_access 1200 self.create_options = create_options 1201 self.file_attributes = file_attributes 1202 self.create_disposition = create_disposition 1203 1204 if not send: 1205 return create, self._create_response 1206 1207 log.info("Session: %s, Tree Connect: %s - sending SMB2 Create Request " 1208 "for file %s" % (self.tree_connect.session.username, 1209 self.tree_connect.share_name, 1210 self.file_name)) 1211 1212 log.debug(create) 1213 request = self.connection.send(create, 1214 self.tree_connect.session.session_id, 1215 self.tree_connect.tree_connect_id) 1216 return self._create_response(request) 1217 1218 def _create_response(self, request): 1219 log.info("Session: %s, Tree Connect: %s - receiving SMB2 Create " 1220 "Response" % (self.tree_connect.session.username, 1221 self.tree_connect.share_name)) 1222 response = self.connection.receive(request) 1223 create_response = SMB2CreateResponse() 1224 create_response.unpack(response['data'].get_value()) 1225 1226 # Manually set the length so the debug log won't fail due to some servers returning a padded value which is not 1227 # reflected in the padding2 of the context response. 1228 create_response['create_contexts_length'] = len(create_response['buffer']) 1229 1230 self._connected = True 1231 log.debug(create_response) 1232 1233 self.file_id = create_response['file_id'].get_value() 1234 self.tree_connect.session.open_table[self.file_id] = self 1235 self.oplock_level = create_response['oplock_level'].get_value() 1236 self.durable = False 1237 self.resilient_handle = False 1238 self.last_disconnect_time = 0 1239 1240 self.create_action = create_response['create_action'].get_value() 1241 self.creation_time = create_response['creation_time'].get_value() 1242 self.last_access_time = create_response['last_access_time'].get_value() 1243 self.last_write_time = create_response['last_write_time'].get_value() 1244 self.change_time = create_response['change_time'].get_value() 1245 self.allocation_size = create_response['allocation_size'].get_value() 1246 self.end_of_file = create_response['end_of_file'].get_value() 1247 self.file_attributes = create_response['file_attributes'].get_value() 1248 1249 create_contexts_response = None 1250 if create_response['create_contexts_length'].get_value() > 0: 1251 create_contexts_response = [] 1252 for context in create_response['buffer'].get_value(): 1253 create_contexts_response.append(context.get_context_data()) 1254 1255 return create_contexts_response 1256 1257 def read(self, offset, length, min_length=0, unbuffered=False, wait=True, 1258 send=True): 1259 """ 1260 Reads from an opened file or pipe 1261 1262 Supports out of band send function, call this function with send=False 1263 to return a tuple of (SMB2ReadRequest, receive_func) instead of 1264 sending the the request and waiting for the response. The receive_func 1265 can be used to get the response from the server by passing in the 1266 Request that was used to sent it out of band. 1267 1268 :param offset: The offset to start the read of the file. 1269 :param length: The number of bytes to read from the offset. 1270 :param min_length: The minimum number of bytes to be read for a 1271 successful operation. 1272 :param unbuffered: Whether to the server should cache the read data at 1273 intermediate layers, only value for SMB 3.0.2 or newer 1274 :param wait: If send=True, whether to wait for a response if 1275 STATUS_PENDING was received from the server or fail. 1276 :param send: Whether to send the request in the same call or return the 1277 message to the caller and the unpack function 1278 :return: A byte string of the bytes read 1279 """ 1280 if length > self.connection.max_read_size: 1281 raise SMBException("The requested read length %d is greater than " 1282 "the maximum negotiated read size %d" 1283 % (length, self.connection.max_read_size)) 1284 1285 read = SMB2ReadRequest() 1286 read['length'] = length 1287 read['offset'] = offset 1288 read['minimum_count'] = min_length 1289 read['file_id'] = self.file_id 1290 read['padding'] = b"\x50" 1291 1292 if unbuffered: 1293 if self.connection.dialect < Dialects.SMB_3_0_2: 1294 raise SMBUnsupportedFeature(self.connection.dialect, 1295 Dialects.SMB_3_0_2, 1296 "SMB2_READFLAG_READ_UNBUFFERED", 1297 True) 1298 read['flags'].set_flag(ReadFlags.SMB2_READFLAG_READ_UNBUFFERED) 1299 1300 if not send: 1301 return read, self._read_response 1302 1303 log.info("Session: %s, Tree Connect ID: %s - sending SMB2 Read " 1304 "Request for file %s" % (self.tree_connect.session.username, 1305 self.tree_connect.share_name, 1306 self.file_name)) 1307 log.debug(read) 1308 request = self.connection.send(read, 1309 self.tree_connect.session.session_id, 1310 self.tree_connect.tree_connect_id) 1311 return self._read_response(request, wait) 1312 1313 def _read_response(self, request, wait=True): 1314 log.info("Session: %s, Tree Connect ID: %s - receiving SMB2 Read " 1315 "Response" % (self.tree_connect.session.username, 1316 self.tree_connect.share_name)) 1317 response = self.connection.receive(request, wait=wait) 1318 read_response = SMB2ReadResponse() 1319 read_response.unpack(response['data'].get_value()) 1320 log.debug(read_response) 1321 1322 return read_response['buffer'].get_value() 1323 1324 def write(self, data, offset=0, write_through=False, unbuffered=False, 1325 wait=True, send=True): 1326 """ 1327 Writes data to an opened file. 1328 1329 Supports out of band send function, call this function with send=False 1330 to return a tuple of (SMBWriteRequest, receive_func) instead of 1331 sending the the request and waiting for the response. The receive_func 1332 can be used to get the response from the server by passing in the 1333 Request that was used to sent it out of band. 1334 1335 :param data: The bytes data to write. 1336 :param offset: The offset in the file to write the bytes at 1337 :param write_through: Whether written data is persisted to the 1338 underlying storage, not valid for SMB 2.0.2. 1339 :param unbuffered: Whether to the server should cache the write data at 1340 intermediate layers, only value for SMB 3.0.2 or newer 1341 :param wait: If send=True, whether to wait for a response if 1342 STATUS_PENDING was received from the server or fail. 1343 :param send: Whether to send the request in the same call or return the 1344 message to the caller and the unpack function 1345 :return: The number of bytes written 1346 """ 1347 data_len = len(data) 1348 if data_len > self.connection.max_write_size: 1349 raise SMBException("The requested write length %d is greater than " 1350 "the maximum negotiated write size %d" 1351 % (data_len, self.connection.max_write_size)) 1352 1353 write = SMB2WriteRequest() 1354 write['length'] = len(data) 1355 write['offset'] = offset 1356 write['file_id'] = self.file_id 1357 write['buffer'] = data 1358 1359 if write_through: 1360 if self.connection.dialect < Dialects.SMB_2_1_0: 1361 raise SMBUnsupportedFeature(self.connection.dialect, 1362 Dialects.SMB_2_1_0, 1363 "SMB2_WRITEFLAG_WRITE_THROUGH", 1364 True) 1365 write['flags'].set_flag(WriteFlags.SMB2_WRITEFLAG_WRITE_THROUGH) 1366 1367 if unbuffered: 1368 if self.connection.dialect < Dialects.SMB_3_0_2: 1369 raise SMBUnsupportedFeature(self.connection.dialect, 1370 Dialects.SMB_3_0_2, 1371 "SMB2_WRITEFLAG_WRITE_UNBUFFERED", 1372 True) 1373 write['flags'].set_flag(WriteFlags.SMB2_WRITEFLAG_WRITE_UNBUFFERED) 1374 1375 if not send: 1376 return write, self._write_response 1377 1378 log.info("Session: %s, Tree Connect: %s - sending SMB2 Write Request " 1379 "for file %s" % (self.tree_connect.session.username, 1380 self.tree_connect.share_name, 1381 self.file_name)) 1382 log.debug(write) 1383 request = self.connection.send(write, 1384 self.tree_connect.session.session_id, 1385 self.tree_connect.tree_connect_id) 1386 return self._write_response(request, wait) 1387 1388 def _write_response(self, request, wait=True): 1389 log.info("Session: %s, Tree Connect: %s - receiving SMB2 Write " 1390 "Response" % (self.tree_connect.session.username, 1391 self.tree_connect.share_name)) 1392 response = self.connection.receive(request, wait=wait) 1393 write_response = SMB2WriteResponse() 1394 write_response.unpack(response['data'].get_value()) 1395 log.debug(write_response) 1396 1397 return write_response['count'].get_value() 1398 1399 def flush(self, send=True): 1400 """ 1401 A command sent by the client to request that a server flush all cached 1402 file information for the opened file. 1403 1404 Supports out of band send function, call this function with send=False 1405 to return a tuple of (SMB2FlushRequest, receive_func) instead of 1406 sending the the request and waiting for the response. The receive_func 1407 can be used to get the response from the server by passing in the 1408 Request that was used to sent it out of band. 1409 1410 :param send: Whether to send the request in the same call or return the 1411 message to the caller and the unpack function 1412 :return: The SMB2FlushResponse received from the server 1413 """ 1414 flush = SMB2FlushRequest() 1415 flush['file_id'] = self.file_id 1416 1417 if not send: 1418 return flush, self._flush_response 1419 1420 log.info("Session: %s, Tree Connect: %s - sending SMB2 Flush Request " 1421 "for file %s" % (self.tree_connect.session.username, 1422 self.tree_connect.share_name, 1423 self.file_name)) 1424 log.debug(flush) 1425 request = self.connection.send(flush, 1426 self.tree_connect.session.session_id, 1427 self.tree_connect.tree_connect_id) 1428 return self._flush_response(request) 1429 1430 def _flush_response(self, request): 1431 log.info("Session: %s, Tree Connect: %s - receiving SMB2 Flush " 1432 "Response" % (self.tree_connect.session.username, 1433 self.tree_connect.share_name)) 1434 response = self.connection.receive(request) 1435 flush_response = SMB2FlushResponse() 1436 flush_response.unpack(response['data'].get_value()) 1437 log.debug(flush_response) 1438 return flush_response 1439 1440 def query_directory(self, pattern, file_information_class, flags=None, 1441 file_index=0, max_output=MAX_PAYLOAD_SIZE, send=True): 1442 """ 1443 Run a Query/Find on an opened directory based on the params passed in. 1444 1445 Supports out of band send function, call this function with send=False 1446 to return a tuple of (SMB2QueryDirectoryRequest, receive_func) instead 1447 of sending the the request and waiting for the response. The 1448 receive_func can be used to get the response from the server by passing 1449 in the Request that was used to sent it out of band. 1450 1451 :param pattern: The string pattern to use for the query, this pattern 1452 format is based on the SMB server but * is usually a wildcard 1453 :param file_information_class: FileInformationClass that defines the 1454 format of the result that is returned 1455 :param flags: QueryDirectoryFlags that control how the operation must 1456 be processed. 1457 :param file_index: If the flags SMB2_INDEX_SPECIFIED, this is the index 1458 the query should resume on, otherwise should be 0 1459 :param max_output: The maximum output size, defaulted to the max credit 1460 size but can be increased to reduced round trip operations. 1461 :param send: Whether to send the request in the same call or return the 1462 message to the caller and the unpack function 1463 :return: A list of structures defined in query_info.py, the list entry 1464 structure is based on the value of file_information_class in the 1465 request message 1466 """ 1467 query = SMB2QueryDirectoryRequest() 1468 query['file_information_class'] = file_information_class 1469 query['flags'] = flags 1470 query['file_index'] = file_index 1471 query['file_id'] = self.file_id 1472 query['output_buffer_length'] = max_output 1473 query['buffer'] = pattern.encode('utf-16-le') 1474 1475 if not send: 1476 return query, self._query_directory_response 1477 1478 log.info("Session: %s, Tree Connect: %s - sending SMB2 Query " 1479 "Directory Request for directory %s" 1480 % (self.tree_connect.session.username, 1481 self.tree_connect.share_name, self.file_name)) 1482 log.debug(query) 1483 request = self.connection.send(query, 1484 self.tree_connect.session.session_id, 1485 self.tree_connect.tree_connect_id) 1486 return self._query_directory_response(request) 1487 1488 def _query_directory_response(self, request): 1489 log.info("Session: %s, Tree Connect: %s - receiving SMB2 Query " 1490 "Response" % (self.tree_connect.session.username, 1491 self.tree_connect.share_name)) 1492 response = self.connection.receive(request) 1493 query_response = SMB2QueryDirectoryResponse() 1494 query_response.unpack(response['data'].get_value()) 1495 log.debug(query_response) 1496 1497 query_request = SMB2QueryDirectoryRequest() 1498 query_request.unpack(request.message['data'].get_value()) 1499 file_cl = query_request['file_information_class'].get_value() 1500 data = query_response['buffer'].get_value() 1501 results = SMB2QueryDirectoryRequest.unpack_response(file_cl, data) 1502 return results 1503 1504 def close(self, get_attributes=False, send=True): 1505 """ 1506 Closes an opened file. 1507 1508 Supports out of band send function, call this function with send=False 1509 to return a tuple of (SMB2CloseRequest, receive_func) instead of 1510 sending the the request and waiting for the response. The receive_func 1511 can be used to get the response from the server by passing in the 1512 Request that was used to sent it out of band. 1513 1514 :param get_attributes: (Bool) whether to get the latest attributes on 1515 the close and set them on the Open object 1516 :param send: Whether to send the request in the same call or return the 1517 message to the caller and the unpack function 1518 :return: SMB2CloseResponse message received from the server 1519 """ 1520 # it is already closed and this isn't for an out of band request 1521 if not self._connected and send: 1522 return 1523 1524 close = SMB2CloseRequest() 1525 1526 close['file_id'] = self.file_id 1527 if get_attributes: 1528 close['flags'] = CloseFlags.SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB 1529 1530 if not send: 1531 return close, self._close_response 1532 1533 log.info("Session: %s, Tree Connect: %s - sending SMB2 Close Request " 1534 "for file %s" % (self.tree_connect.session.username, 1535 self.tree_connect.share_name, 1536 self.file_name)) 1537 log.debug(close) 1538 request = self.connection.send(close, 1539 self.tree_connect.session.session_id, 1540 self.tree_connect.tree_connect_id) 1541 return self._close_response(request) 1542 1543 def _close_response(self, request): 1544 log.info("Session: %s, Tree Connect: %s - receiving SMB2 Close " 1545 "Response" % (self.tree_connect.session.username, 1546 self.tree_connect.share_name)) 1547 try: 1548 response = self.connection.receive(request) 1549 except FileClosed: 1550 self._connected = False 1551 self.tree_connect.session.open_table.pop(self.file_id, None) 1552 return 1553 1554 c_resp = SMB2CloseResponse() 1555 c_resp.unpack(response['data'].get_value()) 1556 log.debug(c_resp) 1557 self._connected = False 1558 del self.tree_connect.session.open_table[self.file_id] 1559 1560 # update the attributes if requested 1561 close_request = SMB2CloseRequest() 1562 close_request.unpack(request.message['data'].get_value()) 1563 if close_request['flags'].has_flag( 1564 CloseFlags.SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB): 1565 self.creation_time = c_resp['creation_time'].get_value() 1566 self.last_access_time = c_resp['last_access_time'].get_value() 1567 self.last_write_time = c_resp['last_write_time'].get_value() 1568 self.change_time = c_resp['change_time'].get_value() 1569 self.allocation_size = c_resp['allocation_size'].get_value() 1570 self.end_of_file = c_resp['end_of_file'].get_value() 1571 self.file_attributes = c_resp['file_attributes'].get_value() 1572 return c_resp 1573