On Wed, 8 Jan 2025 06:14:24 +0900
Takashi Yano wrote:
> Hi Corinna,
> 
> On Tue, 7 Jan 2025 17:48:59 +0100
> Corinna Vinschen wrote:
> > Hi Takashi,
> > 
> > Happy New Year!
> > 
> > On Dec 26 21:34, Takashi Yano wrote:
> > > @@ -613,6 +613,22 @@ check_file_access (path_conv &pc, int flags, bool 
> > > effective)
> > >    if (flags & X_OK)
> > >      desired |= FILE_EXECUTE;
> > >  
> > > +  /* The Administrator has full access permission regardless of ACL,
> > > +     however, access() should return -1 if 'x' permission is set
> > > +     for neither user, group nor others, even though NtOpenFile()
> > > +     succeeds. */
> > 
> > The explanation isn't quite right, see below.
> > 
> > > +  if ((flags & X_OK) && !pc.isdir ())
> > > +    {
> > > +      struct stat st;
> > > +      if (stat (pc.get_posix (), &st))
> > > + goto out;
> > > +      else if ((st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)
> > > + {
> > > +   set_errno (EACCES);
> > > +   goto out;
> > > + }
> > > +    }
> > > +
> > 
> > Calling stat here is not the right thing to do.  It slows down access()
> > as well as exec'ing applications a lot because it adds the overhead of a
> > full system call on each invocation.
> > 
> > When I saw your patch this morning for the first time, I was inclined to
> > request that you simply revert a0933cd17d19 ("Correction for samba/SMB
> > share").  The behaviour on Samba was not a regression, but this here
> > is, so it would be prudent to rethink the entire approach.
> > 
> > However, it occured to me that there may be a simpler way to fix this:
> > 
> > The reason for this behaviour is the way SE_BACKUP_PRIVILEGE works.  To
> > allow a user with backup privileges full access to files, you have to
> > enable the SE_BACKUP_PRIVILEGE in the user's token *and* you have to
> > open files with FILE_OPEN_FOR_BACKUP_INTENT.  The problem now is this:
> > SE_BACKUP_PRIVILEGE + FILE_OPEN_FOR_BACKUP_INTENT allow to open the
> > file, no matter what.  In particular, they allow to open the file for
> > FILE_EXECUTE, even if the execute perms in the ACL deny the user
> > execution of the file.
> > 
> > So... given how this is supposed to work, we must not use the
> > FILE_OPEN_FOR_BACKUP_INTENT flag when checking for execute permissions
> > and the result should be the desired one.  I tested this locally, and I
> > don't see a regression compared to 3.5.4.
> > 
> > Patch attached.  Please review.
> 
> Thanks for reviewing and the counter patch.
> 
> With your patch, access(_, X_OK) returns -1 for a directory without 'x'
> permission even with Administrator.
> This seems due to lack of FILE_OPEN_FOR_BACKUP_INTENT.
> 
> How about simpler patch attached?

Revised a bit.

-- 
Takashi Yano <takashi.y...@nifty.ne.jp>
From 15bed39093acd20b7114fc706f455c5c40e00c29 Mon Sep 17 00:00:00 2001
From: Corinna Vinschen <cori...@vinschen.de>
Date: Tue, 7 Jan 2025 17:44:44 +0100
Subject: [PATCH v3] Cygwin: access: Fix X_OK behaviour for backup operators
 and admins

After commit a0933cd17d19, access(_, X_OK) returns 0 if the user
holds SE_BACKUP_PRIVILEGE, even if the file's ACL denies execution
to the user.  This is triggered by trying to open the file with
FILE_OPEN_FOR_BACKUP_INTENT.

Fix check_file_access() so it checks for X_OK without specifying
the FILE_OPEN_FOR_BACKUP_INTENT flag if the file is not a directory.

Rearrange function slightly and add comments for easier comprehension.

Fixes: a0933cd17d19 ("Cygwin: access: Correction for samba/SMB share")
Reported-by: Bruno Haible <br...@clisp.org>
Signed-off-by: Corinna Vinschen <cori...@vinschen.de>
---
 winsup/cygwin/sec/base.cc | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)

diff --git a/winsup/cygwin/sec/base.cc b/winsup/cygwin/sec/base.cc
index 647c27ec6..9f1527b7b 100644
--- a/winsup/cygwin/sec/base.cc
+++ b/winsup/cygwin/sec/base.cc
@@ -604,8 +604,14 @@ check_access (security_descriptor &sd, GENERIC_MAPPING 
&mapping,
 int
 check_file_access (path_conv &pc, int flags, bool effective)
 {
-  int ret = -1;
+  NTSTATUS status;
   ACCESS_MASK desired = 0;
+  ULONG opts = 0;
+  OBJECT_ATTRIBUTES attr;
+  IO_STATUS_BLOCK io;
+  HANDLE h = NULL;
+  int ret = -1;
+
   if (flags & R_OK)
     desired |= FILE_READ_DATA;
   if (flags & W_OK)
@@ -616,13 +622,21 @@ check_file_access (path_conv &pc, int flags, bool 
effective)
   if (!effective)
     cygheap->user.deimpersonate ();
 
-  OBJECT_ATTRIBUTES attr;
   pc.init_reopen_attr (attr, pc.handle ());
-  NTSTATUS status;
-  IO_STATUS_BLOCK io;
-  HANDLE h;
-  status = NtOpenFile (&h, desired, &attr, &io, FILE_SHARE_VALID_FLAGS,
-                      FILE_OPEN_FOR_BACKUP_INTENT);
+
+  /* For R_OK and W_OK we check with FILE_OPEN_FOR_BACKUP_INTENT since
+     we want to enable the full power of backup/restore privileges.
+     For X_OK, drop the FILE_OPEN_FOR_BACKUP_INTENT flag.  If the caller
+     holds SE_BACKUP_PRIVILEGE, FILE_OPEN_FOR_BACKUP_INTENT opens the file,
+     no matter what access is requested.
+     For directories, FILE_OPEN_FOR_BACKUP_INTENT flag is always set
+     regardless of X_OK. */
+  if (!(flags & X_OK) || pc.isdir ())
+    opts |= FILE_OPEN_FOR_BACKUP_INTENT;
+  else /* For a regular file to be executable, it must also be readable. */
+    desired |= FILE_READ_DATA;
+
+  status = NtOpenFile (&h, desired, &attr, &io, FILE_SHARE_VALID_FLAGS, opts);
   if (NT_SUCCESS (status))
     {
       NtClose (h);
-- 
2.45.1

Reply via email to