diff options
Diffstat (limited to 'paramiko/transport.py')
-rw-r--r-- | paramiko/transport.py | 956 |
1 files changed, 534 insertions, 422 deletions
diff --git a/paramiko/transport.py b/paramiko/transport.py index 7fe34d40..a0890805 100644 --- a/paramiko/transport.py +++ b/paramiko/transport.py @@ -39,18 +39,49 @@ from paramiko.auth_handler import AuthHandler from paramiko.ssh_gss import GSSAuth from paramiko.channel import Channel from paramiko.common import ( - xffffffff, cMSG_CHANNEL_OPEN, cMSG_IGNORE, cMSG_GLOBAL_REQUEST, DEBUG, - MSG_KEXINIT, MSG_IGNORE, MSG_DISCONNECT, MSG_DEBUG, ERROR, WARNING, - cMSG_UNIMPLEMENTED, INFO, cMSG_KEXINIT, cMSG_NEWKEYS, MSG_NEWKEYS, - cMSG_REQUEST_SUCCESS, cMSG_REQUEST_FAILURE, CONNECTION_FAILED_CODE, - OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, OPEN_SUCCEEDED, - cMSG_CHANNEL_OPEN_FAILURE, cMSG_CHANNEL_OPEN_SUCCESS, MSG_GLOBAL_REQUEST, - MSG_REQUEST_SUCCESS, MSG_REQUEST_FAILURE, MSG_CHANNEL_OPEN_SUCCESS, - MSG_CHANNEL_OPEN_FAILURE, MSG_CHANNEL_OPEN, MSG_CHANNEL_SUCCESS, - MSG_CHANNEL_FAILURE, MSG_CHANNEL_DATA, MSG_CHANNEL_EXTENDED_DATA, - MSG_CHANNEL_WINDOW_ADJUST, MSG_CHANNEL_REQUEST, MSG_CHANNEL_EOF, - MSG_CHANNEL_CLOSE, MIN_WINDOW_SIZE, MIN_PACKET_SIZE, MAX_WINDOW_SIZE, - DEFAULT_WINDOW_SIZE, DEFAULT_MAX_PACKET_SIZE, HIGHEST_USERAUTH_MESSAGE_ID, + xffffffff, + cMSG_CHANNEL_OPEN, + cMSG_IGNORE, + cMSG_GLOBAL_REQUEST, + DEBUG, + MSG_KEXINIT, + MSG_IGNORE, + MSG_DISCONNECT, + MSG_DEBUG, + ERROR, + WARNING, + cMSG_UNIMPLEMENTED, + INFO, + cMSG_KEXINIT, + cMSG_NEWKEYS, + MSG_NEWKEYS, + cMSG_REQUEST_SUCCESS, + cMSG_REQUEST_FAILURE, + CONNECTION_FAILED_CODE, + OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, + OPEN_SUCCEEDED, + cMSG_CHANNEL_OPEN_FAILURE, + cMSG_CHANNEL_OPEN_SUCCESS, + MSG_GLOBAL_REQUEST, + MSG_REQUEST_SUCCESS, + MSG_REQUEST_FAILURE, + MSG_CHANNEL_OPEN_SUCCESS, + MSG_CHANNEL_OPEN_FAILURE, + MSG_CHANNEL_OPEN, + MSG_CHANNEL_SUCCESS, + MSG_CHANNEL_FAILURE, + MSG_CHANNEL_DATA, + MSG_CHANNEL_EXTENDED_DATA, + MSG_CHANNEL_WINDOW_ADJUST, + MSG_CHANNEL_REQUEST, + MSG_CHANNEL_EOF, + MSG_CHANNEL_CLOSE, + MIN_WINDOW_SIZE, + MIN_PACKET_SIZE, + MAX_WINDOW_SIZE, + DEFAULT_WINDOW_SIZE, + DEFAULT_MAX_PACKET_SIZE, + HIGHEST_USERAUTH_MESSAGE_ID, ) from paramiko.compress import ZlibCompressor, ZlibDecompressor from paramiko.dsskey import DSSKey @@ -69,7 +100,10 @@ from paramiko.ecdsakey import ECDSAKey from paramiko.server import ServerInterface from paramiko.sftp_client import SFTPClient from paramiko.ssh_exception import ( - SSHException, BadAuthenticationType, ChannelException, ProxyCommandFailure, + SSHException, + BadAuthenticationType, + ChannelException, + ProxyCommandFailure, ) from paramiko.util import retry_on_signal, ClosingContextManager, clamp_value @@ -77,12 +111,14 @@ from paramiko.util import retry_on_signal, ClosingContextManager, clamp_value # for thread cleanup _active_threads = [] + def _join_lingering_threads(): for thr in _active_threads: thr.stop_thread() import atexit + atexit.register(_join_lingering_threads) @@ -96,31 +132,32 @@ class Transport(threading.Thread, ClosingContextManager): Instances of this class may be used as context managers. """ + _ENCRYPT = object() _DECRYPT = object() - _PROTO_ID = '2.0' - _CLIENT_ID = 'paramiko_%s' % paramiko.__version__ + _PROTO_ID = "2.0" + _CLIENT_ID = "paramiko_%s" % paramiko.__version__ # These tuples of algorithm identifiers are in preference order; do not # reorder without reason! _preferred_ciphers = ( - 'aes128-ctr', - 'aes192-ctr', - 'aes256-ctr', - 'aes128-cbc', - 'aes192-cbc', - 'aes256-cbc', - 'blowfish-cbc', - '3des-cbc', + "aes128-ctr", + "aes192-ctr", + "aes256-ctr", + "aes128-cbc", + "aes192-cbc", + "aes256-cbc", + "blowfish-cbc", + "3des-cbc", ) _preferred_macs = ( - 'hmac-sha2-256', - 'hmac-sha2-512', - 'hmac-sha1', - 'hmac-md5', - 'hmac-sha1-96', - 'hmac-md5-96', + "hmac-sha2-256", + "hmac-sha2-512", + "hmac-sha1", + "hmac-md5", + "hmac-sha1-96", + "hmac-md5-96", ) _preferred_keys = ( 'ssh-ed25519', @@ -140,71 +177,70 @@ class Transport(threading.Thread, ClosingContextManager): 'diffie-hellman-group1-sha1', ) _preferred_gsskex = ( - 'gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==', - 'gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g==', - 'gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==', + "gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==", + "gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g==", + "gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==", ) - _preferred_compression = ('none',) + _preferred_compression = ("none",) _cipher_info = { - 'aes128-ctr': { - 'class': algorithms.AES, - 'mode': modes.CTR, - 'block-size': 16, - 'key-size': 16 + "aes128-ctr": { + "class": algorithms.AES, + "mode": modes.CTR, + "block-size": 16, + "key-size": 16, }, - 'aes192-ctr': { - 'class': algorithms.AES, - 'mode': modes.CTR, - 'block-size': 16, - 'key-size': 24 + "aes192-ctr": { + "class": algorithms.AES, + "mode": modes.CTR, + "block-size": 16, + "key-size": 24, }, - 'aes256-ctr': { - 'class': algorithms.AES, - 'mode': modes.CTR, - 'block-size': 16, - 'key-size': 32 + "aes256-ctr": { + "class": algorithms.AES, + "mode": modes.CTR, + "block-size": 16, + "key-size": 32, }, - 'blowfish-cbc': { - 'class': algorithms.Blowfish, - 'mode': modes.CBC, - 'block-size': 8, - 'key-size': 16 + "blowfish-cbc": { + "class": algorithms.Blowfish, + "mode": modes.CBC, + "block-size": 8, + "key-size": 16, }, - 'aes128-cbc': { - 'class': algorithms.AES, - 'mode': modes.CBC, - 'block-size': 16, - 'key-size': 16 + "aes128-cbc": { + "class": algorithms.AES, + "mode": modes.CBC, + "block-size": 16, + "key-size": 16, }, - 'aes192-cbc': { - 'class': algorithms.AES, - 'mode': modes.CBC, - 'block-size': 16, - 'key-size': 24 + "aes192-cbc": { + "class": algorithms.AES, + "mode": modes.CBC, + "block-size": 16, + "key-size": 24, }, - 'aes256-cbc': { - 'class': algorithms.AES, - 'mode': modes.CBC, - 'block-size': 16, - 'key-size': 32 + "aes256-cbc": { + "class": algorithms.AES, + "mode": modes.CBC, + "block-size": 16, + "key-size": 32, }, - '3des-cbc': { - 'class': algorithms.TripleDES, - 'mode': modes.CBC, - 'block-size': 8, - 'key-size': 24 + "3des-cbc": { + "class": algorithms.TripleDES, + "mode": modes.CBC, + "block-size": 8, + "key-size": 24, }, } - _mac_info = { - 'hmac-sha1': {'class': sha1, 'size': 20}, - 'hmac-sha1-96': {'class': sha1, 'size': 12}, - 'hmac-sha2-256': {'class': sha256, 'size': 32}, - 'hmac-sha2-512': {'class': sha512, 'size': 64}, - 'hmac-md5': {'class': md5, 'size': 16}, - 'hmac-md5-96': {'class': md5, 'size': 12}, + "hmac-sha1": {"class": sha1, "size": 20}, + "hmac-sha1-96": {"class": sha1, "size": 12}, + "hmac-sha2-256": {"class": sha256, "size": 32}, + "hmac-sha2-512": {"class": sha512, "size": 64}, + "hmac-md5": {"class": md5, "size": 16}, + "hmac-md5-96": {"class": md5, "size": 12}, } _key_info = { @@ -233,20 +269,22 @@ class Transport(threading.Thread, ClosingContextManager): # zlib@openssh.com is just zlib, but only turned on after a successful # authentication. openssh servers may only offer this type because # they've had troubles with security holes in zlib in the past. - 'zlib@openssh.com': (ZlibCompressor, ZlibDecompressor), - 'zlib': (ZlibCompressor, ZlibDecompressor), - 'none': (None, None), + "zlib@openssh.com": (ZlibCompressor, ZlibDecompressor), + "zlib": (ZlibCompressor, ZlibDecompressor), + "none": (None, None), } _modulus_pack = None _active_check_timeout = 0.1 - def __init__(self, - sock, - default_window_size=DEFAULT_WINDOW_SIZE, - default_max_packet_size=DEFAULT_MAX_PACKET_SIZE, - gss_kex=False, - gss_deleg_creds=True): + def __init__( + self, + sock, + default_window_size=DEFAULT_WINDOW_SIZE, + default_max_packet_size=DEFAULT_MAX_PACKET_SIZE, + gss_kex=False, + gss_deleg_creds=True, + ): """ Create a new SSH session over an existing socket, or socket-like object. This only creates the `.Transport` object; it doesn't begin @@ -295,7 +333,7 @@ class Transport(threading.Thread, ClosingContextManager): if isinstance(sock, string_types): # convert "host:port" into (host, port) - hl = sock.split(':', 1) + hl = sock.split(":", 1) if len(hl) == 1: sock = (hl[0], 22) else: @@ -303,7 +341,7 @@ class Transport(threading.Thread, ClosingContextManager): if type(sock) is tuple: # connect to the given (host, port) hostname, port = sock - reason = 'No suitable address family' + reason = "No suitable address family" addrinfos = socket.getaddrinfo( hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM ) @@ -320,7 +358,8 @@ class Transport(threading.Thread, ClosingContextManager): break else: raise SSHException( - 'Unable to connect to %s: %s' % (hostname, reason)) + "Unable to connect to %s: %s" % (hostname, reason) + ) # okay, normal socket-ish flow here... threading.Thread.__init__(self) self.setDaemon(True) @@ -331,9 +370,9 @@ class Transport(threading.Thread, ClosingContextManager): # negotiated crypto parameters self.packetizer = Packetizer(sock) - self.local_version = 'SSH-' + self._PROTO_ID + '-' + self._CLIENT_ID - self.remote_version = '' - self.local_cipher = self.remote_cipher = '' + self.local_version = "SSH-" + self._PROTO_ID + "-" + self._CLIENT_ID + self.remote_version = "" + self.local_cipher = self.remote_cipher = "" self.local_kex_init = self.remote_kex_init = None self.local_mac = self.remote_mac = None self.local_compression = self.remote_compression = None @@ -365,8 +404,8 @@ class Transport(threading.Thread, ClosingContextManager): # tracking open channels self._channels = ChannelMap() - self.channel_events = {} # (id -> Event) - self.channels_seen = {} # (id -> True) + self.channel_events = {} # (id -> Event) + self.channels_seen = {} # (id -> True) self._channel_counter = 0 self.default_max_packet_size = default_max_packet_size self.default_window_size = default_window_size @@ -378,7 +417,7 @@ class Transport(threading.Thread, ClosingContextManager): self.clear_to_send = threading.Event() self.clear_to_send_lock = threading.Lock() self.clear_to_send_timeout = 30.0 - self.log_name = 'paramiko.transport' + self.log_name = "paramiko.transport" self.logger = util.get_logger(self.log_name) self.packetizer.set_log(self.logger) self.auth_handler = None @@ -406,22 +445,22 @@ class Transport(threading.Thread, ClosingContextManager): """ Returns a string representation of this object, for debugging. """ - out = '<paramiko.Transport at %s' % hex(long(id(self)) & xffffffff) + out = "<paramiko.Transport at %s" % hex(long(id(self)) & xffffffff) if not self.active: - out += ' (unconnected)' + out += " (unconnected)" else: - if self.local_cipher != '': - out += ' (cipher %s, %d bits)' % ( + if self.local_cipher != "": + out += " (cipher %s, %d bits)" % ( self.local_cipher, - self._cipher_info[self.local_cipher]['key-size'] * 8 + self._cipher_info[self.local_cipher]["key-size"] * 8, ) if self.is_authenticated(): - out += ' (active; %d open channel(s))' % len(self._channels) + out += " (active; %d open channel(s))" % len(self._channels) elif self.initial_kex_done: - out += ' (connected; awaiting auth)' + out += " (connected; awaiting auth)" else: - out += ' (connecting)' - out += '>' + out += " (connecting)" + out += ">" return out def atfork(self): @@ -508,10 +547,9 @@ class Transport(threading.Thread, ClosingContextManager): e = self.get_exception() if e is not None: raise e - raise SSHException('Negotiation failed.') - if ( - event.is_set() or - (timeout is not None and time.time() >= max_time) + raise SSHException("Negotiation failed.") + if event.is_set() or ( + timeout is not None and time.time() >= max_time ): break @@ -577,7 +615,7 @@ class Transport(threading.Thread, ClosingContextManager): e = self.get_exception() if e is not None: raise e - raise SSHException('Negotiation failed.') + raise SSHException("Negotiation failed.") if event.is_set(): break @@ -644,7 +682,7 @@ class Transport(threading.Thread, ClosingContextManager): """ Transport._modulus_pack = ModulusPack() # places to look for the openssh "moduli" file - file_list = ['/etc/ssh/moduli', '/usr/local/etc/moduli'] + file_list = ["/etc/ssh/moduli", "/usr/local/etc/moduli"] if filename is not None: file_list.insert(0, filename) for fn in file_list: @@ -682,7 +720,7 @@ class Transport(threading.Thread, ClosingContextManager): :return: public key (`.PKey`) of the remote server """ if (not self.active) or (not self.initial_kex_done): - raise SSHException('No existing session') + raise SSHException("No existing session") return self.host_key def is_active(self): @@ -696,10 +734,7 @@ class Transport(threading.Thread, ClosingContextManager): return self.active def open_session( - self, - window_size=None, - max_packet_size=None, - timeout=None, + self, window_size=None, max_packet_size=None, timeout=None ): """ Request a new channel to the server, of type ``"session"``. This is @@ -726,10 +761,12 @@ class Transport(threading.Thread, ClosingContextManager): .. versionchanged:: 1.15 Added the ``window_size`` and ``max_packet_size`` arguments. """ - return self.open_channel('session', - window_size=window_size, - max_packet_size=max_packet_size, - timeout=timeout) + return self.open_channel( + "session", + window_size=window_size, + max_packet_size=max_packet_size, + timeout=timeout, + ) def open_x11_channel(self, src_addr=None): """ @@ -745,7 +782,7 @@ class Transport(threading.Thread, ClosingContextManager): `.SSHException` -- if the request is rejected or the session ends prematurely """ - return self.open_channel('x11', src_addr=src_addr) + return self.open_channel("x11", src_addr=src_addr) def open_forward_agent_channel(self): """ @@ -759,7 +796,7 @@ class Transport(threading.Thread, ClosingContextManager): :raises: `.SSHException` -- if the request is rejected or the session ends prematurely """ - return self.open_channel('auth-agent@openssh.com') + return self.open_channel("auth-agent@openssh.com") def open_forwarded_tcpip_channel(self, src_addr, dest_addr): """ @@ -771,15 +808,17 @@ class Transport(threading.Thread, ClosingContextManager): :param src_addr: originator's address :param dest_addr: local (server) connected address """ - return self.open_channel('forwarded-tcpip', dest_addr, src_addr) + return self.open_channel("forwarded-tcpip", dest_addr, src_addr) - def open_channel(self, - kind, - dest_addr=None, - src_addr=None, - window_size=None, - max_packet_size=None, - timeout=None): + def open_channel( + self, + kind, + dest_addr=None, + src_addr=None, + window_size=None, + max_packet_size=None, + timeout=None, + ): """ Request a new channel to the server. `Channels <.Channel>` are socket-like objects used for the actual transfer of data across the @@ -816,7 +855,7 @@ class Transport(threading.Thread, ClosingContextManager): Added the ``window_size`` and ``max_packet_size`` arguments. """ if not self.active: - raise SSHException('SSH session not active') + raise SSHException("SSH session not active") timeout = 3600 if timeout is None else timeout self.lock.acquire() try: @@ -829,12 +868,12 @@ class Transport(threading.Thread, ClosingContextManager): m.add_int(chanid) m.add_int(window_size) m.add_int(max_packet_size) - if (kind == 'forwarded-tcpip') or (kind == 'direct-tcpip'): + if (kind == "forwarded-tcpip") or (kind == "direct-tcpip"): m.add_string(dest_addr[0]) m.add_int(dest_addr[1]) m.add_string(src_addr[0]) m.add_int(src_addr[1]) - elif kind == 'x11': + elif kind == "x11": m.add_string(src_addr[0]) m.add_int(src_addr[1]) chan = Channel(chanid) @@ -852,18 +891,18 @@ class Transport(threading.Thread, ClosingContextManager): if not self.active: e = self.get_exception() if e is None: - e = SSHException('Unable to open channel.') + e = SSHException("Unable to open channel.") raise e if event.is_set(): break elif start_ts + timeout < time.time(): - raise SSHException('Timeout opening channel.') + raise SSHException("Timeout opening channel.") chan = self._channels.get(chanid) if chan is not None: return chan e = self.get_exception() if e is None: - e = SSHException('Unable to open channel.') + e = SSHException("Unable to open channel.") raise e def request_port_forward(self, address, port, handler=None): @@ -900,20 +939,22 @@ class Transport(threading.Thread, ClosingContextManager): `.SSHException` -- if the server refused the TCP forward request """ if not self.active: - raise SSHException('SSH session not active') + raise SSHException("SSH session not active") port = int(port) response = self.global_request( - 'tcpip-forward', (address, port), wait=True + "tcpip-forward", (address, port), wait=True ) if response is None: - raise SSHException('TCP forwarding request denied') + raise SSHException("TCP forwarding request denied") if port == 0: port = response.get_int() if handler is None: + def default_handler(channel, src_addr, dest_addr_port): # src_addr, src_port = src_addr_port # dest_addr, dest_port = dest_addr_port self._queue_incoming_channel(channel) + handler = default_handler self._tcp_handler = handler return port @@ -930,7 +971,7 @@ class Transport(threading.Thread, ClosingContextManager): if not self.active: return self._tcp_handler = None - self.global_request('cancel-tcpip-forward', (address, port), wait=True) + self.global_request("cancel-tcpip-forward", (address, port), wait=True) def open_sftp_client(self): """ @@ -983,7 +1024,7 @@ class Transport(threading.Thread, ClosingContextManager): e = self.get_exception() if e is not None: raise e - raise SSHException('Negotiation failed.') + raise SSHException("Negotiation failed.") if self.completion_event.is_set(): break return @@ -999,8 +1040,10 @@ class Transport(threading.Thread, ClosingContextManager): seconds to wait before sending a keepalive packet (or 0 to disable keepalives). """ + def _request(x=weakref.proxy(self)): - return x.global_request('keepalive@lag.net', wait=False) + return x.global_request("keepalive@lag.net", wait=False) + self.packetizer.set_keepalive(interval, _request) def global_request(self, kind, data=None, wait=True): @@ -1068,7 +1111,7 @@ class Transport(threading.Thread, ClosingContextManager): def connect( self, hostkey=None, - username='', + username="", password=None, pkey=None, gss_host=None, @@ -1128,31 +1171,36 @@ class Transport(threading.Thread, ClosingContextManager): if (hostkey is not None) and not gss_kex: key = self.get_remote_server_key() if ( - key.get_name() != hostkey.get_name() or - key.asbytes() != hostkey.asbytes() + key.get_name() != hostkey.get_name() + or key.asbytes() != hostkey.asbytes() ): - self._log(DEBUG, 'Bad host key from server') - self._log(DEBUG, 'Expected: %s: %s' % ( - hostkey.get_name(), repr(hostkey.asbytes())) + self._log(DEBUG, "Bad host key from server") + self._log( + DEBUG, + "Expected: %s: %s" + % (hostkey.get_name(), repr(hostkey.asbytes())), ) - self._log(DEBUG, 'Got : %s: %s' % ( - key.get_name(), repr(key.asbytes())) + self._log( + DEBUG, + "Got : %s: %s" % (key.get_name(), repr(key.asbytes())), ) - raise SSHException('Bad host key from server') - self._log(DEBUG, 'Host key verified (%s)' % hostkey.get_name()) + raise SSHException("Bad host key from server") + self._log(DEBUG, "Host key verified (%s)" % hostkey.get_name()) if (pkey is not None) or (password is not None) or gss_auth or gss_kex: if gss_auth: - self._log(DEBUG, 'Attempting GSS-API auth... (gssapi-with-mic)') # noqa + self._log( + DEBUG, "Attempting GSS-API auth... (gssapi-with-mic)" + ) # noqa self.auth_gssapi_with_mic(username, gss_host, gss_deleg_creds) elif gss_kex: - self._log(DEBUG, 'Attempting GSS-API auth... (gssapi-keyex)') + self._log(DEBUG, "Attempting GSS-API auth... (gssapi-keyex)") self.auth_gssapi_keyex(username) elif pkey is not None: - self._log(DEBUG, 'Attempting public-key auth...') + self._log(DEBUG, "Attempting public-key auth...") self.auth_publickey(username, pkey) else: - self._log(DEBUG, 'Attempting password auth...') + self._log(DEBUG, "Attempting password auth...") self.auth_password(username, password) return @@ -1207,9 +1255,9 @@ class Transport(threading.Thread, ClosingContextManager): closed. """ return ( - self.active and - self.auth_handler is not None and - self.auth_handler.is_authenticated() + self.active + and self.auth_handler is not None + and self.auth_handler.is_authenticated() ) def get_username(self): @@ -1259,7 +1307,7 @@ class Transport(threading.Thread, ClosingContextManager): .. versionadded:: 1.5 """ if (not self.active) or (not self.initial_kex_done): - raise SSHException('No existing session') + raise SSHException("No existing session") my_event = threading.Event() self.auth_handler = AuthHandler(self) self.auth_handler.auth_none(username, my_event) @@ -1315,7 +1363,7 @@ class Transport(threading.Thread, ClosingContextManager): if (not self.active) or (not self.initial_kex_done): # we should never try to send the password unless we're on a secure # link - raise SSHException('No existing session') + raise SSHException("No existing session") if event is None: my_event = threading.Event() else: @@ -1330,12 +1378,13 @@ class Transport(threading.Thread, ClosingContextManager): except BadAuthenticationType as e: # if password auth isn't allowed, but keyboard-interactive *is*, # try to fudge it - if not fallback or ('keyboard-interactive' not in e.allowed_types): + if not fallback or ("keyboard-interactive" not in e.allowed_types): raise try: + def handler(title, instructions, fields): if len(fields) > 1: - raise SSHException('Fallback authentication failed.') + raise SSHException("Fallback authentication failed.") if len(fields) == 0: # for some reason, at least on os x, a 2nd request will # be made with zero fields requested. maybe it's just @@ -1343,6 +1392,7 @@ class Transport(threading.Thread, ClosingContextManager): # type we're doing here. *shrug* :) return [] return [password] + return self.auth_interactive(username, handler) except SSHException: # attempt failed; just raise the original exception @@ -1385,7 +1435,7 @@ class Transport(threading.Thread, ClosingContextManager): """ if (not self.active) or (not self.initial_kex_done): # we should never try to authenticate unless we're on a secure link - raise SSHException('No existing session') + raise SSHException("No existing session") if event is None: my_event = threading.Event() else: @@ -1397,7 +1447,7 @@ class Transport(threading.Thread, ClosingContextManager): return [] return self.auth_handler.wait_for_response(my_event) - def auth_interactive(self, username, handler, submethods=''): + def auth_interactive(self, username, handler, submethods=""): """ Authenticate to the server interactively. A handler is used to answer arbitrary questions from the server. On many servers, this is just a @@ -1442,7 +1492,7 @@ class Transport(threading.Thread, ClosingContextManager): """ if (not self.active) or (not self.initial_kex_done): # we should never try to authenticate unless we're on a secure link - raise SSHException('No existing session') + raise SSHException("No existing session") my_event = threading.Event() self.auth_handler = AuthHandler(self) self.auth_handler.auth_interactive( @@ -1450,7 +1500,7 @@ class Transport(threading.Thread, ClosingContextManager): ) return self.auth_handler.wait_for_response(my_event) - def auth_interactive_dumb(self, username, handler=None, submethods=''): + def auth_interactive_dumb(self, username, handler=None, submethods=""): """ Autenticate to the server interactively but dumber. Just print the prompt and / or instructions to stdout and send back @@ -1459,6 +1509,7 @@ class Transport(threading.Thread, ClosingContextManager): """ if not handler: + def handler(title, instructions, prompt_list): answers = [] if title: @@ -1466,9 +1517,10 @@ class Transport(threading.Thread, ClosingContextManager): if instructions: print(instructions.strip()) for prompt, show_input in prompt_list: - print(prompt.strip(), end=' ') + print(prompt.strip(), end=" ") answers.append(input()) return answers + return self.auth_interactive(username, handler, submethods) def auth_gssapi_with_mic(self, username, gss_host, gss_deleg_creds): @@ -1490,7 +1542,7 @@ class Transport(threading.Thread, ClosingContextManager): """ if (not self.active) or (not self.initial_kex_done): # we should never try to authenticate unless we're on a secure link - raise SSHException('No existing session') + raise SSHException("No existing session") my_event = threading.Event() self.auth_handler = AuthHandler(self) self.auth_handler.auth_gssapi_with_mic( @@ -1515,7 +1567,7 @@ class Transport(threading.Thread, ClosingContextManager): """ if (not self.active) or (not self.initial_kex_done): # we should never try to authenticate unless we're on a secure link - raise SSHException('No existing session') + raise SSHException("No existing session") my_event = threading.Event() self.auth_handler = AuthHandler(self) self.auth_handler.auth_gssapi_keyex(username, my_event) @@ -1582,9 +1634,9 @@ class Transport(threading.Thread, ClosingContextManager): .. versionadded:: 1.5.2 """ if compress: - self._preferred_compression = ('zlib@openssh.com', 'zlib', 'none') + self._preferred_compression = ("zlib@openssh.com", "zlib", "none") else: - self._preferred_compression = ('none',) + self._preferred_compression = ("none",) def getpeername(self): """ @@ -1598,9 +1650,9 @@ class Transport(threading.Thread, ClosingContextManager): the address of the remote host, if known, as a ``(str, int)`` tuple. """ - gp = getattr(self.sock, 'getpeername', None) + gp = getattr(self.sock, "getpeername", None) if gp is None: - return 'unknown', 0 + return "unknown", 0 return gp() def stop_thread(self): @@ -1619,10 +1671,10 @@ class Transport(threading.Thread, ClosingContextManager): # our socket and packetizer are both closed (but where we'd # otherwise be sitting forever on that recv()). while ( - self.is_alive() and - self is not threading.current_thread() and - not self.sock._closed and - not self.packetizer.closed + self.is_alive() + and self is not threading.current_thread() + and not self.sock._closed + and not self.packetizer.closed ): self.join(0.1) @@ -1664,14 +1716,18 @@ class Transport(threading.Thread, ClosingContextManager): while True: self.clear_to_send.wait(0.1) if not self.active: - self._log(DEBUG, 'Dropping user packet because connection is dead.') # noqa + self._log( + DEBUG, "Dropping user packet because connection is dead." + ) # noqa return self.clear_to_send_lock.acquire() if self.clear_to_send.is_set(): break self.clear_to_send_lock.release() if time.time() > start + self.clear_to_send_timeout: - raise SSHException('Key-exchange timed out waiting for key negotiation') # noqa + raise SSHException( + "Key-exchange timed out waiting for key negotiation" + ) # noqa try: self._send_message(data) finally: @@ -1695,9 +1751,11 @@ class Transport(threading.Thread, ClosingContextManager): def _verify_key(self, host_key, sig): key = self._key_info[self.host_key_type](Message(host_key)) if key is None: - raise SSHException('Unknown host key type') + raise SSHException("Unknown host key type") if not key.verify_ssh_sig(self.H, Message(sig)): - raise SSHException('Signature verification (%s) failed.' % self.host_key_type) # noqa + raise SSHException( + "Signature verification (%s) failed." % self.host_key_type + ) # noqa self.host_key = key def _compute_key(self, id, nbytes): @@ -1709,16 +1767,17 @@ class Transport(threading.Thread, ClosingContextManager): m.add_bytes(self.session_id) # Fallback to SHA1 for kex engines that fail to specify a hex # algorithm, or for e.g. transport tests that don't run kexinit. - hash_algo = getattr(self.kex_engine, 'hash_algo', None) + hash_algo = getattr(self.kex_engine, "hash_algo", None) hash_select_msg = "kex engine %s specified hash_algo %r" % ( - self.kex_engine.__class__.__name__, hash_algo + self.kex_engine.__class__.__name__, + hash_algo, ) if hash_algo is None: hash_algo = sha1 hash_select_msg += ", falling back to sha1" - if not hasattr(self, '_logged_hash_selection'): + if not hasattr(self, "_logged_hash_selection"): self._log(DEBUG, hash_select_msg) - setattr(self, '_logged_hash_selection', True) + setattr(self, "_logged_hash_selection", True) out = sofar = hash_algo(m.asbytes()).digest() while len(out) < nbytes: m = Message() @@ -1732,11 +1791,11 @@ class Transport(threading.Thread, ClosingContextManager): def _get_cipher(self, name, key, iv, operation): if name not in self._cipher_info: - raise SSHException('Unknown client cipher ' + name) + raise SSHException("Unknown client cipher " + name) else: cipher = Cipher( - self._cipher_info[name]['class'](key), - self._cipher_info[name]['mode'](iv), + self._cipher_info[name]["class"](key), + self._cipher_info[name]["mode"](iv), backend=default_backend(), ) if operation is self._ENCRYPT: @@ -1746,8 +1805,10 @@ class Transport(threading.Thread, ClosingContextManager): def _set_forward_agent_handler(self, handler): if handler is None: + def default_handler(channel): self._queue_incoming_channel(channel) + self._forward_agent_handler = default_handler else: self._forward_agent_handler = handler @@ -1758,6 +1819,7 @@ class Transport(threading.Thread, ClosingContextManager): # by default, use the same mechanism as accept() def default_handler(channel, src_addr_port): self._queue_incoming_channel(channel) + self._x11_handler = default_handler else: self._x11_handler = handler @@ -1791,9 +1853,9 @@ class Transport(threading.Thread, ClosingContextManager): Otherwise (client mode, authed, or pre-auth message) returns None. """ if ( - not self.server_mode or - ptype <= HIGHEST_USERAUTH_MESSAGE_ID or - self.is_authenticated() + not self.server_mode + or ptype <= HIGHEST_USERAUTH_MESSAGE_ID + or self.is_authenticated() ): return None # WELP. We must be dealing with someone trying to do non-auth things @@ -1804,13 +1866,13 @@ class Transport(threading.Thread, ClosingContextManager): reply.add_byte(cMSG_REQUEST_FAILURE) # Channel opens let us reject w/ a specific type + message. elif ptype == MSG_CHANNEL_OPEN: - kind = message.get_text() # noqa + kind = message.get_text() # noqa chanid = message.get_int() reply.add_byte(cMSG_CHANNEL_OPEN_FAILURE) reply.add_int(chanid) reply.add_int(OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED) - reply.add_string('') - reply.add_string('en') + reply.add_string("") + reply.add_string("en") # NOTE: Post-open channel messages do not need checking; the above will # reject attemps to open channels, meaning that even if a malicious # user tries to send a MSG_CHANNEL_REQUEST, it will simply fall under @@ -1832,13 +1894,15 @@ class Transport(threading.Thread, ClosingContextManager): _active_threads.append(self) tid = hex(long(id(self)) & xffffffff) if self.server_mode: - self._log(DEBUG, 'starting thread (server mode): %s' % tid) + self._log(DEBUG, "starting thread (server mode): %s" % tid) else: - self._log(DEBUG, 'starting thread (client mode): %s' % tid) + self._log(DEBUG, "starting thread (client mode): %s" % tid) try: try: - self.packetizer.write_all(b(self.local_version + '\r\n')) - self._log(DEBUG, 'Local version/idstring: %s' % self.local_version) # noqa + self.packetizer.write_all(b(self.local_version + "\r\n")) + self._log( + DEBUG, "Local version/idstring: %s" % self.local_version + ) # noqa self._check_banner() # The above is actually very much part of the handshake, but # sometimes the banner can be read but the machine is not @@ -1870,7 +1934,10 @@ class Transport(threading.Thread, ClosingContextManager): continue if len(self._expected_packet) > 0: if ptype not in self._expected_packet: - raise SSHException('Expecting packet from %r, got %d' % (self._expected_packet, ptype)) # noqa + raise SSHException( + "Expecting packet from %r, got %d" + % (self._expected_packet, ptype) + ) # noqa self._expected_packet = tuple() if (ptype >= 30) and (ptype <= 41): self.kex_engine.parse_next(ptype, m) @@ -1888,45 +1955,53 @@ class Transport(threading.Thread, ClosingContextManager): if chan is not None: self._channel_handler_table[ptype](chan, m) elif chanid in self.channels_seen: - self._log(DEBUG, 'Ignoring message for dead channel %d' % chanid) # noqa + self._log( + DEBUG, + "Ignoring message for dead channel %d" + % chanid, + ) # noqa else: - self._log(ERROR, 'Channel request for unknown channel %d' % chanid) # noqa + self._log( + ERROR, + "Channel request for unknown channel %d" + % chanid, + ) # noqa self.active = False self.packetizer.close() elif ( - self.auth_handler is not None and - ptype in self.auth_handler._handler_table + self.auth_handler is not None + and ptype in self.auth_handler._handler_table ): handler = self.auth_handler._handler_table[ptype] handler(self.auth_handler, m) if len(self._expected_packet) > 0: continue else: - self._log(WARNING, 'Oops, unhandled type %d' % ptype) + self._log(WARNING, "Oops, unhandled type %d" % ptype) msg = Message() msg.add_byte(cMSG_UNIMPLEMENTED) msg.add_int(m.seqno) self._send_message(msg) self.packetizer.complete_handshake() except SSHException as e: - self._log(ERROR, 'Exception: ' + str(e)) + self._log(ERROR, "Exception: " + str(e)) self._log(ERROR, util.tb_strings()) self.saved_exception = e except EOFError as e: - self._log(DEBUG, 'EOF in transport thread') + self._log(DEBUG, "EOF in transport thread") self.saved_exception = e except socket.error as e: if type(e.args) is tuple: if e.args: - emsg = '%s (%d)' % (e.args[1], e.args[0]) + emsg = "%s (%d)" % (e.args[1], e.args[0]) else: # empty tuple, e.g. socket.timeout emsg = str(e) or repr(e) else: emsg = e.args - self._log(ERROR, 'Socket exception: ' + emsg) + self._log(ERROR, "Socket exception: " + emsg) self.saved_exception = e except Exception as e: - self._log(ERROR, 'Unknown exception: ' + str(e)) + self._log(ERROR, "Unknown exception: " + str(e)) self._log(ERROR, util.tb_strings()) self.saved_exception = e _active_threads.remove(self) @@ -1955,7 +2030,6 @@ class Transport(threading.Thread, ClosingContextManager): if self.sys.modules is not None: raise - def _log_agreement(self, which, local, remote): # Log useful, non-duplicative line re: an agreed-upon algorithm. # Old code implied algorithms could be asymmetrical (different for @@ -1997,32 +2071,32 @@ class Transport(threading.Thread, ClosingContextManager): raise except Exception as e: raise SSHException( - 'Error reading SSH protocol banner' + str(e) + "Error reading SSH protocol banner" + str(e) ) - if buf[:4] == 'SSH-': + if buf[:4] == "SSH-": break - self._log(DEBUG, 'Banner: ' + buf) - if buf[:4] != 'SSH-': + self._log(DEBUG, "Banner: " + buf) + if buf[:4] != "SSH-": raise SSHException('Indecipherable protocol version "' + buf + '"') # save this server version string for later self.remote_version = buf - self._log(DEBUG, 'Remote version/idstring: %s' % buf) + self._log(DEBUG, "Remote version/idstring: %s" % buf) # pull off any attached comment # NOTE: comment used to be stored in a variable and then...never used. # since 2003. ca 877cd974b8182d26fa76d566072917ea67b64e67 - i = buf.find(' ') + i = buf.find(" ") if i >= 0: buf = buf[:i] # parse out version string and make sure it matches - segs = buf.split('-', 2) + segs = buf.split("-", 2) if len(segs) < 3: - raise SSHException('Invalid SSH banner') + raise SSHException("Invalid SSH banner") version = segs[1] client = segs[2] - if version != '1.99' and version != '2.0': - msg = 'Incompatible version ({0} instead of 2.0)' + if version != "1.99" and version != "2.0": + msg = "Incompatible version ({0} instead of 2.0)" raise SSHException(msg.format(version)) - msg = 'Connected (version {0}, client {1})'.format(version, client) + msg = "Connected (version {0}, client {1})".format(version, client) self._log(INFO, msg) def _send_kex_init(self): @@ -2038,25 +2112,27 @@ class Transport(threading.Thread, ClosingContextManager): self.gss_kex_used = False self.in_kex = True if self.server_mode: - mp_required_prefix = 'diffie-hellman-group-exchange-sha' + mp_required_prefix = "diffie-hellman-group-exchange-sha" kex_mp = [ - k for k - in self._preferred_kex + k + for k in self._preferred_kex if k.startswith(mp_required_prefix) ] if (self._modulus_pack is None) and (len(kex_mp) > 0): # can't do group-exchange if we don't have a pack of potential # primes pkex = [ - k for k - in self.get_security_options().kex + k + for k in self.get_security_options().kex if not k.startswith(mp_required_prefix) ] self.get_security_options().kex = pkex - available_server_keys = list(filter( - list(self.server_key_dict.keys()).__contains__, - self._preferred_keys - )) + available_server_keys = list( + filter( + list(self.server_key_dict.keys()).__contains__, + self._preferred_keys, + ) + ) else: available_server_keys = self._preferred_keys @@ -2080,7 +2156,7 @@ class Transport(threading.Thread, ClosingContextManager): self._send_message(m) def _parse_kex_init(self, m): - m.get_bytes(16) # cookie, discarded + m.get_bytes(16) # cookie, discarded kex_algo_list = m.get_list() server_key_algo_list = m.get_list() client_encrypt_algo_list = m.get_list() @@ -2092,20 +2168,32 @@ class Transport(threading.Thread, ClosingContextManager): client_lang_list = m.get_list() server_lang_list = m.get_list() kex_follows = m.get_boolean() - m.get_int() # unused - - self._log(DEBUG, - 'kex algos:' + str(kex_algo_list) + - ' server key:' + str(server_key_algo_list) + - ' client encrypt:' + str(client_encrypt_algo_list) + - ' server encrypt:' + str(server_encrypt_algo_list) + - ' client mac:' + str(client_mac_algo_list) + - ' server mac:' + str(server_mac_algo_list) + - ' client compress:' + str(client_compress_algo_list) + - ' server compress:' + str(server_compress_algo_list) + - ' client lang:' + str(client_lang_list) + - ' server lang:' + str(server_lang_list) + - ' kex follows?' + str(kex_follows) + m.get_int() # unused + + self._log( + DEBUG, + "kex algos:" + + str(kex_algo_list) + + " server key:" + + str(server_key_algo_list) + + " client encrypt:" + + str(client_encrypt_algo_list) + + " server encrypt:" + + str(server_encrypt_algo_list) + + " client mac:" + + str(client_mac_algo_list) + + " server mac:" + + str(server_mac_algo_list) + + " client compress:" + + str(client_compress_algo_list) + + " server compress:" + + str(server_compress_algo_list) + + " client lang:" + + str(client_lang_list) + + " server lang:" + + str(server_lang_list) + + " kex follows?" + + str(kex_follows), ) # as a server, we pick the first item in the client's list that we @@ -2113,122 +2201,149 @@ class Transport(threading.Thread, ClosingContextManager): # as a client, we pick the first item in our list that the server # supports. if self.server_mode: - agreed_kex = list(filter( - self._preferred_kex.__contains__, - kex_algo_list - )) + agreed_kex = list( + filter(self._preferred_kex.__contains__, kex_algo_list) + ) else: - agreed_kex = list(filter( - kex_algo_list.__contains__, - self._preferred_kex - )) + agreed_kex = list( + filter(kex_algo_list.__contains__, self._preferred_kex) + ) if len(agreed_kex) == 0: - raise SSHException('Incompatible ssh peer (no acceptable kex algorithm)') # noqa + raise SSHException( + "Incompatible ssh peer (no acceptable kex algorithm)" + ) # noqa self.kex_engine = self._kex_info[agreed_kex[0]](self) self._log(DEBUG, "Kex agreed: %s" % agreed_kex[0]) if self.server_mode: - available_server_keys = list(filter( - list(self.server_key_dict.keys()).__contains__, - self._preferred_keys - )) - agreed_keys = list(filter( - available_server_keys.__contains__, server_key_algo_list - )) + available_server_keys = list( + filter( + list(self.server_key_dict.keys()).__contains__, + self._preferred_keys, + ) + ) + agreed_keys = list( + filter( + available_server_keys.__contains__, server_key_algo_list + ) + ) else: - agreed_keys = list(filter( - server_key_algo_list.__contains__, self._preferred_keys - )) + agreed_keys = list( + filter(server_key_algo_list.__contains__, self._preferred_keys) + ) if len(agreed_keys) == 0: - raise SSHException('Incompatible ssh peer (no acceptable host key)') # noqa + raise SSHException( + "Incompatible ssh peer (no acceptable host key)" + ) # noqa self.host_key_type = agreed_keys[0] if self.server_mode and (self.get_server_key() is None): - raise SSHException('Incompatible ssh peer (can\'t match requested host key type)') # noqa - self._log_agreement( - 'HostKey', agreed_keys[0], agreed_keys[0] - ) + raise SSHException( + "Incompatible ssh peer (can't match requested host key type)" + ) # noqa + self._log_agreement("HostKey", agreed_keys[0], agreed_keys[0]) if self.server_mode: - agreed_local_ciphers = list(filter( - self._preferred_ciphers.__contains__, - server_encrypt_algo_list - )) - agreed_remote_ciphers = list(filter( - self._preferred_ciphers.__contains__, - client_encrypt_algo_list - )) + agreed_local_ciphers = list( + filter( + self._preferred_ciphers.__contains__, + server_encrypt_algo_list, + ) + ) + agreed_remote_ciphers = list( + filter( + self._preferred_ciphers.__contains__, + client_encrypt_algo_list, + ) + ) else: - agreed_local_ciphers = list(filter( - client_encrypt_algo_list.__contains__, - self._preferred_ciphers - )) - agreed_remote_ciphers = list(filter( - server_encrypt_algo_list.__contains__, - self._preferred_ciphers - )) + agreed_local_ciphers = list( + filter( + client_encrypt_algo_list.__contains__, + self._preferred_ciphers, + ) + ) + agreed_remote_ciphers = list( + filter( + server_encrypt_algo_list.__contains__, + self._preferred_ciphers, + ) + ) if len(agreed_local_ciphers) == 0 or len(agreed_remote_ciphers) == 0: - raise SSHException('Incompatible ssh server (no acceptable ciphers)') # noqa + raise SSHException( + "Incompatible ssh server (no acceptable ciphers)" + ) # noqa self.local_cipher = agreed_local_ciphers[0] self.remote_cipher = agreed_remote_ciphers[0] self._log_agreement( - 'Cipher', local=self.local_cipher, remote=self.remote_cipher + "Cipher", local=self.local_cipher, remote=self.remote_cipher ) if self.server_mode: - agreed_remote_macs = list(filter( - self._preferred_macs.__contains__, client_mac_algo_list - )) - agreed_local_macs = list(filter( - self._preferred_macs.__contains__, server_mac_algo_list - )) + agreed_remote_macs = list( + filter(self._preferred_macs.__contains__, client_mac_algo_list) + ) + agreed_local_macs = list( + filter(self._preferred_macs.__contains__, server_mac_algo_list) + ) else: - agreed_local_macs = list(filter( - client_mac_algo_list.__contains__, self._preferred_macs - )) - agreed_remote_macs = list(filter( - server_mac_algo_list.__contains__, self._preferred_macs - )) + agreed_local_macs = list( + filter(client_mac_algo_list.__contains__, self._preferred_macs) + ) + agreed_remote_macs = list( + filter(server_mac_algo_list.__contains__, self._preferred_macs) + ) if (len(agreed_local_macs) == 0) or (len(agreed_remote_macs) == 0): - raise SSHException('Incompatible ssh server (no acceptable macs)') + raise SSHException("Incompatible ssh server (no acceptable macs)") self.local_mac = agreed_local_macs[0] self.remote_mac = agreed_remote_macs[0] self._log_agreement( - 'MAC', local=self.local_mac, remote=self.remote_mac + "MAC", local=self.local_mac, remote=self.remote_mac ) if self.server_mode: - agreed_remote_compression = list(filter( - self._preferred_compression.__contains__, - client_compress_algo_list - )) - agreed_local_compression = list(filter( - self._preferred_compression.__contains__, - server_compress_algo_list - )) + agreed_remote_compression = list( + filter( + self._preferred_compression.__contains__, + client_compress_algo_list, + ) + ) + agreed_local_compression = list( + filter( + self._preferred_compression.__contains__, + server_compress_algo_list, + ) + ) else: - agreed_local_compression = list(filter( - client_compress_algo_list.__contains__, - self._preferred_compression - )) - agreed_remote_compression = list(filter( - server_compress_algo_list.__contains__, - self._preferred_compression - )) + agreed_local_compression = list( + filter( + client_compress_algo_list.__contains__, + self._preferred_compression, + ) + ) + agreed_remote_compression = list( + filter( + server_compress_algo_list.__contains__, + self._preferred_compression, + ) + ) if ( - len(agreed_local_compression) == 0 or - len(agreed_remote_compression) == 0 + len(agreed_local_compression) == 0 + or len(agreed_remote_compression) == 0 ): - msg = 'Incompatible ssh server (no acceptable compression) {0!r} {1!r} {2!r}' # noqa - raise SSHException(msg.format( - agreed_local_compression, agreed_remote_compression, - self._preferred_compression, - )) + msg = "Incompatible ssh server (no acceptable compression) {0!r} {1!r} {2!r}" # noqa + raise SSHException( + msg.format( + agreed_local_compression, + agreed_remote_compression, + self._preferred_compression, + ) + ) self.local_compression = agreed_local_compression[0] self.remote_compression = agreed_remote_compression[0] self._log_agreement( - 'Compression', + "Compression", local=self.local_compression, - remote=self.remote_compression + remote=self.remote_compression, ) # save for computing hash later... @@ -2241,40 +2356,36 @@ class Transport(threading.Thread, ClosingContextManager): def _activate_inbound(self): """switch on newly negotiated encryption parameters for inbound traffic""" - block_size = self._cipher_info[self.remote_cipher]['block-size'] + block_size = self._cipher_info[self.remote_cipher]["block-size"] if self.server_mode: - IV_in = self._compute_key('A', block_size) + IV_in = self._compute_key("A", block_size) key_in = self._compute_key( - 'C', self._cipher_info[self.remote_cipher]['key-size'] + "C", self._cipher_info[self.remote_cipher]["key-size"] ) else: - IV_in = self._compute_key('B', block_size) + IV_in = self._compute_key("B", block_size) key_in = self._compute_key( - 'D', self._cipher_info[self.remote_cipher]['key-size'] + "D", self._cipher_info[self.remote_cipher]["key-size"] ) engine = self._get_cipher( self.remote_cipher, key_in, IV_in, self._DECRYPT ) - mac_size = self._mac_info[self.remote_mac]['size'] - mac_engine = self._mac_info[self.remote_mac]['class'] + mac_size = self._mac_info[self.remote_mac]["size"] + mac_engine = self._mac_info[self.remote_mac]["class"] # initial mac keys are done in the hash's natural size (not the # potentially truncated transmission size) if self.server_mode: - mac_key = self._compute_key('E', mac_engine().digest_size) + mac_key = self._compute_key("E", mac_engine().digest_size) else: - mac_key = self._compute_key('F', mac_engine().digest_size) + mac_key = self._compute_key("F", mac_engine().digest_size) self.packetizer.set_inbound_cipher( engine, block_size, mac_engine, mac_size, mac_key ) compress_in = self._compression_info[self.remote_compression][1] - if ( - compress_in is not None and - ( - self.remote_compression != 'zlib@openssh.com' or - self.authenticated - ) + if compress_in is not None and ( + self.remote_compression != "zlib@openssh.com" or self.authenticated ): - self._log(DEBUG, 'Switching on inbound compression ...') + self._log(DEBUG, "Switching on inbound compression ...") self.packetizer.set_inbound_compressor(compress_in()) def _activate_outbound(self): @@ -2283,37 +2394,37 @@ class Transport(threading.Thread, ClosingContextManager): m = Message() m.add_byte(cMSG_NEWKEYS) self._send_message(m) - block_size = self._cipher_info[self.local_cipher]['block-size'] + block_size = self._cipher_info[self.local_cipher]["block-size"] if self.server_mode: - IV_out = self._compute_key('B', block_size) + IV_out = self._compute_key("B", block_size) key_out = self._compute_key( - 'D', self._cipher_info[self.local_cipher]['key-size']) + "D", self._cipher_info[self.local_cipher]["key-size"] + ) else: - IV_out = self._compute_key('A', block_size) + IV_out = self._compute_key("A", block_size) key_out = self._compute_key( - 'C', self._cipher_info[self.local_cipher]['key-size']) + "C", self._cipher_info[self.local_cipher]["key-size"] + ) engine = self._get_cipher( - self.local_cipher, key_out, IV_out, self._ENCRYPT) - mac_size = self._mac_info[self.local_mac]['size'] - mac_engine = self._mac_info[self.local_mac]['class'] + self.local_cipher, key_out, IV_out, self._ENCRYPT + ) + mac_size = self._mac_info[self.local_mac]["size"] + mac_engine = self._mac_info[self.local_mac]["class"] # initial mac keys are done in the hash's natural size (not the # potentially truncated transmission size) if self.server_mode: - mac_key = self._compute_key('F', mac_engine().digest_size) + mac_key = self._compute_key("F", mac_engine().digest_size) else: - mac_key = self._compute_key('E', mac_engine().digest_size) - sdctr = self.local_cipher.endswith('-ctr') + mac_key = self._compute_key("E", mac_engine().digest_size) + sdctr = self.local_cipher.endswith("-ctr") self.packetizer.set_outbound_cipher( - engine, block_size, mac_engine, mac_size, mac_key, sdctr) + engine, block_size, mac_engine, mac_size, mac_key, sdctr + ) compress_out = self._compression_info[self.local_compression][0] - if ( - compress_out is not None and - ( - self.local_compression != 'zlib@openssh.com' or - self.authenticated - ) + if compress_out is not None and ( + self.local_compression != "zlib@openssh.com" or self.authenticated ): - self._log(DEBUG, 'Switching on outbound compression ...') + self._log(DEBUG, "Switching on outbound compression ...") self.packetizer.set_outbound_compressor(compress_out()) if not self.packetizer.need_rekey(): self.in_kex = False @@ -2323,17 +2434,17 @@ class Transport(threading.Thread, ClosingContextManager): def _auth_trigger(self): self.authenticated = True # delayed initiation of compression - if self.local_compression == 'zlib@openssh.com': + if self.local_compression == "zlib@openssh.com": compress_out = self._compression_info[self.local_compression][0] - self._log(DEBUG, 'Switching on outbound compression ...') + self._log(DEBUG, "Switching on outbound compression ...") self.packetizer.set_outbound_compressor(compress_out()) - if self.remote_compression == 'zlib@openssh.com': + if self.remote_compression == "zlib@openssh.com": compress_in = self._compression_info[self.remote_compression][1] - self._log(DEBUG, 'Switching on inbound compression ...') + self._log(DEBUG, "Switching on inbound compression ...") self.packetizer.set_inbound_compressor(compress_in()) def _parse_newkeys(self, m): - self._log(DEBUG, 'Switch to new keys ...') + self._log(DEBUG, "Switch to new keys ...") self._activate_inbound() # can also free a bunch of stuff here self.local_kex_init = self.remote_kex_init = None @@ -2361,7 +2472,7 @@ class Transport(threading.Thread, ClosingContextManager): def _parse_disconnect(self, m): code = m.get_int() desc = m.get_text() - self._log(INFO, 'Disconnect (code %d): %s' % (code, desc)) + self._log(INFO, "Disconnect (code %d): %s" % (code, desc)) def _parse_global_request(self, m): kind = m.get_text() @@ -2369,17 +2480,16 @@ class Transport(threading.Thread, ClosingContextManager): want_reply = m.get_boolean() if not self.server_mode: self._log( - DEBUG, - 'Rejecting "%s" global request from server.' % kind + DEBUG, 'Rejecting "%s" global request from server.' % kind ) ok = False - elif kind == 'tcpip-forward': + elif kind == "tcpip-forward": address = m.get_text() port = m.get_int() ok = self.server_object.check_port_forward_request(address, port) if ok: ok = (ok,) - elif kind == 'cancel-tcpip-forward': + elif kind == "cancel-tcpip-forward": address = m.get_text() port = m.get_int() self.server_object.cancel_port_forward_request(address, port) @@ -2400,13 +2510,13 @@ class Transport(threading.Thread, ClosingContextManager): self._send_message(msg) def _parse_request_success(self, m): - self._log(DEBUG, 'Global request successful.') + self._log(DEBUG, "Global request successful.") self.global_response = m if self.completion_event is not None: self.completion_event.set() def _parse_request_failure(self, m): - self._log(DEBUG, 'Global request denied.') + self._log(DEBUG, "Global request denied.") self.global_response = None if self.completion_event is not None: self.completion_event.set() @@ -2418,13 +2528,14 @@ class Transport(threading.Thread, ClosingContextManager): server_max_packet_size = m.get_int() chan = self._channels.get(chanid) if chan is None: - self._log(WARNING, 'Success for unrequested channel! [??]') + self._log(WARNING, "Success for unrequested channel! [??]") return self.lock.acquire() try: chan._set_remote_channel( - server_chanid, server_window_size, server_max_packet_size) - self._log(DEBUG, 'Secsh channel %d opened.' % chanid) + server_chanid, server_window_size, server_max_packet_size + ) + self._log(DEBUG, "Secsh channel %d opened." % chanid) if chanid in self.channel_events: self.channel_events[chanid].set() del self.channel_events[chanid] @@ -2437,11 +2548,11 @@ class Transport(threading.Thread, ClosingContextManager): reason = m.get_int() reason_str = m.get_text() m.get_text() # ignored language - reason_text = CONNECTION_FAILED_CODE.get(reason, '(unknown code)') + reason_text = CONNECTION_FAILED_CODE.get(reason, "(unknown code)") self._log( ERROR, - 'Secsh channel %d open FAILED: %s: %s' % ( - chanid, reason_str, reason_text) + "Secsh channel %d open FAILED: %s: %s" + % (chanid, reason_str, reason_text), ) self.lock.acquire() try: @@ -2462,37 +2573,37 @@ class Transport(threading.Thread, ClosingContextManager): max_packet_size = m.get_int() reject = False if ( - kind == 'auth-agent@openssh.com' and - self._forward_agent_handler is not None + kind == "auth-agent@openssh.com" + and self._forward_agent_handler is not None ): - self._log(DEBUG, 'Incoming forward agent connection') + self._log(DEBUG, "Incoming forward agent connection") self.lock.acquire() try: my_chanid = self._next_channel() finally: self.lock.release() - elif (kind == 'x11') and (self._x11_handler is not None): + elif (kind == "x11") and (self._x11_handler is not None): origin_addr = m.get_text() origin_port = m.get_int() self._log( DEBUG, - 'Incoming x11 connection from %s:%d' % ( - origin_addr, origin_port) + "Incoming x11 connection from %s:%d" + % (origin_addr, origin_port), ) self.lock.acquire() try: my_chanid = self._next_channel() finally: self.lock.release() - elif (kind == 'forwarded-tcpip') and (self._tcp_handler is not None): + elif (kind == "forwarded-tcpip") and (self._tcp_handler is not None): server_addr = m.get_text() server_port = m.get_int() origin_addr = m.get_text() origin_port = m.get_int() self._log( DEBUG, - 'Incoming tcp forwarded connection from %s:%d' % ( - origin_addr, origin_port) + "Incoming tcp forwarded connection from %s:%d" + % (origin_addr, origin_port), ) self.lock.acquire() try: @@ -2501,8 +2612,8 @@ class Transport(threading.Thread, ClosingContextManager): self.lock.release() elif not self.server_mode: self._log( - DEBUG, - 'Rejecting "%s" channel request from server.' % kind) + DEBUG, 'Rejecting "%s" channel request from server.' % kind + ) reject = True reason = OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED else: @@ -2511,7 +2622,7 @@ class Transport(threading.Thread, ClosingContextManager): my_chanid = self._next_channel() finally: self.lock.release() - if kind == 'direct-tcpip': + if kind == "direct-tcpip": # handle direct-tcpip requests coming from the client dest_addr = m.get_text() dest_port = m.get_int() @@ -2520,23 +2631,24 @@ class Transport(threading.Thread, ClosingContextManager): reason = self.server_object.check_channel_direct_tcpip_request( my_chanid, (origin_addr, origin_port), - (dest_addr, dest_port) + (dest_addr, dest_port), ) else: reason = self.server_object.check_channel_request( - kind, my_chanid) + kind, my_chanid + ) if reason != OPEN_SUCCEEDED: self._log( - DEBUG, - 'Rejecting "%s" channel request from client.' % kind) + DEBUG, 'Rejecting "%s" channel request from client.' % kind + ) reject = True if reject: msg = Message() msg.add_byte(cMSG_CHANNEL_OPEN_FAILURE) msg.add_int(chanid) msg.add_int(reason) - msg.add_string('') - msg.add_string('en') + msg.add_string("") + msg.add_string("en") self._send_message(msg) return @@ -2547,9 +2659,11 @@ class Transport(threading.Thread, ClosingContextManager): self.channels_seen[my_chanid] = True chan._set_transport(self) chan._set_window( - self.default_window_size, self.default_max_packet_size) + self.default_window_size, self.default_max_packet_size + ) chan._set_remote_channel( - chanid, initial_window_size, max_packet_size) + chanid, initial_window_size, max_packet_size + ) finally: self.lock.release() m = Message() @@ -2559,17 +2673,15 @@ class Transport(threading.Thread, ClosingContextManager): m.add_int(self.default_window_size) m.add_int(self.default_max_packet_size) self._send_message(m) - self._log(DEBUG, 'Secsh channel %d (%s) opened.', my_chanid, kind) - if kind == 'auth-agent@openssh.com': + self._log(DEBUG, "Secsh channel %d (%s) opened.", my_chanid, kind) + if kind == "auth-agent@openssh.com": self._forward_agent_handler(chan) - elif kind == 'x11': + elif kind == "x11": self._x11_handler(chan, (origin_addr, origin_port)) - elif kind == 'forwarded-tcpip': + elif kind == "forwarded-tcpip": chan.origin_addr = (origin_addr, origin_port) self._tcp_handler( - chan, - (origin_addr, origin_port), - (server_addr, server_port) + chan, (origin_addr, origin_port), (server_addr, server_port) ) else: self._queue_incoming_channel(chan) @@ -2578,7 +2690,7 @@ class Transport(threading.Thread, ClosingContextManager): m.get_boolean() # always_display msg = m.get_string() m.get_string() # language - self._log(DEBUG, 'Debug msg: {0}'.format(util.safe_string(msg))) + self._log(DEBUG, "Debug msg: {0}".format(util.safe_string(msg))) def _get_subsystem_handler(self, name): try: @@ -2612,7 +2724,7 @@ class Transport(threading.Thread, ClosingContextManager): } -class SecurityOptions (object): +class SecurityOptions(object): """ Simple object containing the security preferences of an ssh transport. These are tuples of acceptable ciphers, digests, key types, and key @@ -2624,7 +2736,8 @@ class SecurityOptions (object): ``ValueError`` will be raised. If you try to assign something besides a tuple to one of the fields, ``TypeError`` will be raised. """ - __slots__ = '_transport' + + __slots__ = "_transport" def __init__(self, transport): self._transport = transport @@ -2633,17 +2746,17 @@ class SecurityOptions (object): """ Returns a string representation of this object, for debugging. """ - return '<paramiko.SecurityOptions for %s>' % repr(self._transport) + return "<paramiko.SecurityOptions for %s>" % repr(self._transport) def _set(self, name, orig, x): if type(x) is list: x = tuple(x) if type(x) is not tuple: - raise TypeError('expected tuple or list') + raise TypeError("expected tuple or list") possible = list(getattr(self._transport, orig).keys()) forbidden = [n for n in x if n not in possible] if len(forbidden) > 0: - raise ValueError('unknown cipher') + raise ValueError("unknown cipher") setattr(self._transport, name, x) @property @@ -2653,7 +2766,7 @@ class SecurityOptions (object): @ciphers.setter def ciphers(self, x): - self._set('_preferred_ciphers', '_cipher_info', x) + self._set("_preferred_ciphers", "_cipher_info", x) @property def digests(self): @@ -2662,7 +2775,7 @@ class SecurityOptions (object): @digests.setter def digests(self, x): - self._set('_preferred_macs', '_mac_info', x) + self._set("_preferred_macs", "_mac_info", x) @property def key_types(self): @@ -2671,8 +2784,7 @@ class SecurityOptions (object): @key_types.setter def key_types(self, x): - self._set('_preferred_keys', '_key_info', x) - + self._set("_preferred_keys", "_key_info", x) @property def kex(self): @@ -2681,7 +2793,7 @@ class SecurityOptions (object): @kex.setter def kex(self, x): - self._set('_preferred_kex', '_kex_info', x) + self._set("_preferred_kex", "_kex_info", x) @property def compression(self): @@ -2690,10 +2802,10 @@ class SecurityOptions (object): @compression.setter def compression(self, x): - self._set('_preferred_compression', '_compression_info', x) + self._set("_preferred_compression", "_compression_info", x) -class ChannelMap (object): +class ChannelMap(object): def __init__(self): # (id -> Channel) self._map = weakref.WeakValueDictionary() |