backup/restore: add option to use uncommon compression filter anyway
Previous commit introduced protection against uncommon (potentially malicious) compression filters. This breaks restoring backups made with a custom compression filter. Add an option to override this check, by naming compression filter to use explicitly.
This commit is contained in:
parent
10f15e6669
commit
14f77860bf
@ -72,6 +72,13 @@ Options
|
||||
this limit and restore such (broken, or potentially malicious) backup
|
||||
anyway.
|
||||
|
||||
.. option:: --compression-filter, -Z
|
||||
|
||||
Force specific compression filter, instead of the one named in the backup
|
||||
header. The compression filter is a command that accepts ``-d`` option to
|
||||
decompress data on stdin and output it to stdout. This can be used to
|
||||
override built-in protection against uncommon compression.
|
||||
|
||||
.. option:: --dest-vm=APPVM, -d APPVM
|
||||
|
||||
Restore from a backup located in a specific AppVM
|
||||
|
@ -192,6 +192,11 @@ class BackupHeader(object):
|
||||
else:
|
||||
raise QubesException("Unrecognized header type")
|
||||
if not header.validator(value):
|
||||
if key == 'compression-filter':
|
||||
raise QubesException(
|
||||
"Unusual compression filter '{f}' found. Use "
|
||||
"--compression-filter={f} to use it anyway.".format(
|
||||
f=value))
|
||||
raise QubesException("Invalid value for header: {}".format(key))
|
||||
setattr(self, header.field, value)
|
||||
|
||||
@ -902,7 +907,8 @@ class BackupRestore(object):
|
||||
self.subdir = subdir
|
||||
self.username = os.path.basename(subdir)
|
||||
|
||||
def __init__(self, app, backup_location, backup_vm, passphrase):
|
||||
def __init__(self, app, backup_location, backup_vm, passphrase,
|
||||
force_compression_filter=None):
|
||||
super(BackupRestore, self).__init__()
|
||||
|
||||
#: qubes.Qubes instance
|
||||
@ -919,6 +925,10 @@ class BackupRestore(object):
|
||||
#: backup path, inside VM pointed by :py:attr:`backup_vm`
|
||||
self.backup_location = backup_location
|
||||
|
||||
#: force using specific application for (de)compression, instead of
|
||||
#: the one named in the backup header
|
||||
self.force_compression_filter = force_compression_filter
|
||||
|
||||
#: passphrase protecting backup integrity and optionally decryption
|
||||
self.passphrase = passphrase
|
||||
|
||||
@ -1252,7 +1262,9 @@ class BackupRestore(object):
|
||||
"failed). Is the password correct?")
|
||||
filename = os.path.join(self.tmpdir, filename)
|
||||
with open(filename, 'rb') as f_header:
|
||||
header_data = BackupHeader(f_header.read())
|
||||
header_data = BackupHeader(
|
||||
f_header.read(),
|
||||
compression_filter=self.force_compression_filter)
|
||||
os.unlink(filename)
|
||||
|
||||
return header_data
|
||||
|
@ -58,7 +58,8 @@ class TC_00_qvm_backup_restore(qubesadmin.tests.QubesTestCase):
|
||||
mock_handle_broken.assert_called_once_with(
|
||||
self.app, mock.ANY, mock_restore_info)
|
||||
mock_backup.assert_called_once_with(
|
||||
self.app, '/some/path', None, 'testpass')
|
||||
self.app, '/some/path', None, 'testpass',
|
||||
force_compression_filter=None)
|
||||
self.assertAllCalled()
|
||||
|
||||
@mock.patch('qubesadmin.tools.qvm_backup_restore.input', create=True)
|
||||
@ -92,7 +93,8 @@ class TC_00_qvm_backup_restore(qubesadmin.tests.QubesTestCase):
|
||||
qubesadmin.tools.qvm_backup_restore.main(['/some/path', 'test-vm'],
|
||||
app=self.app)
|
||||
mock_backup.assert_called_once_with(
|
||||
self.app, '/some/path', None, 'testpass')
|
||||
self.app, '/some/path', None, 'testpass',
|
||||
force_compression_filter=None)
|
||||
self.assertEqual(mock_backup.return_value.options.exclude, ['test-vm2'])
|
||||
self.assertAllCalled()
|
||||
|
||||
|
@ -72,6 +72,11 @@ parser.add_argument("--ignore-size-limit", action="store_true",
|
||||
dest="ignore_size_limit", default=False,
|
||||
help="Ignore size limit calculated from backup metadata")
|
||||
|
||||
parser.add_argument("--compression-filter", "-Z", action="store",
|
||||
dest="compression",
|
||||
help="Force specific compression filter program, "
|
||||
"instead of the one from the backup header")
|
||||
|
||||
parser.add_argument("-d", "--dest-vm", action="store", dest="appvm",
|
||||
help="Specify VM containing the backup to be restored")
|
||||
|
||||
@ -213,7 +218,8 @@ def main(args=None, app=None):
|
||||
|
||||
try:
|
||||
backup = BackupRestore(args.app, args.backup_location,
|
||||
appvm, passphrase)
|
||||
appvm, passphrase,
|
||||
force_compression_filter=args.compression)
|
||||
except qubesadmin.exc.QubesException as e:
|
||||
parser.error_runtime(str(e))
|
||||
# unreachable - error_runtime will raise SystemExit
|
||||
|
Loading…
Reference in New Issue
Block a user