| # Copyright (c) 2003-2016 CORE Security Technologies |
| # |
| # This software is provided under under a slightly modified version |
| # of the Apache Software License. See the accompanying LICENSE file |
| # for more information. |
| # |
| # Copyright (C) 2001 Michael Teo <michaelteo@bigfoot.com> |
| # smb.py - SMB/CIFS library |
| # |
| # This software is provided 'as-is', without any express or implied warranty. |
| # In no event will the author be held liable for any damages arising from the |
| # use of this software. |
| # |
| # Permission is granted to anyone to use this software for any purpose, |
| # including commercial applications, and to alter it and redistribute it |
| # freely, subject to the following restrictions: |
| # |
| # 1. The origin of this software must not be misrepresented; you must not |
| # claim that you wrote the original software. If you use this software |
| # in a product, an acknowledgment in the product documentation would be |
| # appreciated but is not required. |
| # |
| # 2. Altered source versions must be plainly marked as such, and must not be |
| # misrepresented as being the original software. |
| # |
| # 3. This notice cannot be removed or altered from any source distribution. |
| # |
| # Altered source done by Alberto Solino (@agsolino) |
| |
| # Todo: |
| # [ ] Try [SMB]transport fragmentation using Transact requests |
| # [ ] Try other methods of doing write (write_raw, transact2, write, write_and_unlock, write_and_close, write_mpx) |
| # [-] Try replacements for SMB_COM_NT_CREATE_ANDX (CREATE, T_TRANSACT_CREATE, OPEN_ANDX works |
| # [x] Fix forceWriteAndx, which needs to send a RecvRequest, because recv() will not send it |
| # [x] Fix Recv() when using RecvAndx and the answer comes splet in several packets |
| # [ ] Try [SMB]transport fragmentation with overlaping segments |
| # [ ] Try [SMB]transport fragmentation with out of order segments |
| # [x] Do chained AndX requests |
| # [ ] Transform the rest of the calls to structure |
| # [X] Implement TRANS/TRANS2 reassembly for list_path |
| |
| import os |
| import socket |
| import string |
| from binascii import a2b_hex |
| import datetime |
| from struct import pack, unpack |
| from contextlib import contextmanager |
| |
| from impacket import nmb, ntlm, nt_errors, LOG |
| from impacket.structure import Structure |
| from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, SPNEGO_NegTokenResp |
| |
| # For signing |
| import hashlib |
| |
| unicode_support = 0 |
| unicode_convert = 1 |
| |
| try: |
| from cStringIO import StringIO |
| except ImportError: |
| from StringIO import StringIO |
| |
| # Dialect for SMB1 |
| SMB_DIALECT = 'NT LM 0.12' |
| |
| # Shared Device Type |
| SHARED_DISK = 0x00 |
| SHARED_DISK_HIDDEN = 0x80000000 |
| SHARED_PRINT_QUEUE = 0x01 |
| SHARED_DEVICE = 0x02 |
| SHARED_IPC = 0x03 |
| |
| # Extended attributes mask |
| ATTR_ARCHIVE = 0x020 |
| ATTR_COMPRESSED = 0x800 |
| ATTR_NORMAL = 0x080 |
| ATTR_HIDDEN = 0x002 |
| ATTR_READONLY = 0x001 |
| ATTR_TEMPORARY = 0x100 |
| ATTR_DIRECTORY = 0x010 |
| ATTR_SYSTEM = 0x004 |
| |
| # Service Type |
| SERVICE_DISK = 'A:' |
| SERVICE_PRINTER = 'LPT1:' |
| SERVICE_IPC = 'IPC' |
| SERVICE_COMM = 'COMM' |
| SERVICE_ANY = '?????' |
| |
| # Server Type (Can be used to mask with SMBMachine.get_type() or SMBDomain.get_type()) |
| SV_TYPE_WORKSTATION = 0x00000001 |
| SV_TYPE_SERVER = 0x00000002 |
| SV_TYPE_SQLSERVER = 0x00000004 |
| SV_TYPE_DOMAIN_CTRL = 0x00000008 |
| SV_TYPE_DOMAIN_BAKCTRL = 0x00000010 |
| SV_TYPE_TIME_SOURCE = 0x00000020 |
| SV_TYPE_AFP = 0x00000040 |
| SV_TYPE_NOVELL = 0x00000080 |
| SV_TYPE_DOMAIN_MEMBER = 0x00000100 |
| SV_TYPE_PRINTQ_SERVER = 0x00000200 |
| SV_TYPE_DIALIN_SERVER = 0x00000400 |
| SV_TYPE_XENIX_SERVER = 0x00000800 |
| SV_TYPE_NT = 0x00001000 |
| SV_TYPE_WFW = 0x00002000 |
| SV_TYPE_SERVER_NT = 0x00004000 |
| SV_TYPE_POTENTIAL_BROWSER = 0x00010000 |
| SV_TYPE_BACKUP_BROWSER = 0x00020000 |
| SV_TYPE_MASTER_BROWSER = 0x00040000 |
| SV_TYPE_DOMAIN_MASTER = 0x00080000 |
| SV_TYPE_LOCAL_LIST_ONLY = 0x40000000 |
| SV_TYPE_DOMAIN_ENUM = 0x80000000 |
| |
| # Options values for SMB.stor_file and SMB.retr_file |
| SMB_O_CREAT = 0x10 # Create the file if file does not exists. Otherwise, operation fails. |
| SMB_O_EXCL = 0x00 # When used with SMB_O_CREAT, operation fails if file exists. Cannot be used with SMB_O_OPEN. |
| SMB_O_OPEN = 0x01 # Open the file if the file exists |
| SMB_O_TRUNC = 0x02 # Truncate the file if the file exists |
| |
| # Share Access Mode |
| SMB_SHARE_COMPAT = 0x00 |
| SMB_SHARE_DENY_EXCL = 0x10 |
| SMB_SHARE_DENY_WRITE = 0x20 |
| SMB_SHARE_DENY_READEXEC = 0x30 |
| SMB_SHARE_DENY_NONE = 0x40 |
| SMB_ACCESS_READ = 0x00 |
| SMB_ACCESS_WRITE = 0x01 |
| SMB_ACCESS_READWRITE = 0x02 |
| SMB_ACCESS_EXEC = 0x03 |
| |
| TRANS_DISCONNECT_TID = 1 |
| TRANS_NO_RESPONSE = 2 |
| |
| STATUS_SUCCESS = 0x00000000 |
| STATUS_LOGON_FAILURE = 0xC000006D |
| STATUS_LOGON_TYPE_NOT_GRANTED = 0xC000015B |
| MAX_TFRAG_SIZE = 5840 |
| EVASION_NONE = 0 |
| EVASION_LOW = 1 |
| EVASION_HIGH = 2 |
| EVASION_MAX = 3 |
| RPC_X_BAD_STUB_DATA = 0x6F7 |
| |
| # SMB_FILE_ATTRIBUTES |
| |
| SMB_FILE_ATTRIBUTE_NORMAL = 0x0000 |
| SMB_FILE_ATTRIBUTE_READONLY = 0x0001 |
| SMB_FILE_ATTRIBUTE_HIDDEN = 0x0002 |
| SMB_FILE_ATTRIBUTE_SYSTEM = 0x0004 |
| SMB_FILE_ATTRIBUTE_VOLUME = 0x0008 |
| SMB_FILE_ATTRIBUTE_DIRECTORY = 0x0010 |
| SMB_FILE_ATTRIBUTE_ARCHIVE = 0x0020 |
| SMB_SEARCH_ATTRIBUTE_READONLY = 0x0100 |
| SMB_SEARCH_ATTRIBUTE_HIDDEN = 0x0200 |
| SMB_SEARCH_ATTRIBUTE_SYSTEM = 0x0400 |
| SMB_SEARCH_ATTRIBUTE_DIRECTORY = 0x1000 |
| SMB_SEARCH_ATTRIBUTE_ARCHIVE = 0x2000 |
| |
| # Session SetupAndX Action flags |
| SMB_SETUP_GUEST = 0x01 |
| SMB_SETUP_USE_LANMAN_KEY = 0x02 |
| |
| # QUERY_INFORMATION levels |
| SMB_INFO_ALLOCATION = 0x0001 |
| SMB_INFO_VOLUME = 0x0002 |
| FILE_FS_SIZE_INFORMATION = 0x0003 |
| SMB_QUERY_FS_VOLUME_INFO = 0x0102 |
| SMB_QUERY_FS_SIZE_INFO = 0x0103 |
| SMB_QUERY_FILE_EA_INFO = 0x0103 |
| SMB_QUERY_FS_DEVICE_INFO = 0x0104 |
| SMB_QUERY_FS_ATTRIBUTE_INFO = 0x0105 |
| SMB_QUERY_FILE_BASIC_INFO = 0x0101 |
| SMB_QUERY_FILE_STANDARD_INFO = 0x0102 |
| SMB_QUERY_FILE_ALL_INFO = 0x0107 |
| FILE_FS_FULL_SIZE_INFORMATION = 0x03EF |
| |
| # SET_INFORMATION levels |
| SMB_SET_FILE_DISPOSITION_INFO = 0x0102 |
| SMB_SET_FILE_BASIC_INFO = 0x0101 |
| SMB_SET_FILE_END_OF_FILE_INFO = 0x0104 |
| |
| |
| # File System Attributes |
| FILE_CASE_SENSITIVE_SEARCH = 0x00000001 |
| FILE_CASE_PRESERVED_NAMES = 0x00000002 |
| FILE_UNICODE_ON_DISK = 0x00000004 |
| FILE_PERSISTENT_ACLS = 0x00000008 |
| FILE_FILE_COMPRESSION = 0x00000010 |
| FILE_VOLUME_IS_COMPRESSED = 0x00008000 |
| |
| # FIND_FIRST2 flags and levels |
| SMB_FIND_CLOSE_AFTER_REQUEST = 0x0001 |
| SMB_FIND_CLOSE_AT_EOS = 0x0002 |
| SMB_FIND_RETURN_RESUME_KEYS = 0x0004 |
| SMB_FIND_CONTINUE_FROM_LAST = 0x0008 |
| SMB_FIND_WITH_BACKUP_INTENT = 0x0010 |
| |
| FILE_DIRECTORY_FILE = 0x00000001 |
| FILE_DELETE_ON_CLOSE = 0x00001000 |
| FILE_NON_DIRECTORY_FILE = 0x00000040 |
| |
| SMB_FIND_INFO_STANDARD = 0x0001 |
| SMB_FIND_FILE_DIRECTORY_INFO = 0x0101 |
| SMB_FIND_FILE_FULL_DIRECTORY_INFO= 0x0102 |
| SMB_FIND_FILE_NAMES_INFO = 0x0103 |
| SMB_FIND_FILE_BOTH_DIRECTORY_INFO= 0x0104 |
| SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO = 0x105 |
| SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO = 0x106 |
| |
| |
| # DesiredAccess flags |
| FILE_READ_DATA = 0x00000001 |
| FILE_WRITE_DATA = 0x00000002 |
| FILE_APPEND_DATA = 0x00000004 |
| FILE_EXECUTE = 0x00000020 |
| MAXIMUM_ALLOWED = 0x02000000 |
| GENERIC_ALL = 0x10000000 |
| GENERIC_EXECUTE = 0x20000000 |
| GENERIC_WRITE = 0x40000000 |
| GENERIC_READ = 0x80000000 |
| |
| # ShareAccess flags |
| FILE_SHARE_NONE = 0x00000000 |
| FILE_SHARE_READ = 0x00000001 |
| FILE_SHARE_WRITE = 0x00000002 |
| FILE_SHARE_DELETE = 0x00000004 |
| |
| # CreateDisposition flags |
| FILE_SUPERSEDE = 0x00000000 |
| FILE_OPEN = 0x00000001 |
| FILE_CREATE = 0x00000002 |
| FILE_OPEN_IF = 0x00000003 |
| FILE_OVERWRITE = 0x00000004 |
| FILE_OVERWRITE_IF = 0x00000005 |
| |
| def strerror(errclass, errcode): |
| if errclass == 0x01: |
| return 'OS error', ERRDOS.get(errcode, 'Unknown error') |
| elif errclass == 0x02: |
| return 'Server error', ERRSRV.get(errcode, 'Unknown error') |
| elif errclass == 0x03: |
| return 'Hardware error', ERRHRD.get(errcode, 'Unknown error') |
| # This is not a standard error class for SMB |
| #elif errclass == 0x80: |
| # return 'Browse error', ERRBROWSE.get(errcode, 'Unknown error') |
| elif errclass == 0xff: |
| return 'Bad command', 'Bad command. Please file bug report' |
| else: |
| return 'Unknown error', 'Unknown error' |
| |
| # Raised when an error has occured during a session |
| class SessionError(Exception): |
| # SMB X/Open error codes for the ERRDOS error class |
| ERRsuccess = 0 |
| ERRbadfunc = 1 |
| ERRbadfile = 2 |
| ERRbadpath = 3 |
| ERRnofids = 4 |
| ERRnoaccess = 5 |
| ERRbadfid = 6 |
| ERRbadmcb = 7 |
| ERRnomem = 8 |
| ERRbadmem = 9 |
| ERRbadenv = 10 |
| ERRbadaccess = 12 |
| ERRbaddata = 13 |
| ERRres = 14 |
| ERRbaddrive = 15 |
| ERRremcd = 16 |
| ERRdiffdevice = 17 |
| ERRnofiles = 18 |
| ERRgeneral = 31 |
| ERRbadshare = 32 |
| ERRlock = 33 |
| ERRunsup = 50 |
| ERRnetnamedel = 64 |
| ERRnosuchshare = 67 |
| ERRfilexists = 80 |
| ERRinvalidparam = 87 |
| ERRcannotopen = 110 |
| ERRinsufficientbuffer = 122 |
| ERRinvalidname = 123 |
| ERRunknownlevel = 124 |
| ERRnotlocked = 158 |
| ERRrename = 183 |
| ERRbadpipe = 230 |
| ERRpipebusy = 231 |
| ERRpipeclosing = 232 |
| ERRnotconnected = 233 |
| ERRmoredata = 234 |
| ERRnomoreitems = 259 |
| ERRbaddirectory = 267 |
| ERReasnotsupported = 282 |
| ERRlogonfailure = 1326 |
| ERRbuftoosmall = 2123 |
| ERRunknownipc = 2142 |
| ERRnosuchprintjob = 2151 |
| ERRinvgroup = 2455 |
| |
| # here's a special one from observing NT |
| ERRnoipc = 66 |
| |
| # These errors seem to be only returned by the NT printer driver system |
| ERRdriveralreadyinstalled = 1795 |
| ERRunknownprinterport = 1796 |
| ERRunknownprinterdriver = 1797 |
| ERRunknownprintprocessor = 1798 |
| ERRinvalidseparatorfile = 1799 |
| ERRinvalidjobpriority = 1800 |
| ERRinvalidprintername = 1801 |
| ERRprinteralreadyexists = 1802 |
| ERRinvalidprintercommand = 1803 |
| ERRinvaliddatatype = 1804 |
| ERRinvalidenvironment = 1805 |
| |
| ERRunknownprintmonitor = 3000 |
| ERRprinterdriverinuse = 3001 |
| ERRspoolfilenotfound = 3002 |
| ERRnostartdoc = 3003 |
| ERRnoaddjob = 3004 |
| ERRprintprocessoralreadyinstalled = 3005 |
| ERRprintmonitoralreadyinstalled = 3006 |
| ERRinvalidprintmonitor = 3007 |
| ERRprintmonitorinuse = 3008 |
| ERRprinterhasjobsqueued = 3009 |
| |
| # Error codes for the ERRSRV class |
| |
| ERRerror = 1 |
| ERRbadpw = 2 |
| ERRbadtype = 3 |
| ERRaccess = 4 |
| ERRinvnid = 5 |
| ERRinvnetname = 6 |
| ERRinvdevice = 7 |
| ERRqfull = 49 |
| ERRqtoobig = 50 |
| ERRinvpfid = 52 |
| ERRsmbcmd = 64 |
| ERRsrverror = 65 |
| ERRfilespecs = 67 |
| ERRbadlink = 68 |
| ERRbadpermits = 69 |
| ERRbadpid = 70 |
| ERRsetattrmode = 71 |
| ERRpaused = 81 |
| ERRmsgoff = 82 |
| ERRnoroom = 83 |
| ERRrmuns = 87 |
| ERRtimeout = 88 |
| ERRnoresource = 89 |
| ERRtoomanyuids = 90 |
| ERRbaduid = 91 |
| ERRuseMPX = 250 |
| ERRuseSTD = 251 |
| ERRcontMPX = 252 |
| ERRbadPW = None |
| ERRnosupport = 0 |
| ERRunknownsmb = 22 |
| |
| # Error codes for the ERRHRD class |
| |
| ERRnowrite = 19 |
| ERRbadunit = 20 |
| ERRnotready = 21 |
| ERRbadcmd = 22 |
| ERRdata = 23 |
| ERRbadreq = 24 |
| ERRseek = 25 |
| ERRbadmedia = 26 |
| ERRbadsector = 27 |
| ERRnopaper = 28 |
| ERRwrite = 29 |
| ERRread = 30 |
| ERRwrongdisk = 34 |
| ERRFCBunavail = 35 |
| ERRsharebufexc = 36 |
| ERRdiskfull = 39 |
| |
| |
| hard_msgs = { |
| 19: ("ERRnowrite", "Attempt to write on write-protected diskette."), |
| 20: ("ERRbadunit", "Unknown unit."), |
| 21: ("ERRnotready", "Drive not ready."), |
| 22: ("ERRbadcmd", "Unknown command."), |
| 23: ("ERRdata", "Data error (CRC)."), |
| 24: ("ERRbadreq", "Bad request structure length."), |
| 25: ("ERRseek", "Seek error."), |
| 26: ("ERRbadmedia", "Unknown media type."), |
| 27: ("ERRbadsector", "Sector not found."), |
| 28: ("ERRnopaper", "Printer out of paper."), |
| 29: ("ERRwrite", "Write fault."), |
| 30: ("ERRread", "Read fault."), |
| 31: ("ERRgeneral", "General failure."), |
| 32: ("ERRbadshare", "An open conflicts with an existing open."), |
| 33: ("ERRlock", "A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."), |
| 34: ("ERRwrongdisk", "The wrong disk was found in a drive."), |
| 35: ("ERRFCBUnavail", "No FCBs are available to process request."), |
| 36: ("ERRsharebufexc", "A sharing buffer has been exceeded.") |
| } |
| |
| dos_msgs = { |
| ERRbadfunc: ("ERRbadfunc", "Invalid function."), |
| ERRbadfile: ("ERRbadfile", "File not found."), |
| ERRbadpath: ("ERRbadpath", "Directory invalid."), |
| ERRnofids: ("ERRnofids", "No file descriptors available"), |
| ERRnoaccess: ("ERRnoaccess", "Access denied."), |
| ERRbadfid: ("ERRbadfid", "Invalid file handle."), |
| ERRbadmcb: ("ERRbadmcb", "Memory control blocks destroyed."), |
| ERRnomem: ("ERRnomem", "Insufficient server memory to perform the requested function."), |
| ERRbadmem: ("ERRbadmem", "Invalid memory block address."), |
| ERRbadenv: ("ERRbadenv", "Invalid environment."), |
| 11: ("ERRbadformat", "Invalid format."), |
| ERRbadaccess: ("ERRbadaccess", "Invalid open mode."), |
| ERRbaddata: ("ERRbaddata", "Invalid data."), |
| ERRres: ("ERRres", "reserved."), |
| ERRbaddrive: ("ERRbaddrive", "Invalid drive specified."), |
| ERRremcd: ("ERRremcd", "A Delete Directory request attempted to remove the server's current directory."), |
| ERRdiffdevice: ("ERRdiffdevice", "Not same device."), |
| ERRnofiles: ("ERRnofiles", "A File Search command can find no more files matching the specified criteria."), |
| ERRbadshare: ("ERRbadshare", "The sharing mode specified for an Open conflicts with existing FIDs on the file."), |
| ERRlock: ("ERRlock", "A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."), |
| ERRunsup: ("ERRunsup", "The operation is unsupported"), |
| ERRnosuchshare: ("ERRnosuchshare", "You specified an invalid share name"), |
| ERRfilexists: ("ERRfilexists", "The file named in a Create Directory, Make New File or Link request already exists."), |
| ERRinvalidname: ("ERRinvalidname", "Invalid name"), |
| ERRbadpipe: ("ERRbadpipe", "Pipe invalid."), |
| ERRpipebusy: ("ERRpipebusy", "All instances of the requested pipe are busy."), |
| ERRpipeclosing: ("ERRpipeclosing", "Pipe close in progress."), |
| ERRnotconnected: ("ERRnotconnected", "No process on other end of pipe."), |
| ERRmoredata: ("ERRmoredata", "There is more data to be returned."), |
| ERRinvgroup: ("ERRinvgroup", "Invalid workgroup (try the -W option)"), |
| ERRlogonfailure: ("ERRlogonfailure", "Logon failure"), |
| ERRdiskfull: ("ERRdiskfull", "Disk full"), |
| ERRgeneral: ("ERRgeneral", "General failure"), |
| ERRunknownlevel: ("ERRunknownlevel", "Unknown info level") |
| } |
| |
| server_msgs = { |
| 1: ("ERRerror", "Non-specific error code."), |
| 2: ("ERRbadpw", "Bad password - name/password pair in a Tree Connect or Session Setup are invalid."), |
| 3: ("ERRbadtype", "reserved."), |
| 4: ("ERRaccess", "The requester does not have the necessary access rights within the specified context for the requested function. The context is defined by the TID or the UID."), |
| 5: ("ERRinvnid", "The tree ID (TID) specified in a command was invalid."), |
| 6: ("ERRinvnetname", "Invalid network name in tree connect."), |
| 7: ("ERRinvdevice", "Invalid device - printer request made to non-printer connection or non-printer request made to printer connection."), |
| 49: ("ERRqfull", "Print queue full (files) -- returned by open print file."), |
| 50: ("ERRqtoobig", "Print queue full -- no space."), |
| 51: ("ERRqeof", "EOF on print queue dump."), |
| 52: ("ERRinvpfid", "Invalid print file FID."), |
| 64: ("ERRsmbcmd", "The server did not recognize the command received."), |
| 65: ("ERRsrverror","The server encountered an internal error, e.g., system file unavailable."), |
| 67: ("ERRfilespecs", "The file handle (FID) and pathname parameters contained an invalid combination of values."), |
| 68: ("ERRreserved", "reserved."), |
| 69: ("ERRbadpermits", "The access permissions specified for a file or directory are not a valid combination. The server cannot set the requested attribute."), |
| 70: ("ERRreserved", "reserved."), |
| 71: ("ERRsetattrmode", "The attribute mode in the Set File Attribute request is invalid."), |
| 81: ("ERRpaused", "Server is paused."), |
| 82: ("ERRmsgoff", "Not receiving messages."), |
| 83: ("ERRnoroom", "No room to buffer message."), |
| 87: ("ERRrmuns", "Too many remote user names."), |
| 88: ("ERRtimeout", "Operation timed out."), |
| 89: ("ERRnoresource", "No resources currently available for request."), |
| 90: ("ERRtoomanyuids", "Too many UIDs active on this session."), |
| 91: ("ERRbaduid", "The UID is not known as a valid ID on this session."), |
| 250: ("ERRusempx","Temp unable to support Raw, use MPX mode."), |
| 251: ("ERRusestd","Temp unable to support Raw, use standard read/write."), |
| 252: ("ERRcontmpx", "Continue in MPX mode."), |
| 253: ("ERRreserved", "reserved."), |
| 254: ("ERRreserved", "reserved."), |
| 0xFFFF: ("ERRnosupport", "Function not supported.") |
| } |
| # Error clases |
| |
| ERRDOS = 0x1 |
| error_classes = { 0: ("SUCCESS", {}), |
| ERRDOS: ("ERRDOS", dos_msgs), |
| 0x02: ("ERRSRV",server_msgs), |
| 0x03: ("ERRHRD",hard_msgs), |
| 0x04: ("ERRXOS", {} ), |
| 0xE1: ("ERRRMX1", {} ), |
| 0xE2: ("ERRRMX2", {} ), |
| 0xE3: ("ERRRMX3", {} ), |
| 0xFF: ("ERRCMD", {} ) } |
| |
| |
| |
| def __init__( self, error_string, error_class, error_code, nt_status = 0): |
| Exception.__init__(self, error_string) |
| self.nt_status = nt_status |
| self._args = error_string |
| if nt_status: |
| self.error_class = 0 |
| self.error_code = (error_code << 16) + error_class |
| else: |
| self.error_class = error_class |
| self.error_code = error_code |
| |
| |
| def get_error_class( self ): |
| return self.error_class |
| |
| def get_error_code( self ): |
| return self.error_code |
| |
| def __str__( self ): |
| error_class = SessionError.error_classes.get( self.error_class, None ) |
| if not error_class: |
| error_code_str = self.error_code |
| error_class_str = self.error_class |
| else: |
| error_class_str = error_class[0] |
| error_code = error_class[1].get( self.error_code, None ) |
| if not error_code: |
| error_code_str = self.error_code |
| else: |
| error_code_str = '%s(%s)' % error_code |
| |
| if self.nt_status: |
| return 'SMB SessionError: %s(%s)' % nt_errors.ERROR_MESSAGES[self.error_code] |
| else: |
| # Fall back to the old format |
| return 'SMB SessionError: class: %s, code: %s' % (error_class_str, error_code_str) |
| |
| |
| # Raised when an supported feature is present/required in the protocol but is not |
| # currently supported by pysmb |
| class UnsupportedFeature(Exception): pass |
| |
| # Contains information about a SMB shared device/service |
| class SharedDevice: |
| def __init__(self, name, share_type, comment): |
| self.__name = name |
| self.__type = share_type |
| self.__comment = comment |
| |
| def get_name(self): |
| return self.__name |
| |
| def get_type(self): |
| return self.__type |
| |
| def get_comment(self): |
| return self.__comment |
| |
| def __repr__(self): |
| return '<SharedDevice instance: name=' + self.__name + ', type=' + str(self.__type) + ', comment="' + self.__comment + '">' |
| |
| |
| # Contains information about the shared file/directory |
| class SharedFile: |
| def __init__(self, ctime, atime, mtime, filesize, allocsize, attribs, shortname, longname): |
| self.__ctime = ctime |
| self.__atime = atime |
| self.__mtime = mtime |
| self.__filesize = filesize |
| self.__allocsize = allocsize |
| self.__attribs = attribs |
| try: |
| self.__shortname = shortname[:string.index(shortname, '\0')] |
| except ValueError: |
| self.__shortname = shortname |
| try: |
| self.__longname = longname[:string.index(longname, '\0')] |
| except ValueError: |
| self.__longname = longname |
| |
| def get_ctime(self): |
| return self.__ctime |
| |
| def get_ctime_epoch(self): |
| return self.__convert_smbtime(self.__ctime) |
| |
| def get_mtime(self): |
| return self.__mtime |
| |
| def get_mtime_epoch(self): |
| return self.__convert_smbtime(self.__mtime) |
| |
| def get_atime(self): |
| return self.__atime |
| |
| def get_atime_epoch(self): |
| return self.__convert_smbtime(self.__atime) |
| |
| def get_filesize(self): |
| return self.__filesize |
| |
| def get_allocsize(self): |
| return self.__allocsize |
| |
| def get_attributes(self): |
| return self.__attribs |
| |
| def is_archive(self): |
| return self.__attribs & ATTR_ARCHIVE |
| |
| def is_compressed(self): |
| return self.__attribs & ATTR_COMPRESSED |
| |
| def is_normal(self): |
| return self.__attribs & ATTR_NORMAL |
| |
| def is_hidden(self): |
| return self.__attribs & ATTR_HIDDEN |
| |
| def is_readonly(self): |
| return self.__attribs & ATTR_READONLY |
| |
| def is_temporary(self): |
| return self.__attribs & ATTR_TEMPORARY |
| |
| def is_directory(self): |
| return self.__attribs & ATTR_DIRECTORY |
| |
| def is_system(self): |
| return self.__attribs & ATTR_SYSTEM |
| |
| def get_shortname(self): |
| return self.__shortname |
| |
| def get_longname(self): |
| return self.__longname |
| |
| def __repr__(self): |
| return '<SharedFile instance: shortname="' + self.__shortname + '", longname="' + self.__longname + '", filesize=' + str(self.__filesize) + '>' |
| |
| @staticmethod |
| def __convert_smbtime(t): |
| x = t >> 32 |
| y = t & 0xffffffffL |
| geo_cal_offset = 11644473600.0 # = 369.0 * 365.25 * 24 * 60 * 60 - (3.0 * 24 * 60 * 60 + 6.0 * 60 * 60) |
| return (x * 4.0 * (1 << 30) + (y & 0xfff00000L)) * 1.0e-7 - geo_cal_offset |
| |
| |
| # Contain information about a SMB machine |
| class SMBMachine: |
| def __init__(self, nbname, nbt_type, comment): |
| self.__nbname = nbname |
| self.__type = nbt_type |
| self.__comment = comment |
| |
| def __repr__(self): |
| return '<SMBMachine instance: nbname="' + self.__nbname + '", type=' + hex(self.__type) + ', comment="' + self.__comment + '">' |
| |
| class SMBDomain: |
| def __init__(self, nbgroup, domain_type, master_browser): |
| self.__nbgroup = nbgroup |
| self.__type = domain_type |
| self.__master_browser = master_browser |
| |
| def __repr__(self): |
| return '<SMBDomain instance: nbgroup="' + self.__nbgroup + '", type=' + hex(self.__type) + ', master browser="' + self.__master_browser + '">' |
| |
| # Represents a SMB Packet |
| class NewSMBPacket(Structure): |
| structure = ( |
| ('Signature', '"\xffSMB'), |
| ('Command','B=0'), |
| ('ErrorClass','B=0'), |
| ('_reserved','B=0'), |
| ('ErrorCode','<H=0'), |
| ('Flags1','B=0'), |
| ('Flags2','<H=0'), |
| ('PIDHigh','<H=0'), |
| ('SecurityFeatures','8s=""'), |
| ('Reserved','<H=0'), |
| ('Tid','<H=0xffff'), |
| ('Pid','<H=0'), |
| ('Uid','<H=0'), |
| ('Mid','<H=0'), |
| ('Data','*:'), |
| ) |
| |
| def __init__(self, **kargs): |
| Structure.__init__(self, **kargs) |
| |
| if self.fields.has_key('Flags2') is False: |
| self['Flags2'] = 0 |
| if self.fields.has_key('Flags1') is False: |
| self['Flags1'] = 0 |
| |
| if not kargs.has_key('data'): |
| self['Data'] = [] |
| |
| def addCommand(self, command): |
| if len(self['Data']) == 0: |
| self['Command'] = command.command |
| else: |
| self['Data'][-1]['Parameters']['AndXCommand'] = command.command |
| self['Data'][-1]['Parameters']['AndXOffset'] = len(self) |
| self['Data'].append(command) |
| |
| def isMoreData(self): |
| return (self['Command'] in [SMB.SMB_COM_TRANSACTION, SMB.SMB_COM_READ_ANDX, SMB.SMB_COM_READ_RAW] and |
| self['ErrorClass'] == 1 and self['ErrorCode'] == SessionError.ERRmoredata) |
| |
| def isMoreProcessingRequired(self): |
| return self['ErrorClass'] == 0x16 and self['ErrorCode'] == 0xc000 |
| |
| def isValidAnswer(self, cmd): |
| # this was inside a loop reading more from the net (with recv_packet(None)) |
| if self['Command'] == cmd: |
| if (self['ErrorClass'] == 0x00 and |
| self['ErrorCode'] == 0x00): |
| return 1 |
| elif self.isMoreData(): |
| return 1 |
| elif self.isMoreProcessingRequired(): |
| return 1 |
| raise SessionError, ("SMB Library Error", self['ErrorClass'] + (self['_reserved'] << 8), self['ErrorCode'], self['Flags2'] & SMB.FLAGS2_NT_STATUS) |
| else: |
| raise UnsupportedFeature, ("Unexpected answer from server: Got %d, Expected %d" % (self['Command'], cmd)) |
| |
| |
| class SMBCommand(Structure): |
| structure = ( |
| ('WordCount', 'B=len(Parameters)/2'), |
| ('_ParametersLength','_-Parameters','WordCount*2'), |
| ('Parameters',':'), # default set by constructor |
| ('ByteCount','<H-Data'), |
| ('Data',':'), # default set by constructor |
| ) |
| |
| def __init__(self, commandOrData = None, data = None, **kargs): |
| if type(commandOrData) == type(0): |
| self.command = commandOrData |
| else: |
| data = data or commandOrData |
| |
| Structure.__init__(self, data = data, **kargs) |
| |
| if data is None: |
| self['Parameters'] = '' |
| self['Data'] = '' |
| |
| class AsciiOrUnicodeStructure(Structure): |
| UnicodeStructure = () |
| AsciiStructure = () |
| def __init__(self, flags = 0, **kargs): |
| if flags & SMB.FLAGS2_UNICODE: |
| self.structure = self.UnicodeStructure |
| else: |
| self.structure = self.AsciiStructure |
| Structure.__init__(self, **kargs) |
| |
| class SMBCommand_Parameters(Structure): |
| pass |
| |
| class SMBAndXCommand_Parameters(Structure): |
| commonHdr = ( |
| ('AndXCommand','B=0xff'), |
| ('_reserved','B=0'), |
| ('AndXOffset','<H=0'), |
| ) |
| structure = ( # default structure, overriden by subclasses |
| ('Data',':=""'), |
| ) |
| |
| ############# TRANSACTIONS RELATED |
| # TRANS2_QUERY_FS_INFORMATION |
| # QUERY_FS Information Levels |
| # SMB_QUERY_FS_ATTRIBUTE_INFO |
| class SMBQueryFsAttributeInfo(Structure): |
| structure = ( |
| ('FileSystemAttributes','<L'), |
| ('MaxFilenNameLengthInBytes','<L'), |
| ('LengthOfFileSystemName','<L-FileSystemName'), |
| ('FileSystemName',':'), |
| ) |
| |
| class SMBQueryFsInfoVolume(AsciiOrUnicodeStructure): |
| commonHdr = ( |
| ('ulVolSerialNbr','<L=0xABCDEFAA'), |
| ('cCharCount','<B-VolumeLabel'), |
| ) |
| AsciiStructure = ( |
| ('VolumeLabel','z'), |
| ) |
| UnicodeStructure = ( |
| ('VolumeLabel','u'), |
| ) |
| |
| # FILE_FS_SIZE_INFORMATION |
| class FileFsSizeInformation(Structure): |
| structure = ( |
| ('TotalAllocationUnits','<q=148529400'), |
| ('AvailableAllocationUnits','<q=14851044'), |
| ('SectorsPerAllocationUnit','<L=2'), |
| ('BytesPerSector','<L=512'), |
| ) |
| |
| # SMB_QUERY_FS_SIZE_INFO |
| class SMBQueryFsSizeInfo(Structure): |
| structure = ( |
| ('TotalAllocationUnits','<q=148529400'), |
| ('TotalFreeAllocationUnits','<q=14851044'), |
| ('SectorsPerAllocationUnit','<L=2'), |
| ('BytesPerSector','<L=512'), |
| ) |
| # FILE_FS_FULL_SIZE_INFORMATION |
| class SMBFileFsFullSizeInformation(Structure): |
| structure = ( |
| ('TotalAllocationUnits','<q=148529400'), |
| ('CallerAvailableAllocationUnits','<q=148529400'), |
| ('ActualAvailableAllocationUnits','<q=148529400'), |
| ('SectorsPerAllocationUnit','<L=15'), |
| ('BytesPerSector','<L=512') |
| ) |
| # SMB_QUERY_FS_VOLUME_INFO |
| class SMBQueryFsVolumeInfo(Structure): |
| structure = ( |
| ('VolumeCreationTime','<q'), |
| ('SerialNumber','<L=0xABCDEFAA'), |
| ('VolumeLabelSize','<L=len(VolumeLabel)'), |
| ('Reserved','<H=0x10'), |
| ('VolumeLabel',':') |
| ) |
| # SMB_FIND_FILE_BOTH_DIRECTORY_INFO level |
| class SMBFindFileBothDirectoryInfo(AsciiOrUnicodeStructure): |
| commonHdr = ( |
| ('NextEntryOffset','<L=0'), |
| ('FileIndex','<L=0'), |
| ('CreationTime','<q'), |
| ('LastAccessTime','<q'), |
| ('LastWriteTime','<q'), |
| ('LastChangeTime','<q'), |
| ('EndOfFile','<q=0'), |
| ('AllocationSize','<q=0'), |
| ('ExtFileAttributes','<L=0'), |
| ) |
| AsciiStructure = ( |
| ('FileNameLength','<L-FileName','len(FileName)'), |
| ('EaSize','<L=0'), |
| ('ShortNameLength','<B=0'), |
| ('Reserved','<B=0'), |
| ('ShortName','24s'), |
| ('FileName',':'), |
| ) |
| UnicodeStructure = ( |
| ('FileNameLength','<L-FileName','len(FileName)*2'), |
| ('EaSize','<L=0'), |
| ('ShortNameLength','<B=0'), |
| ('Reserved','<B=0'), |
| ('ShortName','24s'), |
| ('FileName',':'), |
| ) |
| |
| # SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO level |
| class SMBFindFileIdFullDirectoryInfo(AsciiOrUnicodeStructure): |
| commonHdr = ( |
| ('NextEntryOffset','<L=0'), |
| ('FileIndex','<L=0'), |
| ('CreationTime','<q'), |
| ('LastAccessTime','<q'), |
| ('LastWriteTime','<q'), |
| ('LastChangeTime','<q'), |
| ('EndOfFile','<q=0'), |
| ('AllocationSize','<q=0'), |
| ('ExtFileAttributes','<L=0'), |
| ) |
| AsciiStructure = ( |
| ('FileNameLength','<L-FileName','len(FileName)'), |
| ('EaSize','<L=0'), |
| ('FileID','<q=0'), |
| ('FileName',':'), |
| ) |
| UnicodeStructure = ( |
| ('FileNameLength','<L-FileName','len(FileName)*2'), |
| ('EaSize','<L=0'), |
| ('FileID','<q=0'), |
| ('FileName',':'), |
| ) |
| |
| # SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO level |
| class SMBFindFileIdBothDirectoryInfo(AsciiOrUnicodeStructure): |
| commonHdr = ( |
| ('NextEntryOffset','<L=0'), |
| ('FileIndex','<L=0'), |
| ('CreationTime','<q'), |
| ('LastAccessTime','<q'), |
| ('LastWriteTime','<q'), |
| ('LastChangeTime','<q'), |
| ('EndOfFile','<q=0'), |
| ('AllocationSize','<q=0'), |
| ('ExtFileAttributes','<L=0'), |
| ) |
| AsciiStructure = ( |
| ('FileNameLength','<L-FileName','len(FileName)'), |
| ('EaSize','<L=0'), |
| ('ShortNameLength','<B=0'), |
| ('Reserved','<B=0'), |
| ('ShortName','24s'), |
| ('Reserved','<H=0'), |
| ('FileID','<q=0'), |
| ('FileName','z'), |
| ) |
| UnicodeStructure = ( |
| ('FileNameLength','<L-FileName','len(FileName)*2'), |
| ('EaSize','<L=0'), |
| ('ShortNameLength','<B=0'), |
| ('Reserved','<B=0'), |
| ('ShortName','24s'), |
| ('Reserved','<H=0'), |
| ('FileID','<q=0'), |
| ('FileName',':'), |
| ) |
| |
| # SMB_FIND_FILE_DIRECTORY_INFO level |
| class SMBFindFileDirectoryInfo(AsciiOrUnicodeStructure): |
| commonHdr = ( |
| ('NextEntryOffset','<L=0'), |
| ('FileIndex','<L=0'), |
| ('CreationTime','<q'), |
| ('LastAccessTime','<q'), |
| ('LastWriteTime','<q'), |
| ('LastChangeTime','<q'), |
| ('EndOfFile','<q=0'), |
| ('AllocationSize','<q=1'), |
| ('ExtFileAttributes','<L=0'), |
| ) |
| AsciiStructure = ( |
| ('FileNameLength','<L-FileName','len(FileName)'), |
| ('FileName','z'), |
| ) |
| UnicodeStructure = ( |
| ('FileNameLength','<L-FileName','len(FileName)*2'), |
| ('FileName',':'), |
| ) |
| |
| # SMB_FIND_FILE_NAMES_INFO level |
| class SMBFindFileNamesInfo(AsciiOrUnicodeStructure): |
| commonHdr = ( |
| ('NextEntryOffset','<L=0'), |
| ('FileIndex','<L=0'), |
| ) |
| AsciiStructure = ( |
| ('FileNameLength','<L-FileName','len(FileName)'), |
| ('FileName','z'), |
| ) |
| UnicodeStructure = ( |
| ('FileNameLength','<L-FileName','len(FileName)*2'), |
| ('FileName',':'), |
| ) |
| |
| # SMB_FIND_FILE_FULL_DIRECTORY_INFO level |
| class SMBFindFileFullDirectoryInfo(AsciiOrUnicodeStructure): |
| commonHdr = ( |
| ('NextEntryOffset','<L=0'), |
| ('FileIndex','<L=0'), |
| ('CreationTime','<q'), |
| ('LastAccessTime','<q'), |
| ('LastWriteTime','<q'), |
| ('LastChangeTime','<q'), |
| ('EndOfFile','<q=0'), |
| ('AllocationSize','<q=1'), |
| ('ExtFileAttributes','<L=0'), |
| ) |
| AsciiStructure = ( |
| ('FileNameLength','<L-FileName','len(FileName)'), |
| ('EaSize','<L'), |
| ('FileName','z'), |
| ) |
| UnicodeStructure = ( |
| ('FileNameLength','<L-FileName','len(FileName)*2'), |
| ('EaSize','<L'), |
| ('FileName',':'), |
| ) |
| |
| # SMB_FIND_INFO_STANDARD level |
| class SMBFindInfoStandard(AsciiOrUnicodeStructure): |
| commonHdr = ( |
| ('ResumeKey','<L=0xff'), |
| ('CreationDate','<H=0'), |
| ('CreationTime','<H=0'), |
| ('LastAccessDate','<H=0'), |
| ('LastAccessTime','<H=0'), |
| ('LastWriteDate','<H=0'), |
| ('LastWriteTime','<H=0'), |
| ('EaSize','<L'), |
| ('AllocationSize','<L=1'), |
| ('ExtFileAttributes','<H=0'), |
| ) |
| AsciiStructure = ( |
| ('FileNameLength','<B-FileName','len(FileName)'), |
| ('FileName','z'), |
| ) |
| UnicodeStructure = ( |
| ('FileNameLength','<B-FileName','len(FileName)*2'), |
| ('FileName',':'), |
| ) |
| |
| # SET_FILE_INFORMATION structures |
| # SMB_SET_FILE_DISPOSITION_INFO |
| class SMBSetFileDispositionInfo(Structure): |
| structure = ( |
| ('DeletePending','<B'), |
| ) |
| |
| # SMB_SET_FILE_BASIC_INFO |
| class SMBSetFileBasicInfo(Structure): |
| structure = ( |
| ('CreationTime','<q'), |
| ('LastAccessTime','<q'), |
| ('LastWriteTime','<q'), |
| ('ChangeTime','<q'), |
| ('ExtFileAttributes','<H'), |
| ('Reserved','<L'), |
| ) |
| |
| # FILE_STREAM_INFORMATION |
| class SMBFileStreamInformation(Structure): |
| commonHdr = ( |
| ('NextEntryOffset','<L=0'), |
| ('StreamNameLength','<L=0'), |
| ('StreamSize','<q=0'), |
| ('StreamAllocationSize','<q=0'), |
| ('StreamName',':=""'), |
| ) |
| |
| # FILE_NETWORK_OPEN_INFORMATION |
| class SMBFileNetworkOpenInfo(Structure): |
| structure = ( |
| ('CreationTime','<q=0'), |
| ('LastAccessTime','<q=0'), |
| ('LastWriteTime','<q=0'), |
| ('ChangeTime','<q=0'), |
| ('AllocationSize','<q=0'), |
| ('EndOfFile','<q=0'), |
| ('FileAttributes','<L=0'), |
| ('Reserved','<L=0'), |
| ) |
| |
| # SMB_SET_FILE_END_OF_FILE_INFO |
| class SMBSetFileEndOfFileInfo(Structure): |
| structure = ( |
| ('EndOfFile','<q'), |
| ) |
| |
| # TRANS2_FIND_NEXT2 |
| class SMBFindNext2_Parameters(AsciiOrUnicodeStructure): |
| commonHdr = ( |
| ('SID','<H'), |
| ('SearchCount','<H'), |
| ('InformationLevel','<H'), |
| ('ResumeKey','<L'), |
| ('Flags','<H'), |
| ) |
| AsciiStructure = ( |
| ('FileName','z'), |
| ) |
| UnicodeStructure = ( |
| ('FileName','u'), |
| ) |
| |
| class SMBFindNext2Response_Parameters(Structure): |
| structure = ( |
| ('SearchCount','<H'), |
| ('EndOfSearch','<H=1'), |
| ('EaErrorOffset','<H=0'), |
| ('LastNameOffset','<H=0'), |
| ) |
| |
| class SMBFindNext2_Data(Structure): |
| structure = ( |
| ('GetExtendedAttributesListLength','_-GetExtendedAttributesList', 'self["GetExtendedAttributesListLength"]'), |
| ('GetExtendedAttributesList',':'), |
| ) |
| |
| |
| # TRANS2_FIND_FIRST2 |
| class SMBFindFirst2Response_Parameters(Structure): |
| structure = ( |
| ('SID','<H'), |
| ('SearchCount','<H'), |
| ('EndOfSearch','<H=1'), |
| ('EaErrorOffset','<H=0'), |
| ('LastNameOffset','<H=0'), |
| ) |
| |
| class SMBFindFirst2_Parameters(AsciiOrUnicodeStructure): |
| commonHdr = ( |
| ('SearchAttributes','<H'), |
| ('SearchCount','<H'), |
| ('Flags','<H'), |
| ('InformationLevel','<H'), |
| ('SearchStorageType','<L'), |
| ) |
| AsciiStructure = ( |
| ('FileName','z'), |
| ) |
| UnicodeStructure = ( |
| ('FileName','u'), |
| ) |
| |
| class SMBFindFirst2_Data(Structure): |
| structure = ( |
| ('GetExtendedAttributesListLength','_-GetExtendedAttributesList', 'self["GetExtendedAttributesListLength"]'), |
| ('GetExtendedAttributesList',':'), |
| ) |
| |
| # TRANS2_SET_PATH_INFORMATION |
| class SMBSetPathInformation_Parameters(AsciiOrUnicodeStructure): |
| commonHdr = ( |
| ('InformationLevel','<H'), |
| ('Reserved','<L'), |
| ) |
| AsciiStructure = ( |
| ('FileName','z'), |
| ) |
| UnicodeStructure = ( |
| ('FileName','u'), |
| ) |
| |
| class SMBSetPathInformationResponse_Parameters(Structure): |
| structure = ( |
| ('EaErrorOffset','<H=0'), |
| ) |
| |
| # TRANS2_SET_FILE_INFORMATION |
| class SMBSetFileInformation_Parameters(Structure): |
| structure = ( |
| ('FID','<H'), |
| ('InformationLevel','<H'), |
| ('Reserved','<H'), |
| ) |
| |
| class SMBSetFileInformationResponse_Parameters(Structure): |
| structure = ( |
| ('EaErrorOffset','<H=0'), |
| ) |
| |
| # TRANS2_QUERY_FILE_INFORMATION |
| class SMBQueryFileInformation_Parameters(Structure): |
| structure = ( |
| ('FID','<H'), |
| ('InformationLevel','<H'), |
| ) |
| |
| class SMBQueryFileInformationResponse_Parameters(Structure): |
| structure = ( |
| ('EaErrorOffset','<H=0'), |
| ) |
| |
| class SMBQueryFileInformation_Data(Structure): |
| structure = ( |
| ('GetExtendedAttributeList',':'), |
| ) |
| |
| # TRANS2_QUERY_PATH_INFORMATION |
| class SMBQueryPathInformationResponse_Parameters(Structure): |
| structure = ( |
| ('EaErrorOffset','<H=0'), |
| ) |
| |
| class SMBQueryPathInformation_Parameters(AsciiOrUnicodeStructure): |
| commonHdr = ( |
| ('InformationLevel','<H'), |
| ('Reserved','<L=0'), |
| ) |
| AsciiStructure = ( |
| ('FileName','z'), |
| ) |
| UnicodeStructure = ( |
| ('FileName','u'), |
| ) |
| |
| class SMBQueryPathInformation_Data(Structure): |
| structure = ( |
| ('GetExtendedAttributeList',':'), |
| ) |
| |
| |
| # SMB_QUERY_FILE_EA_INFO |
| class SMBQueryFileEaInfo(Structure): |
| structure = ( |
| ('EaSize','<L=0'), |
| ) |
| |
| # SMB_QUERY_FILE_BASIC_INFO |
| class SMBQueryFileBasicInfo(Structure): |
| structure = ( |
| ('CreationTime','<q'), |
| ('LastAccessTime','<q'), |
| ('LastWriteTime','<q'), |
| ('LastChangeTime','<q'), |
| ('ExtFileAttributes','<L'), |
| #('Reserved','<L=0'), |
| ) |
| |
| # SMB_QUERY_FILE_STANDARD_INFO |
| class SMBQueryFileStandardInfo(Structure): |
| structure = ( |
| ('AllocationSize','<q'), |
| ('EndOfFile','<q'), |
| ('NumberOfLinks','<L=0'), |
| ('DeletePending','<B=0'), |
| ('Directory','<B'), |
| ) |
| |
| # SMB_QUERY_FILE_ALL_INFO |
| class SMBQueryFileAllInfo(Structure): |
| structure = ( |
| ('CreationTime','<q'), |
| ('LastAccessTime','<q'), |
| ('LastWriteTime','<q'), |
| ('LastChangeTime','<q'), |
| ('ExtFileAttributes','<L'), |
| ('Reserved','<L=0'), |
| ('AllocationSize','<q'), |
| ('EndOfFile','<q'), |
| ('NumberOfLinks','<L=0'), |
| ('DeletePending','<B=0'), |
| ('Directory','<B'), |
| ('Reserved','<H=0'), |
| ('EaSize','<L=0'), |
| ('FileNameLength','<L-FileName','len(FileName)'), |
| ('FileName',':'), |
| ) |
| |
| # \PIPE\LANMAN NetShareEnum |
| class SMBNetShareEnum(Structure): |
| structure = ( |
| ('RAPOpcode','<H=0'), |
| ('ParamDesc','z'), |
| ('DataDesc','z'), |
| ('InfoLevel','<H'), |
| ('ReceiveBufferSize','<H'), |
| ) |
| |
| class SMBNetShareEnumResponse(Structure): |
| structure = ( |
| ('Status','<H=0'), |
| ('Convert','<H=0'), |
| ('EntriesReturned','<H'), |
| ('EntriesAvailable','<H'), |
| ) |
| |
| class NetShareInfo1(Structure): |
| structure = ( |
| ('NetworkName','13s'), |
| ('Pad','<B=0'), |
| ('Type','<H=0'), |
| ('RemarkOffsetLow','<H=0'), |
| ('RemarkOffsetHigh','<H=0'), |
| ) |
| |
| # \PIPE\LANMAN NetServerGetInfo |
| class SMBNetServerGetInfoResponse(Structure): |
| structure = ( |
| ('Status','<H=0'), |
| ('Convert','<H=0'), |
| ('TotalBytesAvailable','<H'), |
| ) |
| |
| class SMBNetServerInfo1(Structure): |
| # Level 1 Response |
| structure = ( |
| ('ServerName','16s'), |
| ('MajorVersion','B=5'), |
| ('MinorVersion','B=0'), |
| ('ServerType','<L=3'), |
| ('ServerCommentLow','<H=0'), |
| ('ServerCommentHigh','<H=0'), |
| ) |
| |
| # \PIPE\LANMAN NetShareGetInfo |
| class SMBNetShareGetInfo(Structure): |
| structure = ( |
| ('RAPOpcode','<H=0'), |
| ('ParamDesc','z'), |
| ('DataDesc','z'), |
| ('ShareName','z'), |
| ('InfoLevel','<H'), |
| ('ReceiveBufferSize','<H'), |
| ) |
| |
| class SMBNetShareGetInfoResponse(Structure): |
| structure = ( |
| ('Status','<H=0'), |
| ('Convert','<H=0'), |
| ('TotalBytesAvailable','<H'), |
| ) |
| |
| ############# Security Features |
| class SecurityFeatures(Structure): |
| structure = ( |
| ('Key','<L=0'), |
| ('CID','<H=0'), |
| ('SequenceNumber','<H=0'), |
| ) |
| |
| ############# SMB_COM_QUERY_INFORMATION2 (0x23) |
| class SMBQueryInformation2_Parameters(Structure): |
| structure = ( |
| ('Fid','<H'), |
| ) |
| |
| class SMBQueryInformation2Response_Parameters(Structure): |
| structure = ( |
| ('CreateDate','<H'), |
| ('CreationTime','<H'), |
| ('LastAccessDate','<H'), |
| ('LastAccessTime','<H'), |
| ('LastWriteDate','<H'), |
| ('LastWriteTime','<H'), |
| ('FileDataSize','<L'), |
| ('FileAllocationSize','<L'), |
| ('FileAttributes','<L'), |
| ) |
| |
| |
| |
| ############# SMB_COM_SESSION_SETUP_ANDX (0x73) |
| class SMBSessionSetupAndX_Parameters(SMBAndXCommand_Parameters): |
| structure = ( |
| ('MaxBuffer','<H'), |
| ('MaxMpxCount','<H'), |
| ('VCNumber','<H'), |
| ('SessionKey','<L'), |
| ('AnsiPwdLength','<H'), |
| ('UnicodePwdLength','<H'), |
| ('_reserved','<L=0'), |
| ('Capabilities','<L'), |
| ) |
| |
| class SMBSessionSetupAndX_Extended_Parameters(SMBAndXCommand_Parameters): |
| structure = ( |
| ('MaxBufferSize','<H'), |
| ('MaxMpxCount','<H'), |
| ('VcNumber','<H'), |
| ('SessionKey','<L'), |
| ('SecurityBlobLength','<H'), |
| ('Reserved','<L=0'), |
| ('Capabilities','<L'), |
| ) |
| |
| class SMBSessionSetupAndX_Data(AsciiOrUnicodeStructure): |
| AsciiStructure = ( |
| ('AnsiPwdLength','_-AnsiPwd','self["AnsiPwdLength"]'), |
| ('UnicodePwdLength','_-UnicodePwd','self["UnicodePwdLength"]'), |
| ('AnsiPwd',':=""'), |
| ('UnicodePwd',':=""'), |
| ('Account','z=""'), |
| ('PrimaryDomain','z=""'), |
| ('NativeOS','z=""'), |
| ('NativeLanMan','z=""'), |
| ) |
| |
| UnicodeStructure = ( |
| ('AnsiPwdLength','_-AnsiPwd','self["AnsiPwdLength"]'), |
| ('UnicodePwdLength','_-UnicodePwd','self["UnicodePwdLength"]'), |
| ('AnsiPwd',':=""'), |
| ('UnicodePwd',':=""'), |
| ('Account','u=""'), |
| ('PrimaryDomain','u=""'), |
| ('NativeOS','u=""'), |
| ('NativeLanMan','u=""'), |
| ) |
| |
| class SMBSessionSetupAndX_Extended_Data(AsciiOrUnicodeStructure): |
| AsciiStructure = ( |
| ('SecurityBlobLength','_-SecurityBlob','self["SecurityBlobLength"]'), |
| ('SecurityBlob',':'), |
| ('NativeOS','z=""'), |
| ('NativeLanMan','z=""'), |
| ) |
| |
| UnicodeStructure = ( |
| ('SecurityBlobLength','_-SecurityBlob','self["SecurityBlobLength"]'), |
| ('SecurityBlob',':'), |
| ('NativeOS','u=""'), |
| ('NativeLanMan','u=""'), |
| ) |
| |
| class SMBSessionSetupAndXResponse_Parameters(SMBAndXCommand_Parameters): |
| structure = ( |
| ('Action','<H'), |
| ) |
| |
| class SMBSessionSetupAndX_Extended_Response_Parameters(SMBAndXCommand_Parameters): |
| structure = ( |
| ('Action','<H=0'), |
| ('SecurityBlobLength','<H'), |
| ) |
| |
| class SMBSessionSetupAndXResponse_Data(AsciiOrUnicodeStructure): |
| AsciiStructure = ( |
| ('NativeOS','z=""'), |
| ('NativeLanMan','z=""'), |
| ('PrimaryDomain','z=""'), |
| ) |
| |
| UnicodeStructure = ( |
| ('NativeOS','u=""'), |
| ('NativeLanMan','u=""'), |
| ('PrimaryDomain','u=""'), |
| ) |
| |
| class SMBSessionSetupAndX_Extended_Response_Data(AsciiOrUnicodeStructure): |
| AsciiStructure = ( |
| ('SecurityBlobLength','_-SecurityBlob','self["SecurityBlobLength"]'), |
| ('SecurityBlob',':'), |
| ('NativeOS','z=""'), |
| ('NativeLanMan','z=""'), |
| ) |
| |
| UnicodeStructure = ( |
| ('SecurityBlobLength','_-SecurityBlob','self["SecurityBlobLength"]'), |
| ('SecurityBlob',':'), |
| ('NativeOS','u=""'), |
| ('NativeLanMan','u=""'), |
| ) |
| |
| ############# SMB_COM_TREE_CONNECT (0x70) |
| class SMBTreeConnect_Parameters(SMBCommand_Parameters): |
| structure = ( |
| ) |
| |
| class SMBTreeConnect_Data(SMBCommand_Parameters): |
| structure = ( |
| ('PathFormat','"\x04'), |
| ('Path','z'), |
| ('PasswordFormat','"\x04'), |
| ('Password','z'), |
| ('ServiceFormat','"\x04'), |
| ('Service','z'), |
| ) |
| |
| ############# SMB_COM_TREE_CONNECT_ANDX (0x75) |
| class SMBTreeConnectAndX_Parameters(SMBAndXCommand_Parameters): |
| structure = ( |
| ('Flags','<H=0'), |
| ('PasswordLength','<H'), |
| ) |
| |
| class SMBTreeConnectAndXResponse_Parameters(SMBAndXCommand_Parameters): |
| structure = ( |
| ('OptionalSupport','<H=0'), |
| ) |
| |
| class SMBTreeConnectAndXExtendedResponse_Parameters(SMBAndXCommand_Parameters): |
| structure = ( |
| ('OptionalSupport','<H=1'), |
| ('MaximalShareAccessRights','<L=0x1fffff'), |
| ('GuestMaximalShareAccessRights','<L=0x1fffff'), |
| ) |
| |
| class SMBTreeConnectAndX_Data(AsciiOrUnicodeStructure): |
| AsciiStructure = ( |
| ('_PasswordLength','_-Password','self["_PasswordLength"]'), |
| ('Password',':'), |
| ('Path','z'), |
| ('Service','z'), |
| ) |
| |
| UnicodeStructure = ( |
| ('_PasswordLength','_-Password','self["_PasswordLength"] if self["_PasswordLength"] > 0 else 1'), |
| ('Password',':'), |
| ('Path','u'), |
| ('Service','z'), |
| ) |
| |
| class SMBTreeConnectAndXResponse_Data(AsciiOrUnicodeStructure): |
| AsciiStructure = ( |
| ('Service','z'), |
| ('PadLen','_-Pad','self["PadLen"]'), |
| ('Pad',':=""'), |
| ('NativeFileSystem','z'), |
| ) |
| UnicodeStructure = ( |
| ('Service','z'), |
| ('PadLen','_-Pad','self["PadLen"]'), |
| ('Pad',':=""'), |
| ('NativeFileSystem','u'), |
| ) |
| |
| ############# SMB_COM_NT_CREATE_ANDX (0xA2) |
| class SMBNtCreateAndX_Parameters(SMBAndXCommand_Parameters): |
| structure = ( |
| ('_reserved', 'B=0'), |
| ('FileNameLength','<H'), # NameLength |
| ('CreateFlags','<L'), # Flags |
| ('RootFid','<L=0'), # RootDirectoryFID |
| ('AccessMask','<L'), # DesiredAccess |
| ('AllocationSizeLo','<L=0'), # AllocationSize |
| ('AllocationSizeHi','<L=0'), |
| ('FileAttributes','<L=0'), # ExtFileAttributes |
| ('ShareAccess','<L=3'), # |
| ('Disposition','<L=1'), # CreateDisposition |
| ('CreateOptions','<L'), # CreateOptions |
| ('Impersonation','<L=2'), |
| ('SecurityFlags','B=3'), |
| ) |
| |
| class SMBNtCreateAndXResponse_Parameters(SMBAndXCommand_Parameters): |
| # XXX Is there a memory leak in the response for NTCreate (where the Data section would be) in Win 2000, Win XP, and Win 2003? |
| structure = ( |
| ('OplockLevel', 'B=0'), |
| ('Fid','<H'), |
| ('CreateAction','<L'), |
| ('CreateTime','<q=0'), |
| ('LastAccessTime','<q=0'), |
| ('LastWriteTime','<q=0'), |
| ('LastChangeTime','<q=0'), |
| ('FileAttributes','<L=0x80'), |
| ('AllocationSize','<q=0'), |
| ('EndOfFile','<q=0'), |
| ('FileType','<H=0'), |
| ('IPCState','<H=0'), |
| ('IsDirectory','B'), |
| ) |
| |
| class SMBNtCreateAndXExtendedResponse_Parameters(SMBAndXCommand_Parameters): |
| # [MS-SMB] Extended response description |
| structure = ( |
| ('OplockLevel', 'B=0'), |
| ('Fid','<H'), |
| ('CreateAction','<L'), |
| ('CreateTime','<q=0'), |
| ('LastAccessTime','<q=0'), |
| ('LastWriteTime','<q=0'), |
| ('LastChangeTime','<q=0'), |
| ('FileAttributes','<L=0x80'), |
| ('AllocationSize','<q=0'), |
| ('EndOfFile','<q=0'), |
| ('FileType','<H=0'), |
| ('IPCState','<H=0'), |
| ('IsDirectory','B'), |
| ('VolumeGUID','16s'), |
| ('FileIdLow','<L=0'), |
| ('FileIdHigh','<L=0'), |
| ('MaximalAccessRights','<L=0x12019b'), |
| ('GuestMaximalAccessRights','<L=0x120089'), |
| ) |
| |
| class SMBNtCreateAndX_Data(AsciiOrUnicodeStructure): |
| AsciiStructure = ( |
| ('FileName','z'), |
| ) |
| UnicodeStructure = ( |
| ('Pad','B'), |
| ('FileName','u'), |
| ) |
| |
| ############# SMB_COM_OPEN_ANDX (0xD2) |
| class SMBOpenAndX_Parameters(SMBAndXCommand_Parameters): |
| structure = ( |
| ('Flags','<H=0'), |
| ('DesiredAccess','<H=0'), |
| ('SearchAttributes','<H=0'), |
| ('FileAttributes','<H=0'), |
| ('CreationTime','<L=0'), |
| ('OpenMode','<H=1'), # SMB_O_OPEN = 1 |
| ('AllocationSize','<L=0'), |
| ('Reserved','8s=""'), |
| ) |
| |
| class SMBOpenAndX_Data(SMBNtCreateAndX_Data): |
| pass |
| |
| class SMBOpenAndXResponse_Parameters(SMBAndXCommand_Parameters): |
| structure = ( |
| ('Fid','<H=0'), |
| ('FileAttributes','<H=0'), |
| ('LastWriten','<L=0'), |
| ('FileSize','<L=0'), |
| ('GrantedAccess','<H=0'), |
| ('FileType','<H=0'), |
| ('IPCState','<H=0'), |
| ('Action','<H=0'), |
| ('ServerFid','<L=0'), |
| ('_reserved','<H=0'), |
| ) |
| |
| ############# SMB_COM_WRITE (0x0B) |
| class SMBWrite_Parameters(SMBCommand_Parameters): |
| structure = ( |
| ('Fid','<H'), |
| ('Count','<H'), |
| ('Offset','<L'), |
| ('Remaining','<H'), |
| ) |
| |
| class SMBWriteResponse_Parameters(SMBCommand_Parameters): |
| structure = ( |
| ('Count','<H'), |
| ) |
| |
| class SMBWrite_Data(Structure): |
| structure = ( |
| ('BufferFormat','<B=1'), |
| ('DataLength','<H-Data'), |
| ('Data',':'), |
| ) |
| |
| |
| ############# SMB_COM_WRITE_ANDX (0x2F) |
| class SMBWriteAndX_Parameters(SMBAndXCommand_Parameters): |
| structure = ( |
| ('Fid','<H=0'), |
| ('Offset','<L=0'), |
| ('_reserved','<L=0xff'), |
| ('WriteMode','<H=8'), |
| ('Remaining','<H=0'), |
| ('DataLength_Hi','<H=0'), |
| ('DataLength','<H=0'), |
| ('DataOffset','<H=0'), |
| ('HighOffset','<L=0'), |
| ) |
| |
| class SMBWriteAndX_Data_Short(Structure): |
| structure = ( |
| ('_PadLen','_-Pad','self["DataOffset"] - 59'), |
| ('Pad',':'), |
| #('Pad','<B=0'), |
| ('DataLength','_-Data','self["DataLength"]'), |
| ('Data',':'), |
| ) |
| |
| class SMBWriteAndX_Data(Structure): |
| structure = ( |
| ('_PadLen','_-Pad','self["DataOffset"] - 63'), |
| ('Pad',':'), |
| #('Pad','<B=0'), |
| ('DataLength','_-Data','self["DataLength"]'), |
| ('Data',':'), |
| ) |
| |
| |
| class SMBWriteAndX_Parameters_Short(SMBAndXCommand_Parameters): |
| structure = ( |
| ('Fid','<H'), |
| ('Offset','<L'), |
| ('_reserved','<L=0xff'), |
| ('WriteMode','<H=8'), |
| ('Remaining','<H'), |
| ('DataLength_Hi','<H=0'), |
| ('DataLength','<H'), |
| ('DataOffset','<H=0'), |
| ) |
| |
| class SMBWriteAndXResponse_Parameters(SMBAndXCommand_Parameters): |
| structure = ( |
| ('Count','<H'), |
| ('Available','<H'), |
| ('Reserved','<L=0'), |
| ) |
| |
| ############# SMB_COM_WRITE_RAW (0x1D) |
| class SMBWriteRaw_Parameters(SMBCommand_Parameters): |
| structure = ( |
| ('Fid','<H'), |
| ('Count','<H'), |
| ('_reserved','<H=0'), |
| ('Offset','<L'), |
| ('Timeout','<L=0'), |
| ('WriteMode','<H=0'), |
| ('_reserved2','<L=0'), |
| ('DataLength','<H'), |
| ('DataOffset','<H=0'), |
| ) |
| |
| ############# SMB_COM_READ (0x0A) |
| class SMBRead_Parameters(SMBCommand_Parameters): |
| structure = ( |
| ('Fid','<H'), |
| ('Count','<H'), |
| ('Offset','<L'), |
| ('Remaining','<H=Count'), |
| ) |
| |
| class SMBReadResponse_Parameters(Structure): |
| structure = ( |
| ('Count','<H=0'), |
| ('_reserved','8s=""'), |
| ) |
| |
| class SMBReadResponse_Data(Structure): |
| structure = ( |
| ('BufferFormat','<B=0x1'), |
| ('DataLength','<H-Data'), |
| ('Data',':'), |
| ) |
| |
| ############# SMB_COM_READ_RAW (0x1A) |
| class SMBReadRaw_Parameters(SMBCommand_Parameters): |
| structure = ( |
| ('Fid','<H'), |
| ('Offset','<L'), |
| ('MaxCount','<H'), |
| ('MinCount','<H=MaxCount'), |
| ('Timeout','<L=0'), |
| ('_reserved','<H=0'), |
| ) |
| |
| ############# SMB_COM_NT_TRANSACT (0xA0) |
| class SMBNTTransaction_Parameters(SMBCommand_Parameters): |
| structure = ( |
| ('MaxSetupCount','<B=0'), |
| ('Reserved1','<H=0'), |
| ('TotalParameterCount','<L'), |
| ('TotalDataCount','<L'), |
| ('MaxParameterCount','<L=1024'), |
| ('MaxDataCount','<L=65504'), |
| ('ParameterCount','<L'), |
| ('ParameterOffset','<L'), |
| ('DataCount','<L'), |
| ('DataOffset','<L'), |
| ('SetupCount','<B=len(Setup)/2'), |
| ('Function','<H=0'), |
| ('SetupLength','_-Setup','SetupCount*2'), |
| ('Setup',':'), |
| ) |
| |
| class SMBNTTransactionResponse_Parameters(SMBCommand_Parameters): |
| structure = ( |
| ('Reserved1','3s=""'), |
| ('TotalParameterCount','<L'), |
| ('TotalDataCount','<L'), |
| ('ParameterCount','<L'), |
| ('ParameterOffset','<L'), |
| ('ParameterDisplacement','<L=0'), |
| ('DataCount','<L'), |
| ('DataOffset','<L'), |
| ('DataDisplacement','<L=0'), |
| ('SetupCount','<B=0'), |
| ('SetupLength','_-Setup','SetupCount*2'), |
| ('Setup',':'), |
| ) |
| |
| class SMBNTTransaction_Data(Structure): |
| structure = ( |
| ('Pad1Length','_-Pad1','self["Pad1Length"]'), |
| ('Pad1',':'), |
| ('NT_Trans_ParametersLength','_-NT_Trans_Parameters','self["NT_Trans_ParametersLength"]'), |
| ('NT_Trans_Parameters',':'), |
| ('Pad2Length','_-Pad2','self["Pad2Length"]'), |
| ('Pad2',':'), |
| ('NT_Trans_DataLength','_-NT_Trans_Data','self["NT_Trans_DataLength"]'), |
| ('NT_Trans_Data',':'), |
| ) |
| |
| class SMBNTTransactionResponse_Data(Structure): |
| structure = ( |
| ('Pad1Length','_-Pad1','self["Pad1Length"]'), |
| ('Pad1',':'), |
| ('Trans_ParametersLength','_-Trans_Parameters','self["Trans_ParametersLength"]'), |
| ('Trans_Parameters',':'), |
| ('Pad2Length','_-Pad2','self["Pad2Length"]'), |
| ('Pad2',':'), |
| ('Trans_DataLength','_-Trans_Data','self["Trans_DataLength"]'), |
| ('Trans_Data',':'), |
| ) |
| |
| |
| ############# SMB_COM_TRANSACTION2_SECONDARY (0x33) |
| class SMBTransaction2Secondary_Parameters(SMBCommand_Parameters): |
| structure = ( |
| ('TotalParameterCount','<H'), |
| ('TotalDataCount','<H'), |
| ('ParameterCount','<H'), |
| ('ParameterOffset','<H'), |
| ('DataCount','<H'), |
| ('DataOffset','<H'), |
| ('DataDisplacement','<H=0'), |
| ('FID','<H'), |
| ) |
| |
| class SMBTransaction2Secondary_Data(Structure): |
| structure = ( |
| ('Pad1Length','_-Pad1','self["Pad1Length"]'), |
| ('Pad1',':'), |
| ('Trans_ParametersLength','_-Trans_Parameters','self["Trans_ParametersLength"]'), |
| ('Trans_Parameters',':'), |
| ('Pad2Length','_-Pad2','self["Pad2Length"]'), |
| ('Pad2',':'), |
| ('Trans_DataLength','_-Trans_Data','self["Trans_DataLength"]'), |
| ('Trans_Data',':'), |
| ) |
| |
| |
| ############# SMB_COM_TRANSACTION2 (0x32) |
| |
| class SMBTransaction2_Parameters(SMBCommand_Parameters): |
| structure = ( |
| ('TotalParameterCount','<H'), |
| ('TotalDataCount','<H'), |
| ('MaxParameterCount','<H=1024'), |
| ('MaxDataCount','<H=65504'), |
| ('MaxSetupCount','<B=0'), |
| ('Reserved1','<B=0'), |
| ('Flags','<H=0'), |
| ('Timeout','<L=0'), |
| ('Reserved2','<H=0'), |
| ('ParameterCount','<H'), |
| ('ParameterOffset','<H'), |
| ('DataCount','<H'), |
| ('DataOffset','<H'), |
| ('SetupCount','<B=len(Setup)/2'), |
| ('Reserved3','<B=0'), |
| ('SetupLength','_-Setup','SetupCount*2'), |
| ('Setup',':'), |
| ) |
| |
| class SMBTransaction2Response_Parameters(SMBCommand_Parameters): |
| structure = ( |
| ('TotalParameterCount','<H'), |
| ('TotalDataCount','<H'), |
| ('Reserved1','<H=0'), |
| ('ParameterCount','<H'), |
| ('ParameterOffset','<H'), |
| ('ParameterDisplacement','<H=0'), |
| ('DataCount','<H'), |
| ('DataOffset','<H'), |
| ('DataDisplacement','<H=0'), |
| ('SetupCount','<B=0'), |
| ('Reserved2','<B=0'), |
| ('SetupLength','_-Setup','SetupCount*2'), |
| ('Setup',':'), |
| ) |
| |
| class SMBTransaction2_Data(Structure): |
| structure = ( |
| # ('NameLength','_-Name','1'), |
| # ('Name',':'), |
| ('Pad1Length','_-Pad1','self["Pad1Length"]'), |
| ('Pad1',':'), |
| ('Trans_ParametersLength','_-Trans_Parameters','self["Trans_ParametersLength"]'), |
| ('Trans_Parameters',':'), |
| ('Pad2Length','_-Pad2','self["Pad2Length"]'), |
| ('Pad2',':'), |
| ('Trans_DataLength','_-Trans_Data','self["Trans_DataLength"]'), |
| ('Trans_Data',':'), |
| ) |
| |
| class SMBTransaction2Response_Data(Structure): |
| structure = ( |
| ('Pad1Length','_-Pad1','self["Pad1Length"]'), |
| ('Pad1',':'), |
| ('Trans_ParametersLength','_-Trans_Parameters','self["Trans_ParametersLength"]'), |
| ('Trans_Parameters',':'), |
| ('Pad2Length','_-Pad2','self["Pad2Length"]'), |
| ('Pad2',':'), |
| ('Trans_DataLength','_-Trans_Data','self["Trans_DataLength"]'), |
| ('Trans_Data',':'), |
| ) |
| |
| ############# SMB_COM_QUERY_INFORMATION (0x08) |
| |
| class SMBQueryInformation_Data(AsciiOrUnicodeStructure): |
| AsciiStructure = ( |
| ('BufferFormat','B=4'), |
| ('FileName','z'), |
| ) |
| UnicodeStructure = ( |
| ('BufferFormat','B=4'), |
| ('FileName','u'), |
| ) |
| |
| |
| class SMBQueryInformationResponse_Parameters(Structure): |
| structure = ( |
| ('FileAttributes','<H'), |
| ('LastWriteTime','<L'), |
| ('FileSize','<L'), |
| ('Reserved','"0123456789'), |
| ) |
| |
| ############# SMB_COM_TRANSACTION (0x25) |
| class SMBTransaction_Parameters(SMBCommand_Parameters): |
| structure = ( |
| ('TotalParameterCount','<H'), |
| ('TotalDataCount','<H'), |
| ('MaxParameterCount','<H=1024'), |
| ('MaxDataCount','<H=65504'), |
| ('MaxSetupCount','<B=0'), |
| ('Reserved1','<B=0'), |
| ('Flags','<H=0'), |
| ('Timeout','<L=0'), |
| ('Reserved2','<H=0'), |
| ('ParameterCount','<H'), |
| ('ParameterOffset','<H'), |
| ('DataCount','<H'), |
| ('DataOffset','<H'), |
| ('SetupCount','<B=len(Setup)/2'), |
| ('Reserved3','<B=0'), |
| ('SetupLength','_-Setup','SetupCount*2'), |
| ('Setup',':'), |
| ) |
| |
| class SMBTransactionResponse_Parameters(SMBCommand_Parameters): |
| structure = ( |
| ('TotalParameterCount','<H'), |
| ('TotalDataCount','<H'), |
| ('Reserved1','<H=0'), |
| ('ParameterCount','<H'), |
| ('ParameterOffset','<H'), |
| ('ParameterDisplacement','<H=0'), |
| ('DataCount','<H'), |
| ('DataOffset','<H'), |
| ('DataDisplacement','<H=0'), |
| ('SetupCount','<B'), |
| ('Reserved2','<B=0'), |
| ('SetupLength','_-Setup','SetupCount*2'), |
| ('Setup',':'), |
| ) |
| |
| # TODO: We should merge these both. But this will require fixing |
| # the instances where this structure is used on the client side |
| class SMBTransaction_SData(AsciiOrUnicodeStructure): |
| AsciiStructure = ( |
| ('Name','z'), |
| ('Trans_ParametersLength','_-Trans_Parameters'), |
| ('Trans_Parameters',':'), |
| ('Trans_DataLength','_-Trans_Data'), |
| ('Trans_Data',':'), |
| ) |
| UnicodeStructure = ( |
| ('Pad','B'), |
| ('Name','u'), |
| ('Trans_ParametersLength','_-Trans_Parameters'), |
| ('Trans_Parameters',':'), |
| ('Trans_DataLength','_-Trans_Data'), |
| ('Trans_Data',':'), |
| ) |
| |
| class SMBTransaction_Data(Structure): |
| structure = ( |
| ('NameLength','_-Name'), |
| ('Name',':'), |
| ('Trans_ParametersLength','_-Trans_Parameters'), |
| ('Trans_Parameters',':'), |
| ('Trans_DataLength','_-Trans_Data'), |
| ('Trans_Data',':'), |
| ) |
| |
| class SMBTransactionResponse_Data(Structure): |
| structure = ( |
| ('Trans_ParametersLength','_-Trans_Parameters'), |
| ('Trans_Parameters',':'), |
| ('Trans_DataLength','_-Trans_Data'), |
| ('Trans_Data',':'), |
| ) |
| |
| ############# SMB_COM_READ_ANDX (0x2E) |
| class SMBReadAndX_Parameters(SMBAndXCommand_Parameters): |
| structure = ( |
| ('Fid','<H'), |
| ('Offset','<L'), |
| ('MaxCount','<H'), |
| ('MinCount','<H=MaxCount'), |
| ('_reserved','<L=0x0'), |
| ('Remaining','<H=MaxCount'), |
| ('HighOffset','<L=0'), |
| ) |
| |
| class SMBReadAndX_Parameters2(SMBAndXCommand_Parameters): |
| structure = ( |
| ('Fid','<H'), |
| ('Offset','<L'), |
| ('MaxCount','<H'), |
| ('MinCount','<H=MaxCount'), |
| ('_reserved','<L=0xffffffff'), |
| ('Remaining','<H=MaxCount'), |
| ) |
| |
| class SMBReadAndXResponse_Parameters(SMBAndXCommand_Parameters): |
| structure = ( |
| ('Remaining','<H=0'), |
| ('DataMode','<H=0'), |
| ('_reserved','<H=0'), |
| ('DataCount','<H'), |
| ('DataOffset','<H'), |
| ('DataCount_Hi','<L'), |
| ('_reserved2','6s=""'), |
| ) |
| |
| ############# SMB_COM_ECHO (0x2B) |
| class SMBEcho_Data(Structure): |
| structure = ( |
| ('Data',':'), |
| ) |
| |
| class SMBEcho_Parameters(Structure): |
| structure = ( |
| ('EchoCount','<H'), |
| ) |
| |
| class SMBEchoResponse_Data(Structure): |
| structure = ( |
| ('Data',':'), |
| ) |
| |
| class SMBEchoResponse_Parameters(Structure): |
| structure = ( |
| ('SequenceNumber','<H=1'), |
| ) |
| |
| ############# SMB_COM_QUERY_INFORMATION_DISK (0x80) |
| class SMBQueryInformationDiskResponse_Parameters(Structure): |
| structure = ( |
| ('TotalUnits','<H'), |
| ('BlocksPerUnit','<H'), |
| ('BlockSize','<H'), |
| ('FreeUnits','<H'), |
| ('Reserved','<H=0'), |
| ) |
| |
| |
| ############# SMB_COM_LOGOFF_ANDX (0x74) |
| class SMBLogOffAndX(SMBAndXCommand_Parameters): |
| strucure = () |
| |
| ############# SMB_COM_CLOSE (0x04) |
| class SMBClose_Parameters(SMBCommand_Parameters): |
| structure = ( |
| ('FID','<H'), |
| ('Time','<L=0'), |
| ) |
| |
| ############# SMB_COM_FLUSH (0x05) |
| class SMBFlush_Parameters(SMBCommand_Parameters): |
| structure = ( |
| ('FID','<H'), |
| ) |
| |
| ############# SMB_COM_CREATE_DIRECTORY (0x00) |
| class SMBCreateDirectory_Data(AsciiOrUnicodeStructure): |
| AsciiStructure = ( |
| ('BufferFormat','<B=4'), |
| ('DirectoryName','z'), |
| ) |
| UnicodeStructure = ( |
| ('BufferFormat','<B=4'), |
| ('DirectoryName','u'), |
| ) |
| |
| ############# SMB_COM_DELETE (0x06) |
| class SMBDelete_Data(AsciiOrUnicodeStructure): |
| AsciiStructure = ( |
| ('BufferFormat','<B=4'), |
| ('FileName','z'), |
| ) |
| UnicodeStructure = ( |
| ('BufferFormat','<B=4'), |
| ('FileName','u'), |
| ) |
| |
| class SMBDelete_Parameters(Structure): |
| structure = ( |
| ('SearchAttributes','<H'), |
| ) |
| |
| ############# SMB_COM_DELETE_DIRECTORY (0x01) |
| class SMBDeleteDirectory_Data(AsciiOrUnicodeStructure): |
| AsciiStructure = ( |
| ('BufferFormat','<B=4'), |
| ('DirectoryName','z'), |
| ) |
| UnicodeStructure = ( |
| ('BufferFormat','<B=4'), |
| ('DirectoryName','u'), |
| ) |
| |
| ############# SMB_COM_CHECK_DIRECTORY (0x10) |
| class SMBCheckDirectory_Data(AsciiOrUnicodeStructure): |
| AsciiStructure = ( |
| ('BufferFormat','<B=4'), |
| ('DirectoryName','z'), |
| ) |
| UnicodeStructure = ( |
| ('BufferFormat','<B=4'), |
| ('DirectoryName','u'), |
| ) |
| |
| ############# SMB_COM_RENAME (0x07) |
| class SMBRename_Parameters(SMBCommand_Parameters): |
| structure = ( |
| ('SearchAttributes','<H'), |
| ) |
| |
| class SMBRename_Data(AsciiOrUnicodeStructure): |
| AsciiStructure = ( |
| ('BufferFormat1','<B=4'), |
| ('OldFileName','z'), |
| ('BufferFormat2','<B=4'), |
| ('NewFileName','z'), |
| ) |
| UnicodeStructure = ( |
| ('BufferFormat1','<B=4'), |
| ('OldFileName','u'), |
| ('BufferFormat2','<B=4'), |
| ('Pad','B=0'), |
| ('NewFileName','u'), |
| ) |
| |
| |
| ############# SMB_COM_OPEN (0x02) |
| class SMBOpen_Parameters(SMBCommand_Parameters): |
| structure = ( |
| ('DesiredAccess','<H=0'), |
| ('SearchAttributes','<H=0'), |
| ) |
| |
| class SMBOpen_Data(AsciiOrUnicodeStructure): |
| AsciiStructure = ( |
| ('FileNameFormat','"\x04'), |
| ('FileName','z'), |
| ) |
| UnicodeStructure = ( |
| ('FileNameFormat','"\x04'), |
| ('FileName','z'), |
| ) |
| |
| class SMBOpenResponse_Parameters(SMBCommand_Parameters): |
| structure = ( |
| ('Fid','<H=0'), |
| ('FileAttributes','<H=0'), |
| ('LastWriten','<L=0'), |
| ('FileSize','<L=0'), |
| ('GrantedAccess','<H=0'), |
| ) |
| |
| ############# EXTENDED SECURITY CLASSES |
| class SMBExtended_Security_Parameters(Structure): |
| structure = ( |
| ('DialectIndex','<H'), |
| ('SecurityMode','<B'), |
| ('MaxMpxCount','<H'), |
| ('MaxNumberVcs','<H'), |
| ('MaxBufferSize','<L'), |
| ('MaxRawSize','<L'), |
| ('SessionKey','<L'), |
| ('Capabilities','<L'), |
| ('LowDateTime','<L'), |
| ('HighDateTime','<L'), |
| ('ServerTimeZone','<H'), |
| ('ChallengeLength','<B'), |
| ) |
| |
| class SMBExtended_Security_Data(Structure): |
| structure = ( |
| ('ServerGUID','16s'), |
| ('SecurityBlob',':'), |
| ) |
| |
| class SMBNTLMDialect_Parameters(Structure): |
| structure = ( |
| ('DialectIndex','<H'), |
| ('SecurityMode','<B'), |
| ('MaxMpxCount','<H'), |
| ('MaxNumberVcs','<H'), |
| ('MaxBufferSize','<L'), |
| ('MaxRawSize','<L'), |
| ('SessionKey','<L'), |
| ('Capabilities','<L'), |
| ('LowDateTime','<L'), |
| ('HighDateTime','<L'), |
| ('ServerTimeZone','<H'), |
| ('ChallengeLength','<B'), |
| ) |
| |
| class SMBNTLMDialect_Data(Structure): |
| structure = ( |
| ('ChallengeLength','_-Challenge','self["ChallengeLength"]'), |
| ('Challenge',':'), |
| ('Payload',':'), |
| # For some reason on an old Linux this field is not present, we have to check this out. There must be a flag stating this. |
| ('DomainName','_'), |
| ('ServerName','_'), |
| ) |
| def __init__(self,data = None, alignment = 0): |
| Structure.__init__(self,data,alignment) |
| #self['ChallengeLength']=8 |
| |
| def fromString(self,data): |
| Structure.fromString(self,data) |
| self['DomainName'] = '' |
| self['ServerName'] = '' |
| |
| class SMB: |
| # SMB Command Codes |
| SMB_COM_CREATE_DIRECTORY = 0x00 |
| SMB_COM_DELETE_DIRECTORY = 0x01 |
| SMB_COM_OPEN = 0x02 |
| SMB_COM_CREATE = 0x03 |
| SMB_COM_CLOSE = 0x04 |
| SMB_COM_FLUSH = 0x05 |
| SMB_COM_DELETE = 0x06 |
| SMB_COM_RENAME = 0x07 |
| SMB_COM_QUERY_INFORMATION = 0x08 |
| SMB_COM_SET_INFORMATION = 0x09 |
| SMB_COM_READ = 0x0A |
| SMB_COM_WRITE = 0x0B |
| SMB_COM_LOCK_BYTE_RANGE = 0x0C |
| SMB_COM_UNLOCK_BYTE_RANGE = 0x0D |
| SMB_COM_CREATE_TEMPORARY = 0x0E |
| SMB_COM_CREATE_NEW = 0x0F |
| SMB_COM_CHECK_DIRECTORY = 0x10 |
| SMB_COM_PROCESS_EXIT = 0x11 |
| SMB_COM_SEEK = 0x12 |
| SMB_COM_LOCK_AND_READ = 0x13 |
| SMB_COM_WRITE_AND_UNLOCK = 0x14 |
| SMB_COM_READ_RAW = 0x1A |
| SMB_COM_READ_MPX = 0x1B |
| SMB_COM_READ_MPX_SECONDARY = 0x1C |
| SMB_COM_WRITE_RAW = 0x1D |
| SMB_COM_WRITE_MPX = 0x1E |
| SMB_COM_WRITE_MPX_SECONDARY = 0x1F |
| SMB_COM_WRITE_COMPLETE = 0x20 |
| SMB_COM_QUERY_SERVER = 0x21 |
| SMB_COM_SET_INFORMATION2 = 0x22 |
| SMB_COM_QUERY_INFORMATION2 = 0x23 |
| SMB_COM_LOCKING_ANDX = 0x24 |
| SMB_COM_TRANSACTION = 0x25 |
| SMB_COM_TRANSACTION_SECONDARY = 0x26 |
| SMB_COM_IOCTL = 0x27 |
| SMB_COM_IOCTL_SECONDARY = 0x28 |
| SMB_COM_COPY = 0x29 |
| SMB_COM_MOVE = 0x2A |
| SMB_COM_ECHO = 0x2B |
| SMB_COM_WRITE_AND_CLOSE = 0x2C |
| SMB_COM_OPEN_ANDX = 0x2D |
| SMB_COM_READ_ANDX = 0x2E |
| SMB_COM_WRITE_ANDX = 0x2F |
| SMB_COM_NEW_FILE_SIZE = 0x30 |
| SMB_COM_CLOSE_AND_TREE_DISC = 0x31 |
| SMB_COM_TRANSACTION2 = 0x32 |
| SMB_COM_TRANSACTION2_SECONDARY = 0x33 |
| SMB_COM_FIND_CLOSE2 = 0x34 |
| SMB_COM_FIND_NOTIFY_CLOSE = 0x35 |
| # Used by Xenix/Unix 0x60 - 0x6E |
| SMB_COM_TREE_CONNECT = 0x70 |
| SMB_COM_TREE_DISCONNECT = 0x71 |
| SMB_COM_NEGOTIATE = 0x72 |
| SMB_COM_SESSION_SETUP_ANDX = 0x73 |
| SMB_COM_LOGOFF_ANDX = 0x74 |
| SMB_COM_TREE_CONNECT_ANDX = 0x75 |
| SMB_COM_QUERY_INFORMATION_DISK = 0x80 |
| SMB_COM_SEARCH = 0x81 |
| SMB_COM_FIND = 0x82 |
| SMB_COM_FIND_UNIQUE = 0x83 |
| SMB_COM_FIND_CLOSE = 0x84 |
| SMB_COM_NT_TRANSACT = 0xA0 |
| SMB_COM_NT_TRANSACT_SECONDARY = 0xA1 |
| SMB_COM_NT_CREATE_ANDX = 0xA2 |
| SMB_COM_NT_CANCEL = 0xA4 |
| SMB_COM_NT_RENAME = 0xA5 |
| SMB_COM_OPEN_PRINT_FILE = 0xC0 |
| SMB_COM_WRITE_PRINT_FILE = 0xC1 |
| SMB_COM_CLOSE_PRINT_FILE = 0xC2 |
| SMB_COM_GET_PRINT_QUEUE = 0xC3 |
| SMB_COM_READ_BULK = 0xD8 |
| SMB_COM_WRITE_BULK = 0xD9 |
| SMB_COM_WRITE_BULK_DATA = 0xDA |
| |
| # TRANSACT codes |
| TRANS_TRANSACT_NMPIPE = 0x26 |
| |
| # TRANSACT2 codes |
| TRANS2_FIND_FIRST2 = 0x0001 |
| TRANS2_FIND_NEXT2 = 0x0002 |
| TRANS2_QUERY_FS_INFORMATION = 0x0003 |
| TRANS2_QUERY_PATH_INFORMATION = 0x0005 |
| TRANS2_QUERY_FILE_INFORMATION = 0x0007 |
| TRANS2_SET_FILE_INFORMATION = 0x0008 |
| TRANS2_SET_PATH_INFORMATION = 0x0006 |
| |
| # Security Share Mode (Used internally by SMB class) |
| SECURITY_SHARE_MASK = 0x01 |
| SECURITY_SHARE_SHARE = 0x00 |
| SECURITY_SHARE_USER = 0x01 |
| SECURITY_SIGNATURES_ENABLED = 0X04 |
| SECURITY_SIGNATURES_REQUIRED = 0X08 |
| |
| # Security Auth Mode (Used internally by SMB class) |
| SECURITY_AUTH_MASK = 0x02 |
| SECURITY_AUTH_ENCRYPTED = 0x02 |
| SECURITY_AUTH_PLAINTEXT = 0x00 |
| |
| # Raw Mode Mask (Used internally by SMB class. Good for dialect up to and including LANMAN2.1) |
| RAW_READ_MASK = 0x01 |
| RAW_WRITE_MASK = 0x02 |
| |
| # Capabilities Mask (Used internally by SMB class. Good for dialect NT LM 0.12) |
| CAP_RAW_MODE = 0x00000001 |
| CAP_MPX_MODE = 0x0002 |
| CAP_UNICODE = 0x0004 |
| CAP_LARGE_FILES = 0x0008 |
| CAP_EXTENDED_SECURITY = 0x80000000 |
| CAP_USE_NT_ERRORS = 0x40 |
| CAP_NT_SMBS = 0x10 |
| CAP_LARGE_READX = 0x00004000 |
| CAP_LARGE_WRITEX = 0x00008000 |
| CAP_RPC_REMOTE_APIS = 0x20 |
| |
| # Flags1 Mask |
| FLAGS1_LOCK_AND_READ_OK = 0x01 |
| FLAGS1_PATHCASELESS = 0x08 |
| FLAGS1_CANONICALIZED_PATHS = 0x10 |
| FLAGS1_REPLY = 0x80 |
| |
| # Flags2 Mask |
| FLAGS2_LONG_NAMES = 0x0001 |
| FLAGS2_EAS = 0x0002 |
| FLAGS2_SMB_SECURITY_SIGNATURE = 0x0004 |
| FLAGS2_IS_LONG_NAME = 0x0040 |
| FLAGS2_DFS = 0x1000 |
| FLAGS2_PAGING_IO = 0x2000 |
| FLAGS2_NT_STATUS = 0x4000 |
| FLAGS2_UNICODE = 0x8000 |
| FLAGS2_COMPRESSED = 0x0008 |
| FLAGS2_SMB_SECURITY_SIGNATURE_REQUIRED = 0x0010 |
| FLAGS2_EXTENDED_SECURITY = 0x0800 |
| |
| # Dialect's Security Mode flags |
| NEGOTIATE_USER_SECURITY = 0x01 |
| NEGOTIATE_ENCRYPT_PASSWORDS = 0x02 |
| NEGOTIATE_SECURITY_SIGNATURE_ENABLE = 0x04 |
| NEGOTIATE_SECURITY_SIGNATURE_REQUIRED = 0x08 |
| |
| # Tree Connect AndX Response optionalSuppor flags |
| SMB_SUPPORT_SEARCH_BITS = 0x01 |
| SMB_SHARE_IS_IN_DFS = 0x02 |
| |
| def __init__(self, remote_name, remote_host, my_name = None, host_type = nmb.TYPE_SERVER, sess_port = 445, timeout=None, UDP = 0, session = None, negPacket = None): |
| # The uid attribute will be set when the client calls the login() method |
| self._uid = 0 |
| self.__server_name = '' |
| self.__server_os = '' |
| self.__server_os_major = None |
| self.__server_os_minor = None |
| self.__server_os_build = None |
| self.__server_lanman = '' |
| self.__server_domain = '' |
| self.__server_dns_domain_name = '' |
| self.__remote_name = string.upper(remote_name) |
| self.__remote_host = remote_host |
| self.__isNTLMv2 = True |
| self._dialects_parameters = None |
| self._dialects_data = None |
| # Credentials |
| self.__userName = '' |
| self.__password = '' |
| self.__domain = '' |
| self.__lmhash = '' |
| self.__nthash = '' |
| self.__aesKey = '' |
| self.__kdc = '' |
| self.__TGT = None |
| self.__TGS = None |
| |
| # Negotiate Protocol Result, used everywhere |
| # Could be extended or not, flags should be checked before |
| self._dialect_data = 0 |
| self._dialect_parameters = 0 |
| self._action = 0 |
| self._sess = None |
| self.encrypt_passwords = True |
| self.tid = 0 |
| self.fid = 0 |
| |
| # Signing stuff |
| self._SignSequenceNumber = 0 |
| self._SigningSessionKey = '' |
| self._SigningChallengeResponse = '' |
| self._SignatureEnabled = False |
| self._SignatureVerificationEnabled = False |
| self._SignatureRequired = False |
| |
| # Base flags (default flags, can be overriden using set_flags()) |
| self.__flags1 = SMB.FLAGS1_PATHCASELESS | SMB.FLAGS1_CANONICALIZED_PATHS |
| self.__flags2 = SMB.FLAGS2_EXTENDED_SECURITY | SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES |
| |
| if timeout is None: |
| self.__timeout = 60 |
| else: |
| self.__timeout = timeout |
| |
| # If port 445 and the name sent is *SMBSERVER we're setting the name to the IP. |
| # This is to help some old applications still believing |
| # *SMSBSERVER will work against modern OSes. If port is NETBIOS_SESSION_PORT the user better |
| # know about *SMBSERVER's limitations |
| if sess_port == 445 and remote_name == '*SMBSERVER': |
| self.__remote_name = remote_host |
| |
| if session is None: |
| if not my_name: |
| my_name = socket.gethostname() |
| i = string.find(my_name, '.') |
| if i > -1: |
| my_name = my_name[:i] |
| |
| if UDP: |
| self._sess = nmb.NetBIOSUDPSession(my_name, remote_name, remote_host, host_type, sess_port, self.__timeout) |
| else: |
| self._sess = nmb.NetBIOSTCPSession(my_name, remote_name, remote_host, host_type, sess_port, self.__timeout) |
| |
| # Initialize session values (_dialect_data and _dialect_parameters) |
| self.neg_session() |
| |
| # Call login() without any authentication information to |
| # setup a session if the remote server |
| # is in share mode. |
| if (self._dialects_parameters['SecurityMode'] & SMB.SECURITY_SHARE_MASK) == SMB.SECURITY_SHARE_SHARE: |
| self.login('', '') |
| else: |
| self._sess = session |
| self.neg_session(negPacket = negPacket) |
| # Call login() without any authentication information to |
| # setup a session if the remote server |
| # is in share mode. |
| if (self._dialects_parameters['SecurityMode'] & SMB.SECURITY_SHARE_MASK) == SMB.SECURITY_SHARE_SHARE: |
| self.login('', '') |
| |
| @staticmethod |
| def ntlm_supported(): |
| return False |
| |
| def get_remote_name(self): |
| return self.__remote_name |
| |
| def get_remote_host(self): |
| return self.__remote_host |
| |
| def get_flags(self): |
| return self.__flags1, self.__flags2 |
| |
| def set_flags(self, flags1=None, flags2=None): |
| if flags1 is not None: |
| self.__flags1 = flags1 |
| if flags2 is not None: |
| self.__flags2 = flags2 |
| |
| def set_timeout(self, timeout): |
| prev_timeout = self.__timeout |
| self.__timeout = timeout |
| return prev_timeout |
| |
| def get_timeout(self): |
| return self.__timeout |
| |
| @contextmanager |
| def use_timeout(self, timeout): |
| prev_timeout = self.set_timeout(timeout) |
| try: |
| yield |
| finally: |
| self.set_timeout(prev_timeout) |
| |
| def get_session(self): |
| return self._sess |
| |
| def get_tid(self): |
| return self.tid |
| |
| def get_fid(self): |
| return self.fid |
| |
| def isGuestSession(self): |
| return self._action & SMB_SETUP_GUEST |
| |
| def doesSupportNTLMv2(self): |
| return self.__isNTLMv2 |
| |
| def __del__(self): |
| if self._sess: |
| self._sess.close() |
| |
| def recvSMB(self): |
| r = self._sess.recv_packet(self.__timeout) |
| return NewSMBPacket(data = r.get_trailer()) |
| |
| @staticmethod |
| def __decode_trans(params, data): |
| totparamcnt, totdatacnt, _, paramcnt, paramoffset, paramds, datacnt, dataoffset, datads, setupcnt = unpack('<HHHHHHHHHB', params[:19]) |
| if paramcnt + paramds < totparamcnt or datacnt + datads < totdatacnt: |
| has_more = 1 |
| else: |
| has_more = 0 |
| paramoffset = paramoffset - 55 - setupcnt * 2 |
| dataoffset = dataoffset - 55 - setupcnt * 2 |
| return has_more, params[20:20 + setupcnt * 2], data[paramoffset:paramoffset + paramcnt], data[dataoffset:dataoffset + datacnt] |
| |
| # TODO: Move this to NewSMBPacket, it belongs there |
| def signSMB(self, packet, signingSessionKey, signingChallengeResponse): |
| # This logic MUST be applied for messages sent in response to any of the higher-layer actions and in |
| # compliance with the message sequencing rules. |
| # * The client or server that sends the message MUST provide the 32-bit sequence number for this |
| # message, as specified in sections 3.2.4.1 and 3.3.4.1. |
| # * The SMB_FLAGS2_SMB_SECURITY_SIGNATURE flag in the header MUST be set. |
| # * To generate the signature, a 32-bit sequence number is copied into the |
| # least significant 32 bits of the SecuritySignature field and the remaining |
| # 4 bytes are set to 0x00. |
| # * The MD5 algorithm, as specified in [RFC1321], MUST be used to generate a hash of the SMB |
| # message from the start of the SMB Header, which is defined as follows. |
| # CALL MD5Init( md5context ) |
| # CALL MD5Update( md5context, Connection.SigningSessionKey ) |
| # CALL MD5Update( md5context, Connection.SigningChallengeResponse ) |
| # CALL MD5Update( md5context, SMB message ) |
| # CALL MD5Final( digest, md5context ) |
| # SET signature TO the first 8 bytes of the digest |
| # The resulting 8-byte signature MUST be copied into the SecuritySignature field of the SMB Header, |
| # after which the message can be transmitted. |
| |
| #print "seq(%d) signingSessionKey %r, signingChallengeResponse %r" % (self._SignSequenceNumber, signingSessionKey, signingChallengeResponse) |
| packet['SecurityFeatures'] = pack('<q',self._SignSequenceNumber) |
| # Sign with the sequence |
| m = hashlib.md5() |
| m.update( signingSessionKey ) |
| m.update( signingChallengeResponse ) |
| m.update( str(packet) ) |
| # Replace sequence with acual hash |
| packet['SecurityFeatures'] = m.digest()[:8] |
| if self._SignatureVerificationEnabled: |
| self._SignSequenceNumber +=1 |
| else: |
| self._SignSequenceNumber +=2 |
| |
| def checkSignSMB(self, packet, signingSessionKey, signingChallengeResponse): |
| # Let's check |
| signature = packet['SecurityFeatures'] |
| #print "Signature received: %r " % signature |
| self.signSMB(packet, signingSessionKey, signingChallengeResponse) |
| #print "Signature calculated: %r" % packet['SecurityFeatures'] |
| if self._SignatureVerificationEnabled is not True: |
| self._SignSequenceNumber -= 1 |
| return packet['SecurityFeatures'] == signature |
| |
| def sendSMB(self,smb): |
| smb['Uid'] = self._uid |
| #At least on AIX, PIDs can exceed 16 bits, so we mask them out |
| smb['Pid'] = (os.getpid() & 0xFFFF) |
| # set flags |
| smb['Flags1'] |= self.__flags1 |
| smb['Flags2'] |= self.__flags2 |
| if self._SignatureEnabled: |
| smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE |
| self.signSMB(smb, self._SigningSessionKey, self._SigningChallengeResponse) |
| |
| self._sess.send_packet(str(smb)) |
| |
| @staticmethod |
| def isValidAnswer(s, cmd): |
| while 1: |
| if s.rawData(): |
| if s.get_command() == cmd: |
| if s.get_error_class() == 0x00 and s.get_error_code() == 0x00: |
| return 1 |
| else: |
| raise SessionError, ( "SMB Library Error", s.get_error_class()+ (s.get_reserved() << 8), s.get_error_code() , s.get_flags2() & SMB.FLAGS2_NT_STATUS ) |
| else: |
| break |
| return 0 |
| |
| def neg_session(self, extended_security = True, negPacket = None): |
| def parsePacket(smb): |
| if smb.isValidAnswer(SMB.SMB_COM_NEGOTIATE): |
| sessionResponse = SMBCommand(smb['Data'][0]) |
| self._dialects_parameters = SMBNTLMDialect_Parameters(sessionResponse['Parameters']) |
| self._dialects_data = SMBNTLMDialect_Data() |
| self._dialects_data['ChallengeLength'] = self._dialects_parameters['ChallengeLength'] |
| self._dialects_data.fromString(sessionResponse['Data']) |
| if self._dialects_parameters['Capabilities'] & SMB.CAP_EXTENDED_SECURITY: |
| # Whether we choose it or it is enforced by the server, we go for extended security |
| self._dialects_parameters = SMBExtended_Security_Parameters(sessionResponse['Parameters']) |
| self._dialects_data = SMBExtended_Security_Data(sessionResponse['Data']) |
| # Let's setup some variable for later use |
| if self._dialects_parameters['SecurityMode'] & SMB.SECURITY_SIGNATURES_REQUIRED: |
| self._SignatureRequired = True |
| |
| # Interestingly, the security Blob might be missing sometimes. |
| #spnego = SPNEGO_NegTokenInit(self._dialects_data['SecurityBlob']) |
| #for i in spnego['MechTypes']: |
| # print "Mech Found: %s" % MechTypes[i] |
| return 1 |
| |
| # If not, let's try the old way |
| else: |
| if self._dialects_data['ServerName'] is not None: |
| self.__server_name = self._dialects_data['ServerName'] |
| |
| if self._dialects_parameters['DialectIndex'] == 0xffff: |
| raise UnsupportedFeature,"Remote server does not know NT LM 0.12" |
| return 1 |
| else: |
| return 0 |
| |
| if negPacket is None: |
| smb = NewSMBPacket() |
| negSession = SMBCommand(SMB.SMB_COM_NEGOTIATE) |
| flags2 = self.get_flags()[1] |
| if extended_security is True: |
| self.set_flags(flags2=flags2|SMB.FLAGS2_EXTENDED_SECURITY) |
| else: |
| self.set_flags(flags2=flags2 & (~SMB.FLAGS2_EXTENDED_SECURITY)) |
| |
| negSession['Data'] = '\x02NT LM 0.12\x00' |
| smb.addCommand(negSession) |
| self.sendSMB(smb) |
| |
| while 1: |
| smb = self.recvSMB() |
| return parsePacket(smb) |
| else: |
| |
| return parsePacket( NewSMBPacket( data = negPacket)) |
| |
| def tree_connect(self, path, password = '', service = SERVICE_ANY): |
| LOG.warning("[MS-CIFS] This is an original Core Protocol command.This command has been deprecated.Client Implementations SHOULD use SMB_COM_TREE_CONNECT_ANDX") |
| |
| # return 0x800 |
| if password: |
| # Password is only encrypted if the server passed us an "encryption" during protocol dialect |
| if self._dialects_parameters['ChallengeLength'] > 0: |
| # this code is untested |
| password = self.get_ntlmv1_response(ntlm.compute_lmhash(password)) |
| |
| if not unicode_support: |
| if unicode_convert: |
| path = str(path) |
| else: |
| raise Exception('SMB: Can\t conver path from unicode!') |
| |
| smb = NewSMBPacket() |
| treeConnect = SMBCommand(SMB.SMB_COM_TREE_CONNECT) |
| treeConnect['Parameters'] = SMBTreeConnect_Parameters() |
| treeConnect['Data'] = SMBTreeConnect_Data() |
| treeConnect['Data']['Path'] = path.upper() |
| treeConnect['Data']['Password'] = password |
| treeConnect['Data']['Service'] = service |
| smb.addCommand(treeConnect) |
| self.sendSMB(smb) |
| |
| while 1: |
| smb = self.recvSMB() |
| if smb.isValidAnswer(SMB.SMB_COM_TREE_CONNECT): |
| # XXX Here we are ignoring the rest of the response |
| return smb['Tid'] |
| return smb['Tid'] |
| |
| def get_uid(self): |
| return self._uid |
| |
| def set_uid(self, uid): |
| self._uid = uid |
| |
| def tree_connect_andx(self, path, password = None, service = SERVICE_ANY, smb_packet=None): |
| if password: |
| # Password is only encrypted if the server passed us an "encryption" during protocol dialect |
| if self._dialects_parameters['ChallengeLength'] > 0: |
| # this code is untested |
| password = self.get_ntlmv1_response(ntlm.compute_lmhash(password)) |
| else: |
| password = '\x00' |
| |
| if not unicode_support: |
| if unicode_convert: |
| path = str(path) |
| else: |
| raise Exception('SMB: Can\t convert path from unicode!') |
| |
| if smb_packet is None: |
| smb = NewSMBPacket() |
| else: |
| smb = smb_packet |
| |
| # Just in case this came with the full path ,let's just leave |
| # the sharename, we'll take care of the rest |
| |
| share = path.split('\\')[-1] |
| try: |
| _, _, _, _, sockaddr = socket.getaddrinfo(self.get_remote_host(), 80, 0, 0, socket.IPPROTO_TCP)[0] |
| remote_host = sockaddr[0] |
| except Exception: |
| remote_host = self.get_remote_host() |
| |
| path = '\\\\' + remote_host + '\\' +share |
| path = path.upper().encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else path |
| |
| treeConnect = SMBCommand(SMB.SMB_COM_TREE_CONNECT_ANDX) |
| treeConnect['Parameters'] = SMBTreeConnectAndX_Parameters() |
| treeConnect['Data'] = SMBTreeConnectAndX_Data(flags=self.__flags2) |
| treeConnect['Parameters']['PasswordLength'] = len(password) |
| treeConnect['Data']['Password'] = password |
| treeConnect['Data']['Path'] = path |
| treeConnect['Data']['Service'] = service |
| |
| if self.__flags2 & SMB.FLAGS2_UNICODE: |
| treeConnect['Data']['Pad'] = 0x0 |
| |
| smb.addCommand(treeConnect) |
| |
| # filename = "\PIPE\epmapper" |
| |
| # ntCreate = SMBCommand(SMB.SMB_COM_NT_CREATE_ANDX) |
| # ntCreate['Parameters'] = SMBNtCreateAndX_Parameters() |
| # ntCreate['Data'] = SMBNtCreateAndX_Data() |
| # ntCreate['Parameters']['FileNameLength'] = len(filename) |
| # ntCreate['Parameters']['CreateFlags'] = 0 |
| # ntCreate['Parameters']['AccessMask'] = 0x3 |
| # ntCreate['Parameters']['CreateOptions'] = 0x0 |
| # ntCreate['Data']['FileName'] = filename |
| |
| # smb.addCommand(ntCreate) |
| self.sendSMB(smb) |
| |
| while 1: |
| smb = self.recvSMB() |
| if smb.isValidAnswer(SMB.SMB_COM_TREE_CONNECT_ANDX): |
| # XXX Here we are ignoring the rest of the response |
| self.tid = smb['Tid'] |
| return self.tid |
| self.tid = smb['Tid'] |
| return self.tid |
| |
| # backwars compatibility |
| connect_tree = tree_connect_andx |
| |
| @staticmethod |
| def getDialect(): |
| return SMB_DIALECT |
| |
| def get_server_name(self): |
| #return self._dialects_data['ServerName'] |
| return self.__server_name |
| |
| def get_session_key(self): |
| return self._SigningSessionKey |
| |
| def set_session_key(self, key): |
| self._SigningSessionKey = key |
| |
| def get_encryption_key(self): |
| if self._dialects_data.fields.has_key('Challenge'): |
| return self._dialects_data['Challenge'] |
| else: |
| return None |
| |
| def get_server_time(self): |
| timestamp = self._dialects_parameters['HighDateTime'] |
| timestamp <<= 32 |
| timestamp |= self._dialects_parameters['LowDateTime'] |
| timestamp -= 116444736000000000 |
| timestamp /= 10000000 |
| d = datetime.datetime.utcfromtimestamp(timestamp) |
| return d.strftime("%a, %d %b %Y %H:%M:%S GMT") |
| |
| def disconnect_tree(self, tid): |
| smb = NewSMBPacket() |
| smb['Tid'] = tid |
| |
| smb.addCommand(SMBCommand(SMB.SMB_COM_TREE_DISCONNECT)) |
| |
| self.sendSMB(smb) |
| self.recvSMB() |
| |
| def open(self, tid, filename, open_mode, desired_access): |
| filename = string.replace(filename,'/', '\\') |
| filename = filename.encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else filename |
| |
| smb = NewSMBPacket() |
| smb['Tid'] = tid |
| |
| openFile = SMBCommand(SMB.SMB_COM_OPEN) |
| openFile['Parameters'] = SMBOpen_Parameters() |
| openFile['Parameters']['DesiredAccess'] = desired_access |
| openFile['Parameters']['OpenMode'] = open_mode |
| openFile['Parameters']['SearchAttributes'] = ATTR_READONLY | ATTR_HIDDEN | ATTR_ARCHIVE |
| openFile['Data'] = SMBOpen_Data(flags=self.__flags2) |
| openFile['Data']['FileName'] = filename |
| |
| if self.__flags2 & SMB.FLAGS2_UNICODE: |
| openFile['Data']['Pad'] = 0x0 |
| |
| smb.addCommand(openFile) |
| |
| self.sendSMB(smb) |
| |
| smb = self.recvSMB() |
| if smb.isValidAnswer(SMB.SMB_COM_OPEN): |
| # XXX Here we are ignoring the rest of the response |
| openFileResponse = SMBCommand(smb['Data'][0]) |
| openFileParameters = SMBOpenResponse_Parameters(openFileResponse['Parameters']) |
| |
| return ( |
| openFileParameters['Fid'], |
| openFileParameters['FileAttributes'], |
| openFileParameters['LastWriten'], |
| openFileParameters['FileSize'], |
| openFileParameters['GrantedAccess'], |
| ) |
| |
| def open_andx(self, tid, filename, open_mode, desired_access): |
| filename = string.replace(filename,'/', '\\') |
| filename = filename.encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else filename |
| |
| smb = NewSMBPacket() |
| smb['Tid'] = tid |
| |
| openFile = SMBCommand(SMB.SMB_COM_OPEN_ANDX) |
| openFile['Parameters'] = SMBOpenAndX_Parameters() |
| openFile['Parameters']['DesiredAccess'] = desired_access |
| openFile['Parameters']['OpenMode'] = open_mode |
| openFile['Parameters']['SearchAttributes'] = ATTR_READONLY | ATTR_HIDDEN | ATTR_ARCHIVE |
| openFile['Data'] = SMBOpenAndX_Data(flags=self.__flags2) |
| openFile['Data']['FileName'] = filename |
| |
| if self.__flags2 & SMB.FLAGS2_UNICODE: |
| openFile['Data']['Pad'] = 0x0 |
| |
| smb.addCommand(openFile) |
| |
| self.sendSMB(smb) |
| |
| smb = self.recvSMB() |
| if smb.isValidAnswer(SMB.SMB_COM_OPEN_ANDX): |
| # XXX Here we are ignoring the rest of the response |
| openFileResponse = SMBCommand(smb['Data'][0]) |
| openFileParameters = SMBOpenAndXResponse_Parameters(openFileResponse['Parameters']) |
| |
| return ( |
| openFileParameters['Fid'], |
| openFileParameters['FileAttributes'], |
| openFileParameters['LastWriten'], |
| openFileParameters['FileSize'], |
| openFileParameters['GrantedAccess'], |
| openFileParameters['FileType'], |
| openFileParameters['IPCState'], |
| openFileParameters['Action'], |
| openFileParameters['ServerFid'], |
| ) |
| |
| def close(self, tid, fid): |
| smb = NewSMBPacket() |
| smb['Tid'] = tid |
| |
| closeFile = SMBCommand(SMB.SMB_COM_CLOSE) |
| closeFile['Parameters'] = SMBClose_Parameters() |
| closeFile['Parameters']['FID'] = fid |
| smb.addCommand(closeFile) |
| |
| self.sendSMB(smb) |
| smb = self.recvSMB() |
| if smb.isValidAnswer(SMB.SMB_COM_CLOSE): |
| return 1 |
| return 0 |
| |
| def send_trans(self, tid, setup, name, param, data, noAnswer = 0): |
| smb = NewSMBPacket() |
| smb['Tid'] = tid |
| |
| transCommand = SMBCommand(SMB.SMB_COM_TRANSACTION) |
| transCommand['Parameters'] = SMBTransaction_Parameters() |
| transCommand['Data'] = SMBTransaction_Data() |
| |
| transCommand['Parameters']['Setup'] = setup |
| transCommand['Parameters']['TotalParameterCount'] = len(param) |
| transCommand['Parameters']['TotalDataCount'] = len(data) |
| |
| transCommand['Parameters']['ParameterCount'] = len(param) |
| transCommand['Parameters']['ParameterOffset'] = 32+3+28+len(setup)+len(name) |
| |
| transCommand['Parameters']['DataCount'] = len(data) |
| transCommand['Parameters']['DataOffset'] = transCommand['Parameters']['ParameterOffset'] + len(param) |
| |
| transCommand['Data']['Name'] = name |
| transCommand['Data']['Trans_Parameters'] = param |
| transCommand['Data']['Trans_Data'] = data |
| |
| if noAnswer: |
| transCommand['Parameters']['Flags'] = TRANS_NO_RESPONSE |
| |
| smb.addCommand(transCommand) |
| |
| self.sendSMB(smb) |
| |
| def send_trans2(self, tid, setup, name, param, data): |
| smb = NewSMBPacket() |
| smb['Tid'] = tid |
| |
| command = pack('<H', setup) |
| |
| transCommand = SMBCommand(SMB.SMB_COM_TRANSACTION2) |
| transCommand['Parameters'] = SMBTransaction2_Parameters() |
| transCommand['Parameters']['MaxDataCount'] = self._dialects_parameters['MaxBufferSize'] |
| transCommand['Data'] = SMBTransaction2_Data() |
| |
| transCommand['Parameters']['Setup'] = command |
| transCommand['Parameters']['TotalParameterCount'] = len(param) |
| transCommand['Parameters']['TotalDataCount'] = len(data) |
| |
| if len(param) > 0: |
| padLen = (4 - (32+2+28 + len(command)) % 4 ) % 4 |
| padBytes = '\xFF' * padLen |
| transCommand['Data']['Pad1'] = padBytes |
| else: |
| transCommand['Data']['Pad1'] = '' |
| padLen = 0 |
| |
| transCommand['Parameters']['ParameterCount'] = len(param) |
| transCommand['Parameters']['ParameterOffset'] = 32+2+28+len(command)+len(name) + padLen |
| |
| if len(data) > 0: |
| pad2Len = (4 - (32+2+28 + len(command) + padLen + len(param)) % 4) % 4 |
| transCommand['Data']['Pad2'] = '\xFF' * pad2Len |
| else: |
| transCommand['Data']['Pad2'] = '' |
| pad2Len = 0 |
| |
| transCommand['Parameters']['DataCount'] = len(data) |
| transCommand['Parameters']['DataOffset'] = transCommand['Parameters']['ParameterOffset'] + len(param) + pad2Len |
| |
| transCommand['Data']['Name'] = name |
| transCommand['Data']['Trans_Parameters'] = param |
| transCommand['Data']['Trans_Data'] = data |
| smb.addCommand(transCommand) |
| |
| self.sendSMB(smb) |
| |
| def query_file_info(self, tid, fid, fileInfoClass = SMB_QUERY_FILE_STANDARD_INFO): |
| self.send_trans2(tid, SMB.TRANS2_QUERY_FILE_INFORMATION, '\x00', pack('<HH', fid, fileInfoClass), '') |
| |
| resp = self.recvSMB() |
| if resp.isValidAnswer(SMB.SMB_COM_TRANSACTION2): |
| trans2Response = SMBCommand(resp['Data'][0]) |
| trans2Parameters = SMBTransaction2Response_Parameters(trans2Response['Parameters']) |
| # Remove Potential Prefix Padding |
| return trans2Response['Data'][-trans2Parameters['TotalDataCount']:] |
| |
| def __nonraw_retr_file(self, tid, fid, offset, datasize, callback): |
| if (self._dialects_parameters['Capabilities'] & SMB.CAP_LARGE_READX) and self._SignatureEnabled is False: |
| max_buf_size = 65000 |
| else: |
| max_buf_size = self._dialects_parameters['MaxBufferSize'] & ~0x3ff # Read in multiple KB blocks |
| |
| read_offset = offset |
| while read_offset < datasize: |
| data = self.read_andx(tid, fid, read_offset, max_buf_size) |
| |
| callback(data) |
| read_offset += len(data) |
| |
| def __nonraw_stor_file(self, tid, fid, offset, datasize, callback): |
| if (self._dialects_parameters['Capabilities'] & SMB.CAP_LARGE_WRITEX) and self._SignatureEnabled is False: |
| max_buf_size = 65000 |
| else: |
| max_buf_size = self._dialects_parameters['MaxBufferSize'] & ~0x3ff # Write in multiple KB blocks |
| |
| write_offset = offset |
| while 1: |
| data = callback(max_buf_size) |
| if not data: |
| break |
| |
| smb = self.write_andx(tid,fid,data, write_offset) |
| writeResponse = SMBCommand(smb['Data'][0]) |
| writeResponseParameters = SMBWriteAndXResponse_Parameters(writeResponse['Parameters']) |
| write_offset += writeResponseParameters['Count'] |
| |
| def get_server_domain(self): |
| return self.__server_domain |
| |
| def get_server_dns_domain_name(self): |
| return self.__server_dns_domain_name |
| |
| def get_server_os(self): |
| return self.__server_os |
| |
| def get_server_os_major(self): |
| return self.__server_os_major |
| |
| def get_server_os_minor(self): |
| return self.__server_os_minor |
| |
| def get_server_os_build(self): |
| return self.__server_os_build |
| |
| def set_server_os(self, os): |
| self.__server_os = os |
| |
| def get_server_lanman(self): |
| return self.__server_lanman |
| |
| def is_login_required(self): |
| # Login is required if share mode is user. |
| # Otherwise only public services or services in share mode |
| # are allowed. |
| return (self._dialects_parameters['SecurityMode'] & SMB.SECURITY_SHARE_MASK) == SMB.SECURITY_SHARE_USER |
| |
| def is_signing_required(self): |
| return self._SignatureRequired |
| |
| def get_ntlmv1_response(self, key): |
| challenge = self._dialects_data['Challenge'] |
| return ntlm.get_ntlmv1_response(key, challenge) |
| |
| def kerberos_login(self, user, password, domain = '', lmhash = '', nthash = '', aesKey = '', kdcHost = '', TGT=None, TGS=None): |
| # Importing down here so pyasn1 is not required if kerberos is not used. |
| from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set |
| from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS |
| from impacket.krb5 import constants |
| from impacket.krb5.types import Principal, KerberosTime, Ticket |
| from pyasn1.codec.der import decoder, encoder |
| import datetime |
| |
| # login feature does not support unicode |
| # disable it if enabled |
| flags2 = self.__flags2 |
| if flags2 & SMB.FLAGS2_UNICODE: |
| self.__flags2 = flags2 & (flags2 ^ SMB.FLAGS2_UNICODE) |
| |
| # If TGT or TGS are specified, they are in the form of: |
| # TGS['KDC_REP'] = the response from the server |
| # TGS['cipher'] = the cipher used |
| # TGS['sessionKey'] = the sessionKey |
| # If we have hashes, normalize them |
| if lmhash != '' or nthash != '': |
| if len(lmhash) % 2: lmhash = '0%s' % lmhash |
| if len(nthash) % 2: nthash = '0%s' % nthash |
| try: # just in case they were converted already |
| lmhash = a2b_hex(lmhash) |
| nthash = a2b_hex(nthash) |
| except: |
| pass |
| |
| self.__userName = user |
| self.__password = password |
| self.__domain = domain |
| self.__lmhash = lmhash |
| self.__nthash = nthash |
| self.__aesKey = aesKey |
| self.__kdc = kdcHost |
| self.__TGT = TGT |
| self.__TGS = TGS |
| |
| # First of all, we need to get a TGT for the user |
| userName = Principal(user, type=constants.PrincipalNameType.NT_PRINCIPAL.value) |
| if TGT is None: |
| if TGS is None: |
| tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash, aesKey, kdcHost) |
| else: |
| tgt = TGT['KDC_REP'] |
| cipher = TGT['cipher'] |
| sessionKey = TGT['sessionKey'] |
| |
| # Now that we have the TGT, we should ask for a TGS for cifs |
| |
| if TGS is None: |
| serverName = Principal('cifs/%s' % self.__remote_name, type=constants.PrincipalNameType.NT_SRV_INST.value) |
| tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey) |
| else: |
| tgs = TGS['KDC_REP'] |
| cipher = TGS['cipher'] |
| sessionKey = TGS['sessionKey'] |
| |
| smb = NewSMBPacket() |
| |
| # Are we required to sign SMB? If so we do it, if not we skip it |
| if self._SignatureRequired: |
| smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE |
| |
| |
| sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX) |
| sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters() |
| sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data() |
| |
| sessionSetup['Parameters']['MaxBufferSize'] = 61440 |
| sessionSetup['Parameters']['MaxMpxCount'] = 2 |
| sessionSetup['Parameters']['VcNumber'] = 1 |
| sessionSetup['Parameters']['SessionKey'] = 0 |
| sessionSetup['Parameters']['Capabilities'] = SMB.CAP_EXTENDED_SECURITY | SMB.CAP_USE_NT_ERRORS | SMB.CAP_UNICODE | SMB.CAP_LARGE_READX | SMB.CAP_LARGE_WRITEX |
| |
| |
| # Let's build a NegTokenInit with the NTLMSSP |
| # TODO: In the future we should be able to choose different providers |
| |
| blob = SPNEGO_NegTokenInit() |
| |
| # Kerberos v5 mech |
| blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']] |
| |
| # Let's extract the ticket from the TGS |
| tgs = decoder.decode(tgs, asn1Spec = TGS_REP())[0] |
| ticket = Ticket() |
| ticket.from_asn1(tgs['ticket']) |
| |
| # Now let's build the AP_REQ |
| apReq = AP_REQ() |
| apReq['pvno'] = 5 |
| apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) |
| |
| opts = list() |
| apReq['ap-options'] = constants.encodeFlags(opts) |
| seq_set(apReq,'ticket', ticket.to_asn1) |
| |
| authenticator = Authenticator() |
| authenticator['authenticator-vno'] = 5 |
| authenticator['crealm'] = domain |
| seq_set(authenticator, 'cname', userName.components_to_asn1) |
| now = datetime.datetime.utcnow() |
| |
| authenticator['cusec'] = now.microsecond |
| authenticator['ctime'] = KerberosTime.to_asn1(now) |
| |
| encodedAuthenticator = encoder.encode(authenticator) |
| |
| # Key Usage 11 |
| # AP-REQ Authenticator (includes application authenticator |
| # subkey), encrypted with the application session key |
| # (Section 5.5.1) |
| encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11, encodedAuthenticator, None) |
| |
| apReq['authenticator'] = None |
| apReq['authenticator']['etype'] = cipher.enctype |
| apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator |
| |
| blob['MechToken'] = encoder.encode(apReq) |
| |
| sessionSetup['Parameters']['SecurityBlobLength'] = len(blob) |
| sessionSetup['Parameters'].getData() |
| sessionSetup['Data']['SecurityBlob'] = blob.getData() |
| |
| # Fake Data here, don't want to get us fingerprinted |
| sessionSetup['Data']['NativeOS'] = 'Unix' |
| sessionSetup['Data']['NativeLanMan'] = 'Samba' |
| |
| smb.addCommand(sessionSetup) |
| self.sendSMB(smb) |
| |
| smb = self.recvSMB() |
| if smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX): |
| # We will need to use this uid field for all future requests/responses |
| self._uid = smb['Uid'] |
| |
| # Now we have to extract the blob to continue the auth process |
| sessionResponse = SMBCommand(smb['Data'][0]) |
| sessionParameters = SMBSessionSetupAndX_Extended_Response_Parameters(sessionResponse['Parameters']) |
| sessionData = SMBSessionSetupAndX_Extended_Response_Data(flags = smb['Flags2']) |
| sessionData['SecurityBlobLength'] = sessionParameters['SecurityBlobLength'] |
| sessionData.fromString(sessionResponse['Data']) |
| |
| self._action = sessionParameters['Action'] |
| # If smb sign required, let's enable it for the rest of the connection |
| if self._dialects_parameters['SecurityMode'] & SMB.SECURITY_SIGNATURES_REQUIRED: |
| self._SigningSessionKey = sessionKey.contents |
| self._SignSequenceNumber = 2 |
| self._SignatureEnabled = True |
| |
| # restore unicode flag if needed |
| if flags2 & SMB.FLAGS2_UNICODE: |
| self.__flags2 |= SMB.FLAGS2_UNICODE |
| |
| return 1 |
| else: |
| raise Exception('Error: Could not login successfully') |
| |
| def login_extended(self, user, password, domain = '', lmhash = '', nthash = '', use_ntlmv2 = True ): |
| |
| # login feature does not support unicode |
| # disable it if enabled |
| flags2 = self.__flags2 |
| if flags2 & SMB.FLAGS2_UNICODE: |
| self.__flags2 = flags2 & (flags2 ^ SMB.FLAGS2_UNICODE) |
| |
| # Once everything's working we should join login methods into a single one |
| smb = NewSMBPacket() |
| # Are we required to sign SMB? If so we do it, if not we skip it |
| if self._SignatureRequired: |
| smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE |
| |
| sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX) |
| sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters() |
| sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data() |
| |
| sessionSetup['Parameters']['MaxBufferSize'] = 61440 |
| sessionSetup['Parameters']['MaxMpxCount'] = 2 |
| sessionSetup['Parameters']['VcNumber'] = 1 |
| sessionSetup['Parameters']['SessionKey'] = 0 |
| sessionSetup['Parameters']['Capabilities'] = SMB.CAP_EXTENDED_SECURITY | SMB.CAP_USE_NT_ERRORS | SMB.CAP_UNICODE | SMB.CAP_LARGE_READX | SMB.CAP_LARGE_WRITEX |
| |
| |
| # Let's build a NegTokenInit with the NTLMSSP |
| # TODO: In the future we should be able to choose different providers |
| |
| blob = SPNEGO_NegTokenInit() |
| |
| # NTLMSSP |
| blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']] |
| auth = ntlm.getNTLMSSPType1('','',self._SignatureRequired, use_ntlmv2 = use_ntlmv2) |
| blob['MechToken'] = str(auth) |
| |
| sessionSetup['Parameters']['SecurityBlobLength'] = len(blob) |
| sessionSetup['Parameters'].getData() |
| sessionSetup['Data']['SecurityBlob'] = blob.getData() |
| |
| # Fake Data here, don't want to get us fingerprinted |
| sessionSetup['Data']['NativeOS'] = 'Unix' |
| sessionSetup['Data']['NativeLanMan'] = 'Samba' |
| |
| smb.addCommand(sessionSetup) |
| self.sendSMB(smb) |
| |
| smb = self.recvSMB() |
| if smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX): |
| # We will need to use this uid field for all future requests/responses |
| self._uid = smb['Uid'] |
| |
| # Now we have to extract the blob to continue the auth process |
| sessionResponse = SMBCommand(smb['Data'][0]) |
| sessionParameters = SMBSessionSetupAndX_Extended_Response_Parameters(sessionResponse['Parameters']) |
| sessionData = SMBSessionSetupAndX_Extended_Response_Data(flags = smb['Flags2']) |
| sessionData['SecurityBlobLength'] = sessionParameters['SecurityBlobLength'] |
| sessionData.fromString(sessionResponse['Data']) |
| respToken = SPNEGO_NegTokenResp(sessionData['SecurityBlob']) |
| |
| # Let's parse some data and keep it to ourselves in case it is asked |
| ntlmChallenge = ntlm.NTLMAuthChallenge(respToken['ResponseToken']) |
| if ntlmChallenge['TargetInfoFields_len'] > 0: |
| av_pairs = ntlm.AV_PAIRS(ntlmChallenge['TargetInfoFields'][:ntlmChallenge['TargetInfoFields_len']]) |
| if av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] is not None: |
| try: |
| self.__server_name = av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1].decode('utf-16le') |
| except: |
| # For some reason, we couldn't decode Unicode here.. silently discard the operation |
| pass |
| if av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] is not None: |
| try: |
| if self.__server_name != av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le'): |
| self.__server_domain = av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le') |
| except: |
| # For some reason, we couldn't decode Unicode here.. silently discard the operation |
| pass |
| if av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] is not None: |
| try: |
| self.__server_dns_domain_name = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME][1].decode('utf-16le') |
| except: |
| # For some reason, we couldn't decode Unicode here.. silently discard the operation |
| pass |
| |
| # Parse Version to know the target Operating system name. Not provided elsewhere anymore |
| if ntlmChallenge.fields.has_key('Version'): |
| version = ntlmChallenge['Version'] |
| |
| if len(version) >= 4: |
| self.__server_os_major, self.__server_os_minor, self.__server_os_build = unpack('<BBH',version[:4]) |
| |
| type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, respToken['ResponseToken'], user, password, domain, lmhash, nthash, use_ntlmv2 = use_ntlmv2) |
| |
| if exportedSessionKey is not None: |
| self._SigningSessionKey = exportedSessionKey |
| |
| smb = NewSMBPacket() |
| |
| # Are we required to sign SMB? If so we do it, if not we skip it |
| if self._SignatureRequired: |
| smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE |
| |
| respToken2 = SPNEGO_NegTokenResp() |
| respToken2['ResponseToken'] = str(type3) |
| |
| # Reusing the previous structure |
| sessionSetup['Parameters']['SecurityBlobLength'] = len(respToken2) |
| sessionSetup['Data']['SecurityBlob'] = respToken2.getData() |
| |
| # Storing some info for later use |
| self.__server_os = sessionData['NativeOS'] |
| self.__server_lanman = sessionData['NativeLanMan'] |
| |
| smb.addCommand(sessionSetup) |
| self.sendSMB(smb) |
| |
| smb = self.recvSMB() |
| self._uid = 0 |
| if smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX): |
| self._uid = smb['Uid'] |
| sessionResponse = SMBCommand(smb['Data'][0]) |
| sessionParameters = SMBSessionSetupAndXResponse_Parameters(sessionResponse['Parameters']) |
| |
| self._action = sessionParameters['Action'] |
| # If smb sign required, let's enable it for the rest of the connection |
| if self._dialects_parameters['SecurityMode'] & SMB.SECURITY_SIGNATURES_REQUIRED: |
| self._SignSequenceNumber = 2 |
| self._SignatureEnabled = True |
| |
| # restore unicode flag if needed |
| if flags2 & SMB.FLAGS2_UNICODE: |
| self.__flags2 |= SMB.FLAGS2_UNICODE |
| |
| return 1 |
| else: |
| raise Exception('Error: Could not login successfully') |
| |
| def getCredentials(self): |
| return ( |
| self.__userName, |
| self.__password, |
| self.__domain, |
| self.__lmhash, |
| self.__nthash, |
| self.__aesKey, |
| self.__TGT, |
| self.__TGS) |
| |
| def getIOCapabilities(self): |
| res = dict() |
| if (self._dialects_parameters['Capabilities'] & SMB.CAP_LARGE_READX) and self._SignatureEnabled is False: |
| max_size = 65000 |
| else: |
| max_size = self._dialects_parameters['MaxBufferSize'] # Read in multiple KB blocks |
| res['MaxReadSize'] = max_size |
| res['MaxWriteSize'] = max_size |
| return res |
| |
| def login(self, user, password, domain = '', lmhash = '', nthash = '', ntlm_fallback = True): |
| |
| # If we have hashes, normalize them |
| if lmhash != '' or nthash != '': |
| if len(lmhash) % 2: lmhash = '0%s' % lmhash |
| if len(nthash) % 2: nthash = '0%s' % nthash |
| try: # just in case they were converted already |
| lmhash = a2b_hex(lmhash) |
| nthash = a2b_hex(nthash) |
| except: |
| pass |
| |
| self.__userName = user |
| self.__password = password |
| self.__domain = domain |
| self.__lmhash = lmhash |
| self.__nthash = nthash |
| self.__aesKey = '' |
| self.__TGT = None |
| self.__TGS = None |
| |
| if self._dialects_parameters['Capabilities'] & SMB.CAP_EXTENDED_SECURITY: |
| try: |
| self.login_extended(user, password, domain, lmhash, nthash, use_ntlmv2 = True) |
| except: |
| # If the target OS is Windows 5.0 or Samba, let's try using NTLMv1 |
| if ntlm_fallback and ((self.get_server_lanman().find('Windows 2000') != -1) or (self.get_server_lanman().find('Samba') != -1)): |
| self.login_extended(user, password, domain, lmhash, nthash, use_ntlmv2 = False) |
| self.__isNTLMv2 = False |
| else: |
| raise |
| elif ntlm_fallback: |
| self.login_standard(user, password, domain, lmhash, nthash) |
| self.__isNTLMv2 = False |
| else: |
| raise SessionError('Cannot authenticate against target, enable ntlm_fallback') |
| |
| def login_standard(self, user, password, domain = '', lmhash = '', nthash = ''): |
| |
| # login feature does not support unicode |
| # disable it if enabled |
| flags2 = self.__flags2 |
| if flags2 & SMB.FLAGS2_UNICODE: |
| self.__flags2 = flags2 & (flags2 ^ SMB.FLAGS2_UNICODE) |
| |
| # Only supports NTLMv1 |
| # Password is only encrypted if the server passed us an "encryption key" during protocol dialect negotiation |
| if self._dialects_parameters['ChallengeLength'] > 0: |
| if lmhash != '' or nthash != '': |
| pwd_ansi = self.get_ntlmv1_response(lmhash) |
| pwd_unicode = self.get_ntlmv1_response(nthash) |
| elif password: |
| lmhash = ntlm.compute_lmhash(password) |
| nthash = ntlm.compute_nthash(password) |
| pwd_ansi = self.get_ntlmv1_response(lmhash) |
| pwd_unicode = self.get_ntlmv1_response(nthash) |
| else: # NULL SESSION |
| pwd_ansi = '' |
| pwd_unicode = '' |
| else: |
| pwd_ansi = password |
| pwd_unicode = '' |
| |
| smb = NewSMBPacket() |
| |
| sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX) |
| sessionSetup['Parameters'] = SMBSessionSetupAndX_Parameters() |
| sessionSetup['Data'] = SMBSessionSetupAndX_Data() |
| |
| sessionSetup['Parameters']['MaxBuffer'] = 61440 |
| sessionSetup['Parameters']['MaxMpxCount'] = 2 |
| sessionSetup['Parameters']['VCNumber'] = os.getpid() |
| sessionSetup['Parameters']['SessionKey'] = self._dialects_parameters['SessionKey'] |
| sessionSetup['Parameters']['AnsiPwdLength'] = len(pwd_ansi) |
| sessionSetup['Parameters']['UnicodePwdLength'] = len(pwd_unicode) |
| sessionSetup['Parameters']['Capabilities'] = SMB.CAP_RAW_MODE | SMB.CAP_USE_NT_ERRORS | SMB.CAP_LARGE_READX | SMB.CAP_LARGE_WRITEX |
| |
| sessionSetup['Data']['AnsiPwd'] = pwd_ansi |
| sessionSetup['Data']['UnicodePwd'] = pwd_unicode |
| sessionSetup['Data']['Account'] = str(user) |
| sessionSetup['Data']['PrimaryDomain'] = str(domain) |
| sessionSetup['Data']['NativeOS'] = str(os.name) |
| sessionSetup['Data']['NativeLanMan'] = 'pysmb' |
| smb.addCommand(sessionSetup) |
| |
| self.sendSMB(smb) |
| |
| smb = self.recvSMB() |
| if smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX): |
| # We will need to use this uid field for all future requests/responses |
| self._uid = smb['Uid'] |
| sessionResponse = SMBCommand(smb['Data'][0]) |
| sessionParameters = SMBSessionSetupAndXResponse_Parameters(sessionResponse['Parameters']) |
| sessionData = SMBSessionSetupAndXResponse_Data(flags = smb['Flags2'], data = sessionResponse['Data']) |
| |
| self._action = sessionParameters['Action'] |
| |
| # Still gotta figure out how to do this with no EXTENDED_SECURITY |
| if sessionParameters['Action'] & SMB_SETUP_USE_LANMAN_KEY == 0: |
| self._SigningChallengeResponse = sessionSetup['Data']['UnicodePwd'] |
| self._SigningSessionKey = nthash |
| else: |
| self._SigningChallengeResponse = sessionSetup['Data']['AnsiPwd'] |
| self._SigningSessionKey = lmhash |
| |
| #self._SignSequenceNumber = 1 |
| #self.checkSignSMB(smb, self._SigningSessionKey ,self._SigningChallengeResponse) |
| #self._SignatureEnabled = True |
| self.__server_os = sessionData['NativeOS'] |
| self.__server_lanman = sessionData['NativeLanMan'] |
| self.__server_domain = sessionData['PrimaryDomain'] |
| |
| # restore unicode flag if needed |
| if flags2 & SMB.FLAGS2_UNICODE: |
| self.__flags2 |= SMB.FLAGS2_UNICODE |
| |
| return 1 |
| else: raise Exception('Error: Could not login successfully') |
| |
| def waitNamedPipe(self, tid, pipe, timeout = 5, noAnswer = 0): |
| smb = NewSMBPacket() |
| smb['Tid'] = tid |
| |
| transCommand = SMBCommand(SMB.SMB_COM_TRANSACTION) |
| transCommand['Parameters'] = SMBTransaction_Parameters() |
| transCommand['Data'] = SMBTransaction_Data() |
| |
| setup = '\x53\x00\x00\x00' |
| name = '\\PIPE%s\x00' % pipe |
| transCommand['Parameters']['Setup'] = setup |
| transCommand['Parameters']['TotalParameterCount'] = 0 |
| transCommand['Parameters']['TotalDataCount'] = 0 |
| transCommand['Parameters']['MaxParameterCount'] = 0 |
| transCommand['Parameters']['MaxDataCount'] = 0 |
| transCommand['Parameters']['Timeout'] = timeout * 1000 |
| |
| transCommand['Parameters']['ParameterCount'] = 0 |
| transCommand['Parameters']['ParameterOffset'] = 32+3+28+len(setup)+len(name) |
| |
| transCommand['Parameters']['DataCount'] = 0 |
| transCommand['Parameters']['DataOffset'] = 0 |
| |
| transCommand['Data']['Name'] = name |
| transCommand['Data']['Trans_Parameters'] = '' |
| transCommand['Data']['Trans_Data'] = '' |
| |
| if noAnswer: |
| transCommand['Parameters']['Flags'] = TRANS_NO_RESPONSE |
| |
| smb.addCommand(transCommand) |
| self.sendSMB(smb) |
| |
| smb = self.recvSMB() |
| if smb.isValidAnswer(SMB.SMB_COM_TRANSACTION): |
| return 1 |
| return 0 |
| |
| def read(self, tid, fid, offset=0, max_size = None, wait_answer=1): |
| if not max_size: |
| max_size = self._dialects_parameters['MaxBufferSize'] # Read in multiple KB blocks |
| |
| # max_size is not working, because although it would, the server returns an error (More data avail) |
| |
| smb = NewSMBPacket() |
| smb['Tid'] = tid |
| |
| read = SMBCommand(SMB.SMB_COM_READ) |
| read['Parameters'] = SMBRead_Parameters() |
| read['Parameters']['Fid'] = fid |
| read['Parameters']['Offset'] = offset |
| read['Parameters']['Count'] = max_size |
| smb.addCommand(read) |
| |
| if wait_answer: |
| while 1: |
| self.sendSMB(smb) |
| ans = self.recvSMB() |
| |
| if ans.isValidAnswer(SMB.SMB_COM_READ): |
| readResponse = SMBCommand(ans['Data'][0]) |
| readData = SMBReadResponse_Data(readResponse['Data']) |
| |
| return readData['Data'] |
| |
| return None |
| |
| def read_andx(self, tid, fid, offset=0, max_size = None, wait_answer=1, smb_packet=None): |
| if not max_size: |
| if (self._dialects_parameters['Capabilities'] & SMB.CAP_LARGE_READX) and self._SignatureEnabled is False: |
| max_size = 65000 |
| else: |
| max_size = self._dialects_parameters['MaxBufferSize'] # Read in multiple KB blocks |
| |
| # max_size is not working, because although it would, the server returns an error (More data avail) |
| |
| if smb_packet is None: |
| smb = NewSMBPacket() |
| smb['Tid'] = tid |
| |
| readAndX = SMBCommand(SMB.SMB_COM_READ_ANDX) |
| readAndX['Parameters'] = SMBReadAndX_Parameters() |
| readAndX['Parameters']['Fid'] = fid |
| readAndX['Parameters']['Offset'] = offset |
| readAndX['Parameters']['MaxCount'] = max_size |
| smb.addCommand(readAndX) |
| else: |
| smb = smb_packet |
| |
| if wait_answer: |
| answer = '' |
| while 1: |
| self.sendSMB(smb) |
| ans = self.recvSMB() |
| |
| if ans.isValidAnswer(SMB.SMB_COM_READ_ANDX): |
| # XXX Here we are only using a few fields from the response |
| readAndXResponse = SMBCommand(ans['Data'][0]) |
| readAndXParameters = SMBReadAndXResponse_Parameters(readAndXResponse['Parameters']) |
| |
| offset = readAndXParameters['DataOffset'] |
| count = readAndXParameters['DataCount']+0x10000*readAndXParameters['DataCount_Hi'] |
| answer += str(ans)[offset:offset+count] |
| if not ans.isMoreData(): |
| return answer |
| max_size = min(max_size, readAndXParameters['Remaining']) |
| readAndX['Parameters']['Offset'] += count # XXX Offset is not important (apparently) |
| else: |
| self.sendSMB(smb) |
| ans = self.recvSMB() |
| |
| try: |
| if ans.isValidAnswer(SMB.SMB_COM_READ_ANDX): |
| return ans |
| else: |
| return None |
| except: |
| return ans |
| |
| return None |
| |
| def read_raw(self, tid, fid, offset=0, max_size = None, wait_answer=1): |
| if not max_size: |
| max_size = self._dialects_parameters['MaxBufferSize'] # Read in multiple KB blocks |
| |
| # max_size is not working, because although it would, the server returns an error (More data avail) |
| smb = NewSMBPacket() |
| smb['Tid'] = tid |
| |
| readRaw = SMBCommand(SMB.SMB_COM_READ_RAW) |
| readRaw['Parameters'] = SMBReadRaw_Parameters() |
| readRaw['Parameters']['Fid'] = fid |
| readRaw['Parameters']['Offset'] = offset |
| readRaw['Parameters']['MaxCount'] = max_size |
| smb.addCommand(readRaw) |
| |
| self.sendSMB(smb) |
| if wait_answer: |
| data = self._sess.recv_packet(self.__timeout).get_trailer() |
| if not data: |
| # If there is no data it means there was an error |
| data = self.read_andx(tid, fid, offset, max_size) |
| return data |
| |
| return None |
| |
| def write(self,tid,fid,data, offset = 0, wait_answer=1): |
| smb = NewSMBPacket() |
| smb['Tid'] = tid |
| |
| write = SMBCommand(SMB.SMB_COM_WRITE) |
| write['Parameters'] = SMBWrite_Parameters() |
| write['Data'] = SMBWrite_Data() |
| write['Parameters']['Fid'] = fid |
| write['Parameters']['Count'] = len(data) |
| write['Parameters']['Offset'] = offset |
| write['Parameters']['Remaining'] = len(data) |
| write['Data']['Data'] = data |
| smb.addCommand(write) |
| |
| self.sendSMB(smb) |
| |
| if wait_answer: |
| smb = self.recvSMB() |
| if smb.isValidAnswer(SMB.SMB_COM_WRITE): |
| return smb |
| return None |
| |
| def write_andx(self,tid,fid,data, offset = 0, wait_answer=1, write_pipe_mode = False, smb_packet=None): |
| if smb_packet is None: |
| smb = NewSMBPacket() |
| smb['Tid'] = tid |
| |
| writeAndX = SMBCommand(SMB.SMB_COM_WRITE_ANDX) |
| smb.addCommand(writeAndX) |
| |
| writeAndX['Parameters'] = SMBWriteAndX_Parameters() |
| writeAndX['Parameters']['Fid'] = fid |
| writeAndX['Parameters']['Offset'] = offset |
| writeAndX['Parameters']['WriteMode'] = 8 |
| writeAndX['Parameters']['Remaining'] = len(data) |
| writeAndX['Parameters']['DataLength'] = len(data) |
| writeAndX['Parameters']['DataOffset'] = len(smb) # this length already includes the parameter |
| writeAndX['Data'] = data |
| |
| if write_pipe_mode is True: |
| # First of all we gotta know what the MaxBuffSize is |
| maxBuffSize = self._dialects_parameters['MaxBufferSize'] |
| if len(data) > maxBuffSize: |
| chunks_size = maxBuffSize - 60 |
| writeAndX['Parameters']['WriteMode'] = 0x0c |
| sendData = '\xff\xff' + data |
| totalLen = len(sendData) |
| writeAndX['Parameters']['DataLength'] = chunks_size |
| writeAndX['Parameters']['Remaining'] = totalLen-2 |
| writeAndX['Data'] = sendData[:chunks_size] |
| |
| self.sendSMB(smb) |
| if wait_answer: |
| smbResp = self.recvSMB() |
| smbResp.isValidAnswer(SMB.SMB_COM_WRITE_ANDX) |
| |
| alreadySent = chunks_size |
| sendData = sendData[chunks_size:] |
| |
| while alreadySent < totalLen: |
| writeAndX['Parameters']['WriteMode'] = 0x04 |
| writeAndX['Parameters']['DataLength'] = len(sendData[:chunks_size]) |
| writeAndX['Data'] = sendData[:chunks_size] |
| self.sendSMB(smb) |
| if wait_answer: |
| smbResp = self.recvSMB() |
| smbResp.isValidAnswer(SMB.SMB_COM_WRITE_ANDX) |
| alreadySent += writeAndX['Parameters']['DataLength'] |
| sendData = sendData[chunks_size:] |
| |
| return smbResp |
| |
| else: |
| smb = smb_packet |
| |
| self.sendSMB(smb) |
| |
| if wait_answer: |
| smb = self.recvSMB() |
| if smb.isValidAnswer(SMB.SMB_COM_WRITE_ANDX): |
| return smb |
| return None |
| |
| def write_raw(self,tid,fid,data, offset = 0, wait_answer=1): |
| LOG.warning("[MS-CIFS] This command was introduced in the CorePlus dialect, but is often listed as part of the LAN Manager 1.0 dialect.This command has been deprecated.Clients SHOULD use SMB_COM_WRITE_ANDX") |
| smb = NewSMBPacket() |
| smb['Tid'] = tid |
| |
| writeRaw = SMBCommand(SMB.SMB_COM_WRITE_RAW) |
| writeRaw['Parameters'] = SMBWriteRaw_Parameters() |
| writeRaw['Parameters']['Fid'] = fid |
| writeRaw['Parameters']['Offset'] = offset |
| writeRaw['Parameters']['Count'] = len(data) |
| writeRaw['Parameters']['DataLength'] = 0 |
| writeRaw['Parameters']['DataOffset'] = 0 |
| smb.addCommand(writeRaw) |
| |
| self.sendSMB(smb) |
| self._sess.send_packet(data) |
| |
| if wait_answer: |
| smb = self.recvSMB() |
| if smb.isValidAnswer(SMB.SMB_COM_WRITE_RAW): |
| return smb |
| return None |
| |
| def TransactNamedPipe(self, tid, fid, data = '', noAnswer = 0, waitAnswer = 1, offset = 0): |
| self.send_trans(tid,pack('<HH', 0x26, fid),'\\PIPE\\\x00','',data, noAnswer = noAnswer) |
| |
| if noAnswer or not waitAnswer: |
| return |
| smb = self.recvSMB() |
| if smb.isValidAnswer(SMB.SMB_COM_TRANSACTION): |
| transResponse = SMBCommand(smb['Data'][0]) |
| transParameters = SMBTransactionResponse_Parameters(transResponse['Parameters']) |
| return transResponse['Data'][-transParameters['TotalDataCount']:] # Remove Potential Prefix Padding |
| return None |
| |
| def TransactNamedPipeRecv(self): |
| s = self.recvSMB() |
| if s.isValidAnswer(SMB.SMB_COM_TRANSACTION): |
| transResponse = SMBCommand(s['Data'][0]) |
| transParameters = SMBTransactionResponse_Parameters(transResponse['Parameters']) |
| return transResponse['Data'][-transParameters['TotalDataCount']:] # Remove Potential Prefix Padding |
| return None |
| |
| def nt_create_andx(self,tid,filename, smb_packet=None, cmd = None, shareAccessMode = FILE_SHARE_READ | FILE_SHARE_WRITE, disposition = FILE_OPEN, accessMask = 0x2019f): |
| filename = filename.replace('/', '\\') |
| filename = filename.encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else filename |
| |
| if smb_packet is None: |
| smb = NewSMBPacket() |
| smb['Tid'] = tid |
| else: |
| smb = smb_packet |
| |
| if cmd is None: |
| ntCreate = SMBCommand(SMB.SMB_COM_NT_CREATE_ANDX) |
| ntCreate['Parameters'] = SMBNtCreateAndX_Parameters() |
| ntCreate['Data'] = SMBNtCreateAndX_Data(flags=self.__flags2) |
| ntCreate['Parameters']['FileNameLength'] = len(filename) |
| ntCreate['Parameters']['CreateFlags'] = 0x16 |
| ntCreate['Parameters']['AccessMask'] = accessMask |
| ntCreate['Parameters']['CreateOptions'] = 0x40 |
| ntCreate['Parameters']['ShareAccess'] = shareAccessMode |
| ntCreate['Parameters']['Disposition'] = disposition |
| ntCreate['Data']['FileName'] = filename |
| |
| if self.__flags2 & SMB.FLAGS2_UNICODE: |
| ntCreate['Data']['Pad'] = 0x0 |
| else: |
| ntCreate = cmd |
| |
| smb.addCommand(ntCreate) |
| |
| self.sendSMB(smb) |
| |
| while 1: |
| smb = self.recvSMB() |
| if smb.isValidAnswer(SMB.SMB_COM_NT_CREATE_ANDX): |
| # XXX Here we are ignoring the rest of the response |
| ntCreateResponse = SMBCommand(smb['Data'][0]) |
| ntCreateParameters = SMBNtCreateAndXResponse_Parameters(ntCreateResponse['Parameters']) |
| |
| self.fid = ntCreateParameters['Fid'] |
| return ntCreateParameters['Fid'] |
| |
| def logoff(self): |
| smb = NewSMBPacket() |
| |
| logOff = SMBCommand(SMB.SMB_COM_LOGOFF_ANDX) |
| logOff['Parameters'] = SMBLogOffAndX() |
| smb.addCommand(logOff) |
| |
| self.sendSMB(smb) |
| self.recvSMB() |
| # Let's clear some fields so you can login again under the same session |
| self._uid = 0 |
| |
| def list_path(self, service, path = '*', password = None): |
| path = path.replace('/', '\\') |
| path = path.encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else path |
| |
| tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password) |
| try: |
| findFirstParameter = SMBFindFirst2_Parameters() |
| findFirstParameter['SearchAttributes'] = SMB_FILE_ATTRIBUTE_DIRECTORY | SMB_FILE_ATTRIBUTE_HIDDEN | \ |
| SMB_FILE_ATTRIBUTE_SYSTEM | SMB_FILE_ATTRIBUTE_READONLY | \ |
| SMB_FILE_ATTRIBUTE_ARCHIVE |
| findFirstParameter['SearchCount'] = 512 |
| findFirstParameter['Flags'] = SMB_FIND_RETURN_RESUME_KEYS | SMB_FIND_CLOSE_AT_EOS |
| findFirstParameter['InformationLevel'] = SMB_FIND_FILE_BOTH_DIRECTORY_INFO |
| findFirstParameter['SearchStorageType'] = 0 |
| findFirstParameter['FileName'] = path + ('\x00\x00' if self.__flags2 & SMB.FLAGS2_UNICODE else '\x00') |
| self.send_trans2(tid, SMB.TRANS2_FIND_FIRST2, '\x00', findFirstParameter, '') |
| files = [ ] |
| |
| totalDataCount = 1 |
| findData = '' |
| findFirst2ParameterBlock = '' |
| while len(findData) < totalDataCount: |
| resp = self.recvSMB() |
| |
| if resp.isValidAnswer(SMB.SMB_COM_TRANSACTION2): |
| trans2Response = SMBCommand(resp['Data'][0]) |
| trans2Parameters = SMBTransaction2Response_Parameters(trans2Response['Parameters']) |
| totalDataCount = trans2Parameters['TotalDataCount'] |
| findFirst2ParameterBlock += trans2Response['Data'][trans2Parameters['ParameterOffset']-55:][:trans2Parameters['ParameterCount']] |
| findData += trans2Response['Data'][trans2Parameters['DataOffset']-55:] |
| |
| findParameterBlock = SMBFindFirst2Response_Parameters(findFirst2ParameterBlock) |
| # Save the SID for resume operations |
| sid = findParameterBlock['SID'] |
| |
| while True: |
| record = SMBFindFileBothDirectoryInfo(data = findData) |
| |
| shortname = record['ShortName'].decode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else record['ShortName'] |
| filename = record['FileName'].decode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else record['FileName'] |
| |
| fileRecord = SharedFile(record['CreationTime'], record['LastAccessTime'], record['LastChangeTime'], |
| record['EndOfFile'], record['AllocationSize'], record['ExtFileAttributes'], |
| shortname, filename) |
| files.append(fileRecord) |
| if record['NextEntryOffset'] > 0 and len(findData[record['NextEntryOffset']:]) > 0: |
| findData = findData[record['NextEntryOffset']:] |
| else: |
| # More data to search? |
| if findParameterBlock['EndOfSearch'] == 0: |
| resume_filename = record['FileName'] |
| findNextParameter = SMBFindNext2_Parameters() |
| findNextParameter['SID'] = sid |
| findNextParameter['SearchCount'] = 1024 |
| findNextParameter['InformationLevel'] = SMB_FIND_FILE_BOTH_DIRECTORY_INFO |
| findNextParameter['ResumeKey'] = 0 |
| findNextParameter['Flags'] = SMB_FIND_RETURN_RESUME_KEYS | SMB_FIND_CLOSE_AT_EOS |
| findNextParameter['FileName'] = resume_filename + ('\x00\x00' if self.__flags2 & SMB.FLAGS2_UNICODE else '\x00') |
| self.send_trans2(tid, SMB.TRANS2_FIND_NEXT2, '\x00', findNextParameter, '') |
| findData = '' |
| findNext2ParameterBlock = '' |
| totalDataCount = 1 |
| while len(findData) < totalDataCount: |
| resp = self.recvSMB() |
| |
| if resp.isValidAnswer(SMB.SMB_COM_TRANSACTION2): |
| trans2Response = SMBCommand(resp['Data'][0]) |
| trans2Parameters = SMBTransaction2Response_Parameters(trans2Response['Parameters']) |
| totalDataCount = trans2Parameters['TotalDataCount'] |
| findNext2ParameterBlock += trans2Response['Data'][trans2Parameters['ParameterOffset']-55:][:trans2Parameters['ParameterCount']] |
| findData += trans2Response['Data'][trans2Parameters['DataOffset']-55:] |
| findParameterBlock = SMBFindNext2Response_Parameters(findNext2ParameterBlock) |
| else: |
| break |
| finally: |
| self.disconnect_tree(tid) |
| |
| return files |
| |
| def retr_file(self, service, filename, callback, mode = FILE_OPEN, offset = 0, password = None, shareAccessMode = SMB_ACCESS_READ): |
| filename = string.replace(filename, '/', '\\') |
| |
| fid = -1 |
| tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password) |
| try: |
| fid = self.nt_create_andx(tid, filename, shareAccessMode = shareAccessMode, accessMask = 0x20089) |
| |
| res = self.query_file_info(tid, fid) |
| datasize = SMBQueryFileStandardInfo(res)['EndOfFile'] |
| |
| self.__nonraw_retr_file(tid, fid, offset, datasize, callback) |
| finally: |
| if fid >= 0: |
| self.close(tid, fid) |
| self.disconnect_tree(tid) |
| |
| def stor_file(self, service, filename, callback, mode = FILE_OVERWRITE_IF, offset = 0, password = None, shareAccessMode = SMB_ACCESS_WRITE): |
| filename = string.replace(filename, '/', '\\') |
| |
| fid = -1 |
| tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password) |
| try: |
| fid = self.nt_create_andx(tid, filename, shareAccessMode = shareAccessMode, disposition = mode ) |
| |
| self.__nonraw_stor_file(tid, fid, offset, 0, callback) |
| finally: |
| if fid >= 0: |
| self.close(tid, fid) |
| self.disconnect_tree(tid) |
| |
| def stor_file_nonraw(self, service, filename, callback, mode = FILE_OVERWRITE_IF, offset = 0, password = None, shareAccessMode = SMB_ACCESS_WRITE ): |
| filename = string.replace(filename, '/', '\\') |
| |
| fid = -1 |
| tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password) |
| try: |
| fid = self.nt_create_andx(tid, filename, shareAccessMode = shareAccessMode, disposition = mode) |
| self.__nonraw_stor_file(tid, fid, offset, 0, callback) |
| finally: |
| if fid >= 0: |
| self.close(tid, fid) |
| self.disconnect_tree(tid) |
| |
| def check_dir(self, service, path, password = None): |
| path = string.replace(path,'/', '\\') |
| tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password) |
| try: |
| smb = NewSMBPacket() |
| smb['Tid'] = tid |
| smb['Mid'] = 0 |
| |
| cmd = SMBCommand(SMB.SMB_COM_CHECK_DIRECTORY) |
| cmd['Parameters'] = '' |
| cmd['Data'] = SMBCheckDirectory_Data(flags = self.__flags2) |
| cmd['Data']['DirectoryName'] = path.encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else path |
| smb.addCommand(cmd) |
| |
| self.sendSMB(smb) |
| |
| while 1: |
| s = self.recvSMB() |
| if s.isValidAnswer(SMB.SMB_COM_CHECK_DIRECTORY): |
| return |
| finally: |
| self.disconnect_tree(tid) |
| |
| def remove(self, service, path, password = None): |
| path = string.replace(path,'/', '\\') |
| # Perform a list to ensure the path exists |
| self.list_path(service, path, password) |
| |
| tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password) |
| try: |
| smb = NewSMBPacket() |
| smb['Tid'] = tid |
| smb['Mid'] = 0 |
| |
| cmd = SMBCommand(SMB.SMB_COM_DELETE) |
| cmd['Parameters'] = SMBDelete_Parameters() |
| cmd['Parameters']['SearchAttributes'] = ATTR_HIDDEN | ATTR_SYSTEM | ATTR_ARCHIVE |
| cmd['Data'] = SMBDelete_Data(flags = self.__flags2) |
| cmd['Data']['FileName'] = (path + '\x00').encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else (path + '\x00') |
| smb.addCommand(cmd) |
| |
| self.sendSMB(smb) |
| |
| while 1: |
| s = self.recvSMB() |
| if s.isValidAnswer(SMB.SMB_COM_DELETE): |
| return |
| finally: |
| self.disconnect_tree(tid) |
| |
| def rmdir(self, service, path, password = None): |
| path = string.replace(path,'/', '\\') |
| # Check that the directory exists |
| self.check_dir(service, path, password) |
| |
| tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password) |
| try: |
| path = path.encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else path |
| |
| smb = NewSMBPacket() |
| smb['Tid'] = tid |
| createDir = SMBCommand(SMB.SMB_COM_DELETE_DIRECTORY) |
| createDir['Data'] = SMBDeleteDirectory_Data(flags=self.__flags2) |
| createDir['Data']['DirectoryName'] = path |
| smb.addCommand(createDir) |
| |
| self.sendSMB(smb) |
| |
| while 1: |
| s = self.recvSMB() |
| if s.isValidAnswer(SMB.SMB_COM_DELETE_DIRECTORY): |
| return |
| finally: |
| self.disconnect_tree(tid) |
| |
| def mkdir(self, service, path, password = None): |
| path = string.replace(path,'/', '\\') |
| tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password) |
| try: |
| path = path.encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else path |
| |
| smb = NewSMBPacket() |
| smb['Tid'] = tid |
| smb['Mid'] = 0 |
| |
| createDir = SMBCommand(SMB.SMB_COM_CREATE_DIRECTORY) |
| createDir['Data'] = SMBCreateDirectory_Data(flags=self.__flags2) |
| createDir['Data']['DirectoryName'] = path |
| smb.addCommand(createDir) |
| |
| self.sendSMB(smb) |
| |
| smb = self.recvSMB() |
| if smb.isValidAnswer(SMB.SMB_COM_CREATE_DIRECTORY): |
| return 1 |
| return 0 |
| finally: |
| self.disconnect_tree(tid) |
| |
| def rename(self, service, old_path, new_path, password = None): |
| old_path = string.replace(old_path,'/', '\\') |
| new_path = string.replace(new_path,'/', '\\') |
| tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password) |
| try: |
| smb = NewSMBPacket() |
| smb['Tid'] = tid |
| smb['Mid'] = 0 |
| |
| renameCmd = SMBCommand(SMB.SMB_COM_RENAME) |
| renameCmd['Parameters'] = SMBRename_Parameters() |
| renameCmd['Parameters']['SearchAttributes'] = ATTR_SYSTEM | ATTR_HIDDEN | ATTR_DIRECTORY |
| renameCmd['Data'] = SMBRename_Data(flags = self.__flags2) |
| renameCmd['Data']['OldFileName'] = old_path.encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else old_path |
| renameCmd['Data']['NewFileName'] = new_path.encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else new_path |
| smb.addCommand(renameCmd) |
| |
| self.sendSMB(smb) |
| |
| smb = self.recvSMB() |
| if smb.isValidAnswer(SMB.SMB_COM_RENAME): |
| return 1 |
| return 0 |
| finally: |
| self.disconnect_tree(tid) |
| |
| def writeFile(self, treeId, fileId, data, offset = 0): |
| if (self._dialects_parameters['Capabilities'] & SMB.CAP_LARGE_WRITEX) and self._SignatureEnabled is False: |
| max_buf_size = 65000 |
| else: |
| max_buf_size = self._dialects_parameters['MaxBufferSize'] & ~0x3ff # Write in multiple KB blocks |
| |
| write_offset = offset |
| while 1: |
| if len(data) == 0: |
| break |
| writeData = data[:max_buf_size] |
| data = data[max_buf_size:] |
| |
| smb = self.write_andx(treeId,fileId,writeData, write_offset) |
| writeResponse = SMBCommand(smb['Data'][0]) |
| writeResponseParameters = SMBWriteAndXResponse_Parameters(writeResponse['Parameters']) |
| write_offset += writeResponseParameters['Count'] |
| |
| def get_socket(self): |
| return self._sess.get_socket() |
| |
| ERRDOS = { 1: 'Invalid function', |
| 2: 'File not found', |
| 3: 'Invalid directory', |
| 4: 'Too many open files', |
| 5: 'Access denied', |
| 6: 'Invalid file handle. Please file a bug report.', |
| 7: 'Memory control blocks destroyed', |
| 8: 'Out of memory', |
| 9: 'Invalid memory block address', |
| 10: 'Invalid environment', |
| 11: 'Invalid format', |
| 12: 'Invalid open mode', |
| 13: 'Invalid data', |
| 15: 'Invalid drive', |
| 16: 'Attempt to remove server\'s current directory', |
| 17: 'Not the same device', |
| 18: 'No files found', |
| 32: 'Sharing mode conflicts detected', |
| 33: 'Lock request conflicts detected', |
| 80: 'File already exists' |
| } |
| |
| ERRSRV = { 1: 'Non-specific error', |
| 2: 'Bad password', |
| 4: 'Access denied', |
| 5: 'Invalid tid. Please file a bug report.', |
| 6: 'Invalid network name', |
| 7: 'Invalid device', |
| 49: 'Print queue full', |
| 50: 'Print queue full', |
| 51: 'EOF on print queue dump', |
| 52: 'Invalid print file handle', |
| 64: 'Command not recognized. Please file a bug report.', |
| 65: 'Internal server error', |
| 67: 'Invalid path', |
| 69: 'Invalid access permissions', |
| 71: 'Invalid attribute mode', |
| 81: 'Server is paused', |
| 82: 'Not receiving messages', |
| 83: 'No room to buffer messages', |
| 87: 'Too many remote user names', |
| 88: 'Operation timeout', |
| 89: 'Out of resources', |
| 91: 'Invalid user handle. Please file a bug report.', |
| 250: 'Temporarily unable to support raw mode for transfer', |
| 251: 'Temporarily unable to support raw mode for transfer', |
| 252: 'Continue in MPX mode', |
| 65535: 'Unsupported function' |
| } |
| |
| ERRHRD = { 19: 'Media is write-protected', |
| 20: 'Unknown unit', |
| 21: 'Drive not ready', |
| 22: 'Unknown command', |
| 23: 'CRC error', |
| 24: 'Bad request', |
| 25: 'Seek error', |
| 26: 'Unknown media type', |
| 27: 'Sector not found', |
| 28: 'Printer out of paper', |
| 29: 'Write fault', |
| 30: 'Read fault', |
| 31: 'General failure', |
| 32: 'Open conflicts with an existing open', |
| 33: 'Invalid lock request', |
| 34: 'Wrong disk in drive', |
| 35: 'FCBs not available', |
| 36: 'Sharing buffer exceeded' |
| } |
| |