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
155
157 self.__dump_packets = hexdump
158
160 return self.__dump_packets
161
163 return self.__mac_size_in
164
166 return self.__mac_size_out
167
169 """
170 Returns C{True} if a new set of keys needs to be negotiated. This
171 will be triggered during a packet read or write, so it should be
172 checked after every read or write, or at least after every few.
173
174 @return: C{True} if a new set of keys needs to be negotiated
175 """
176 return self.__need_rekey
177
179 """
180 Turn on/off the callback keepalive. If C{interval} seconds pass with
181 no data read from or written to the socket, the callback will be
182 executed and the timer will be reset.
183 """
184 self.__keepalive_interval = interval
185 self.__keepalive_callback = callback
186 self.__keepalive_last = time.time()
187
188 - def read_all(self, n, check_rekey=False):
189 """
190 Read as close to N bytes as possible, blocking as long as necessary.
191
192 @param n: number of bytes to read
193 @type n: int
194 @return: the data read
195 @rtype: str
196 @raise EOFError: if the socket was closed before all the bytes could
197 be read
198 """
199 out = ''
200
201 if len(self.__remainder) > 0:
202 out = self.__remainder[:n]
203 self.__remainder = self.__remainder[n:]
204 n -= len(out)
205 if PY22:
206 return self._py22_read_all(n, out)
207 while n > 0:
208 got_timeout = False
209 try:
210 x = self.__socket.recv(n)
211 if len(x) == 0:
212 raise EOFError()
213 out += x
214 n -= len(x)
215 except socket.timeout:
216 got_timeout = True
217 except socket.error, e:
218
219
220
221 if (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EAGAIN):
222 got_timeout = True
223 elif (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EINTR):
224
225 pass
226 elif self.__closed:
227 raise EOFError()
228 else:
229 raise
230 if got_timeout:
231 if self.__closed:
232 raise EOFError()
233 if check_rekey and (len(out) == 0) and self.__need_rekey:
234 raise NeedRekeyException()
235 self._check_keepalive()
236 return out
237
239 self.__keepalive_last = time.time()
240 while len(out) > 0:
241 retry_write = False
242 try:
243 n = self.__socket.send(out)
244 except socket.timeout:
245 retry_write = True
246 except socket.error, e:
247 if (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EAGAIN):
248 retry_write = True
249 elif (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EINTR):
250
251 retry_write = True
252 else:
253 n = -1
254 except ProxyCommandFailure:
255 raise
256 except Exception:
257
258 n = -1
259 if retry_write:
260 n = 0
261 if self.__closed:
262 n = -1
263 if n < 0:
264 raise EOFError()
265 if n == len(out):
266 break
267 out = out[n:]
268 return
269
271 """
272 Read a line from the socket. We assume no data is pending after the
273 line, so it's okay to attempt large reads.
274 """
275 buf = self.__remainder
276 while not '\n' in buf:
277 buf += self._read_timeout(timeout)
278 n = buf.index('\n')
279 self.__remainder = buf[n+1:]
280 buf = buf[:n]
281 if (len(buf) > 0) and (buf[-1] == '\r'):
282 buf = buf[:-1]
283 return buf
284
286 """
287 Write a block of data using the current cipher, as an SSH block.
288 """
289
290 data = str(data)
291 cmd = ord(data[0])
292 if cmd in MSG_NAMES:
293 cmd_name = MSG_NAMES[cmd]
294 else:
295 cmd_name = '$%x' % cmd
296 orig_len = len(data)
297 self.__write_lock.acquire()
298 try:
299 if self.__compress_engine_out is not None:
300 data = self.__compress_engine_out(data)
301 packet = self._build_packet(data)
302 if self.__dump_packets:
303 self._log(DEBUG, 'Write packet <%s>, length %d' % (cmd_name, orig_len))
304 self._log(DEBUG, util.format_binary(packet, 'OUT: '))
305 if self.__block_engine_out != None:
306 out = self.__block_engine_out.encrypt(packet)
307 else:
308 out = packet
309
310 if self.__block_engine_out != None:
311 payload = struct.pack('>I', self.__sequence_number_out) + packet
312 out += compute_hmac(self.__mac_key_out, payload, self.__mac_engine_out)[:self.__mac_size_out]
313 self.__sequence_number_out = (self.__sequence_number_out + 1) & 0xffffffffL
314 self.write_all(out)
315
316 self.__sent_bytes += len(out)
317 self.__sent_packets += 1
318 if ((self.__sent_packets >= self.REKEY_PACKETS) or (self.__sent_bytes >= self.REKEY_BYTES)) \
319 and not self.__need_rekey:
320
321 self._log(DEBUG, 'Rekeying (hit %d packets, %d bytes sent)' %
322 (self.__sent_packets, self.__sent_bytes))
323 self.__received_bytes_overflow = 0
324 self.__received_packets_overflow = 0
325 self._trigger_rekey()
326 finally:
327 self.__write_lock.release()
328
330 """
331 Only one thread should ever be in this function (no other locking is
332 done).
333
334 @raise SSHException: if the packet is mangled
335 @raise NeedRekeyException: if the transport should rekey
336 """
337 header = self.read_all(self.__block_size_in, check_rekey=True)
338 if self.__block_engine_in != None:
339 header = self.__block_engine_in.decrypt(header)
340 if self.__dump_packets:
341 self._log(DEBUG, util.format_binary(header, 'IN: '));
342 packet_size = struct.unpack('>I', header[:4])[0]
343
344 leftover = header[4:]
345 if (packet_size - len(leftover)) % self.__block_size_in != 0:
346 raise SSHException('Invalid packet blocking')
347 buf = self.read_all(packet_size + self.__mac_size_in - len(leftover))
348 packet = buf[:packet_size - len(leftover)]
349 post_packet = buf[packet_size - len(leftover):]
350 if self.__block_engine_in != None:
351 packet = self.__block_engine_in.decrypt(packet)
352 if self.__dump_packets:
353 self._log(DEBUG, util.format_binary(packet, 'IN: '));
354 packet = leftover + packet
355
356 if self.__mac_size_in > 0:
357 mac = post_packet[:self.__mac_size_in]
358 mac_payload = struct.pack('>II', self.__sequence_number_in, packet_size) + packet
359 my_mac = compute_hmac(self.__mac_key_in, mac_payload, self.__mac_engine_in)[:self.__mac_size_in]
360 if my_mac != mac:
361 raise SSHException('Mismatched MAC')
362 padding = ord(packet[0])
363 payload = packet[1:packet_size - padding]
364
365 if self.__dump_packets:
366 self._log(DEBUG, 'Got payload (%d bytes, %d padding)' % (packet_size, padding))
367
368 if self.__compress_engine_in is not None:
369 payload = self.__compress_engine_in(payload)
370
371 msg = Message(payload[1:])
372 msg.seqno = self.__sequence_number_in
373 self.__sequence_number_in = (self.__sequence_number_in + 1) & 0xffffffffL
374
375
376 raw_packet_size = packet_size + self.__mac_size_in + 4
377 self.__received_bytes += raw_packet_size
378 self.__received_packets += 1
379 if self.__need_rekey:
380
381
382 self.__received_bytes_overflow += raw_packet_size
383 self.__received_packets_overflow += 1
384 if (self.__received_packets_overflow >= self.REKEY_PACKETS_OVERFLOW_MAX) or \
385 (self.__received_bytes_overflow >= self.REKEY_BYTES_OVERFLOW_MAX):
386 raise SSHException('Remote transport is ignoring rekey requests')
387 elif (self.__received_packets >= self.REKEY_PACKETS) or \
388 (self.__received_bytes >= self.REKEY_BYTES):
389
390 self._log(DEBUG, 'Rekeying (hit %d packets, %d bytes received)' %
391 (self.__received_packets, self.__received_bytes))
392 self.__received_bytes_overflow = 0
393 self.__received_packets_overflow = 0
394 self._trigger_rekey()
395
396 cmd = ord(payload[0])
397 if cmd in MSG_NAMES:
398 cmd_name = MSG_NAMES[cmd]
399 else:
400 cmd_name = '$%x' % cmd
401 if self.__dump_packets:
402 self._log(DEBUG, 'Read packet <%s>, length %d' % (cmd_name, len(payload)))
403 return cmd, msg
404
405
406
407
408
409 - def _log(self, level, msg):
410 if self.__logger is None:
411 return
412 if issubclass(type(msg), list):
413 for m in msg:
414 self.__logger.log(level, m)
415 else:
416 self.__logger.log(level, msg)
417
419 if (not self.__keepalive_interval) or (not self.__block_engine_out) or \
420 self.__need_rekey:
421
422 return
423 now = time.time()
424 if now > self.__keepalive_last + self.__keepalive_interval:
425 self.__keepalive_callback()
426 self.__keepalive_last = now
427
429 while n > 0:
430 r, w, e = select.select([self.__socket], [], [], 0.1)
431 if self.__socket not in r:
432 if self.__closed:
433 raise EOFError()
434 self._check_keepalive()
435 else:
436 x = self.__socket.recv(n)
437 if len(x) == 0:
438 raise EOFError()
439 out += x
440 n -= len(x)
441 return out
442
444 start = time.time()
445 while True:
446 r, w, e = select.select([self.__socket], [], [], 0.1)
447 if self.__socket in r:
448 x = self.__socket.recv(1)
449 if len(x) == 0:
450 raise EOFError()
451 break
452 if self.__closed:
453 raise EOFError()
454 now = time.time()
455 if now - start >= timeout:
456 raise socket.timeout()
457 return x
458
460 if PY22:
461 return self._py22_read_timeout(timeout)
462 start = time.time()
463 while True:
464 try:
465 x = self.__socket.recv(128)
466 if len(x) == 0:
467 raise EOFError()
468 break
469 except socket.timeout:
470 pass
471 except EnvironmentError, e:
472 if ((type(e.args) is tuple) and (len(e.args) > 0) and
473 (e.args[0] == errno.EINTR)):
474 pass
475 else:
476 raise
477 if self.__closed:
478 raise EOFError()
479 now = time.time()
480 if now - start >= timeout:
481 raise socket.timeout()
482 return x
483
485
486 bsize = self.__block_size_out
487 padding = 3 + bsize - ((len(payload) + 8) % bsize)
488 packet = struct.pack('>IB', len(payload) + padding + 1, padding)
489 packet += payload
490 if self.__sdctr_out or self.__block_engine_out is None:
491
492
493 packet += (chr(0) * padding)
494 else:
495 packet += rng.read(padding)
496 return packet
497
499
500 self.__need_rekey = True
501