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, 30) 61 REKEY_BYTES = pow(2, 30) 62
63 - def __init__(self, socket):
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 # used for noticing when to re-key: 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 # current inbound/outbound ciphering: 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 # lock around outbound writes (packet computation) 96 self.__write_lock = threading.RLock() 97 98 # keepalives: 99 self.__keepalive_interval = 0 100 self.__keepalive_last = time.time() 101 self.__keepalive_callback = None
102
103 - def set_log(self, log):
104 """ 105 Set the python log object to use for logging. 106 """ 107 self.__logger = log
108
109 - def set_outbound_cipher(self, block_engine, block_size, mac_engine, mac_size, mac_key):
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 # wait until the reset happens in both directions before clearing rekey flag 121 self.__init_count |= 1 122 if self.__init_count == 3: 123 self.__init_count = 0 124 self.__need_rekey = False
125
126 - def set_inbound_cipher(self, block_engine, block_size, mac_engine, mac_size, mac_key):
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 # wait until the reset happens in both directions before clearing rekey flag 139 self.__init_count |= 2 140 if self.__init_count == 3: 141 self.__init_count = 0 142 self.__need_rekey = False
143
144 - def set_outbound_compressor(self, compressor):
145 self.__compress_engine_out = compressor
146
147 - def set_inbound_compressor(self, compressor):
148 self.__compress_engine_in = compressor
149
150 - def close(self):
151 self.__closed = True 152 self.__socket.close()
153
154 - def set_hexdump(self, hexdump):
155 self.__dump_packets = hexdump
156
157 - def get_hexdump(self):
158 return self.__dump_packets
159
160 - def get_mac_size_in(self):
161 return self.__mac_size_in
162
163 - def get_mac_size_out(self):
164 return self.__mac_size_out
165
166 - def need_rekey(self):
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
176 - def set_keepalive(self, interval, callback):
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 # handle over-reading from reading the banner line 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 # on Linux, sometimes instead of socket.timeout, we get 217 # EAGAIN. this is a bug in recent (> 2.6.9) kernels but 218 # we need to work around it. 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 # syscall interrupted; try again 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
236 - def write_all(self, out):
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 # syscall interrupted; try again 249 pass 250 else: 251 n = -1 252 except Exception: 253 # could be: (32, 'Broken pipe') 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
266 - def readline(self, timeout):
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
281 - def send_message(self, data):
282 """ 283 Write a block of data using the current cipher, as an SSH block. 284 """ 285 # encrypt this sucka 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 # + mac 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 # only ask once for rekeying 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
324 - def read_message(self):
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 # leftover contains decrypted bytes from the first block (after the length field) 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 # check for rekey 371 self.__received_bytes += packet_size + self.__mac_size_in + 4 372 self.__received_packets += 1 373 if self.__need_rekey: 374 # we've asked to rekey -- give them 20 packets to comply before 375 # dropping the connection 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 # only ask once for rekeying 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 ########## protected 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
409 - def _check_keepalive(self):
410 if (not self.__keepalive_interval) or (not self.__block_engine_out) or \ 411 self.__need_rekey: 412 # wait till we're encrypting, and not in the middle of rekeying 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
419 - def _py22_read_all(self, n, out):
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
434 - def _py22_read_timeout(self, timeout):
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
450 - def _read_timeout(self, timeout):
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
469 - def _build_packet(self, payload):
470 # pad up at least 4 bytes, to nearest block-size (usually 8) 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 # cute trick i caught openssh doing: if we're not encrypting, 479 # don't waste random bytes for the padding 480 packet += (chr(0) * padding) 481 return packet
482
483 - def _trigger_rekey(self):
484 # outside code should check for this flag 485 self.__need_rekey = True
486