signing: disable signing if authentication failed but guest login is enabled
[scrambla.git] / smb2 / negotiate_protocol.py
1 # coding: utf-8
2
3 # Copyright (C) 2020 by Ronnie Sahlberg<ronniesahlberg@gmail.com>
4 #
5
6 import struct
7 from enum import Enum
8
9 from smb2.header import Direction
10 from smb2.timestamps import WinToTimeval, TimevalToWin
11
12 #
13 # SMB2 Negotiate_Protocol
14 #
15
16 VERSION_0202 = 0x0202
17 VERSION_0210 = 0x0210
18 VERSION_0300 = 0x0300
19 VERSION_0302 = 0x0302
20 VERSION_0311 = 0x0311
21
22 #
23 # Negotiate Context Types
24 #
25 SMB2_PREAUTH_INTEGRITY_CAPABILITIES = 0x0001
26 SMB2_ENCRYPTION_CAPABILITIES        = 0x0002
27 SMB2_COMPRESSION_CAPABILITIES       = 0x0003
28
29 #
30 # Hashes
31 #
32 SHA_512 = 0x0001
33
34 #
35 # Encryption Types
36 #
37 AES_128_CCM = 0x0001
38 AES_128_GCM = 0x0002
39 AES_256_CCM = 0x0003
40 AES_256_GCM = 0x0004
41
42 #
43 # Compression Types
44 #
45 COMPRESSION_NONE       = 0x0000
46 COMPRESSION_LZNT1      = 0x0001
47 COMPRESSION_LZ77       = 0x0002
48 COMPRESSION_LZ77_H     = 0x0003
49 COMPRESSION_PATTERN_V1 = 0x0004
50
51 #
52 # Capabilities
53 #
54 SMB2_GLOBAL_CAP_DFS                = 0x00000001
55 SMB2_GLOBAL_CAP_LEASING            = 0x00000002
56 SMB2_GLOBAL_CAP_LARGE_MTU          = 0x00000004
57 SMB2_GLOBAL_CAP_MULTI_CHANNEL      = 0x00000008
58 SMB2_GLOBAL_CAP_PERSISTENT_HANDLES = 0x00000010
59 SMB2_GLOBAL_CAP_DIRECTORY_LEASING  = 0x00000020
60 SMB2_GLOBAL_CAP_ENCRYPTION         = 0x00000040
61
62
63 def _decode_context(context):
64     if context['context_type'] == SMB2_PREAUTH_INTEGRITY_CAPABILITIES:
65         _count = struct.unpack_from('<H', context['data'], 0)[0]
66         _len = struct.unpack_from('<H', context['data'], 2)[0]
67         context.update({'hash_algorithms': []})
68         for i in range(_count):
69             context['hash_algorithms'].append(struct.unpack_from('<H', context['data'], 4 + i * 2)[0])
70         context.update({'salt': context['data'][4 + _count * 2:4 + _count * 2 + _len]})
71         del context['data']
72         return
73         
74     if context['context_type'] == SMB2_ENCRYPTION_CAPABILITIES:
75         _count = struct.unpack_from('<H', context['data'], 0)[0]
76         context.update({'ciphers': []})
77         for i in range(_count):
78             context['ciphers'].append(struct.unpack_from('<H', context['data'], 2 + i * 2)[0])
79         del context['data']
80         return
81         
82     if context['context_type'] == SMB2_COMPRESSION_CAPABILITIES:
83         _count = struct.unpack_from('<H', context['data'], 0)[0]
84         context.update({'flags': struct.unpack_from('<I', context['data'], 4)[0]})
85         context.update({'compression_algorithms': []})
86         for i in range(_count):
87             context['compression_algorithms'].append(struct.unpack_from('<H', context['data'], 8 + i * 2)[0])
88         del context['data']
89         return
90
91 def _decode_contexts(buf, count):
92     contexts = {}
93     for _ in range(count):
94         _type = struct.unpack_from('<H', buf, 0)[0]
95         _len = struct.unpack_from('<H', buf, 2)[0]
96
97         context = {}
98         context.update({'context_type': _type})
99         context.update({'data': buf[8:_len + 8]})
100         _decode_context(context)
101         contexts.update({_type: context})
102
103         buf = buf[((_len + 7) & 0xfff8) + 8:]
104     return contexts
105
106 def _encode_context(context):
107     if context['context_type'] == SMB2_PREAUTH_INTEGRITY_CAPABILITIES:
108         buf = bytearray(8 + 4)
109         struct.pack_into('<H', buf, 8, len(context['hash_algorithms']))
110         struct.pack_into('<H', buf, 10, len(context['salt']))
111         for _a in context['hash_algorithms']:
112             _tmp = bytearray(2)
113             struct.pack_into('<H', _tmp, 0, _a)
114             buf = buf + _tmp
115
116         buf = buf + context['salt']
117
118         struct.pack_into('<H', buf, 0, context['context_type'])
119         struct.pack_into('<H', buf, 2, len(buf) - 8)
120         return buf
121
122     if context['context_type'] == SMB2_ENCRYPTION_CAPABILITIES:
123         buf = bytearray(8 + 2)
124         struct.pack_into('<H', buf, 8, len(context['ciphers']))
125         for _a in context['ciphers']:
126             _tmp = bytearray(2)
127             struct.pack_into('<H', _tmp, 0, _a)
128             buf = buf + _tmp
129         
130         struct.pack_into('<H', buf, 0, context['context_type'])
131         struct.pack_into('<H', buf, 2, len(buf) - 8)
132         return buf
133
134     if context['context_type'] == SMB2_COMPRESSION_CAPABILITIES:
135         buf = bytearray(8 + 8)
136         struct.pack_into('<H', buf, 8, len(context['compression_algorithms']))
137         struct.pack_into('<I', buf, 12, context['flags'])
138         
139         for _a in context['compression_algorithms']:
140             _tmp = bytearray(2)
141             struct.pack_into('<H', _tmp, 0, _a)
142             buf = buf + _tmp
143         
144         struct.pack_into('<H', buf, 0, context['context_type'])
145         struct.pack_into('<H', buf, 2, len(buf) - 8)
146         return buf
147
148     # unknown, just send the 'data' blob as is
149     buf = bytearray(8)
150     buf = buf + context['data']
151     struct.pack_into('<H', buf, 0, context['context_type'])
152     struct.pack_into('<H', buf, 2, len(buf) - 8)
153     return buf
154     
155 def _encode_contexts(contexts):
156     buf = bytearray(0)
157     for context in contexts.values():
158         # Add padding if we need to
159         _len = len(buf)
160         if _len % 8:
161             _pad = ((_len + 7) & 0xfff8) - _len
162             buf = buf + bytearray(_pad)
163
164         buf = buf + _encode_context(context)
165
166     return buf
167
168 def _decode_request(hdr):
169     """
170     Decode a Negotiate_Protocol request
171     """
172     result = {}
173     result.update({'structure_size': struct.unpack_from('<H', hdr, 0)[0]})
174     result.update({'security_mode': struct.unpack_from('<I', hdr, 4)[0]})
175     result.update({'capabilities': struct.unpack_from('<I', hdr, 8)[0]})
176     result.update({'client_guid': hdr[12:12 + 16]})
177
178     # Dialects
179     _num = struct.unpack_from('<H', hdr, 2)[0]
180     result.update({'dialects': []})
181     for i in range(_num):
182         result['dialects'].append(struct.unpack_from('<H', hdr, 36 + i *2)[0])
183     if (VERSION_0311 in result['dialects']):
184         # Negotiate Context Offset and Count
185         _offset = struct.unpack_from('<I', hdr, 28)[0] - 64
186         _num = struct.unpack_from('<H', hdr, 32)[0]
187
188         if _num:
189             result.update({'contexts': _decode_contexts(hdr[_offset:], _num)})
190
191     return result
192
193 def _encode_request(hdr):
194     """
195     Encode a Negotiate_Protocol request
196     """
197     result = bytearray(36)
198     struct.pack_into('<H', result, 0, 36)
199     struct.pack_into('<I', result, 4, hdr['security_mode'])
200     struct.pack_into('<I', result, 8, hdr['capabilities'])
201     if 'client_guid' in hdr:
202         result[12:12 + 16] = hdr['client_guid']
203
204     # Dialects
205     _num = len(hdr['dialects'])
206     struct.pack_into('<H', result, 2, _num)
207     result.extend(bytearray(_num * 2))
208     for i in range(_num):
209         struct.pack_into('<H', result, 36 + i *2, hdr['dialects'][i])
210     
211     if 'contexts' in hdr:
212         # The contexts are padded to start on 8 byte boundary
213         _len = len(result)
214         if _len % 8:
215             _pad = ((_len + 7) & 0xfff8) - _len
216             result.extend(bytearray(_pad))
217
218         # Negotiate Context Offset and Count
219         struct.pack_into('<I', result, 28, len(result) + 64)
220         struct.pack_into('<H', result, 32, len(hdr['contexts']))
221
222         # Encode the actual contexts
223         result = result + _encode_contexts(hdr['contexts'])
224
225     return result
226
227 def _decode_reply(hdr):
228     """
229     Decode a Negotiate_Protocol reply
230     """
231     result = {}
232     result.update({'structure_size': struct.unpack_from('<H', hdr, 0)[0]})
233     result.update({'security_mode': struct.unpack_from('<H', hdr, 2)[0]})
234     result.update({'dialect_revision': struct.unpack_from('<H', hdr, 4)[0]})
235     result.update({'server_guid': hdr[8:8 + 16]})
236     result.update({'capabilities': struct.unpack_from('<I', hdr, 24)[0]})
237     result.update({'max_transact_size': struct.unpack_from('<I', hdr, 28)[0]})
238     result.update({'max_read_size': struct.unpack_from('<I', hdr, 32)[0]})
239     result.update({'max_write_size': struct.unpack_from('<I', hdr, 36)[0]})
240     result.update({'system_time': WinToTimeval(struct.unpack_from('<Q', hdr, 40)[0])})
241     result.update({'server_start_time': WinToTimeval(struct.unpack_from('<Q', hdr, 48)[0])})
242
243     _sec_offset = struct.unpack_from('<H', hdr, 56)[0]
244     _sec_len = struct.unpack_from('<H', hdr, 58)[0]
245     if _sec_len:
246         result.update({'security_buffer': hdr[_sec_offset - 64:_sec_offset - 64 + _sec_len]})
247         
248     _context_count = 0
249     if result['dialect_revision'] == VERSION_0311:
250         _context_count = struct.unpack_from('<H', hdr, 6)[0]
251         _context_offset = struct.unpack_from('<I', hdr, 60)[0]
252         if _context_count:
253             result.update({'contexts': _decode_contexts(hdr[_context_offset - 64:], _context_count)})
254     
255     return result
256
257 def _encode_reply(hdr):
258     """
259     Encode a Negotiate_Protocol reply
260     """
261     result = bytearray(64)
262     struct.pack_into('<H', result, 0, 65)
263     struct.pack_into('<H', result, 2, hdr['security_mode'])
264     struct.pack_into('<H', result, 4, hdr['dialect_revision'])
265     if 'server_guid' in hdr:
266         result[8:8 + 16] = hdr['server_guid']
267     struct.pack_into('<I', result, 24, hdr['capabilities'])
268     struct.pack_into('<I', result, 28, hdr['max_transact_size'])
269     struct.pack_into('<I', result, 32, hdr['max_read_size'])
270     struct.pack_into('<I', result, 36, hdr['max_write_size'])
271     struct.pack_into('<Q', result, 40, TimevalToWin(hdr['system_time']))
272     if 'server_start_time' in hdr:
273         struct.pack_into('<Q', result, 48, TimevalToWin(hdr['server_start_time']))
274     if 'security_buffer' in hdr:
275         # Security buffer is at offset 64(smb2 hdr) + 64(negotiate reply)
276         struct.pack_into('<H', result, 56, 64 + 64)
277         struct.pack_into('<H', result, 58, len(hdr['security_buffer']))
278         result = result + hdr['security_buffer']
279
280     # Encode the actual contexts
281     if hdr['dialect_revision'] == VERSION_0311:
282         if 'contexts' in hdr:
283             # The contexts are padded to start on 8 byte boundary
284             _len = len(result)
285             if _len % 8:
286                 _pad = ((_len + 7) & 0xfff8) - _len
287                 result.extend(bytearray(_pad))
288
289             # Negotiate Context Offset and Count
290             struct.pack_into('<I', result, 60, len(result) + 64)
291             struct.pack_into('<H', result, 6, len(hdr['contexts']))
292                 
293             result = result + _encode_contexts(hdr['contexts'])
294             
295     return result
296
297
298 class NegotiateProtocol(object):
299     """
300     A class for Negotiate_Protocol
301     """
302
303     def __init__(self, **kwargs):
304         True
305
306     def __del__(self):
307         True
308
309     @staticmethod
310     def decode(direction, hdr):
311         """
312         Decode a Negotiate_Protocol PDU
313         """
314         if direction == Direction.REPLY:
315             return _decode_reply(hdr)
316         return _decode_request(hdr)
317
318     @staticmethod
319     def encode(direction, hdr):
320         """
321         Encode a Negotiate_Protocol PDU
322         """
323         if direction == Direction.REPLY:
324             return _encode_reply(hdr)
325         return _encode_request(hdr)
326