1
0
Fork 0
mirror of https://github.com/borgbackup/borg.git synced 2024-12-25 17:27:31 +00:00

secure_erase: avoid collateral damage, fixes #6768

if a hardlink copy of a repo was made and a new repo config
shall be saved, do NOT fill in random garbage before deleting
the previous repo config, because that would damage the hardlink
copy.
This commit is contained in:
Thomas Waldmann 2022-06-13 15:47:50 +02:00
parent eed5038922
commit ba1f8926cc
2 changed files with 18 additions and 8 deletions

View file

@ -189,11 +189,21 @@ def scandir_inorder(*, path, fd=None):
return sorted(os.scandir(arg), key=scandir_keyfunc) return sorted(os.scandir(arg), key=scandir_keyfunc)
def secure_erase(path): def secure_erase(path, *, avoid_collateral_damage):
"""Attempt to securely erase a file by writing random data over it before deleting it.""" """Attempt to securely erase a file by writing random data over it before deleting it.
If avoid_collateral_damage is True, we only secure erase if the total link count is 1,
otherwise we just do a normal "delete" (unlink) without first overwriting it with random.
This avoids other hardlinks pointing to same inode as <path> getting damaged, but might be less secure.
A typical scenario where this is useful are quick "hardlink copies" of bigger directories.
If avoid_collateral_damage is False, we always secure erase.
If there are hardlinks pointing to the same inode as <path>, they will contain random garbage afterwards.
"""
with open(path, 'r+b') as fd: with open(path, 'r+b') as fd:
length = os.stat(fd.fileno()).st_size st = os.stat(fd.fileno())
fd.write(os.urandom(length)) if not (st.st_nlink > 1 and avoid_collateral_damage):
fd.write(os.urandom(st.st_size))
fd.flush() fd.flush()
os.fsync(fd.fileno()) os.fsync(fd.fileno())
os.unlink(path) os.unlink(path)

View file

@ -306,7 +306,7 @@ def save_config(self, path, config):
if os.path.isfile(old_config_path): if os.path.isfile(old_config_path):
logger.warning("Old config file not securely erased on previous config update") logger.warning("Old config file not securely erased on previous config update")
secure_erase(old_config_path) secure_erase(old_config_path, avoid_collateral_damage=True)
if os.path.isfile(config_path): if os.path.isfile(config_path):
link_error_msg = ("Failed to securely erase old repository config file (hardlinks not supported). " link_error_msg = ("Failed to securely erase old repository config file (hardlinks not supported). "
@ -333,7 +333,7 @@ def save_config(self, path, config):
"read-only repositories." % (e.strerror, e.filename)) "read-only repositories." % (e.strerror, e.filename))
if os.path.isfile(old_config_path): if os.path.isfile(old_config_path):
secure_erase(old_config_path) secure_erase(old_config_path, avoid_collateral_damage=True)
def save_key(self, keydata): def save_key(self, keydata):
assert self.config assert self.config