* fhandler_tty.cc (fhandler_pty_slave::open): Add S_IFCHR flag in call to create_object_sd_from_attribute. (fhandler_pty_slave::fstat): Ditto in call to get_object_attribute. (fhandler_pty_slave::fchmod): Add an orig_mode flag and ditto for both calls. (fhandler_pty_master::setup): Ditto in call to create_object_sd_from_attribute. * security.cc (get_object_attribute): Never add S_IFCHR to mode here. Let the caller decide. (create_object_sd_from_attribute): Ditto. Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
		
			
				
	
	
		
			751 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			751 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* security.cc: NT file access control functions
 | 
						|
 | 
						|
   Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
 | 
						|
   2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Red Hat, Inc.
 | 
						|
 | 
						|
   Originaly written by Gunther Ebert, gunther.ebert@ixos-leipzig.de
 | 
						|
   Completely rewritten by Corinna Vinschen <corinna@vinschen.de>
 | 
						|
 | 
						|
This file is part of Cygwin.
 | 
						|
 | 
						|
This software is a copyrighted work licensed under the terms of the
 | 
						|
Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | 
						|
details. */
 | 
						|
 | 
						|
#include "winsup.h"
 | 
						|
#include <unistd.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <sys/acl.h>
 | 
						|
#include "cygerrno.h"
 | 
						|
#include "security.h"
 | 
						|
#include "path.h"
 | 
						|
#include "fhandler.h"
 | 
						|
#include "dtable.h"
 | 
						|
#include "pinfo.h"
 | 
						|
#include "cygheap.h"
 | 
						|
#include "ntdll.h"
 | 
						|
#include "tls_pbuf.h"
 | 
						|
#include <aclapi.h>
 | 
						|
 | 
						|
#define ALL_SECURITY_INFORMATION (DACL_SECURITY_INFORMATION \
 | 
						|
				  | GROUP_SECURITY_INFORMATION \
 | 
						|
				  | OWNER_SECURITY_INFORMATION)
 | 
						|
 | 
						|
static GENERIC_MAPPING NO_COPY_RO file_mapping = { FILE_GENERIC_READ,
 | 
						|
						   FILE_GENERIC_WRITE,
 | 
						|
						   FILE_GENERIC_EXECUTE,
 | 
						|
						   FILE_ALL_ACCESS };
 | 
						|
LONG
 | 
						|
get_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd,
 | 
						|
	     bool justcreated)
 | 
						|
{
 | 
						|
  NTSTATUS status = STATUS_SUCCESS;
 | 
						|
  OBJECT_ATTRIBUTES attr;
 | 
						|
  IO_STATUS_BLOCK io;
 | 
						|
  ULONG len = SD_MAXIMUM_SIZE, rlen;
 | 
						|
 | 
						|
  /* Allocate space for the security descriptor. */
 | 
						|
  if (!sd.malloc (len))
 | 
						|
    {
 | 
						|
      set_errno (ENOMEM);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  /* Try to fetch the security descriptor if the handle is valid. */
 | 
						|
  if (fh)
 | 
						|
    {
 | 
						|
      status = NtQuerySecurityObject (fh, ALL_SECURITY_INFORMATION,
 | 
						|
				      sd, len, &rlen);
 | 
						|
      if (!NT_SUCCESS (status))
 | 
						|
	debug_printf ("NtQuerySecurityObject (%S), status %y",
 | 
						|
		      pc.get_nt_native_path (), status);
 | 
						|
    }
 | 
						|
  /* If the handle was NULL, or fetching with the original handle didn't work,
 | 
						|
     try to reopen the file with READ_CONTROL and fetch the security descriptor
 | 
						|
     using that handle. */
 | 
						|
  if (!fh || !NT_SUCCESS (status))
 | 
						|
    {
 | 
						|
      status = NtOpenFile (&fh, READ_CONTROL,
 | 
						|
			   fh ? pc.init_reopen_attr (attr, fh)
 | 
						|
			      : pc.get_object_attr (attr, sec_none_nih),
 | 
						|
			   &io, FILE_SHARE_VALID_FLAGS,
 | 
						|
			   FILE_OPEN_FOR_BACKUP_INTENT);
 | 
						|
      if (!NT_SUCCESS (status))
 | 
						|
	{
 | 
						|
	  sd.free ();
 | 
						|
	  __seterrno_from_nt_status (status);
 | 
						|
	  return -1;
 | 
						|
	}
 | 
						|
      status = NtQuerySecurityObject (fh, ALL_SECURITY_INFORMATION,
 | 
						|
				      sd, len, &rlen);
 | 
						|
      NtClose (fh);
 | 
						|
      if (!NT_SUCCESS (status))
 | 
						|
	{
 | 
						|
	  sd.free ();
 | 
						|
	  __seterrno_from_nt_status (status);
 | 
						|
	  return -1;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  /* We have a security descriptor now.  Unfortunately, if you want to know
 | 
						|
     if an ACE is inherited from the parent object, this isn't sufficient.
 | 
						|
 | 
						|
     In the simple case, the SDs control word contains one of the
 | 
						|
     SE_DACL_AUTO_INHERITED or SE_DACL_PROTECTED flags, or at least one of
 | 
						|
     the ACEs has the INHERITED_ACE flag set.  In all of these cases we
 | 
						|
     know the DACL has been inherited.
 | 
						|
 | 
						|
     If none of these flags is set in the SD, the information whether
 | 
						|
     or not an ACE has been inherited is not available in the DACL of the
 | 
						|
     object.  In this case GetSecurityInfo fetches the SD from the parent
 | 
						|
     directory and tests if the object's SD contains inherited ACEs from the
 | 
						|
     parent.
 | 
						|
 | 
						|
     Note that we're not testing the SE_DACL_AUTO_INHERITED and
 | 
						|
     SE_DACL_PROTECTED flags here because we know the state the file's SD
 | 
						|
     is in.  Since we're creating all files with a NULL descriptor, the DACL
 | 
						|
     is either inherited from the parent, or it's the default DACL.  In
 | 
						|
     neither case, one of these flags is set.
 | 
						|
 | 
						|
     For speed, we're not calling RtlConvertToAutoInheritSecurityObject
 | 
						|
     anymore (but keep the code here for reference).  Rather we just test
 | 
						|
     if one of the parent's ACEs is inheritable.  If so, we know we inherited
 | 
						|
     it and set the SE_DACL_AUTO_INHERITED flag.  If not, we may assume our
 | 
						|
     object's DACL is the default DACL.
 | 
						|
 | 
						|
     This functionality is slow and the extra information is only required
 | 
						|
     when the file has been created and the permissions are about to be set
 | 
						|
     to POSIX permissions.  Therefore we only use it in case the file just
 | 
						|
     got created. */
 | 
						|
  if (justcreated)
 | 
						|
    {
 | 
						|
      PACL dacl;
 | 
						|
      BOOLEAN exists, def;
 | 
						|
      ACCESS_ALLOWED_ACE *ace;
 | 
						|
      UNICODE_STRING dirname;
 | 
						|
      PSECURITY_DESCRIPTOR psd;
 | 
						|
      tmp_pathbuf tp;
 | 
						|
 | 
						|
      /* Open the parent directory with READ_CONTROL... */
 | 
						|
      RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, NULL);
 | 
						|
      InitializeObjectAttributes (&attr, &dirname, pc.objcaseinsensitive (),
 | 
						|
				  NULL, NULL);
 | 
						|
      status = NtOpenFile (&fh, READ_CONTROL, &attr, &io,
 | 
						|
			   FILE_SHARE_VALID_FLAGS,
 | 
						|
			   FILE_OPEN_FOR_BACKUP_INTENT
 | 
						|
			   | FILE_OPEN_REPARSE_POINT);
 | 
						|
      if (!NT_SUCCESS (status))
 | 
						|
	{
 | 
						|
	  debug_printf ("NtOpenFile (%S), status %y", &dirname, status);
 | 
						|
	  return 0;
 | 
						|
	}
 | 
						|
      /* ... fetch the parent's security descriptor ... */
 | 
						|
      psd = (PSECURITY_DESCRIPTOR) tp.w_get ();
 | 
						|
      status = NtQuerySecurityObject (fh, ALL_SECURITY_INFORMATION,
 | 
						|
				      psd, len, &rlen);
 | 
						|
      NtClose (fh);
 | 
						|
      if (!NT_SUCCESS (status))
 | 
						|
	{
 | 
						|
	  debug_printf ("NtQuerySecurityObject (%S), status %y",
 | 
						|
			&dirname, status);
 | 
						|
	  return 0;
 | 
						|
	}
 | 
						|
#if 0
 | 
						|
      /* ... and create a new security descriptor in which all inherited ACEs
 | 
						|
	 are marked with the INHERITED_ACE flag.  For a description of the
 | 
						|
	 undocumented RtlConvertToAutoInheritSecurityObject function from
 | 
						|
	 ntdll.dll see the MSDN man page for the advapi32 function
 | 
						|
	 ConvertToAutoInheritPrivateObjectSecurity.  Fortunately the latter
 | 
						|
	 is just a shim. */
 | 
						|
      PSECURITY_DESCRIPTOR nsd;
 | 
						|
      status = RtlConvertToAutoInheritSecurityObject (psd, sd, &nsd, NULL,
 | 
						|
						      pc.isdir (),
 | 
						|
						      &file_mapping);
 | 
						|
      if (!NT_SUCCESS (status))
 | 
						|
	{
 | 
						|
	  debug_printf ("RtlConvertToAutoInheritSecurityObject (%S), status %y",
 | 
						|
			&dirname, status);
 | 
						|
	  return 0;
 | 
						|
	}
 | 
						|
      /* Eventually copy the new security descriptor into sd and delete the
 | 
						|
	 original one created by RtlConvertToAutoInheritSecurityObject from
 | 
						|
	 the heap. */
 | 
						|
      len = RtlLengthSecurityDescriptor (nsd);
 | 
						|
      memcpy ((PSECURITY_DESCRIPTOR) sd, nsd, len);
 | 
						|
      RtlDeleteSecurityObject (&nsd);
 | 
						|
#else
 | 
						|
      /* ... and check the parent descriptor for inheritable ACEs matching
 | 
						|
	 our current object type (file/dir).  The simple truth in our case
 | 
						|
	 is, either the parent dir had inheritable ACEs and all our ACEs are
 | 
						|
	 inherited, or the parent dir didn't have inheritable ACEs and all
 | 
						|
	 our ACEs are taken from the default DACL. */
 | 
						|
      bool inherited = false;
 | 
						|
      BYTE search_flags = pc.isdir () ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
 | 
						|
				      : SUB_OBJECTS_ONLY_INHERIT;
 | 
						|
      if (NT_SUCCESS (RtlGetDaclSecurityDescriptor (psd, &exists, &dacl, &def))
 | 
						|
	  && exists && dacl)
 | 
						|
	for (ULONG idx = 0; idx < dacl->AceCount; ++idx)
 | 
						|
	  if (NT_SUCCESS (RtlGetAce (dacl, idx, (PVOID *) &ace))
 | 
						|
	      && (ace->Header.AceFlags & search_flags))
 | 
						|
	    {
 | 
						|
	      inherited = true;
 | 
						|
	      break;
 | 
						|
	    }
 | 
						|
      /* Then, if the parent descriptor contained inheritable ACEs, we mark
 | 
						|
	 the SD as SE_DACL_AUTO_INHERITED.  Note that this requires the
 | 
						|
	 matching check in get_posix_access.  If we ever revert to
 | 
						|
	 RtlConvertToAutoInheritSecurityObject, the check in get_posix_access
 | 
						|
	 has to test every single ACE for the INHERITED_ACE flag again. */
 | 
						|
      if (inherited
 | 
						|
	  && NT_SUCCESS (RtlGetDaclSecurityDescriptor (sd, &exists, &dacl,
 | 
						|
						       &def))
 | 
						|
	  && exists && dacl)
 | 
						|
	RtlSetControlSecurityDescriptor (sd, SE_DACL_AUTO_INHERITED,
 | 
						|
					     SE_DACL_AUTO_INHERITED);
 | 
						|
#endif
 | 
						|
    }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
LONG
 | 
						|
set_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd, bool is_chown)
 | 
						|
{
 | 
						|
  NTSTATUS status = STATUS_SUCCESS;
 | 
						|
  int retry = 0;
 | 
						|
  int res = -1;
 | 
						|
 | 
						|
  for (; retry < 2; ++retry)
 | 
						|
    {
 | 
						|
      if (fh)
 | 
						|
	{
 | 
						|
	  status = NtSetSecurityObject (fh,
 | 
						|
					is_chown ? ALL_SECURITY_INFORMATION
 | 
						|
						 : DACL_SECURITY_INFORMATION,
 | 
						|
					sd);
 | 
						|
	  if (NT_SUCCESS (status))
 | 
						|
	    {
 | 
						|
	      res = 0;
 | 
						|
	      break;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      if (!retry)
 | 
						|
	{
 | 
						|
	  OBJECT_ATTRIBUTES attr;
 | 
						|
	  IO_STATUS_BLOCK io;
 | 
						|
	  status = NtOpenFile (&fh, (is_chown ? WRITE_OWNER  : 0) | WRITE_DAC,
 | 
						|
			       fh ? pc.init_reopen_attr (attr, fh)
 | 
						|
				  : pc.get_object_attr (attr, sec_none_nih),
 | 
						|
			       &io,
 | 
						|
			       FILE_SHARE_VALID_FLAGS,
 | 
						|
			       FILE_OPEN_FOR_BACKUP_INTENT);
 | 
						|
	  if (!NT_SUCCESS (status))
 | 
						|
	    {
 | 
						|
	      fh = NULL;
 | 
						|
	      break;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
  if (retry && fh)
 | 
						|
    NtClose (fh);
 | 
						|
  if (!NT_SUCCESS (status))
 | 
						|
    __seterrno_from_nt_status (status);
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
get_reg_sd (HANDLE handle, security_descriptor &sd_ret)
 | 
						|
{
 | 
						|
  LONG ret;
 | 
						|
  DWORD len = 0;
 | 
						|
 | 
						|
  ret = RegGetKeySecurity ((HKEY) handle, ALL_SECURITY_INFORMATION,
 | 
						|
			   sd_ret, &len);
 | 
						|
  if (ret == ERROR_INSUFFICIENT_BUFFER)
 | 
						|
    {
 | 
						|
      if (!sd_ret.malloc (len))
 | 
						|
	set_errno (ENOMEM);
 | 
						|
      else
 | 
						|
	ret = RegGetKeySecurity ((HKEY) handle, ALL_SECURITY_INFORMATION,
 | 
						|
				 sd_ret, &len);
 | 
						|
    }
 | 
						|
  if (ret != ERROR_SUCCESS)
 | 
						|
    {
 | 
						|
      __seterrno ();
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
get_reg_attribute (HKEY hkey, mode_t *attribute, uid_t *uidret,
 | 
						|
		   gid_t *gidret)
 | 
						|
{
 | 
						|
  security_descriptor sd;
 | 
						|
 | 
						|
  if (!get_reg_sd (hkey, sd))
 | 
						|
    {
 | 
						|
      get_posix_access (sd, attribute, uidret, gidret, NULL, 0);
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
  /* The entries are already set to default values */
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
get_file_attribute (HANDLE handle, path_conv &pc,
 | 
						|
		    mode_t *attribute, uid_t *uidret, gid_t *gidret)
 | 
						|
{
 | 
						|
  if (pc.has_acls ())
 | 
						|
    {
 | 
						|
      security_descriptor sd;
 | 
						|
 | 
						|
      if (!get_file_sd (handle, pc, sd, false))
 | 
						|
	{
 | 
						|
	  get_posix_access (sd, attribute, uidret, gidret, NULL, 0);
 | 
						|
	  return 0;
 | 
						|
	}
 | 
						|
      /* ENOSYS is returned by get_file_sd if fetching the DACL from a remote
 | 
						|
	 share returns STATUS_INVALID_NETWORK_RESPONSE, which in turn is
 | 
						|
	 converted to ERROR_BAD_NET_RESP.  This potentially occurs when trying
 | 
						|
	 to fetch DACLs from a NT4 machine which is not part of the domain of
 | 
						|
	 the requesting machine. */
 | 
						|
      else if (get_errno () != ENOSYS)
 | 
						|
	{
 | 
						|
	  if (uidret)
 | 
						|
	    *uidret = ILLEGAL_UID;
 | 
						|
	  if (gidret)
 | 
						|
	    *gidret = ILLEGAL_GID;
 | 
						|
 | 
						|
	  return -1;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  if (uidret)
 | 
						|
    *uidret = myself->uid;
 | 
						|
  if (gidret)
 | 
						|
    *gidret = myself->gid;
 | 
						|
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
add_access_allowed_ace (PACL acl, DWORD attributes, PSID sid, size_t &len_add,
 | 
						|
			DWORD inherit)
 | 
						|
{
 | 
						|
  NTSTATUS status = RtlAddAccessAllowedAceEx (acl, ACL_REVISION, inherit,
 | 
						|
					      attributes, sid);
 | 
						|
  if (!NT_SUCCESS (status))
 | 
						|
    {
 | 
						|
      __seterrno_from_nt_status (status);
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
  len_add += sizeof (ACCESS_ALLOWED_ACE) - sizeof (DWORD) + RtlLengthSid (sid);
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
add_access_denied_ace (PACL acl, DWORD attributes, PSID sid, size_t &len_add,
 | 
						|
		       DWORD inherit)
 | 
						|
{
 | 
						|
  NTSTATUS status = RtlAddAccessDeniedAceEx (acl, ACL_REVISION, inherit,
 | 
						|
					     attributes, sid);
 | 
						|
  if (!NT_SUCCESS (status))
 | 
						|
    {
 | 
						|
      __seterrno_from_nt_status (status);
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
  len_add += sizeof (ACCESS_DENIED_ACE) - sizeof (DWORD) + RtlLengthSid (sid);
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
set_security_attribute (path_conv &pc, int attribute, PSECURITY_ATTRIBUTES psa,
 | 
						|
			security_descriptor &sd)
 | 
						|
{
 | 
						|
  psa->lpSecurityDescriptor = sd.malloc (SECURITY_DESCRIPTOR_MIN_LENGTH);
 | 
						|
  RtlCreateSecurityDescriptor ((PSECURITY_DESCRIPTOR) psa->lpSecurityDescriptor,
 | 
						|
				SECURITY_DESCRIPTOR_REVISION);
 | 
						|
  psa->lpSecurityDescriptor = set_posix_access (attribute, geteuid32 (),
 | 
						|
						getegid32 (), NULL, 0,
 | 
						|
						sd, false);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
get_object_sd (HANDLE handle, security_descriptor &sd)
 | 
						|
{
 | 
						|
  ULONG len = 0;
 | 
						|
  NTSTATUS status;
 | 
						|
 | 
						|
  status = NtQuerySecurityObject (handle, ALL_SECURITY_INFORMATION,
 | 
						|
				  sd, len, &len);
 | 
						|
  if (status != STATUS_BUFFER_TOO_SMALL)
 | 
						|
    {
 | 
						|
      __seterrno_from_nt_status (status);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  if (!sd.malloc (len))
 | 
						|
    {
 | 
						|
      set_errno (ENOMEM);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  status = NtQuerySecurityObject (handle, ALL_SECURITY_INFORMATION,
 | 
						|
				  sd, len, &len);
 | 
						|
  if (!NT_SUCCESS (status))
 | 
						|
    {
 | 
						|
      __seterrno_from_nt_status (status);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
get_object_attribute (HANDLE handle, uid_t *uidret, gid_t *gidret,
 | 
						|
		      mode_t *attribute)
 | 
						|
{
 | 
						|
  security_descriptor sd;
 | 
						|
 | 
						|
  if (get_object_sd (handle, sd))
 | 
						|
    return -1;
 | 
						|
  return get_posix_access (sd, attribute, uidret, gidret, NULL, 0)
 | 
						|
	 >= 0 ? 0 : -1;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
create_object_sd_from_attribute (uid_t uid, gid_t gid, mode_t attribute,
 | 
						|
				 security_descriptor &sd)
 | 
						|
{
 | 
						|
  return set_posix_access (attribute, uid, gid, NULL, 0, sd, false)
 | 
						|
  	 ? 0 : -1;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
set_object_sd (HANDLE handle, security_descriptor &sd, bool chown)
 | 
						|
{
 | 
						|
  NTSTATUS status;
 | 
						|
  status = NtSetSecurityObject (handle, chown ? ALL_SECURITY_INFORMATION
 | 
						|
					      : DACL_SECURITY_INFORMATION, sd);
 | 
						|
  if (!NT_SUCCESS (status))
 | 
						|
    {
 | 
						|
      __seterrno_from_nt_status (status);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
set_object_attribute (HANDLE handle, uid_t uid, gid_t gid, mode_t attribute)
 | 
						|
{
 | 
						|
  security_descriptor sd;
 | 
						|
 | 
						|
  if (create_object_sd_from_attribute (uid, gid, attribute, sd)
 | 
						|
      || set_object_sd (handle, sd, uid != ILLEGAL_UID || gid != ILLEGAL_GID))
 | 
						|
    return -1;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
set_created_file_access (HANDLE handle, path_conv &pc, mode_t attr)
 | 
						|
{
 | 
						|
  int ret = -1;
 | 
						|
  security_descriptor sd, sd_ret;
 | 
						|
  mode_t attr_rd;
 | 
						|
  uid_t uid;
 | 
						|
  gid_t gid;
 | 
						|
  tmp_pathbuf tp;
 | 
						|
  aclent_t *aclp;
 | 
						|
  int nentries, idx;
 | 
						|
 | 
						|
  if (!get_file_sd (handle, pc, sd, true))
 | 
						|
    {
 | 
						|
      attr |= S_JUSTCREATED;
 | 
						|
      if (pc.isdir ())
 | 
						|
	attr |= S_IFDIR;
 | 
						|
      attr_rd = attr;
 | 
						|
      aclp = (aclent_t *) tp.c_get ();
 | 
						|
      if ((nentries = get_posix_access (sd, &attr_rd, &uid, &gid,
 | 
						|
					aclp, MAX_ACL_ENTRIES)) >= 0)
 | 
						|
	{
 | 
						|
	  if (S_ISLNK (attr))
 | 
						|
	    {
 | 
						|
	      /* Symlinks always get the request POSIX perms. */
 | 
						|
	      aclp[0].a_perm = (attr >> 6) & S_IRWXO;
 | 
						|
	      if ((idx = searchace (aclp, nentries, GROUP_OBJ)) >= 0)
 | 
						|
		aclp[idx].a_perm = (attr >> 3) & S_IRWXO;
 | 
						|
	      if (nentries > MIN_ACL_ENTRIES
 | 
						|
		  && (idx = searchace (aclp, nentries, CLASS_OBJ)) >= 0)
 | 
						|
		aclp[idx].a_perm = (attr >> 3) & S_IRWXO;
 | 
						|
	      if ((idx = searchace (aclp, nentries, OTHER_OBJ)) >= 0)
 | 
						|
		aclp[idx].a_perm = attr & S_IRWXO;
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      /* Overwrite ACL permissions as required by POSIX 1003.1e
 | 
						|
		 draft 17. */
 | 
						|
	      aclp[0].a_perm &= (attr >> 6) & S_IRWXO;
 | 
						|
	      /* Deliberate deviation from POSIX 1003.1e here.  We're not
 | 
						|
		 writing CLASS_OBJ *or* GROUP_OBJ, but both.  Otherwise we're
 | 
						|
		 going to be in constant trouble with user expectations. */
 | 
						|
	      if ((idx = searchace (aclp, nentries, GROUP_OBJ)) >= 0)
 | 
						|
		aclp[idx].a_perm &= (attr >> 3) & S_IRWXO;
 | 
						|
	      if (nentries > MIN_ACL_ENTRIES
 | 
						|
		  && (idx = searchace (aclp, nentries, CLASS_OBJ)) >= 0)
 | 
						|
		aclp[idx].a_perm &= (attr >> 3) & S_IRWXO;
 | 
						|
	      if ((idx = searchace (aclp, nentries, OTHER_OBJ)) >= 0)
 | 
						|
		aclp[idx].a_perm &= attr & S_IRWXO;
 | 
						|
	    }
 | 
						|
	  /* Construct appropriate inherit attribute for new directories.
 | 
						|
	     Basically we do this only for the sake of non-Cygwin applications.
 | 
						|
	     Cygwin applications don't need these.  Additionally, if the
 | 
						|
	     S_ISGID bit is set, propagate it. */
 | 
						|
	  if (S_ISDIR (attr))
 | 
						|
	    {
 | 
						|
	      if (searchace (aclp, nentries, DEF_USER_OBJ) < 0)
 | 
						|
		{
 | 
						|
		  aclp[nentries].a_type = DEF_USER_OBJ;
 | 
						|
		  aclp[nentries].a_id = ILLEGAL_UID;
 | 
						|
		  aclp[nentries++].a_perm = (attr >> 6) & S_IRWXO;
 | 
						|
		}
 | 
						|
	      if (searchace (aclp, nentries, DEF_GROUP_OBJ) < 0)
 | 
						|
		{
 | 
						|
		  aclp[nentries].a_type = DEF_GROUP_OBJ;
 | 
						|
		  aclp[nentries].a_id = ILLEGAL_GID;
 | 
						|
		  aclp[nentries++].a_perm = (attr >> 3) & S_IRWXO;
 | 
						|
		}
 | 
						|
	      if (searchace (aclp, nentries, DEF_OTHER_OBJ) < 0)
 | 
						|
		{
 | 
						|
		  aclp[nentries].a_type = DEF_OTHER_OBJ;
 | 
						|
		  aclp[nentries].a_id = ILLEGAL_UID;
 | 
						|
		  aclp[nentries++].a_perm = attr & S_IRWXO;
 | 
						|
		}
 | 
						|
	      if (attr_rd & S_ISGID)
 | 
						|
		attr |= S_ISGID;
 | 
						|
	    }
 | 
						|
	  if (set_posix_access (attr, uid, gid, aclp, nentries, sd_ret,
 | 
						|
				pc.fs_is_samba ()))
 | 
						|
	    ret = set_file_sd (handle, pc, sd_ret, attr_rd & S_ISGID);
 | 
						|
	}
 | 
						|
    }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
check_access (security_descriptor &sd, GENERIC_MAPPING &mapping,
 | 
						|
	      ACCESS_MASK desired, int flags, bool effective)
 | 
						|
{
 | 
						|
  int ret = -1;
 | 
						|
  NTSTATUS status, allow;
 | 
						|
  ACCESS_MASK granted;
 | 
						|
  DWORD plen = sizeof (PRIVILEGE_SET) + 3 * sizeof (LUID_AND_ATTRIBUTES);
 | 
						|
  PPRIVILEGE_SET pset = (PPRIVILEGE_SET) alloca (plen);
 | 
						|
  HANDLE tok = ((effective && cygheap->user.issetuid ())
 | 
						|
		? cygheap->user.imp_token ()
 | 
						|
		: hProcImpToken);
 | 
						|
 | 
						|
  if (!tok)
 | 
						|
    {
 | 
						|
      if (!DuplicateTokenEx (hProcToken, MAXIMUM_ALLOWED, NULL,
 | 
						|
			    SecurityImpersonation, TokenImpersonation,
 | 
						|
			    &hProcImpToken))
 | 
						|
	 {
 | 
						|
	    __seterrno ();
 | 
						|
	    return ret;
 | 
						|
	 }
 | 
						|
      tok = hProcImpToken;
 | 
						|
    }
 | 
						|
 | 
						|
  status = NtAccessCheck (sd, tok, desired, &mapping, pset, &plen, &granted,
 | 
						|
			  &allow);
 | 
						|
  if (!NT_SUCCESS (status))
 | 
						|
    __seterrno ();
 | 
						|
  else if (!NT_SUCCESS (allow))
 | 
						|
    {
 | 
						|
      /* CV, 2006-10-16: Now, that's really weird.  Imagine a user who has no
 | 
						|
	 standard access to a file, but who has backup and restore privileges
 | 
						|
	 and these privileges are enabled in the access token.  One would
 | 
						|
	 expect that the AccessCheck function takes this into consideration
 | 
						|
	 when returning the access status.  Otherwise, why bother with the
 | 
						|
	 pset parameter, right?
 | 
						|
	 But not so.  AccessCheck actually returns a status of "false" here,
 | 
						|
	 even though opening a file with backup resp. restore intent
 | 
						|
	 naturally succeeds for this user.  This definitely spoils the results
 | 
						|
	 of access(2) for administrative users or the SYSTEM account.  So, in
 | 
						|
	 case the access check fails, another check against the user's
 | 
						|
	 backup/restore privileges has to be made.  Sigh. */
 | 
						|
      int granted_flags = 0;
 | 
						|
      BOOLEAN has_priv;
 | 
						|
 | 
						|
      if (flags & R_OK)
 | 
						|
	{
 | 
						|
	  pset->PrivilegeCount = 1;
 | 
						|
	  pset->Control = 0;
 | 
						|
	  pset->Privilege[0].Luid.HighPart = 0L;
 | 
						|
	  pset->Privilege[0].Luid.LowPart = SE_BACKUP_PRIVILEGE;
 | 
						|
	  pset->Privilege[0].Attributes = 0;
 | 
						|
	  status = NtPrivilegeCheck (tok, pset, &has_priv);
 | 
						|
	  if (NT_SUCCESS (status) && has_priv)
 | 
						|
	    granted_flags |= R_OK;
 | 
						|
	}
 | 
						|
      if (flags & W_OK)
 | 
						|
	{
 | 
						|
	  pset->PrivilegeCount = 1;
 | 
						|
	  pset->Control = 0;
 | 
						|
	  pset->Privilege[0].Luid.HighPart = 0L;
 | 
						|
	  pset->Privilege[0].Luid.LowPart = SE_RESTORE_PRIVILEGE;
 | 
						|
	  pset->Privilege[0].Attributes = 0;
 | 
						|
	  status = NtPrivilegeCheck (tok, pset, &has_priv);
 | 
						|
	  if (NT_SUCCESS (status) && has_priv)
 | 
						|
	    granted_flags |= W_OK;
 | 
						|
	}
 | 
						|
      if (granted_flags == flags)
 | 
						|
	ret = 0;
 | 
						|
      else
 | 
						|
	set_errno (EACCES);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    ret = 0;
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
/* Samba override.  Check security descriptor for Samba UNIX user and group
 | 
						|
   accounts and check if we have an RFC 2307 mapping to a Windows account.
 | 
						|
   Create a new security descriptor with all of the UNIX accounts with
 | 
						|
   valid mapping replaced with their Windows counterpart. */
 | 
						|
static void
 | 
						|
convert_samba_sd (security_descriptor &sd_ret)
 | 
						|
{
 | 
						|
  NTSTATUS status;
 | 
						|
  BOOLEAN dummy;
 | 
						|
  PSID sid;
 | 
						|
  cygsid owner;
 | 
						|
  cygsid group;
 | 
						|
  SECURITY_DESCRIPTOR sd;
 | 
						|
  cyg_ldap cldap;
 | 
						|
  tmp_pathbuf tp;
 | 
						|
  PACL acl, oacl;
 | 
						|
  size_t acl_len;
 | 
						|
  PACCESS_ALLOWED_ACE ace;
 | 
						|
 | 
						|
  if (!NT_SUCCESS (RtlGetOwnerSecurityDescriptor (sd_ret, &sid, &dummy)))
 | 
						|
    return;
 | 
						|
  owner = sid;
 | 
						|
  if (!NT_SUCCESS (RtlGetGroupSecurityDescriptor (sd_ret, &sid, &dummy)))
 | 
						|
    return;
 | 
						|
  group = sid;
 | 
						|
 | 
						|
  if (sid_id_auth (owner) == 22)
 | 
						|
    {
 | 
						|
      struct passwd *pwd;
 | 
						|
      uid_t uid = owner.get_uid (&cldap);
 | 
						|
      if (uid < UNIX_POSIX_OFFSET && (pwd = internal_getpwuid (uid)))
 | 
						|
	owner.getfrompw (pwd);
 | 
						|
    }
 | 
						|
  if (sid_id_auth (group) == 22)
 | 
						|
    {
 | 
						|
      struct group *grp;
 | 
						|
      gid_t gid = group.get_gid (&cldap);
 | 
						|
      if (gid < UNIX_POSIX_OFFSET && (grp = internal_getgrgid (gid)))
 | 
						|
	group.getfromgr (grp);
 | 
						|
    }
 | 
						|
 | 
						|
  if (!NT_SUCCESS (RtlGetDaclSecurityDescriptor (sd_ret, &dummy,
 | 
						|
						 &oacl, &dummy)))
 | 
						|
    return;
 | 
						|
  acl = (PACL) tp.w_get ();
 | 
						|
  RtlCreateAcl (acl, ACL_MAXIMUM_SIZE, ACL_REVISION);
 | 
						|
  acl_len = sizeof (ACL);
 | 
						|
 | 
						|
  for (DWORD i = 0; i < oacl->AceCount; ++i)
 | 
						|
    if (NT_SUCCESS (RtlGetAce (oacl, i, (PVOID *) &ace)))
 | 
						|
      {
 | 
						|
	cygsid ace_sid ((PSID) &ace->SidStart);
 | 
						|
	if (sid_id_auth (ace_sid) == 22)
 | 
						|
	  {
 | 
						|
	    if (sid_sub_auth (ace_sid, 0) == 1) /* user */
 | 
						|
	      {
 | 
						|
		struct passwd *pwd;
 | 
						|
		uid_t uid = ace_sid.get_uid (&cldap);
 | 
						|
		if (uid < UNIX_POSIX_OFFSET && (pwd = internal_getpwuid (uid)))
 | 
						|
		  ace_sid.getfrompw (pwd);
 | 
						|
	      }
 | 
						|
	    else if (sid_sub_auth (ace_sid, 0) == 2) /* group */
 | 
						|
	      {
 | 
						|
		struct group *grp;
 | 
						|
		gid_t gid = ace_sid.get_gid (&cldap);
 | 
						|
		if (gid < UNIX_POSIX_OFFSET && (grp = internal_getgrgid (gid)))
 | 
						|
		  ace_sid.getfromgr (grp);
 | 
						|
	      }
 | 
						|
	  }
 | 
						|
	if (!add_access_allowed_ace (acl, ace->Mask, ace_sid, acl_len,
 | 
						|
				     ace->Header.AceFlags))
 | 
						|
	  return;
 | 
						|
      }
 | 
						|
  acl->AclSize = acl_len;
 | 
						|
 | 
						|
  RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
 | 
						|
  RtlSetControlSecurityDescriptor (&sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED);
 | 
						|
  RtlSetOwnerSecurityDescriptor (&sd, owner, FALSE);
 | 
						|
  RtlSetGroupSecurityDescriptor (&sd, group, FALSE);
 | 
						|
 | 
						|
  status = RtlSetDaclSecurityDescriptor (&sd, TRUE, acl, FALSE);
 | 
						|
  if (!NT_SUCCESS (status))
 | 
						|
    return;
 | 
						|
  DWORD sd_size = 0;
 | 
						|
  status = RtlAbsoluteToSelfRelativeSD (&sd, sd_ret, &sd_size);
 | 
						|
  if (sd_size > 0 && sd_ret.malloc (sd_size))
 | 
						|
    RtlAbsoluteToSelfRelativeSD (&sd, sd_ret, &sd_size);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
check_file_access (path_conv &pc, int flags, bool effective)
 | 
						|
{
 | 
						|
  security_descriptor sd;
 | 
						|
  int ret = -1;
 | 
						|
  ACCESS_MASK desired = 0;
 | 
						|
  if (flags & R_OK)
 | 
						|
    desired |= FILE_READ_DATA;
 | 
						|
  if (flags & W_OK)
 | 
						|
    desired |= FILE_WRITE_DATA;
 | 
						|
  if (flags & X_OK)
 | 
						|
    desired |= FILE_EXECUTE;
 | 
						|
  if (!get_file_sd (pc.handle (), pc, sd, false))
 | 
						|
    {
 | 
						|
      /* Tweak Samba security descriptor as necessary. */
 | 
						|
      if (pc.fs_is_samba ())
 | 
						|
	convert_samba_sd (sd);
 | 
						|
      ret = check_access (sd, file_mapping, desired, flags, effective);
 | 
						|
    }
 | 
						|
  debug_printf ("flags %y, ret %d", flags, ret);
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
check_registry_access (HANDLE hdl, int flags, bool effective)
 | 
						|
{
 | 
						|
  security_descriptor sd;
 | 
						|
  int ret = -1;
 | 
						|
  static GENERIC_MAPPING NO_COPY_RO reg_mapping = { KEY_READ,
 | 
						|
						    KEY_WRITE,
 | 
						|
						    KEY_EXECUTE,
 | 
						|
						    KEY_ALL_ACCESS };
 | 
						|
  ACCESS_MASK desired = 0;
 | 
						|
  if (flags & R_OK)
 | 
						|
    desired |= KEY_ENUMERATE_SUB_KEYS;
 | 
						|
  if (flags & W_OK)
 | 
						|
    desired |= KEY_SET_VALUE;
 | 
						|
  if (flags & X_OK)
 | 
						|
    desired |= KEY_QUERY_VALUE;
 | 
						|
 | 
						|
  if ((HKEY) hdl == HKEY_PERFORMANCE_DATA)
 | 
						|
    /* RegGetKeySecurity() always fails with ERROR_INVALID_HANDLE.  */
 | 
						|
    ret = 0;
 | 
						|
  else if (!get_reg_sd (hdl, sd))
 | 
						|
    ret = check_access (sd, reg_mapping, desired, flags, effective);
 | 
						|
 | 
						|
  /* As long as we can't write the registry... */
 | 
						|
  if (flags & W_OK)
 | 
						|
    {
 | 
						|
      set_errno (EROFS);
 | 
						|
      ret = -1;
 | 
						|
    }
 | 
						|
  debug_printf ("flags %y, ret %d", flags, ret);
 | 
						|
  return ret;
 | 
						|
}
 |