upgrade: --disable-tam

# Conflicts:
#	src/borg/helpers.py
#	src/borg/testsuite/archiver.py
This commit is contained in:
Marian Beermann 2016-12-18 23:32:42 +01:00
parent c7c8c0fb57
commit df40b3840c
3 changed files with 64 additions and 16 deletions

View File

@ -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')

View File

@ -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):

View File

@ -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):