Merge pull request #6770 from ThomasWaldmann/safe-secure-erase-master

secure_erase: avoid collateral damage, fixes #6768
This commit is contained in:
TW 2022-06-13 19:41:23 +02:00 committed by GitHub
commit 9747ce459b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 18 additions and 8 deletions

View File

@ -189,13 +189,23 @@ def scandir_inorder(*, path, fd=None):
return sorted(os.scandir(arg), key=scandir_keyfunc)
def secure_erase(path):
"""Attempt to securely erase a file by writing random data over it before deleting it."""
def secure_erase(path, *, avoid_collateral_damage):
"""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:
length = os.stat(fd.fileno()).st_size
fd.write(os.urandom(length))
fd.flush()
os.fsync(fd.fileno())
st = os.stat(fd.fileno())
if not (st.st_nlink > 1 and avoid_collateral_damage):
fd.write(os.urandom(st.st_size))
fd.flush()
os.fsync(fd.fileno())
os.unlink(path)

View File

@ -306,7 +306,7 @@ class Repository:
if os.path.isfile(old_config_path):
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):
link_error_msg = ("Failed to securely erase old repository config file (hardlinks not supported). "
@ -333,7 +333,7 @@ class Repository:
"read-only repositories." % (e.strerror, e.filename))
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):
assert self.config