public inbox for cygwin@cygwin.com
 help / color / mirror / Atom feed
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)

      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).