From f4dedacb9040d27d9844f51c81c28e0247d3e4a3 Mon Sep 17 00:00:00 2001 From: Jeff Forcier Date: Sat, 16 Dec 2023 13:02:05 -0500 Subject: [PATCH] Raise new exception type when unexpected messages appear Reference:https://github.com/paramiko/paramiko/commit/f4dedacb9040d27d9844f51c81c28e0247d3e4a3 Conflict:The changlog file is adapted for different versions. The context of the test case import module is adapted. --- paramiko/__init__.py | 1 + paramiko/ssh_exception.py | 9 +++++++++ paramiko/transport.py | 6 +++++- tests/test_transport.py | 22 +++++++++++++++++++--- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/paramiko/__init__.py b/paramiko/__init__.py index cbc240a..1bc91d0 100644 --- a/paramiko/__init__.py +++ b/paramiko/__init__.py @@ -43,6 +43,7 @@ from paramiko.ssh_exception import ( ConfigParseError, CouldNotCanonicalize, IncompatiblePeer, + MessageOrderError, PasswordRequiredException, ProxyCommandFailure, SSHException, diff --git a/paramiko/ssh_exception.py b/paramiko/ssh_exception.py index 620ab25..8a1413b 100644 --- a/paramiko/ssh_exception.py +++ b/paramiko/ssh_exception.py @@ -235,3 +235,12 @@ class ConfigParseError(SSHException): """ pass + + +class MessageOrderError(SSHException): + """ + Out-of-order protocol messages were received, violating "strict kex" mode. + .. versionadded:: 3.4 + """ + + pass diff --git a/paramiko/transport.py b/paramiko/transport.py index 2d6d581..eb1bcd6 100644 --- a/paramiko/transport.py +++ b/paramiko/transport.py @@ -110,6 +110,7 @@ from paramiko.ssh_exception import ( BadAuthenticationType, ChannelException, IncompatiblePeer, + MessageOrderError, ProxyCommandFailure, ) from paramiko.util import retry_on_signal, ClosingContextManager, clamp_value @@ -2129,7 +2130,10 @@ class Transport(threading.Thread, ClosingContextManager): continue if len(self._expected_packet) > 0: if ptype not in self._expected_packet: - raise SSHException( + exc_class = SSHException + if self.agreed_on_strict_kex: + exc_class = MessageOrderError + raise exc_class( "Expecting packet from {!r}, got {:d}".format( self._expected_packet, ptype ) diff --git a/tests/test_transport.py b/tests/test_transport.py index c8cd498..19023eb 100644 --- a/tests/test_transport.py +++ b/tests/test_transport.py @@ -42,6 +42,7 @@ from paramiko import ( SSHException, AuthenticationException, IncompatiblePeer, + MessageOrderError, SecurityOptions, ServerInterface, Transport, @@ -64,7 +65,7 @@ from paramiko.message import Message from .util import needs_builtin, _support, requires_sha1_signing, slow from .loop import LoopSocket -from pytest import skip, mark +from pytest import skip, mark, raises LONG_BANNER = """\ @@ -1504,5 +1505,20 @@ class TestStrictKex: def test_sequence_numbers_reset_on_newkeys(self): skip() - def test_error_raised_on_out_of_order_handshakes(self): - skip() + def test_MessageOrderError_raised_on_out_of_order_messages(self): + with raises(MessageOrderError): + with server() as (tc, _): + # A bit artificial as it's outside kexinit/handshake, but much + # easier to trigger and still in line with behavior under test + tc._expect_packet(MSG_KEXINIT) + tc.open_session() + + def test_SSHException_raised_on_out_of_order_messages_when_not_strict(self): + # This is kind of dumb (either situation is still fatal!) but whatever, + # may as well be strict with our new strict flag... + with raises(SSHException) as info: # would be true either way, but + with server(client_init=dict(strict_kex=False), + ) as (tc, _): + tc._expect_packet(MSG_KEXINIT) + tc.open_session() + assert info.type is SSHException # NOT MessageOrderError! -- 2.33.0