Skip to content

Commit

Permalink
SSHClient: fix the host key test
Browse files Browse the repository at this point in the history
Skip the host key check only, if the transport actually used
gssapi-keyex. Add tests for the missing-host-key RejectPolicy.

Before this change, a man-in-the-middle attack on the paramiko ssh
client with gss_kex=True was possible by having a server that does not
support gssapi-keyex and gives any or no host key.
  • Loading branch information
Anselm Kruis committed Aug 4, 2017
1 parent 853a37f commit 1b2697b
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 1 deletion.
2 changes: 1 addition & 1 deletion paramiko/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ def connect(
# 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:
if not self._transport.gss_kex_used:
our_server_key = self._system_host_keys.get(
server_hostkey_name, {}).get(keytype)
if our_server_key is None:
Expand Down
61 changes: 61 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ def _test_connection(self, **kwargs):
self.assertTrue(self.ts.is_active())
self.assertEqual('slowdive', self.ts.get_username())
self.assertEqual(True, self.ts.is_authenticated())
self.assertEqual(False, self.tc.get_transport().gss_kex_used)

# Command execution functions?
stdin, stdout, stderr = self.tc.exec_command('yes')
Expand Down Expand Up @@ -366,3 +367,63 @@ def test_8_auth_trickledown(self):
password='pygmalion',
)
self._test_connection(**kwargs)

def test_9_auth_trickledown_gsskex(self):
"""
Failed gssapi-keyex auth doesn't prevent subsequent key auth from succeeding
"""
if not paramiko.GSS_AUTH_AVAILABLE:
return # for python 2.6 lacks skipTest
kwargs = dict(
gss_kex=True,
key_filename=[test_path('test_rsa.key')],
)
self._test_connection(**kwargs)

def test_10_auth_trickledown_gssauth(self):
"""
Failed gssapi-with-mic auth doesn't prevent subsequent key auth from succeeding
"""
if not paramiko.GSS_AUTH_AVAILABLE:
return # for python 2.6 lacks skipTest
kwargs = dict(
gss_auth=True,
key_filename=[test_path('test_rsa.key')],
)
self._test_connection(**kwargs)

def test_11_reject_policy(self):
"""
verify that SSHClient's RejectPolicy works.
"""
threading.Thread(target=self._run).start()

self.tc = paramiko.SSHClient()
self.tc.set_missing_host_key_policy(paramiko.RejectPolicy())
self.assertEqual(0, len(self.tc.get_host_keys()))
self.assertRaises(
paramiko.SSHException,
self.tc.connect,
password='pygmalion', **self.connect_kwargs
)

def test_12_reject_policy_gsskex(self):
"""
verify that SSHClient's RejectPolicy works,
even if gssapi-keyex was enabled but not used.
"""
# Test for a bug present in paramiko versions released before 2017-08-01
if not paramiko.GSS_AUTH_AVAILABLE:
return # for python 2.6 lacks skipTest
threading.Thread(target=self._run).start()

self.tc = paramiko.SSHClient()
self.tc.set_missing_host_key_policy(paramiko.RejectPolicy())
self.assertEqual(0, len(self.tc.get_host_keys()))
self.assertRaises(
paramiko.SSHException,
self.tc.connect,
password='pygmalion',
gss_kex=True,
**self.connect_kwargs
)

0 comments on commit 1b2697b

Please sign in to comment.