From: John Ruckstuhl <john.ruckstuhl@gmail.com>
To: cygwin@cygwin.com, John.Ruckstuhl@gmail.com
Subject: Re: cygwin utils can access directory and its contents, but W10 utils claim to have no access, why?
Date: Tue, 2 Apr 2024 19:07:54 -0700 [thread overview]
Message-ID: <CAOBROv0RwvjeCFZ0W0C4LA4i56P8W+QmWbgMn7MfVFY5yo_OCw@mail.gmail.com> (raw)
In-Reply-To: <CAOBROv25QD8w703nim4yU0EzXz-Gw3WzrsUicYbm6q9u5y0ozQ@mail.gmail.com>
On Mon, Jan 22, 2024 at 10:59 AM John Ruckstuhl
<john.ruckstuhl@gmail.com> wrote:
>
> Thanks for the replies Brian & Corinna, I learned a lot.
>
> On Mon, Jan 22, 2024 at 1:48 AM Corinna Vinschen
> <corinna-cygwin@cygwin.com> wrote:
> > Users in the Administrators group have these privileges in their user
> > token. Under UAC, both privileges are removed from the token. In an
> > elevated shell, though, both privileges are present.
> >
> > The funny thing here is this: While both privileges are present in the
> > token, they are disabled by default.
> >
> > They have to be enabled explicitely before you can exercise the
> > privileges. Usually you do this in the same application
> > programatically.
>
> Now I see...
> Local administrator Bob reports his User Access Token info
> (with Windows whoami, not cygwin whoami)
> C:\>whoami /priv
>
> PRIVILEGES INFORMATION
> ----------------------
>
> Privilege Name ... State
> ========================================= ... ========
> ...
> SeBackupPrivilege ... Disabled
> SeRestorePrivilege ... Disabled
> ...
>
> C:\>
>
> Thanks very much.
> John Ruckstuhl
The clue about UAT and disabled privileges was crucial.
Thank you again, Corinna.
I'm still doing my cleanup with ordinary Python (not the Cygwin Python
linked to the cygwin dll).
But I wrote an EnablePrivilege function to enable the privs if if necessary.
Best regards,
John Ruckstuhl
1 """
2 This module exports functions for removing old files and directories.
3
4 Functions
5 myremove -- Remove the file or dir, and return the number
of bytes freed.
6 """
7
8 # standard library imports
9 import logging
10 import os
11 import shutil
12 import subprocess
13
14 # related third party imports
15 import win32api
16 import win32security
17
18 # no local application/library specific imports
19
20
21 logger = logging.getLogger(__name__)
22
23 # this custom startupinfo is used to suppress display of the
subprocess window
24 startupinfo = subprocess.STARTUPINFO()
25 startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
26
27
28 def _remove(path):
29 """Remove the file or dir, and return the number of bytes freed."""
30
31 ...
32
33
34 def myremove(path):
35 """Remove the file or dir, and return the number of bytes freed.
36
37 Attempt removal different ways, until successful (or defeated).
38
39 1. Attempt call _remove (without additional prep).
40 2. Attempt to enable privileges SeBackupPrivilege and
41 SeRestorePrivilege, then call _remove.
42 3. Attempt to takeown, then call _remove.
43
44 The enable privileges approach is ~6x faster than the
takeown approach,
45 because takeown of an MEI dir containing ~1K files takes
time (8-9 sec).
46
47 Example results, enable privs approach
48 MEI dirs: 113/113 removed, 2047.5 MB recovered in 2.0 minutes
49
50 Example results, takeown approach
51 MEI dirs: 126/126 removed, 2453.8 MB recovered in 15.0 minutes
52
53 By default, the User Access Token for a member of local group
54 Administrators has the privileges SeBackupPrivilege and
SeRestorePrivilege,
55 but they are disabled.
56
57 The MEI dirs are typically ~1000 files occupying ~20 MB.
58 The MEI dirs are created by users running the pyinstaller -generated
59 executables ...
60
61 Presumably the process that calls this function is run as a user
62 which is a member of local group "Administrators". So
this process will
63 have privileges SeBackupPrivilege and SeRestorePrivilege granted but
64 disabled. Also this process will be able to take
ownership, if needed.
65 """
66
67 logger.debug('%s(%r)', 'myremove', path)
68
69 # first try, attempt _remove
70 try:
71 logger.debug('%s(%r)', '_remove', path)
72 nbytes = _remove(path)
73 return nbytes
74 except WindowsError as e:
75 if e.args == (5, 'Access is denied'):
76 # report the exception and carry on to next attempt
77 logger.debug(e)
78 else:
79 # re-raise any other WindowsError
80 raise
81
82 # second try, attempt enable privs then _remove
83 try:
84 logger.debug('%s(%r)', '_remove', path)
85 EnablePrivilege(win32security.SE_BACKUP_NAME)
86 EnablePrivilege(win32security.SE_RESTORE_NAME)
87 nbytes = _remove(path)
88 return nbytes
89 except Exception as e:
90 # report the exception and carry on to next attempt
91 logger.debug(e)
92
93 # third try, attempt takeown then _remove
94 try:
95 logger.debug('%s(%r)', '_remove', path)
96 takeown(path)
97 nbytes = _remove(path)
98 return nbytes
99 except Exception as e:
100 # report the exception and carry on ...
101 logger.debug(e)
102
103 # accept defeat
104 raise
105
106
107 def takeown(path):
108 subprocess.check_call(
109 [
110 'takeown',
111 '/A',
112 '/F', path.replace('/', '\\'),
113 '/R',
114 '/D', 'Y',
115 ],
116 startupinfo=startupinfo)
117
118
119 def EnablePrivilege(privilege):
120 """Enable a privilege, if it's already granted in the User
Access Token.
121
122 From [1]:
123 When a user holds a privilege, it allows that user to
do things that
124 other users without that privilege are not allowed to
do. For example,
125 the SeBackupPrivilege allows a user to read any file,
even if the
126 security descriptor denies access.
127 But just having the SeBackupPrivilege is not enough:
it needs to be
128 enabled
129
130 [1]
https://blog.didierstevens.com/2021/07/19/using-sebackupprivilege-with-python
# noqa: E501
131 """
132 hToken = win32security.OpenProcessToken(
133 win32api.GetCurrentProcess(),
134 win32security.TOKEN_ADJUST_PRIVILEGES |
win32security.TOKEN_QUERY
135 )
136 win32security.AdjustTokenPrivileges(
137 hToken,
138 0,
139 [(
140 win32security.LookupPrivilegeValue(None, privilege),
141 win32security.SE_PRIVILEGE_ENABLED
142 )]
143 )
144 win32api.CloseHandle(hToken)
prev parent reply other threads:[~2024-04-03 2:08 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-01-22 6:18 John Ruckstuhl
2024-01-22 7:33 ` Brian Inglis
2024-01-22 9:48 ` Corinna Vinschen
2024-01-22 18:59 ` John Ruckstuhl
2024-04-03 2:07 ` John Ruckstuhl [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CAOBROv0RwvjeCFZ0W0C4LA4i56P8W+QmWbgMn7MfVFY5yo_OCw@mail.gmail.com \
--to=john.ruckstuhl@gmail.com \
--cc=cygwin@cygwin.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).