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