From 2a55620864c55fdd858e80ca984a1f304c51034fed51768f929f7f604f2d97be Mon Sep 17 00:00:00 2001 From: Matej Cepl Date: Thu, 29 Feb 2024 21:00:39 +0000 Subject: [PATCH] - (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. OBS-URL: https://build.opensuse.org/package/show/devel:languages:python:Factory/python312?expand=0&rev=39 --- CVE-2023-6597-TempDir-cleaning-symlink.patch | 165 +++++++++++++++++++ python312.changes | 8 + python312.spec | 3 + 3 files changed, 176 insertions(+) create mode 100644 CVE-2023-6597-TempDir-cleaning-symlink.patch diff --git a/CVE-2023-6597-TempDir-cleaning-symlink.patch b/CVE-2023-6597-TempDir-cleaning-symlink.patch new file mode 100644 index 0000000..c62fbbe --- /dev/null +++ b/CVE-2023-6597-TempDir-cleaning-symlink.patch @@ -0,0 +1,165 @@ +--- + Lib/tempfile.py | 16 + + Lib/test/test_tempfile.py | 113 ++++++++++ + Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst | 2 + 3 files changed, 131 insertions(+) + +--- a/Lib/tempfile.py ++++ b/Lib/tempfile.py +@@ -285,6 +285,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 +@@ -1781,6 +1781,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 +@@ -1955,6 +2052,22 @@ class TestTemporaryDirectory(BaseTestCas + + 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) ++ ++ 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() +--- /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/python312.changes b/python312.changes index b95e91a..d148cbe 100644 --- a/python312.changes +++ b/python312.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +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. + ------------------------------------------------------------------- Tue Feb 20 18:12:05 UTC 2024 - Matej Cepl diff --git a/python312.spec b/python312.spec index 80748f1..2efc117 100644 --- a/python312.spec +++ b/python312.spec @@ -163,6 +163,9 @@ Patch36: CVE-2023-27043-email-parsing-errors.patch # PATCH-FIX-UPSTREAM libexpat260.patch gh#python/cpython#115288 # Fix tests for XMLPullParser with Expat 2.6.0 Patch37: 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) +Patch38: CVE-2023-6597-TempDir-cleaning-symlink.patch BuildRequires: autoconf-archive BuildRequires: automake BuildRequires: fdupes