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