diff options
Diffstat (limited to 'paramiko/transport.py')
-rw-r--r-- | paramiko/transport.py | 105 |
1 files changed, 78 insertions, 27 deletions
diff --git a/paramiko/transport.py b/paramiko/transport.py index bd145c1e..8919043f 100644 --- a/paramiko/transport.py +++ b/paramiko/transport.py @@ -145,6 +145,9 @@ class Transport(threading.Thread, ClosingContextManager): # These tuples of algorithm identifiers are in preference order; do not # reorder without reason! + # NOTE: if you need to modify these, we suggest leveraging the + # `disabled_algorithms` constructor argument (also available in SSHClient) + # instead of monkeypatching or subclassing. _preferred_ciphers = ( "aes128-ctr", "aes192-ctr", @@ -306,6 +309,7 @@ class Transport(threading.Thread, ClosingContextManager): default_max_packet_size=DEFAULT_MAX_PACKET_SIZE, gss_kex=False, gss_deleg_creds=True, + disabled_algorithms=None, ): """ Create a new SSH session over an existing socket, or socket-like @@ -352,12 +356,30 @@ class Transport(threading.Thread, ClosingContextManager): :param bool gss_deleg_creds: Whether to enable GSSAPI credential delegation when GSSAPI is in play. Default: ``True``. + :param dict disabled_algorithms: + If given, must be a dictionary mapping algorithm type to an + iterable of algorithm identifiers, which will be disabled for the + lifetime of the transport. + + Keys should match the last word in the class' builtin algorithm + tuple attributes, such as ``"ciphers"`` to disable names within + ``_preferred_ciphers``; or ``"kex"`` to disable something defined + inside ``_preferred_kex``. Values should exactly match members of + the matching attribute. + + For example, if you need to disable + ``diffie-hellman-group16-sha512`` key exchange (perhaps because + your code talks to a server which implements it differently from + Paramiko), specify ``disabled_algorithms={"kex": + ["diffie-hellman-group16-sha512"]}``. .. versionchanged:: 1.15 Added the ``default_window_size`` and ``default_max_packet_size`` arguments. .. versionchanged:: 1.15 Added the ``gss_kex`` and ``gss_deleg_creds`` kwargs. + .. versionchanged:: 2.6 + Added the ``disabled_algorithms`` kwarg. """ self.active = False self.hostname = None @@ -465,6 +487,7 @@ class Transport(threading.Thread, ClosingContextManager): self.handshake_timeout = 15 # how long (seconds) to wait for the auth response. self.auth_timeout = 30 + self.disabled_algorithms = disabled_algorithms or {} # server mode: self.server_mode = False @@ -474,6 +497,34 @@ class Transport(threading.Thread, ClosingContextManager): self.server_accept_cv = threading.Condition(self.lock) self.subsystem_table = {} + def _filter_algorithm(self, type_): + default = getattr(self, "_preferred_{}".format(type_)) + return tuple( + x + for x in default + if x not in self.disabled_algorithms.get(type_, []) + ) + + @property + def preferred_ciphers(self): + return self._filter_algorithm("ciphers") + + @property + def preferred_macs(self): + return self._filter_algorithm("macs") + + @property + def preferred_keys(self): + return self._filter_algorithm("keys") + + @property + def preferred_kex(self): + return self._filter_algorithm("kex") + + @property + def preferred_compression(self): + return self._filter_algorithm("compression") + def __repr__(self): """ Returns a string representation of this object, for debugging. @@ -2206,7 +2257,7 @@ class Transport(threading.Thread, ClosingContextManager): mp_required_prefix = "diffie-hellman-group-exchange-sha" kex_mp = [ k - for k in self._preferred_kex + for k in self.preferred_kex if k.startswith(mp_required_prefix) ] if (self._modulus_pack is None) and (len(kex_mp) > 0): @@ -2221,23 +2272,23 @@ class Transport(threading.Thread, ClosingContextManager): available_server_keys = list( filter( list(self.server_key_dict.keys()).__contains__, - self._preferred_keys, + self.preferred_keys, ) ) else: - available_server_keys = self._preferred_keys + available_server_keys = self.preferred_keys m = Message() m.add_byte(cMSG_KEXINIT) m.add_bytes(os.urandom(16)) - m.add_list(self._preferred_kex) + m.add_list(self.preferred_kex) m.add_list(available_server_keys) - m.add_list(self._preferred_ciphers) - m.add_list(self._preferred_ciphers) - m.add_list(self._preferred_macs) - m.add_list(self._preferred_macs) - m.add_list(self._preferred_compression) - m.add_list(self._preferred_compression) + m.add_list(self.preferred_ciphers) + m.add_list(self.preferred_ciphers) + m.add_list(self.preferred_macs) + m.add_list(self.preferred_macs) + m.add_list(self.preferred_compression) + m.add_list(self.preferred_compression) m.add_string(bytes()) m.add_string(bytes()) m.add_boolean(False) @@ -2293,11 +2344,11 @@ class Transport(threading.Thread, ClosingContextManager): # supports. if self.server_mode: agreed_kex = list( - filter(self._preferred_kex.__contains__, kex_algo_list) + filter(self.preferred_kex.__contains__, kex_algo_list) ) else: agreed_kex = list( - filter(kex_algo_list.__contains__, self._preferred_kex) + filter(kex_algo_list.__contains__, self.preferred_kex) ) if len(agreed_kex) == 0: raise SSHException( @@ -2310,7 +2361,7 @@ class Transport(threading.Thread, ClosingContextManager): available_server_keys = list( filter( list(self.server_key_dict.keys()).__contains__, - self._preferred_keys, + self.preferred_keys, ) ) agreed_keys = list( @@ -2320,7 +2371,7 @@ class Transport(threading.Thread, ClosingContextManager): ) else: agreed_keys = list( - filter(server_key_algo_list.__contains__, self._preferred_keys) + filter(server_key_algo_list.__contains__, self.preferred_keys) ) if len(agreed_keys) == 0: raise SSHException( @@ -2336,13 +2387,13 @@ class Transport(threading.Thread, ClosingContextManager): if self.server_mode: agreed_local_ciphers = list( filter( - self._preferred_ciphers.__contains__, + self.preferred_ciphers.__contains__, server_encrypt_algo_list, ) ) agreed_remote_ciphers = list( filter( - self._preferred_ciphers.__contains__, + self.preferred_ciphers.__contains__, client_encrypt_algo_list, ) ) @@ -2350,13 +2401,13 @@ class Transport(threading.Thread, ClosingContextManager): agreed_local_ciphers = list( filter( client_encrypt_algo_list.__contains__, - self._preferred_ciphers, + self.preferred_ciphers, ) ) agreed_remote_ciphers = list( filter( server_encrypt_algo_list.__contains__, - self._preferred_ciphers, + self.preferred_ciphers, ) ) if len(agreed_local_ciphers) == 0 or len(agreed_remote_ciphers) == 0: @@ -2371,17 +2422,17 @@ class Transport(threading.Thread, ClosingContextManager): if self.server_mode: agreed_remote_macs = list( - filter(self._preferred_macs.__contains__, client_mac_algo_list) + filter(self.preferred_macs.__contains__, client_mac_algo_list) ) agreed_local_macs = list( - filter(self._preferred_macs.__contains__, server_mac_algo_list) + filter(self.preferred_macs.__contains__, server_mac_algo_list) ) else: agreed_local_macs = list( - filter(client_mac_algo_list.__contains__, self._preferred_macs) + filter(client_mac_algo_list.__contains__, self.preferred_macs) ) agreed_remote_macs = list( - filter(server_mac_algo_list.__contains__, self._preferred_macs) + 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)") @@ -2394,13 +2445,13 @@ class Transport(threading.Thread, ClosingContextManager): if self.server_mode: agreed_remote_compression = list( filter( - self._preferred_compression.__contains__, + self.preferred_compression.__contains__, client_compress_algo_list, ) ) agreed_local_compression = list( filter( - self._preferred_compression.__contains__, + self.preferred_compression.__contains__, server_compress_algo_list, ) ) @@ -2408,13 +2459,13 @@ class Transport(threading.Thread, ClosingContextManager): agreed_local_compression = list( filter( client_compress_algo_list.__contains__, - self._preferred_compression, + self.preferred_compression, ) ) agreed_remote_compression = list( filter( server_compress_algo_list.__contains__, - self._preferred_compression, + self.preferred_compression, ) ) if ( @@ -2427,7 +2478,7 @@ class Transport(threading.Thread, ClosingContextManager): msg.format( agreed_local_compression, agreed_remote_compression, - self._preferred_compression, + self.preferred_compression, ) ) self.local_compression = agreed_local_compression[0] |