Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-asyncssh for openSUSE:Factory
checked in at 2023-12-18 22:57:21
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-asyncssh (Old)
and /work/SRC/openSUSE:Factory/.python-asyncssh.new.9037 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-asyncssh"
Mon Dec 18 22:57:21 2023 rev:26 rq:1133889 version:2.14.2
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-asyncssh/python-asyncssh.changes
2023-11-13 22:20:41.635111844 +0100
+++
/work/SRC/openSUSE:Factory/.python-asyncssh.new.9037/python-asyncssh.changes
2023-12-18 22:57:30.619055507 +0100
@@ -1,0 +2,14 @@
+Mon Dec 18 15:55:18 UTC 2023 - Dirk Müller <[email protected]>
+
+- update to 2.14.2 (bsc#1218165, CVE-2023-48795):
+ * Implemented "strict kex" support and other countermeasures to
+ * protect against the Terrapin Attack described in
+ CVE-2023-48795
+ * Fixed config parser to properly an optional equals delimiter
+ in all config arguments.
+ * Fixed TCP send error handling to avoid race condition when
+ receiving incoming disconnect message.
+ * Improved type signature in SSHConnection async context
+ manager.
+
+-------------------------------------------------------------------
Old:
----
asyncssh-2.14.1.tar.gz
New:
----
asyncssh-2.14.2.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-asyncssh.spec ++++++
--- /var/tmp/diff_new_pack.Ol77YX/_old 2023-12-18 22:57:31.707095353 +0100
+++ /var/tmp/diff_new_pack.Ol77YX/_new 2023-12-18 22:57:31.711095500 +0100
@@ -16,10 +16,9 @@
#
-%define skip_python2 1
-%define skip_python36 1
+%{?sle15_python_module_pythons}
Name: python-asyncssh
-Version: 2.14.1
+Version: 2.14.2
Release: 0
Summary: Asynchronous SSHv2 client and server library
License: EPL-2.0 OR GPL-2.0-or-later
++++++ asyncssh-2.14.1.tar.gz -> asyncssh-2.14.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/asyncssh-2.14.1/.github/workflows/run_tests.yml
new/asyncssh-2.14.2/.github/workflows/run_tests.yml
--- old/asyncssh-2.14.1/.github/workflows/run_tests.yml 2023-11-09
03:28:14.000000000 +0100
+++ new/asyncssh-2.14.2/.github/workflows/run_tests.yml 2023-11-09
04:54:11.000000000 +0100
@@ -67,7 +67,9 @@
- name: Install Linux dependencies
if: ${{ runner.os == 'Linux' }}
- run: sudo apt install -y --no-install-recommends libnettle8
libsodium-dev libssl-dev libkrb5-dev ssh cmake ninja-build
+ run: |
+ sudo apt update
+ sudo apt install -y --no-install-recommends libnettle8 libsodium-dev
libssl-dev libkrb5-dev ssh cmake ninja-build
- name: Install macOS dependencies
if: ${{ runner.os == 'macOS' }}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/asyncssh-2.14.1/PKG-INFO new/asyncssh-2.14.2/PKG-INFO
--- old/asyncssh-2.14.1/PKG-INFO 2023-11-09 04:02:21.000000000 +0100
+++ new/asyncssh-2.14.2/PKG-INFO 2023-12-18 16:49:17.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: asyncssh
-Version: 2.14.1
+Version: 2.14.2
Summary: AsyncSSH: Asynchronous SSHv2 client and server library
Home-page: http://asyncssh.timeheart.net
Author: Ron Frederick
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/asyncssh-2.14.1/asyncssh/config.py
new/asyncssh-2.14.2/asyncssh/config.py
--- old/asyncssh-2.14.1/asyncssh/config.py 2022-12-27 22:30:36.000000000
+0100
+++ new/asyncssh-2.14.2/asyncssh/config.py 2023-12-18 16:45:25.000000000
+0100
@@ -315,22 +315,26 @@
continue
try:
- args = shlex.split(line)
+ split_args = shlex.split(line)
except ValueError as exc:
self._error(str(exc))
- option = args.pop(0)
+ args = []
- if option.endswith('='):
- option = option[:-1]
- elif '=' in option:
- option, arg = option.split('=', 1)
- args[:0] =[arg]
- elif args and args[0] == '=':
- del args[0]
- elif args and args[0].startswith('='):
- args[0] = args[0][1:]
+ for arg in split_args:
+ if arg.startswith('='):
+ if len(arg) > 1:
+ args.append(arg[1:])
+ elif arg.endswith('='):
+ args.append(arg[:-1])
+ elif '=' in arg:
+ arg, val = arg.split('=', 1)
+ args.append(arg)
+ args.append(val)
+ else:
+ args.append(arg)
+ option = args.pop(0)
loption = option.lower()
if loption in self._no_split:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/asyncssh-2.14.1/asyncssh/connection.py
new/asyncssh-2.14.2/asyncssh/connection.py
--- old/asyncssh-2.14.1/asyncssh/connection.py 2023-11-09 03:28:14.000000000
+0100
+++ new/asyncssh-2.14.2/asyncssh/connection.py 2023-12-18 16:45:25.000000000
+0100
@@ -179,6 +179,7 @@
_ProtocolFactory = Union[_ClientFactory, _ServerFactory]
_Conn = TypeVar('_Conn', 'SSHClientConnection', 'SSHServerConnection')
+_ConnSelf = TypeVar('_ConnSelf', bound='SSHConnection')
class _TunnelProtocol(Protocol):
"""Base protocol for connections to tunnel SSH over"""
@@ -317,12 +318,6 @@
self._conn.connection_lost(exc)
- def is_closing(self) -> bool:
- """Return whether the transport is closing or not"""
-
- assert self._transport is not None
- return self._transport.is_closing()
-
def write(self, data: bytes) -> None:
"""Write data to this tunnel"""
@@ -866,6 +861,7 @@
self._kexinit_sent = False
self._kex_complete = False
self._ignore_first_kex = False
+ self._strict_kex = False
self._gss: Optional[GSSBase] = None
self._gss_kex = False
@@ -944,7 +940,7 @@
self._disable_trivial_auth = False
- async def __aenter__(self) -> 'SSHConnection':
+ async def __aenter__(self: _ConnSelf) -> _ConnSelf:
"""Allow SSHConnection to be used as an async context manager"""
return self
@@ -1403,19 +1399,22 @@
(alg_type, b','.join(local_algs).decode('ascii'),
b','.join(remote_algs).decode('ascii')))
- def _get_ext_info_kex_alg(self) -> List[bytes]:
- """Return the kex alg to add if any to request extension info"""
+ def _get_extra_kex_algs(self) -> List[bytes]:
+ """Return the extra kex algs to add"""
- return [b'ext-info-c' if self.is_client() else b'ext-info-s']
+ if self.is_client():
+ return [b'ext-info-c', b'[email protected]']
+ else:
+ return [b'ext-info-s', b'[email protected]']
def _send(self, data: bytes) -> None:
"""Send data to the SSH connection"""
if self._transport:
- if self._transport.is_closing():
- self._force_close(BrokenPipeError())
- else:
+ try:
self._transport.write(data)
+ except BrokenPipeError: # pragma: no cover
+ pass
def _send_version(self) -> None:
"""Start the SSH handshake"""
@@ -1551,6 +1550,11 @@
else:
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 MSG_USERAUTH_FIRST <= pkttype <= MSG_USERAUTH_LAST:
if self._auth:
handler = self._auth
@@ -1586,8 +1590,13 @@
raise ProtocolError(str(exc)) from None
if not processed:
- self.logger.debug1('Unknown packet type %d received', pkttype)
- self.send_packet(MSG_UNIMPLEMENTED, UInt32(seq))
+ if self._strict_kex and not self._recv_encryption:
+ exc_reason = 'Strict key exchange violation: ' \
+ 'unexpected packet type %d received' % pkttype
+ else:
+ self.logger.debug1('Unknown packet type %d received',
+ pkttype)
+ self.send_packet(MSG_UNIMPLEMENTED, UInt32(seq))
if exc_reason:
raise ProtocolError(exc_reason)
@@ -1596,9 +1605,16 @@
self._auth_final = True
if self._transport:
- self._recv_seq = (seq + 1) & 0xffffffff
self._recv_handler = self._recv_pkthdr
+ if self._recv_seq == 0xffffffff and not self._recv_encryption:
+ raise ProtocolError('Sequence rollover before kex complete')
+
+ if pkttype == MSG_NEWKEYS and self._strict_kex:
+ self._recv_seq = 0
+ else:
+ self._recv_seq = (seq + 1) & 0xffffffff
+
return True
def send_packet(self, pkttype: int, *args: bytes,
@@ -1650,7 +1666,15 @@
mac = b''
self._send(packet + mac)
- self._send_seq = (seq + 1) & 0xffffffff
+
+ if self._send_seq == 0xffffffff and not self._send_encryption:
+ self._send_seq = 0
+ raise ProtocolError('Sequence rollover before kex complete')
+
+ if pkttype == MSG_NEWKEYS and self._strict_kex:
+ self._send_seq = 0
+ else:
+ self._send_seq = (seq + 1) & 0xffffffff
if self._kex_complete:
self._rekey_bytes_sent += pktlen
@@ -1694,7 +1718,7 @@
kex_algs = expand_kex_algs(self._kex_algs, gss_mechs,
bool(self._server_host_key_algs)) + \
- self._get_ext_info_kex_alg()
+ self._get_extra_kex_algs()
host_key_algs = self._server_host_key_algs or [b'null']
@@ -2196,13 +2220,27 @@
if self.is_server():
self._client_kexinit = packet.get_consumed_payload()
- if b'ext-info-c' in peer_kex_algs and not self._session_id:
- self._can_send_ext_info = True
+ if not self._session_id:
+ if b'ext-info-c' in peer_kex_algs:
+ self._can_send_ext_info = True
+
+ if b'[email protected]' in peer_kex_algs:
+ self._strict_kex = True
else:
self._server_kexinit = packet.get_consumed_payload()
- if b'ext-info-s' in peer_kex_algs and not self._session_id:
- self._can_send_ext_info = True
+ if not self._session_id:
+ if b'ext-info-s' in peer_kex_algs:
+ self._can_send_ext_info = True
+
+ if b'[email protected]' in peer_kex_algs:
+ self._strict_kex = True
+
+ if self._strict_kex and not self._recv_encryption and \
+ self._recv_seq != 0:
+ raise ProtocolError('Strict key exchange violation: '
+ 'KEXINIT was not the first packet')
+
if self._kexinit_sent:
self._kexinit_sent = False
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/asyncssh-2.14.1/asyncssh/version.py
new/asyncssh-2.14.2/asyncssh/version.py
--- old/asyncssh-2.14.1/asyncssh/version.py 2023-11-09 03:30:33.000000000
+0100
+++ new/asyncssh-2.14.2/asyncssh/version.py 2023-12-18 16:45:49.000000000
+0100
@@ -26,4 +26,4 @@
__url__ = 'http://asyncssh.timeheart.net'
-__version__ = '2.14.1'
+__version__ = '2.14.2'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/asyncssh-2.14.1/asyncssh.egg-info/PKG-INFO
new/asyncssh-2.14.2/asyncssh.egg-info/PKG-INFO
--- old/asyncssh-2.14.1/asyncssh.egg-info/PKG-INFO 2023-11-09
04:02:21.000000000 +0100
+++ new/asyncssh-2.14.2/asyncssh.egg-info/PKG-INFO 2023-12-18
16:49:17.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: asyncssh
-Version: 2.14.1
+Version: 2.14.2
Summary: AsyncSSH: Asynchronous SSHv2 client and server library
Home-page: http://asyncssh.timeheart.net
Author: Ron Frederick
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/asyncssh-2.14.1/docs/changes.rst
new/asyncssh-2.14.2/docs/changes.rst
--- old/asyncssh-2.14.1/docs/changes.rst 2023-11-09 03:50:21.000000000
+0100
+++ new/asyncssh-2.14.2/docs/changes.rst 2023-12-18 16:45:57.000000000
+0100
@@ -3,13 +3,32 @@
Change Log
==========
+Release 2.14.2 (18 Dec 2023)
+----------------------------
+
+* Implemented "strict kex" support and other countermeasures to
+ protect against the Terrapin Attack described in `CVE-2023-48795
+ <https://github.com/advisories/GHSA-hfmc-7525-mj55>`. Thanks once
+ again go to Fabian Bäumer, Marcus Brinkmann, and Jörg Schwenk for
+ identifying and reporting this vulnerability and providing detailed
+ analysis and suggestions about proposed fixes.
+
+* Fixed config parser to properly an optional equals delimiter in all
+ config arguments. Thanks go to Fawaz Orabi for reporting this issue.
+
+* Fixed TCP send error handling to avoid race condition when receiving
+ incoming disconnect message.
+
+* Improved type signature in SSHConnection async context manager. Thanks
+ go to Pieter-Jan Briers for providing this.
+
Release 2.14.1 (8 Nov 2023)
---------------------------
* Hardened AsyncSSH state machine against potential message
injection attacks, described in more detail in `CVE-2023-46445
- <https://github.com/advisories/CVE-2023-46445>`_ and `CVE-2023-46446
- <https://github.com/advisories/CVE-2023-46446>`_. Thanks go to
+ <https://github.com/advisories/GHSA-cfc2-wr2v-gxm5>`_ and `CVE-2023-46446
+ <https://github.com/advisories/GHSA-c35q-ffpf-5qpm>`_. Thanks go to
Fabian Bäumer, Marcus Brinkmann, and Jörg Schwenk for identifying
and reporting these vulnerabilities and providing detailed analysis
and suggestions about the proposed fixes.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/asyncssh-2.14.1/tests/test_connection.py
new/asyncssh-2.14.2/tests/test_connection.py
--- old/asyncssh-2.14.1/tests/test_connection.py 2023-11-09
03:28:14.000000000 +0100
+++ new/asyncssh-2.14.2/tests/test_connection.py 2023-12-18
16:45:25.000000000 +0100
@@ -30,9 +30,10 @@
from unittest.mock import patch
import asyncssh
-from asyncssh.constants import MSG_DEBUG
+from asyncssh.constants import MSG_IGNORE, MSG_DEBUG
from asyncssh.constants import MSG_SERVICE_REQUEST, MSG_SERVICE_ACCEPT
-from asyncssh.constants import MSG_KEXINIT, MSG_NEWKEYS, MSG_KEX_FIRST
+from asyncssh.constants import MSG_KEXINIT, MSG_NEWKEYS
+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
@@ -43,6 +44,7 @@
from asyncssh.crypto.cipher import GCMCipher
from asyncssh.encryption import get_encryption_algs
from asyncssh.kex import get_kex_algs
+from asyncssh.kex_dh import MSG_KEX_ECDH_REPLY
from asyncssh.mac import _HMAC, _mac_handler, get_mac_algs
from asyncssh.packet import Boolean, NameList, String, UInt32
from asyncssh.public_key import get_default_public_key_algs
@@ -51,8 +53,8 @@
from .server import Server, ServerTestCase
-from .util import asynctest, gss_available, nc_available, patch_gss
-from .util import patch_getnameinfo, x509_available
+from .util import asynctest, patch_extra_kex, patch_getnameinfo, patch_gss
+from .util import gss_available, nc_available, x509_available
class _CheckAlgsClientConnection(asyncssh.SSHClientConnection):
@@ -931,22 +933,6 @@
await self.connect(kex_algs=['fail'])
@asynctest
- async def test_skip_ext_info(self):
- """Test not requesting extension info from the server"""
-
- def skip_ext_info(self):
- """Don't request extension information"""
-
- # pylint: disable=unused-argument
-
- return []
-
- with patch('asyncssh.connection.SSHConnection._get_ext_info_kex_alg',
- skip_ext_info):
- async with self.connect():
- pass
-
- @asynctest
async def test_unknown_ext_info(self):
"""Test receiving unknown extension information"""
@@ -971,6 +957,54 @@
await self.connect()
@asynctest
+ async def test_message_before_kexinit_strict_kex(self):
+ """Test receiving a message before KEXINIT with strict_kex enabled"""
+
+ def send_packet(self, pkttype, *args, **kwargs):
+ if pkttype == MSG_KEXINIT:
+ self.send_packet(MSG_IGNORE, String(b''))
+
+ asyncssh.connection.SSHConnection.send_packet(
+ self, pkttype, *args, **kwargs)
+
+ with patch('asyncssh.connection.SSHClientConnection.send_packet',
+ send_packet):
+ with self.assertRaises(asyncssh.ProtocolError):
+ await self.connect()
+
+ @asynctest
+ async def test_message_during_kex_strict_kex(self):
+ """Test receiving an unexpected message with strict_kex enabled"""
+
+ def send_packet(self, pkttype, *args, **kwargs):
+ if pkttype == MSG_KEX_ECDH_REPLY:
+ self.send_packet(MSG_IGNORE, String(b''))
+
+ asyncssh.connection.SSHConnection.send_packet(
+ self, pkttype, *args, **kwargs)
+
+ with patch('asyncssh.connection.SSHServerConnection.send_packet',
+ send_packet):
+ with self.assertRaises(asyncssh.ProtocolError):
+ await self.connect()
+
+ @asynctest
+ async def test_unknown_message_during_kex_strict_kex(self):
+ """Test receiving an unknown message with strict_kex enabled"""
+
+ def send_packet(self, pkttype, *args, **kwargs):
+ if pkttype == MSG_KEX_ECDH_REPLY:
+ self.send_packet(MSG_KEX_LAST)
+
+ asyncssh.connection.SSHConnection.send_packet(
+ self, pkttype, *args, **kwargs)
+
+ with patch('asyncssh.connection.SSHServerConnection.send_packet',
+ send_packet):
+ with self.assertRaises(asyncssh.ProtocolError):
+ await self.connect()
+
+ @asynctest
async def test_encryption_algs(self):
"""Test connecting with different encryption algorithms"""
@@ -1602,6 +1636,81 @@
await self.create_connection(_InternalErrorClient)
+@patch_extra_kex
+class _TestConnectionNoStrictKex(ServerTestCase):
+ """Unit tests for connection API with ext info and strict kex disabled"""
+
+ @classmethod
+ async def start_server(cls):
+ """Start an SSH server to connect to"""
+
+ return (await cls.create_server(_TunnelServer, gss_host=(),
+ compression_algs='*',
+ encryption_algs='*',
+ kex_algs='*', mac_algs='*'))
+
+ @asynctest
+ async def test_skip_ext_info(self):
+ """Test not requesting extension info from the server"""
+
+ async with self.connect():
+ pass
+
+ @asynctest
+ async def test_message_before_kexinit(self):
+ """Test receiving a message before KEXINIT"""
+
+ def send_packet(self, pkttype, *args, **kwargs):
+ if pkttype == MSG_KEXINIT:
+ self.send_packet(MSG_IGNORE, String(b''))
+
+ asyncssh.connection.SSHConnection.send_packet(
+ self, pkttype, *args, **kwargs)
+
+ with patch('asyncssh.connection.SSHClientConnection.send_packet',
+ send_packet):
+ async with self.connect():
+ pass
+
+ @asynctest
+ async def test_message_during_kex(self):
+ """Test receiving an unexpected message in key exchange"""
+
+ def send_packet(self, pkttype, *args, **kwargs):
+ if pkttype == MSG_KEX_ECDH_REPLY:
+ self.send_packet(MSG_IGNORE, String(b''))
+
+ asyncssh.connection.SSHConnection.send_packet(
+ self, pkttype, *args, **kwargs)
+
+ with patch('asyncssh.connection.SSHServerConnection.send_packet',
+ send_packet):
+ async with self.connect():
+ pass
+
+ @asynctest
+ async def test_sequence_wrap_during_kex(self):
+ """Test sequence wrap during initial key exchange"""
+
+ def send_packet(self, pkttype, *args, **kwargs):
+ if pkttype == MSG_KEXINIT:
+ if self._options.command == 'send':
+ self._send_seq = 0xfffffffe
+ else:
+ self._recv_seq = 0xfffffffe
+
+ asyncssh.connection.SSHConnection.send_packet(
+ self, pkttype, *args, **kwargs)
+
+ with patch('asyncssh.connection.SSHClientConnection.send_packet',
+ send_packet):
+ with self.assertRaises(asyncssh.ProtocolError):
+ await self.connect(command='send')
+
+ with self.assertRaises(asyncssh.ProtocolError):
+ await self.connect(command='recv')
+
+
class _TestConnectionListenSock(ServerTestCase):
"""Unit test for specifying a listen socket"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/asyncssh-2.14.1/tests/test_connection_auth.py
new/asyncssh-2.14.2/tests/test_connection_auth.py
--- old/asyncssh-2.14.1/tests/test_connection_auth.py 2023-10-22
04:09:07.000000000 +0200
+++ new/asyncssh-2.14.2/tests/test_connection_auth.py 2023-12-18
16:45:25.000000000 +0100
@@ -739,7 +739,7 @@
return []
- with patch('asyncssh.connection.SSHConnection._get_ext_info_kex_alg',
+ with patch('asyncssh.connection.SSHConnection._get_extra_kex_algs',
skip_ext_info):
try:
async with self.connect(username='user',
@@ -1245,7 +1245,7 @@
return []
- with patch('asyncssh.connection.SSHConnection._get_ext_info_kex_alg',
+ with patch('asyncssh.connection.SSHConnection._get_extra_kex_algs',
skip_ext_info):
try:
async with self.connect(username='ckey', client_keys='ckey',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/asyncssh-2.14.1/tests/util.py
new/asyncssh-2.14.2/tests/util.py
--- old/asyncssh-2.14.1/tests/util.py 2023-10-01 02:35:42.000000000 +0200
+++ new/asyncssh-2.14.2/tests/util.py 2023-12-18 16:45:25.000000000 +0100
@@ -106,6 +106,20 @@
return patch('socket.getnameinfo', getnameinfo)(cls)
+def patch_extra_kex(cls):
+ """Decorator for skipping extra kex algs"""
+
+ def skip_extra_kex_algs(self):
+ """Don't send extra key exchange algorithms"""
+
+ # pylint: disable=unused-argument
+
+ return []
+
+ return patch('asyncssh.connection.SSHConnection._get_extra_kex_algs',
+ skip_extra_kex_algs)(cls)
+
+
def patch_gss(cls):
"""Decorator for patching GSSAPI classes"""