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

Reply via email to