diff options
-rw-r--r--[-rwxr-xr-x] | demos/demo_simple.py | 11 | ||||
-rw-r--r-- | paramiko/client.py | 2 | ||||
-rw-r--r-- | paramiko/transport.py | 12 | ||||
-rw-r--r-- | sites/www/changelog.rst | 6 | ||||
-rw-r--r-- | tests/test_client.py | 61 |
5 files changed, 81 insertions, 11 deletions
diff --git a/demos/demo_simple.py b/demos/demo_simple.py index 3a17988c..7ae3d8c8 100755..100644 --- a/demos/demo_simple.py +++ b/demos/demo_simple.py @@ -37,8 +37,10 @@ except ImportError: # setup logging paramiko.util.log_to_file('demo_simple.log') # Paramiko client configuration -UseGSSAPI = True # enable GSS-API / SSPI authentication -DoGSSAPIKeyExchange = True +UseGSSAPI = paramiko.GSS_AUTH_AVAILABLE # enable "gssapi-with-mic" authentication, if supported by your python installation +DoGSSAPIKeyExchange = paramiko.GSS_AUTH_AVAILABLE # enable "gssapi-kex" key exchange, if supported by your python installation +# UseGSSAPI = False +# DoGSSAPIKeyExchange = False port = 22 # get hostname @@ -64,7 +66,7 @@ if username == '': username = input('Username [%s]: ' % default_username) if len(username) == 0: username = default_username -if not UseGSSAPI or (not UseGSSAPI and not DoGSSAPIKeyExchange): +if not UseGSSAPI and not DoGSSAPIKeyExchange: password = getpass.getpass('Password for %s@%s: ' % (username, hostname)) @@ -74,7 +76,7 @@ try: client.load_system_host_keys() client.set_missing_host_key_policy(paramiko.WarningPolicy()) print('*** Connecting...') - if not UseGSSAPI or (not UseGSSAPI and not DoGSSAPIKeyExchange): + if not UseGSSAPI and not DoGSSAPIKeyExchange: client.connect(hostname, port, username, password) else: # SSPI works only with the FQDN of the target host @@ -83,6 +85,7 @@ try: client.connect(hostname, port, username, gss_auth=UseGSSAPI, gss_kex=DoGSSAPIKeyExchange) except Exception: + # traceback.print_exc() password = getpass.getpass('Password for %s@%s: ' % (username, hostname)) client.connect(hostname, port, username, password) diff --git a/paramiko/client.py b/paramiko/client.py index 224109bf..39617d5b 100644 --- a/paramiko/client.py +++ b/paramiko/client.py @@ -353,7 +353,7 @@ class SSHClient (ClosingContextManager): # 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: diff --git a/paramiko/transport.py b/paramiko/transport.py index c68397dd..1ab60841 100644 --- a/paramiko/transport.py +++ b/paramiko/transport.py @@ -132,6 +132,11 @@ class Transport(threading.Thread, ClosingContextManager): 'diffie-hellman-group-exchange-sha1', 'diffie-hellman-group-exchange-sha256', ) + _preferred_gsskex = ( + 'gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==', + 'gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g==', + 'gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==', + ) _preferred_compression = ('none',) _cipher_info = { @@ -333,12 +338,7 @@ class Transport(threading.Thread, ClosingContextManager): self.gss_host = None if self.use_gss_kex: self.kexgss_ctxt = GSSAuth("gssapi-keyex", gss_deleg_creds) - self._preferred_kex = ('gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==', - 'gss-group14-sha1-toWM5Slw5Ew8Mqkay+al2g==', - 'gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==', - 'diffie-hellman-group-exchange-sha1', - 'diffie-hellman-group14-sha1', - 'diffie-hellman-group1-sha1') + self._preferred_kex = self._preferred_gsskex + self._preferred_kex # state used during negotiation self.kex_engine = None diff --git a/sites/www/changelog.rst b/sites/www/changelog.rst index cb3ddb67..988aedac 100644 --- a/sites/www/changelog.rst +++ b/sites/www/changelog.rst @@ -2,6 +2,12 @@ Changelog ========= +* :bug:`1060` Fix key exchange (kex) algorithm list for GSSAPI authentication; + previously, the list used solely out-of-date algorithms, and now contains + newer ones listed preferentially before the old. Credit: Anselm Kruis. +* :bug:`1055` (also :issue:`1056`, :issue:`1057`, :issue:`1058`, :issue:`1059`) + Fix up host-key checking in our GSSAPI support, which was previously using an + incorrect API call. Thanks to Anselm Kruis for the patches. * :release:`2.0.6 <2017-06-09>` * :release:`1.18.3 <2017-06-09>` * :release:`1.17.5 <2017-06-09>` diff --git a/tests/test_client.py b/tests/test_client.py index bfdf5f81..9da6eaca 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -141,6 +141,7 @@ class SSHClientTest (unittest.TestCase): 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') @@ -366,3 +367,63 @@ class SSHClientTest (unittest.TestCase): 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 + ) |