14c613800365aaa0c85574893c2c7d5b18a02a7f
[scrambla.git] / server / server.py
1 #!/usr/bin/env python
2 # coding: utf-8
3
4 from Crypto.Hash import CMAC
5 from Crypto.Cipher import AES
6 import hashlib
7 import hmac
8 import os
9 import secrets
10 import socket
11 import stat
12 import struct
13 import time
14 import spnego
15 try:
16     from config import Config
17 except:
18     print('FATAL: No configuration file found.')
19     raise
20 if Config.ntlm_user_file:
21     os.environ['NTLM_USER_FILE'] = Config.ntlm_user_file 
22
23 from smb2.header import *
24 from smb2.error_response import *
25 from smb2.negotiate_protocol import *
26 from smb2.session_setup import *
27 from smb2.session_logoff import *
28 from smb2.tree_connect import *
29 from smb2.tree_disconnect import *
30 from smb2.create import *
31 from smb2.close import *
32 from smb2.read import *
33 from smb2.query_info import *
34 from smb2.query_directory import *
35 from smb2.file_info import *
36 from smb2.filesystem_info import *
37 from smb2.dir_info import *
38
39 SMB2_KEY_SIZE = 16
40
41 class File(object):
42
43     def __init__(self, path, flags, at, **kwargs):
44         self.path = '.' if not path else path
45         self.fd = os.open(self.path, flags, dir_fd=at)
46         _st = os.stat(self.fd)
47         self.de = []
48         if stat.S_ISDIR(_st.st_mode):
49             self.scandir()
50
51     def __del__(self):
52         if self.fd:
53             os.close(self.fd)
54
55     def stat(self):
56         return os.fstat(self.fd)
57
58     def scandir(self):
59         _dirents = []
60         with os.scandir(self.fd) as it:
61             for _e in it:
62                 _st = _e.stat(follow_symlinks=False)
63                 _a = FILE_ATTRIBUTE_SPARSE_FILE
64                 if stat.S_ISDIR(_st.st_mode):
65                     _a = _a | FILE_ATTRIBUTE_DIRECTORY
66                 _de = {'file_index': 0,
67                        'creation_time': (0, 0, 0),
68                        'last_access_time': (int(_st.st_atime), 0, 0),
69                        'last_write_time': (int(_st.st_mtime), 0, 0),
70                        'change_time': (int(_st.st_ctime), 0, 0),
71                        'end_of_file': _st.st_size,
72                        'allocation_size': _st.st_size,
73                        'file_attributes': _a,
74                        'ea_size': 0,
75                        'file_id': _st.st_ino,
76                        'file_name': bytes(_e.name, encoding='utf=8'),
77                        }
78                 _dirents.append((_e, _de,))
79         self.de = _dirents
80
81     def pread(self, length, offset):
82         return os.pread(self.fd, length, offset)
83
84
85 class Server(object):
86     """
87     A class for a SMB2 Server
88     """
89
90     sessions = {}
91     trees = {}
92     files = {}
93     dialect = 0
94     
95     def __init__(self, s, **kwargs):
96         self._s = s
97         self._sp = spnego.server(socket.gethostname())
98         self._guest = False
99         self._sesid = 1
100         self._treeid = 1
101         self._fileid = 1
102         self._last_fid = (0, 0)
103         self.signing_key = None
104         self._use_signing = False
105
106         print('Socket', self._s)
107         self.Run()
108
109     def __del__(self):
110         True
111
112     def srv_read(self, hdr, pdu):
113         #
114         # Read
115         #
116         if not hdr['tree_id'] in self.trees:
117             self._compound_error = Status.INVALID_PARAMETER
118             return (self._compound_error,
119                     ErrorResponse.encode({'error_data' : bytes(1)}))
120         _fid = pdu['file_id']
121         if _fid == (0xffffffffffffffff, 0xffffffffffffffff):
122             _fid = self._last_fid
123
124         try:
125             _f = self.files[_fid]
126         except KeyError:
127             self._compound_error = Status.INVALID_PARAMETER
128             return (self._compound_error,
129                     ErrorResponse.encode({'error_data' : bytes(1)}))
130
131         _st = _f.stat()
132         if pdu['offset'] >= _st.st_size:
133             self._compound_error = Status.END_OF_FILE
134             return (self._compound_error,
135                     ErrorResponse.encode({'error_data' : bytes(1)}))
136             
137         _b = _f.pread(pdu['length'], pdu['offset'])
138         return (Status.SUCCESS,
139                 Read.encode(Direction.REPLY,
140                        {'data_remaining': 0,
141                         'data': _b,
142                         }))
143
144         
145     def srv_close(self, hdr, pdu):
146         #
147         # Close
148         #
149         if not hdr['tree_id'] in self.trees:
150             self._compound_error = Status.INVALID_PARAMETER
151             return (self._compound_error,
152                     ErrorResponse.encode({'error_data' : bytes(1)}))
153         _fid = pdu['file_id']
154         if _fid == (0xffffffffffffffff, 0xffffffffffffffff):
155             _fid = self._last_fid
156
157         try:
158             _f = self.files[_fid]
159         except KeyError:
160             self._compound_error = Status.INVALID_PARAMETER
161             return (self._compound_error,
162                     ErrorResponse.encode({'error_data' : bytes(1)}))
163         del self.files[_fid]
164         del _f
165         return (Status.SUCCESS,
166                 Close.encode(Direction.REPLY,
167                        {'flags': 0,
168                         }))
169
170
171     def srv_query_dir(self, hdr, pdu):
172         #
173         # Query Directory
174         #
175         if not hdr['tree_id'] in self.trees:
176             self._compound_error = Status.INVALID_PARAMETER
177             return (self._compound_error,
178                     ErrorResponse.encode({'error_data' : bytes(1)}))
179         try:
180             DirInfoClass(pdu['info_class'])
181         except ValueError:
182             print('QueryDir: Can not handle info_type', pdu['info_class'])
183             self._compound_error = Status.INVALID_PARAMETER
184             return (self._compound_error,
185                     ErrorResponse.encode({'error_data' : bytes(1)}))
186
187         _fid = pdu['file_id']
188         if _fid == (0xffffffffffffffff, 0xffffffffffffffff):
189             _fid = self._last_fid
190
191         try: 
192             _f = self.files[_fid]
193         except KeyError:
194             self._compound_error = Status.INVALID_PARAMETER
195             return (self._compound_error,
196                     ErrorResponse.encode({'error_data' : bytes(1)}))
197
198         if pdu['flags'] & (SMB2_RESTART_SCANS | SMB2_REOPEN):
199             _f.scandir()
200
201         if not _f.de:
202             self._compound_error = Status.NO_MORE_FILES
203             return (self._compound_error,
204                     ErrorResponse.encode({'error_data' : bytes(1)}))
205             
206         _b = bytearray(0)
207         _obl = pdu['output_buffer_length']
208         _pos = 0
209         while _f.de:
210             _i = DirInfo.encode_single(
211                 DirInfoClass(pdu['info_class']), _f.de[0][1])
212             if len(_i) > _obl:
213                 break
214             _f.de = _f.de[1:]
215             
216             _obl = _obl - len(_i)
217             _pos = len(_b)
218             _b = _b + _i
219         struct.pack_into('<I', _b, _pos, 0)
220         return (Status.SUCCESS,
221                 QueryDirectory.encode(Direction.REPLY,
222                        {'data': _b,
223                         }))
224
225     def _query_file_info(self, f, c):
226         _st = f.stat()
227         _a = FILE_ATTRIBUTE_SPARSE_FILE
228         if stat.S_ISDIR(_st.st_mode):
229             _a = _a | FILE_ATTRIBUTE_DIRECTORY
230             
231         _ac = 0
232         if stat.S_IRUSR & _st.st_mode:
233             _ac = _ac | FILE_READ_DATA
234         if stat.S_IWUSR & _st.st_mode:
235             _ac = _ac | FILE_WRITE_DATA
236         if stat.S_IXUSR & _st.st_mode:
237             _ac = _ac | FILE_EXECUTE
238         _fi = FileInfo.encode(FileInfoClass(c),
239                         {'creation_time': (0, 0, 0),
240                          'last_access_time': (int(_st.st_atime), 0, 0),
241                          'last_write_time': (int(_st.st_mtime), 0, 0),
242                          'change_time': (int(_st.st_ctime), 0, 0),
243                          'file_attributes': _a,
244                          'allocation_size': _st.st_size,
245                          'end_of_file': _st.st_size,
246                          'number_of_links': _st.st_nlink,
247                          'delete_pending': 0,
248                          'directory': 0 if stat.S_ISDIR(_st.st_mode) else 1,
249                          'index_number': _st.st_ino,
250                          'ea_size': 0,
251                          'access_flags': _ac,
252                          'current_byte_offset': 0,
253                          'mode': 0,
254                          'alignment_requirement': 0,
255                          })
256         if FileInfoClass(c) == FileInfoClass.ALL_INFORMATION:
257             # windows adds 4 bytes of junk here
258             _fi = _fi + bytearray(4)
259             
260         return (Status.SUCCESS,
261                 QueryInfo.encode(Direction.REPLY,
262                                 {'buffer': _fi,
263                                  }))
264
265     def _query_fs_info(self, t, c):
266         if FSInfoClass(c) == FSInfoClass.ATTRIBUTE:
267             _fi = FSInfo.encode(FSInfoClass.ATTRIBUTE,
268                     {'attributes': SUPPORTS_OBJECT_IDS | SUPPORTS_SPARSE_FILES | UNICODE_ON_DISK | CASE_PRESERVED_NAMES | CASE_SENSITIVE_SEARCH,
269                      'maximum_component_name_length': 255,
270                      'file_system_name': 'pysmb3d'
271                      })
272             return (Status.SUCCESS,
273                 QueryInfo.encode(Direction.REPLY,
274                     {'buffer': _fi,
275                      }))
276         if FSInfoClass(c) == FSInfoClass.DEVICE:
277             _fi = FSInfo.encode(FSInfoClass.DEVICE,
278                     {'device_type': DeviceType.DISK.value,
279                      'characteristics': DEVICE_IS_MOUNTED 
280                      })
281             return (Status.SUCCESS,
282                 QueryInfo.encode(Direction.REPLY,
283                     {'buffer': _fi,
284                      }))
285         if FSInfoClass(c) == FSInfoClass.VOLUME:
286             _fi = FSInfo.encode(FSInfoClass.VOLUME,
287                     {'creation_time': (0, 0, 0),
288                      'serial_number': 0,
289                      'supports_objects': 0,
290                      'label': t[1]
291                      })
292             return (Status.SUCCESS,
293                 QueryInfo.encode(Direction.REPLY,
294                     {'buffer': _fi,
295                      }))
296
297         if FSInfoClass(c) == FSInfoClass.SECTOR_SIZE:
298             _stfs = os.fstatvfs(t[0])
299             _fi = FSInfo.encode(FSInfoClass.SECTOR_SIZE,
300                     {'logical_bytes_per_sector': _stfs.f_bsize,
301                      'physical_bytes_per_sector_for_atomicity': _stfs.f_bsize,
302                      'physical_bytes_per_sector_for_performance': _stfs.f_bsize,
303                      'effective_physical_bytes_per_sector_for_atomicity': _stfs.f_bsize,
304                      'flags': SSINFO_FLAGS_ALIGNED_DEVICE | SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE,
305                      'byte_offset_for_sector_alignment': 0,
306                      'byte_offset_for_partition_alignment': 0
307                      })
308             return (Status.SUCCESS,
309                 QueryInfo.encode(Direction.REPLY,
310                     {'buffer': _fi,
311                      }))
312         
313         if FSInfoClass(c) == FSInfoClass.FULL_SIZE:
314             _stfs = os.fstatvfs(t[0])
315             _fi = FSInfo.encode(FSInfoClass.FULL_SIZE,
316                     {'total_allocation_units': _stfs.f_blocks,
317                      'caller_available_allocation_units': _stfs.f_bavail,
318                      'actual_available_allocation_units': _stfs.f_bfree,
319                      'sectors_per_allocation_unit': 1,
320                      'bytes_per_sector': _stfs.f_bsize,
321                      })
322             return (Status.SUCCESS,
323                 QueryInfo.encode(Direction.REPLY,
324                     {'buffer': _fi,
325                      }))
326
327         return (Status.INVALID_PARAMETER,
328                 ErrorResponse.encode({'error_data' : bytes(1)}))
329             
330     def srv_query_info(self, hdr, pdu):
331         #
332         # Query Info
333         # can only handle INFO_FILE for now
334         #
335         if not hdr['tree_id'] in self.trees:
336             self._compound_error = Status.INVALID_PARAMETER
337             return (self._compound_error,
338                     ErrorResponse.encode({'error_data' : bytes(1)}))
339
340         _fid = pdu['file_id']
341         if _fid == (0xffffffffffffffff, 0xffffffffffffffff):
342             _fid = self._last_fid
343
344         try:
345             _f = self.files[_fid]
346         except KeyError:
347             self._compound_error = Status.INVALID_PARAMETER
348             return (self._compound_error,
349                     ErrorResponse.encode({'error_data' : bytes(1)}))
350
351         if pdu['info_type'] == SMB2_0_INFO_FILE:
352             return self._query_file_info(_f, pdu['file_info_class'])
353         
354         if pdu['info_type'] == SMB2_0_INFO_FILESYSTEM:
355             return self._query_fs_info(self.trees[hdr['tree_id']], pdu['file_info_class'])
356         
357         print('QueryInfo: Can not handle info type', pdu['info_type'])
358         self._compound_error = Status.INVALID_PARAMETER
359         return (self._compound_error,
360                 ErrorResponse.encode({'error_data' : bytes(1)}))
361
362         
363     def srv_create(self, hdr, pdu):
364         #
365         # Create/Open
366         #
367         if not hdr['tree_id'] in self.trees:
368             self._compound_error = Status.INVALID_PARAMETER
369             return (self._compound_error,
370                     ErrorResponse.encode({'error_data' : bytes(1)}))
371         t = self.trees[hdr['tree_id']]
372
373         flags = 0
374         if Disposition(pdu['create_disposition']) != Disposition.OPEN:
375             self._compound_error = Status.INVALID_PARAMETER
376             return (self._compound_error,
377                     ErrorResponse.encode({'error_data' : bytes(1)}))
378         if pdu['desired_access'] & (FILE_GENERIC_READ | FILE_GENERIC_ALL | FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_READ_DATA):
379             # O_RDONLY is 0
380             flags = flags | os.O_RDONLY
381
382         try:
383             f = File(pdu['path'].decode(), flags, t[0])
384             _st = f.stat()
385         except FileNotFoundError:
386             self._compound_error = Status.OBJECT_NAME_NOT_FOUND
387             return (self._compound_error,
388                     ErrorResponse.encode({'error_data' : bytes(1)}))
389
390         self._last_fid = (self._fileid, self._fileid)
391         self._fileid = self._fileid + 1
392         self.files.update({self._last_fid: f})
393
394         _a = FILE_ATTRIBUTE_SPARSE_FILE
395         if stat.S_ISDIR(_st.st_mode):
396             _a = _a | FILE_ATTRIBUTE_DIRECTORY
397             
398         return (Status.SUCCESS,
399                 Create.encode(Direction.REPLY,
400                        {'oplock_level': Oplock.LEVEL_NONE.value,
401                         'flags': 0,
402                         'create_action': Action.OPENED.value,
403                         'creation_time': (0, 0, 0),
404                         'last_access_time': (int(_st.st_atime), 0, 0),
405                         'last_write_time': (int(_st.st_mtime), 0, 0),
406                         'change_time': (int(_st.st_ctime), 0, 0),
407                         'allocation_size': _st.st_size,
408                         'end_of_file': _st.st_size,
409                         'file_attributes': _a,
410                         'file_id': self._last_fid,
411                         }))
412
413     def srv_tree_disconn(self, hdr, pdu):
414         #
415         # Disconnect a share
416         #
417         if not hdr['tree_id'] in self.trees:
418             self._compound_error = Status.INVALID_PARAMETER
419             return (self._compound_error,
420                     ErrorResponse.encode({'error_data' : bytes(1)}))
421
422         os.close(self.trees[hdr['tree_id']][0])
423         del self.trees[hdr['tree_id']]
424         return (Status.SUCCESS,
425                 TreeDisconnect.encode(Direction.REPLY, {}))
426         
427     def srv_tree_conn(self, hdr, pdu):
428         #
429         # Connect to a share
430         #
431         _p = pdu['path'][2:].decode().replace('\\', '/')
432         _p = _p[_p.find('/') + 1:]
433         if not _p in Config.shares:
434             print('Share not found', _p)
435             return (Status.BAD_NETWORK_NAME,
436                     ErrorResponse.encode({'error_data' : bytes(1)}))
437
438         fd = os.open(Config.shares[_p], os.O_RDONLY)
439
440         hdr['tree_id'] = self._treeid
441         self.trees.update({self._treeid: (fd, _p,)})
442         self._treeid = self._treeid + 1
443
444         return (Status.SUCCESS,
445                 TreeConnect.encode(Direction.REPLY,
446                        {'share_type': SMB2_SHARE_TYPE_DISK,
447                         'share_flags': 0,
448                         'capabilities': 0,
449                         'maximal_access': 0x001f00a9,
450                         }))
451         
452     def generate_keys(self, session_key):
453         def derive_key(session_key, label, context):
454             input_key = session_key[:16]
455
456             hm = hmac.new(input_key, None, hashlib.sha256)
457             counter=bytearray(4)
458             struct.pack_into('>I', counter, 0, 1)
459             hm.update(counter)
460             hm.update(label)
461             hm.update(bytes(1))
462             hm.update(context)
463             keylen=bytearray(4)
464             struct.pack_into('>I', keylen, 0, SMB2_KEY_SIZE * 8)
465             hm.update(keylen)
466             return hm.digest()
467
468         if self.dialect <= VERSION_0210:
469             self.signing_key = session_key
470         elif self.dialect <= VERSION_0302:
471             self.signing_key = derive_key(session_key,
472                     bytes('SMB2AESCMAC', encoding='ascii') + bytes(1),
473                     bytes('SmbSign', encoding='ascii') + bytes(1),
474                     )[:SMB2_KEY_SIZE]
475         else: # dialect >= VERSION_0311
476             self.signing_key = derive_key(session_key,
477                     bytes('SMBSigningKey', encoding='ascii') + bytes(1),
478                     self.preauth_hash,
479                     )[:SMB2_KEY_SIZE]
480
481     def srv_sess_setup(self, hdr, pdu):
482         try:
483             sm = self._sp.step(pdu['security_buffer'])
484         except Exception as e:
485             if Config.guest_login:
486                 print('Authentication failed. Logging in as guest')
487                 self._guest = True
488                 hdr['session_id'] = self._sesid
489                 self.sessions.update({self._sesid: (None,)})
490                 self._sesid = self._sesid + 1
491                 return (Status.SUCCESS,
492                         SessionSetup.encode(Direction.REPLY,
493                                 {'session_flags': SMB2_SESSION_FLAG_IS_GUEST,
494                                  }))
495             else:
496                 print('Exception', e)
497                 raise
498
499         if self._sp.complete:
500             # self._sp.session_key
501             # self._sp.negotiated_protocol == 'ntlm'
502             print('Authenticated as', self._sp.client_principal)
503             self.generate_keys(self._sp.session_key)
504             #
505             # TODO store user/session data in this tuple
506             hdr['session_id'] = self._sesid
507             self.sessions.update({self._sesid: (None,)})
508             self._sesid = self._sesid + 1
509         
510             return (Status.SUCCESS,
511                     SessionSetup.encode(Direction.REPLY,
512                         {'session_flags': 0,
513                          }))
514             
515         return (Status.MORE_PROCESSING_REQUIRED,
516                 SessionSetup.encode(Direction.REPLY,
517                                     {'session_flags': 0,
518                                      'security_buffer': sm,
519                                      }))
520         
521     def srv_sess_logoff(self, hdr, pdu):
522         del self.sessions[hdr['session_id']]
523         return (Status.SUCCESS,
524                 TreeDisconnect.encode(Direction.REPLY, {}))
525         
526     def srv_neg_prot(self, hdr, pdu):
527         if Config.signing_required:
528             if pdu['security_mode'] & SMB2_NEGOTIATE_SIGNING_ENABLED == 0:
529                 print('Signing required but client does not offer signing')
530                 raise ValueError
531         if Config.signing_enabled:
532             if pdu['security_mode'] & SMB2_NEGOTIATE_SIGNING_ENABLED != 0:
533                 self._use_signing = True
534         else:
535             if pdu['security_mode'] & SMB2_NEGOTIATE_SIGNING_REQUIRED != 0:
536                 print('Signing disabled but client requires it')
537                 raise ValueError
538
539         # Only allow version 3.02
540         if not VERSION_0302 in pdu['dialects']:
541             print('No supported dialect in Negotiate Protocol')
542             return (Status.INVALID_PARAMETER,
543                     ErrorResponse.encode({'error_data' : bytes(1)}))
544         self.dialect = VERSION_0302
545         return (Status.SUCCESS,
546                 NegotiateProtocol.encode(Direction.REPLY,
547                        {'security_mode': SMB2_NEGOTIATE_SIGNING_ENABLED,
548                         'dialect_revision': self.dialect,
549                         'capabilities': 0,
550                         'max_transact_size': 65536,
551                         'max_read_size': 65536,
552                         'max_write_size': 65536,
553                         'system_time': (int(time.time()), 0, 0)}))
554
555     def VerifySignature(self, hdr, cmd):
556         if self.dialect == VERSION_0202:
557             print('Can not compute signature for 2.02 yet')
558             raise ValueError
559         else:
560             mac = cmd[48:64]
561             zsc = cmd[:48] + bytes(16) + cmd[64:]
562             co = CMAC.new(self.signing_key, ciphermod=AES)
563             co.update(zsc)
564             co.verify(mac)
565
566     def ComputeSignature(self, buf):
567         if self.dialect == VERSION_0202:
568             print('Can not compute signature for 2.02 yet')
569             raise ValueError
570         else:
571             co = CMAC.new(self.signing_key, ciphermod=AES)
572             co.update(buf)
573             d = co.digest()
574             return d
575
576     def ProcessCommands(self, cmds):
577         r = []
578         self._compound_error = Status.SUCCESS
579         for cmd in cmds:
580             #
581             # Decode the command pdu
582             #
583             h = cmd[0]
584             if h['flags'] & SIGNED:
585                 self.VerifySignature(cmd[0], cmd[1])
586             ct = {
587                 Command.NEGOTIATE_PROTOCOL: (NegotiateProtocol, self.srv_neg_prot),
588                 Command.SESSION_SETUP: (SessionSetup, self.srv_sess_setup),
589                 Command.SESSION_LOGOFF: (SessionLogoff, self.srv_sess_logoff),
590                 Command.TREE_CONNECT: (TreeConnect, self.srv_tree_conn),
591                 Command.TREE_DISCONNECT: (TreeDisconnect, self.srv_tree_disconn),
592                 Command.CREATE: (Create, self.srv_create),
593                 Command.CLOSE: (Close, self.srv_close),
594                 Command.READ: (Read, self.srv_read),
595                 Command.QUERY_INFO: (QueryInfo, self.srv_query_info),
596                 Command.QUERY_DIRECTORY: (QueryDirectory, self.srv_query_dir),
597                 }
598
599             f = RESPONSE
600             f = f | (h['flags'] & RELATED)
601             _sign = self._use_signing
602             if h['command'] == Command.NEGOTIATE_PROTOCOL.value:
603                 _sign = False
604             if h['command'] == Command.SESSION_SETUP.value:
605                 _sign = False
606             if _sign:
607                 f = f | SIGNED
608
609             if self._compound_error != Status.SUCCESS:
610                 rh = Header.encode({'protocol_id': SMB2_MAGIC,
611                                     'credit_charge': h['credit_charge'],
612                                     'status': self._compound_error.value,
613                                     'command': h['command'],
614                                     'credit_response': h['credit_request'],
615                                     'flags': f,
616                                     'message_id': h['message_id'],
617                                     'process_id': h['process_id'],
618                                     'tree_id': h['tree_id'],
619                                     'session_id': h['session_id']})
620                 r.append((rh,
621                     ErrorResponse.encode({'error_data' : bytes(1)})))
622                 continue
623
624             try:
625                 c = ct.get(Command(h['command']))
626                 req = c[0].decode(Direction.REQUEST, cmd[1][64:])
627             except:
628                 print('Can not handle command', h['command'], 'yet.')
629                 rh = Header.encode({'protocol_id': SMB2_MAGIC,
630                                     'credit_charge': h['credit_charge'],
631                                     'status': Status.INVALID_PARAMETER.value,
632                                     'command': h['command'],
633                                     'credit_response': h['credit_request'],
634                                     'flags': f,
635                                     'message_id': h['message_id'],
636                                     'process_id': h['process_id'],
637                                     'tree_id': h['tree_id'],
638                                     'session_id': h['session_id']})
639                 r.append((rh,
640                           ErrorResponse.encode({'error_data' : bytes(1)})))
641                 continue
642
643             rep = c[1](h, req)
644
645             if h['command'] == Command.SESSION_SETUP.value and self._use_signing and rep[0].value == 0:
646                 f = f | SIGNED
647
648             rh = Header.encode({'protocol_id': SMB2_MAGIC,
649                                 'credit_charge': h['credit_charge'],
650                                 'status': rep[0].value,
651                                 'command': h['command'],
652                                 'credit_response': h['credit_request'],
653                                 'flags': f,
654                                 'message_id': h['message_id'],
655                                 'process_id': h['process_id'],
656                                 'tree_id': h['tree_id'],
657                                 'session_id': h['session_id']})
658             r.append((rh, rep[1]))
659
660             # next command need special handling to write straight into the
661             # encoded buffer and adding padding
662         return r
663
664     def SplitBuffer(self, buf):
665         cmds = []
666         while buf:
667             _h = Header.decode(buf[:64])
668             if _h['protocol_id'] != SMB2_MAGIC:
669                 print('Not a SMB2 header')
670                 raise ValueError
671             if _h['next_command']:
672                 cmds.append((_h, buf[:_h['next_command']]))
673                 buf = buf[_h['next_command']:]
674             else:
675                 cmds.append((_h, buf[:]))
676                 buf = []
677         return cmds
678
679     def Run(self):
680         while True:
681             #
682             # Read the SPL and data from the socket
683             #
684             spl = bytes(0)
685             while len(spl) < 4:
686                 _b = self._s.recv(4 - len(spl))
687                 if not _b:
688                     print('Socket closed by client')
689                     return
690                 spl = spl + _b
691             _spl = struct.unpack_from('>I', spl, 0)[0]
692
693             buf = bytes(0)
694             while len(buf) < _spl:
695                 _b = self._s.recv(_spl - len(buf))
696                 if not _b:
697                     print('Socket closed by client')
698                     return
699                 buf = buf + _b
700
701             #
702             # Split the buffer into a list of (header, command) tuples
703             #
704             cmds = self.SplitBuffer(buf)
705
706             #
707             # Process the commands
708             #
709             rep = self.ProcessCommands(cmds)
710
711             #
712             # Concatenate them into a single bytearray, take care of padding
713             # and next command
714             #
715             buf = bytearray(0)
716             _pos = 0
717             _last_pos = 0
718             _num = len(rep)
719             for idx, r in enumerate(rep):
720                 buf = buf + r[0] + r[1]
721                 _len = len(buf)
722                 if _len % 8:
723                     _pad = ((_len + 7) & 0xfff8) - _len
724                     buf = buf + bytearray(_pad)
725
726                 if idx + 1 != _num:
727                     struct.pack_into('<I', buf, _pos + 20, len(buf) - _pos)
728                 if buf[_pos + 16] & SIGNED:
729                     buf[_pos + 48:_pos + 64] = self.ComputeSignature(buf[_pos:])
730
731                 _last_pos = _pos
732                 _pos = len(buf)
733
734             spl = bytearray(4)
735             struct.pack_into('>I', spl, 0, len(buf))
736             while len(spl):
737                 l = self._s.send(spl)
738                 spl = spl[l:]
739             while len(buf):
740                 l = self._s.send(buf)
741                 buf = buf[l:]
742