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
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 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.__block_engine_out is not None:
492 packet += rng.read(padding)
493 else:
494
495
496 packet += (chr(0) * padding)
497 return packet
498
500
501 self.__need_rekey = True
502