--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: bookworm
X-Debbugs-Cc: python-async...@packages.debian.org
Control: affects -1 + src:python-asyncssh
User: release.debian....@packages.debian.org
Usertags: pu
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
[ Reason ]
Fixes for CVE-2023-46445 and CVE-2023-46446 have already been supplied to
sid/trixie and bullseye (via LTS). Only Bookworm is still affected by these
CVEs. This upload intends to provide a fix for users of Debian stable as well.
[ Impact ]
If not applied, users will be vuln erable to CVE-2023-46445 and CVE-2023-46446.
[ Tests ]
The upstream test suite has been enabled in the package. Upstream also supplied
multiple test cases to test the changed code. The tests are successful.
[ Risks ]
There is always the risk of a regression. However, all tests are successful,
and the fix has been applied to Bullseye via Debian LTS in September this year.
There have been no reported regressions or issues.
[ Checklist ]
[x] *all* changes are documented in the d/changelog
[x] I reviewed all changes and I approve them
[x] attach debdiff against the package in (old)stable
[x] the issue is verified as fixed in unstable
[ Changes ]
The patch supplied by upstream puts additional restrictions on when messages
are accepted during the SSH handshake to avoid message injection attacks from a
rogue client or server.
[ Other info ]
The patch contains further links to the original report and the upstream patch.
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCgAdFiEEvu1N7VVEpMA+KD3HS80FZ8KW0F0FAmd0HC4ACgkQS80FZ8KW
0F2tsA//amio5p4FiguQx6MLIqSJCQQ0DGpfPdn4lSQ/VJfzRlL5BKjtkYFfuIGc
5kqgqnSbO493ijMmkFajmjus8YPQlMFy63tBAVGYj7Wsc1kzvU0Fgj/dS6oEDw+S
uBHkR4V80nG/5UIcHmQ2aEWYJkAPfJyg1Wu43qmqF56/JIu1aECFd3P70/DKzOHM
SBFeF4ySabuRNMTb7+cp7id5ETEtquRs/nV2L+E9K1j52g/EURYX+DGdw0JPGgTi
SmphswuEjMTYuwZcuqGYee4xWB4foyxiaUNpj0kIRxfgW5OUvAwc4jnQKBND15wU
vG2yfSjys5KncDIJjUgn07yviMExzPreS0wiEM+OaAHA6C42ZxHF6pWTq1LRQCKt
vVt4Ch2HfyzRJw176k67w2sRgpgAb8zzrwdLHHySbiRgbswx2IrexxEuUg7q86nX
m0fRGt0os3otmM+eAjoAMeD4DZ+w/yZwFECqNNM+Vv3wtuyV9ccErQ4z9wF2+5ly
tKtY1OeJtceBrROMTjAaVK5nIUFwx3/3rH2obTGe5Ny2O58x1jsdb4fjRj8Q3t65
f0a/YBcoJvq0MhO9pQE0LWW0IXtd/QGADqu8mm/6zU8CQ8RnqGL3+6sbNS73lZ53
TUOeIhy368Qm48MgmBYP57Yj4xqLUH/+iTYOfPM2uhWVX0dnB8w=
=dYA2
-----END PGP SIGNATURE-----
diff -Nru python-asyncssh-2.10.1/debian/changelog
python-asyncssh-2.10.1/debian/changelog
--- python-asyncssh-2.10.1/debian/changelog 2024-04-16 16:14:32.000000000
+0200
+++ python-asyncssh-2.10.1/debian/changelog 2024-12-31 14:12:00.000000000
+0100
@@ -1,3 +1,18 @@
+python-asyncssh (2.10.1-2+deb12u2~1.gbpfeadf8) UNRELEASED; urgency=high
+
+ ** SNAPSHOT build @feadf821d32b3f10db7b0f238468a1d802a02cc1 **
+
+ * Non-maintainer upload by the Debian LTS team.
+ * UNRELEASED
+ * debian/patches/CVE-2023-46445-and-CVE-2023-46446.patch: Add patch to fix
+ CVE-2023-46445 and CVE-2023-46446 (Rogue Session Attack, Rogue Extension
+ Negotiation):
+ - Put additional restrictions on when messages are accepted during the
+ SSH handshake to avoid message injection attacks from a rogue client
+ or server (closes: #1055999, #1056000).
+
+ -- Daniel Leidert <dleid...@debian.org> Tue, 31 Dec 2024 14:12:00 +0100
+
python-asyncssh (2.10.1-2+deb12u1) bookworm-security; urgency=medium
* Apply and tweak upstream security fix for CVE-2023-48795
diff -Nru python-asyncssh-2.10.1/debian/gbp.conf
python-asyncssh-2.10.1/debian/gbp.conf
--- python-asyncssh-2.10.1/debian/gbp.conf 2022-12-22 04:41:08.000000000
+0100
+++ python-asyncssh-2.10.1/debian/gbp.conf 2024-12-31 14:12:00.000000000
+0100
@@ -1,2 +1,2 @@
[DEFAULT]
-debian-branch=debian/master
+debian-branch=debian/bookworm
diff -Nru
python-asyncssh-2.10.1/debian/patches/CVE-2023-46445-and-CVE-2023-46446.patch
python-asyncssh-2.10.1/debian/patches/CVE-2023-46445-and-CVE-2023-46446.patch
---
python-asyncssh-2.10.1/debian/patches/CVE-2023-46445-and-CVE-2023-46446.patch
1970-01-01 01:00:00.000000000 +0100
+++
python-asyncssh-2.10.1/debian/patches/CVE-2023-46445-and-CVE-2023-46446.patch
2024-12-31 14:12:00.000000000 +0100
@@ -0,0 +1,519 @@
+From: Ron Frederick <r...@timeheart.net>
+Date: Wed, 8 Nov 2023 18:06:33 -0800
+Subject: [PATCH] Harden AsyncSSH state machine against message injection
+ during handshake
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 8bit
+
+This commit puts additional restrictions on when messages are accepted
+during the SSH handshake to avoid message injection attacks from a
+rogue client or server.
+
+More detailed information will be available in CVE-2023-46445 and
+CVE-2023-46446, to be published shortly.
+
+Thanks go to Fabian Bäumer, Marcus Brinkmann, and Jörg Schwenk for
+identifying and reporting these vulnerabilities and providing
+detailed analysis and suggestions for how to protect against them,
+as well as review comments on the proposed fix.
+
+
+Reviewed-By: Daniel Leidert <dleid...@debian.org>
+Origin:
https://github.com/ronf/asyncssh/commit/83e43f5ea3470a8617fc388c72b062c7136efd7e
+Bug: https://github.com/advisories/GHSA-cfc2-wr2v-gxm5
+Bug: https://github.com/advisories/GHSA-c35q-ffpf-5qpm
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2023-46445
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2023-46446
+Bug-Freexian-Security:
https://deb.freexian.com/extended-lts/tracker/CVE-2023-46445
+Bug-Freexian-Security:
https://deb.freexian.com/extended-lts/tracker/CVE-2023-46446
+---
+ asyncssh/connection.py | 132 ++++++++++++++++++++++++++---------------
+ tests/test_connection.py | 151 ++++++++++++++++++++++++++++++++++++++---------
+ 2 files changed, 207 insertions(+), 76 deletions(-)
+
+diff --git a/asyncssh/connection.py b/asyncssh/connection.py
+index 6c23e3b..bd53fbc 100644
+--- a/asyncssh/connection.py
++++ b/asyncssh/connection.py
+@@ -844,6 +844,8 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
+ self._can_send_ext_info = False
+ self._extensions_to_send: 'OrderedDict[bytes, bytes]' = OrderedDict()
+
++ self._can_recv_ext_info = False
++
+ self._server_sig_algs: Set[bytes] = set()
+
+ self._next_service: Optional[bytes] = None
+@@ -853,6 +855,7 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
+ self._auth: Optional[Auth] = None
+ self._auth_in_progress = False
+ self._auth_complete = False
++ self._auth_final = False
+ self._auth_methods = [b'none']
+ self._auth_was_trivial = True
+ self._username = ''
+@@ -1485,20 +1488,30 @@ class SSHConnection(SSHPacketHandler,
asyncio.Protocol):
+ skip_reason = ''
+ exc_reason = ''
+
+- if self._kex and MSG_KEX_FIRST <= pkttype <= MSG_KEX_LAST:
+- if self._ignore_first_kex: # pragma: no cover
+- skip_reason = 'ignored first kex'
+- self._ignore_first_kex = False
++ if MSG_KEX_FIRST <= pkttype <= MSG_KEX_LAST:
++ if self._kex:
++ if self._ignore_first_kex: # pragma: no cover
++ skip_reason = 'ignored first kex'
++ self._ignore_first_kex = False
++ else:
++ handler = self._kex
+ else:
+- handler = self._kex
++ skip_reason = 'kex not in progress'
++ exc_reason = 'Key exchange not in progress'
+ elif self._strict_kex and not self._recv_encryption and \
+ MSG_IGNORE <= pkttype <= MSG_DEBUG:
+ skip_reason = 'strict kex violation'
+ exc_reason = 'Strict key exchange violation: ' \
+ 'unexpected packet type %d received' % pkttype
+- elif (self._auth and
+- MSG_USERAUTH_FIRST <= pkttype <= MSG_USERAUTH_LAST):
+- handler = self._auth
++ elif MSG_USERAUTH_FIRST <= pkttype <= MSG_USERAUTH_LAST:
++ if self._auth:
++ handler = self._auth
++ else:
++ skip_reason = 'auth not in progress'
++ exc_reason = 'Authentication not in progress'
++ elif pkttype > MSG_KEX_LAST and not self._recv_encryption:
++ skip_reason = 'invalid request before kex complete'
++ exc_reason = 'Invalid request before key exchange was complete'
+ elif pkttype > MSG_USERAUTH_LAST and not self._auth_complete:
+ skip_reason = 'invalid request before auth complete'
+ exc_reason = 'Invalid request before authentication was complete'
+@@ -1536,6 +1549,9 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
+ if exc_reason:
+ raise ProtocolError(exc_reason)
+
++ if pkttype > MSG_USERAUTH_LAST:
++ self._auth_final = True
++
+ if self._transport:
+ self._recv_handler = self._recv_pkthdr
+ if self._recv_seq == 0xffffffff and not self._recv_encryption:
+@@ -1559,9 +1575,7 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
+ self._send_kexinit()
+ self._kexinit_sent = True
+
+- if (((pkttype in {MSG_SERVICE_REQUEST, MSG_SERVICE_ACCEPT} or
+- pkttype > MSG_KEX_LAST) and not self._kex_complete) or
+- (pkttype == MSG_USERAUTH_BANNER and
++ if ((pkttype == MSG_USERAUTH_BANNER and
+ not (self._auth_in_progress or self._auth_complete)) or
+ (pkttype > MSG_USERAUTH_LAST and not self._auth_complete)):
+ self._deferred_packets.append((pkttype, args))
+@@ -1781,9 +1795,11 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
+ not self._waiter.cancelled():
+ self._waiter.set_result(None)
+ self._wait = None
+- else:
+- self.send_service_request(_USERAUTH_SERVICE)
++ return
+ else:
++ self._extensions_to_send[b'server-sig-algs'] = \
++ b','.join(self._sig_algs)
++
+ self._send_encryption = next_enc_sc
+ self._send_enchdrlen = 1 if etm_sc else 5
+ self._send_blocksize = max(8, enc_blocksize_sc)
+@@ -1804,17 +1820,18 @@ class SSHConnection(SSHPacketHandler,
asyncio.Protocol):
+ recv_mac=self._mac_alg_cs.decode('ascii'),
+ recv_compression=self._cmp_alg_cs.decode('ascii'))
+
+- if first_kex:
+- self._next_service = _USERAUTH_SERVICE
+-
+- self._extensions_to_send[b'server-sig-algs'] = \
+- b','.join(self._sig_algs)
+-
+ if self._can_send_ext_info:
+ self._send_ext_info()
+ self._can_send_ext_info = False
+
+ self._kex_complete = True
++
++ if first_kex:
++ if self.is_client():
++ self.send_service_request(_USERAUTH_SERVICE)
++ else:
++ self._next_service = _USERAUTH_SERVICE
++
+ self._send_deferred_packets()
+
+ def send_service_request(self, service: bytes) -> None:
+@@ -2050,18 +2067,25 @@ class SSHConnection(SSHPacketHandler,
asyncio.Protocol):
+ service = packet.get_string()
+ packet.check_end()
+
+- if service == self._next_service:
+- self.logger.debug2('Accepting request for service %s', service)
++ if self.is_client():
++ raise ProtocolError('Unexpected service request received')
+
+- self.send_packet(MSG_SERVICE_ACCEPT, String(service))
++ if not self._recv_encryption:
++ raise ProtocolError('Service request received before kex
complete')
+
+- if (self.is_server() and # pragma: no branch
+- not self._auth_in_progress and
+- service == _USERAUTH_SERVICE):
+- self._auth_in_progress = True
+- self._send_deferred_packets()
+- else:
+- raise ServiceNotAvailable('Unexpected service request received')
++ if service != self._next_service:
++ raise ServiceNotAvailable('Unexpected service in service request')
++
++ self.logger.debug2('Accepting request for service %s', service)
++
++ self.send_packet(MSG_SERVICE_ACCEPT, String(service))
++
++ self._next_service = None
++
++ if service == _USERAUTH_SERVICE: # pragma: no branch
++ self._auth_in_progress = True
++ self._can_recv_ext_info = False
++ self._send_deferred_packets()
+
+ def _process_service_accept(self, _pkttype: int, _pktid: int,
+ packet: SSHPacket) -> None:
+@@ -2070,27 +2094,35 @@ class SSHConnection(SSHPacketHandler,
asyncio.Protocol):
+ service = packet.get_string()
+ packet.check_end()
+
+- if service == self._next_service:
+- self.logger.debug2('Request for service %s accepted', service)
++ if self.is_server():
++ raise ProtocolError('Unexpected service accept received')
+
+- self._next_service = None
++ if not self._recv_encryption:
++ raise ProtocolError('Service accept received before kex complete')
+
+- if (self.is_client() and # pragma: no branch
+- service == _USERAUTH_SERVICE):
+- self.logger.info('Beginning auth for user %s', self._username)
++ if service != self._next_service:
++ raise ServiceNotAvailable('Unexpected service in service accept')
+
+- self._auth_in_progress = True
++ self.logger.debug2('Request for service %s accepted', service)
+
+- # This method is only in SSHClientConnection
+- # pylint: disable=no-member
+- cast('SSHClientConnection', self).try_next_auth()
+- else:
+- raise ServiceNotAvailable('Unexpected service accept received')
++ self._next_service = None
++
++ if service == _USERAUTH_SERVICE: # pragma: no branch
++ self.logger.info('Beginning auth for user %s', self._username)
++
++ self._auth_in_progress = True
++
++ # This method is only in SSHClientConnection
++ # pylint: disable=no-member
++ cast('SSHClientConnection', self).try_next_auth()
+
+ def _process_ext_info(self, _pkttype: int, _pktid: int,
+ packet: SSHPacket) -> None:
+ """Process extension information"""
+
++ if not self._can_recv_ext_info:
++ raise ProtocolError('Unexpected ext_info received')
++
+ extensions: Dict[bytes, bytes] = {}
+
+ self.logger.debug2('Received extension info')
+@@ -2229,6 +2261,7 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
+ self._decompress_after_auth = self._next_decompress_after_auth
+
+ self._next_recv_encryption = None
++ self._can_recv_ext_info = True
+ else:
+ raise ProtocolError('New keys not negotiated')
+
+@@ -2256,8 +2289,10 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
+ if self.is_client():
+ raise ProtocolError('Unexpected userauth request')
+ elif self._auth_complete:
+- # Silently ignore requests if we're already authenticated
+- pass
++ # Silently ignore additional auth requests after auth succeeds,
++ # until the client sends a non-auth message
++ if self._auth_final:
++ raise ProtocolError('Unexpected userauth request')
+ else:
+ if username != self._username:
+ self.logger.info('Beginning auth for user %s', username)
+@@ -2299,7 +2334,7 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
+ self._auth = lookup_server_auth(cast(SSHServerConnection, self),
+ self._username, method, packet)
+
+- def _process_userauth_failure(self, _pkttype: int, pktid: int,
++ def _process_userauth_failure(self, _pkttype: int, _pktid: int,
+ packet: SSHPacket) -> None:
+ """Process a user authentication failure response"""
+
+@@ -2339,10 +2374,9 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
+ # pylint: disable=no-member
+ cast(SSHClientConnection, self).try_next_auth()
+ else:
+- self.logger.debug2('Unexpected userauth failure response')
+- self.send_packet(MSG_UNIMPLEMENTED, UInt32(pktid))
++ raise ProtocolError('Unexpected userauth failure response')
+
+- def _process_userauth_success(self, _pkttype: int, pktid: int,
++ def _process_userauth_success(self, _pkttype: int, _pktid: int,
+ packet: SSHPacket) -> None:
+ """Process a user authentication success response"""
+
+@@ -2368,6 +2402,7 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
+ self._auth = None
+ self._auth_in_progress = False
+ self._auth_complete = True
++ self._can_recv_ext_info = False
+
+ if self._agent:
+ self._agent.close()
+@@ -2395,8 +2430,7 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
+ self._waiter.set_result(None)
+ self._wait = None
+ else:
+- self.logger.debug2('Unexpected userauth success response')
+- self.send_packet(MSG_UNIMPLEMENTED, UInt32(pktid))
++ raise ProtocolError('Unexpected userauth success response')
+
+ def _process_userauth_banner(self, _pkttype: int, _pktid: int,
+ packet: SSHPacket) -> None:
+diff --git a/tests/test_connection.py b/tests/test_connection.py
+index 4b85e9c..e75ba10 100644
+--- a/tests/test_connection.py
++++ b/tests/test_connection.py
+@@ -31,12 +31,13 @@ import unittest
+ from unittest.mock import patch
+
+ import asyncssh
+-from asyncssh.constants import MSG_UNIMPLEMENTED, MSG_DEBUG, MSG_IGNORE
++from asyncssh.constants import MSG_DEBUG, MSG_IGNORE
+ from asyncssh.constants import MSG_SERVICE_REQUEST, MSG_SERVICE_ACCEPT
+-from asyncssh.constants import MSG_KEXINIT, MSG_NEWKEYS
++from asyncssh.constants import MSG_KEXINIT, MSG_NEWKEYS, MSG_KEX_FIRST
+ from asyncssh.constants import MSG_KEX_FIRST, MSG_KEX_LAST
+ from asyncssh.constants import MSG_USERAUTH_REQUEST, MSG_USERAUTH_SUCCESS
+ from asyncssh.constants import MSG_USERAUTH_FAILURE, MSG_USERAUTH_BANNER
++from asyncssh.constants import MSG_USERAUTH_FIRST
+ from asyncssh.constants import MSG_GLOBAL_REQUEST
+ from asyncssh.constants import MSG_CHANNEL_OPEN, MSG_CHANNEL_OPEN_CONFIRMATION
+ from asyncssh.constants import MSG_CHANNEL_OPEN_FAILURE, MSG_CHANNEL_DATA
+@@ -341,14 +342,6 @@ class _VersionReportingServer(Server):
+ return False
+
+
+-def disconnect_on_unimplemented(self, pkttype, pktid, packet):
+- """Process an unimplemented message response"""
+-
+- # pylint: disable=unused-argument
+-
+- self.disconnect(asyncssh.DISC_BY_APPLICATION, 'Unexpected response')
+-
+-
+ @patch_gss
+ @patch('asyncssh.connection.SSHClientConnection', _CheckAlgsClientConnection)
+ class _TestConnection(ServerTestCase):
+@@ -923,8 +916,8 @@ class _TestConnection(ServerTestCase):
+
+ with patch('asyncssh.connection.SSHClientConnection.send_newkeys',
+ send_newkeys):
+- async with self.connect():
+- pass
++ with self.assertRaises(asyncssh.ProtocolError):
++ await self.connect()
+
+ @asynctest
+ async def test_message_before_kexinit_strict_kex(self):
+@@ -1098,21 +1091,85 @@ class _TestConnection(ServerTestCase):
+ await conn.wait_closed()
+
+ @asynctest
+- async def test_invalid_service_request(self):
+- """Test invalid service request"""
++ async def test_service_request_before_kex_complete(self):
++ """Test service request before kex is complete"""
++
++ def send_newkeys(self, k, h):
++ """Finish a key exchange and send a new keys message"""
++
++ self.send_packet(MSG_SERVICE_REQUEST, String('ssh-userauth'))
++
++ asyncssh.connection.SSHConnection.send_newkeys(self, k, h)
++
++ with patch('asyncssh.connection.SSHClientConnection.send_newkeys',
++ send_newkeys):
++ with self.assertRaises(asyncssh.ProtocolError):
++ await self.connect()
++
++ @asynctest
++ async def test_service_accept_before_kex_complete(self):
++ """Test service accept before kex is complete"""
++
++ def send_newkeys(self, k, h):
++ """Finish a key exchange and send a new keys message"""
++
++ self.send_packet(MSG_SERVICE_ACCEPT, String('ssh-userauth'))
++
++ asyncssh.connection.SSHConnection.send_newkeys(self, k, h)
++
++ with patch('asyncssh.connection.SSHServerConnection.send_newkeys',
++ send_newkeys):
++ with self.assertRaises(asyncssh.ProtocolError):
++ await self.connect()
++
++ @asynctest
++ async def test_unexpected_service_name_in_request(self):
++ """Test unexpected service name in service request"""
+
+ conn = await self.connect()
+ conn.send_packet(MSG_SERVICE_REQUEST, String('xxx'))
+ await conn.wait_closed()
+
+ @asynctest
+- async def test_invalid_service_accept(self):
+- """Test invalid service accept"""
++ async def test_unexpected_service_name_in_accept(self):
++ """Test unexpected service name in accept sent by server"""
++
++ def send_newkeys(self, k, h):
++ """Finish a key exchange and send a new keys message"""
++
++ asyncssh.connection.SSHConnection.send_newkeys(self, k, h)
++
++ self.send_packet(MSG_SERVICE_ACCEPT, String('xxx'))
++
++ with patch('asyncssh.connection.SSHServerConnection.send_newkeys',
++ send_newkeys):
++ with self.assertRaises(asyncssh.ServiceNotAvailable):
++ await self.connect()
++
++ @asynctest
++ async def test_service_accept_from_client(self):
++ """Test service accept sent by client"""
+
+ conn = await self.connect()
+- conn.send_packet(MSG_SERVICE_ACCEPT, String('xxx'))
++ conn.send_packet(MSG_SERVICE_ACCEPT, String('ssh-userauth'))
+ await conn.wait_closed()
+
++ @asynctest
++ async def test_service_request_from_server(self):
++ """Test service request sent by server"""
++
++ def send_newkeys(self, k, h):
++ """Finish a key exchange and send a new keys message"""
++
++ asyncssh.connection.SSHConnection.send_newkeys(self, k, h)
++
++ self.send_packet(MSG_SERVICE_REQUEST, String('ssh-userauth'))
++
++ with patch('asyncssh.connection.SSHServerConnection.send_newkeys',
++ send_newkeys):
++ with self.assertRaises(asyncssh.ProtocolError):
++ await self.connect()
++
+ @asynctest
+ async def test_packet_decode_error(self):
+ """Test SSH packet decode error"""
+@@ -1319,6 +1376,39 @@ class _TestConnection(ServerTestCase):
+ conn.send_packet(MSG_NEWKEYS)
+ await conn.wait_closed()
+
++ @asynctest
++ async def test_kex_after_kex_complete(self):
++ """Test kex request when kex not in progress"""
++
++ conn = await self.connect()
++ conn.send_packet(MSG_KEX_FIRST)
++ await conn.wait_closed()
++
++ @asynctest
++ async def test_userauth_after_auth_complete(self):
++ """Test userauth request when auth not in progress"""
++
++ conn = await self.connect()
++ conn.send_packet(MSG_USERAUTH_FIRST)
++ await conn.wait_closed()
++
++ @asynctest
++ async def test_userauth_before_kex_complete(self):
++ """Test receiving userauth before kex is complete"""
++
++ def send_newkeys(self, k, h):
++ """Finish a key exchange and send a new keys message"""
++
++ self.send_packet(MSG_USERAUTH_REQUEST, String('guest'),
++ String('ssh-connection'), String('none'))
++
++ asyncssh.connection.SSHConnection.send_newkeys(self, k, h)
++
++ with patch('asyncssh.connection.SSHClientConnection.send_newkeys',
++ send_newkeys):
++ with self.assertRaises(asyncssh.ProtocolError):
++ await self.connect()
++
+ @asynctest
+ async def test_invalid_userauth_service(self):
+ """Test invalid service in userauth request"""
+@@ -1368,25 +1458,32 @@ class _TestConnection(ServerTestCase):
+ String('ssh-connection'), String('none'))
+ await asyncio.sleep(0.1)
+
++ @asynctest
++ async def test_late_userauth_request(self):
++ """Test userauth request after auth is final"""
++
++ async with self.connect() as conn:
++ conn.send_packet(MSG_GLOBAL_REQUEST, String('xxx'),
++ Boolean(False))
++ conn.send_packet(MSG_USERAUTH_REQUEST, String('guest'),
++ String('ssh-connection'), String('none'))
++ await conn.wait_closed()
++
+ @asynctest
+ async def test_unexpected_userauth_success(self):
+ """Test unexpected userauth success response"""
+
+- with patch.dict('asyncssh.connection.SSHConnection._packet_handlers',
+- {MSG_UNIMPLEMENTED: disconnect_on_unimplemented}):
+- conn = await self.connect()
+- conn.send_packet(MSG_USERAUTH_SUCCESS)
+- await conn.wait_closed()
++ conn = await self.connect()
++ conn.send_packet(MSG_USERAUTH_SUCCESS)
++ await conn.wait_closed()
+
+ @asynctest
+ async def test_unexpected_userauth_failure(self):
+ """Test unexpected userauth failure response"""
+
+- with patch.dict('asyncssh.connection.SSHConnection._packet_handlers',
+- {MSG_UNIMPLEMENTED: disconnect_on_unimplemented}):
+- conn = await self.connect()
+- conn.send_packet(MSG_USERAUTH_FAILURE, NameList([]),
Boolean(False))
+- await conn.wait_closed()
++ conn = await self.connect()
++ conn.send_packet(MSG_USERAUTH_FAILURE, NameList([]), Boolean(False))
++ await conn.wait_closed()
+
+ @asynctest
+ async def test_unexpected_userauth_banner(self):
diff -Nru python-asyncssh-2.10.1/debian/patches/series
python-asyncssh-2.10.1/debian/patches/series
--- python-asyncssh-2.10.1/debian/patches/series 2024-04-16
16:14:32.000000000 +0200
+++ python-asyncssh-2.10.1/debian/patches/series 2024-12-31
14:12:00.000000000 +0100
@@ -4,3 +4,4 @@
0004-Handle-ConnectionRefusedError-when-connecting-to-223.patch
mock-pathlib-expanduser.patch
CVE-2023-48795.patch
+CVE-2023-46445-and-CVE-2023-46446.patch
--- End Message ---