Merge pull request #6771 from ThomasWaldmann/safe-secure-erase-1.1

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

View file

@ -2389,13 +2389,23 @@ def json_print(obj):
print(json_dump(obj))
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

@ -301,7 +301,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). "
@ -328,7 +328,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