3 # Copyright (C) 2020 by Ronnie Sahlberg<ronniesahlberg@gmail.com>
9 from smb2.header import Direction
10 from smb2.timestamps import WinToTimeval, TimevalToWin
13 # SMB2 Negotiate_Protocol
23 # Negotiate Context Types
25 SMB2_PREAUTH_INTEGRITY_CAPABILITIES = 0x0001
26 SMB2_ENCRYPTION_CAPABILITIES = 0x0002
27 SMB2_COMPRESSION_CAPABILITIES = 0x0003
45 COMPRESSION_NONE = 0x0000
46 COMPRESSION_LZNT1 = 0x0001
47 COMPRESSION_LZ77 = 0x0002
48 COMPRESSION_LZ77_H = 0x0003
49 COMPRESSION_PATTERN_V1 = 0x0004
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
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]})
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])
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])
91 def _decode_contexts(buf, count):
93 for _ in range(count):
94 _type = struct.unpack_from('<H', buf, 0)[0]
95 _len = struct.unpack_from('<H', buf, 2)[0]
98 context.update({'context_type': _type})
99 context.update({'data': buf[8:_len + 8]})
100 _decode_context(context)
101 contexts.update({_type: context})
103 buf = buf[((_len + 7) & 0xfff8) + 8:]
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']:
113 struct.pack_into('<H', _tmp, 0, _a)
116 buf = buf + context['salt']
118 struct.pack_into('<H', buf, 0, context['context_type'])
119 struct.pack_into('<H', buf, 2, len(buf) - 8)
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']:
127 struct.pack_into('<H', _tmp, 0, _a)
130 struct.pack_into('<H', buf, 0, context['context_type'])
131 struct.pack_into('<H', buf, 2, len(buf) - 8)
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'])
139 for _a in context['compression_algorithms']:
141 struct.pack_into('<H', _tmp, 0, _a)
144 struct.pack_into('<H', buf, 0, context['context_type'])
145 struct.pack_into('<H', buf, 2, len(buf) - 8)
148 # unknown, just send the 'data' blob as is
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)
155 def _encode_contexts(contexts):
157 for context in contexts.values():
158 # Add padding if we need to
161 _pad = ((_len + 7) & 0xfff8) - _len
162 buf = buf + bytearray(_pad)
164 buf = buf + _encode_context(context)
168 def _decode_request(hdr):
170 Decode a Negotiate_Protocol request
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]})
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]
189 result.update({'contexts': _decode_contexts(hdr[_offset:], _num)})
193 def _encode_request(hdr):
195 Encode a Negotiate_Protocol request
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']
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])
211 if 'contexts' in hdr:
212 # The contexts are padded to start on 8 byte boundary
215 _pad = ((_len + 7) & 0xfff8) - _len
216 result.extend(bytearray(_pad))
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']))
222 # Encode the actual contexts
223 result = result + _encode_contexts(hdr['contexts'])
227 def _decode_reply(hdr):
229 Decode a Negotiate_Protocol reply
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])})
243 _sec_offset = struct.unpack_from('<H', hdr, 56)[0]
244 _sec_len = struct.unpack_from('<H', hdr, 58)[0]
246 result.update({'security_buffer': hdr[_sec_offset - 64:_sec_offset - 64 + _sec_len]})
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]
253 result.update({'contexts': _decode_contexts(hdr[_context_offset - 64:], _context_count)})
257 def _encode_reply(hdr):
259 Encode a Negotiate_Protocol reply
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']
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
286 _pad = ((_len + 7) & 0xfff8) - _len
287 result.extend(bytearray(_pad))
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']))
293 result = result + _encode_contexts(hdr['contexts'])
298 class NegotiateProtocol(object):
300 A class for Negotiate_Protocol
303 def __init__(self, **kwargs):
310 def decode(direction, hdr):
312 Decode a Negotiate_Protocol PDU
314 if direction == Direction.REPLY:
315 return _decode_reply(hdr)
316 return _decode_request(hdr)
319 def encode(direction, hdr):
321 Encode a Negotiate_Protocol PDU
323 if direction == Direction.REPLY:
324 return _encode_reply(hdr)
325 return _encode_request(hdr)