Parcourir la source

upgrade: --disable-tam

# Conflicts:
#	src/borg/helpers.py
#	src/borg/testsuite/archiver.py
Marian Beermann il y a 8 ans
Parent
commit
df40b3840c
3 fichiers modifiés avec 64 ajouts et 16 suppressions
  1. 25 3
      src/borg/archiver.py
  2. 11 4
      src/borg/helpers.py
  3. 28 9
      src/borg/testsuite/archiver.py

+ 25 - 3
src/borg/archiver.py

@@ -61,6 +61,8 @@ def argument(args, str_or_bool):
     """If bool is passed, return it. If str is passed, retrieve named attribute from args."""
     if isinstance(str_or_bool, str):
         return getattr(args, str_or_bool)
+    if isinstance(str_or_bool, (list, tuple)):
+        return any(getattr(args, item) for item in str_or_bool)
     return str_or_bool
 
 
@@ -1062,29 +1064,43 @@ class Archiver:
                           DASHES, logger=logging.getLogger('borg.output.stats'))
         return self.exit_code
 
-    @with_repository(fake='tam', invert_fake=True, manifest=False, exclusive=True)
+    @with_repository(fake=('tam', 'disable_tam'), invert_fake=True, manifest=False, exclusive=True)
     def do_upgrade(self, args, repository, manifest=None, key=None):
         """upgrade a repository from a previous version"""
         if args.tam:
             manifest, key = Manifest.load(repository, force_tam_not_required=args.force)
 
-            if not manifest.tam_verified:
+            if not manifest.tam_verified or not manifest.config.get(b'tam_required', False):
                 # The standard archive listing doesn't include the archive ID like in borg 1.1.x
                 print('Manifest contents:')
                 for archive_info in manifest.archives.list(sort_by=['ts']):
                     print(format_archive(archive_info), '[%s]' % bin_to_hex(archive_info.id))
+                manifest.config[b'tam_required'] = True
                 manifest.write()
                 repository.commit()
             if not key.tam_required:
                 key.tam_required = True
                 key.change_passphrase(key._passphrase)
-                print('Updated key')
+                print('Key updated')
                 if hasattr(key, 'find_key'):
                     print('Key location:', key.find_key())
             if not tam_required(repository):
                 tam_file = tam_required_file(repository)
                 open(tam_file, 'w').close()
                 print('Updated security database')
+        elif args.disable_tam:
+            manifest, key = Manifest.load(repository, force_tam_not_required=True)
+            if tam_required(repository):
+                os.unlink(tam_required_file(repository))
+            if key.tam_required:
+                key.tam_required = False
+                key.change_passphrase(key._passphrase)
+                print('Key updated')
+                if hasattr(key, 'find_key'):
+                    print('Key location:', key.find_key())
+            manifest.config[b'tam_required'] = False
+            manifest.write()
+            repository.commit()
         else:
             # mainly for upgrades from Attic repositories,
             # but also supports borg 0.xx -> 1.0 upgrade.
@@ -2356,6 +2372,10 @@ class Archiver:
         If a repository is accidentally modified with a pre-1.0.9 client after
         this upgrade, use ``borg upgrade --tam --force REPO`` to remedy it.
 
+        If you routinely do this you might not want to enable this upgrade
+        (which will leave you exposed to the security issue). You can
+        reverse the upgrade by issuing ``borg upgrade --disable-tam REPO``.
+
         See
         https://borgbackup.readthedocs.io/en/stable/changes.html#pre-1-0-9-manifest-spoofing-vulnerability
         for details.
@@ -2419,6 +2439,8 @@ class Archiver:
                                help="""Force upgrade""")
         subparser.add_argument('--tam', dest='tam', action='store_true',
                                help="""Enable manifest authentication (in key and cache) (Borg 1.0.9 and later)""")
+        subparser.add_argument('--disable-tam', dest='disable_tam', action='store_true',
+                               help="""Disable manifest authentication (in key and cache)""")
         subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
                                type=location_validator(archive=False),
                                help='path to the repository to be upgraded')

+ 11 - 4
src/borg/helpers.py

@@ -221,10 +221,17 @@ class Manifest:
         manifest.config = m.config
         # valid item keys are whatever is known in the repo or every key we know
         manifest.item_keys = ITEM_KEYS | frozenset(key.decode() for key in m.get('item_keys', []))
-        if manifest.config.get(b'tam_required', False) and manifest.tam_verified and not tam_required(repository):
-            logger.debug('Manifest is TAM verified and says TAM is required, updating security database...')
-            file = tam_required_file(repository)
-            open(file, 'w').close()
+
+        if manifest.tam_verified:
+            manifest_required = manifest.config.get(b'tam_required', False)
+            security_required = tam_required(repository)
+            if manifest_required and not security_required:
+                logger.debug('Manifest is TAM verified and says TAM is required, updating security database...')
+                file = tam_required_file(repository)
+                open(file, 'w').close()
+            if not manifest_required and security_required:
+                logger.debug('Manifest is TAM verified and says TAM is *not* required, updating security database...')
+                os.unlink(tam_required_file(repository))
         return manifest, key
 
     def write(self):

+ 28 - 9
src/borg/testsuite/archiver.py

@@ -2283,6 +2283,17 @@ class ArchiverCheckTestCase(ArchiverTestCaseBase):
 
 
 class ManifestAuthenticationTest(ArchiverTestCaseBase):
+    def spoof_manifest(self, repository):
+        with repository:
+            _, key = Manifest.load(repository)
+            repository.put(Manifest.MANIFEST_ID, key.encrypt(Chunk(msgpack.packb({
+                'version': 1,
+                'archives': {},
+                'config': {},
+                'timestamp': (datetime.utcnow() + timedelta(days=1)).isoformat(),
+            }))))
+            repository.commit()
+
     def test_fresh_init_tam_required(self):
         self.cmd('init', self.repository_location)
         repository = Repository(self.repository_path, exclusive=True)
@@ -2322,15 +2333,7 @@ class ManifestAuthenticationTest(ArchiverTestCaseBase):
         assert 'archive1234' in output
         assert 'TAM-verified manifest' in output
         # Try to spoof / modify pre-1.0.9
-        with repository:
-            _, key = Manifest.load(repository)
-            repository.put(Manifest.MANIFEST_ID, key.encrypt(Chunk(msgpack.packb({
-                'version': 1,
-                'archives': {},
-                'config': {},
-                'timestamp': (datetime.utcnow() + timedelta(days=1)).isoformat(),
-            }))))
-            repository.commit()
+        self.spoof_manifest(repository)
         # Fails
         with pytest.raises(TAMRequiredError):
             self.cmd('list', self.repository_location)
@@ -2338,6 +2341,22 @@ class ManifestAuthenticationTest(ArchiverTestCaseBase):
         self.cmd('upgrade', '--tam', '--force', self.repository_location)
         self.cmd('list', self.repository_location)
 
+    def test_disable(self):
+        self.cmd('init', self.repository_location)
+        self.create_src_archive('archive1234')
+        self.cmd('upgrade', '--disable-tam', self.repository_location)
+        repository = Repository(self.repository_path, exclusive=True)
+        self.spoof_manifest(repository)
+        assert not self.cmd('list', self.repository_location)
+
+    def test_disable2(self):
+        self.cmd('init', self.repository_location)
+        self.create_src_archive('archive1234')
+        repository = Repository(self.repository_path, exclusive=True)
+        self.spoof_manifest(repository)
+        self.cmd('upgrade', '--disable-tam', self.repository_location)
+        assert not self.cmd('list', self.repository_location)
+
 
 @pytest.mark.skipif(sys.platform == 'cygwin', reason='remote is broken on cygwin and hangs')
 class RemoteArchiverTestCase(ArchiverTestCase):