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