From ddf9c4a7444970b5ad4c0ed4a82bdc7bd4964c15 Mon Sep 17 00:00:00 2001
From: Corinna Vinschen <corinna@vinschen.de>
Date: Wed, 14 Apr 2004 13:40:07 +0000
Subject: [PATCH] 	* fhandler.cc (fhandler_base::open): Accomodate
 query_write_control 	query_state. 	(fhandler_base::fchown): New method. 
 * fhandler.h: Declare fchown method in fhandler_base, 	fhandler_disk_file and
 fhandler_virtual. 	(enum query_state): Add query_write_control. 	*
 fhandler_disk_file.cc (fhandler_disk_file::fchmod): Set query_state 	to
 query_write_control.  Only remove FILE_ATTRIBUTE_READONLY if not 
 setting security descriptor. 	(fhandler_disk_file::fchown): New method. 
 * fhandler_virtual.cc (fhandler_virtual::fchown): New method. 	* sec_acl.cc
 (setacl): Call write_sd with additional handle attribute. 	* security.cc
 (write_sd): Take handle argument.  Only request owner 	if getting
 SE_RESTORE_NAME privilege failed.  Only open file if 	NtSetSecurityObject
 failed or handle is NULL. 	(set_nt_attribute): Call write_sd with
 additional handle attribute. 	* security.h (write_sd): Declare with
 additional handle argument.

---
 winsup/cygwin/ChangeLog             | 20 +++++++++
 winsup/cygwin/fhandler.cc           | 20 ++++++++-
 winsup/cygwin/fhandler.h            |  8 +++-
 winsup/cygwin/fhandler_disk_file.cc | 45 ++++++++++++++++++---
 winsup/cygwin/fhandler_virtual.cc   |  8 ++++
 winsup/cygwin/sec_acl.cc            |  2 +-
 winsup/cygwin/security.cc           | 63 ++++++++++++++++-------------
 winsup/cygwin/security.h            |  2 +-
 8 files changed, 129 insertions(+), 39 deletions(-)

diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog
index 18fc01886..7ee5de3ec 100644
--- a/winsup/cygwin/ChangeLog
+++ b/winsup/cygwin/ChangeLog
@@ -1,3 +1,23 @@
+2004-04-14  Corinna Vinschen  <corinna@vinschen.de>
+
+	* fhandler.cc (fhandler_base::open): Accomodate query_write_control
+	query_state.
+	(fhandler_base::fchown): New method.
+	* fhandler.h: Declare fchown method in fhandler_base,
+	fhandler_disk_file and fhandler_virtual.
+	(enum query_state): Add query_write_control.
+	* fhandler_disk_file.cc (fhandler_disk_file::fchmod): Set query_state
+	to query_write_control.  Only remove FILE_ATTRIBUTE_READONLY if not
+	setting security descriptor.
+	(fhandler_disk_file::fchown): New method.
+	* fhandler_virtual.cc (fhandler_virtual::fchown): New method.
+	* sec_acl.cc (setacl): Call write_sd with additional handle attribute.
+	* security.cc (write_sd): Take handle argument.  Only request owner
+	if getting SE_RESTORE_NAME privilege failed.  Only open file if
+	NtSetSecurityObject failed or handle is NULL.
+	(set_nt_attribute): Call write_sd with additional handle attribute.
+	* security.h (write_sd): Declare with additional handle argument.
+
 2004-04-14  Corinna Vinschen  <corinna@vinschen.de>
 
 	* autoload.cc (NtSetSecurityObject): Add.
diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc
index 540fca58c..b93102dd1 100644
--- a/winsup/cygwin/fhandler.cc
+++ b/winsup/cygwin/fhandler.cc
@@ -443,7 +443,18 @@ fhandler_base::open (int flags, mode_t mode)
     }
 
   if (query_open ())
-    access = (query_open () == query_read_control ? READ_CONTROL : 0);
+    switch (query_open ())
+      {
+	case query_null_access:
+	  access = 0;
+	  break;
+	case query_read_control:
+	  access = READ_CONTROL;
+	  break;
+	case query_write_control:
+	  access = READ_CONTROL | WRITE_OWNER | WRITE_DAC;
+	  break;
+      }
   else if (get_major () == DEV_TAPE_MAJOR)
     access = GENERIC_READ | GENERIC_WRITE;
   else if ((flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDONLY)
@@ -1411,3 +1422,10 @@ fhandler_base::fchmod (mode_t mode)
   /* By default, just succeeds. */
   return 0;
 }
+
+int
+fhandler_base::fchown (__uid32_t uid, __gid32_t gid)
+{
+  /* By default, just succeeds. */
+  return 0;
+}
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index ece8d4984..0ee198c54 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -62,8 +62,9 @@ enum bg_check_types
 
 enum query_state {
   no_query = 0,
-  query_read_control = 1,
-  query_null_access = 2
+  query_null_access = 1,
+  query_read_control = 2,
+  query_write_control = 3
 };
 
 class fhandler_base
@@ -243,6 +244,7 @@ class fhandler_base
   int __stdcall fstat_by_handle (struct __stat64 *buf) __attribute__ ((regparm (2)));
   int __stdcall fstat_by_name (struct __stat64 *buf) __attribute__ ((regparm (2)));
   virtual int __stdcall fchmod (mode_t mode) __attribute__ ((regparm (1)));
+  virtual int __stdcall fchown (__uid32_t uid, __gid32_t gid) __attribute__ ((regparm (2)));
   virtual int ioctl (unsigned int cmd, void *);
   virtual int fcntl (int cmd, void *);
   virtual char const *ttyname () { return get_name (); }
@@ -568,6 +570,7 @@ class fhandler_disk_file: public fhandler_base
   bool isdevice () { return false; }
   int __stdcall fstat (struct __stat64 *buf) __attribute__ ((regparm (2)));
   int __stdcall fchmod (mode_t mode) __attribute__ ((regparm (1)));
+  int __stdcall fchown (__uid32_t uid, __gid32_t gid) __attribute__ ((regparm (2)));
 
   HANDLE mmap (caddr_t *addr, size_t len, DWORD access, int flags, _off64_t off);
   int munmap (HANDLE h, caddr_t addr, size_t len);
@@ -1101,6 +1104,7 @@ class fhandler_virtual : public fhandler_base
   int close (void);
   int __stdcall fstat (struct stat *buf) __attribute__ ((regparm (2)));
   int __stdcall fchmod (mode_t mode) __attribute__ ((regparm (1)));
+  int __stdcall fchown (__uid32_t uid, __gid32_t gid) __attribute__ ((regparm (2)));
   virtual bool fill_filebuf ();
   void fixup_after_exec ();
 };
diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc
index 28f930617..f649d4dbd 100644
--- a/winsup/cygwin/fhandler_disk_file.cc
+++ b/winsup/cygwin/fhandler_disk_file.cc
@@ -377,14 +377,18 @@ fhandler_disk_file::fchmod (mode_t mode)
   if (pc.is_fs_special ())
     return chmod_device (pc, mode);
 
-  query_open (query_read_control);
-  if (!get_io_handle () && !(oret = open_fs (O_BINARY, 0)))
-    return -1;
+  if (!get_io_handle ())
+    {
+      query_open (query_write_control);
+      if (!(oret = open_fs (O_BINARY, 0)))
+	return -1;
+    }
 
-  SetFileAttributes (get_win32_name (), (DWORD) pc & ~FILE_ATTRIBUTE_READONLY);
+  if (!allow_ntsec && allow_ntea) /* Not necessary when manipulating SD. */
+    SetFileAttributes (pc, (DWORD) pc & ~FILE_ATTRIBUTE_READONLY);
   if (pc.isdir ())
     mode |= S_IFDIR;
-  if (!set_file_attribute (pc.has_acls (), get_io_handle (), get_win32_name (),
+  if (!set_file_attribute (pc.has_acls (), get_io_handle (), pc,
 			   ILLEGAL_UID, ILLEGAL_GID, mode)
       && allow_ntsec)
     res = 0;
@@ -410,6 +414,37 @@ fhandler_disk_file::fchmod (mode_t mode)
   return res;
 }
 
+int __stdcall
+fhandler_disk_file::fchown (__uid32_t uid, __gid32_t gid)
+{
+  int oret = 0;
+  if (!get_io_handle ())
+    {
+      query_open (query_write_control);
+      if (!(oret = open_fs (O_BINARY, 0)))
+	return -1;
+    }
+
+  mode_t attrib = 0;
+  if (pc.isdir ())
+    attrib |= S_IFDIR;
+  int res = get_file_attribute (pc.has_acls (), get_io_handle (), pc, &attrib);
+  if (!res)
+    res = set_file_attribute (pc.has_acls (), get_io_handle (), pc,
+			      uid, gid, attrib);
+  if (res && (!pc.has_acls () || !allow_ntsec))
+    {
+      /* fake - if not supported, pretend we're like win95
+         where it just works */
+      res = 0;
+    }
+
+  if (oret)
+    close_fs ();
+
+  return res;
+}
+
 fhandler_disk_file::fhandler_disk_file () :
   fhandler_base ()
 {
diff --git a/winsup/cygwin/fhandler_virtual.cc b/winsup/cygwin/fhandler_virtual.cc
index 65be71b23..16b83923e 100644
--- a/winsup/cygwin/fhandler_virtual.cc
+++ b/winsup/cygwin/fhandler_virtual.cc
@@ -234,3 +234,11 @@ fhandler_virtual::fchmod (mode_t mode)
   set_errno (EPERM);
   return -1;
 }
+
+int
+fhandler_virtual::fchown (__uid32_t uid, __gid32_t gid)
+{
+  /* Same as on Linux. */
+  set_errno (EPERM);
+  return -1;
+}
diff --git a/winsup/cygwin/sec_acl.cc b/winsup/cygwin/sec_acl.cc
index dc2473e9e..a8dbe2f90 100644
--- a/winsup/cygwin/sec_acl.cc
+++ b/winsup/cygwin/sec_acl.cc
@@ -223,7 +223,7 @@ setacl (const char *file, int nentries, __aclent32_t *aclbufp)
       return -1;
     }
   debug_printf ("Created SD-Size: %d", sd_ret.size ());
-  return write_sd (file, sd_ret);
+  return write_sd (NULL, file, sd_ret);
 }
 
 /* Temporary access denied bits */
diff --git a/winsup/cygwin/security.cc b/winsup/cygwin/security.cc
index c932c2441..0915a77e6 100644
--- a/winsup/cygwin/security.cc
+++ b/winsup/cygwin/security.cc
@@ -1117,16 +1117,8 @@ read_sd (const char *file, security_descriptor &sd)
 }
 
 LONG
-write_sd (const char *file, security_descriptor &sd)
+write_sd (HANDLE fh, const char *file, security_descriptor &sd)
 {
-  BOOL dummy;
-  cygpsid owner;
-
-  if (!GetSecurityDescriptorOwner (sd, (PSID *) &owner, &dummy))
-    {
-      __seterrno ();
-      return -1;
-    }
   /* Try turning privilege on, may not have WRITE_OWNER or WRITE_DAC access.
      Must have privilege to set different owner, else BackupWrite misbehaves */
   static int NO_COPY saved_res; /* 0: never, 1: failed, 2 & 3: OK */
@@ -1140,29 +1132,42 @@ write_sd (const char *file, security_descriptor &sd)
     }
   else
     res = saved_res;
-  if (res == 1 && owner != cygheap->user.sid ())
+  if (res == 1)
     {
-      set_errno (EPERM);
-      return -1;
+      BOOL dummy;
+      cygpsid owner;
+
+      if (!GetSecurityDescriptorOwner (sd, (PSID *) &owner, &dummy))
+	{
+	  __seterrno ();
+	  return -1;
+	}
+      if (owner != cygheap->user.sid ())
+	{
+	  set_errno (EPERM);
+	  return -1;
+	}
     }
-  HANDLE fh;
-  if ((fh = CreateFile (file,
-		        WRITE_OWNER | WRITE_DAC,
-		        FILE_SHARE_READ | FILE_SHARE_WRITE,
-		        &sec_none_nih,
-		        OPEN_EXISTING,
-		        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
-		        NULL)) == INVALID_HANDLE_VALUE)
+  NTSTATUS ret;
+  int retry = 0;
+  for (; retry < 2; ++retry)
     {
-      __seterrno ();
-      return -1;
+      if (retry && (fh = CreateFile (file, WRITE_OWNER | WRITE_DAC,
+				     FILE_SHARE_READ | FILE_SHARE_WRITE,
+				     &sec_none_nih, OPEN_EXISTING,
+				     FILE_ATTRIBUTE_NORMAL
+				     | FILE_FLAG_BACKUP_SEMANTICS,
+				     NULL)) == INVALID_HANDLE_VALUE)
+	break;
+      if (fh && (ret = NtSetSecurityObject (fh,
+					    DACL_SECURITY_INFORMATION 
+					    | GROUP_SECURITY_INFORMATION
+					    | OWNER_SECURITY_INFORMATION,
+					    sd)) == STATUS_SUCCESS)
+	break;
     }
-  NTSTATUS ret = NtSetSecurityObject (fh,
-				      DACL_SECURITY_INFORMATION 
-				      | GROUP_SECURITY_INFORMATION
-				      | OWNER_SECURITY_INFORMATION,
-				      sd);
-  CloseHandle (fh);
+  if (retry && fh != INVALID_HANDLE_VALUE)
+    CloseHandle (fh);
   if (ret != STATUS_SUCCESS)
     {
       __seterrno_from_win_error (RtlNtStatusToDosError (ret));
@@ -1802,7 +1807,7 @@ set_nt_attribute (HANDLE handle, const char *file,
   if (!alloc_sd (uid, gid, attribute, sd))
     return -1;
 
-  return write_sd (file, sd);
+  return write_sd (handle, file, sd);
 }
 
 int
diff --git a/winsup/cygwin/security.h b/winsup/cygwin/security.h
index 2fb7c8085..e81a8d8ae 100644
--- a/winsup/cygwin/security.h
+++ b/winsup/cygwin/security.h
@@ -256,7 +256,7 @@ int __stdcall set_file_attribute (bool, HANDLE, const char *, __uid32_t, __gid32
 int __stdcall get_object_attribute (HANDLE handle, SE_OBJECT_TYPE object_type, mode_t *,
 				  __uid32_t * = NULL, __gid32_t * = NULL);
 LONG __stdcall read_sd (const char *file, security_descriptor &sd);
-LONG __stdcall write_sd (const char *file, security_descriptor &sd);
+LONG __stdcall write_sd (HANDLE fh, const char *file, security_descriptor &sd);
 bool __stdcall add_access_allowed_ace (PACL acl, int offset, DWORD attributes, PSID sid, size_t &len_add, DWORD inherit);
 bool __stdcall add_access_denied_ace (PACL acl, int offset, DWORD attributes, PSID sid, size_t &len_add, DWORD inherit);
 int __stdcall check_file_access (const char *, int);