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 = self.__remainder
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 >= self.REKEY_PACKETS) or (self.__sent_bytes >= self.REKEY_BYTES)) \
315 and not self.__need_rekey:
316
317 self._log(DEBUG, 'Rekeying (hit %d packets, %d bytes sent)' %
318 (self.__sent_packets, self.__sent_bytes))
319 self.__received_packets_overflow = 0
320 self._trigger_rekey()
321 finally:
322 self.__write_lock.release()
323
325 """
326 Only one thread should ever be in this function (no other locking is
327 done).
328
329 @raise SSHException: if the packet is mangled
330 @raise NeedRekeyException: if the transport should rekey
331 """
332 header = self.read_all(self.__block_size_in, check_rekey=True)
333 if self.__block_engine_in != None:
334 header = self.__block_engine_in.decrypt(header)
335 if self.__dump_packets:
336 self._log(DEBUG, util.format_binary(header, 'IN: '));
337 packet_size = struct.unpack('>I', header[:4])[0]
338
339 leftover = header[4:]
340 if (packet_size - len(leftover)) % self.__block_size_in != 0:
341 raise SSHException('Invalid packet blocking')
342 buf = self.read_all(packet_size + self.__mac_size_in - len(leftover))
343 packet = buf[:packet_size - len(leftover)]
344 post_packet = buf[packet_size - len(leftover):]
345 if self.__block_engine_in != None:
346 packet = self.__block_engine_in.decrypt(packet)
347 if self.__dump_packets:
348 self._log(DEBUG, util.format_binary(packet, 'IN: '));
349 packet = leftover + packet
350
351 if self.__mac_size_in > 0:
352 mac = post_packet[:self.__mac_size_in]
353 mac_payload = struct.pack('>II', self.__sequence_number_in, packet_size) + packet
354 my_mac = compute_hmac(self.__mac_key_in, mac_payload, self.__mac_engine_in)[:self.__mac_size_in]
355 if my_mac != mac:
356 raise SSHException('Mismatched MAC')
357 padding = ord(packet[0])
358 payload = packet[1:packet_size - padding]
359
360 if self.__dump_packets:
361 self._log(DEBUG, 'Got payload (%d bytes, %d padding)' % (packet_size, padding))
362
363 if self.__compress_engine_in is not None:
364 payload = self.__compress_engine_in(payload)
365
366 msg = Message(payload[1:])
367 msg.seqno = self.__sequence_number_in
368 self.__sequence_number_in = (self.__sequence_number_in + 1) & 0xffffffffL
369
370
371 self.__received_bytes += packet_size + self.__mac_size_in + 4
372 self.__received_packets += 1
373 if self.__need_rekey:
374
375
376 self.__received_packets_overflow += 1
377 if self.__received_packets_overflow >= 20:
378 raise SSHException('Remote transport is ignoring rekey requests')
379 elif (self.__received_packets >= self.REKEY_PACKETS) or \
380 (self.__received_bytes >= self.REKEY_BYTES):
381
382 self._log(DEBUG, 'Rekeying (hit %d packets, %d bytes received)' %
383 (self.__received_packets, self.__received_bytes))
384 self.__received_packets_overflow = 0
385 self._trigger_rekey()
386
387 cmd = ord(payload[0])
388 if cmd in MSG_NAMES:
389 cmd_name = MSG_NAMES[cmd]
390 else:
391 cmd_name = '$%x' % cmd
392 if self.__dump_packets:
393 self._log(DEBUG, 'Read packet <%s>, length %d' % (cmd_name, len(payload)))
394 return cmd, msg
395
396
397
398
399
400 - def _log(self, level, msg):
401 if self.__logger is None:
402 return
403 if issubclass(type(msg), list):
404 for m in msg:
405 self.__logger.log(level, m)
406 else:
407 self.__logger.log(level, msg)
408
410 if (not self.__keepalive_interval) or (not self.__block_engine_out) or \
411 self.__need_rekey:
412
413 return
414 now = time.time()
415 if now > self.__keepalive_last + self.__keepalive_interval:
416 self.__keepalive_callback()
417 self.__keepalive_last = now
418
420 while n > 0:
421 r, w, e = select.select([self.__socket], [], [], 0.1)
422 if self.__socket not in r:
423 if self.__closed:
424 raise EOFError()
425 self._check_keepalive()
426 else:
427 x = self.__socket.recv(n)
428 if len(x) == 0:
429 raise EOFError()
430 out += x
431 n -= len(x)
432 return out
433
435 start = time.time()
436 while True:
437 r, w, e = select.select([self.__socket], [], [], 0.1)
438 if self.__socket in r:
439 x = self.__socket.recv(1)
440 if len(x) == 0:
441 raise EOFError()
442 break
443 if self.__closed:
444 raise EOFError()
445 now = time.time()
446 if now - start >= timeout:
447 raise socket.timeout()
448 return x
449
451 if PY22:
452 return self._py22_read_timeout(timeout)
453 start = time.time()
454 while True:
455 try:
456 x = self.__socket.recv(128)
457 if len(x) == 0:
458 raise EOFError()
459 break
460 except socket.timeout:
461 pass
462 if self.__closed:
463 raise EOFError()
464 now = time.time()
465 if now - start >= timeout:
466 raise socket.timeout()
467 return x
468
470
471 bsize = self.__block_size_out
472 padding = 3 + bsize - ((len(payload) + 8) % bsize)
473 packet = struct.pack('>IB', len(payload) + padding + 1, padding)
474 packet += payload
475 if self.__block_engine_out is not None:
476 packet += rng.read(padding)
477 else:
478
479
480 packet += (chr(0) * padding)
481 return packet
482
484
485 self.__need_rekey = True
486