1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 """
20 Packet handling
21 """
22
23 import errno
24 import os
25 import socket
26 import struct
27 import threading
28 import time
29 from hmac import HMAC
30
31 from paramiko import util
32 from paramiko.common import linefeed_byte, cr_byte_value, asbytes, MSG_NAMES, \
33 DEBUG, xffffffff, zero_byte
34 from paramiko.py3compat import u, byte_ord
35 from paramiko.ssh_exception import SSHException, ProxyCommandFailure
36 from paramiko.message import Message
37
38
40 return HMAC(key, message, digest_class).digest()
41
42
45
46
48 """
49 Implementation of the base SSH packet protocol.
50 """
51
52
53
54 REKEY_PACKETS = pow(2, 29)
55 REKEY_BYTES = pow(2, 29)
56
57 REKEY_PACKETS_OVERFLOW_MAX = pow(2, 29)
58 REKEY_BYTES_OVERFLOW_MAX = pow(2, 29)
59
61 self.__socket = socket
62 self.__logger = None
63 self.__closed = False
64 self.__dump_packets = False
65 self.__need_rekey = False
66 self.__init_count = 0
67 self.__remainder = bytes()
68
69
70 self.__sent_bytes = 0
71 self.__sent_packets = 0
72 self.__received_bytes = 0
73 self.__received_packets = 0
74 self.__received_bytes_overflow = 0
75 self.__received_packets_overflow = 0
76
77
78 self.__block_size_out = 8
79 self.__block_size_in = 8
80 self.__mac_size_out = 0
81 self.__mac_size_in = 0
82 self.__block_engine_out = None
83 self.__block_engine_in = None
84 self.__sdctr_out = False
85 self.__mac_engine_out = None
86 self.__mac_engine_in = None
87 self.__mac_key_out = bytes()
88 self.__mac_key_in = bytes()
89 self.__compress_engine_out = None
90 self.__compress_engine_in = None
91 self.__sequence_number_out = 0
92 self.__sequence_number_in = 0
93
94
95 self.__write_lock = threading.RLock()
96
97
98 self.__keepalive_interval = 0
99 self.__keepalive_last = time.time()
100 self.__keepalive_callback = None
101
103 """
104 Set the Python log object to use for logging.
105 """
106 self.__logger = log
107
108 - def set_outbound_cipher(self, block_engine, block_size, mac_engine, mac_size, mac_key, sdctr=False):
109 """
110 Switch outbound data cipher.
111 """
112 self.__block_engine_out = block_engine
113 self.__sdctr_out = sdctr
114 self.__block_size_out = block_size
115 self.__mac_engine_out = mac_engine
116 self.__mac_size_out = mac_size
117 self.__mac_key_out = mac_key
118 self.__sent_bytes = 0
119 self.__sent_packets = 0
120
121 self.__init_count |= 1
122 if self.__init_count == 3:
123 self.__init_count = 0
124 self.__need_rekey = False
125
127 """
128 Switch inbound data cipher.
129 """
130 self.__block_engine_in = block_engine
131 self.__block_size_in = block_size
132 self.__mac_engine_in = mac_engine
133 self.__mac_size_in = mac_size
134 self.__mac_key_in = mac_key
135 self.__received_bytes = 0
136 self.__received_packets = 0
137 self.__received_bytes_overflow = 0
138 self.__received_packets_overflow = 0
139
140 self.__init_count |= 2
141 if self.__init_count == 3:
142 self.__init_count = 0
143 self.__need_rekey = False
144
146 self.__compress_engine_out = compressor
147
149 self.__compress_engine_in = compressor
150
152 self.__closed = True
153 self.__socket.close()
154
156 self.__dump_packets = hexdump
157
159 return self.__dump_packets
160
162 return self.__mac_size_in
163
165 return self.__mac_size_out
166
168 """
169 Returns ``True`` if a new set of keys needs to be negotiated. This
170 will be triggered during a packet read or write, so it should be
171 checked after every read or write, or at least after every few.
172 """
173 return self.__need_rekey
174
176 """
177 Turn on/off the callback keepalive. If ``interval`` seconds pass with
178 no data read from or written to the socket, the callback will be
179 executed and the timer will be reset.
180 """
181 self.__keepalive_interval = interval
182 self.__keepalive_callback = callback
183 self.__keepalive_last = time.time()
184
185 - def read_all(self, n, check_rekey=False):
186 """
187 Read as close to N bytes as possible, blocking as long as necessary.
188
189 :param int n: number of bytes to read
190 :return: the data read, as a `str`
191
192 :raises EOFError:
193 if the socket was closed before all the bytes could be read
194 """
195 out = bytes()
196
197 if len(self.__remainder) > 0:
198 out = self.__remainder[:n]
199 self.__remainder = self.__remainder[n:]
200 n -= len(out)
201 while n > 0:
202 got_timeout = False
203 try:
204 x = self.__socket.recv(n)
205 if len(x) == 0:
206 raise EOFError()
207 out += x
208 n -= len(x)
209 except socket.timeout:
210 got_timeout = True
211 except socket.error as e:
212
213
214
215 if (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EAGAIN):
216 got_timeout = True
217 elif (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EINTR):
218
219 pass
220 elif self.__closed:
221 raise EOFError()
222 else:
223 raise
224 if got_timeout:
225 if self.__closed:
226 raise EOFError()
227 if check_rekey and (len(out) == 0) and self.__need_rekey:
228 raise NeedRekeyException()
229 self._check_keepalive()
230 return out
231
233 self.__keepalive_last = time.time()
234 while len(out) > 0:
235 retry_write = False
236 try:
237 n = self.__socket.send(out)
238 except socket.timeout:
239 retry_write = True
240 except socket.error as e:
241 if (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EAGAIN):
242 retry_write = True
243 elif (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EINTR):
244
245 retry_write = True
246 else:
247 n = -1
248 except ProxyCommandFailure:
249 raise
250 except Exception:
251
252 n = -1
253 if retry_write:
254 n = 0
255 if self.__closed:
256 n = -1
257 if n < 0:
258 raise EOFError()
259 if n == len(out):
260 break
261 out = out[n:]
262 return
263
265 """
266 Read a line from the socket. We assume no data is pending after the
267 line, so it's okay to attempt large reads.
268 """
269 buf = self.__remainder
270 while not linefeed_byte in buf:
271 buf += self._read_timeout(timeout)
272 n = buf.index(linefeed_byte)
273 self.__remainder = buf[n + 1:]
274 buf = buf[:n]
275 if (len(buf) > 0) and (buf[-1] == cr_byte_value):
276 buf = buf[:-1]
277 return u(buf)
278
280 """
281 Write a block of data using the current cipher, as an SSH block.
282 """
283
284 data = asbytes(data)
285 cmd = byte_ord(data[0])
286 if cmd in MSG_NAMES:
287 cmd_name = MSG_NAMES[cmd]
288 else:
289 cmd_name = '$%x' % cmd
290 orig_len = len(data)
291 self.__write_lock.acquire()
292 try:
293 if self.__compress_engine_out is not None:
294 data = self.__compress_engine_out(data)
295 packet = self._build_packet(data)
296 if self.__dump_packets:
297 self._log(DEBUG, 'Write packet <%s>, length %d' % (cmd_name, orig_len))
298 self._log(DEBUG, util.format_binary(packet, 'OUT: '))
299 if self.__block_engine_out is not None:
300 out = self.__block_engine_out.encrypt(packet)
301 else:
302 out = packet
303
304 if self.__block_engine_out is not None:
305 payload = struct.pack('>I', self.__sequence_number_out) + packet
306 out += compute_hmac(self.__mac_key_out, payload, self.__mac_engine_out)[:self.__mac_size_out]
307 self.__sequence_number_out = (self.__sequence_number_out + 1) & xffffffff
308 self.write_all(out)
309
310 self.__sent_bytes += len(out)
311 self.__sent_packets += 1
312 if (self.__sent_packets >= self.REKEY_PACKETS or self.__sent_bytes >= self.REKEY_BYTES)\
313 and not self.__need_rekey:
314
315 self._log(DEBUG, 'Rekeying (hit %d packets, %d bytes sent)' %
316 (self.__sent_packets, self.__sent_bytes))
317 self.__received_bytes_overflow = 0
318 self.__received_packets_overflow = 0
319 self._trigger_rekey()
320 finally:
321 self.__write_lock.release()
322
324 """
325 Only one thread should ever be in this function (no other locking is
326 done).
327
328 :raises SSHException: if the packet is mangled
329 :raises NeedRekeyException: if the transport should rekey
330 """
331 header = self.read_all(self.__block_size_in, check_rekey=True)
332 if self.__block_engine_in is not None:
333 header = self.__block_engine_in.decrypt(header)
334 if self.__dump_packets:
335 self._log(DEBUG, util.format_binary(header, 'IN: '))
336 packet_size = struct.unpack('>I', header[:4])[0]
337
338 leftover = header[4:]
339 if (packet_size - len(leftover)) % self.__block_size_in != 0:
340 raise SSHException('Invalid packet blocking')
341 buf = self.read_all(packet_size + self.__mac_size_in - len(leftover))
342 packet = buf[:packet_size - len(leftover)]
343 post_packet = buf[packet_size - len(leftover):]
344 if self.__block_engine_in is not None:
345 packet = self.__block_engine_in.decrypt(packet)
346 if self.__dump_packets:
347 self._log(DEBUG, util.format_binary(packet, 'IN: '))
348 packet = leftover + packet
349
350 if self.__mac_size_in > 0:
351 mac = post_packet[:self.__mac_size_in]
352 mac_payload = struct.pack('>II', self.__sequence_number_in, packet_size) + packet
353 my_mac = compute_hmac(self.__mac_key_in, mac_payload, self.__mac_engine_in)[:self.__mac_size_in]
354 if not util.constant_time_bytes_eq(my_mac, mac):
355 raise SSHException('Mismatched MAC')
356 padding = byte_ord(packet[0])
357 payload = packet[1:packet_size - padding]
358
359 if self.__dump_packets:
360 self._log(DEBUG, 'Got payload (%d bytes, %d padding)' % (packet_size, padding))
361
362 if self.__compress_engine_in is not None:
363 payload = self.__compress_engine_in(payload)
364
365 msg = Message(payload[1:])
366 msg.seqno = self.__sequence_number_in
367 self.__sequence_number_in = (self.__sequence_number_in + 1) & xffffffff
368
369
370 raw_packet_size = packet_size + self.__mac_size_in + 4
371 self.__received_bytes += raw_packet_size
372 self.__received_packets += 1
373 if self.__need_rekey:
374
375
376 self.__received_bytes_overflow += raw_packet_size
377 self.__received_packets_overflow += 1
378 if (self.__received_packets_overflow >= self.REKEY_PACKETS_OVERFLOW_MAX) or \
379 (self.__received_bytes_overflow >= self.REKEY_BYTES_OVERFLOW_MAX):
380 raise SSHException('Remote transport is ignoring rekey requests')
381 elif (self.__received_packets >= self.REKEY_PACKETS) or \
382 (self.__received_bytes >= self.REKEY_BYTES):
383
384 self._log(DEBUG, 'Rekeying (hit %d packets, %d bytes received)' %
385 (self.__received_packets, self.__received_bytes))
386 self.__received_bytes_overflow = 0
387 self.__received_packets_overflow = 0
388 self._trigger_rekey()
389
390 cmd = byte_ord(payload[0])
391 if cmd in MSG_NAMES:
392 cmd_name = MSG_NAMES[cmd]
393 else:
394 cmd_name = '$%x' % cmd
395 if self.__dump_packets:
396 self._log(DEBUG, 'Read packet <%s>, length %d' % (cmd_name, len(payload)))
397 return cmd, msg
398
399
400
401 - def _log(self, level, msg):
402 if self.__logger is None:
403 return
404 if issubclass(type(msg), list):
405 for m in msg:
406 self.__logger.log(level, m)
407 else:
408 self.__logger.log(level, msg)
409
411 if (not self.__keepalive_interval) or (not self.__block_engine_out) or \
412 self.__need_rekey:
413
414 return
415 now = time.time()
416 if now > self.__keepalive_last + self.__keepalive_interval:
417 self.__keepalive_callback()
418 self.__keepalive_last = now
419
421 start = time.time()
422 while True:
423 try:
424 x = self.__socket.recv(128)
425 if len(x) == 0:
426 raise EOFError()
427 break
428 except socket.timeout:
429 pass
430 except EnvironmentError as e:
431 if (type(e.args) is tuple and len(e.args) > 0 and
432 e.args[0] == errno.EINTR):
433 pass
434 else:
435 raise
436 if self.__closed:
437 raise EOFError()
438 now = time.time()
439 if now - start >= timeout:
440 raise socket.timeout()
441 return x
442
444
445 bsize = self.__block_size_out
446 padding = 3 + bsize - ((len(payload) + 8) % bsize)
447 packet = struct.pack('>IB', len(payload) + padding + 1, padding)
448 packet += payload
449 if self.__sdctr_out or self.__block_engine_out is None:
450
451
452 packet += (zero_byte * padding)
453 else:
454 packet += os.urandom(padding)
455 return packet
456
458
459 self.__need_rekey = True
460