浏览代码

BORG_WORKAROUNDS=authenticated_no_key to extract from authenticated repos without key, fixes #7700

Thomas Waldmann 1 年之前
父节点
当前提交
df753c0312
共有 3 个文件被更改,包括 40 次插入0 次删除
  1. 16 0
      docs/usage/general/environment.rst.inc
  2. 2 0
      src/borg/archiver.py
  3. 22 0
      src/borg/crypto/key.py

+ 16 - 0
docs/usage/general/environment.rst.inc

@@ -99,6 +99,22 @@ General:
             caused EROFS. You will need this to make archives from volume shadow copies
             caused EROFS. You will need this to make archives from volume shadow copies
             in WSL1 (Windows Subsystem for Linux 1).
             in WSL1 (Windows Subsystem for Linux 1).
 
 
+        authenticated_no_key
+            Work around a lost passphrase or key for an ``authenticated`` mode repository
+            (these are only authenticated, but not encrypted).
+            If the key is missing in the repository config, add ``key = anything`` there.
+
+            This workaround is **only** for emergencies and **only** to extract data
+            from an affected repository (read-only access)::
+
+                BORG_WORKAROUNDS=authenticated_no_key borg extract repo::archive
+
+            After you have extracted all data you need, you MUST delete the repository::
+
+                BORG_WORKAROUNDS=authenticated_no_key borg delete repo
+
+            Now you can init a fresh repo. Make sure you do not use the workaround any more.
+
 Some automatic "answerers" (if set, they automatically answer confirmation questions):
 Some automatic "answerers" (if set, they automatically answer confirmation questions):
     BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=no (or =yes)
     BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=no (or =yes)
         For "Warning: Attempting to access a previously unknown unencrypted repository"
         For "Warning: Attempting to access a previously unknown unencrypted repository"

+ 2 - 0
src/borg/archiver.py

@@ -3110,6 +3110,8 @@ class Archiver:
 
 
         If you do **not** want to encrypt the contents of your backups, but still
         If you do **not** want to encrypt the contents of your backups, but still
         want to detect malicious tampering use ``--encryption authenticated``.
         want to detect malicious tampering use ``--encryption authenticated``.
+        To normally work with ``authenticated`` repos, you will need the passphrase, but
+        there is an emergency workaround, see ``BORG_WORKAROUNDS=authenticated_no_key`` docs.
 
 
         If ``BLAKE2b`` is faster than ``SHA-256`` on your hardware, use ``--encryption authenticated-blake2``,
         If ``BLAKE2b`` is faster than ``SHA-256`` on your hardware, use ``--encryption authenticated-blake2``,
         ``--encryption repokey-blake2`` or ``--encryption keyfile-blake2``. Note: for remote backups
         ``--encryption repokey-blake2`` or ``--encryption keyfile-blake2``. Note: for remote backups

+ 22 - 0
src/borg/crypto/key.py

@@ -22,6 +22,7 @@ from ..helpers import get_limited_unpacker
 from ..helpers import bin_to_hex
 from ..helpers import bin_to_hex
 from ..helpers import prepare_subprocess_env
 from ..helpers import prepare_subprocess_env
 from ..helpers import msgpack
 from ..helpers import msgpack
+from ..helpers import workarounds
 from ..item import Key, EncryptedKey
 from ..item import Key, EncryptedKey
 from ..platform import SaveFile
 from ..platform import SaveFile
 from .nonces import NonceManager
 from .nonces import NonceManager
@@ -30,6 +31,10 @@ from .low_level import AES, bytes_to_long, bytes_to_int, num_aes_blocks, hmac_sh
 PREFIX = b'\0' * 8
 PREFIX = b'\0' * 8
 
 
 
 
+# workaround for lost passphrase or key in "authenticated" or "authenticated-blake2" mode
+AUTHENTICATED_NO_KEY = 'authenticated_no_key' in workarounds
+
+
 class NoPassphraseFailure(Error):
 class NoPassphraseFailure(Error):
     """can not acquire a passphrase: {}"""
     """can not acquire a passphrase: {}"""
 
 
@@ -228,6 +233,8 @@ class KeyBase:
         unpacker = get_limited_unpacker('manifest')
         unpacker = get_limited_unpacker('manifest')
         unpacker.feed(data)
         unpacker.feed(data)
         unpacked = unpacker.unpack()
         unpacked = unpacker.unpack()
+        if AUTHENTICATED_NO_KEY:
+            return unpacked, True  # True is a lie.
         if b'tam' not in unpacked:
         if b'tam' not in unpacked:
             if tam_required:
             if tam_required:
                 raise TAMRequiredError(self.repository._location.canonical_path())
                 raise TAMRequiredError(self.repository._location.canonical_path())
@@ -836,6 +843,19 @@ class AuthenticatedKeyBase(RepoKey):
     # It's only authenticated, not encrypted.
     # It's only authenticated, not encrypted.
     logically_encrypted = False
     logically_encrypted = False
 
 
+    def _load(self, key_data, passphrase):
+        if AUTHENTICATED_NO_KEY:
+            # fake _load if we have no key or passphrase
+            NOPE = bytes(32)  # 256 bit all-zero
+            self.repository_id = NOPE
+            self.enc_key = NOPE
+            self.enc_hmac_key = NOPE
+            self.id_key = NOPE
+            self.chunk_seed = 0
+            self.tam_required = False
+            return True
+        return super()._load(key_data, passphrase)
+
     def load(self, target, passphrase):
     def load(self, target, passphrase):
         success = super().load(target, passphrase)
         success = super().load(target, passphrase)
         self.logically_encrypted = False
         self.logically_encrypted = False
@@ -867,6 +887,8 @@ class AuthenticatedKeyBase(RepoKey):
         if not decompress:
         if not decompress:
             return payload
             return payload
         data = self.decompress(payload)
         data = self.decompress(payload)
+        if AUTHENTICATED_NO_KEY:
+            return data
         self.assert_id(id, data)
         self.assert_id(id, data)
         return data
         return data