Headline
CVE-2022-23651: Merge pull request from GHSA-p867-fxfr-ph2w · Backblaze/b2-sdk-python@6247663
b2-sdk-python is a python library to access cloud storage provided by backblaze. Linux and Mac releases of the SDK version 1.14.0 and below contain a key disclosure vulnerability that, in certain conditions, can be exploited by local attackers through a time-of-check-time-of-use (TOCTOU) race condition. SDK users of the SqliteAccountInfo format are vulnerable while users of the InMemoryAccountInfo format are safe. The SqliteAccountInfo saves API keys (and bucket name-to-id mapping) in a local database file ($XDG_CONFIG_HOME/b2/account_info, ~/.b2_account_info or a user-defined path). When first created, the file is world readable and is (typically a few milliseconds) later altered to be private to the user. If the directory containing the file is readable by a local attacker then during the brief period between file creation and permission modification, a local attacker can race to open the file and maintain a handle to it. This allows the local attacker to read the contents after the file after the sensitive information has been saved to it. Consumers of this SDK who rely on it to save data using SqliteAccountInfo class should upgrade to the latest version of the SDK. Those who believe a local user might have opened a handle using this race condition, should remove the affected database files and regenerate all application keys. Users should upgrade to b2-sdk-python 1.14.1 or later.
@@ -14,6 +14,7 @@ import os import platform import shutil import stat import tempfile
import pytest @@ -319,15 +320,29 @@ def setUp(self, request): os.unlink(self.db_path) except OSError: pass self.home = tempfile.mkdtemp() self.test_home = tempfile.mkdtemp()
yield for cleanup_method in [lambda: os.unlink(self.db_path), lambda: shutil.rmtree(self.home)]: for cleanup_method in [ lambda: os.unlink(self.db_path), lambda: shutil.rmtree(self.test_home) ]: try: cleanup_method cleanup_method() except OSError: pass
@pytest.mark.skipif( platform.system() == ‘Windows’, reason=’different permission system on Windows’ ) def test_permissions(self): “"” Test that a new database won’t be readable by just any user “"” s = SqliteAccountInfo(file_name=self.db_path,) mode = os.stat(self.db_path).st_mode assert stat.filemode(mode) == '-rw-------'
def test_corrupted(self): “"” Test that a corrupted file will be replaced with a blank file. @@ -371,7 +386,7 @@ def _make_sqlite_account_info(self, env=None, last_upgrade_to_run=None): :param dict env: Override Environment variables. “"” # Override HOME to ensure hermetic tests with mock.patch('os.environ’, env or {’HOME’: self.home}): with mock.patch('os.environ’, env or {’HOME’: self.test_home}): return SqliteAccountInfo( file_name=self.db_path if not env else None, last_upgrade_to_run=last_upgrade_to_run, @@ -380,24 +395,24 @@ def _make_sqlite_account_info(self, env=None, last_upgrade_to_run=None): def test_uses_default(self): account_info = self._make_sqlite_account_info( env={ 'HOME’: self.home, 'USERPROFILE’: self.home, 'HOME’: self.test_home, 'USERPROFILE’: self.test_home, } ) actual_path = os.path.abspath(account_info.filename) assert os.path.join(self.home, ‘.b2_account_info’) == actual_path assert os.path.join(self.test_home, ‘.b2_account_info’) == actual_path
def test_uses_xdg_config_home(self, apiver): with WindowsSafeTempDir() as d: account_info = self._make_sqlite_account_info( env={ 'HOME’: self.home, 'USERPROFILE’: self.home, 'HOME’: self.test_home, 'USERPROFILE’: self.test_home, XDG_CONFIG_HOME_ENV_VAR: d, } ) if apiver in ['v0’, ‘v1’]: expected_path = os.path.abspath(os.path.join(self.home, ‘.b2_account_info’)) expected_path = os.path.abspath(os.path.join(self.test_home, ‘.b2_account_info’)) else: assert os.path.exists(os.path.join(d, ‘b2’)) expected_path = os.path.abspath(os.path.join(d, 'b2’, ‘account_info’)) @@ -406,12 +421,12 @@ def test_uses_xdg_config_home(self, apiver):
def test_uses_existing_file_and_ignores_xdg(self): with WindowsSafeTempDir() as d: default_db_file_location = os.path.join(self.home, ‘.b2_account_info’) default_db_file_location = os.path.join(self.test_home, ‘.b2_account_info’) open(default_db_file_location, ‘a’).close() account_info = self._make_sqlite_account_info( env={ 'HOME’: self.home, 'USERPROFILE’: self.home, 'HOME’: self.test_home, 'USERPROFILE’: self.test_home, XDG_CONFIG_HOME_ENV_VAR: d, } ) @@ -423,8 +438,8 @@ def test_account_info_env_var_overrides_xdg_config_home(self): with WindowsSafeTempDir() as d: account_info = self._make_sqlite_account_info( env={ 'HOME’: self.home, 'USERPROFILE’: self.home, 'HOME’: self.test_home, 'USERPROFILE’: self.test_home, XDG_CONFIG_HOME_ENV_VAR: d, B2_ACCOUNT_INFO_ENV_VAR: os.path.join(d, ‘b2_account_info’), }