diff --git a/django/core/cache/backends/filebased.py b/django/core/cache/backends/filebased.py index 48b8df90ab..64cc175b18 100644 --- a/django/core/cache/backends/filebased.py +++ b/django/core/cache/backends/filebased.py @@ -113,7 +113,13 @@ class FileBasedCache(BaseCache): self._delete(fname) def _createdir(self): - os.makedirs(self._dir, 0o700, exist_ok=True) + # Set the umask because os.makedirs() doesn't apply the "mode" argument + # to intermediate-level directories. + old_umask = os.umask(0o077) + try: + os.makedirs(self._dir, 0o700, exist_ok=True) + finally: + os.umask(old_umask) def _key_to_file(self, key, version=None): """ diff --git a/docs/releases/2.2.16.txt b/docs/releases/2.2.16.txt index f0c3ec894a..f531871d1a 100644 --- a/docs/releases/2.2.16.txt +++ b/docs/releases/2.2.16.txt @@ -4,7 +4,7 @@ Django 2.2.16 release notes *Expected September 1, 2020* -Django 2.2.16 fixes a security issue and two data loss bugs in 2.2.15. +Django 2.2.16 fixes two security issues and two data loss bugs in 2.2.15. CVE-2020-24583: Incorrect permissions on intermediate-level directories on Python 3.7+ ====================================================================================== @@ -17,6 +17,13 @@ files and to intermediate-level collected static directories when using the You should review and manually fix permissions on existing intermediate-level directories. +CVE-2020-24584: Permission escalation in intermediate-level directories of the file system cache on Python 3.7+ +=============================================================================================================== + +On Python 3.7+, the intermediate-level directories of the file system cache had +the system's standard umask rather than ``0o077`` (no group or others +permissions). + Bugfixes ======== diff --git a/docs/releases/3.0.10.txt b/docs/releases/3.0.10.txt index 4238a9fd71..ebc67d4725 100644 --- a/docs/releases/3.0.10.txt +++ b/docs/releases/3.0.10.txt @@ -4,7 +4,7 @@ Django 3.0.10 release notes *Expected September 1, 2020* -Django 3.0.10 fixes a security issue and two data loss bugs in 3.0.9. +Django 3.0.10 fixes two security issues and two data loss bugs in 3.0.9. CVE-2020-24583: Incorrect permissions on intermediate-level directories on Python 3.7+ ====================================================================================== @@ -17,6 +17,13 @@ files and to intermediate-level collected static directories when using the You should review and manually fix permissions on existing intermediate-level directories. +CVE-2020-24584: Permission escalation in intermediate-level directories of the file system cache on Python 3.7+ +=============================================================================================================== + +On Python 3.7+, the intermediate-level directories of the file system cache had +the system's standard umask rather than ``0o077`` (no group or others +permissions). + Bugfixes ======== diff --git a/tests/cache/tests.py b/tests/cache/tests.py index d8ea676875..c0f90b8326 100644 --- a/tests/cache/tests.py +++ b/tests/cache/tests.py @@ -6,11 +6,13 @@ import os import pickle import re import shutil +import sys import tempfile import threading import time import unittest -from unittest import mock +from pathlib import Path +from unittest import mock, skipIf from django.conf import settings from django.core import management, signals @@ -1443,6 +1445,28 @@ class FileBasedCacheTests(BaseCacheTests, TestCase): # Returns the default instead of erroring. self.assertEqual(cache.get('foo', 'baz'), 'baz') + @skipIf( + sys.platform == 'win32', + 'Windows only partially supports umasks and chmod.', + ) + def test_cache_dir_permissions(self): + os.rmdir(self.dirname) + dir_path = Path(self.dirname) / 'nested' / 'filebasedcache' + for cache_params in settings.CACHES.values(): + cache_params['LOCATION'] = dir_path + setting_changed.send(self.__class__, setting='CACHES', enter=False) + cache.set('foo', 'bar') + self.assertIs(dir_path.exists(), True) + tests = [ + dir_path, + dir_path.parent, + dir_path.parent.parent, + ] + for directory in tests: + with self.subTest(directory=directory): + dir_mode = directory.stat().st_mode & 0o777 + self.assertEqual(dir_mode, 0o700) + def test_get_does_not_ignore_non_filenotfound_exceptions(self): with mock.patch('builtins.open', side_effect=OSError): with self.assertRaises(OSError):