diff --git a/docs/usage.rst b/docs/usage.rst index 51fa600a8..8410d1156 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -189,9 +189,10 @@ Directories and files: Default to '~/.config/borg/keys'. This directory contains keys for encrypted repositories. BORG_KEY_FILE When set, use the given filename as repository key file. - BORG_NONCES_DIR - Default to '~/.config/borg/key-nonces'. This directory contains information borg uses to - track its usage of NONCES ("numbers used once" - usually in encryption context). + BORG_SECURITY_DIR + Default to '~/.config/borg/security'. This directory contains information borg uses to + track its usage of NONCES ("numbers used once" - usually in encryption context) and other + security relevant data. BORG_CACHE_DIR Default to '~/.cache/borg'. This directory contains the local cache and might need a lot of space for dealing with big repositories). diff --git a/src/borg/helpers.py b/src/borg/helpers.py index b3a48d16e..93413845b 100644 --- a/src/borg/helpers.py +++ b/src/borg/helpers.py @@ -288,15 +288,17 @@ def get_keys_dir(): return keys_dir -def get_nonces_dir(): - """Determine where to store the local nonce high watermark""" +def get_security_dir(repository_id=None): + """Determine where to store local security information.""" xdg_config = os.environ.get('XDG_CONFIG_HOME', os.path.join(get_home_dir(), '.config')) - nonces_dir = os.environ.get('BORG_NONCES_DIR', os.path.join(xdg_config, 'borg', 'key-nonces')) - if not os.path.exists(nonces_dir): - os.makedirs(nonces_dir) - os.chmod(nonces_dir, stat.S_IRWXU) - return nonces_dir + security_dir = os.environ.get('BORG_SECURITY_DIR', os.path.join(xdg_config, 'borg', 'security')) + if repository_id: + security_dir = os.path.join(security_dir, repository_id) + if not os.path.exists(security_dir): + os.makedirs(security_dir) + os.chmod(security_dir, stat.S_IRWXU) + return security_dir def get_cache_dir(): diff --git a/src/borg/nonces.py b/src/borg/nonces.py index 4f9299581..e6eb7a2c7 100644 --- a/src/borg/nonces.py +++ b/src/borg/nonces.py @@ -3,7 +3,7 @@ import sys from binascii import unhexlify from .crypto import bytes_to_long, long_to_bytes -from .helpers import get_nonces_dir +from .helpers import get_security_dir from .helpers import bin_to_hex from .platform import SaveFile from .remote import InvalidRPCMethod @@ -19,7 +19,7 @@ class NonceManager: self.enc_cipher = enc_cipher self.end_of_nonce_reservation = None self.manifest_nonce = manifest_nonce - self.nonce_file = os.path.join(get_nonces_dir(), self.repository.id_str) + self.nonce_file = os.path.join(get_security_dir(self.repository.id_str), 'nonce') def get_local_free_nonce(self): try: diff --git a/src/borg/testsuite/helpers.py b/src/borg/testsuite/helpers.py index f41cb4425..a66d681f7 100644 --- a/src/borg/testsuite/helpers.py +++ b/src/borg/testsuite/helpers.py @@ -15,7 +15,7 @@ from ..helpers import Buffer from ..helpers import partial_format, format_file_size, parse_file_size, format_timedelta, format_line, PlaceholderError, replace_placeholders from ..helpers import make_path_safe, clean_lines from ..helpers import prune_within, prune_split -from ..helpers import get_cache_dir, get_keys_dir, get_nonces_dir +from ..helpers import get_cache_dir, get_keys_dir, get_security_dir from ..helpers import is_slow_msgpack from ..helpers import yes, TRUISH, FALSISH, DEFAULTISH from ..helpers import StableDict, int_to_bigint, bigint_to_int, bin_to_hex @@ -660,14 +660,15 @@ def test_get_keys_dir(monkeypatch): assert get_keys_dir() == '/var/tmp' -def test_get_nonces_dir(monkeypatch): - """test that get_nonces_dir respects environment""" +def test_get_security_dir(monkeypatch): + """test that get_security_dir respects environment""" monkeypatch.delenv('XDG_CONFIG_HOME', raising=False) - assert get_nonces_dir() == os.path.join(os.path.expanduser('~'), '.config', 'borg', 'key-nonces') + assert get_security_dir() == os.path.join(os.path.expanduser('~'), '.config', 'borg', 'security') + assert get_security_dir(repository_id='1234') == os.path.join(os.path.expanduser('~'), '.config', 'borg', 'security', '1234') monkeypatch.setenv('XDG_CONFIG_HOME', '/var/tmp/.config') - assert get_nonces_dir() == os.path.join('/var/tmp/.config', 'borg', 'key-nonces') - monkeypatch.setenv('BORG_NONCES_DIR', '/var/tmp') - assert get_nonces_dir() == '/var/tmp' + assert get_security_dir() == os.path.join('/var/tmp/.config', 'borg', 'security') + monkeypatch.setenv('BORG_SECURITY_DIR', '/var/tmp') + assert get_security_dir() == '/var/tmp' def test_file_size(): diff --git a/src/borg/testsuite/key.py b/src/borg/testsuite/key.py index 5f456b0bf..0702301e2 100644 --- a/src/borg/testsuite/key.py +++ b/src/borg/testsuite/key.py @@ -10,7 +10,7 @@ from ..crypto import bytes_to_long, num_aes_blocks from ..helpers import Location from ..helpers import Chunk from ..helpers import IntegrityError -from ..helpers import get_nonces_dir +from ..helpers import get_security_dir from ..key import PlaintextKey, PassphraseKey, KeyfileKey, RepoKey, Blake2KeyfileKey, Blake2RepoKey, AuthenticatedKey from ..key import Passphrase, PasswordRetriesExceeded, bin_to_hex @@ -118,7 +118,7 @@ class TestKey: def test_keyfile_nonce_rollback_protection(self, monkeypatch, keys_dir): monkeypatch.setenv('BORG_PASSPHRASE', 'test') repository = self.MockRepository() - with open(os.path.join(get_nonces_dir(), repository.id_str), "w") as fd: + with open(os.path.join(get_security_dir(repository.id_str), 'nonce'), "w") as fd: fd.write("0000000000002000") key = KeyfileKey.create(repository, self.MockArgs()) data = key.encrypt(Chunk(b'ABC')) diff --git a/src/borg/testsuite/nonces.py b/src/borg/testsuite/nonces.py index 14d1f52d5..d88d260aa 100644 --- a/src/borg/testsuite/nonces.py +++ b/src/borg/testsuite/nonces.py @@ -2,7 +2,7 @@ import os.path import pytest -from ..helpers import get_nonces_dir +from ..helpers import get_security_dir from ..key import bin_to_hex from ..nonces import NonceManager from ..remote import InvalidRPCMethod @@ -61,11 +61,11 @@ class TestNonceManager: self.repository = None def cache_nonce(self): - with open(os.path.join(get_nonces_dir(), self.repository.id_str), "r") as fd: + with open(os.path.join(get_security_dir(self.repository.id_str), 'nonce'), "r") as fd: return fd.read() def set_cache_nonce(self, nonce): - with open(os.path.join(get_nonces_dir(), self.repository.id_str), "w") as fd: + with open(os.path.join(get_security_dir(self.repository.id_str), 'nonce'), "w") as fd: assert fd.write(nonce) def test_empty_cache_and_old_server(self, monkeypatch):