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',
|
'compression-filter': 'compression_filter',
|
||||||
'crypto-algorithm': 'crypto_algorithm',
|
'crypto-algorithm': 'crypto_algorithm',
|
||||||
'hmac-algorithm': 'hmac_algorithm',
|
'hmac-algorithm': 'hmac_algorithm',
|
||||||
|
'backup-id': 'backup_id'
|
||||||
}
|
}
|
||||||
bool_options = ['encrypted', 'compressed']
|
bool_options = ['encrypted', 'compressed']
|
||||||
int_options = ['version']
|
int_options = ['version']
|
||||||
@ -91,7 +92,8 @@ class BackupHeader(object):
|
|||||||
compressed=None,
|
compressed=None,
|
||||||
compression_filter=None,
|
compression_filter=None,
|
||||||
hmac_algorithm=None,
|
hmac_algorithm=None,
|
||||||
crypto_algorithm=None):
|
crypto_algorithm=None,
|
||||||
|
backup_id=None):
|
||||||
# repeat the list to help code completion...
|
# repeat the list to help code completion...
|
||||||
self.version = version
|
self.version = version
|
||||||
self.encrypted = encrypted
|
self.encrypted = encrypted
|
||||||
@ -101,6 +103,7 @@ class BackupHeader(object):
|
|||||||
self.compression_filter = compression_filter
|
self.compression_filter = compression_filter
|
||||||
self.hmac_algorithm = hmac_algorithm
|
self.hmac_algorithm = hmac_algorithm
|
||||||
self.crypto_algorithm = crypto_algorithm
|
self.crypto_algorithm = crypto_algorithm
|
||||||
|
self.backup_id = backup_id
|
||||||
|
|
||||||
if header_data is not None:
|
if header_data is not None:
|
||||||
self.load(header_data)
|
self.load(header_data)
|
||||||
@ -152,6 +155,8 @@ class BackupHeader(object):
|
|||||||
expected_attrs += ['crypto_algorithm']
|
expected_attrs += ['crypto_algorithm']
|
||||||
if self.version >= 3 and self.compressed:
|
if self.version >= 3 and self.compressed:
|
||||||
expected_attrs += ['compression_filter']
|
expected_attrs += ['compression_filter']
|
||||||
|
if self.version >= 4:
|
||||||
|
expected_attrs += ['backup_id']
|
||||||
for key in expected_attrs:
|
for key in expected_attrs:
|
||||||
if getattr(self, key) is None:
|
if getattr(self, key) is None:
|
||||||
raise qubes.exc.QubesException(
|
raise qubes.exc.QubesException(
|
||||||
@ -353,6 +358,10 @@ class Backup(object):
|
|||||||
#: callback for progress reporting. Will be called with one argument
|
#: callback for progress reporting. Will be called with one argument
|
||||||
#: - progress in percents
|
#: - progress in percents
|
||||||
self.progress_callback = None
|
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():
|
for key, value in kwargs.iteritems():
|
||||||
if hasattr(self, key):
|
if hasattr(self, key):
|
||||||
@ -535,6 +544,7 @@ class Backup(object):
|
|||||||
encrypted=self.encrypted,
|
encrypted=self.encrypted,
|
||||||
compressed=self.compressed,
|
compressed=self.compressed,
|
||||||
compression_filter=self.compression_filter,
|
compression_filter=self.compression_filter,
|
||||||
|
backup_id=self.backup_id,
|
||||||
)
|
)
|
||||||
backup_header.save(header_file_path)
|
backup_header.save(header_file_path)
|
||||||
# Start encrypt, scrypt will also handle integrity
|
# Start encrypt, scrypt will also handle integrity
|
||||||
@ -722,8 +732,12 @@ class Backup(object):
|
|||||||
|
|
||||||
# Start encrypt, scrypt will also handle integrity
|
# Start encrypt, scrypt will also handle integrity
|
||||||
# protection
|
# protection
|
||||||
scrypt_passphrase = os.path.relpath(chunkfile[:-4],
|
scrypt_passphrase = \
|
||||||
self.tmpdir) + '!' + passphrase
|
'{backup_id}!{filename}!{passphrase}'.format(
|
||||||
|
backup_id=self.backup_id,
|
||||||
|
filename=os.path.relpath(chunkfile[:-4],
|
||||||
|
self.tmpdir),
|
||||||
|
passphrase=passphrase)
|
||||||
scrypt = launch_scrypt(
|
scrypt = launch_scrypt(
|
||||||
"enc", "-", chunkfile, scrypt_passphrase)
|
"enc", "-", chunkfile, scrypt_passphrase)
|
||||||
|
|
||||||
@ -1622,7 +1636,14 @@ class BackupRestore(object):
|
|||||||
fulloutput = os.path.join(self.tmpdir, output)
|
fulloutput = os.path.join(self.tmpdir, output)
|
||||||
else:
|
else:
|
||||||
fulloutput = os.path.join(self.tmpdir, origname)
|
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)
|
p = launch_scrypt('dec', fullname, fulloutput, passphrase)
|
||||||
(_, stderr) = p.communicate()
|
(_, stderr) = p.communicate()
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
|
Loading…
Reference in New Issue
Block a user