Browse Source

upgrade: --disable-tam

Marian Beermann 8 years ago
parent
commit
4d6141a607
3 changed files with 64 additions and 16 deletions
  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 bool is passed, return it. If str is passed, retrieve named attribute from args."""
     if isinstance(str_or_bool, str):
     if isinstance(str_or_bool, str):
         return getattr(args, str_or_bool)
         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
     return str_or_bool
 
 
 
 
@@ -721,29 +723,43 @@ class Archiver:
                           DASHES)
                           DASHES)
         return self.exit_code
         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):
     def do_upgrade(self, args, repository, manifest=None, key=None):
         """upgrade a repository from a previous version"""
         """upgrade a repository from a previous version"""
         if args.tam:
         if args.tam:
             manifest, key = Manifest.load(repository, force_tam_not_required=args.force)
             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
                 # The standard archive listing doesn't include the archive ID like in borg 1.1.x
                 print('Manifest contents:')
                 print('Manifest contents:')
                 for archive_info in manifest.list_archive_infos(sort_by='ts'):
                 for archive_info in manifest.list_archive_infos(sort_by='ts'):
                     print(format_archive(archive_info), '[%s]' % bin_to_hex(archive_info.id))
                     print(format_archive(archive_info), '[%s]' % bin_to_hex(archive_info.id))
+                manifest.config[b'tam_required'] = True
                 manifest.write()
                 manifest.write()
                 repository.commit()
                 repository.commit()
             if not key.tam_required:
             if not key.tam_required:
                 key.tam_required = True
                 key.tam_required = True
                 key.change_passphrase(key._passphrase)
                 key.change_passphrase(key._passphrase)
-                print('Updated key')
+                print('Key updated')
                 if hasattr(key, 'find_key'):
                 if hasattr(key, 'find_key'):
                     print('Key location:', key.find_key())
                     print('Key location:', key.find_key())
             if not tam_required(repository):
             if not tam_required(repository):
                 tam_file = tam_required_file(repository)
                 tam_file = tam_required_file(repository)
                 open(tam_file, 'w').close()
                 open(tam_file, 'w').close()
                 print('Updated security database')
                 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:
         else:
             # mainly for upgrades from Attic repositories,
             # mainly for upgrades from Attic repositories,
             # but also supports borg 0.xx -> 1.0 upgrade.
             # 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
         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.
         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
         See
         https://borgbackup.readthedocs.io/en/stable/changes.html#pre-1-0-9-manifest-spoofing-vulnerability
         https://borgbackup.readthedocs.io/en/stable/changes.html#pre-1-0-9-manifest-spoofing-vulnerability
         for details.
         for details.
@@ -1729,6 +1749,8 @@ class Archiver:
                                help="""Force upgrade""")
                                help="""Force upgrade""")
         subparser.add_argument('--tam', dest='tam', action='store_true',
         subparser.add_argument('--tam', dest='tam', action='store_true',
                                help="""Enable manifest authentication (in key and cache) (Borg 1.0.9 and later)""")
                                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='',
         subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
                                type=location_validator(archive=False),
                                type=location_validator(archive=False),
                                help='path to the repository to be upgraded')
                                help='path to the repository to be upgraded')

+ 11 - 4
borg/helpers.py

@@ -129,10 +129,17 @@ class Manifest:
         manifest.config = m[b'config']
         manifest.config = m[b'config']
         # valid item keys are whatever is known in the repo or every key we know
         # 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
         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
         return manifest, key
 
 
     def write(self):
     def write(self):

+ 28 - 9
borg/testsuite/archiver.py

@@ -1534,6 +1534,17 @@ class ArchiverCheckTestCase(ArchiverTestCaseBase):
 
 
 
 
 class ManifestAuthenticationTest(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):
     def test_fresh_init_tam_required(self):
         self.cmd('init', self.repository_location)
         self.cmd('init', self.repository_location)
         repository = Repository(self.repository_path, exclusive=True)
         repository = Repository(self.repository_path, exclusive=True)
@@ -1573,15 +1584,7 @@ class ManifestAuthenticationTest(ArchiverTestCaseBase):
         assert 'archive1234' in output
         assert 'archive1234' in output
         assert 'TAM-verified manifest' in output
         assert 'TAM-verified manifest' in output
         # Try to spoof / modify pre-1.0.9
         # 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
         # Fails
         with pytest.raises(TAMRequiredError):
         with pytest.raises(TAMRequiredError):
             self.cmd('list', self.repository_location)
             self.cmd('list', self.repository_location)
@@ -1589,6 +1592,22 @@ class ManifestAuthenticationTest(ArchiverTestCaseBase):
         self.cmd('upgrade', '--tam', '--force', self.repository_location)
         self.cmd('upgrade', '--tam', '--force', self.repository_location)
         self.cmd('list', 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')
 @pytest.mark.skipif(sys.platform == 'cygwin', reason='remote is broken on cygwin and hangs')
 class RemoteArchiverTestCase(ArchiverTestCase):
 class RemoteArchiverTestCase(ArchiverTestCase):