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 distributed 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  try: 
 37      from r_hmac import HMAC 
 38  except ImportError: 
 39      from Crypto.Hash.HMAC import HMAC 
 40   
41 -def compute_hmac(key, message, digest_class):
42 return HMAC(key, message, digest_class).digest()
43 44
45 -class NeedRekeyException (Exception):
46 pass
47 48
49 -class Packetizer (object):
50 """ 51 Implementation of the base SSH packet protocol. 52 """ 53 54 # READ the secsh RFC's before raising these values. if anything, 55 # they should probably be lower. 56 REKEY_PACKETS = pow(2, 29) 57 REKEY_BYTES = pow(2, 29) 58 59 REKEY_PACKETS_OVERFLOW_MAX = pow(2,29) # Allow receiving this many packets after a re-key request before terminating 60 REKEY_BYTES_OVERFLOW_MAX = pow(2,29) # Allow receiving this many bytes after a re-key request before terminating 61
62 - def __init__(self, socket):
63 self.__socket = socket 64 self.__logger = None 65 self.__closed = False 66 self.__dump_packets = False 67 self.__need_rekey = False 68 self.__init_count = 0 69 self.__remainder = '' 70 71 # used for noticing when to re-key: 72 self.__sent_bytes = 0 73 self.__sent_packets = 0 74 self.__received_bytes = 0 75 self.__received_packets = 0 76 self.__received_bytes_overflow = 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.__sdctr_out = False 87 self.__mac_engine_out = None 88 self.__mac_engine_in = None 89 self.__mac_key_out = '' 90 self.__mac_key_in = '' 91 self.__compress_engine_out = None 92 self.__compress_engine_in = None 93 self.__sequence_number_out = 0L 94 self.__sequence_number_in = 0L 95 96 # lock around outbound writes (packet computation) 97 self.__write_lock = threading.RLock() 98 99 # keepalives: 100 self.__keepalive_interval = 0 101 self.__keepalive_last = time.time() 102 self.__keepalive_callback = None
103
104 - def set_log(self, log):
105 """ 106 Set the python log object to use for logging. 107 """ 108 self.__logger = log
109
110 - def set_outbound_cipher(self, block_engine, block_size, mac_engine, mac_size, mac_key, sdctr=False):
111 """ 112 Switch outbound data cipher. 113 """ 114 self.__block_engine_out = block_engine 115 self.__sdctr_out = sdctr 116 self.__block_size_out = block_size 117 self.__mac_engine_out = mac_engine 118 self.__mac_size_out = mac_size 119 self.__mac_key_out = mac_key 120 self.__sent_bytes = 0 121 self.__sent_packets = 0 122 # wait until the reset happens in both directions before clearing rekey flag 123 self.__init_count |= 1 124 if self.__init_count == 3: 125 self.__init_count = 0 126 self.__need_rekey = False
127
128 - def set_inbound_cipher(self, block_engine, block_size, mac_engine, mac_size, mac_key):
129 """ 130 Switch inbound data cipher. 131 """ 132 self.__block_engine_in = block_engine 133 self.__block_size_in = block_size 134 self.__mac_engine_in = mac_engine 135 self.__mac_size_in = mac_size 136 self.__mac_key_in = mac_key 137 self.__received_bytes = 0 138 self.__received_packets = 0 139 self.__received_bytes_overflow = 0 140 self.__received_packets_overflow = 0 141 # wait until the reset happens in both directions before clearing rekey flag 142 self.__init_count |= 2 143 if self.__init_count == 3: 144 self.__init_count = 0 145 self.__need_rekey = False
146
147 - def set_outbound_compressor(self, compressor):
148 self.__compress_engine_out = compressor
149
150 - def set_inbound_compressor(self, compressor):
151 self.__compress_engine_in = compressor
152
153 - def close(self):
154 self.__closed = True
155
156 - def set_hexdump(self, hexdump):
157 self.__dump_packets = hexdump
158
159 - def get_hexdump(self):
160 return self.__dump_packets
161
162 - def get_mac_size_in(self):
163 return self.__mac_size_in
164
165 - def get_mac_size_out(self):
166 return self.__mac_size_out
167
168 - def need_rekey(self):
169 """ 170 Returns C{True} if a new set of keys needs to be negotiated. This 171 will be triggered during a packet read or write, so it should be 172 checked after every read or write, or at least after every few. 173 174 @return: C{True} if a new set of keys needs to be negotiated 175 """ 176 return self.__need_rekey
177
178 - def set_keepalive(self, interval, callback):
179 """ 180 Turn on/off the callback keepalive. If C{interval} seconds pass with 181 no data read from or written to the socket, the callback will be 182 executed and the timer will be reset. 183 """ 184 self.__keepalive_interval = interval 185 self.__keepalive_callback = callback 186 self.__keepalive_last = time.time()
187
188 - def read_all(self, n, check_rekey=False):
189 """ 190 Read as close to N bytes as possible, blocking as long as necessary. 191 192 @param n: number of bytes to read 193 @type n: int 194 @return: the data read 195 @rtype: str 196 @raise EOFError: if the socket was closed before all the bytes could 197 be read 198 """ 199 out = '' 200 # handle over-reading from reading the banner line 201 if len(self.__remainder) > 0: 202 out = self.__remainder[:n] 203 self.__remainder = self.__remainder[n:] 204 n -= len(out) 205 if PY22: 206 return self._py22_read_all(n, out) 207 while n > 0: 208 got_timeout = False 209 try: 210 x = self.__socket.recv(n) 211 if len(x) == 0: 212 raise EOFError() 213 out += x 214 n -= len(x) 215 except socket.timeout: 216 got_timeout = True 217 except socket.error, e: 218 # on Linux, sometimes instead of socket.timeout, we get 219 # EAGAIN. this is a bug in recent (> 2.6.9) kernels but 220 # we need to work around it. 221 if (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EAGAIN): 222 got_timeout = True 223 elif (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EINTR): 224 # syscall interrupted; try again 225 pass 226 elif self.__closed: 227 raise EOFError() 228 else: 229 raise 230 if got_timeout: 231 if self.__closed: 232 raise EOFError() 233 if check_rekey and (len(out) == 0) and self.__need_rekey: 234 raise NeedRekeyException() 235 self._check_keepalive() 236 return out
237
238 - def write_all(self, out):
239 self.__keepalive_last = time.time() 240 while len(out) > 0: 241 retry_write = False 242 try: 243 n = self.__socket.send(out) 244 except socket.timeout: 245 retry_write = True 246 except socket.error, e: 247 if (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EAGAIN): 248 retry_write = True 249 elif (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EINTR): 250 # syscall interrupted; try again 251 retry_write = True 252 else: 253 n = -1 254 except ProxyCommandFailure: 255 raise # so it doesn't get swallowed by the below catchall 256 except Exception: 257 # could be: (32, 'Broken pipe') 258 n = -1 259 if retry_write: 260 n = 0 261 if self.__closed: 262 n = -1 263 if n < 0: 264 raise EOFError() 265 if n == len(out): 266 break 267 out = out[n:] 268 return
269
270 - def readline(self, timeout):
271 """ 272 Read a line from the socket. We assume no data is pending after the 273 line, so it's okay to attempt large reads. 274 """ 275 buf = self.__remainder 276 while not '\n' in buf: 277 buf += self._read_timeout(timeout) 278 n = buf.index('\n') 279 self.__remainder = buf[n+1:] 280 buf = buf[:n] 281 if (len(buf) > 0) and (buf[-1] == '\r'): 282 buf = buf[:-1] 283 return buf
284
285 - def send_message(self, data):
286 """ 287 Write a block of data using the current cipher, as an SSH block. 288 """ 289 # encrypt this sucka 290 data = str(data) 291 cmd = ord(data[0]) 292 if cmd in MSG_NAMES: 293 cmd_name = MSG_NAMES[cmd] 294 else: 295 cmd_name = '$%x' % cmd 296 orig_len = len(data) 297 self.__write_lock.acquire() 298 try: 299 if self.__compress_engine_out is not None: 300 data = self.__compress_engine_out(data) 301 packet = self._build_packet(data) 302 if self.__dump_packets: 303 self._log(DEBUG, 'Write packet <%s>, length %d' % (cmd_name, orig_len)) 304 self._log(DEBUG, util.format_binary(packet, 'OUT: ')) 305 if self.__block_engine_out != None: 306 out = self.__block_engine_out.encrypt(packet) 307 else: 308 out = packet 309 # + mac 310 if self.__block_engine_out != None: 311 payload = struct.pack('>I', self.__sequence_number_out) + packet 312 out += compute_hmac(self.__mac_key_out, payload, self.__mac_engine_out)[:self.__mac_size_out] 313 self.__sequence_number_out = (self.__sequence_number_out + 1) & 0xffffffffL 314 self.write_all(out) 315 316 self.__sent_bytes += len(out) 317 self.__sent_packets += 1 318 if ((self.__sent_packets >= self.REKEY_PACKETS) or (self.__sent_bytes >= self.REKEY_BYTES)) \ 319 and not self.__need_rekey: 320 # only ask once for rekeying 321 self._log(DEBUG, 'Rekeying (hit %d packets, %d bytes sent)' % 322 (self.__sent_packets, self.__sent_bytes)) 323 self.__received_bytes_overflow = 0 324 self.__received_packets_overflow = 0 325 self._trigger_rekey() 326 finally: 327 self.__write_lock.release()
328
329 - def read_message(self):
330 """ 331 Only one thread should ever be in this function (no other locking is 332 done). 333 334 @raise SSHException: if the packet is mangled 335 @raise NeedRekeyException: if the transport should rekey 336 """ 337 header = self.read_all(self.__block_size_in, check_rekey=True) 338 if self.__block_engine_in != None: 339 header = self.__block_engine_in.decrypt(header) 340 if self.__dump_packets: 341 self._log(DEBUG, util.format_binary(header, 'IN: ')); 342 packet_size = struct.unpack('>I', header[:4])[0] 343 # leftover contains decrypted bytes from the first block (after the length field) 344 leftover = header[4:] 345 if (packet_size - len(leftover)) % self.__block_size_in != 0: 346 raise SSHException('Invalid packet blocking') 347 buf = self.read_all(packet_size + self.__mac_size_in - len(leftover)) 348 packet = buf[:packet_size - len(leftover)] 349 post_packet = buf[packet_size - len(leftover):] 350 if self.__block_engine_in != None: 351 packet = self.__block_engine_in.decrypt(packet) 352 if self.__dump_packets: 353 self._log(DEBUG, util.format_binary(packet, 'IN: ')); 354 packet = leftover + packet 355 356 if self.__mac_size_in > 0: 357 mac = post_packet[:self.__mac_size_in] 358 mac_payload = struct.pack('>II', self.__sequence_number_in, packet_size) + packet 359 my_mac = compute_hmac(self.__mac_key_in, mac_payload, self.__mac_engine_in)[:self.__mac_size_in] 360 if my_mac != mac: 361 raise SSHException('Mismatched MAC') 362 padding = ord(packet[0]) 363 payload = packet[1:packet_size - padding] 364 365 if self.__dump_packets: 366 self._log(DEBUG, 'Got payload (%d bytes, %d padding)' % (packet_size, padding)) 367 368 if self.__compress_engine_in is not None: 369 payload = self.__compress_engine_in(payload) 370 371 msg = Message(payload[1:]) 372 msg.seqno = self.__sequence_number_in 373 self.__sequence_number_in = (self.__sequence_number_in + 1) & 0xffffffffL 374 375 # check for rekey 376 raw_packet_size = packet_size + self.__mac_size_in + 4 377 self.__received_bytes += raw_packet_size 378 self.__received_packets += 1 379 if self.__need_rekey: 380 # we've asked to rekey -- give them some packets to comply before 381 # dropping the connection 382 self.__received_bytes_overflow += raw_packet_size 383 self.__received_packets_overflow += 1 384 if (self.__received_packets_overflow >= self.REKEY_PACKETS_OVERFLOW_MAX) or \ 385 (self.__received_bytes_overflow >= self.REKEY_BYTES_OVERFLOW_MAX): 386 raise SSHException('Remote transport is ignoring rekey requests') 387 elif (self.__received_packets >= self.REKEY_PACKETS) or \ 388 (self.__received_bytes >= self.REKEY_BYTES): 389 # only ask once for rekeying 390 self._log(DEBUG, 'Rekeying (hit %d packets, %d bytes received)' % 391 (self.__received_packets, self.__received_bytes)) 392 self.__received_bytes_overflow = 0 393 self.__received_packets_overflow = 0 394 self._trigger_rekey() 395 396 cmd = ord(payload[0]) 397 if cmd in MSG_NAMES: 398 cmd_name = MSG_NAMES[cmd] 399 else: 400 cmd_name = '$%x' % cmd 401 if self.__dump_packets: 402 self._log(DEBUG, 'Read packet <%s>, length %d' % (cmd_name, len(payload))) 403 return cmd, msg
404 405 406 ########## protected 407 408
409 - def _log(self, level, msg):
410 if self.__logger is None: 411 return 412 if issubclass(type(msg), list): 413 for m in msg: 414 self.__logger.log(level, m) 415 else: 416 self.__logger.log(level, msg)
417
418 - def _check_keepalive(self):
419 if (not self.__keepalive_interval) or (not self.__block_engine_out) or \ 420 self.__need_rekey: 421 # wait till we're encrypting, and not in the middle of rekeying 422 return 423 now = time.time() 424 if now > self.__keepalive_last + self.__keepalive_interval: 425 self.__keepalive_callback() 426 self.__keepalive_last = now
427
428 - def _py22_read_all(self, n, out):
429 while n > 0: 430 r, w, e = select.select([self.__socket], [], [], 0.1) 431 if self.__socket not in r: 432 if self.__closed: 433 raise EOFError() 434 self._check_keepalive() 435 else: 436 x = self.__socket.recv(n) 437 if len(x) == 0: 438 raise EOFError() 439 out += x 440 n -= len(x) 441 return out
442
443 - def _py22_read_timeout(self, timeout):
444 start = time.time() 445 while True: 446 r, w, e = select.select([self.__socket], [], [], 0.1) 447 if self.__socket in r: 448 x = self.__socket.recv(1) 449 if len(x) == 0: 450 raise EOFError() 451 break 452 if self.__closed: 453 raise EOFError() 454 now = time.time() 455 if now - start >= timeout: 456 raise socket.timeout() 457 return x
458
459 - def _read_timeout(self, timeout):
460 if PY22: 461 return self._py22_read_timeout(timeout) 462 start = time.time() 463 while True: 464 try: 465 x = self.__socket.recv(128) 466 if len(x) == 0: 467 raise EOFError() 468 break 469 except socket.timeout: 470 pass 471 except EnvironmentError, e: 472 if ((type(e.args) is tuple) and (len(e.args) > 0) and 473 (e.args[0] == errno.EINTR)): 474 pass 475 else: 476 raise 477 if self.__closed: 478 raise EOFError() 479 now = time.time() 480 if now - start >= timeout: 481 raise socket.timeout() 482 return x
483
484 - def _build_packet(self, payload):
485 # pad up at least 4 bytes, to nearest block-size (usually 8) 486 bsize = self.__block_size_out 487 padding = 3 + bsize - ((len(payload) + 8) % bsize) 488 packet = struct.pack('>IB', len(payload) + padding + 1, padding) 489 packet += payload 490 if self.__sdctr_out or self.__block_engine_out is None: 491 # cute trick i caught openssh doing: if we're not encrypting or SDCTR mode (RFC4344), 492 # don't waste random bytes for the padding 493 packet += (chr(0) * padding) 494 else: 495 packet += rng.read(padding) 496 return packet
497
498 - def _trigger_rekey(self):
499 # outside code should check for this flag 500 self.__need_rekey = True
501