diff --git a/paramiko-2.7.2-drop-pytest-relaxed.patch b/0003-remove-pytest-relaxed-dep.patch similarity index 50% rename from paramiko-2.7.2-drop-pytest-relaxed.patch rename to 0003-remove-pytest-relaxed-dep.patch index 2be787f61fe1a1c68a5b7332410aeeb29a565cbf..b66a58a773dc89715e7302c03e747c1b28687710 100644 --- a/paramiko-2.7.2-drop-pytest-relaxed.patch +++ b/0003-remove-pytest-relaxed-dep.patch @@ -1,14 +1,23 @@ -From 953d9a1f1055de97e35c7060fcebc7283eff9e29 Mon Sep 17 00:00:00 2001 -From: zhaorenhai -Date: Fri, 29 Jan 2021 06:48:10 +0000 -Subject: [PATCH] drop pytest-relaxed - ---- - tests/test_client.py | 24 ++++++++++++------------ - 1 file changed, 12 insertions(+), 12 deletions(-) - -diff --git a/tests/test_client.py b/tests/test_client.py -index 60ad310c..2d665cdd 100644 +--- 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 @@ -33,7 +33,7 @@ import warnings @@ -16,47 +25,44 @@ index 60ad310c..2d665cdd 100644 from tempfile import mkstemp -from pytest_relaxed import raises -+from pytest import raises ++import pytest from mock import patch, Mock import paramiko -@@ -684,10 +684,10 @@ class PasswordPassphraseTests(ClientTest): +@@ -733,11 +733,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 raises(SSHException): ++ with pytest.raises(SSHException): + # Using the "right" password in the "wrong" field shouldn't work. -+ self._test_connection(passphrase='pygmalion') ++ self._test_connection(passphrase="pygmalion") + @requires_sha1_signing def test_passphrase_kwarg_used_for_key_passphrase(self): - # Straightforward again, with new passphrase kwarg. -@@ -705,14 +705,14 @@ class PasswordPassphraseTests(ClientTest): +@@ -757,15 +757,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. + # 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 raises(AuthenticationException): # TODO: more granular -+ # Sanity: if we're given both fields, the password field is NOT used as -+ # a passphrase. ++ with pytest.raises(AuthenticationException): + self._test_connection( -+ key_filename=_support('test_rsa_password.key'), -+ password='television', -+ passphrase='wat? lol no', ++ key_filename=_support("test_rsa_password.key"), ++ password="television", ++ passphrase="wat? lol no", + ) --- -2.27.0 - diff --git a/backport-CVE-2022-24302.patch b/backport-CVE-2022-24302.patch deleted file mode 100644 index 23181beb9c79cf6e5d36b705eee043149bf0a43b..0000000000000000000000000000000000000000 --- a/backport-CVE-2022-24302.patch +++ /dev/null @@ -1,147 +0,0 @@ -From 4c491e299c9b800358b16fa4886d8d94f45abe2e Mon Sep 17 00:00:00 2001 -From: Jeff Forcier -Date: Fri, 25 Feb 2022 14:50:42 -0500 -Subject: [PATCH] Fix CVE re: PKey.write_private_key chmod race - -CVE-2022-24302 (see changelog for link) - -Conflict:NA -Reference:https://github.com/paramiko/paramiko/commit/4c491e299c9b800358b16fa4886d8d94f45abe2e - ---- - paramiko/pkey.py | 12 ++++++++- - sites/www/changelog.rst | 14 ++++++++++ - tests/test_pkey.py | 58 ++++++++++++++++++++++++++++++++++++++++- - 3 files changed, 82 insertions(+), 2 deletions(-) - -diff --git a/paramiko/pkey.py b/paramiko/pkey.py -index 3a07426..84d0c84 100644 ---- a/paramiko/pkey.py -+++ b/paramiko/pkey.py -@@ -544,7 +544,17 @@ class PKey(object): - - :raises: ``IOError`` -- if there was an error writing the file. - """ -- with open(filename, "w") as f: -+ # Ensure that we create new key files directly with a user-only mode, -+ # instead of opening, writing, then chmodding, which leaves us open to -+ # CVE-2022-24302. -+ # NOTE: O_TRUNC is a noop on new files, and O_CREAT is a noop on -+ # existing files, so using all 3 in both cases is fine. Ditto the use -+ # of the 'mode' argument; it should be safe to give even for existing -+ # files (though it will not act like a chmod in that case). -+ kwargs = dict(flags=os.O_WRONLY | os.O_TRUNC | os.O_CREAT, mode=o600) -+ # NOTE: yea, you still gotta inform the FLO that it is in "write" mode -+ with os.fdopen(os.open(filename, **kwargs), mode="w") as f: -+ # TODO 3.0: remove the now redundant chmod - os.chmod(filename, o600) - self._write_private_key(f, key, format, password=password) - -diff --git a/sites/www/changelog.rst b/sites/www/changelog.rst -index f3ff074..91b54f1 100644 ---- a/sites/www/changelog.rst -+++ b/sites/www/changelog.rst -@@ -2,6 +2,20 @@ - Changelog - ========= - -+- :bug:`-` (`CVE-2022-24302 -+ `_) Creation -+ of new private key files using `~paramiko.pkey.PKey` subclasses was subject -+ to a race condition between file creation & 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. -+ -+ Thanks to Jan Schejbal for the report & feedback on the solution, and to -+ Jeremy Katz at Tidelift for coordinating the disclosure. - - :release:`2.7.2 <2020-08-30>` - - :support:`- backported` Update our CI to catch issues with sdist generation, - installation and testing. -diff --git a/tests/test_pkey.py b/tests/test_pkey.py -index 18c27bb..1c82742 100644 ---- a/tests/test_pkey.py -+++ b/tests/test_pkey.py -@@ -23,14 +23,16 @@ Some unit tests for public/private key objects. - - import unittest - import os -+import stat - from binascii import hexlify - from hashlib import md5 - - from paramiko import RSAKey, DSSKey, ECDSAKey, Ed25519Key, Message, util - from paramiko.py3compat import StringIO, byte_chr, b, bytes, PY2 -+from paramiko.common import o600 - - from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateNumbers --from mock import patch -+from mock import patch, Mock - - from .util import _support - -@@ -618,3 +620,57 @@ class KeyTest(unittest.TestCase): - key1.load_certificate, - _support("test_rsa.key-cert.pub"), - ) -+ -+ @patch("paramiko.pkey.os") -+ def _test_keyfile_race(self, os_, exists): -+ # Re: CVE-2022-24302 -+ password = "television" -+ newpassword = "radio" -+ source = _support("test_ecdsa_384.key") -+ new = source + ".new" -+ # Mock setup -+ os_.path.exists.return_value = exists -+ # Attach os flag values to mock -+ for attr, value in vars(os).items(): -+ if attr.startswith("O_"): -+ setattr(os_, attr, value) -+ # Load fixture key -+ key = ECDSAKey(filename=source, password=password) -+ key._write_private_key = Mock() -+ # Write out in new location -+ key.write_private_key_file(new, password=newpassword) -+ # Expected open via os module -+ os_.open.assert_called_once_with(new, flags=os.O_WRONLY | os.O_CREAT | os.O_TRUNC, mode=o600) -+ os_.fdopen.assert_called_once_with(os_.open.return_value, mode="w") -+ # Old chmod still around for backwards compat -+ os_.chmod.assert_called_once_with(new, o600) -+ assert ( -+ key._write_private_key.call_args[0][0] -+ == os_.fdopen.return_value.__enter__.return_value -+ ) -+ -+ def test_new_keyfiles_avoid_file_descriptor_race_on_chmod(self): -+ self._test_keyfile_race(exists=False) -+ -+ def test_existing_keyfiles_still_work_ok(self): -+ self._test_keyfile_race(exists=True) -+ -+ def test_new_keyfiles_avoid_descriptor_race_integration(self): -+ # Integration-style version of above -+ password = "television" -+ newpassword = "radio" -+ source = _support("test_ecdsa_384.key") -+ new = source + ".new" -+ # Load fixture key -+ key = ECDSAKey(filename=source, password=password) -+ try: -+ # Write out in new location -+ key.write_private_key_file(new, password=newpassword) -+ # Test mode -+ assert stat.S_IMODE(os.stat(new).st_mode) == o600 -+ # Prove can open with new password -+ reloaded = ECDSAKey(filename=new, password=newpassword) -+ assert reloaded == key -+ finally: -+ if os.path.exists(new): -+ os.unlink(new) --- -2.27.0 - diff --git a/backport-Skip-tests-requiring-invoke.patch b/backport-Skip-tests-requiring-invoke.patch new file mode 100644 index 0000000000000000000000000000000000000000..3499e8d3b7b713378b0ecf88c8beda9dbd92122a --- /dev/null +++ b/backport-Skip-tests-requiring-invoke.patch @@ -0,0 +1,37 @@ +From 2dc654a20c4f1908d587060809a9d67b31352497 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= +Date: Thu, 16 Apr 2020 09:46:39 +0200 +Subject: [PATCH] Skip tests requiring invoke if it's not installed + +Since invoke is an optional dependency and only one group of tests +require it, skip them gracefully rather than failing if it's not +present. +--- + tests/test_config.py | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/tests/test_config.py b/tests/test_config.py +index 5e9aa0592..2095061f2 100644 +--- a/tests/test_config.py ++++ b/tests/test_config.py +@@ -6,7 +6,11 @@ + + from paramiko.py3compat import string_types + +-from invoke import Result ++try: ++ from invoke import Result ++except ImportError: ++ Result = None ++ + from mock import patch + from pytest import raises, mark, fixture + +@@ -705,6 +709,7 @@ def inner(command, *args, **kwargs): + return inner + + ++@mark.skipif(Result is None, reason="requires invoke package") + class TestMatchExec(object): + @patch("paramiko.config.invoke", new=None) + @patch("paramiko.config.invoke_import_error", new=ImportError("meh")) diff --git a/backport-Use-args-not-kwargs-to-retain-py2-compat-for-now.patch b/backport-Use-args-not-kwargs-to-retain-py2-compat-for-now.patch deleted file mode 100644 index 42dc8ad86912f43410319a69a5616cce38ee1cd8..0000000000000000000000000000000000000000 --- a/backport-Use-args-not-kwargs-to-retain-py2-compat-for-now.patch +++ /dev/null @@ -1,72 +0,0 @@ -From 76b781754bfefe21706762442c422bac523701e4 Mon Sep 17 00:00:00 2001 -From: Jeff Forcier -Date: Mon, 14 Mar 2022 19:21:01 -0400 -Subject: [PATCH] Use args, not kwargs, to retain py2 compat for now - -This patch is the rear patch of CVE-2022-24302 - -Conflict:NA -Reference:https://github.com/paramiko/paramiko/commit/76b781754bfefe21706762442c422bac523701e4 - ---- - paramiko/pkey.py | 5 +++-- - sites/www/changelog.rst | 8 ++++++++ - tests/test_pkey.py | 6 ++++-- - 3 files changed, 15 insertions(+), 4 deletions(-) - -diff --git a/paramiko/pkey.py b/paramiko/pkey.py -index 84d0c84..77c5a4e 100644 ---- a/paramiko/pkey.py -+++ b/paramiko/pkey.py -@@ -551,9 +551,10 @@ class PKey(object): - # existing files, so using all 3 in both cases is fine. Ditto the use - # of the 'mode' argument; it should be safe to give even for existing - # files (though it will not act like a chmod in that case). -- kwargs = dict(flags=os.O_WRONLY | os.O_TRUNC | os.O_CREAT, mode=o600) -+ # TODO 3.0: turn into kwargs again -+ args = [os.O_WRONLY | os.O_TRUNC | os.O_CREAT, o600] - # NOTE: yea, you still gotta inform the FLO that it is in "write" mode -- with os.fdopen(os.open(filename, **kwargs), mode="w") as f: -+ with os.fdopen(os.open(filename, *args), "w") as f: - # TODO 3.0: remove the now redundant chmod - os.chmod(filename, o600) - self._write_private_key(f, key, format, password=password) -diff --git a/sites/www/changelog.rst b/sites/www/changelog.rst -index 91b54f1..1c6537f 100644 ---- a/sites/www/changelog.rst -+++ b/sites/www/changelog.rst -@@ -2,6 +2,14 @@ - Changelog - ========= - -+- :bug:`2001` Fix Python 2 compatibility breakage introduced in 2.10.1. Spotted -+ by Christian Hammond. -+ -+ .. warning:: -+ This is almost certainly the last time we will fix Python 2 related -+ errors! Please see `the roadmap -+ `_. -+ - - :bug:`-` (`CVE-2022-24302 - `_) Creation - of new private key files using `~paramiko.pkey.PKey` subclasses was subject -diff --git a/tests/test_pkey.py b/tests/test_pkey.py -index 1c82742..e1a17f5 100644 ---- a/tests/test_pkey.py -+++ b/tests/test_pkey.py -@@ -640,8 +640,10 @@ class KeyTest(unittest.TestCase): - # Write out in new location - key.write_private_key_file(new, password=newpassword) - # Expected open via os module -- os_.open.assert_called_once_with(new, flags=os.O_WRONLY | os.O_CREAT | os.O_TRUNC, mode=o600) -- os_.fdopen.assert_called_once_with(os_.open.return_value, mode="w") -+ os_.open.assert_called_once_with( -+ new, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, o600 -+ ) -+ os_.fdopen.assert_called_once_with(os_.open.return_value, "w") - # Old chmod still around for backwards compat - os_.chmod.assert_called_once_with(new, o600) - assert ( --- -2.27.0 - diff --git a/backport-fix-error-in-sftp-testcase.patch b/backport-fix-error-in-sftp-testcase.patch new file mode 100644 index 0000000000000000000000000000000000000000..d861faafefabb9c8c62318c6a7aa2f215a2e00f2 --- /dev/null +++ b/backport-fix-error-in-sftp-testcase.patch @@ -0,0 +1,60 @@ +From 47cfed55575c21ac558e6d00a4ab1814406be651 Mon Sep 17 00:00:00 2001 +From: Stanislav Levin +Date: Sat, 4 Jun 2022 03:03:23 GMT+8 +Subject: [PATCH] fix error in sftp testcase + +Conflict:NA +Reference:https://github.com/paramiko/paramiko/commit/47cfed55575c21ac558e6d00a4ab1814406be651 + +--- + tests/__init__.py | 4 ++-- + tests/test_sftp.py | 4 ++-- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/tests/__init__.py b/tests/__init__.py +index 9850f3c..2a9cb4b 100644 +--- a/tests/__init__.py ++++ b/tests/__init__.py +@@ -36,7 +36,7 @@ def requireNonAsciiLocale(category_name="LC_ALL"): + def _decorate_with_locale(category, try_locales, test_method): + """Decorate test_method to run after switching to a different locale.""" + +- def _test_under_locale(testself, sftp): ++ def _test_under_locale(testself, *args, **kwargs): + original = locale.setlocale(category) + while try_locales: + try: +@@ -46,7 +46,7 @@ def _decorate_with_locale(category, try_locales, test_method): + try_locales.pop(0) + else: + try: +- return test_method(testself) ++ return test_method(testself, *args, **kwargs) + finally: + locale.setlocale(category, original) + skipTest = getattr(testself, "skipTest", None) +diff --git a/tests/test_sftp.py b/tests/test_sftp.py +index 0650e8d..b53ece7 100644 +--- a/tests/test_sftp.py ++++ b/tests/test_sftp.py +@@ -277,7 +277,7 @@ class TestSFTP(object): + sftp.open(sftp.FOLDER + "/canard.txt", "w").close() + try: + folder_contents = sftp.listdir(sftp.FOLDER) +- self.assertEqual(["canard.txt"], folder_contents) ++ assert ["canard.txt"] == folder_contents + finally: + sftp.remove(sftp.FOLDER + "/canard.txt") + +@@ -797,7 +797,7 @@ class TestSFTP(object): + """Test SFTPAttributes under a locale with non-ascii time strings.""" + some_stat = os.stat(sftp.FOLDER) + sftp_attributes = SFTPAttributes.from_stat(some_stat, u("a_directory")) +- self.assertTrue(b"a_directory" in sftp_attributes.asbytes()) ++ assert b"a_directory" in sftp_attributes.asbytes() + + def test_sftp_attributes_empty_str(self, sftp): + sftp_attributes = SFTPAttributes() +-- +2.23.0 + diff --git a/paramiko-2.11.0.tar.gz b/paramiko-2.11.0.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..f9a3aac5e784c159790147a2f3ecf9aa3a42d239 Binary files /dev/null and b/paramiko-2.11.0.tar.gz differ diff --git a/paramiko-2.7.2.tar.gz b/paramiko-2.7.2.tar.gz deleted file mode 100644 index 1aea7d6ea3bcef1c8309c4e9ab200df89f885d4f..0000000000000000000000000000000000000000 Binary files a/paramiko-2.7.2.tar.gz and /dev/null differ diff --git a/python-paramiko.spec b/python-paramiko.spec index 74effd737efdba276009d1f00d9face1618d9258..e43543473212f69e313c419cc3f3b8bddfe53fc1 100644 --- a/python-paramiko.spec +++ b/python-paramiko.spec @@ -1,14 +1,16 @@ Name: python-paramiko -Version: 2.7.2 -Release: 2 +Version: 2.11.0 +Release: 1 Summary: Python SSH module License: LGPLv2+ URL: https://github.com/paramiko/paramiko Source0: https://github.com/paramiko/paramiko/archive/%{version}/paramiko-%{version}.tar.gz -Patch0: paramiko-2.7.2-drop-pytest-relaxed.patch -Patch6000: backport-CVE-2022-24302.patch -Patch6001: backport-Use-args-not-kwargs-to-retain-py2-compat-for-now.patch +# Skip tests requiring invoke if it's not installed +# Can be removed when https://github.com/paramiko/paramiko/pull/1667/ is released +Patch6000: backport-Skip-tests-requiring-invoke.patch +Patch6001: 0003-remove-pytest-relaxed-dep.patch +Patch6002: backport-fix-error-in-sftp-testcase.patch BuildArch: noarch @@ -21,9 +23,9 @@ connections to remote machines. Summary: Python SSH module BuildRequires: python3-devel python3-setuptools python3-bcrypt >= 3.1.3 python3-pytest BuildRequires: python3-cryptography >= 2.5 python3-pyasn1 >= 0.1.7 python3-pynacl >= 1.0.1 -BuildRequires: python3-invoke >= 1.3 python3-mock >= 2.0 +BuildRequires: python3-mock >= 2.0 Requires: python3-bcrypt >= 3.1.3 python3-cryptography >= 1.5 -Requires: python3-pyasn1 >= 0.1.7 python3-pynacl >= 1.0.1 python3-invoke >= 1.3 +Requires: python3-pyasn1 >= 0.1.7 python3-pynacl >= 1.0.1 %{?python_provide:%python_provide python3-paramiko} %description -n python3-paramiko @@ -56,7 +58,6 @@ sphinx-build -b html sites/docs/ html/ rm -f html/.buildinfo %check -rm -f tests/test_sftp*.py PYTHONPATH=%{buildroot}%{python3_sitelib} pytest-%{python3_version} %files -n python3-paramiko @@ -68,6 +69,9 @@ PYTHONPATH=%{buildroot}%{python3_sitelib} pytest-%{python3_version} %doc html/ demos/ NEWS README.rst %changelog +* Tue Oct 17 2023 zhuyuncheng - 2.11.0-1 +- upgrade to 2.11.0-1 + * Mon Mar 28 2022 dongyuzhen - 2.7.2-2 - fix CVE-2022-24302 and the rear patch of CVE-2022-24302 diff --git a/python-paramiko.yaml b/python-paramiko.yaml new file mode 100644 index 0000000000000000000000000000000000000000..41d3544d9a6db569aaea059e8b9f170d67279fba --- /dev/null +++ b/python-paramiko.yaml @@ -0,0 +1,4 @@ +version_control: github +src_repo: paramiko/paramiko +tag_prefix: ^v +separator: .