Home | History | Annotate | Download | only in impacket
      1 # Copyright (c) 2003-2016 CORE Security Technologies
      2 #
      3 # This software is provided under under a slightly modified version
      4 # of the Apache Software License. See the accompanying LICENSE file
      5 # for more information.
      6 #
      7 # Author: Alberto Solino (@agsolino)
      8 #
      9 # Description:
     10 #   [MS-SMB2] Protocol Implementation (SMB2 and SMB3)
     11 #   As you might see in the code, it's implemented strictly following 
     12 #   the structures defined in the protocol specification. This may
     13 #   not be the most efficient way (e.g. self._Connection is the
     14 #   same to self._Session in the context of this library ) but
     15 #   it certainly helps following the document way easier.
     16 #
     17 # ToDo: 
     18 # [X] Implement SMB2_CHANGE_NOTIFY
     19 # [X] Implement SMB2_QUERY_INFO
     20 # [X] Implement SMB2_SET_INFO
     21 # [ ] Implement SMB2_OPLOCK_BREAK
     22 # [X] Implement SMB3 signing 
     23 # [ ] Implement SMB3 encryption
     24 # [ ] Add more backward compatible commands from the smb.py code
     25 # [ ] Fix up all the 'ToDo' comments inside the code
     26 #
     27 
     28 import socket
     29 import ntpath
     30 import random
     31 import string
     32 import struct
     33 from binascii import a2b_hex
     34 from contextlib import contextmanager
     35 
     36 from impacket import nmb, ntlm, uuid, crypto, LOG
     37 from impacket.smb3structs import *
     38 from impacket.nt_errors import STATUS_SUCCESS, STATUS_MORE_PROCESSING_REQUIRED, STATUS_INVALID_PARAMETER, \
     39     STATUS_NO_MORE_FILES, STATUS_PENDING, STATUS_NOT_IMPLEMENTED, ERROR_MESSAGES
     40 from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, SPNEGO_NegTokenResp
     41 
     42 
     43 # For signing
     44 import hashlib, hmac, copy
     45 
     46 # Structs to be used
     47 TREE_CONNECT = {
     48     'ShareName'       : '',
     49     'TreeConnectId'   : 0,
     50     'Session'         : 0,
     51     'IsDfsShare'      : False,
     52     # If the client implements the SMB 3.0 dialect, 
     53     # the client MUST also implement the following
     54     'IsCAShare'       : False,
     55     'EncryptData'     : False,
     56     'IsScaleoutShare' : False,
     57     # Outside the protocol
     58     'NumberOfUses'    : 0,
     59 }
     60 
     61 FILE = {
     62     'OpenTable'       : [],
     63     'LeaseKey'        : '',
     64     'LeaseState'      : 0,
     65     'LeaseEpoch'      : 0,
     66 }
     67 
     68 OPEN = {
     69     'FileID'             : '',
     70     'TreeConnect'        : 0,
     71     'Connection'         : 0, # Not Used
     72     'Oplocklevel'        : 0,
     73     'Durable'            : False,
     74     'FileName'           : '',
     75     'ResilientHandle'    : False,
     76     'LastDisconnectTime' : 0,
     77     'ResilientTimeout'   : 0,
     78     'OperationBuckets'   : [],
     79     # If the client implements the SMB 3.0 dialect, 
     80     # the client MUST implement the following
     81     'CreateGuid'         : '',
     82     'IsPersistent'       : False,
     83     'DesiredAccess'      : '',
     84     'ShareMode'          : 0,
     85     'CreateOption'       : '',
     86     'FileAttributes'     : '',
     87     'CreateDisposition'  : '',
     88 }
     89 
     90 REQUEST = {
     91     'CancelID'     : '',
     92     'Message'      : '',
     93     'Timestamp'    : 0,
     94 }
     95 
     96 CHANNEL = {
     97     'SigningKey' : '',
     98     'Connection' : 0,
     99 }
    100 
    101 
    102 class SessionError(Exception):
    103     def __init__( self, error = 0, packet=0):
    104         Exception.__init__(self)
    105         self.error = error
    106         self.packet = packet
    107        
    108     def get_error_code( self ):
    109         return self.error
    110 
    111     def get_error_packet( self ):
    112         return self.packet
    113 
    114     def __str__( self ):
    115         return 'SMB SessionError: %s(%s)' % (ERROR_MESSAGES[self.error])
    116 
    117 
    118 class SMB3:
    119     def __init__(self, remote_name, remote_host, my_name = None, host_type = nmb.TYPE_SERVER, sess_port = 445, timeout=60, UDP = 0, preferredDialect = None, session = None):
    120 
    121         # [MS-SMB2] Section 3
    122         self.RequireMessageSigning = False    #
    123         self.ConnectionTable = {}
    124         self.GlobalFileTable = {}
    125         self.ClientGuid = ''.join([random.choice(string.letters) for i in range(16)])
    126         # Only for SMB 3.0
    127         self.EncryptionAlgorithmList = ['AES-CCM']
    128         self.MaxDialect = []
    129         self.RequireSecureNegotiate = False
    130 
    131         # Per Transport Connection Data
    132         self._Connection = {
    133             # Indexed by SessionID
    134             #'SessionTable'             : {},    
    135             # Indexed by MessageID
    136             'OutstandingRequests'      : {},
    137             'OutstandingResponses'     : {},    #
    138             'SequenceWindow'           : 0,     #
    139             'GSSNegotiateToken'        : '',    #
    140             'MaxTransactSize'          : 0,     #
    141             'MaxReadSize'              : 0,     #
    142             'MaxWriteSize'             : 0,     #
    143             'ServerGuid'               : '',    #
    144             'RequireSigning'           : False, #
    145             'ServerName'               : '',    #
    146             # If the client implements the SMB 2.1 or SMB 3.0 dialects, it MUST 
    147             # also implement the following
    148             'Dialect'                  : '',    #
    149             'SupportsFileLeasing'      : False, #
    150             'SupportsMultiCredit'      : False, #
    151             # If the client implements the SMB 3.0 dialect, 
    152             # it MUST also implement the following
    153             'SupportsDirectoryLeasing' : False, #
    154             'SupportsMultiChannel'     : False, #
    155             'SupportsPersistentHandles': False, #
    156             'SupportsEncryption'       : False, #
    157             'ClientCapabilities'       : 0,
    158             'ServerCapabilities'       : 0,    #
    159             'ClientSecurityMode'       : 0,    #
    160             'ServerSecurityMode'       : 0,    #
    161             # Outside the protocol
    162             'ServerIP'                 : '',    #
    163         }
    164    
    165         self._Session = {
    166             'SessionID'                : 0,   #
    167             'TreeConnectTable'         : {},    #
    168             'SessionKey'               : '',    #
    169             'SigningRequired'          : False, #
    170             'Connection'               : 0,     # 
    171             'UserCredentials'          : '',    #
    172             'OpenTable'                : {},    #
    173             # If the client implements the SMB 3.0 dialect, 
    174             # it MUST also implement the following
    175             'ChannelList'              : [],
    176             'ChannelSequence'          : 0,
    177             #'EncryptData'              : False,
    178             'EncryptData'              : True,
    179             'EncryptionKey'            : '',
    180             'DecryptionKey'            : '',
    181             'SigningKey'               : '',  
    182             'ApplicationKey'           : '',
    183             # Outside the protocol
    184             'SessionFlags'             : 0,     # 
    185             'ServerName'               : '',    #
    186             'ServerDomain'             : '',    #
    187             'ServerDNSDomainName'      : '',    #
    188             'ServerOS'                 : '',    #
    189             'SigningActivated'         : False, #
    190         }
    191 
    192         self.SMB_PACKET = SMB2Packet
    193         
    194         self._timeout = timeout
    195         self._Connection['ServerIP'] = remote_host
    196         self._NetBIOSSession = None
    197 
    198         self.__userName = ''
    199         self.__password = ''
    200         self.__domain   = ''
    201         self.__lmhash   = ''
    202         self.__nthash   = ''
    203         self.__kdc      = ''
    204         self.__aesKey   = ''
    205         self.__TGT      = None
    206         self.__TGS      = None
    207 
    208         if sess_port == 445 and remote_name == '*SMBSERVER':
    209            self._Connection['ServerName'] = remote_host
    210         else:
    211            self._Connection['ServerName'] = remote_name
    212 
    213         if session is None:
    214             if not my_name:
    215                 my_name = socket.gethostname()
    216                 i = string.find(my_name, '.')
    217                 if i > -1:
    218                     my_name = my_name[:i]
    219 
    220             if UDP:
    221                 self._NetBIOSSession = nmb.NetBIOSUDPSession(my_name, self._Connection['ServerName'], remote_host, host_type, sess_port, self._timeout)
    222             else:
    223                 self._NetBIOSSession = nmb.NetBIOSTCPSession(my_name, self._Connection['ServerName'], remote_host, host_type, sess_port, self._timeout)
    224 
    225                 self.negotiateSession(preferredDialect)
    226         else:
    227             self._NetBIOSSession = session
    228             # We should increase the SequenceWindow since a packet was already received.
    229             self._Connection['SequenceWindow'] += 1
    230             # Let's negotiate again using the same connection
    231             self.negotiateSession(preferredDialect)
    232 
    233     def printStatus(self):
    234         print "CONNECTION"
    235         for i in self._Connection.items():
    236             print "%-40s : %s" % i
    237         print
    238         print "SESSION"
    239         for i in self._Session.items():
    240             print "%-40s : %s" % i
    241 
    242     def getServerName(self):
    243         return self._Session['ServerName']
    244 
    245     def getServerIP(self):
    246         return self._Connection['ServerIP']
    247 
    248     def getServerDomain(self):
    249         return self._Session['ServerDomain']
    250 
    251     def getServerDNSDomainName(self):
    252         return self._Session['ServerDNSDomainName']
    253 
    254     def getServerOS(self):
    255         return self._Session['ServerOS']
    256 
    257     def getServerOSMajor(self):
    258         return self._Session['ServerOSMajor']
    259 
    260     def getServerOSMinor(self):
    261         return self._Session['ServerOSMinor']
    262 
    263     def getServerOSBuild(self):
    264         return self._Session['ServerOSBuild']
    265 
    266     def isGuestSession(self):
    267         return self._Session['SessionFlags'] & SMB2_SESSION_FLAG_IS_GUEST 
    268 
    269     def setTimeout(self, timeout):
    270         self._timeout = timeout
    271 
    272     @contextmanager
    273     def useTimeout(self, timeout):
    274         prev_timeout = self.getTimeout(timeout)
    275         try:
    276             yield
    277         finally:
    278             self.setTimeout(prev_timeout)
    279 
    280     def getDialect(self):
    281         return self._Connection['Dialect']
    282 
    283 
    284     def signSMB(self, packet):
    285         packet['Signature'] = '\x00'*16
    286         if self._Connection['Dialect'] == SMB2_DIALECT_21 or self._Connection['Dialect'] == SMB2_DIALECT_002:
    287             if len(self._Session['SessionKey']) > 0:
    288                 signature = hmac.new(self._Session['SessionKey'], str(packet), hashlib.sha256).digest()
    289                 packet['Signature'] = signature[:16]
    290         else:
    291             if len(self._Session['SessionKey']) > 0:
    292                 p = str(packet)
    293                 signature = crypto.AES_CMAC(self._Session['SigningKey'], p, len(p))
    294                 packet['Signature'] = signature
    295      
    296     def sendSMB(self, packet):
    297         # The idea here is to receive multiple/single commands and create a compound request, and send it
    298         # Should return the MessageID for later retrieval. Implement compounded related requests.
    299 
    300         # If Connection.Dialect is equal to "3.000" and if Connection.SupportsMultiChannel or
    301         # Connection.SupportsPersistentHandles is TRUE, the client MUST set ChannelSequence in the
    302         # SMB2 header to Session.ChannelSequence
    303 
    304         # Check this is not a CANCEL request. If so, don't consume sequece numbers
    305         if packet['Command'] is not SMB2_CANCEL:
    306             packet['MessageID'] = self._Connection['SequenceWindow']
    307             self._Connection['SequenceWindow'] += 1
    308         packet['SessionID'] = self._Session['SessionID']
    309 
    310         # Default the credit charge to 1 unless set by the caller
    311         if packet.fields.has_key('CreditCharge') is False:
    312             packet['CreditCharge'] = 1
    313 
    314         # Standard credit request after negotiating protocol
    315         if self._Connection['SequenceWindow'] > 3:
    316             packet['CreditRequestResponse'] = 127
    317 
    318         messageId = packet['MessageID']
    319 
    320         if self._Session['SigningActivated'] is True and self._Connection['SequenceWindow'] > 2:
    321             if packet['TreeID'] > 0 and self._Session['TreeConnectTable'].has_key(packet['TreeID']) is True:
    322                 if self._Session['TreeConnectTable'][packet['TreeID']]['EncryptData'] is False:
    323                     packet['Flags'] = SMB2_FLAGS_SIGNED
    324                     self.signSMB(packet)
    325             elif packet['TreeID'] == 0:
    326                 packet['Flags'] = SMB2_FLAGS_SIGNED
    327                 self.signSMB(packet)
    328 
    329         if (self._Session['SessionFlags'] & SMB2_SESSION_FLAG_ENCRYPT_DATA) or ( packet['TreeID'] != 0 and self._Session['TreeConnectTable'][packet['TreeID']]['EncryptData'] is True):
    330             plainText = str(packet)
    331             transformHeader = SMB2_TRANSFORM_HEADER()
    332             transformHeader['Nonce'] = ''.join([random.choice(string.letters) for i in range(11)])
    333             transformHeader['OriginalMessageSize'] = len(plainText)
    334             transformHeader['EncryptionAlgorithm'] = SMB2_ENCRYPTION_AES128_CCM
    335             transformHeader['SessionID'] = self._Session['SessionID'] 
    336             from Crypto.Cipher import AES
    337             try: 
    338                 AES.MODE_CCM
    339             except:
    340                 LOG.critical("Your pycrypto doesn't support AES.MODE_CCM. Currently only pycrypto experimental supports this mode.\nDownload it from https://www.dlitz.net/software/pycrypto ")
    341                 raise 
    342             cipher = AES.new(self._Session['EncryptionKey'], AES.MODE_CCM,  transformHeader['Nonce'])
    343             cipher.update(str(transformHeader)[20:])
    344             cipherText = cipher.encrypt(plainText)
    345             transformHeader['Signature'] = cipher.digest()
    346             packet = str(transformHeader) + cipherText
    347 
    348         self._NetBIOSSession.send_packet(str(packet))
    349         return messageId
    350 
    351     def recvSMB(self, packetID = None):
    352         # First, verify we don't have the packet already
    353         if self._Connection['OutstandingResponses'].has_key(packetID):
    354             return self._Connection['OutstandingResponses'].pop(packetID) 
    355 
    356         data = self._NetBIOSSession.recv_packet(self._timeout) 
    357 
    358         if data.get_trailer().startswith('\xfdSMB'):
    359             # Packet is encrypted
    360             transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer())
    361             from Crypto.Cipher import AES
    362             try: 
    363                 AES.MODE_CCM
    364             except:
    365                 LOG.critical("Your pycrypto doesn't support AES.MODE_CCM. Currently only pycrypto experimental supports this mode.\nDownload it from https://www.dlitz.net/software/pycrypto ")
    366                 raise 
    367             cipher = AES.new(self._Session['DecryptionKey'], AES.MODE_CCM,  transformHeader['Nonce'][:11])
    368             cipher.update(str(transformHeader)[20:])
    369             plainText = cipher.decrypt(data.get_trailer()[len(SMB2_TRANSFORM_HEADER()):])
    370             #cipher.verify(transformHeader['Signature'])
    371             packet = SMB2Packet(plainText)
    372         else:
    373             # In all SMB dialects for a response this field is interpreted as the Status field. 
    374             # This field can be set to any value. For a list of valid status codes, 
    375             # see [MS-ERREF] section 2.3.
    376             packet = SMB2Packet(data.get_trailer())
    377 
    378         # Loop while we receive pending requests
    379         if packet['Status'] == STATUS_PENDING:
    380             status = STATUS_PENDING
    381             while status == STATUS_PENDING:
    382                 data = self._NetBIOSSession.recv_packet(self._timeout) 
    383                 if data.get_trailer().startswith('\xfeSMB'):
    384                     packet = SMB2Packet(data.get_trailer())
    385                 else:
    386                     # Packet is encrypted
    387                     transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer())
    388                     from Crypto.Cipher import AES
    389                     try: 
    390                         AES.MODE_CCM
    391                     except:
    392                         LOG.critical("Your pycrypto doesn't support AES.MODE_CCM. Currently only pycrypto experimental supports this mode.\nDownload it from https://www.dlitz.net/software/pycrypto ")
    393                         raise 
    394                     cipher = AES.new(self._Session['DecryptionKey'], AES.MODE_CCM,  transformHeader['Nonce'][:11])
    395                     cipher.update(str(transformHeader)[20:])
    396                     plainText = cipher.decrypt(data.get_trailer()[len(SMB2_TRANSFORM_HEADER()):])
    397                     #cipher.verify(transformHeader['Signature'])
    398                     packet = SMB2Packet(plainText)
    399                 status = packet['Status']
    400 
    401         if packet['MessageID'] == packetID or packetID is None:
    402         #    if self._Session['SigningRequired'] is True:
    403         #        self.signSMB(packet)
    404             # Let's update the sequenceWindow based on the CreditsCharged
    405             self._Connection['SequenceWindow'] += (packet['CreditCharge'] - 1)
    406             return packet
    407         else:
    408             self._Connection['OutstandingResponses'][packet['MessageID']] = packet
    409             return self.recvSMB(packetID) 
    410 
    411     def negotiateSession(self, preferredDialect = None):
    412         packet = self.SMB_PACKET()
    413         packet['Command'] = SMB2_NEGOTIATE
    414         negSession = SMB2Negotiate()
    415 
    416         negSession['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED 
    417         if self.RequireMessageSigning is True:
    418             negSession['SecurityMode'] |= SMB2_NEGOTIATE_SIGNING_REQUIRED
    419         negSession['Capabilities'] = SMB2_GLOBAL_CAP_ENCRYPTION
    420         negSession['ClientGuid'] = self.ClientGuid
    421         if preferredDialect is not None:
    422             negSession['Dialects'] = [preferredDialect]
    423         else:
    424             negSession['Dialects'] = [SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30]
    425         negSession['DialectCount'] = len(negSession['Dialects'])
    426         packet['Data'] = negSession
    427 
    428         # Storing this data for later use
    429         self._Connection['ClientSecurityMode'] = negSession['SecurityMode']
    430         self._Connection['Capabilities']       = negSession['Capabilities']
    431 
    432         packetID = self.sendSMB(packet)
    433         ans = self.recvSMB(packetID)
    434         if ans.isValidAnswer(STATUS_SUCCESS):
    435              # ToDo this:
    436              # If the DialectRevision in the SMB2 NEGOTIATE Response is 0x02FF, the client MUST issue a new
    437              # SMB2 NEGOTIATE request as described in section 3.2.4.2.2.2 with the only exception 
    438              # that the client MUST allocate sequence number 1 from Connection.SequenceWindow, and MUST set
    439              # MessageId field of the SMB2 header to 1. Otherwise, the client MUST proceed as follows.
    440             negResp = SMB2Negotiate_Response(ans['Data'])
    441             self._Connection['MaxTransactSize']   = min(0x100000,negResp['MaxTransactSize'])
    442             self._Connection['MaxReadSize']       = min(0x100000,negResp['MaxReadSize'])
    443             self._Connection['MaxWriteSize']      = min(0x100000,negResp['MaxWriteSize'])
    444             self._Connection['ServerGuid']        = negResp['ServerGuid']
    445             self._Connection['GSSNegotiateToken'] = negResp['Buffer']
    446             self._Connection['Dialect']           = negResp['DialectRevision']
    447             if (negResp['SecurityMode'] & SMB2_NEGOTIATE_SIGNING_REQUIRED) == SMB2_NEGOTIATE_SIGNING_REQUIRED:
    448                 self._Connection['RequireSigning'] = True
    449             if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_LEASING) == SMB2_GLOBAL_CAP_LEASING: 
    450                 self._Connection['SupportsFileLeasing'] = True
    451             if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_LARGE_MTU) == SMB2_GLOBAL_CAP_LARGE_MTU:
    452                 self._Connection['SupportsMultiCredit'] = True
    453 
    454             if self._Connection['Dialect'] == SMB2_DIALECT_30:
    455                 # Switching to the right packet format
    456                 self.SMB_PACKET = SMB3Packet
    457                 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) == SMB2_GLOBAL_CAP_DIRECTORY_LEASING:
    458                     self._Connection['SupportsDirectoryLeasing'] = True
    459                 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_MULTI_CHANNEL) == SMB2_GLOBAL_CAP_MULTI_CHANNEL:
    460                     self._Connection['SupportsMultiChannel'] = True
    461                 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) == SMB2_GLOBAL_CAP_PERSISTENT_HANDLES:
    462                     self._Connection['SupportsPersistentHandles'] = True
    463                 if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_ENCRYPTION) == SMB2_GLOBAL_CAP_ENCRYPTION:
    464                     self._Connection['SupportsEncryption'] = True
    465 
    466                 self._Connection['ServerCapabilities'] = negResp['Capabilities']
    467                 self._Connection['ServerSecurityMode'] = negResp['SecurityMode']
    468 
    469     def getCredentials(self):
    470         return (
    471             self.__userName,
    472             self.__password,
    473             self.__domain,
    474             self.__lmhash,
    475             self.__nthash,
    476             self.__aesKey, 
    477             self.__TGT, 
    478             self.__TGS)
    479 
    480     def kerberosLogin(self, user, password, domain = '', lmhash = '', nthash = '', aesKey='', kdcHost = '', TGT=None, TGS=None):
    481         # If TGT or TGS are specified, they are in the form of:
    482         # TGS['KDC_REP'] = the response from the server
    483         # TGS['cipher'] = the cipher used
    484         # TGS['sessionKey'] = the sessionKey
    485         # If we have hashes, normalize them
    486         if lmhash != '' or nthash != '':
    487             if len(lmhash) % 2:     lmhash = '0%s' % lmhash
    488             if len(nthash) % 2:     nthash = '0%s' % nthash
    489             try: # just in case they were converted already
    490                 lmhash = a2b_hex(lmhash)
    491                 nthash = a2b_hex(nthash)
    492             except:
    493                 pass
    494 
    495         self.__userName = user
    496         self.__password = password
    497         self.__domain   = domain
    498         self.__lmhash   = lmhash
    499         self.__nthash   = nthash
    500         self.__kdc      = kdcHost
    501         self.__aesKey   = aesKey
    502         self.__TGT      = TGT
    503         self.__TGS      = TGS
    504        
    505         sessionSetup = SMB2SessionSetup()
    506         if self.RequireMessageSigning is True:
    507            sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED
    508         else:
    509            sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED
    510 
    511         sessionSetup['Flags'] = 0
    512         #sessionSetup['Capabilities'] = SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_DFS
    513 
    514         # Importing down here so pyasn1 is not required if kerberos is not used.
    515         from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set
    516         from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS
    517         from impacket.krb5 import constants
    518         from impacket.krb5.types import Principal, KerberosTime, Ticket
    519         from pyasn1.codec.der import decoder, encoder
    520         import datetime
    521 
    522         # First of all, we need to get a TGT for the user
    523         userName = Principal(user, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
    524         if TGT is None:
    525             if TGS is None:
    526                 tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash, aesKey, kdcHost)
    527         else:
    528             tgt = TGT['KDC_REP']
    529             cipher = TGT['cipher']
    530             sessionKey = TGT['sessionKey'] 
    531 
    532         # Save the ticket
    533         # If you want, for debugging purposes
    534 #        from impacket.krb5.ccache import CCache
    535 #        ccache = CCache()
    536 #        try:
    537 #            if TGS is None:
    538 #                ccache.fromTGT(tgt, oldSessionKey, sessionKey)
    539 #            else:
    540 #                ccache.fromTGS(TGS['KDC_REP'], TGS['oldSessionKey'], TGS['sessionKey'] )
    541 #            ccache.saveFile('/tmp/ticket.bin')
    542 #        except Exception, e:
    543 #            print e
    544 #            pass
    545 
    546         # Now that we have the TGT, we should ask for a TGS for cifs
    547 
    548         if TGS is None:
    549             serverName = Principal('cifs/%s' % (self._Connection['ServerName']), type=constants.PrincipalNameType.NT_SRV_INST.value)
    550             tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey)
    551         else:
    552             tgs = TGS['KDC_REP']
    553             cipher = TGS['cipher']
    554             sessionKey = TGS['sessionKey'] 
    555 
    556         # Let's build a NegTokenInit with a Kerberos REQ_AP
    557 
    558         blob = SPNEGO_NegTokenInit() 
    559 
    560         # Kerberos
    561         blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']]
    562 
    563         # Let's extract the ticket from the TGS
    564         tgs = decoder.decode(tgs, asn1Spec = TGS_REP())[0]
    565         ticket = Ticket()
    566         ticket.from_asn1(tgs['ticket'])
    567         
    568         # Now let's build the AP_REQ
    569         apReq = AP_REQ()
    570         apReq['pvno'] = 5
    571         apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value)
    572 
    573         opts = list()
    574         apReq['ap-options'] = constants.encodeFlags(opts)
    575         seq_set(apReq,'ticket', ticket.to_asn1)
    576 
    577         authenticator = Authenticator()
    578         authenticator['authenticator-vno'] = 5
    579         authenticator['crealm'] = domain
    580         seq_set(authenticator, 'cname', userName.components_to_asn1)
    581         now = datetime.datetime.utcnow()
    582 
    583         authenticator['cusec'] = now.microsecond
    584         authenticator['ctime'] = KerberosTime.to_asn1(now)
    585 
    586         encodedAuthenticator = encoder.encode(authenticator)
    587 
    588         # Key Usage 11
    589         # AP-REQ Authenticator (includes application authenticator
    590         # subkey), encrypted with the application session key
    591         # (Section 5.5.1)
    592         encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11, encodedAuthenticator, None)
    593 
    594         apReq['authenticator'] = None
    595         apReq['authenticator']['etype'] = cipher.enctype
    596         apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator
    597 
    598         blob['MechToken'] = encoder.encode(apReq)
    599 
    600         sessionSetup['SecurityBufferLength'] = len(blob)
    601         sessionSetup['Buffer']               = blob.getData()
    602 
    603         packet = self.SMB_PACKET()
    604         packet['Command'] = SMB2_SESSION_SETUP
    605         packet['Data']    = sessionSetup
    606 
    607         packetID = self.sendSMB(packet)
    608         ans = self.recvSMB(packetID)
    609         if ans.isValidAnswer(STATUS_SUCCESS):
    610             self._Session['SessionID']       = ans['SessionID']
    611             self._Session['SigningRequired'] = self._Connection['RequireSigning']
    612             self._Session['UserCredentials'] = (user, password, domain, lmhash, nthash)
    613             self._Session['Connection']      = self._NetBIOSSession.get_socket()
    614 
    615             self._Session['SessionKey']  = sessionKey.contents[:16]
    616             if self._Session['SigningRequired'] is True and self._Connection['Dialect'] == SMB2_DIALECT_30:
    617                 self._Session['SigningKey']  = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2AESCMAC\x00", "SmbSign\x00", 128)
    618 
    619             # Calculate the key derivations for dialect 3.0
    620             if self._Session['SigningRequired'] is True:
    621                 self._Session['SigningActivated'] = True
    622             if self._Connection['Dialect'] == SMB2_DIALECT_30:
    623                 self._Session['ApplicationKey']  = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2APP\x00", "SmbRpc\x00", 128)
    624                 self._Session['EncryptionKey']   = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2AESCCM\x00", "ServerIn \x00", 128)
    625                 self._Session['DecryptionKey']   = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2AESCCM\x00", "ServerOut\x00", 128)
    626        
    627             return True
    628         else:
    629             # We clean the stuff we used in case we want to authenticate again
    630             # within the same connection
    631             self._Session['UserCredentials']   = ''
    632             self._Session['Connection']        = 0
    633             self._Session['SessionID']         = 0
    634             self._Session['SigningRequired']   = False
    635             self._Session['SigningKey']        = ''
    636             self._Session['SessionKey']        = ''
    637             self._Session['SigningActivated']  = False
    638             raise
    639 
    640 
    641     def login(self, user, password, domain = '', lmhash = '', nthash = ''):
    642         # If we have hashes, normalize them
    643         if lmhash != '' or nthash != '':
    644             if len(lmhash) % 2:     lmhash = '0%s' % lmhash
    645             if len(nthash) % 2:     nthash = '0%s' % nthash
    646             try: # just in case they were converted already
    647                 lmhash = a2b_hex(lmhash)
    648                 nthash = a2b_hex(nthash)
    649             except:
    650                 pass
    651 
    652         self.__userName = user
    653         self.__password = password
    654         self.__domain   = domain
    655         self.__lmhash   = lmhash
    656         self.__nthash   = nthash
    657         self.__aesKey   = ''
    658         self.__TGT      = None
    659         self.__TGS      = None
    660        
    661         sessionSetup = SMB2SessionSetup()
    662         if self.RequireMessageSigning is True:
    663            sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED
    664         else:
    665            sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED
    666 
    667         sessionSetup['Flags'] = 0
    668         #sessionSetup['Capabilities'] = SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_DFS
    669 
    670         # Let's build a NegTokenInit with the NTLMSSP
    671         # TODO: In the future we should be able to choose different providers
    672 
    673         blob = SPNEGO_NegTokenInit() 
    674 
    675         # NTLMSSP
    676         blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
    677         auth = ntlm.getNTLMSSPType1('','', self._Connection['RequireSigning'])
    678         blob['MechToken'] = str(auth)
    679 
    680         sessionSetup['SecurityBufferLength'] = len(blob)
    681         sessionSetup['Buffer']               = blob.getData()
    682 
    683         # ToDo:
    684         # If this authentication is for establishing an alternative channel for an existing Session, as specified
    685         # in section 3.2.4.1.7, the client MUST also set the following values:
    686         # The SessionId field in the SMB2 header MUST be set to the Session.SessionId for the new
    687         # channel being established.
    688         # The SMB2_SESSION_FLAG_BINDING bit MUST be set in the Flags field.
    689         # The PreviousSessionId field MUST be set to zero.
    690 
    691         packet = self.SMB_PACKET()
    692         packet['Command'] = SMB2_SESSION_SETUP
    693         packet['Data']    = sessionSetup
    694 
    695         packetID = self.sendSMB(packet)
    696         ans = self.recvSMB(packetID)
    697         if ans.isValidAnswer(STATUS_MORE_PROCESSING_REQUIRED):
    698             self._Session['SessionID']       = ans['SessionID']
    699             self._Session['SigningRequired'] = self._Connection['RequireSigning']
    700             self._Session['UserCredentials'] = (user, password, domain, lmhash, nthash)
    701             self._Session['Connection']      = self._NetBIOSSession.get_socket()
    702             sessionSetupResponse = SMB2SessionSetup_Response(ans['Data'])
    703             respToken = SPNEGO_NegTokenResp(sessionSetupResponse['Buffer'])
    704 
    705             # Let's parse some data and keep it to ourselves in case it is asked
    706             ntlmChallenge = ntlm.NTLMAuthChallenge(respToken['ResponseToken'])
    707             if ntlmChallenge['TargetInfoFields_len'] > 0:
    708                 av_pairs = ntlm.AV_PAIRS(ntlmChallenge['TargetInfoFields'][:ntlmChallenge['TargetInfoFields_len']])
    709                 if av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] is not None:
    710                    try:
    711                        self._Session['ServerName'] = av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1].decode('utf-16le')
    712                    except:
    713                        # For some reason, we couldn't decode Unicode here.. silently discard the operation
    714                        pass 
    715                 if av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] is not None:
    716                    try:
    717                        if self._Session['ServerName'] != av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le'): 
    718                            self._Session['ServerDomain'] = av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le')
    719                    except:
    720                        # For some reason, we couldn't decode Unicode here.. silently discard the operation
    721                        pass 
    722                 if av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] is not None:
    723                    try:
    724                        self._Session['ServerDNSDomainName'] = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME][1].decode('utf-16le')
    725                    except:
    726                        # For some reason, we couldn't decode Unicode here.. silently discard the operation
    727                        pass 
    728 
    729                 # Parse Version to know the target Operating system name. Not provided elsewhere anymore
    730                 if ntlmChallenge.fields.has_key('Version'):
    731                     version = ntlmChallenge['Version']
    732 
    733                     if len(version) >= 4:
    734                         self._Session['ServerOS'] = "Windows %d.%d Build %d" % (ord(version[0]), ord(version[1]), struct.unpack('<H',version[2:4])[0])
    735                         self._Session["ServerOSMajor"] = ord(version[0])
    736                         self._Session["ServerOSMinor"] = ord(version[1])
    737                         self._Session["ServerOSBuild"] = struct.unpack('<H',version[2:4])[0]
    738 
    739             type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, respToken['ResponseToken'], user, password, domain, lmhash, nthash)
    740    
    741             if exportedSessionKey is not None: 
    742                 self._Session['SessionKey']  = exportedSessionKey
    743                 if self._Session['SigningRequired'] is True and self._Connection['Dialect'] == SMB2_DIALECT_30:
    744                     self._Session['SigningKey']  = crypto.KDF_CounterMode(exportedSessionKey, "SMB2AESCMAC\x00", "SmbSign\x00", 128)
    745 
    746             respToken2 = SPNEGO_NegTokenResp()
    747             respToken2['ResponseToken'] = str(type3)
    748 
    749             # Reusing the previous structure
    750             sessionSetup['SecurityBufferLength'] = len(respToken2)
    751             sessionSetup['Buffer']               = respToken2.getData()
    752 
    753             packetID = self.sendSMB(packet)
    754             packet = self.recvSMB(packetID)
    755             try:
    756                 if packet.isValidAnswer(STATUS_SUCCESS):
    757                     sessionSetupResponse = SMB2SessionSetup_Response(packet['Data'])
    758                     self._Session['SessionFlags'] = sessionSetupResponse['SessionFlags']
    759 
    760                     # Calculate the key derivations for dialect 3.0
    761                     if self._Session['SigningRequired'] is True:
    762                         self._Session['SigningActivated'] = True
    763                     if self._Connection['Dialect'] == SMB2_DIALECT_30:
    764                         self._Session['ApplicationKey']  = crypto.KDF_CounterMode(exportedSessionKey, "SMB2APP\x00", "SmbRpc\x00", 128)
    765                         self._Session['EncryptionKey']   = crypto.KDF_CounterMode(exportedSessionKey, "SMB2AESCCM\x00", "ServerIn \x00", 128)
    766                         self._Session['DecryptionKey']   = crypto.KDF_CounterMode(exportedSessionKey, "SMB2AESCCM\x00", "ServerOut\x00", 128)
    767  
    768                     return True
    769             except:
    770                 # We clean the stuff we used in case we want to authenticate again
    771                 # within the same connection
    772                 self._Session['UserCredentials']   = ''
    773                 self._Session['Connection']        = 0
    774                 self._Session['SessionID']         = 0
    775                 self._Session['SigningRequired']   = False
    776                 self._Session['SigningKey']        = ''
    777                 self._Session['SessionKey']        = ''
    778                 self._Session['SigningActivated']  = False
    779                 raise
    780 
    781     def connectTree(self, share):
    782 
    783         # Just in case this came with the full path (maybe an SMB1 client), let's just leave 
    784         # the sharename, we'll take care of the rest
    785 
    786         #print self._Session['TreeConnectTable']
    787         share = share.split('\\')[-1]
    788         if self._Session['TreeConnectTable'].has_key(share):
    789             # Already connected, no need to reconnect
    790             treeEntry =  self._Session['TreeConnectTable'][share]
    791             treeEntry['NumberOfUses'] += 1
    792             self._Session['TreeConnectTable'][treeEntry['TreeConnectId']]['NumberOfUses'] += 1
    793             return treeEntry['TreeConnectId']
    794 
    795         #path = share
    796         try:
    797             _, _, _, _, sockaddr = socket.getaddrinfo(self._Connection['ServerIP'], 80, 0, 0, socket.IPPROTO_TCP)[0]
    798             remoteHost = sockaddr[0]
    799         except:
    800             remoteHost = self._Connection['ServerIP']
    801         path = '\\\\' + remoteHost + '\\' +share
    802 
    803         treeConnect = SMB2TreeConnect()
    804         treeConnect['Buffer']     = path.encode('utf-16le')
    805         treeConnect['PathLength'] = len(path)*2
    806          
    807         packet = self.SMB_PACKET()
    808         packet['Command'] = SMB2_TREE_CONNECT
    809         packet['Data'] = treeConnect
    810         packetID = self.sendSMB(packet)
    811         packet = self.recvSMB(packetID)
    812         if packet.isValidAnswer(STATUS_SUCCESS):
    813            treeConnectResponse = SMB2TreeConnect_Response(packet['Data'])
    814            treeEntry = copy.deepcopy(TREE_CONNECT)
    815            treeEntry['ShareName']     = share
    816            treeEntry['TreeConnectId'] = packet['TreeID']
    817            treeEntry['Session']       = packet['SessionID']
    818            treeEntry['NumberOfUses'] += 1
    819            if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_DFS) == SMB2_SHARE_CAP_DFS:
    820                treeEntry['IsDfsShare'] = True
    821            if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) == SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY:
    822                treeEntry['IsCAShare'] = True
    823 
    824            if self._Connection['Dialect'] == SMB2_DIALECT_30:
    825                if (self._Connection['SupportsEncryption'] is True) and ((treeConnectResponse['ShareFlags'] & SMB2_SHAREFLAG_ENCRYPT_DATA) == SMB2_SHAREFLAG_ENCRYPT_DATA):
    826                    treeEntry['EncryptData'] = True
    827                    # ToDo: This and what follows
    828                    # If Session.EncryptData is FALSE, the client MUST then generate an encryption key, a
    829                    # decryption key as specified in section 3.1.4.2, by providing the following inputs and store
    830                    # them in Session.EncryptionKey and Session.DecryptionKey:
    831                if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_SCALEOUT) == SMB2_SHARE_CAP_SCALEOUT:
    832                    treeEntry['IsScaleoutShare'] = True
    833 
    834            self._Session['TreeConnectTable'][packet['TreeID']] = treeEntry
    835            self._Session['TreeConnectTable'][share]            = treeEntry
    836 
    837            return packet['TreeID'] 
    838 
    839     def disconnectTree(self, treeId):
    840         if self._Session['TreeConnectTable'].has_key(treeId) is False:
    841             raise SessionError(STATUS_INVALID_PARAMETER)
    842 
    843         if self._Session['TreeConnectTable'].has_key(treeId):
    844             # More than 1 use? descrease it and return, if not, send the packet
    845             if self._Session['TreeConnectTable'][treeId]['NumberOfUses'] > 1:
    846                 treeEntry =  self._Session['TreeConnectTable'][treeId]
    847                 treeEntry['NumberOfUses'] -= 1
    848                 self._Session['TreeConnectTable'][treeEntry['ShareName']]['NumberOfUses'] -= 1
    849                 return True
    850 
    851         packet = self.SMB_PACKET()
    852         packet['Command'] = SMB2_TREE_DISCONNECT
    853         packet['TreeID'] = treeId
    854         treeDisconnect = SMB2TreeDisconnect()
    855         packet['Data'] = treeDisconnect
    856         packetID = self.sendSMB(packet)
    857         packet = self.recvSMB(packetID)
    858         if packet.isValidAnswer(STATUS_SUCCESS):
    859             shareName = self._Session['TreeConnectTable'][treeId]['ShareName']
    860             del(self._Session['TreeConnectTable'][shareName])
    861             del(self._Session['TreeConnectTable'][treeId])
    862             return True
    863 
    864     def create(self, treeId, fileName, desiredAccess, shareMode, creationOptions, creationDisposition, fileAttributes, impersonationLevel = SMB2_IL_IMPERSONATION, securityFlags = 0, oplockLevel = SMB2_OPLOCK_LEVEL_NONE, createContexts = None):
    865         if self._Session['TreeConnectTable'].has_key(treeId) is False:
    866             raise SessionError(STATUS_INVALID_PARAMETER)
    867 
    868         fileName = string.replace(fileName, '/', '\\')
    869         if len(fileName) > 0:
    870             fileName = ntpath.normpath(fileName)
    871             if fileName[0] == '\\':
    872                 fileName = fileName[1:]
    873 
    874         if self._Session['TreeConnectTable'][treeId]['IsDfsShare'] is True:
    875             pathName = fileName
    876         else:
    877             pathName = '\\\\' + self._Connection['ServerName'] + '\\' + fileName
    878 
    879         fileEntry = copy.deepcopy(FILE)
    880         fileEntry['LeaseKey']   = uuid.generate()
    881         fileEntry['LeaseState'] = SMB2_LEASE_NONE
    882         self.GlobalFileTable[pathName] = fileEntry 
    883 
    884         if self._Connection['Dialect'] == SMB2_DIALECT_30 and self._Connection['SupportsDirectoryLeasing'] is True:
    885            # Is this file NOT on the root directory?
    886            if len(fileName.split('\\')) > 2:
    887                parentDir = ntpath.dirname(pathName)
    888            if self.GlobalFileTable.has_key(parentDir):
    889                LOG.critical("Don't know what to do now! :-o")
    890                raise
    891            else:
    892                parentEntry = copy.deepcopy(FILE)
    893                parentEntry['LeaseKey']   = uuid.generate()
    894                parentEntry['LeaseState'] = SMB2_LEASE_NONE 
    895                self.GlobalFileTable[parentDir] = parentEntry 
    896                
    897         packet = self.SMB_PACKET()
    898         packet['Command'] = SMB2_CREATE
    899         packet['TreeID']  = treeId
    900         if self._Session['TreeConnectTable'][treeId]['IsDfsShare'] is True:
    901             packet['Flags'] = SMB2_FLAGS_DFS_OPERATIONS
    902 
    903         smb2Create = SMB2Create()
    904         smb2Create['SecurityFlags']        = 0
    905         smb2Create['RequestedOplockLevel'] = oplockLevel
    906         smb2Create['ImpersonationLevel']   = impersonationLevel
    907         smb2Create['DesiredAccess']        = desiredAccess
    908         smb2Create['FileAttributes']       = fileAttributes
    909         smb2Create['ShareAccess']          = shareMode
    910         smb2Create['CreateDisposition']    = creationDisposition
    911         smb2Create['CreateOptions']        = creationOptions
    912        
    913         smb2Create['NameLength']           = len(fileName)*2
    914         if fileName != '':
    915             smb2Create['Buffer']               = fileName.encode('utf-16le')
    916         else:
    917             smb2Create['Buffer']               = '\x00'
    918 
    919         if createContexts is not None:
    920             smb2Create['Buffer'] += createContexts
    921             smb2Create['CreateContextsOffset'] = len(SMB2Packet()) + SMB2Create.SIZE + smb2Create['NameLength']
    922             smb2Create['CreateContextsLength'] = len(createContexts)
    923         else:
    924             smb2Create['CreateContextsOffset'] = 0
    925             smb2Create['CreateContextsLength'] = 0
    926 
    927         packet['Data'] = smb2Create
    928 
    929         packetID = self.sendSMB(packet)
    930         ans = self.recvSMB(packetID)
    931         if ans.isValidAnswer(STATUS_SUCCESS):
    932             createResponse = SMB2Create_Response(ans['Data'])
    933 
    934             openFile = copy.deepcopy(OPEN)
    935             openFile['FileID']      = createResponse['FileID']
    936             openFile['TreeConnect'] = treeId
    937             openFile['Oplocklevel'] = oplockLevel
    938             openFile['Durable']     = False
    939             openFile['ResilientHandle']    = False
    940             openFile['LastDisconnectTime'] = 0
    941             openFile['FileName'] = pathName
    942 
    943             # ToDo: Complete the OperationBuckets
    944             if self._Connection['Dialect'] == SMB2_DIALECT_30:
    945                 openFile['DesiredAccess']     = oplockLevel
    946                 openFile['ShareMode']         = oplockLevel
    947                 openFile['CreateOptions']     = oplockLevel
    948                 openFile['FileAttributes']    = oplockLevel
    949                 openFile['CreateDisposition'] = oplockLevel
    950 
    951             # ToDo: Process the contexts            
    952             self._Session['OpenTable'][str(createResponse['FileID'])] = openFile
    953 
    954             # The client MUST generate a handle for the Open, and it MUST 
    955             # return success and the generated handle to the calling application.
    956             # In our case, str(FileID)
    957             return str(createResponse['FileID'])
    958 
    959     def close(self, treeId, fileId):
    960         if self._Session['TreeConnectTable'].has_key(treeId) is False:
    961             raise SessionError(STATUS_INVALID_PARAMETER)
    962         if self._Session['OpenTable'].has_key(fileId) is False:
    963             raise SessionError(STATUS_INVALID_PARAMETER)
    964 
    965         packet = self.SMB_PACKET()
    966         packet['Command'] = SMB2_CLOSE
    967         packet['TreeID']  = treeId
    968 
    969         smbClose = SMB2Close()
    970         smbClose['Flags']  = 0
    971         smbClose['FileID'] = fileId
    972         
    973         packet['Data'] = smbClose
    974 
    975         packetID = self.sendSMB(packet)
    976         ans = self.recvSMB(packetID)
    977 
    978         if ans.isValidAnswer(STATUS_SUCCESS):
    979             del(self.GlobalFileTable[self._Session['OpenTable'][fileId]['FileName']])
    980             del(self._Session['OpenTable'][fileId])
    981              
    982             # ToDo Remove stuff from GlobalFileTable
    983             return True
    984 
    985     def read(self, treeId, fileId, offset = 0, bytesToRead = 0, waitAnswer = True):
    986         # IMPORTANT NOTE: As you can see, this was coded as a recursive function
    987         # Hence, you can exhaust the memory pretty easy ( large bytesToRead )
    988         # This function should NOT be used for reading files directly, but another higher
    989         # level function should be used that will break the read into smaller pieces
    990 
    991         if self._Session['TreeConnectTable'].has_key(treeId) is False:
    992             raise SessionError(STATUS_INVALID_PARAMETER)
    993         if self._Session['OpenTable'].has_key(fileId) is False:
    994             raise SessionError(STATUS_INVALID_PARAMETER)
    995 
    996         packet = self.SMB_PACKET()
    997         packet['Command'] = SMB2_READ
    998         packet['TreeID']  = treeId
    999 
   1000         if self._Connection['MaxReadSize'] < bytesToRead:
   1001             maxBytesToRead = self._Connection['MaxReadSize']
   1002         else: 
   1003             maxBytesToRead = bytesToRead
   1004 
   1005         if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True:
   1006             packet['CreditCharge'] = ( 1 + (maxBytesToRead - 1) / 65536)
   1007         else: 
   1008             maxBytesToRead = min(65536,bytesToRead)
   1009 
   1010         smbRead = SMB2Read()
   1011         smbRead['Padding']  = 0x50
   1012         smbRead['FileID']   = fileId
   1013         smbRead['Length']   = maxBytesToRead
   1014         smbRead['Offset']   = offset
   1015         packet['Data'] = smbRead
   1016 
   1017         packetID = self.sendSMB(packet)
   1018         ans = self.recvSMB(packetID)
   1019 
   1020         if ans.isValidAnswer(STATUS_SUCCESS):
   1021             readResponse = SMB2Read_Response(ans['Data'])
   1022             retData = readResponse['Buffer']
   1023             if readResponse['DataRemaining'] > 0:
   1024                 retData += self.read(treeId, fileId, offset+len(retData), readResponse['DataRemaining'], waitAnswer)
   1025             return retData
   1026        
   1027     def write(self, treeId, fileId, data, offset = 0, bytesToWrite = 0, waitAnswer = True):
   1028         # IMPORTANT NOTE: As you can see, this was coded as a recursive function
   1029         # Hence, you can exhaust the memory pretty easy ( large bytesToWrite )
   1030         # This function should NOT be used for writing directly to files, but another higher
   1031         # level function should be used that will break the writes into smaller pieces
   1032 
   1033         if self._Session['TreeConnectTable'].has_key(treeId) is False:
   1034             raise SessionError(STATUS_INVALID_PARAMETER)
   1035         if self._Session['OpenTable'].has_key(fileId) is False:
   1036             raise SessionError(STATUS_INVALID_PARAMETER)
   1037 
   1038         packet = self.SMB_PACKET()
   1039         packet['Command'] = SMB2_WRITE
   1040         packet['TreeID']  = treeId
   1041 
   1042         if self._Connection['MaxWriteSize'] < bytesToWrite:
   1043             maxBytesToWrite = self._Connection['MaxWriteSize']
   1044         else: 
   1045             maxBytesToWrite = bytesToWrite
   1046 
   1047         if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True:
   1048             packet['CreditCharge'] = ( 1 + (maxBytesToWrite - 1) / 65536)
   1049         else: 
   1050             maxBytesToWrite = min(65536,bytesToWrite)
   1051 
   1052         smbWrite = SMB2Write()
   1053         smbWrite['FileID'] = fileId
   1054         smbWrite['Length'] = maxBytesToWrite
   1055         smbWrite['Offset'] = offset
   1056         smbWrite['WriteChannelInfoOffset'] = 0
   1057         smbWrite['Buffer'] = data[:maxBytesToWrite]
   1058         packet['Data'] = smbWrite
   1059 
   1060         packetID = self.sendSMB(packet)
   1061         if waitAnswer is True:
   1062             ans = self.recvSMB(packetID)
   1063         else:
   1064             return maxBytesToWrite
   1065 
   1066         if ans.isValidAnswer(STATUS_SUCCESS):
   1067             writeResponse = SMB2Write_Response(ans['Data'])
   1068             bytesWritten = writeResponse['Count']
   1069             if bytesWritten < bytesToWrite:
   1070                 bytesWritten += self.write(treeId, fileId, data[bytesWritten:], offset+bytesWritten, bytesToWrite-bytesWritten, waitAnswer)
   1071             return bytesWritten
   1072 
   1073     def queryDirectory(self, treeId, fileId, searchString = '*', resumeIndex = 0, informationClass = FILENAMES_INFORMATION, maxBufferSize = None, enumRestart = False, singleEntry = False):
   1074         if self._Session['TreeConnectTable'].has_key(treeId) is False:
   1075             raise SessionError(STATUS_INVALID_PARAMETER)
   1076         if self._Session['OpenTable'].has_key(fileId) is False:
   1077             raise SessionError(STATUS_INVALID_PARAMETER)
   1078 
   1079         packet = self.SMB_PACKET()
   1080         packet['Command'] = SMB2_QUERY_DIRECTORY
   1081         packet['TreeID']  = treeId
   1082 
   1083         queryDirectory = SMB2QueryDirectory()
   1084         queryDirectory['FileInformationClass'] = informationClass
   1085         if resumeIndex != 0 :
   1086             queryDirectory['Flags'] = SMB2_INDEX_SPECIFIED
   1087         queryDirectory['FileIndex'] = resumeIndex
   1088         queryDirectory['FileID']    = fileId
   1089         if maxBufferSize is None:
   1090             maxBufferSize = self._Connection['MaxReadSize']
   1091         queryDirectory['OutputBufferLength'] = maxBufferSize
   1092         queryDirectory['FileNameLength']     = len(searchString)*2
   1093         queryDirectory['Buffer']             = searchString.encode('utf-16le')
   1094 
   1095         packet['Data'] = queryDirectory
   1096 
   1097         if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True:
   1098             packet['CreditCharge'] = ( 1 + (maxBufferSize - 1) / 65536)
   1099 
   1100         packetID = self.sendSMB(packet)
   1101         ans = self.recvSMB(packetID)
   1102         if ans.isValidAnswer(STATUS_SUCCESS):
   1103             queryDirectoryResponse = SMB2QueryDirectory_Response(ans['Data'])
   1104             return queryDirectoryResponse['Buffer']
   1105 
   1106     def echo(self):
   1107         packet = self.SMB_PACKET()
   1108         packet['Command'] = SMB2_ECHO
   1109         smbEcho = SMB2Echo()
   1110         packet['Data'] = smbEcho
   1111         packetID = self.sendSMB(packet)
   1112         ans = self.recvSMB(packetID)
   1113         if ans.isValidAnswer(STATUS_SUCCESS):
   1114             return True
   1115 
   1116     def cancel(self, packetID):
   1117         packet = self.SMB_PACKET()
   1118         packet['Command']   = SMB2_CANCEL
   1119         packet['MessageID'] = packetID
   1120 
   1121         smbCancel = SMB2Cancel()
   1122 
   1123         packet['Data']      = smbCancel
   1124         self.sendSMB(packet)
   1125 
   1126     def ioctl(self, treeId, fileId = None, ctlCode = -1, flags = 0, inputBlob = '',  maxInputResponse = None, maxOutputResponse = None, waitAnswer = 1):
   1127         if self._Session['TreeConnectTable'].has_key(treeId) is False:
   1128             raise SessionError(STATUS_INVALID_PARAMETER)
   1129         if fileId is None:
   1130             fileId = '\xff'*16
   1131         else:
   1132             if self._Session['OpenTable'].has_key(fileId) is False:
   1133                 raise SessionError(STATUS_INVALID_PARAMETER)
   1134 
   1135         packet = self.SMB_PACKET()
   1136         packet['Command']            = SMB2_IOCTL
   1137         packet['TreeID']             = treeId
   1138        
   1139         smbIoctl = SMB2Ioctl()
   1140         smbIoctl['FileID']             = fileId
   1141         smbIoctl['CtlCode']            = ctlCode
   1142         smbIoctl['MaxInputResponse']   = maxInputResponse
   1143         smbIoctl['MaxOutputResponse']  = maxOutputResponse
   1144         smbIoctl['InputCount']         = len(inputBlob)
   1145         if len(inputBlob) == 0:
   1146             smbIoctl['InputOffset'] = 0
   1147             smbIoctl['Buffer']      = '\x00'
   1148         else:
   1149             smbIoctl['Buffer']             = inputBlob
   1150         smbIoctl['OutputOffset']       = 0
   1151         smbIoctl['MaxOutputResponse']  = maxOutputResponse
   1152         smbIoctl['Flags']              = flags
   1153 
   1154         packet['Data'] = smbIoctl
   1155  
   1156         packetID = self.sendSMB(packet)
   1157 
   1158         if waitAnswer == 0:
   1159             return True
   1160 
   1161         ans = self.recvSMB(packetID)
   1162 
   1163         if ans.isValidAnswer(STATUS_SUCCESS):
   1164             smbIoctlResponse = SMB2Ioctl_Response(ans['Data'])
   1165             return smbIoctlResponse['Buffer']
   1166 
   1167     def flush(self,treeId, fileId):
   1168         if self._Session['TreeConnectTable'].has_key(treeId) is False:
   1169             raise SessionError(STATUS_INVALID_PARAMETER)
   1170         if self._Session['OpenTable'].has_key(fileId) is False:
   1171             raise SessionError(STATUS_INVALID_PARAMETER)
   1172 
   1173         packet = self.SMB_PACKET()
   1174         packet['Command'] = SMB2_FLUSH
   1175         packet['TreeID']  = treeId
   1176 
   1177         smbFlush = SMB2Flush()
   1178         smbFlush['FileID'] = fileId
   1179 
   1180         packet['Data'] = smbFlush
   1181 
   1182         packetID = self.sendSMB(packet)
   1183         ans = self.recvSMB(packetID)
   1184 
   1185         if ans.isValidAnswer(STATUS_SUCCESS):
   1186             return True
   1187 
   1188     def lock(self, treeId, fileId, locks, lockSequence = 0):
   1189         if self._Session['TreeConnectTable'].has_key(treeId) is False:
   1190             raise SessionError(STATUS_INVALID_PARAMETER)
   1191         if self._Session['OpenTable'].has_key(fileId) is False:
   1192             raise SessionError(STATUS_INVALID_PARAMETER)
   1193 
   1194         packet = self.SMB_PACKET()
   1195         packet['Command'] = SMB2_LOCK
   1196         packet['TreeID']  = treeId
   1197 
   1198         smbLock = SMB2Lock()
   1199         smbLock['FileID']       = fileId
   1200         smbLock['LockCount']    = len(locks)
   1201         smbLock['LockSequence'] = lockSequence
   1202         smbLock['Locks']        = ''.join(str(x) for x in locks)
   1203 
   1204         packet['Data'] = smbLock
   1205 
   1206         packetID = self.sendSMB(packet)
   1207         ans = self.recvSMB(packetID)
   1208 
   1209         if ans.isValidAnswer(STATUS_SUCCESS):
   1210             smbFlushResponse = SMB2Lock_Response(ans['Data'])
   1211             return True
   1212 
   1213         # ToDo:
   1214         # If Open.ResilientHandle is TRUE or Connection.SupportsMultiChannel is TRUE, the client MUST
   1215         # do the following:
   1216         # The client MUST scan through Open.OperationBuckets and find an element with its Free field
   1217         # set to TRUE. If no such element could be found, an implementation-specific error MUST be
   1218         # returned to the application.
   1219         # Let the zero-based array index of the element chosen above be referred to as BucketIndex, and
   1220         # let BucketNumber = BucketIndex +1.
   1221         # Set Open.OperationBuckets[BucketIndex].Free = FALSE
   1222         # Let the SequenceNumber of the element chosen above be referred to as BucketSequence.
   1223         # The LockSequence field of the SMB2 lock request MUST be set to (BucketNumber<< 4) +
   1224         # BucketSequence.
   1225         # Increment the SequenceNumber of the element chosen above using MOD 16 arithmetic.
   1226 
   1227     def logoff(self):
   1228         packet = self.SMB_PACKET()
   1229         packet['Command'] = SMB2_LOGOFF
   1230 
   1231         smbLogoff = SMB2Logoff()
   1232 
   1233         packet['Data'] = smbLogoff
   1234 
   1235         packetID = self.sendSMB(packet)
   1236         ans = self.recvSMB(packetID)
   1237 
   1238         if ans.isValidAnswer(STATUS_SUCCESS):
   1239             # We clean the stuff we used in case we want to authenticate again
   1240             # within the same connection
   1241             self._Session['UserCredentials']   = ''
   1242             self._Session['Connection']        = 0
   1243             self._Session['SessionID']         = 0
   1244             self._Session['SigningRequired']   = False
   1245             self._Session['SigningKey']        = ''
   1246             self._Session['SessionKey']        = ''
   1247             self._Session['SigningActivated']  = False
   1248             return True
   1249 
   1250     def queryInfo(self, treeId, fileId, inputBlob = '', infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_STANDARD_INFO, additionalInformation = 0, flags = 0 ):
   1251         if self._Session['TreeConnectTable'].has_key(treeId) is False:
   1252             raise SessionError(STATUS_INVALID_PARAMETER)
   1253         if self._Session['OpenTable'].has_key(fileId) is False:
   1254             raise SessionError(STATUS_INVALID_PARAMETER)
   1255 
   1256         packet = self.SMB_PACKET()
   1257         packet['Command'] = SMB2_QUERY_INFO
   1258         packet['TreeID']  = treeId
   1259 
   1260         queryInfo = SMB2QueryInfo()
   1261         queryInfo['FileID']                = fileId
   1262         queryInfo['InfoType']              = SMB2_0_INFO_FILE 
   1263         queryInfo['FileInfoClass']         = fileInfoClass 
   1264         queryInfo['OutputBufferLength']    = 65535
   1265         queryInfo['AdditionalInformation'] = additionalInformation
   1266         if len(inputBlob) == 0:
   1267             queryInfo['InputBufferOffset'] = 0
   1268             queryInfo['Buffer']            = '\x00'
   1269         else:
   1270             queryInfo['InputBufferLength'] = len(inputBlob)
   1271             queryInfo['Buffer']            = inputBlob
   1272         queryInfo['Flags']                 = flags
   1273 
   1274         packet['Data'] = queryInfo
   1275         packetID = self.sendSMB(packet)
   1276         ans = self.recvSMB(packetID)
   1277 
   1278         if ans.isValidAnswer(STATUS_SUCCESS):
   1279             queryResponse = SMB2QueryInfo_Response(ans['Data'])
   1280             return queryResponse['Buffer']
   1281 
   1282     def setInfo(self, treeId, fileId, inputBlob = '', infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_STANDARD_INFO, additionalInformation = 0 ):
   1283         if self._Session['TreeConnectTable'].has_key(treeId) is False:
   1284             raise SessionError(STATUS_INVALID_PARAMETER)
   1285         if self._Session['OpenTable'].has_key(fileId) is False:
   1286             raise SessionError(STATUS_INVALID_PARAMETER)
   1287 
   1288         packet = self.SMB_PACKET()
   1289         packet['Command'] = SMB2_SET_INFO
   1290         packet['TreeID']  = treeId
   1291 
   1292         setInfo = SMB2SetInfo()
   1293         setInfo['InfoType']              = SMB2_0_INFO_FILE 
   1294         setInfo['FileInfoClass']         = fileInfoClass 
   1295         setInfo['BufferLength']          = len(inputBlob)
   1296         setInfo['AdditionalInformation'] = additionalInformation
   1297         setInfo['FileID']                = fileId
   1298         setInfo['Buffer']                = inputBlob
   1299 
   1300         packet['Data'] = setInfo
   1301         packetID = self.sendSMB(packet)
   1302         ans = self.recvSMB(packetID)
   1303 
   1304         if ans.isValidAnswer(STATUS_SUCCESS):
   1305             return True
   1306 
   1307     def getSessionKey(self):
   1308         if self.getDialect() == SMB2_DIALECT_30: 
   1309            return self._Session['ApplicationKey']
   1310         else:
   1311            return self._Session['SessionKey']
   1312 
   1313     def setSessionKey(self, key):
   1314         if self.getDialect() == SMB2_DIALECT_30:
   1315            self._Session['ApplicationKey'] = key
   1316         else:
   1317            self._Session['SessionKey'] = key
   1318 
   1319     ######################################################################
   1320     # Higher level functions
   1321 
   1322     def rename(self, shareName, oldPath, newPath):
   1323         oldPath = string.replace(oldPath,'/', '\\')
   1324         oldPath = ntpath.normpath(oldPath)
   1325         if len(oldPath) > 0 and oldPath[0] == '\\':
   1326             oldPath = oldPath[1:]
   1327 
   1328         newPath = string.replace(newPath,'/', '\\')
   1329         newPath = ntpath.normpath(newPath)
   1330         if len(newPath) > 0 and newPath[0] == '\\':
   1331             newPath = newPath[1:]
   1332 
   1333         treeId = self.connectTree(shareName)
   1334         fileId = None
   1335         try:
   1336             fileId = self.create(treeId, oldPath, MAXIMUM_ALLOWED ,FILE_SHARE_READ | FILE_SHARE_WRITE |FILE_SHARE_DELETE, 0x200020, FILE_OPEN, 0) 
   1337             renameReq = FILE_RENAME_INFORMATION_TYPE_2()
   1338             renameReq['ReplaceIfExists'] = 1
   1339             renameReq['RootDirectory']   = '\x00'*8
   1340             renameReq['FileNameLength']  = len(newPath)*2
   1341             renameReq['FileName']        = newPath.encode('utf-16le')
   1342             self.setInfo(treeId, fileId, renameReq, infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_RENAME_INFO)
   1343         finally:
   1344             if fileId is not None:
   1345                 self.close(treeId, fileId)
   1346             self.disconnectTree(treeId) 
   1347 
   1348         return True
   1349 
   1350     def writeFile(self, treeId, fileId, data, offset = 0):
   1351         finished = False
   1352         writeOffset = offset
   1353         while not finished:
   1354             if len(data) == 0:
   1355                 break
   1356             writeData = data[:self._Connection['MaxWriteSize']]
   1357             data = data[self._Connection['MaxWriteSize']:]
   1358             written = self.write(treeId, fileId, writeData, writeOffset, len(writeData))
   1359             writeOffset += written
   1360         return writeOffset - offset
   1361 
   1362     def listPath(self, shareName, path, password = None):
   1363         # ToDo: Handle situations where share is password protected
   1364         path = string.replace(path,'/', '\\')
   1365         path = ntpath.normpath(path)
   1366         if len(path) > 0 and path[0] == '\\':
   1367             path = path[1:]
   1368 
   1369         treeId = self.connectTree(shareName)
   1370 
   1371         fileId = None
   1372         try:
   1373             # ToDo, we're assuming it's a directory, we should check what the file type is
   1374             fileId = self.create(treeId, ntpath.dirname(path), FILE_READ_ATTRIBUTES | FILE_READ_DATA ,FILE_SHARE_READ | FILE_SHARE_WRITE |FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN, 0) 
   1375             res = ''
   1376             files = []
   1377             from impacket import smb
   1378             while True:
   1379                 try:
   1380                     res = self.queryDirectory( treeId, fileId, ntpath.basename(path), maxBufferSize = 65535, informationClass = FILE_FULL_DIRECTORY_INFORMATION )
   1381                     nextOffset = 1
   1382                     while nextOffset != 0:
   1383                         fileInfo = smb.SMBFindFileFullDirectoryInfo(smb.SMB.FLAGS2_UNICODE)
   1384                         fileInfo.fromString(res)
   1385                         files.append(smb.SharedFile(fileInfo['CreationTime'],fileInfo['LastAccessTime'],fileInfo['LastChangeTime'],fileInfo['EndOfFile'],fileInfo['AllocationSize'],fileInfo['ExtFileAttributes'],fileInfo['FileName'].decode('utf-16le'), fileInfo['FileName'].decode('utf-16le')))
   1386                         nextOffset = fileInfo['NextEntryOffset']
   1387                         res = res[nextOffset:]
   1388                 except SessionError, e:
   1389                     if (e.get_error_code()) != STATUS_NO_MORE_FILES:
   1390                         raise
   1391                     break 
   1392         finally:
   1393             if fileId is not None:
   1394                 self.close(treeId, fileId)
   1395             self.disconnectTree(treeId) 
   1396 
   1397         return files
   1398 
   1399     def mkdir(self, shareName, pathName, password = None):
   1400         # ToDo: Handle situations where share is password protected
   1401         pathName = string.replace(pathName,'/', '\\')
   1402         pathName = ntpath.normpath(pathName)
   1403         if len(pathName) > 0 and pathName[0] == '\\':
   1404             pathName = pathName[1:]
   1405 
   1406         treeId = self.connectTree(shareName)
   1407 
   1408         fileId = None
   1409         try:
   1410             fileId = self.create(treeId, pathName,GENERIC_ALL ,FILE_SHARE_READ | FILE_SHARE_WRITE |FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_CREATE, 0)          
   1411         finally:
   1412             if fileId is not None:
   1413                 self.close(treeId, fileId)            
   1414             self.disconnectTree(treeId) 
   1415 
   1416         return True
   1417 
   1418     def rmdir(self, shareName, pathName, password = None):
   1419         # ToDo: Handle situations where share is password protected
   1420         pathName = string.replace(pathName,'/', '\\')
   1421         pathName = ntpath.normpath(pathName)
   1422         if len(pathName) > 0 and pathName[0] == '\\':
   1423             pathName = pathName[1:]
   1424 
   1425         treeId = self.connectTree(shareName)
   1426 
   1427         fileId = None
   1428         try:
   1429             fileId = self.create(treeId, pathName, DELETE, FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE, FILE_OPEN, 0)
   1430         finally:
   1431             if fileId is not None:
   1432                 self.close(treeId, fileId)
   1433             self.disconnectTree(treeId) 
   1434 
   1435         return True
   1436 
   1437     def remove(self, shareName, pathName, password = None):
   1438         # ToDo: Handle situations where share is password protected
   1439         pathName = string.replace(pathName,'/', '\\')
   1440         pathName = ntpath.normpath(pathName)
   1441         if len(pathName) > 0 and pathName[0] == '\\':
   1442             pathName = pathName[1:]
   1443 
   1444         treeId = self.connectTree(shareName)
   1445 
   1446         fileId = None
   1447         try:
   1448             fileId = self.create(treeId, pathName,DELETE | FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE, FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE, FILE_OPEN, 0)
   1449         finally:
   1450             if fileId is not None:
   1451                 self.close(treeId, fileId)
   1452             self.disconnectTree(treeId) 
   1453 
   1454         return True
   1455 
   1456     def retrieveFile(self, shareName, path, callback, mode = FILE_OPEN, offset = 0, password = None, shareAccessMode = FILE_SHARE_READ):
   1457         # ToDo: Handle situations where share is password protected
   1458         path = string.replace(path,'/', '\\')
   1459         path = ntpath.normpath(path)
   1460         if len(path) > 0 and path[0] == '\\':
   1461             path = path[1:]
   1462 
   1463         treeId = self.connectTree(shareName)
   1464         fileId = None
   1465         from impacket import smb
   1466         try:
   1467             fileId = self.create(treeId, path, FILE_READ_DATA, shareAccessMode, FILE_NON_DIRECTORY_FILE, mode, 0)
   1468             res = self.queryInfo(treeId, fileId)
   1469             fileInfo = smb.SMBQueryFileStandardInfo(res)
   1470             fileSize = fileInfo['EndOfFile']
   1471             if (fileSize-offset) < self._Connection['MaxReadSize']:
   1472                 # Skip reading 0 bytes files. 
   1473                 if (fileSize-offset) > 0:
   1474                     data = self.read(treeId, fileId, offset, fileSize-offset)
   1475                     callback(data)
   1476             else:
   1477                 written = 0
   1478                 toBeRead = fileSize-offset
   1479                 while written < toBeRead:
   1480                     data = self.read(treeId, fileId, offset, self._Connection['MaxReadSize'])
   1481                     written += len(data)
   1482                     offset  += len(data)
   1483                     callback(data)
   1484         finally:
   1485             if fileId is not None:
   1486                 self.close(treeId, fileId)
   1487             self.disconnectTree(treeId) 
   1488 
   1489     def storeFile(self, shareName, path, callback, mode = FILE_OVERWRITE_IF, offset = 0, password = None, shareAccessMode = FILE_SHARE_WRITE):
   1490         # ToDo: Handle situations where share is password protected
   1491         path = string.replace(path,'/', '\\')
   1492         path = ntpath.normpath(path)
   1493         if len(path) > 0 and path[0] == '\\':
   1494             path = path[1:]
   1495 
   1496         treeId = self.connectTree(shareName)
   1497         fileId = None
   1498         try:
   1499             fileId = self.create(treeId, path, FILE_WRITE_DATA, shareAccessMode, FILE_NON_DIRECTORY_FILE, mode, 0)
   1500             finished = False
   1501             writeOffset = offset
   1502             while not finished:
   1503                 data = callback(self._Connection['MaxWriteSize'])
   1504                 if len(data) == 0:
   1505                     break
   1506                 written = self.write(treeId, fileId, data, writeOffset, len(data))
   1507                 writeOffset += written
   1508         finally:
   1509             if fileId is not None:
   1510                 self.close(treeId, fileId)
   1511             self.disconnectTree(treeId)
   1512 
   1513     def waitNamedPipe(self, treeId, pipename, timeout = 5):
   1514         pipename = ntpath.basename(pipename)
   1515         if self._Session['TreeConnectTable'].has_key(treeId) is False:
   1516             raise SessionError(STATUS_INVALID_PARAMETER)
   1517         if len(pipename) > 0xffff:
   1518             raise SessionError(STATUS_INVALID_PARAMETER)
   1519 
   1520         pipeWait = FSCTL_PIPE_WAIT_STRUCTURE()
   1521         pipeWait['Timeout']          = timeout*100000
   1522         pipeWait['NameLength']       = len(pipename)*2
   1523         pipeWait['TimeoutSpecified'] = 1
   1524         pipeWait['Name']             = pipename.encode('utf-16le')
   1525 
   1526         return self.ioctl(treeId, None, FSCTL_PIPE_WAIT,flags=SMB2_0_IOCTL_IS_FSCTL, inputBlob=pipeWait, maxInputResponse = 0, maxOutputResponse=0)
   1527         
   1528     def getIOCapabilities(self):
   1529         res = dict()
   1530 
   1531         res['MaxReadSize'] = self._Connection['MaxReadSize']
   1532         res['MaxWriteSize'] = self._Connection['MaxWriteSize']
   1533         return res
   1534         
   1535 
   1536     ######################################################################
   1537     # Backward compatibility functions and alias for SMB1 and DCE Transports
   1538     # NOTE: It is strongly recommended not to use these commands
   1539     # when implementing new client calls.
   1540     get_server_name            = getServerName
   1541     get_server_domain          = getServerDomain
   1542     get_server_dns_domain_name = getServerDNSDomainName
   1543     get_remote_name            = getServerName
   1544     get_remote_host            = getServerIP
   1545     get_server_os              = getServerOS
   1546     get_server_os_major        = getServerOSMajor
   1547     get_server_os_minor        = getServerOSMinor
   1548     get_server_os_build        = getServerOSBuild
   1549     tree_connect_andx          = connectTree
   1550     tree_connect               = connectTree
   1551     connect_tree               = connectTree
   1552     disconnect_tree            = disconnectTree 
   1553     set_timeout                = setTimeout
   1554     use_timeout                = useTimeout
   1555     stor_file                  = storeFile
   1556     retr_file                  = retrieveFile
   1557     list_path                  = listPath
   1558 
   1559     def __del__(self):
   1560         if self._NetBIOSSession:
   1561             self._NetBIOSSession.close()
   1562 
   1563 
   1564     def doesSupportNTLMv2(self):
   1565         # Always true :P 
   1566         return True
   1567     
   1568     def is_login_required(self):
   1569         # Always true :P 
   1570         return True
   1571 
   1572     def is_signing_required(self):
   1573         return self._Session["SigningRequired"] 
   1574 
   1575     def nt_create_andx(self, treeId, fileName, smb_packet=None, cmd = None):
   1576         if len(fileName) > 0 and fileName[0] == '\\':
   1577             fileName = fileName[1:]
   1578  
   1579         if cmd is not None:
   1580             from impacket import smb
   1581             ntCreate = smb.SMBCommand(data = str(cmd))
   1582             params = smb.SMBNtCreateAndX_Parameters(ntCreate['Parameters'])
   1583             return self.create(treeId, fileName, params['AccessMask'], params['ShareAccess'],
   1584                                params['CreateOptions'], params['Disposition'], params['FileAttributes'],
   1585                                params['Impersonation'], params['SecurityFlags'])
   1586                                
   1587         else:
   1588             return self.create(treeId, fileName, 
   1589                     FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_READ_EA |
   1590                     FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | READ_CONTROL,
   1591                     FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE, FILE_OPEN, 0 )
   1592                     
   1593     def get_socket(self):
   1594         return self._NetBIOSSession.get_socket()
   1595 
   1596 
   1597     def write_andx(self,tid,fid,data, offset = 0, wait_answer=1, write_pipe_mode = False, smb_packet=None):
   1598         # ToDo: Handle the custom smb_packet situation
   1599         return self.write(tid, fid, data, offset, len(data))
   1600 
   1601     def TransactNamedPipe(self, tid, fid, data, noAnswer = 0, waitAnswer = 1, offset = 0):
   1602         return self.ioctl(tid, fid, FSCTL_PIPE_TRANSCEIVE, SMB2_0_IOCTL_IS_FSCTL, data, maxOutputResponse = 65535, waitAnswer = noAnswer | waitAnswer)
   1603 
   1604     def TransactNamedPipeRecv(self):
   1605         ans = self.recvSMB()
   1606 
   1607         if ans.isValidAnswer(STATUS_SUCCESS):
   1608             smbIoctlResponse = SMB2Ioctl_Response(ans['Data'])
   1609             return smbIoctlResponse['Buffer']
   1610 
   1611 
   1612     def read_andx(self, tid, fid, offset=0, max_size = None, wait_answer=1, smb_packet=None):
   1613         # ToDo: Handle the custom smb_packet situation
   1614         if max_size is None:
   1615             max_size = self._Connection['MaxReadSize']
   1616         return self.read(tid, fid, offset, max_size, wait_answer)
   1617 
   1618     def list_shared(self):
   1619         # In the context of SMB2/3, forget about the old LANMAN, throw NOT IMPLEMENTED
   1620         raise SessionError(STATUS_NOT_IMPLEMENTED)
   1621 
   1622     def open_andx(self, tid, fileName, open_mode, desired_access):
   1623         # ToDo Return all the attributes of the file
   1624         if len(fileName) > 0 and fileName[0] == '\\':
   1625             fileName = fileName[1:]
   1626 
   1627         fileId = self.create(tid,fileName,desired_access, open_mode, FILE_NON_DIRECTORY_FILE, open_mode, 0)
   1628         return fileId, 0, 0, 0, 0, 0, 0, 0, 0
   1629 
   1630