浏览代码

upgrade: --disable-tam

Marian Beermann 8 年之前
父节点
当前提交
4d6141a607
共有 3 个文件被更改,包括 64 次插入16 次删除
  1. 25 3
      borg/archiver.py
  2. 11 4
      borg/helpers.py
  3. 28 9
      borg/testsuite/archiver.py

+ 25 - 3
borg/archiver.py

@@ -48,6 +48,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
 
 
@@ -721,29 +723,43 @@ class Archiver:
                           DASHES)
         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.list_archive_infos(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.
@@ -1666,6 +1682,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.
@@ -1729,6 +1749,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
borg/helpers.py

@@ -129,10 +129,17 @@ class Manifest:
         manifest.config = m[b'config']
         # valid item keys are whatever is known in the repo or every key we know
         manifest.item_keys = frozenset(m.get(b'item_keys', [])) | 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
borg/testsuite/archiver.py

@@ -1534,6 +1534,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(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)
@@ -1573,15 +1584,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(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)
@@ -1589,6 +1592,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):