Imported Upstream version 7.59.0
[platform/upstream/curl.git] / tests / python_dependencies / impacket / smb3.py
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