Skip to content

Commit

Permalink
Merge pull request #911 from mkhon/fix/access-denied
Browse files Browse the repository at this point in the history
file_wipe(): open files with FILE_FLAG_BACKUP_SEMANTICS to skip DACL checks

WFH
  • Loading branch information
az0 committed May 9, 2020
2 parents 9166c50 + bd51f8d commit f93647a
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 10 deletions.
2 changes: 1 addition & 1 deletion bleachbit/GUI.py
Expand Up @@ -116,7 +116,7 @@ class Bleachbit(Gtk.Application):
_auto_exit = False

def __init__(self, uac=True, shred_paths=None, auto_exit=False):
if uac and os.name == 'nt' and Windows.elevate_privileges():
if os.name == 'nt' and Windows.elevate_privileges(uac):
# privileges escalated in other process
sys.exit(0)
Gtk.Application.__init__(
Expand Down
13 changes: 12 additions & 1 deletion bleachbit/Windows.py
Expand Up @@ -56,6 +56,7 @@
import win32file
import win32gui
import win32process
import win32security

from ctypes import windll, c_ulong, c_buffer, byref, sizeof
from win32com.shell import shell, shellcon
Expand Down Expand Up @@ -253,13 +254,23 @@ def detect_registry_key(parent_key):
return True


def elevate_privileges():
def elevate_privileges(uac):
"""On Windows Vista and later, try to get administrator
privileges. If successful, return True (so original process
can exit). If failed or not applicable, return False."""

if shell.IsUserAnAdmin():
logger.debug('already an admin (UAC not required)')
htoken = win32security.OpenProcessToken(
win32api.GetCurrentProcess(), win32security.TOKEN_ADJUST_PRIVILEGES | win32security.TOKEN_QUERY)
newPrivileges = [
(win32security.LookupPrivilegeValue(None, "SeBackupPrivilege"), win32security.SE_PRIVILEGE_ENABLED),
(win32security.LookupPrivilegeValue(None, "SeRestorePrivilege"), win32security.SE_PRIVILEGE_ENABLED),
]
win32security.AdjustTokenPrivileges(htoken, 0, newPrivileges)
win32file.CloseHandle(htoken)
return False
elif not uac:
return False

if hasattr(sys, 'frozen'):
Expand Down
4 changes: 2 additions & 2 deletions bleachbit/WindowsWipe.py
Expand Up @@ -108,7 +108,7 @@
FSCTL_SET_ZERO_DATA)
from win32file import (GENERIC_READ, GENERIC_WRITE, FILE_BEGIN,
FILE_SHARE_READ, FILE_SHARE_WRITE,
OPEN_EXISTING, CREATE_ALWAYS,
OPEN_EXISTING, CREATE_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS,
DRIVE_REMOTE, DRIVE_CDROM, DRIVE_UNKNOWN)
from win32con import (FILE_ATTRIBUTE_ENCRYPTED,
FILE_ATTRIBUTE_COMPRESSED,
Expand Down Expand Up @@ -432,7 +432,7 @@ def determine_win_version():
# CreateFileW gives us Unicode support.
def open_file(file_name, mode=GENERIC_READ):
file_handle = CreateFileW(file_name, mode, 0, None,
OPEN_EXISTING, 0, None)
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, None)
return file_handle


Expand Down
27 changes: 21 additions & 6 deletions tests/TestWindows.py
Expand Up @@ -374,7 +374,8 @@ def test_file_wipe(self):
"""

from bleachbit.WindowsWipe import file_wipe, open_file, close_file, file_make_sparse
from win32file import GENERIC_WRITE
from bleachbit.Windows import elevate_privileges
from win32con import GENERIC_WRITE, WRITE_DAC

dirname = tempfile.mkdtemp(prefix='bleachbit-file-wipe')

Expand All @@ -391,11 +392,24 @@ def _write_file(longname, contents):
self.assertExists(shortname)
return shortname

def _test_wipe(contents, is_sparse=False):
def _deny_access(fh):
import win32security
import ntsecuritycon as con

user, _, _ = win32security.LookupAccountName("", win32api.GetUserName())
dacl = win32security.ACL()
dacl.AddAccessDeniedAce(win32security.ACL_REVISION, con.FILE_GENERIC_READ | con.FILE_GENERIC_WRITE, user)
win32security.SetSecurityInfo(fh, win32security.SE_FILE_OBJECT, win32security.DACL_SECURITY_INFORMATION,
None, None, dacl, None)

def _test_wipe(contents, deny_access = False, is_sparse=False):
shortname = _write_file(longname, contents)
if is_sparse:
fh = open_file(extended_path(longname), mode=GENERIC_WRITE)
file_make_sparse(fh)
if deny_access or is_sparse:
fh = open_file(extended_path(longname), mode=GENERIC_WRITE | WRITE_DAC)
if is_sparse:
file_make_sparse(fh)
if deny_access:
_deny_access(fh)
close_file(fh)
logger.debug('test_file_wipe(): filename length={}, shortname length ={}, contents length={}, is_sparse={}'.format(
len(longname), len(shortname), len(contents), is_sparse))
Expand All @@ -418,7 +432,8 @@ def _test_wipe(contents, is_sparse=False):
_test_wipe(b'secret' * 100000)

# requires wiping of extents: special file case
_test_wipe(b'secret' * 100000, is_sparse=True)
elevate_privileges(False)
_test_wipe(b'secret' * 100000, deny_access = True, is_sparse=True)

shutil.rmtree(dirname, True)

Expand Down

0 comments on commit f93647a

Please sign in to comment.