Package paramiko :: Module packet
[frames] | no frames]

Source Code for Module paramiko.packet

  1  # Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com> 
  2  # 
  3  # This file is part of paramiko. 
  4  # 
  5  # Paramiko is free software; you can redistribute it and/or modify it under the 
  6  # terms of the GNU Lesser General Public License as published by the Free 
  7  # Software Foundation; either version 2.1 of the License, or (at your option) 
  8  # any later version. 
  9  # 
 10  # Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY 
 11  # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 
 12  # A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
 13  # details. 
 14  # 
 15  # You should have received a copy of the GNU Lesser General Public License 
 16  # along with Paramiko; if not, write to the Free Software Foundation, Inc., 
 17  # 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA. 
 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 
42 -def compute_hmac(key, message, digest_class):
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
49 -class NeedRekeyException (Exception):
50 pass
51 52
53 -class Packetizer (object):
54 """ 55 Implementation of the base SSH packet protocol. 56 """ 57 58 # READ the secsh RFC's before raising these values. if anything, 59 # they should probably be lower. 60 REKEY_PACKETS = pow(2, 29) 61 REKEY_BYTES = pow(2, 29) 62 63 REKEY_PACKETS_OVERFLOW_MAX = pow(2,29) # Allow receiving this many packets after a re-key request before terminating 64 REKEY_BYTES_OVERFLOW_MAX = pow(2,29) # Allow receiving this many bytes after a re-key request before terminating 65
66 - def __init__(self, socket):
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 # used for noticing when to re-key: 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 # current inbound/outbound ciphering: 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 # lock around outbound writes (packet computation) 100 self.__write_lock = threading.RLock() 101 102 # keepalives: 103 self.__keepalive_interval = 0 104 self.__keepalive_last = time.time() 105 self.__keepalive_callback = None
106
107 - def set_log(self, log):
108 """ 109 Set the python log object to use for logging. 110 """ 111 self.__logger = log
112
113 - def set_outbound_cipher(self, block_engine, block_size, mac_engine, mac_size, mac_key):
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 # wait until the reset happens in both directions before clearing rekey flag 125 self.__init_count |= 1 126 if self.__init_count == 3: 127 self.__init_count = 0 128 self.__need_rekey = False
129
130 - def set_inbound_cipher(self, block_engine, block_size, mac_engine, mac_size, mac_key):
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 # wait until the reset happens in both directions before clearing rekey flag 144 self.__init_count |= 2 145 if self.__init_count == 3: 146 self.__init_count = 0 147 self.__need_rekey = False
148
149 - def set_outbound_compressor(self, compressor):
150 self.__compress_engine_out = compressor
151
152 - def set_inbound_compressor(self, compressor):
153 self.__compress_engine_in = compressor
154
155 - def close(self):
156 self.__closed = True 157 self.__socket.close()
158
159 - def set_hexdump(self, hexdump):
160 self.__dump_packets = hexdump
161
162 - def get_hexdump(self):
163 return self.__dump_packets
164
165 - def get_mac_size_in(self):
166 return self.__mac_size_in
167
168 - def get_mac_size_out(self):
169 return self.__mac_size_out
170
171 - def need_rekey(self):
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
181 - def set_keepalive(self, interval, callback):
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 # handle over-reading from reading the banner line 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 # on Linux, sometimes instead of socket.timeout, we get 222 # EAGAIN. this is a bug in recent (> 2.6.9) kernels but 223 # we need to work around it. 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 # syscall interrupted; try again 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
241 - def write_all(self, out):
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 # syscall interrupted; try again 254 retry_write = True 255 else: 256 n = -1 257 except ProxyCommandFailure: 258 raise # so it doesn't get swallowed by the below catchall 259 except Exception: 260 # could be: (32, 'Broken pipe') 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
273 - def readline(self, timeout):
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
288 - def send_message(self, data):
289 """ 290 Write a block of data using the current cipher, as an SSH block. 291 """ 292 # encrypt this sucka 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 # + mac 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 # only ask once for rekeying 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
332 - def read_message(self):
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 # leftover contains decrypted bytes from the first block (after the length field) 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 # check for rekey 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 # we've asked to rekey -- give them some packets to comply before 384 # dropping the connection 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 # only ask once for rekeying 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 ########## protected 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
421 - def _check_keepalive(self):
422 if (not self.__keepalive_interval) or (not self.__block_engine_out) or \ 423 self.__need_rekey: 424 # wait till we're encrypting, and not in the middle of rekeying 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
431 - def _py22_read_all(self, n, out):
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
446 - def _py22_read_timeout(self, timeout):
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
462 - def _read_timeout(self, timeout):
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
487 - def _build_packet(self, payload):
488 # pad up at least 4 bytes, to nearest block-size (usually 8) 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 # cute trick i caught openssh doing: if we're not encrypting, 497 # don't waste random bytes for the padding 498 packet += (chr(0) * padding) 499 return packet
500
501 - def _trigger_rekey(self):
502 # outside code should check for this flag 503 self.__need_rekey = True
504