summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJeff Forcier <jeff@bitprophet.org>2023-12-16 13:02:05 -0500
committerJeff Forcier <jeff@bitprophet.org>2023-12-16 16:16:32 -0500
commitf4dedacb9040d27d9844f51c81c28e0247d3e4a3 (patch)
tree223c58a7b9921917ca34113e8d252f899eed78e8
parentc32be441a5ff0dc4914b22d6d1efa392aebe862f (diff)
Raise new exception type when unexpected messages appear
-rw-r--r--paramiko/__init__.py1
-rw-r--r--paramiko/ssh_exception.py10
-rw-r--r--paramiko/transport.py6
-rw-r--r--sites/www/changelog.rst8
-rw-r--r--tests/test_transport.py22
5 files changed, 40 insertions, 7 deletions
diff --git a/paramiko/__init__.py b/paramiko/__init__.py
index b9c47dcc..65148d2a 100644
--- a/paramiko/__init__.py
+++ b/paramiko/__init__.py
@@ -59,6 +59,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 a1e1cfc3..2b68ebe8 100644
--- a/paramiko/ssh_exception.py
+++ b/paramiko/ssh_exception.py
@@ -238,3 +238,13 @@ 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 22992e17..44361b0c 100644
--- a/paramiko/transport.py
+++ b/paramiko/transport.py
@@ -110,6 +110,7 @@ from paramiko.ssh_exception import (
BadAuthenticationType,
ChannelException,
IncompatiblePeer,
+ MessageOrderError,
ProxyCommandFailure,
SSHException,
)
@@ -2148,7 +2149,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/sites/www/changelog.rst b/sites/www/changelog.rst
index 33f4ae1b..a709152e 100644
--- a/sites/www/changelog.rst
+++ b/sites/www/changelog.rst
@@ -18,12 +18,14 @@ Changelog
OpenSSH >= TK (or equivalent, such as Paramiko in server mode, as of this
patch version) and configured to use the new "strict kex" mode. Paramiko
will always attempt to use "strict kex" mode if offered.
- - Paramiko will raise TK if any protocol messages are received
- out-of-order during key exchange. Previously, TK.
+ - Paramiko will now raise an `SSHException` subclass (`MessageOrderError`)
+ when protocol messages are received in unexpected order. (This is not
+ *really* a change in behavior, as most such cases already raised vanilla
+ `SSHException` anyways.)
- Key (re)negotiation -- i.e. ``MSG_NEWKEYS``, whenever it is encountered
-- now resets packet sequence numbers. (This should be invisible to users
during normal operation, only causing exceptions if the exploit is
- encountered.)
+ encountered, which will usually result in, again, `MessageOrderError`.)
Thanks to Fabian Bäumer, Marcus Brinkmann, and Jörg Schwenk for submitting
details on the CVE prior to release.
diff --git a/tests/test_transport.py b/tests/test_transport.py
index 12078ba7..060a6cae 100644
--- a/tests/test_transport.py
+++ b/tests/test_transport.py
@@ -35,6 +35,7 @@ from paramiko import (
AuthHandler,
ChannelException,
IncompatiblePeer,
+ MessageOrderError,
Packetizer,
RSAKey,
SSHException,
@@ -69,7 +70,7 @@ from ._util import (
TestServer as NullServer,
)
from ._loop import LoopSocket
-from pytest import skip, mark
+from pytest import skip, mark, raises
LONG_BANNER = """\
@@ -1279,5 +1280,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!