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
28import socket
29import ntpath
30import random
31import string
32import struct
33from binascii import a2b_hex
34from contextlib import contextmanager
35
36from impacket import nmb, ntlm, uuid, crypto, LOG
37from impacket.smb3structs import *
38from 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
40from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, SPNEGO_NegTokenResp
41
42
43# For signing
44import hashlib, hmac, copy
45
46# Structs to be used
47TREE_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
61FILE = {
62    'OpenTable'       : [],
63    'LeaseKey'        : '',
64    'LeaseState'      : 0,
65    'LeaseEpoch'      : 0,
66}
67
68OPEN = {
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
90REQUEST = {
91    'CancelID'     : '',
92    'Message'      : '',
93    'Timestamp'    : 0,
94}
95
96CHANNEL = {
97    'SigningKey' : '',
98    'Connection' : 0,
99}
100
101
102class 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
118class 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