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