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 self.__socket.close()
156
157 - def set_hexdump(self, hexdump):
158 self.__dump_packets = hexdump
159
160 - def get_hexdump(self):
161 return self.__dump_packets
162
163 - def get_mac_size_in(self):
164 return self.__mac_size_in
165
166 - def get_mac_size_out(self):
167 return self.__mac_size_out
168
169 - def need_rekey(self):
170 """ 171 Returns C{True} if a new set of keys needs to be negotiated. This 172 will be triggered during a packet read or write, so it should be 173 checked after every read or write, or at least after every few. 174 175 @return: C{True} if a new set of keys needs to be negotiated 176 """ 177 return self.__need_rekey
178
179 - def set_keepalive(self, interval, callback):
180 """ 181 Turn on/off the callback keepalive. If C{interval} seconds pass with 182 no data read from or written to the socket, the callback will be 183 executed and the timer will be reset. 184 """ 185 self.__keepalive_interval = interval 186 self.__keepalive_callback = callback 187 self.__keepalive_last = time.time()
188
189 - def read_all(self, n, check_rekey=False):
190 """ 191 Read as close to N bytes as possible, blocking as long as necessary. 192 193 @param n: number of bytes to read 194 @type n: int 195 @return: the data read 196 @rtype: str 197 @raise EOFError: if the socket was closed before all the bytes could 198 be read 199 """ 200 out = '' 201 # handle over-reading from reading the banner line 202 if len(self.__remainder) > 0: 203 out = self.__remainder[:n] 204 self.__remainder = self.__remainder[n:] 205 n -= len(out) 206 if PY22: 207 return self._py22_read_all(n, out) 208 while n > 0: 209 got_timeout = False 210 try: 211 x = self.__socket.recv(n) 212 if len(x) == 0: 213 raise EOFError() 214 out += x 215 n -= len(x) 216 except socket.timeout: 217 got_timeout = True 218 except socket.error, e: 219 # on Linux, sometimes instead of socket.timeout, we get 220 # EAGAIN. this is a bug in recent (> 2.6.9) kernels but 221 # we need to work around it. 222 if (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EAGAIN): 223 got_timeout = True 224 elif (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EINTR): 225 # syscall interrupted; try again 226 pass 227 elif self.__closed: 228 raise EOFError() 229 else: 230 raise 231 if got_timeout: 232 if self.__closed: 233 raise EOFError() 234 if check_rekey and (len(out) == 0) and self.__need_rekey: 235 raise NeedRekeyException() 236 self._check_keepalive() 237 return out
238
239 - def write_all(self, out):
240 self.__keepalive_last = time.time() 241 while len(out) > 0: 242 retry_write = False 243 try: 244 n = self.__socket.send(out) 245 except socket.timeout: 246 retry_write = True 247 except socket.error, e: 248 if (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EAGAIN): 249 retry_write = True 250 elif (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EINTR): 251 # syscall interrupted; try again 252 retry_write = True 253 else: 254 n = -1 255 except ProxyCommandFailure: 256 raise # so it doesn't get swallowed by the below catchall 257 except Exception: 258 # could be: (32, 'Broken pipe') 259 n = -1 260 if retry_write: 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 except EnvironmentError, e: 473 if ((type(e.args) is tuple) and (len(e.args) > 0) and 474 (e.args[0] == errno.EINTR)): 475 pass 476 else: 477 raise 478 if self.__closed: 479 raise EOFError() 480 now = time.time() 481 if now - start >= timeout: 482 raise socket.timeout() 483 return x
484
485 - def _build_packet(self, payload):
486 # pad up at least 4 bytes, to nearest block-size (usually 8) 487 bsize = self.__block_size_out 488 padding = 3 + bsize - ((len(payload) + 8) % bsize) 489 packet = struct.pack('>IB', len(payload) + padding + 1, padding) 490 packet += payload 491 if self.__sdctr_out or self.__block_engine_out is None: 492 # cute trick i caught openssh doing: if we're not encrypting or SDCTR mode (RFC4344), 493 # don't waste random bytes for the padding 494 packet += (chr(0) * padding) 495 else: 496 packet += rng.read(padding) 497 return packet
498
499 - def _trigger_rekey(self):
500 # outside code should check for this flag 501 self.__need_rekey = True
502