backup: add 'backup_id' to integrity protection
This prevent switching parts of backup of the same VM between different backups made by the same user (or actually: with the same passphrase). QubesOS/qubes-issues#971
This commit is contained in:
parent
418d749680
commit
4ad15c082b
@ -80,6 +80,7 @@ class BackupHeader(object):
|
||||
'compression-filter': 'compression_filter',
|
||||
'crypto-algorithm': 'crypto_algorithm',
|
||||
'hmac-algorithm': 'hmac_algorithm',
|
||||
'backup-id': 'backup_id'
|
||||
}
|
||||
bool_options = ['encrypted', 'compressed']
|
||||
int_options = ['version']
|
||||
@ -91,7 +92,8 @@ class BackupHeader(object):
|
||||
compressed=None,
|
||||
compression_filter=None,
|
||||
hmac_algorithm=None,
|
||||
crypto_algorithm=None):
|
||||
crypto_algorithm=None,
|
||||
backup_id=None):
|
||||
# repeat the list to help code completion...
|
||||
self.version = version
|
||||
self.encrypted = encrypted
|
||||
@ -101,6 +103,7 @@ class BackupHeader(object):
|
||||
self.compression_filter = compression_filter
|
||||
self.hmac_algorithm = hmac_algorithm
|
||||
self.crypto_algorithm = crypto_algorithm
|
||||
self.backup_id = backup_id
|
||||
|
||||
if header_data is not None:
|
||||
self.load(header_data)
|
||||
@ -152,6 +155,8 @@ class BackupHeader(object):
|
||||
expected_attrs += ['crypto_algorithm']
|
||||
if self.version >= 3 and self.compressed:
|
||||
expected_attrs += ['compression_filter']
|
||||
if self.version >= 4:
|
||||
expected_attrs += ['backup_id']
|
||||
for key in expected_attrs:
|
||||
if getattr(self, key) is None:
|
||||
raise qubes.exc.QubesException(
|
||||
@ -353,6 +358,10 @@ class Backup(object):
|
||||
#: callback for progress reporting. Will be called with one argument
|
||||
#: - progress in percents
|
||||
self.progress_callback = None
|
||||
#: backup ID, needs to be unique (for a given user),
|
||||
#: not necessary unpredictable; automatically generated
|
||||
self.backup_id = datetime.datetime.now().strftime(
|
||||
'%Y%m%dT%H%M%S-' + str(os.getpid()))
|
||||
|
||||
for key, value in kwargs.iteritems():
|
||||
if hasattr(self, key):
|
||||
@ -535,6 +544,7 @@ class Backup(object):
|
||||
encrypted=self.encrypted,
|
||||
compressed=self.compressed,
|
||||
compression_filter=self.compression_filter,
|
||||
backup_id=self.backup_id,
|
||||
)
|
||||
backup_header.save(header_file_path)
|
||||
# Start encrypt, scrypt will also handle integrity
|
||||
@ -722,8 +732,12 @@ class Backup(object):
|
||||
|
||||
# Start encrypt, scrypt will also handle integrity
|
||||
# protection
|
||||
scrypt_passphrase = os.path.relpath(chunkfile[:-4],
|
||||
self.tmpdir) + '!' + passphrase
|
||||
scrypt_passphrase = \
|
||||
'{backup_id}!{filename}!{passphrase}'.format(
|
||||
backup_id=self.backup_id,
|
||||
filename=os.path.relpath(chunkfile[:-4],
|
||||
self.tmpdir),
|
||||
passphrase=passphrase)
|
||||
scrypt = launch_scrypt(
|
||||
"enc", "-", chunkfile, scrypt_passphrase)
|
||||
|
||||
@ -1622,7 +1636,14 @@ class BackupRestore(object):
|
||||
fulloutput = os.path.join(self.tmpdir, output)
|
||||
else:
|
||||
fulloutput = os.path.join(self.tmpdir, origname)
|
||||
passphrase = origname + '!' + self.passphrase.encode('utf-8')
|
||||
if origname == HEADER_FILENAME:
|
||||
passphrase = origname + '!' + self.passphrase.encode('utf-8')
|
||||
else:
|
||||
passphrase = \
|
||||
'{backup_id}!{filename}!{passphrase}'.format(
|
||||
backup_id=self.header_data.backup_id,
|
||||
filename=origname,
|
||||
passphrase=self.passphrase.encode('utf-8'))
|
||||
p = launch_scrypt('dec', fullname, fulloutput, passphrase)
|
||||
(_, stderr) = p.communicate()
|
||||
if p.returncode != 0:
|
||||
|
Loading…
Reference in New Issue
Block a user