diff --git a/CVE-2023-6597-TempDir-cleaning-symlink.patch b/CVE-2023-6597-TempDir-cleaning-symlink.patch new file mode 100644 index 0000000..7813228 --- /dev/null +++ b/CVE-2023-6597-TempDir-cleaning-symlink.patch @@ -0,0 +1,178 @@ +--- + Lib/tempfile.py | 16 + + Lib/test/test_tempfile.py | 119 ++++++++++ + Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst | 2 + 3 files changed, 137 insertions(+) + +--- a/Lib/tempfile.py ++++ b/Lib/tempfile.py +@@ -286,6 +286,22 @@ def _resetperms(path): + _dont_follow_symlinks(chflags, path, 0) + _dont_follow_symlinks(_os.chmod, path, 0o700) + ++def _dont_follow_symlinks(func, path, *args): ++ # Pass follow_symlinks=False, unless not supported on this platform. ++ if func in _os.supports_follow_symlinks: ++ func(path, *args, follow_symlinks=False) ++ elif _os.name == 'nt' or not _os.path.islink(path): ++ func(path, *args) ++ ++def _resetperms(path): ++ try: ++ chflags = _os.chflags ++ except AttributeError: ++ pass ++ else: ++ _dont_follow_symlinks(chflags, path, 0) ++ _dont_follow_symlinks(_os.chmod, path, 0o700) ++ + + # User visible interfaces. + +--- a/Lib/test/test_tempfile.py ++++ b/Lib/test/test_tempfile.py +@@ -1673,6 +1673,103 @@ class TestTemporaryDirectory(BaseTestCas + new_flags = os.stat(dir1).st_flags + self.assertEqual(new_flags, old_flags) + ++ @os_helper.skip_unless_symlink ++ def test_cleanup_with_symlink_modes(self): ++ # cleanup() should not follow symlinks when fixing mode bits (#91133) ++ with self.do_create(recurse=0) as d2: ++ file1 = os.path.join(d2, 'file1') ++ open(file1, 'wb').close() ++ dir1 = os.path.join(d2, 'dir1') ++ os.mkdir(dir1) ++ for mode in range(8): ++ mode <<= 6 ++ with self.subTest(mode=format(mode, '03o')): ++ def test(target, target_is_directory): ++ d1 = self.do_create(recurse=0) ++ symlink = os.path.join(d1.name, 'symlink') ++ os.symlink(target, symlink, ++ target_is_directory=target_is_directory) ++ try: ++ os.chmod(symlink, mode, follow_symlinks=False) ++ except NotImplementedError: ++ pass ++ try: ++ os.chmod(symlink, mode) ++ except FileNotFoundError: ++ pass ++ os.chmod(d1.name, mode) ++ d1.cleanup() ++ self.assertFalse(os.path.exists(d1.name)) ++ ++ with self.subTest('nonexisting file'): ++ test('nonexisting', target_is_directory=False) ++ with self.subTest('nonexisting dir'): ++ test('nonexisting', target_is_directory=True) ++ ++ with self.subTest('existing file'): ++ os.chmod(file1, mode) ++ old_mode = os.stat(file1).st_mode ++ test(file1, target_is_directory=False) ++ new_mode = os.stat(file1).st_mode ++ self.assertEqual(new_mode, old_mode, ++ '%03o != %03o' % (new_mode, old_mode)) ++ ++ with self.subTest('existing dir'): ++ os.chmod(dir1, mode) ++ old_mode = os.stat(dir1).st_mode ++ test(dir1, target_is_directory=True) ++ new_mode = os.stat(dir1).st_mode ++ self.assertEqual(new_mode, old_mode, ++ '%03o != %03o' % (new_mode, old_mode)) ++ ++ @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.chflags') ++ @os_helper.skip_unless_symlink ++ def test_cleanup_with_symlink_flags(self): ++ # cleanup() should not follow symlinks when fixing flags (#91133) ++ flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK ++ self.check_flags(flags) ++ ++ with self.do_create(recurse=0) as d2: ++ file1 = os.path.join(d2, 'file1') ++ open(file1, 'wb').close() ++ dir1 = os.path.join(d2, 'dir1') ++ os.mkdir(dir1) ++ def test(target, target_is_directory): ++ d1 = self.do_create(recurse=0) ++ symlink = os.path.join(d1.name, 'symlink') ++ os.symlink(target, symlink, ++ target_is_directory=target_is_directory) ++ try: ++ os.chflags(symlink, flags, follow_symlinks=False) ++ except NotImplementedError: ++ pass ++ try: ++ os.chflags(symlink, flags) ++ except FileNotFoundError: ++ pass ++ os.chflags(d1.name, flags) ++ d1.cleanup() ++ self.assertFalse(os.path.exists(d1.name)) ++ ++ with self.subTest('nonexisting file'): ++ test('nonexisting', target_is_directory=False) ++ with self.subTest('nonexisting dir'): ++ test('nonexisting', target_is_directory=True) ++ ++ with self.subTest('existing file'): ++ os.chflags(file1, flags) ++ old_flags = os.stat(file1).st_flags ++ test(file1, target_is_directory=False) ++ new_flags = os.stat(file1).st_flags ++ self.assertEqual(new_flags, old_flags) ++ ++ with self.subTest('existing dir'): ++ os.chflags(dir1, flags) ++ old_flags = os.stat(dir1).st_flags ++ test(dir1, target_is_directory=True) ++ new_flags = os.stat(dir1).st_flags ++ self.assertEqual(new_flags, old_flags) ++ + @support.cpython_only + def test_del_on_collection(self): + # A TemporaryDirectory is deleted when garbage collected +@@ -1861,6 +1958,22 @@ class TestTemporaryDirectory(BaseTestCas + finally: + os_helper.unlink(filename) + ++ def check_flags(self, flags): ++ # skip the test if these flags are not supported (ex: FreeBSD 13) ++ filename = os_helper.TESTFN ++ try: ++ open(filename, "w").close() ++ try: ++ os.chflags(filename, flags) ++ except OSError as exc: ++ # "OSError: [Errno 45] Operation not supported" ++ self.skipTest(f"chflags() doesn't support flags " ++ f"{flags:#b}: {exc}") ++ else: ++ os.chflags(filename, 0) ++ finally: ++ os_helper.unlink(filename) ++ + @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.chflags') + def test_flags(self): + flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK +@@ -1876,6 +1989,12 @@ class TestTemporaryDirectory(BaseTestCas + d.cleanup() + self.assertFalse(os.path.exists(d.name)) + ++ def test_delete_false(self): ++ with tempfile.TemporaryDirectory(delete=False) as working_dir: ++ pass ++ self.assertTrue(os.path.exists(working_dir)) ++ shutil.rmtree(working_dir) ++ + + if __name__ == "__main__": + unittest.main() +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst +@@ -0,0 +1,2 @@ ++Fix a bug in :class:`tempfile.TemporaryDirectory` cleanup, which now no longer ++dereferences symlinks when working around file system permission errors. diff --git a/python311.changes b/python311.changes index 82b745b..dd86098 100644 --- a/python311.changes +++ b/python311.changes @@ -1,3 +1,14 @@ +------------------------------------------------------------------- +Fri Feb 23 01:06:42 UTC 2024 - Matej Cepl + +- (bsc#1219666, CVE-2023-6597) Add + CVE-2023-6597-TempDir-cleaning-symlink.patch (patch from + gh#python/cpython!99930) fixing symlink bug in cleanup of + tempfile.TemporaryDirectory. +- Repurpose skip-failing-tests.patch to increase timeout for + test.test_asyncio.test_tasks.TimeoutTests.test_timeout_time, + which fails on slow machines in IBS (s390x). + ------------------------------------------------------------------- Tue Feb 20 22:14:02 UTC 2024 - Matej Cepl diff --git a/python311.spec b/python311.spec index 43975aa..2feb904 100644 --- a/python311.spec +++ b/python311.spec @@ -168,6 +168,9 @@ Patch40: CVE-2023-27043-email-parsing-errors.patch # PATCH-FIX-UPSTREAM libexpat260.patch gh#python/cpython#115289 # Fix tests for XMLPullParser with Expat 2.6.0 Patch41: libexpat260.patch +# PATCH-FIX-UPSTREAM CVE-2023-6597-TempDir-cleaning-symlink.patch bsc#1219666 mcepl@suse.com +# tempfile.TemporaryDirectory: fix symlink bug in cleanup (from gh#python/cpython!99930) +Patch42: CVE-2023-6597-TempDir-cleaning-symlink.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes @@ -429,6 +432,7 @@ other applications. %patch -P 39 -p1 %patch -P 40 -p1 %patch -P 41 -p1 +%patch -P 42 -p1 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac