summaryrefslogtreecommitdiff
path: root/paramiko
diff options
context:
space:
mode:
authorPierce Lopez <pierce.lopez@gmail.com>2017-02-20 15:36:29 -0500
committerPierce Lopez <pierce.lopez@gmail.com>2017-06-07 01:58:19 -0400
commit4e973d97b0baf8fc9b7b5fbd84af4a869829dce5 (patch)
treee4e112072b241b26911085e3770349dc77e73948 /paramiko
parent047640af7b30b7c4973c86082d53c28d5a90a8e0 (diff)
downloadparamiko-4e973d97b0baf8fc9b7b5fbd84af4a869829dce5.tar.gz
SSHClient: adjust Transport preferred host key types if known host
If we have a host keys that will be checked, we need to negotiate for the type we have. Commonly, openssh could have saved an ecdsa key in known_hosts, but SSHClient will let the Transport negotiate for an rsa key. Then it would consider a key of a non-corresponding type to be "missing". That situation is also now a BadHostKeyException. Before this change, a man-in-the-middle attack on the paramiko ssh client was possible by having only a host key type which differs from what the client has in known_hosts (and then giving any key of that type).
Diffstat (limited to 'paramiko')
-rw-r--r--paramiko/client.py48
1 files changed, 27 insertions, 21 deletions
diff --git a/paramiko/client.py b/paramiko/client.py
index d4bd8cb6..55365705 100644
--- a/paramiko/client.py
+++ b/paramiko/client.py
@@ -341,37 +341,43 @@ class SSHClient (ClosingContextManager):
t.set_log_channel(self._log_channel)
if banner_timeout is not None:
t.banner_timeout = banner_timeout
- t.start_client(timeout=timeout)
- t.set_sshclient(self)
- ResourceManager.register(self, t)
-
- server_key = t.get_remote_server_key()
- keytype = server_key.get_name()
if port == SSH_PORT:
server_hostkey_name = hostname
else:
server_hostkey_name = "[%s]:%d" % (hostname, port)
+ our_server_keys = None
# If GSS-API Key Exchange is performed we are not required to check the
# host key, because the host is authenticated via GSS-API / SSPI as
# well as our client.
if not self._transport.use_gss_kex:
- our_server_key = self._system_host_keys.get(
- server_hostkey_name, {}).get(keytype)
- if our_server_key is None:
- our_server_key = self._host_keys.get(server_hostkey_name,
- {}).get(keytype, None)
- if our_server_key is None:
- # will raise exception if the key is rejected;
- # let that fall out
- self._policy.missing_host_key(self, server_hostkey_name,
- server_key)
- # if the callback returns, assume the key is ok
- our_server_key = server_key
-
- if server_key != our_server_key:
- raise BadHostKeyException(hostname, server_key, our_server_key)
+ our_server_keys = self._system_host_keys.get(server_hostkey_name)
+ if our_server_keys is None:
+ our_server_keys = self._host_keys.get(server_hostkey_name)
+ if our_server_keys is not None:
+ keytype = our_server_keys.keys()[0]
+ sec_opts = t.get_security_options()
+ other_types = [x for x in sec_opts.key_types if x != keytype]
+ sec_opts.key_types = [keytype] + other_types
+
+ t.start_client(timeout=timeout)
+ t.set_sshclient(self)
+ ResourceManager.register(self, t)
+
+ if not self._transport.use_gss_kex:
+ server_key = t.get_remote_server_key()
+ if our_server_keys is None:
+ # will raise exception if the key is rejected
+ self._policy.missing_host_key(
+ self, server_hostkey_name, server_key
+ )
+ else:
+ our_key = our_server_keys.get(server_key.get_name())
+ if our_key != server_key:
+ if our_key is None:
+ our_key = list(our_server_keys.values())[0]
+ raise BadHostKeyException(hostname, server_key, our_key)
if username is None:
username = getpass.getuser()