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 
 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 got_timeout = False 245 try: 246 n = self.__socket.send(out) 247 except socket.timeout: 248 got_timeout = 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 got_timeout = 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 pass 255 else: 256 n = -1 257 except Exception: 258 # could be: (32, 'Broken pipe') 259 n = -1 260 if got_timeout: 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
271 - def readline(self, timeout):
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
286 - def send_message(self, data):
287 """ 288 Write a block of data using the current cipher, as an SSH block. 289 """ 290 # encrypt this sucka 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 # + mac 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 # only ask once for rekeying 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
330 - def read_message(self):
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 # leftover contains decrypted bytes from the first block (after the length field) 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 # check for rekey 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 # we've asked to rekey -- give them some packets to comply before 382 # dropping the connection 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 # only ask once for rekeying 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 ########## protected 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
419 - def _check_keepalive(self):
420 if (not self.__keepalive_interval) or (not self.__block_engine_out) or \ 421 self.__need_rekey: 422 # wait till we're encrypting, and not in the middle of rekeying 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
429 - def _py22_read_all(self, n, out):
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
444 - def _py22_read_timeout(self, timeout):
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
460 - def _read_timeout(self, timeout):
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 if self.__closed: 473 raise EOFError() 474 now = time.time() 475 if now - start >= timeout: 476 raise socket.timeout() 477 return x
478
479 - def _build_packet(self, payload):
480 # pad up at least 4 bytes, to nearest block-size (usually 8) 481 bsize = self.__block_size_out 482 padding = 3 + bsize - ((len(payload) + 8) % bsize) 483 packet = struct.pack('>IB', len(payload) + padding + 1, padding) 484 packet += payload 485 if self.__block_engine_out is not None: 486 packet += rng.read(padding) 487 else: 488 # cute trick i caught openssh doing: if we're not encrypting, 489 # don't waste random bytes for the padding 490 packet += (chr(0) * padding) 491 return packet
492
493 - def _trigger_rekey(self):
494 # outside code should check for this flag 495 self.__need_rekey = True
496