# Copyright (c) 2003-2006 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. # # $Id: smb.py,v 1.7 2006/05/23 21:19:25 gera Exp $ # # -*- mode: python; tab-width: 4 -*- # $Id: smb.py,v 1.7 2006/05/23 21:19:25 gera Exp $ # # Copyright (C) 2001 Michael Teo # 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 # 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 import os, sys, socket, string, re, select, errno import nmb import types from random import randint from struct import * import ntlm from dcerpc import samr from structure import Structure unicode_support = 0 unicode_convert = 1 try: from cStringIO import StringIO except ImportError: from StringIO import StringIO from binascii import a2b_hex CVS_REVISION = '$Revision: 1.7 $' # Shared Device Type SHARED_DISK = 0x00 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 #*********************************** TEMP BEGIN ******************************** def set_key_odd_parity(key): "" for i in range(len(key)): for k in range(7): bit = 0 t = key[i] >> k bit = (t ^ bit) & 0x1 key[i] = (key[i] & 0xFE) | bit return key #*********************************** TEMP END ******************************** 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): # Error codes # 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 ERRgeneral = 31 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, str, error_class, error_code): self.args = str 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) return 'SessionError: %s, class: %s, code: %s' % (self.args, 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, type, comment): self.__name = name self.__type = 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 '' # 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 '' def __convert_smbtime(self, 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, type, comment): self.__nbname = nbname self.__type = type self.__comment = comment def __repr__(self): return '' class SMBDomain: def __init__(self, nbgroup, type, master_browser): self.__nbgroup = nbgroup self.__type = type self.__master_browser = master_browser def __repr__(self): return '' # Represents a SMB Packet class NewSMBPacket(Structure): structure = ( ('Signature', '"\xffSMB'), ('Command','B=0'), ('ErrorClass','B=0'), ('_reserved','B=0'), ('ErrorCode',' 0 and len(self.get_buffer()) >= self._encryption_key_len: self._encryption_key = self.get_buffer()[:self._encryption_key_len] buf = self.get_buffer() # Look for the server domain offset self._server_name = '' self._server_domain = '' try: if self._lsw_capabilities & 0x3: # is this unicode? offset = self._encryption_key_len if offset & 0x01: offset += 1 end = offset while ord(buf[end]) or ord(buf[end+1]): end += 2 self._server_domain = unicode(buf[offset:end],'utf_16_le') end += 2 offset = end while ord(buf[end]) or ord(buf[end+1]): end += 2 self._server_name = unicode(buf[offset:end],'utf_16_le') else: offset = self._encryption_key_len idx1 = string.find(buf,'\0',offset) if idx1 != -1: self._server_domain = buf[offset:idx1] idx2 = string.find(buf, '\0', idx1 + 1) if idx2 != -1: self._server_name = buf[idx1+1:idx2] except: pass else: self._encryption_key = '' def get_selected_dialect(self): return self._selected_dialect def get_security_mode(self): return self._security_mode def get_max_mpx(self): return self._max_mpx def get_max_vc(self): return self._max_vc def get_max_buffer(self): return self._max_buffer def get_max_raw(self): return self._max_raw def get_session_key(self): return self._session_key def get_lsw_capabilities(self): return self._lsw_capabilities def get_msw_capabilities(self): return self._msw_capabilities def get_utc(self): return self._utc_high, self._utc_low def get_minutes_utc(self): return self._minutes_utc def get_encryption_key_len(self): return self._encryption_key_len def get_encryption_key(self): return self._encryption_key def get_server_domain(self): return self._server_domain def get_server_name(self): return self._server_name def is_auth_mode(self): return self._security_mode & SMB.SECURITY_AUTH_MASK def is_share_mode(self): return self._security_mode & SMB.SECURITY_SHARE_MASK def is_rawmode(self): return self._lsw_capabilities & SMB.CAP_RAW_MODE 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 # Security Share Mode (Used internally by SMB class) SECURITY_SHARE_MASK = 0x01 SECURITY_SHARE_SHARE = 0x00 SECURITY_SHARE_USER = 0x01 # 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 = 0x0001 CAP_MPX_MODE = 0x0002 CAP_UNICODE = 0x0004 CAP_LARGE_FILES = 0x0008 CAP_EXTENDED_SECURITY = 0x80000000 # Flags1 Mask FLAGS1_PATHCASELESS = 0x08 # Flags2 Mask FLAGS2_LONG_FILENAME = 0x0001 FLAGS2_USE_NT_ERRORS = 0x4000 FLAGS2_UNICODE = 0x8000 def __init__(self, remote_name, remote_host, my_name = None, host_type = nmb.TYPE_SERVER, sess_port = nmb.NETBIOS_SESSION_PORT, timeout=None): # The uid attribute will be set when the client calls the login() method self.__uid = 0 self.__server_os = '' self.__server_lanman = '' self.__server_domain = '' self.__remote_name = string.upper(remote_name) self.__is_pathcaseless = 0 self.__ntlm_dialect = 0 self.__sess = None if timeout==None: self.__timeout = 30 else: self.__timeout = timeout if not my_name: my_name = socket.gethostname() i = string.find(my_name, '.') if i > -1: my_name = my_name[:i] try: self.__sess = nmb.NetBIOSSession(my_name, remote_name, remote_host, host_type, sess_port, timeout) except socket.error, ex: raise ex # Initialize values __ntlm_dialect, __is_pathcaseless self.__neg_session() # If the following assertion fails, then mean that the encryption key is not sent when # encrypted authentication is required by the server. assert (self.__ntlm_dialect.is_auth_mode() == SMB.SECURITY_AUTH_PLAINTEXT) or (self.__ntlm_dialect.is_auth_mode() == SMB.SECURITY_AUTH_ENCRYPTED and self.__ntlm_dialect.get_encryption_key() and self.__ntlm_dialect.get_encryption_key_len() >= 8) # Call login() without any authentication information to setup a session if the remote server # is in share mode. if self.__ntlm_dialect.is_share_mode() == SMB.SECURITY_SHARE_SHARE: self.login('', '') def set_timeout(self, timeout): self.__timeout = timeout def __del__(self): if self.__sess: self.__sess.close() def __decode_smb(self, data): _, cmd, err_class, _, err_code, flags1, flags2, _, tid, pid, uid, mid, wcount = unpack('<4sBBBHBH12sHHHHB', data[:33]) param_end = 33 + wcount * 2 return cmd, err_class, err_code, flags1, flags2, tid, uid, mid, data[33:param_end], data[param_end + 2:] def recvSMB(self): r = self.__sess.recv_packet(self.__timeout) return NewSMBPacket(data = r.get_trailer()) def recv_packet(self): r = self.__sess.recv_packet(self.__timeout) return SMBPacket(r.get_trailer()) def __decode_trans(self, params, data): totparamcnt, totdatacnt, _, paramcnt, paramoffset, paramds, datacnt, dataoffset, datads, setupcnt = unpack('= 0: self.close(tid, fid) self.disconnect_tree(tid) def stor_file(self, service, filename, callback, mode = SMB_O_CREAT | SMB_O_TRUNC, offset = 0, password = None): filename = string.replace(filename, '/', '\\') fid = -1 tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password) try: fid, attrib, lastwritetime, datasize, grantedaccess, filetype, devicestate, action, serverfid = self.open_andx(tid, filename, mode, SMB_ACCESS_WRITE | SMB_SHARE_DENY_WRITE) # If the max_transmit buffer size is more than 16KB, upload process using non-raw mode is actually # faster than using raw-mode. if self.__ntlm_dialect.get_max_buffer() < 16384 and self.__ntlm_dialect.is_rawmode(): # Once the __raw_stor_file returns, fid is already closed self.__raw_stor_file(tid, fid, offset, datasize, callback) fid = -1 else: self.__nonraw_stor_file(tid, fid, offset, datasize, callback) finally: if fid >= 0: self.close(tid, fid) self.disconnect_tree(tid) def copy(self, src_service, src_path, dest_service, dest_path, callback = None, write_mode = SMB_O_CREAT | SMB_O_TRUNC, src_password = None, dest_password = None): dest_path = string.replace(dest_path, '/', '\\') src_path = string.replace(src_path, '/', '\\') src_tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + src_service, src_password) dest_tid = -1 try: if src_service == dest_service: dest_tid = src_tid else: dest_tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + dest_service, dest_password) dest_fid = self.open_andx(dest_tid, dest_path, write_mode, SMB_ACCESS_WRITE | SMB_SHARE_DENY_WRITE)[0] src_fid, _, _, src_datasize, _, _, _, _, _ = self.open_andx(src_tid, src_path, SMB_O_OPEN, SMB_ACCESS_READ | SMB_SHARE_DENY_WRITE) if callback: callback(0, src_datasize) max_buf_size = (self.__ntlm_dialect.get_max_buffer() >> 10) << 10 read_offset = 0 write_offset = 0 while read_offset < src_datasize: self.__send_smb_packet(SMB.SMB_COM_READ_ANDX, 0, 0, src_tid, 0, pack(' -1 and src_service != dest_service: self.disconnect_tree(dest_tid) def check_dir(self, service, path, password = None): tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password) try: self.__send_smb_packet(SMB.SMB_COM_CHECK_DIRECTORY, 0x08, 0, tid, 0, '', '\x04' + path + '\x00') while 1: s = self.recv_packet() if self.isValidAnswer(s,SMB.SMB_COM_CHECK_DIRECTORY): return finally: self.disconnect_tree(s.get_tid()) def remove(self, service, path, password = None): # 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: self.__send_smb_packet(SMB.SMB_COM_DELETE, 0x08, 0, tid, 0, pack('