From 719fd4544cc48983e33377466340ea9bb684b678 Mon Sep 17 00:00:00 2001 From: tigro Date: Fri, 12 Jul 2024 04:50:47 +0300 Subject: [PATCH] import python-paramiko-2.12.0-2.el8 --- .gitignore | 1 + .python-paramiko.metadata | 1 + SOURCES/0003-remove-pytest-relaxed-dep.patch | 67 ++ SOURCES/0004-remove-mock-dep.patch | 117 +++ SOURCES/0005-classes__str__.patch | 62 ++ SOURCES/0006-terrapin.patch | 717 +++++++++++++++++ SPECS/python-paramiko.spec | 799 +++++++++++++++++++ 7 files changed, 1764 insertions(+) create mode 100644 .gitignore create mode 100644 .python-paramiko.metadata create mode 100644 SOURCES/0003-remove-pytest-relaxed-dep.patch create mode 100644 SOURCES/0004-remove-mock-dep.patch create mode 100644 SOURCES/0005-classes__str__.patch create mode 100644 SOURCES/0006-terrapin.patch create mode 100644 SPECS/python-paramiko.spec diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8febc1b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/paramiko-2.12.0.tar.gz diff --git a/.python-paramiko.metadata b/.python-paramiko.metadata new file mode 100644 index 0000000..1de3a16 --- /dev/null +++ b/.python-paramiko.metadata @@ -0,0 +1 @@ +c8f18401ebcfc7aee9132eaa71bf33c76bf9301e SOURCES/paramiko-2.12.0.tar.gz diff --git a/SOURCES/0003-remove-pytest-relaxed-dep.patch b/SOURCES/0003-remove-pytest-relaxed-dep.patch new file mode 100644 index 0000000..d8a6931 --- /dev/null +++ b/SOURCES/0003-remove-pytest-relaxed-dep.patch @@ -0,0 +1,67 @@ +--- a/dev-requirements.txt ++++ b/dev-requirements.txt +@@ -2,7 +2,6 @@ + invoke==1.6.0 + invocations==2.6.0 + pytest==4.4.2 +-pytest-relaxed==1.1.5 + # pytest-xdist for test dir watching and the inv guard task + pytest-xdist==1.28.0 + mock==2.0.0 +--- a/pytest.ini ++++ b/pytest.ini +@@ -1,7 +1,4 @@ + [pytest] +-# We use pytest-relaxed just for its utils at the moment, so disable it at the +-# plugin level until we adapt test organization to really use it. +-addopts = -p no:relaxed + # Loop on failure + looponfailroots = tests paramiko + # Ignore some warnings we cannot easily handle. +--- a/tests/test_client.py ++++ b/tests/test_client.py +@@ -34,7 +34,6 @@ import weakref + from tempfile import mkstemp + + import pytest +-from pytest_relaxed import raises + from mock import patch, Mock + + import paramiko +@@ -787,11 +786,11 @@ class PasswordPassphraseTests(ClientTest + + # TODO: more granular exception pending #387; should be signaling "no auth + # methods available" because no key and no password +- @raises(SSHException) + @requires_sha1_signing + def test_passphrase_kwarg_not_used_for_password_auth(self): +- # Using the "right" password in the "wrong" field shouldn't work. +- self._test_connection(passphrase="pygmalion") ++ with pytest.raises(SSHException): ++ # Using the "right" password in the "wrong" field shouldn't work. ++ self._test_connection(passphrase="pygmalion") + + @requires_sha1_signing + def test_passphrase_kwarg_used_for_key_passphrase(self): +@@ -811,15 +810,15 @@ class PasswordPassphraseTests(ClientTest + password="television", + ) + +- @raises(AuthenticationException) # TODO: more granular + @requires_sha1_signing + def test_password_kwarg_not_used_for_passphrase_when_passphrase_kwarg_given( # noqa + self + ): + # Sanity: if we're given both fields, the password field is NOT used as + # a passphrase. +- self._test_connection( +- key_filename=_support("test_rsa_password.key"), +- password="television", +- passphrase="wat? lol no", +- ) ++ with pytest.raises(AuthenticationException): ++ self._test_connection( ++ key_filename=_support("test_rsa_password.key"), ++ password="television", ++ passphrase="wat? lol no", ++ ) diff --git a/SOURCES/0004-remove-mock-dep.patch b/SOURCES/0004-remove-mock-dep.patch new file mode 100644 index 0000000..294fe97 --- /dev/null +++ b/SOURCES/0004-remove-mock-dep.patch @@ -0,0 +1,117 @@ +Prefer and use built-in unittest.mock in Python 3.3+ instead +of unnecessarily requiring the external mock package. This helps +distributions that are phasing out Python 2 to remove redundant +packages. + +--- a/dev-requirements.txt ++++ b/dev-requirements.txt +@@ -4,7 +4,7 @@ invocations==2.6.0 + pytest==4.4.2 + # pytest-xdist for test dir watching and the inv guard task + pytest-xdist==1.28.0 +-mock==2.0.0 ++mock==2.0.0;python_version<"3.3" + # Linting! + flake8==3.8.3 + # Formatting! +--- a/tests/test_channelfile.py ++++ b/tests/test_channelfile.py +@@ -1,4 +1,7 @@ +-from mock import patch, MagicMock ++try: ++ from unittest.mock import patch, MagicMock ++except ImportError: ++ from mock import patch, MagicMock + + from paramiko import Channel, ChannelFile, ChannelStderrFile, ChannelStdinFile + +--- a/tests/test_client.py ++++ b/tests/test_client.py +@@ -34,7 +34,10 @@ import weakref + from tempfile import mkstemp + + import pytest +-from mock import patch, Mock ++try: ++ from unittest.mock import patch, Mock ++except ImportError: ++ from mock import patch, Mock + + import paramiko + from paramiko import SSHClient +--- a/tests/test_config.py ++++ b/tests/test_config.py +@@ -11,7 +11,11 @@ try: + except ImportError: + Result = None + +-from mock import patch ++try: ++ from unittest.mock import patch ++except ImportError: ++ from mock import patch ++ + from pytest import raises, mark, fixture + + from paramiko import ( +--- a/tests/test_kex.py ++++ b/tests/test_kex.py +@@ -24,7 +24,11 @@ from binascii import hexlify, unhexlify + import os + import unittest + +-from mock import Mock, patch ++try: ++ from unittest.mock import Mock, patch ++except ImportError: ++ from mock import Mock, patch ++ + import pytest + + from cryptography.hazmat.backends import default_backend +--- a/tests/test_pkey.py ++++ b/tests/test_pkey.py +@@ -41,7 +41,12 @@ from paramiko.common import o600 + + from cryptography.exceptions import UnsupportedAlgorithm + from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateNumbers +-from mock import patch, Mock ++ ++try: ++ from unittest.mock import patch, Mock ++except ImportError: ++ from mock import patch, Mock ++ + import pytest + + from .util import _support, is_low_entropy, requires_sha1_signing +--- a/tests/test_proxy.py ++++ b/tests/test_proxy.py +@@ -1,7 +1,11 @@ + import signal + import socket + +-from mock import patch ++try: ++ from unittest.mock import patch ++except ImportError: ++ from mock import patch ++ + from pytest import raises + + from paramiko import ProxyCommand, ProxyCommandFailure +--- a/tests/test_transport.py ++++ b/tests/test_transport.py +@@ -30,7 +30,11 @@ import time + import threading + import random + import unittest +-from mock import Mock ++ ++try: ++ from unittest.mock import Mock ++except ImportError: ++ from mock import Mock + + from paramiko import ( + AuthHandler, diff --git a/SOURCES/0005-classes__str__.patch b/SOURCES/0005-classes__str__.patch new file mode 100644 index 0000000..af943e8 --- /dev/null +++ b/SOURCES/0005-classes__str__.patch @@ -0,0 +1,62 @@ +--- a/paramiko/agent.py ++++ b/paramiko/agent.py +@@ -415,9 +415,6 @@ class AgentKey(PKey): + def asbytes(self): + return self.blob + +- def __str__(self): +- return self.asbytes() +- + def get_name(self): + return self.name + +--- a/paramiko/message.py ++++ b/paramiko/message.py +@@ -53,10 +53,7 @@ class Message(object): + else: + self.packet = BytesIO() + +- def __str__(self): +- """ +- Return the byte stream content of this message, as a string/bytes obj. +- """ ++ def __bytes__(self): + return self.asbytes() + + def __repr__(self): +--- a/paramiko/pkey.py ++++ b/paramiko/pkey.py +@@ -118,7 +119,7 @@ class PKey(object): + """ + return bytes() + +- def __str__(self): ++ def __bytes__(self): + return self.asbytes() + + # noinspection PyUnresolvedReferences +--- a/tests/test_agent.py ++++ b/tests/test_agent.py +@@ -48,3 +48,10 @@ class AgentTests(unittest.TestCase): + kwargs=dict(algorithm="rsa-sha2-512"), + expectation=SSH_AGENT_RSA_SHA2_512, + ) ++ ++ def test_agent_key_str_kinda_fixed(self): ++ # Tests for a missed spot in Python 3 upgrades: AgentKey.__str__ was ++ # returning bytes, as if under Python 2. When bug present, this ++ # explodes with "__str__ returned non-string". ++ key = AgentKey(ChaosAgent(), b("secret!!!")) ++ assert str(key) == repr(key) +--- a/tests/test_message.py ++++ b/tests/test_message.py +@@ -105,3 +105,9 @@ class MessageTest(unittest.TestCase): + self.assertEqual(msg.get_adaptive_int(), 5) + self.assertEqual(msg.get_so_far(), self.__d[:4]) + self.assertEqual(msg.get_remainder(), self.__d[4:]) ++ ++ def test_bytes_str_and_repr(self): ++ msg = Message(self.__d) ++ assert str(msg) == f"paramiko.Message({self.__d!r})" ++ assert repr(msg) == str(msg) ++ assert bytes(msg) == msg.asbytes() == self.__d diff --git a/SOURCES/0006-terrapin.patch b/SOURCES/0006-terrapin.patch new file mode 100644 index 0000000..42eb3c0 --- /dev/null +++ b/SOURCES/0006-terrapin.patch @@ -0,0 +1,717 @@ +--- a/paramiko/__init__.py ++++ b/paramiko/__init__.py +@@ -19,12 +19,15 @@ + # flake8: noqa + import sys + from paramiko._version import __version__, __version_info__ +-from paramiko.transport import SecurityOptions, Transport ++from paramiko.transport import ( ++ SecurityOptions, ++ Transport, ++) + from paramiko.client import ( +- SSHClient, +- MissingHostKeyPolicy, + AutoAddPolicy, ++ MissingHostKeyPolicy, + RejectPolicy, ++ SSHClient, + WarningPolicy, + ) + from paramiko.auth_handler import AuthHandler +@@ -43,6 +46,7 @@ from paramiko.ssh_exception import ( + ConfigParseError, + CouldNotCanonicalize, + IncompatiblePeer, ++ MessageOrderError, + PasswordRequiredException, + ProxyCommandFailure, + SSHException, +--- a/paramiko/packet.py ++++ b/paramiko/packet.py +@@ -86,6 +86,7 @@ class Packetizer(object): + self.__need_rekey = False + self.__init_count = 0 + self.__remainder = bytes() ++ self._initial_kex_done = False + + # used for noticing when to re-key: + self.__sent_bytes = 0 +@@ -130,6 +131,12 @@ class Packetizer(object): + def closed(self): + return self.__closed + ++ def reset_seqno_out(self): ++ self.__sequence_number_out = 0 ++ ++ def reset_seqno_in(self): ++ self.__sequence_number_in = 0 ++ + def set_log(self, log): + """ + Set the Python log object to use for logging. +@@ -425,9 +432,12 @@ class Packetizer(object): + out += compute_hmac( + self.__mac_key_out, payload, self.__mac_engine_out + )[: self.__mac_size_out] +- self.__sequence_number_out = ( +- self.__sequence_number_out + 1 +- ) & xffffffff ++ next_seq = (self.__sequence_number_out + 1) & xffffffff ++ if next_seq == 0 and not self._initial_kex_done: ++ raise SSHException( ++ "Sequence number rolled over during initial kex!" ++ ) ++ self.__sequence_number_out = next_seq + self.write_all(out) + + self.__sent_bytes += len(out) +@@ -531,7 +541,12 @@ class Packetizer(object): + + msg = Message(payload[1:]) + msg.seqno = self.__sequence_number_in +- self.__sequence_number_in = (self.__sequence_number_in + 1) & xffffffff ++ next_seq = (self.__sequence_number_in + 1) & xffffffff ++ if next_seq == 0 and not self._initial_kex_done: ++ raise SSHException( ++ "Sequence number rolled over during initial kex!" ++ ) ++ self.__sequence_number_in = next_seq + + # check for rekey + raw_packet_size = packet_size + self.__mac_size_in + 4 +--- a/paramiko/ssh_exception.py ++++ b/paramiko/ssh_exception.py +@@ -235,3 +235,13 @@ class ConfigParseError(SSHException): + """ + + pass ++ ++ ++class MessageOrderError(SSHException): ++ """ ++ Out-of-order protocol messages were received, violating "strict kex" mode. ++ ++ .. versionadded:: 3.4 ++ """ ++ ++ pass +--- a/paramiko/transport.py ++++ b/paramiko/transport.py +@@ -106,11 +106,12 @@ from paramiko.ecdsakey import ECDSAKey + from paramiko.server import ServerInterface + from paramiko.sftp_client import SFTPClient + from paramiko.ssh_exception import ( +- SSHException, + BadAuthenticationType, + ChannelException, + IncompatiblePeer, ++ MessageOrderError, + ProxyCommandFailure, ++ SSHException, + ) + from paramiko.util import retry_on_signal, ClosingContextManager, clamp_value + +@@ -329,6 +330,8 @@ class Transport(threading.Thread, Closin + gss_deleg_creds=True, + disabled_algorithms=None, + server_sig_algs=True, ++ strict_kex=True, ++ packetizer_class=None, + ): + """ + Create a new SSH session over an existing socket, or socket-like +@@ -395,6 +398,13 @@ class Transport(threading.Thread, Closin + Whether to send an extra message to compatible clients, in server + mode, with a list of supported pubkey algorithms. Default: + ``True``. ++ :param bool strict_kex: ++ Whether to advertise (and implement, if client also advertises ++ support for) a "strict kex" mode for safer handshaking. Default: ++ ``True``. ++ :param packetizer_class: ++ Which class to use for instantiating the internal packet handler. ++ Default: ``None`` (i.e.: use `Packetizer` as normal). + + .. versionchanged:: 1.15 + Added the ``default_window_size`` and ``default_max_packet_size`` +@@ -405,10 +415,16 @@ class Transport(threading.Thread, Closin + Added the ``disabled_algorithms`` kwarg. + .. versionchanged:: 2.9 + Added the ``server_sig_algs`` kwarg. ++ .. versionchanged:: 3.4 ++ Added the ``strict_kex`` kwarg. ++ .. versionchanged:: 3.4 ++ Added the ``packetizer_class`` kwarg. + """ + self.active = False + self.hostname = None + self.server_extensions = {} ++ self.advertise_strict_kex = strict_kex ++ self.agreed_on_strict_kex = False + + if isinstance(sock, string_types): + # convert "host:port" into (host, port) +@@ -450,7 +466,7 @@ class Transport(threading.Thread, Closin + self.sock.settimeout(self._active_check_timeout) + + # negotiated crypto parameters +- self.packetizer = Packetizer(sock) ++ self.packetizer = (packetizer_class or Packetizer)(sock) + self.local_version = "SSH-" + self._PROTO_ID + "-" + self._CLIENT_ID + self.remote_version = "" + self.local_cipher = self.remote_cipher = "" +@@ -524,6 +540,20 @@ class Transport(threading.Thread, Closin + self.server_accept_cv = threading.Condition(self.lock) + self.subsystem_table = {} + ++ # Handler table, now set at init time for easier per-instance ++ # manipulation and subclass twiddling. ++ self._handler_table = { ++ MSG_EXT_INFO: self._parse_ext_info, ++ MSG_NEWKEYS: self._parse_newkeys, ++ MSG_GLOBAL_REQUEST: self._parse_global_request, ++ MSG_REQUEST_SUCCESS: self._parse_request_success, ++ MSG_REQUEST_FAILURE: self._parse_request_failure, ++ MSG_CHANNEL_OPEN_SUCCESS: self._parse_channel_open_success, ++ MSG_CHANNEL_OPEN_FAILURE: self._parse_channel_open_failure, ++ MSG_CHANNEL_OPEN: self._parse_channel_open, ++ MSG_KEXINIT: self._negotiate_keys, ++ } ++ + def _filter_algorithm(self, type_): + default = getattr(self, "_preferred_{}".format(type_)) + return tuple( +@@ -2067,6 +2097,20 @@ class Transport(threading.Thread, Closin + # be empty.) + return reply + ++ def _enforce_strict_kex(self, ptype): ++ """ ++ Conditionally raise `MessageOrderError` during strict initial kex. ++ ++ This method should only be called inside code that handles non-KEXINIT ++ messages; it does not interrogate ``ptype`` besides using it to log ++ more accurately. ++ """ ++ if self.agreed_on_strict_kex and not self.initial_kex_done: ++ name = MSG_NAMES.get(ptype, f"msg {ptype}") ++ raise MessageOrderError( ++ f"In strict-kex mode, but was sent {name!r}!" ++ ) ++ + def run(self): + # (use the exposed "run" method, because if we specify a thread target + # of a private method, threading.Thread will keep a reference to it +@@ -2111,16 +2155,21 @@ class Transport(threading.Thread, Closin + except NeedRekeyException: + continue + if ptype == MSG_IGNORE: ++ self._enforce_strict_kex(ptype) + continue + elif ptype == MSG_DISCONNECT: + self._parse_disconnect(m) + break + elif ptype == MSG_DEBUG: ++ self._enforce_strict_kex(ptype) + self._parse_debug(m) + 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 + ) +@@ -2135,7 +2184,7 @@ class Transport(threading.Thread, Closin + if error_msg: + self._send_message(error_msg) + else: +- self._handler_table[ptype](self, m) ++ self._handler_table[ptype](m) + elif ptype in self._channel_handler_table: + chanid = m.get_int() + chan = self._channels.get(chanid) +@@ -2342,12 +2391,18 @@ class Transport(threading.Thread, Closin + ) + else: + available_server_keys = self.preferred_keys +- # Signal support for MSG_EXT_INFO. ++ # Signal support for MSG_EXT_INFO so server will send it to us. + # NOTE: doing this here handily means we don't even consider this + # value when agreeing on real kex algo to use (which is a common + # pitfall when adding this apparently). + kex_algos.append("ext-info-c") + ++ # Similar to ext-info, but used in both server modes, so done outside ++ # of above if/else. ++ if self.advertise_strict_kex: ++ which = "s" if self.server_mode else "c" ++ kex_algos.append(f"kex-strict-{which}-v00@openssh.com") ++ + m = Message() + m.add_byte(cMSG_KEXINIT) + m.add_bytes(os.urandom(16)) +@@ -2388,7 +2443,8 @@ class Transport(threading.Thread, Closin + + def _get_latest_kex_init(self): + return self._really_parse_kex_init( +- Message(self._latest_kex_init), ignore_first_byte=True ++ Message(self._latest_kex_init), ++ ignore_first_byte=True, + ) + + def _parse_kex_init(self, m): +@@ -2427,10 +2483,39 @@ class Transport(threading.Thread, Closin + self._log(DEBUG, "kex follows: {}".format(kex_follows)) + self._log(DEBUG, "=== Key exchange agreements ===") + +- # Strip out ext-info "kex algo" ++ # Record, and strip out, ext-info and/or strict-kex non-algorithms + self._remote_ext_info = None +- if kex_algo_list[-1].startswith("ext-info-"): +- self._remote_ext_info = kex_algo_list.pop() ++ self._remote_strict_kex = None ++ to_pop = [] ++ for i, algo in enumerate(kex_algo_list): ++ if algo.startswith("ext-info-"): ++ self._remote_ext_info = algo ++ to_pop.insert(0, i) ++ elif algo.startswith("kex-strict-"): ++ # NOTE: this is what we are expecting from the /remote/ end. ++ which = "c" if self.server_mode else "s" ++ expected = f"kex-strict-{which}-v00@openssh.com" ++ # Set strict mode if agreed. ++ self.agreed_on_strict_kex = ( ++ algo == expected and self.advertise_strict_kex ++ ) ++ self._log( ++ DEBUG, f"Strict kex mode: {self.agreed_on_strict_kex}" ++ ) ++ to_pop.insert(0, i) ++ for i in to_pop: ++ kex_algo_list.pop(i) ++ ++ # CVE mitigation: expect zeroed-out seqno anytime we are performing kex ++ # init phase, if strict mode was negotiated. ++ if ( ++ self.agreed_on_strict_kex ++ and not self.initial_kex_done ++ and m.seqno != 0 ++ ): ++ raise MessageOrderError( ++ "In strict-kex mode, but KEXINIT was not the first packet!" ++ ) + + # as a server, we pick the first item in the client's list that we + # support. +@@ -2631,6 +2716,13 @@ class Transport(threading.Thread, Closin + ): + self._log(DEBUG, "Switching on inbound compression ...") + self.packetizer.set_inbound_compressor(compress_in()) ++ # Reset inbound sequence number if strict mode. ++ if self.agreed_on_strict_kex: ++ self._log( ++ DEBUG, ++ "Resetting inbound seqno after NEWKEYS due to strict mode", ++ ) ++ self.packetizer.reset_seqno_in() + + def _activate_outbound(self): + """switch on newly negotiated encryption parameters for +@@ -2638,6 +2730,13 @@ class Transport(threading.Thread, Closin + m = Message() + m.add_byte(cMSG_NEWKEYS) + self._send_message(m) ++ # Reset outbound sequence number if strict mode. ++ if self.agreed_on_strict_kex: ++ self._log( ++ DEBUG, ++ "Resetting outbound seqno after NEWKEYS due to strict mode", ++ ) ++ self.packetizer.reset_seqno_out() + block_size = self._cipher_info[self.local_cipher]["block-size"] + if self.server_mode: + IV_out = self._compute_key("B", block_size) +@@ -2728,7 +2827,9 @@ class Transport(threading.Thread, Closin + self.auth_handler = AuthHandler(self) + if not self.initial_kex_done: + # this was the first key exchange +- self.initial_kex_done = True ++ # (also signal to packetizer as it sometimes wants to know this ++ # status as well, eg when seqnos rollover) ++ self.initial_kex_done = self.packetizer._initial_kex_done = True + # send an event? + if self.completion_event is not None: + self.completion_event.set() +@@ -2982,18 +3083,6 @@ class Transport(threading.Thread, Closin + finally: + self.lock.release() + +- _handler_table = { +- MSG_EXT_INFO: _parse_ext_info, +- MSG_NEWKEYS: _parse_newkeys, +- MSG_GLOBAL_REQUEST: _parse_global_request, +- MSG_REQUEST_SUCCESS: _parse_request_success, +- MSG_REQUEST_FAILURE: _parse_request_failure, +- MSG_CHANNEL_OPEN_SUCCESS: _parse_channel_open_success, +- MSG_CHANNEL_OPEN_FAILURE: _parse_channel_open_failure, +- MSG_CHANNEL_OPEN: _parse_channel_open, +- MSG_KEXINIT: _negotiate_keys, +- } +- + _channel_handler_table = { + MSG_CHANNEL_SUCCESS: Channel._request_success, + MSG_CHANNEL_FAILURE: Channel._request_failed, +--- a/tests/test_transport.py ++++ b/tests/test_transport.py +@@ -23,12 +23,14 @@ Some unit tests for the ssh2 protocol in + from __future__ import with_statement + + from binascii import hexlify ++import itertools + from contextlib import contextmanager + import select + import socket + import time + import threading + import random ++import sys + import unittest + + try: +@@ -37,14 +39,15 @@ except ImportError: + from mock import Mock + + from paramiko import ( ++ AuthenticationException, + AuthHandler, + ChannelException, + DSSKey, ++ IncompatiblePeer, ++ MessageOrderError, + Packetizer, + RSAKey, + SSHException, +- AuthenticationException, +- IncompatiblePeer, + SecurityOptions, + ServerInterface, + Transport, +@@ -57,7 +60,11 @@ from paramiko.common import ( + MAX_WINDOW_SIZE, + MIN_PACKET_SIZE, + MIN_WINDOW_SIZE, ++ MSG_CHANNEL_OPEN, ++ MSG_DEBUG, ++ MSG_IGNORE, + MSG_KEXINIT, ++ MSG_UNIMPLEMENTED, + MSG_USERAUTH_SUCCESS, + cMSG_CHANNEL_WINDOW_ADJUST, + cMSG_UNIMPLEMENTED, +@@ -67,6 +74,7 @@ from paramiko.message import Message + + from .util import needs_builtin, _support, requires_sha1_signing, slow + from .loop import LoopSocket ++from pytest import mark, raises + + + LONG_BANNER = """\ +@@ -154,6 +162,10 @@ class NullServer(ServerInterface): + self._tcpip_dest = destination + return OPEN_SUCCEEDED + ++# Faux 'packet type' we do not implement and are unlikely ever to (but which is ++# technically "within spec" re RFC 4251 ++MSG_FUGGEDABOUTIT = 253 ++ + + class TransportTest(unittest.TestCase): + def setUp(self): +@@ -1119,6 +1131,16 @@ class TransportTest(unittest.TestCase): + # Real fix's behavior + self._expect_unimplemented() + ++ def test_can_override_packetizer_used(self): ++ class MyPacketizer(Packetizer): ++ pass ++ ++ # control case ++ assert Transport(sock=LoopSocket()).packetizer.__class__ is Packetizer ++ # overridden case ++ tweaked = Transport(sock=LoopSocket(), packetizer_class=MyPacketizer) ++ assert tweaked.packetizer.__class__ is MyPacketizer ++ + + class AlgorithmDisablingTests(unittest.TestCase): + def test_preferred_lists_default_to_private_attribute_contents(self): +@@ -1202,10 +1224,17 @@ def server( + connect=None, + pubkeys=None, + catch_error=False, ++ transport_factory=None, ++ server_transport_factory=None, ++ defer=False, ++ skip_verify=False, + ): + """ + SSH server contextmanager for testing. + ++ Yields a tuple of ``(tc, ts)`` (client- and server-side `Transport` ++ objects), or ``(tc, ts, err)`` when ``catch_error==True``. ++ + :param hostkey: + Host key to use for the server; if None, loads + ``test_rsa.key``. +@@ -1222,6 +1251,17 @@ def server( + :param catch_error: + Whether to capture connection errors & yield from contextmanager. + Necessary for connection_time exception testing. ++ :param transport_factory: ++ Like the same-named param in SSHClient: which Transport class to use. ++ :param server_transport_factory: ++ Like ``transport_factory``, but only impacts the server transport. ++ :param bool defer: ++ Whether to defer authentication during connecting. ++ ++ This is really just shorthand for ``connect={}`` which would do roughly ++ the same thing. Also: this implies skip_verify=True automatically! ++ :param bool skip_verify: ++ Whether NOT to do the default "make sure auth passed" check. + """ + if init is None: + init = {} +@@ -1230,12 +1270,21 @@ def server( + if client_init is None: + client_init = {} + if connect is None: +- connect = dict(username="slowdive", password="pygmalion") ++ # No auth at all please ++ if defer: ++ connect = dict() ++ # Default username based auth ++ else: ++ connect = dict(username="slowdive", password="pygmalion") + socks = LoopSocket() + sockc = LoopSocket() + sockc.link(socks) +- tc = Transport(sockc, **dict(init, **client_init)) +- ts = Transport(socks, **dict(init, **server_init)) ++ if transport_factory is None: ++ transport_factory = Transport ++ if server_transport_factory is None: ++ server_transport_factory = transport_factory ++ tc = transport_factory(sockc, **dict(init, **client_init)) ++ ts = server_transport_factory(socks, **dict(init, **server_init)) + + if hostkey is None: + hostkey = RSAKey.from_private_key_file(_support("test_rsa.key")) +@@ -1354,10 +1403,14 @@ class TestSHA2SignatureKeyExchange(unitt + + + class TestExtInfo(unittest.TestCase): +- def test_ext_info_handshake(self): ++ def test_ext_info_handshake_exposed_in_client_kexinit(self): + with server() as (tc, _): ++ # NOTE: this is latest KEXINIT /sent by us/ (Transport retains it) + kex = tc._get_latest_kex_init() +- assert kex["kex_algo_list"][-1] == "ext-info-c" ++ # flag in KexAlgorithms list ++ assert "ext-info-c" in kex["kex_algo_list"] ++ # data stored on Transport after hearing back from a compatible ++ # server (such as ourselves in server mode) + assert tc.server_extensions == { + "server-sig-algs": b"ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,rsa-sha2-512,rsa-sha2-256,ssh-rsa,ssh-dss" # noqa + } +@@ -1463,3 +1516,187 @@ class TestSHA2SignaturePubkeys(unittest. + ) as (tc, ts): + assert tc.is_authenticated() + assert tc._agreed_pubkey_algorithm == "rsa-sha2-256" ++ ++ ++class BadSeqPacketizer(Packetizer): ++ def read_message(self): ++ cmd, msg = super().read_message() ++ # Only mess w/ seqno if kexinit. ++ if cmd is MSG_KEXINIT: ++ # NOTE: this is /only/ the copy of the seqno which gets ++ # transmitted up from Packetizer; it's not modifying ++ # Packetizer's own internal seqno. For these tests, ++ # modifying the latter isn't required, and is also harder ++ # to do w/o triggering MAC mismatches. ++ msg.seqno = 17 # arbitrary nonzero int ++ return cmd, msg ++ ++ ++class TestStrictKex: ++ def test_kex_algos_includes_kex_strict_c(self): ++ with server() as (tc, _): ++ kex = tc._get_latest_kex_init() ++ assert "kex-strict-c-v00@openssh.com" in kex["kex_algo_list"] ++ ++ @mark.parametrize( ++ "server_active,client_active", ++ itertools.product([True, False], repeat=2), ++ ) ++ def test_mode_agreement(self, server_active, client_active): ++ with server( ++ server_init=dict(strict_kex=server_active), ++ client_init=dict(strict_kex=client_active), ++ ) as (tc, ts): ++ if server_active and client_active: ++ assert tc.agreed_on_strict_kex is True ++ assert ts.agreed_on_strict_kex is True ++ else: ++ assert tc.agreed_on_strict_kex is False ++ assert ts.agreed_on_strict_kex is False ++ ++ def test_mode_advertised_by_default(self): ++ # NOTE: no explicit strict_kex overrides... ++ with server() as (tc, ts): ++ assert all( ++ ( ++ tc.advertise_strict_kex, ++ tc.agreed_on_strict_kex, ++ ts.advertise_strict_kex, ++ ts.agreed_on_strict_kex, ++ ) ++ ) ++ ++ @mark.parametrize( ++ "ptype", ++ ( ++ # "normal" but definitely out-of-order message ++ MSG_CHANNEL_OPEN, ++ # Normally ignored, but not in this case ++ MSG_IGNORE, ++ # Normally triggers debug parsing, but not in this case ++ MSG_DEBUG, ++ # Normally ignored, but...you get the idea ++ MSG_UNIMPLEMENTED, ++ # Not real, so would normally trigger us /sending/ ++ # MSG_UNIMPLEMENTED, but... ++ MSG_FUGGEDABOUTIT, ++ ), ++ ) ++ def test_MessageOrderError_non_kex_messages_in_initial_kex(self, ptype): ++ class AttackTransport(Transport): ++ # Easiest apparent spot on server side which is: ++ # - late enough for both ends to have handshook on strict mode ++ # - early enough to be in the window of opportunity for Terrapin ++ # attack; essentially during actual kex, when the engine is ++ # waiting for things like MSG_KEXECDH_REPLY (for eg curve25519). ++ def _negotiate_keys(self, m): ++ self.clear_to_send_lock.acquire() ++ try: ++ self.clear_to_send.clear() ++ finally: ++ self.clear_to_send_lock.release() ++ if self.local_kex_init is None: ++ # remote side wants to renegotiate ++ self._send_kex_init() ++ self._parse_kex_init(m) ++ # Here, we would normally kick over to kex_engine, but instead ++ # we want the server to send the OOO message. ++ m = Message() ++ m.add_byte(byte_chr(ptype)) ++ # rest of packet unnecessary... ++ self._send_message(m) ++ ++ with raises(MessageOrderError): ++ with server(server_transport_factory=AttackTransport) as (tc, _): ++ pass # above should run and except during connect() ++ ++ 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! ++ ++ def test_error_not_raised_when_kexinit_not_seq_0_but_unstrict(self): ++ with server( ++ client_init=dict( ++ # Disable strict kex ++ strict_kex=False, ++ # Give our clientside a packetizer that sets all kexinit ++ # Message objects to have .seqno==17, which would trigger the ++ # new logic if we'd forgotten to wrap it in strict-kex check ++ packetizer_class=BadSeqPacketizer, ++ ), ++ ): ++ pass # kexinit happens at connect... ++ ++ def test_MessageOrderError_raised_when_kexinit_not_seq_0_and_strict(self): ++ with raises(MessageOrderError): ++ with server( ++ # Give our clientside a packetizer that sets all kexinit ++ # Message objects to have .seqno==17, which should trigger the ++ # new logic (given we are NOT disabling strict-mode) ++ client_init=dict(packetizer_class=BadSeqPacketizer), ++ ): ++ pass # kexinit happens at connect... ++ ++ def test_sequence_numbers_reset_on_newkeys_when_strict(self): ++ with server(defer=True) as (tc, ts): ++ # When in strict mode, these should all be zero or close to it ++ # (post-kexinit, pre-auth). ++ # Server->client will be 1 (EXT_INFO got sent after NEWKEYS) ++ assert tc.packetizer._Packetizer__sequence_number_in == 1 ++ assert ts.packetizer._Packetizer__sequence_number_out == 1 ++ # Client->server will be 0 ++ assert tc.packetizer._Packetizer__sequence_number_out == 0 ++ assert ts.packetizer._Packetizer__sequence_number_in == 0 ++ ++ def test_sequence_numbers_not_reset_on_newkeys_when_not_strict(self): ++ with server(defer=True, client_init=dict(strict_kex=False)) as ( ++ tc, ++ ts, ++ ): ++ # When not in strict mode, these will all be ~3-4 or so ++ # (post-kexinit, pre-auth). Not encoding exact values as it will ++ # change anytime we mess with the test harness... ++ assert tc.packetizer._Packetizer__sequence_number_in != 0 ++ assert tc.packetizer._Packetizer__sequence_number_out != 0 ++ assert ts.packetizer._Packetizer__sequence_number_in != 0 ++ assert ts.packetizer._Packetizer__sequence_number_out != 0 ++ ++ def test_sequence_number_rollover_detected(self): ++ class RolloverTransport(Transport): ++ def __init__(self, *args, **kwargs): ++ super().__init__(*args, **kwargs) ++ # Induce an about-to-rollover seqno, such that it rolls over ++ # during initial kex. ++ setattr( ++ self.packetizer, ++ "_Packetizer__sequence_number_in", ++ sys.maxsize, ++ ) ++ setattr( ++ self.packetizer, ++ "_Packetizer__sequence_number_out", ++ sys.maxsize, ++ ) ++ ++ with raises( ++ SSHException, ++ match=r"Sequence number rolled over during initial kex!", ++ ): ++ with server( ++ client_init=dict( ++ # Disable strict kex - this should happen always ++ strict_kex=False, ++ ), ++ # Transport which tickles its packetizer seqno's ++ transport_factory=RolloverTransport, ++ ): ++ pass # kexinit happens at connect... diff --git a/SPECS/python-paramiko.spec b/SPECS/python-paramiko.spec new file mode 100644 index 0000000..9d4ac7d --- /dev/null +++ b/SPECS/python-paramiko.spec @@ -0,0 +1,799 @@ +%global srcname paramiko + +Name: python-%{srcname} +Version: 2.12.0 +Release: 2%{?dist} +Summary: SSH2 protocol library for python + +# No version specified +License: LGPL-2.1-or-later +URL: https://github.com/paramiko/paramiko +Source0: %{url}/archive/%{version}/%{srcname}-%{version}.tar.gz + +# Remove pytest-relaxed, which depends on pytest4 +Patch3: 0003-remove-pytest-relaxed-dep.patch + +# Avoid use of deprecated python-mock by using unittest.mock instead +Patch4: 0004-remove-mock-dep.patch + +# A handful of lower-level classes (notably 'paramiko.message.Message' and +# 'paramiko.pkey.PKey') previously returned 'bytes' objects from their +# implementation of '__str__', even under Python 3, and there was never any +# '__bytes__' method; these issues have been fixed by renaming '__str__' to +# '__bytes__' and relying on Python's default "stringification returns the +# output of '__repr__'" behavior re: any real attempts to 'str()' such objects +# (backported from version 3.2) +Patch5: 0005-classes__str__.patch + +# Address CVE 2023-48795 (a.k.a. the "Terrapin Attack", a vulnerability found +# in the SSH protocol re: treatment of packet sequence numbers) +# (backported from version 3.4) +Patch6: 0006-terrapin.patch + +BuildArch: noarch + +%global paramiko_desc \ +Paramiko (a combination of the Esperanto words for "paranoid" and "friend") is\ +a module for python 2.3 or greater that implements the SSH2 protocol for secure\ +(encrypted and authenticated) connections to remote machines. Unlike SSL (aka\ +TLS), the SSH2 protocol does not require hierarchical certificates signed by a\ +powerful central authority. You may know SSH2 as the protocol that replaced\ +telnet and rsh for secure access to remote shells, but the protocol also\ +includes the ability to open arbitrary channels to remote services across an\ +encrypted tunnel (this is how sftp works, for example). + +%description +%{paramiko_desc} + +%package -n python%{python3_pkgversion}-%{srcname} +Summary: SSH2 protocol library for python +BuildRequires: python%{python3_pkgversion}-devel +BuildRequires: %{py3_dist bcrypt} >= 3.1.3 +BuildRequires: %{py3_dist cryptography} >= 2.5 +BuildRequires: %{py3_dist pyasn1} >= 0.1.7 +BuildRequires: %{py3_dist pynacl} >= 1.0.1 +BuildRequires: %{py3_dist pytest} +BuildRequires: %{py3_dist setuptools} +BuildRequires: %{py3_dist six} +Recommends: %{py3_dist pyasn1} >= 0.1.7 + +%description -n python%{python3_pkgversion}-%{srcname} +%{paramiko_desc} + +Python 3 version. + +%package doc +Summary: Docs and demo for SSH2 protocol library for python +BuildRequires: /usr/bin/sphinx-build +Requires: %{name} = %{version}-%{release} + +%description doc +%{paramiko_desc} + +This is the documentation and demos. + +%prep +%autosetup -p1 -n %{srcname}-%{version} + +chmod -c a-x demos/* +sed -i -e '/^#!/,1d' demos/* + +%build +%py3_build + +%install +%py3_install + +sphinx-build -b html sites/docs/ html/ +rm html/.buildinfo + +%check +PYTHONPATH=%{buildroot}%{python3_sitelib} pytest-%{python3_version} + +%files -n python%{python3_pkgversion}-%{srcname} +%license LICENSE +%doc NEWS README.rst +%{python3_sitelib}/%{srcname}-*.egg-info/ +%{python3_sitelib}/%{srcname}/ + +%files doc +%doc html/ demos/ + +%changelog +* Fri Jul 12 2024 Arkady L. Shane - 2.12.0-2 +- Rebuilt for MSVSphere 8.10 + +* Fri Dec 29 2023 Paul Howarth - 2.12.0-2 +- Address CVE 2023-48795 (a.k.a. the "Terrapin Attack", a vulnerability found + in the SSH protocol re: treatment of packet sequence numbers) as follows: + - The vulnerability only impacts encrypt-then-MAC digest algorithms in tandem + with CBC ciphers, and ChaCha20-poly1305; of these, Paramiko currently only + implements ``hmac-sha2-(256|512)-etm`` in tandem with 'AES-CBC' + - As the fix for the vulnerability requires both ends of the connection to + cooperate, the below changes will only take effect when the remote end is + OpenSSH ≥ 9.6 (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 by the + server, unless you override this by specifying 'strict_kex=False' in + 'Transport.__init__' + - Paramiko will now raise an 'SSHException' subclass ('MessageOrderError') + when protocol messages are received in unexpected order; this includes + situations like receiving 'MSG_DEBUG' or 'MSG_IGNORE' during initial key + exchange, which are no longer allowed during strict mode + - 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, + which will usually result in, again, 'MessageOrderError') + - Sequence number rollover will now raise 'SSHException' if it occurs during + initial key exchange (regardless of strict mode status) +- Tweak 'ext-info-(c|s)' detection during KEXINIT protocol phase; the original + implementation made assumptions based on an OpenSSH implementation detail +- 'Transport' grew a new 'packetizer_class' kwarg for overriding the + packet-handler class used internally; this is mostly for testing, but advanced + users may find this useful when doing deep hacks +- A handful of lower-level classes (notably 'paramiko.message.Message' and + 'paramiko.pkey.PKey') previously returned 'bytes' objects from their + implementation of '__str__', even under Python 3, and there was never any + '__bytes__' method; these issues have been fixed by renaming '__str__' to + '__bytes__' and relying on Python's default "stringification returns the + output of '__repr__'" behavior re: any real attempts to 'str()' such objects + +* Sun Nov 6 2022 Paul Howarth - 2.12.0-1 +- Update to 2.12.0 (rhbz#2140281) + - Add a 'transport_factory' kwarg to 'SSHClient.connect' for advanced users + to gain more control over early Transport setup and manipulation (GH#2054, + GH#2125) + - Update '~paramiko.client.SSHClient' so it explicitly closes its wrapped + socket object upon encountering socket errors at connection time; this + should help somewhat with certain classes of memory leaks, resource + warnings, and/or errors (though we hasten to remind everyone that Client + and Transport have their own '.close()' methods for use in non-error + situations!) (GH#1822) + - Raise '~paramiko.ssh_exception.SSHException' explicitly when blank private + key data is loaded, instead of the natural result of 'IndexError'; this + should help more bits of Paramiko or Paramiko-adjacent codebases to + correctly handle this class of error (GH#1599, GH#1637) +- Use SPDX-format license tag + +* Fri Jul 22 2022 Fedora Release Engineering - 2.11.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + +* Tue Jun 14 2022 Python Maint - 2.11.0-2 +- Rebuilt for Python 3.11 + +* Tue May 17 2022 Paul Howarth - 2.11.0-1 +- Update to 2.11.0 + - Align signature verification algorithm with OpenSSH re: zero-padding + signatures that don't match their nominal size/length; this shouldn't + affect most users, but will help Paramiko-implemented SSH servers handle + poorly behaved clients such as PuTTY (GH#1933) + - OpenSSH 7.7 and older has a bug preventing it from understanding how to + perform SHA2 signature verification for RSA certificates (specifically + certs - not keys), so when we added SHA2 support it broke all clients using + RSA certificates with these servers; this has been fixed in a manner similar + to what OpenSSH's own client does - a version check is performed and the + algorithm used is downgraded if needed (GH#2017) + - Recent versions of Cryptography have deprecated Blowfish algorithm support; + in lieu of an easy method for users to remove it from the list of + algorithms Paramiko tries to import and use, we've decided to remove it + from our "preferred algorithms" list, which will both discourage use of a + weak algorithm, and avoid warnings (GH#2038, GH#2039) + - Windows-native SSH agent support as merged in 2.10 could encounter + 'Errno 22' 'OSError' exceptions in some scenarios (e.g. server not cleanly + closing a relevant named pipe); this has been worked around and should be + less problematic (GH#2008, GH#2010) + - Add SSH config token expansion (eg '%%h', '%%p') when parsing 'ProxyJump' + directives (GH#1951) + - Apply unittest 'skipIf' to tests currently using SHA1 in their critical + path, to avoid failures on systems starting to disable SHA1 outright in + their crypto backends (e.g. RHEL 9) (GH#2004, GH#2011) + +* Tue Apr 26 2022 Paul Howarth - 2.10.4-1 +- Update to 2.10.4 + - Update 'camelCase' method calls against the 'threading' module to be + 'snake_case'; this and related tweaks should fix some deprecation warnings + under Python 3.10 (GH#1838, GH#1870, GH#2028) + - '~paramiko.pkey.PKey' instances' '__eq__' did not have the usual safety + guard in place to ensure they were being compared to another 'PKey' object, + causing occasional spurious 'BadHostKeyException', among other things + (GH#1964, GH#2023, GH#2024) + - Servers offering certificate variants of hostkey algorithms (e.g. + 'ssh-rsa-cert-v01@openssh.com') could not have their host keys verified by + Paramiko clients, as it only ever considered non-cert key types for that + part of connection handshaking (GH#2035) + +* Mon Mar 21 2022 Paul Howarth - 2.10.3-2 +- Skip tests that would fail without SHA-1 signing support in backend, such as + on EL-9 (GH#2011) + +* Sat Mar 19 2022 Paul Howarth - 2.10.3-1 +- Update to 2.10.3 + - Certificate-based pubkey auth was inadvertently broken when adding SHA2 + support in version 2.9.0 (GH#1963, GH#1977) + - Switch from module-global to thread-local storage when recording thread IDs + for a logging helper; this should avoid one flavor of memory leak for + long-running processes (GH#2002, GH#2003) + +* Tue Mar 15 2022 Paul Howarth - 2.10.2-1 +- Update to 2.10.2 + - Fix Python 2 compatibility breakage introduced in 2.10.1 (GH#2001) +- Re-enable sftp tests, no longer failing under mock + +* Sun Mar 13 2022 Paul Howarth - 2.10.1-1 +- Update to 2.10.1 + - CVE-2022-24302: Creation of new private key files using + '~paramiko.pkey.PKey' subclasses was subject to a race condition between + file creation and mode modification, which could be exploited by an + attacker with knowledge of where the Paramiko-using code would write out + such files; this has been patched by using 'os.open' and 'os.fdopen' to + ensure new files are opened with the correct mode immediately (we've left + the subsequent explicit 'chmod' in place to minimize any possible + disruption, though it may get removed in future backwards-incompatible + updates) + - Add support for the '%%C' token when parsing SSH config files (GH#1976) + - Add support for OpenSSH's Windows agent as a fallback when Putty/WinPageant + isn't available or functional (GH#1509, GH#1837, GH#1868) + - Significantly speed up low-level read/write actions on + '~paramiko.sftp_file.SFTPFile' objects by using 'bytearray'/'memoryview' + (GH#892); this is unlikely to change anything for users of the higher level + methods like 'SFTPClient.get' or 'SFTPClient.getfo', but users of + 'SFTPClient.open' will likely see orders of magnitude improvements for + files larger than a few megabytes in size + - Add 'six' explicitly to install-requires; it snuck into active use at some + point but has only been indicated by transitive dependency on 'bcrypt' + until they somewhat-recently dropped it (GH#1985); this will be short-lived + until we drop Python 2 support + +* Fri Jan 21 2022 Fedora Release Engineering - 2.9.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + +* Fri Jan 14 2022 Paul Howarth - 2.9.2-2 +- Avoid use of deprecated python-mock by using unittest.mock instead + https://github.com/paramiko/paramiko/pull/1666 + +* Sat Jan 8 2022 Paul Howarth - 2.9.2-1 +- Update to 2.9.2 + - Connecting to servers that support 'server-sig-algs' but which have no + overlap between that list and what a Paramiko client supports, now raise + an exception instead of defaulting to 'rsa-sha2-512' (since the use of + 'server-sig-algs' allows us to know what the server supports) + - Enhanced log output when connecting to servers that do not support + 'server-sig-algs' extensions, making the new-as-of-2.9 defaulting to SHA2 + pubkey algorithms more obvious when it kicks in + +* Sat Dec 25 2021 Paul Howarth - 2.9.1-1 +- Update to 2.9.1 + - Server-side support for 'rsa-sha2-256' and 'ssh-rsa' wasn't fully operable + after 2.9.0's release (signatures for RSA pubkeys were always run through + 'rsa-sha2-512' instead) (GH#1935) + +* Fri Dec 24 2021 Paul Howarth - 2.9.0-1 +- Update to 2.9.0 + - Add support for SHA-2 variants of RSA key verification algorithms (as + described in RFC 8332) as well as limited SSH extension negotiation (RFC + 8308) (GH#1326, GH#1643, GH#1644, GH#1925) + How SSH servers/clients decide when and how to use this functionality can be + complicated; Paramiko's support is as follows: + - Client verification of server host key during key exchange will now prefer + rsa-sha2-512, rsa-sha2-256, and legacy ssh-rsa algorithms, in that order, + instead of just ssh-rsa + - Note that the preference order of other algorithm families such as + ed25519 and ecdsa has not changed; for example, those two groups are still + preferred over RSA + - Server mode will now offer all 3 RSA algorithms for host key verification + during key exchange, similar to client mode, if it has been configured + with an RSA host key + - Client mode key exchange now sends the ext-info-c flag signaling support + for MSG_EXT_INFO, and support for parsing the latter (specifically, its + server-sig-algs flag) has been added + - Client mode, when performing public key authentication with an RSA key or + cert, will act as follows: + - In all cases, the list of algorithms to consider is based on the new + preferred_pubkeys list and disabled_algorithms; this list, like with + host keys, prefers SHA2-512, SHA2-256 and SHA1, in that order + - When the server does not send server-sig-algs, Paramiko will attempt + the first algorithm in the above list; clients connecting to legacy + servers should thus use disabled_algorithms to turn off SHA2 + - When the server does send server-sig-algs, the first algorithm + supported by both ends is used, or if there is none, it falls back to + the previous behavior + - SSH agent support grew the ability to specify algorithm flags when + requesting private key signatures; this is now used to forward SHA2 + algorithms when appropriate + - Server mode is now capable of pubkey auth involving SHA-2 signatures from + clients, provided one's server implementation actually provides for doing + so; this includes basic support for sending MSG_EXT_INFO (containing + server-sig-algs only) to clients advertising ext-info-c in their key + exchange list + In order to implement the above, the following API additions were made: + - 'PKey.sign_ssh_data ': Grew an extra, optional + 'algorithm' keyword argument (defaulting to 'None' for most subclasses, + and to "ssh-rsa" for '~paramiko.rsakey.RSAKey') + - A new '~paramiko.ssh_exception.SSHException' subclass was added, + '~paramiko.ssh_exception.IncompatiblePeer', and is raised in all spots + where key exchange aborts due to algorithmic incompatibility; like all + other exceptions in that module, it inherits from 'SSHException', and as + nothing else was changed about the raising (i.e. the attributes and + message text are the same) this change is backwards compatible + - '~paramiko.transport.Transport' grew a '_preferred_pubkeys' attribute and + matching 'preferred_pubkeys' property to match the other, kex-focused, + such members; this allows client pubkey authentication to honor the + 'disabled_algorithms' feature + +* Mon Nov 29 2021 Paul Howarth - 2.8.1-1 +- Update to 2.8.1 + - Fix listdir failure when server uses a locale (GH#985, GH#992); now on + Python 2.7 SFTPAttributes will decode abbreviated month names correctly + rather than raise 'UnicodeDecodeError' + - Deleting items from '~paramiko.hostkeys.HostKeys' would incorrectly raise + 'KeyError' even for valid keys, due to a logic bug (GH#1024) + - Update RSA and ECDSA key decoding subroutines to correctly catch exception + types thrown by modern versions of Cryptography (specifically 'TypeError' + and its internal 'UnsupportedAlgorithm') (GH#1257, GH#1266); these + exception classes will now become '~paramiko.ssh_exception.SSHException' + instances instead of bubbling up + - Update '~paramiko.pkey.PKey' and subclasses to compare ('__eq__') via + direct field/attribute comparison instead of hashing (while retaining the + existing behavior of '__hash__' via a slight refactor) (GH#908) + Warning: + This fixes a security flaw! If you are running Paramiko on 32-bit systems + with low entropy (such as any 32-bit Python 2, or a 32-bit Python 3 that is + running with 'PYTHONHASHSEED=0') it is possible for an attacker to craft a + new keypair from an exfiltrated public key, which Paramiko would consider + equal to the original key. + This could enable attacks such as, but not limited to, the following: + - Paramiko server processes would incorrectly authenticate the attacker + (using their generated private key) as if they were the victim. We see + this as the most plausible attack using this flaw. + - Paramiko client processes would incorrectly validate a connected server + (when host key verification is enabled) while subjected to a + man-in-the-middle attack. This impacts more users than the server-side + version, but also carries higher requirements for the attacker, namely + successful DNS poisoning or other MITM techniques. + +* Mon Oct 11 2021 Paul Howarth - 2.8.0-1 +- Update to 2.8.0 + - Administrivia overhaul, including but not limited to: + - Migrate CI to CircleCI + - Primary dev branch is now 'main' (renamed) + - Many README edits for clarity, modernization etc.; including a bunch more + (and consistent) status badges and unification with main project site + index + - PyPI page much more fleshed out (long_description is now filled in with + the README; sidebar links expanded; etc.) + - flake8, pytest configs split out of setup.cfg into their own files + - Invoke/invocations (used by maintainers/contributors) upgraded to modern + versions + - Newer server-side key exchange algorithms not intended to use SHA1 + (diffie-hellman-group14-sha256, diffie-hellman-group16-sha512) were + incorrectly using SHA1 after all, due to a bug causing them to ignore the + 'hash_algo' class attribute; this has been corrected (GH#1452, GH#1882) + - Add a 'prefetch' keyword argument to 'SFTPClient.get'/'SFTPClient.getfo' so + that users who need to skip SFTP prefetching are able to conditionally turn + it off (GH#1846) + +* Fri Jul 23 2021 Fedora Release Engineering - 2.7.2-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + +* Fri Jun 04 2021 Python Maint - 2.7.2-5 +- Rebuilt for Python 3.10 + +* Wed Mar 3 2021 Paul Howarth - 2.7.2-4 +- Drop invoke dependencies as it requires ancient pytest and we can't expect + it to remain around + +* Tue Mar 02 2021 Dan Radez - 2.7.2-3 +- Removing the python-relax dep using upstream patch + https://github.com/paramiko/paramiko/pull/1665/ + +* Wed Jan 27 2021 Fedora Release Engineering - 2.7.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Mon Aug 31 2020 Paul Howarth - 2.7.2-1 +- Update to 2.7.2 + - Update our CI to catch issues with sdist generation, installation and + testing + - Add missing test suite fixtures directory to MANIFEST.in, reinstating the + ability to run Paramiko's tests from an sdist tarball (GH#1727) + - Remove leading whitespace from OpenSSH RSA test suite static key fixture, + to conform better to spec. (GH#1722) + - Fix incorrect string formatting causing unhelpful error message annotation + when using Kerberos/GSSAPI + - Fix incorrectly swapped order of 'p' and 'q' numbers when loading + OpenSSH-format RSA private keys; at minimum this should address a slowdown + when using such keys, and it also means Paramiko works with Cryptography + 3.1 and above, which complains strenuously when this problem appears + (GH#1723) + +* Wed Jul 29 2020 Fedora Release Engineering - 2.7.1-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Sat May 30 2020 Paul Howarth - 2.7.1-4 +- Avoid FTBFS with pytest 5 (pytest-relaxed pulls in pytest 4) +- Drop explicit dependencies for things that the python dependency generator + finds by itself + +* Sun May 24 2020 Miro Hrončok - 2.7.1-3 +- Rebuilt for Python 3.9 + +* Thu Jan 30 2020 Fedora Release Engineering - 2.7.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Wed Dec 11 2019 Paul Howarth - 2.7.1-1 +- Update to 2.7.1 + - The new-style private key format (added in 2.7.0) suffered from an + unpadding bug that had been fixed earlier for Ed25519 (as that key type has + always used the newer format); that fix has been refactored and applied to + the base key class (GH#1567) + - Fix a bug in support for ECDSA keys under the newly-supported OpenSSH key + format (GH#1565, GH#1566) + +* Wed Dec 4 2019 Paul Howarth - 2.7.0-1 +- Update to 2.7.0 + - Implement support for OpenSSH 6.5-style private key files (typically + denoted as having 'BEGIN OPENSSH PRIVATE KEY' headers instead of PEM + format's 'BEGIN RSA PRIVATE KEY' or similar); if you were getting any sort + of weird auth error from "modern" keys generated on newer operating system + releases (such as macOS Mojave), this is the first update to try (GH#602, + GH#618, GH#1313, GH#1343) + - Token expansion in 'ssh_config' used a different method of determining the + local username ('$USER' environment variable), compared to what the (much + older) client connection code does ('getpass.getuser', which includes + '$USER' but may check other variables first, and is generally much more + comprehensive); both modules now use 'getpass.getuser' + - A couple of outright '~paramiko.config.SSHConfig' parse errors were + previously represented as vanilla 'Exception' instances; as part of recent + feature work a more specific exception class, + '~paramiko.ssh_exception.ConfigParseError', has been created; it is now + also used in those older spots, which is naturally backwards compatible + - Implement support for the 'Match' keyword in 'ssh_config' files; + previously, this keyword was simply ignored and keywords inside such blocks + were treated as if they were part of the previous block (GH#717) + - Note: this feature adds a new optional install dependency 'Invoke' + (https://www.pyinvoke.org), for managing 'Match exec' subprocesses + - Additional installation 'extras_require' "flavors" ('ed25519', 'invoke', + and 'all') have been added to our packaging metadata + - Paramiko's use of 'subprocess' for 'ProxyCommand' support is conditionally + imported to prevent issues on limited interpreter platforms like Google + Compute Engine; however, any resulting 'ImportError' was lost instead of + preserved for raising (in the rare cases where a user tried leveraging + 'ProxyCommand' in such an environment); this has been fixed + - Perform deduplication of 'IdentityFile' contents during 'ssh_config' + parsing; previously, if your config would result in the same value being + encountered more than once, 'IdentityFile' would contain that many copies + of the same string + - Implement most 'canonical hostname' 'ssh_config' functionality + ('CanonicalizeHostname', 'CanonicalDomains', 'CanonicalizeFallbackLocal', + and 'CanonicalizeMaxDots'; 'CanonicalizePermittedCNAMEs' has *not* yet + been implemented) - all were previously silently ignored (GH#897) + - Explicitly document which ssh_config features we currently support; + previously users just had to guess, which is simply no good + - Add new convenience classmethod constructors to + '~paramiko.config.SSHConfig': '~paramiko.config.SSHConfig.from_text', + '~paramiko.config.SSHConfig.from_file', and + '~paramiko.config.SSHConfig.from_path'; no more annoying two-step process! +- Add Recommends: of python3-invoke and python3-pyasn1 for optional + functionality + +* Sun Oct 06 2019 Othman Madjoudj - 2.6.0-5 +- Drop python2 subpackage since it's eol-ed + +* Thu Oct 03 2019 Miro Hrončok - 2.6.0-4 +- Rebuilt for Python 3.8.0rc1 (#1748018) + +* Mon Aug 19 2019 Miro Hrončok - 2.6.0-3 +- Rebuilt for Python 3.8 + +* Fri Jul 26 2019 Fedora Release Engineering - 2.6.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Thu Jun 27 2019 Paul Howarth - 2.6.0-1 +- Update to 2.6.0 + - Add a new keyword argument to 'SSHClient.connect' and + '~paramiko.transport.Transport', 'disabled_algorithms', which allows + selectively disabling one or more kex/key/cipher/etc algorithms; this can + be useful when disabling algorithms your target server (or client) does not + support cleanly, or to work around unpatched bugs in Paramiko's own + implementation thereof (GH#1463) + - Tweak many exception classes so their string representations are more + human-friendly; this also includes incidental changes to some 'super()' + calls (GH#1440, GH#1460) + - Add backwards-compatible support for the 'gssapi' GSSAPI library, as the + previous backend ('python-gssapi') has become defunct (GH#584, GH#1166, + GH#1311) + - 'SSHClient.exec_command' now returns a new subclass, + '~paramiko.channel.ChannelStdinFile', rather than a naïve + '~paramiko.channel.ChannelFile' object for its 'stdin' value, which fixes + issues such as hangs when running remote commands that read from stdin + (GH#322) +- Drop gssapi patch as it's no longer needed +- Drop pytest-relaxed patch as it's no longer needed + +* Thu Jun 27 2019 Paul Howarth - 2.5.1-1 +- Update to 2.5.1 + - Fix Ed25519 key handling so certain key comment lengths don't cause + 'SSHException("Invalid key")' (GH#1306, GH#1400) + +* Mon Jun 10 2019 Paul Howarth - 2.5.0-1 +- Update to 2.5.0 + - Add support for encrypt-then-MAC (ETM) schemes and two newer Diffie-Hellman + group key exchange algorithms ('group14', using SHA256; and 'group16', + using SHA512) + - Add support for Curve25519 key exchange + - Raise Cryptography dependency requirement to version 2.5 (from 1.5) and + update some deprecated uses of its API + - Add support for the modern (as of Python 3.3) import location of + 'MutableMapping' (used in host key management) to avoid the old location + becoming deprecated in Python 3.8 +- Drop hard dependency on pyasn1 as it's only needed for optional GSSAPI + functionality + +* Sat Feb 02 2019 Fedora Release Engineering - 2.4.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Tue Oct 9 2018 Paul Howarth - 2.4.2-1 +- Update to 2.4.2 + - Fix exploit (GH#1283, CVE-2018-1000805) in Paramiko’s server mode (not + client mode) where hostile clients could trick the server into thinking + they were authenticated without actually submitting valid authentication + - Modify protocol message handling such that Transport does not respond to + MSG_UNIMPLEMENTED with its own MSG_UNIMPLEMENTED; this behavior probably + didn’t cause any outright errors, but it doesn’t seem to conform to the + RFCs and could cause (non-infinite) feedback loops in some scenarios + (usually those involving Paramiko on both ends) + - Add *.pub files to the MANIFEST so distributed source packages contain + some necessary test assets (GH#1262) +- Test suite now requires mock ≥ 2.0.0 + +* Sat Jul 14 2018 Fedora Release Engineering - 2.4.1-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Wed Jun 20 2018 Miro Hrončok - 2.4.1-4 +- Rebuilt for Python 3.7 +- Remove dependency on on pytest-relaxed + +* Fri Mar 16 2018 Paul Howarth - 2.4.1-1 +- Update to 2.4.1 + - Fix a security flaw (GH#1175, CVE-2018-7750) in Paramiko's server mode + (this does not impact client use) where authentication status was not + checked before processing channel-open and other requests typically only + sent after authenticating + - Ed25519 auth key decryption raised an unexpected exception when given a + unicode password string (typical in python 3) (GH#1039) + +* Fri Feb 09 2018 Fedora Release Engineering - 2.4.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Sat Nov 18 2017 Athmane Madjoudj - 2.4.0-2 +- Add gssapi patch back since 2.4.0 still not compatible +- Add missing BR (lost during merge) + +* Fri Nov 17 2017 Igor Gnatenko - 2.4.0-1 +- Update to 2.4.0 + +* Wed Nov 15 2017 Athmane Madjoudj - 2.4.0-1 +- Update to 2.4.0 (rhbz #1513208) +- Revamp check section + +* Sun Oct 29 2017 Athmane Madjoudj - 2.3.1-3 +- Add a patch to disable gssapi on unsupported version (rhbz #1507174) + +* Tue Sep 26 2017 Athmane Madjoudj - 2.3.1-2 +- Remove weak deps, paramiko does not support recent gssapi (rhbz #1496148) + +* Sat Sep 23 2017 Athmane Madjoudj - 2.3.1-1 +- Update to 2.3.1 (rhbz #1494764) + +* Wed Sep 20 2017 Paul Howarth - 2.3.0-1 +- 2.3.0. + +* Thu Jul 27 2017 Fedora Release Engineering - 2.2.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Wed Jun 14 2017 Paul Howarth - 2.2.1-1 +- 2.2.1. + +* Sun Jun 11 2017 Paul Howarth - 2.2.0-1 +- 2.2.0. + +* Wed Feb 22 2017 Paul Howarth - 2.1.2-1 +- 2.1.2. + +* Sat Feb 11 2017 Fedora Release Engineering - 2.1.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Mon Dec 19 2016 Miro Hrončok - 2.1.1-2 +- Rebuild for Python 3.6 + +* Fri Dec 16 2016 Jon Ciesla - 2.1.1-1 +- 2.1.1. + +* Fri Dec 09 2016 Jon Ciesla - 2.1.0-1 +- 2.1.0. + +* Fri Dec 09 2016 Jon Ciesla - 2.0.2-1 +- 2.0.2. + +* Tue Jul 19 2016 Fedora Release Engineering - 2.0.0-2 +- https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages + +* Fri Apr 29 2016 Igor Gnatenko - 2.0.0-1 +- Update to 2.0.0 (RHBZ #1331737) + +* Sun Mar 27 2016 Igor Gnatenko - 1.16.0-1 +- Update to 1.16.0 +- Adopt to new packaging guidelines + +* Thu Feb 04 2016 Fedora Release Engineering - 1.15.2-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Tue Nov 10 2015 Fedora Release Engineering - 1.15.2-4 +- Rebuilt for https://fedoraproject.org/wiki/Changes/python3.5 + +* Thu Jun 18 2015 Fedora Release Engineering - 1.15.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Sun Mar 22 2015 Peter Robinson 1.15.2-2 +- Use %%license +- Move duplicated docs to single doc sub package +- Remove old F-15 conditionals + +* Tue Dec 23 2014 Athmane Madjoudj 1.15.2-1 +- Update to 1.15.2 + +* Mon Nov 24 2014 Athmane Madjoudj 1.15.1-5 +- Add conditional to exclude EL since does not have py3 + +* Sat Nov 15 2014 Athmane Madjoudj 1.15.1-4 +- py3dir creation should be in prep section + +* Fri Nov 14 2014 Athmane Madjoudj 1.15.1-3 +- Build each pkg in a clean dir + +* Fri Nov 14 2014 Athmane Madjoudj 1.15.1-2 +- Add support for python3 +- Add BR -devel for python macros. + +* Fri Oct 17 2014 Jeffrey C. Ollie - 1.15.1-1 +- Update to 1.15.1 + +* Fri Jun 13 2014 Orion Poplawski - 1.12.4-1 +- Update to 1.12.4 + +* Sat Jun 07 2014 Fedora Release Engineering - 1.12.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Tue Feb 25 2014 Orion Poplawski - 1.12.2-1 +- Update to 1.12.2 + +* Wed Jan 22 2014 Orion Poplawski - 1.11.3-1 +- Update to 1.11.3 + +* Mon Oct 21 2013 Orion Poplawski - 1.11.0-1 +- Update to 1.11.0 + +* Sun Aug 04 2013 Fedora Release Engineering - 1.10.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Thu May 9 2013 Jeffrey Ollie - 1.10.1-1 +- Update to 1.10.1 + +* Thu Feb 14 2013 Fedora Release Engineering - 1.9.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Wed Jan 2 2013 Jeffrey Ollie - 1.9.0-1 +- Update to 1.9.0 + +* Sat Jul 21 2012 Fedora Release Engineering - 1.7.7.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Sat Jan 14 2012 Fedora Release Engineering - 1.7.7.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Wed Jul 6 2011 Jeffrey C. Ollie - 1.7.7.1-1 +- v1.7.7.1 (George) 21may11 +- ------------------------- +- * Make the verification phase of SFTP.put optional (Larry Wright) +- * Patches to fix AIX support (anonymous) +- * Patch from Michele Bertoldi to allow compression to be turned on in the +- client constructor. +- * Patch from Shad Sharma to raise an exception if the transport isn't active +- when you try to open a new channel. +- * Stop leaking file descriptors in the SSH agent (John Adams) +- * More fixes for Windows address family support (Andrew Bennetts) +- * Use Crypto.Random rather than Crypto.Util.RandomPool +- (Gary van der Merwe, #271791) +- * Support for openssl keys (tehfink) +- * Fix multi-process support by calling Random.atfork (sugarc0de) + +* Tue Feb 08 2011 Fedora Release Engineering - 1.7.6-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Tue Jan 4 2011 Toshio Kuratomi - 1.7.6-3 +- Patch to address deprecation warning from pycrypto +- Simplify build as shown in new python guidelines +- Enable test suite + +* Thu Jul 22 2010 David Malcolm - 1.7.6-2 +- Rebuilt for https://fedoraproject.org/wiki/Features/Python_2.7/MassRebuild + +* Mon Nov 2 2009 Jeffrey C. Ollie - 1.7.6-1 +- v1.7.6 (Fanny) 1nov09 +- --------------------- +- * fixed bugs 411099 (sftp chdir isn't unicode-safe), 363163 & 411910 (more +- IPv6 problems on windows), 413850 (race when server closes the channel), +- 426925 (support port numbers in host keys) + +* Tue Oct 13 2009 Jeremy Katz - 1.7.5-2 +- Fix race condition (#526341) + +* Thu Jul 23 2009 Jeffrey C. Ollie - 1.7.5-1 +- v1.7.5 (Ernest) 19jul09 +- ----------------------- +- * added support for ARC4 cipher and CTR block chaining (Denis Bernard) +- * made transport threads daemonize, to fix python 2.6 atexit behavior +- * support unicode hostnames, and IP6 addresses (Maxime Ripard, Shikhar +- Bhushan) +- * various small bug fixes + +* Thu Feb 26 2009 Fedora Release Engineering - 1.7.4-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild + +* Mon Feb 16 2009 Jeffrey C. Ollie - 1.7.4-4 +- Add demos as documentation. BZ#485742 + +* Sat Nov 29 2008 Ignacio Vazquez-Abrams - 1.7.4-3 +- Rebuild for Python 2.6 + +* Wed Sep 3 2008 Tom "spot" Callaway - 1.7.4-2 +- fix license tag + +* Sun Jul 6 2008 Jeffrey C. Ollie - 1.7.4-1 +- Update to 1.7.4 + +* Mon Mar 24 2008 Jeffrey C. Ollie - 1.7.3-1 +- Update to 1.7.3. + +* Tue Jan 22 2008 Jeffrey C. Ollie - 1.7.2-1 +- Update to 1.7.2. +- Remove upstreamed patch. + +* Mon Jan 14 2008 Jeffrey C. Ollie - 1.7.1-3 +- Update to latest Python packaging guidelines. +- Apply patch that fixes insecure use of RandomPool. + +* Thu Jul 19 2007 Jeffrey C. Ollie - 1.7.1-2 +- Bump rev + +* Thu Jul 19 2007 Jeffrey C. Ollie - 1.7.1-1 +- Update to 1.7.1 + +* Sat Dec 09 2006 Toshio Kuratomi - 1.6.4-1 +- Update to 1.6.4 +- Upstream is now shipping tarballs +- Bump for python 2.5 in devel + +* Mon Oct 9 2006 Jeffrey C. Ollie - 1.6.2-1 +- Update to 1.6.2 + +* Sat Sep 16 2006 Shahms E. King 1.6.1-3 +- Rebuild for FC6 + +* Fri Aug 11 2006 Shahms E. King 1.6.1-2 +- Include, don't ghost .pyo files per new guidelines + +* Tue Aug 08 2006 Shahms E. King 1.6.1-1 +- Update to new upstream version + +* Fri Jun 02 2006 Shahms E. King 1.6-1 +- Update to new upstream version +- ghost the .pyo files + +* Fri May 05 2006 Shahms E. King 1.5.4-2 +- Fix source line and rebuild + +* Fri May 05 2006 Shahms E. King 1.5.4-1 +- Update to new upstream version + +* Wed Apr 12 2006 Shahms E. King 1.5.3-1 + - Initial package