2070 lines
		
	
	
		
			58 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			2070 lines
		
	
	
		
			58 KiB
		
	
	
	
		
			C++
		
	
	
	
/* sec_acl.cc: Solaris compatible ACL functions.
 | 
						|
 | 
						|
   Written 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 <stdlib.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include "cygerrno.h"
 | 
						|
#include "security.h"
 | 
						|
#include "path.h"
 | 
						|
#include "fhandler.h"
 | 
						|
#include "dtable.h"
 | 
						|
#include "cygheap.h"
 | 
						|
#include "ntdll.h"
 | 
						|
#include "tls_pbuf.h"
 | 
						|
#include "sec_posixacl.h"
 | 
						|
 | 
						|
/* How does a correctly constructed new-style Windows ACL claiming to be a
 | 
						|
   POSIX ACL look like?
 | 
						|
 | 
						|
   - NULL deny ACE (special bits, CLASS_OBJ).
 | 
						|
 | 
						|
   - USER_OBJ deny.  If the user has less permissions than the sum of CLASS_OBJ
 | 
						|
     (or GROUP_OBJ if CLASS_OBJ doesn't exist) and OTHER_OBJ, deny the excess
 | 
						|
     permissions so that group and other perms don't spill into the owner perms.
 | 
						|
 | 
						|
       USER_OBJ deny ACE   == ~USER_OBJ & (CLASS_OBJ | OTHER_OBJ)
 | 
						|
     or
 | 
						|
       USER_OBJ deny ACE   == ~USER_OBJ & (GROUP_OBJ | OTHER_OBJ)
 | 
						|
 | 
						|
   - USER deny.  If a user has more permissions than CLASS_OBJ, or if the
 | 
						|
     user has less permissions than OTHER_OBJ, deny the excess permissions.
 | 
						|
 | 
						|
       USER deny ACE       == (USER & ~CLASS_OBJ) | (~USER & OTHER_OBJ)
 | 
						|
 | 
						|
   - USER_OBJ  allow ACE
 | 
						|
   - USER      allow ACEs
 | 
						|
 | 
						|
   The POSIX permissions returned for a USER entry are the allow bits alone!
 | 
						|
 | 
						|
   - GROUP{_OBJ} deny.  If a group has more permissions than CLASS_OBJ,
 | 
						|
     or less permissions than OTHER_OBJ, deny the excess permissions.
 | 
						|
 | 
						|
       GROUP{_OBJ} deny ACEs  == (GROUP & ~CLASS_OBJ)
 | 
						|
 | 
						|
   - GROUP_OBJ	allow ACE
 | 
						|
   - GROUP	allow ACEs
 | 
						|
 | 
						|
   The POSIX permissions returned for a GROUP entry are the allow bits alone!
 | 
						|
 | 
						|
   - 2. GROUP{_OBJ} deny.  If a group has less permissions than OTHER_OBJ,
 | 
						|
     deny the excess permissions.
 | 
						|
 | 
						|
       2. GROUP{_OBJ} deny ACEs  == (~GROUP & OTHER_OBJ)
 | 
						|
 | 
						|
   - OTHER_OBJ	allow ACE
 | 
						|
 | 
						|
   Rinse and repeat for default ACEs with INHERIT flags set.
 | 
						|
 | 
						|
   - Default NULL deny ACE (S_ISGID, CLASS_OBJ). */
 | 
						|
 | 
						|
						/* POSIX <-> Win32 */
 | 
						|
 | 
						|
/* Historically, these bits are stored in a NULL allow SID ACE.  To distinguish
 | 
						|
   the new ACL style from the old one, we're using an access denied ACE, plus
 | 
						|
   setting an as yet unused bit in the access mask.  The new ACEs can exist
 | 
						|
   twice in an ACL, the "normal one" containing CLASS_OBJ and special bits
 | 
						|
   and the one with INHERIT bit set to pass the DEF_CLASS_OBJ bits and the
 | 
						|
   S_ISGID bit on. */
 | 
						|
#define CYG_ACE_ISVTX		0x001		/* 0x200 <-> 0x001 */
 | 
						|
#define CYG_ACE_ISGID		0x002		/* 0x400 <-> 0x002 */
 | 
						|
#define CYG_ACE_ISUID		0x004		/* 0x800 <-> 0x004 */
 | 
						|
#define CYG_ACE_ISBITS_TO_POSIX(val)	\
 | 
						|
				(((val) & 0x007) << 9)
 | 
						|
#define CYG_ACE_ISBITS_TO_WIN(val) \
 | 
						|
				(((val) & (S_ISVTX | S_ISUID | S_ISGID)) >> 9)
 | 
						|
 | 
						|
#define CYG_ACE_MASK_X		0x008		/* 0x001 <-> 0x008 */
 | 
						|
#define CYG_ACE_MASK_W		0x010		/* 0x002 <-> 0x010 */
 | 
						|
#define CYG_ACE_MASK_R		0x020		/* 0x004 <-> 0x020 */
 | 
						|
#define CYG_ACE_MASK_RWX	0x038
 | 
						|
#define CYG_ACE_MASK_VALID	0x040		/* has mask if set */
 | 
						|
#define CYG_ACE_MASK_TO_POSIX(val)	\
 | 
						|
				(((val) & CYG_ACE_MASK_RWX) >> 3)
 | 
						|
#define CYG_ACE_MASK_TO_WIN(val)	\
 | 
						|
				((((val) & S_IRWXO) << 3) \
 | 
						|
				 | CYG_ACE_MASK_VALID)
 | 
						|
#define CYG_ACE_NEW_STYLE	READ_CONTROL	/* New style if set. */
 | 
						|
 | 
						|
/* Define own bit masks rather than using the GENERIC masks.  The latter
 | 
						|
   also contain standard rights, which we don't need here. */
 | 
						|
#define FILE_ALLOW_READ		(FILE_READ_DATA | FILE_READ_ATTRIBUTES | \
 | 
						|
				 FILE_READ_EA)
 | 
						|
#define FILE_DENY_READ		(FILE_READ_DATA | FILE_READ_EA)
 | 
						|
#define FILE_ALLOW_WRITE	(FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | \
 | 
						|
				 FILE_WRITE_EA | FILE_APPEND_DATA)
 | 
						|
#define FILE_DENY_WRITE		FILE_ALLOW_WRITE | FILE_DELETE_CHILD
 | 
						|
#define FILE_DENY_WRITE_OWNER	(FILE_WRITE_DATA | FILE_WRITE_EA | \
 | 
						|
				 FILE_APPEND_DATA | FILE_DELETE_CHILD)
 | 
						|
#define FILE_ALLOW_EXEC		(FILE_EXECUTE)
 | 
						|
#define FILE_DENY_EXEC		FILE_ALLOW_EXEC
 | 
						|
 | 
						|
#define STD_RIGHTS_OTHER	(STANDARD_RIGHTS_READ | SYNCHRONIZE)
 | 
						|
#define STD_RIGHTS_OWNER	(STANDARD_RIGHTS_ALL | SYNCHRONIZE)
 | 
						|
 | 
						|
int
 | 
						|
searchace (aclent_t *aclp, int nentries, int type, uid_t id)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
 | 
						|
  for (i = 0; i < nentries; ++i)
 | 
						|
    if ((aclp[i].a_type == type
 | 
						|
	 && (id == ACL_UNDEFINED_ID || aclp[i].a_id == id))
 | 
						|
	|| !aclp[i].a_type)
 | 
						|
      return i;
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
/* From the attributes and the POSIX ACL list, compute a new-style Cygwin
 | 
						|
   security descriptor.  The function returns a pointer to the
 | 
						|
   SECURITY_DESCRIPTOR in sd_ret, or NULL if the function fails.
 | 
						|
 | 
						|
   This function *requires* a verified and sorted acl list! */
 | 
						|
PSECURITY_DESCRIPTOR
 | 
						|
set_posix_access (mode_t attr, uid_t uid, gid_t gid,
 | 
						|
		  aclent_t *aclbufp, int nentries,
 | 
						|
		  security_descriptor &sd_ret,
 | 
						|
		  bool is_samba)
 | 
						|
{
 | 
						|
  SECURITY_DESCRIPTOR sd;
 | 
						|
  cyg_ldap cldap;
 | 
						|
  PSID owner = NULL, group = NULL;
 | 
						|
  cygsid smb_owner, smb_group;
 | 
						|
  NTSTATUS status;
 | 
						|
  tmp_pathbuf tp;
 | 
						|
  cygpsid *aclsid;
 | 
						|
  PACL acl;
 | 
						|
  size_t acl_len = sizeof (ACL);
 | 
						|
  mode_t user_obj, group_obj, other_obj, deny;
 | 
						|
  mode_t class_obj = 0;
 | 
						|
  DWORD access;
 | 
						|
  int idx, start_idx, tmp_idx;
 | 
						|
  bool owner_eq_group = false;
 | 
						|
  bool dev_has_admins = false;
 | 
						|
  bool has_class_obj;
 | 
						|
 | 
						|
  /* Initialize local security descriptor. */
 | 
						|
  RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
 | 
						|
 | 
						|
  /* As in alloc_sd, set SE_DACL_PROTECTED to prevent the DACL from being
 | 
						|
     modified by inheritable ACEs. */
 | 
						|
  RtlSetControlSecurityDescriptor (&sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED);
 | 
						|
 | 
						|
  /* Fetch owner and group and set in security descriptor.
 | 
						|
 | 
						|
     For Samba we check if there's an RFC2307 mapping in place, otherwise
 | 
						|
     we're trying to create an ACL with the wrong Windows SIDs rather than
 | 
						|
     the correct Unix SIDs.  Same happens below for mapping other USER and
 | 
						|
     GROUP SIDs. */
 | 
						|
  if (is_samba)
 | 
						|
    {
 | 
						|
      uint32_t smb_uid, smb_gid;
 | 
						|
 | 
						|
      smb_uid = cygheap->ugid_cache.reverse_get_uid (uid);
 | 
						|
      if (smb_uid != ILLEGAL_UID)
 | 
						|
	owner = smb_owner.create (22, 2, 1, smb_uid);
 | 
						|
      smb_gid = cygheap->ugid_cache.reverse_get_gid (gid);
 | 
						|
      if (smb_gid != ILLEGAL_GID)
 | 
						|
	group = smb_group.create (22, 2, 2, smb_gid);
 | 
						|
    }
 | 
						|
  if (!owner)
 | 
						|
    owner = sidfromuid (uid, &cldap);
 | 
						|
  if (!group)
 | 
						|
    group = sidfromgid (gid, &cldap);
 | 
						|
  if (!owner || !group)
 | 
						|
    {
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
  status = RtlSetOwnerSecurityDescriptor (&sd, owner, FALSE);
 | 
						|
  if (!NT_SUCCESS (status))
 | 
						|
    {
 | 
						|
      __seterrno_from_nt_status (status);
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
  status = RtlSetGroupSecurityDescriptor (&sd, group, FALSE);
 | 
						|
  if (!NT_SUCCESS (status))
 | 
						|
    {
 | 
						|
      __seterrno_from_nt_status (status);
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
  owner_eq_group = RtlEqualSid (owner, group);
 | 
						|
  if (S_ISCHR (attr))
 | 
						|
    dev_has_admins = well_known_admins_sid == owner
 | 
						|
		     || well_known_admins_sid == group;
 | 
						|
 | 
						|
  /* No POSIX ACL?  Use attr to generate one from scratch. */
 | 
						|
  if (!aclbufp)
 | 
						|
    {
 | 
						|
      aclbufp = (aclent_t *) tp.c_get ();
 | 
						|
      aclbufp[0].a_type = USER_OBJ;
 | 
						|
      aclbufp[0].a_id = ACL_UNDEFINED_ID;
 | 
						|
      aclbufp[0].a_perm = (attr >> 6) & S_IRWXO;
 | 
						|
      aclbufp[1].a_type = GROUP_OBJ;
 | 
						|
      aclbufp[1].a_id = ACL_UNDEFINED_ID;
 | 
						|
      aclbufp[1].a_perm = (attr >> 3) & S_IRWXO;
 | 
						|
      aclbufp[2].a_type = OTHER_OBJ;
 | 
						|
      aclbufp[2].a_id = ACL_UNDEFINED_ID;
 | 
						|
      aclbufp[2].a_perm = attr & S_IRWXO;
 | 
						|
      nentries = MIN_ACL_ENTRIES;
 | 
						|
      if (S_ISDIR (attr))
 | 
						|
	{
 | 
						|
	  aclbufp[3].a_type = DEF_USER_OBJ;
 | 
						|
	  aclbufp[3].a_id = ACL_UNDEFINED_ID;
 | 
						|
	  aclbufp[3].a_perm = (attr >> 6) & S_IRWXO;
 | 
						|
	  aclbufp[4].a_type = GROUP_OBJ;
 | 
						|
	  aclbufp[4].a_id = ACL_UNDEFINED_ID;
 | 
						|
	  aclbufp[4].a_perm = (attr >> 3) & S_IRWXO;
 | 
						|
	  aclbufp[5].a_type = OTHER_OBJ;
 | 
						|
	  aclbufp[5].a_id = ACL_UNDEFINED_ID;
 | 
						|
	  aclbufp[5].a_perm = attr & S_IRWXO;
 | 
						|
	  nentries += MIN_ACL_ENTRIES;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  /* Collect SIDs of all entries in aclbufp. */
 | 
						|
  aclsid = (cygpsid *) tp.w_get ();
 | 
						|
  for (idx = 0; idx < nentries; ++idx)
 | 
						|
    switch (aclbufp[idx].a_type)
 | 
						|
      {
 | 
						|
      case USER_OBJ:
 | 
						|
	aclsid[idx] = owner;
 | 
						|
	break;
 | 
						|
      case DEF_USER_OBJ:
 | 
						|
	aclsid[idx] = well_known_creator_owner_sid;
 | 
						|
	break;
 | 
						|
      case USER:
 | 
						|
      case DEF_USER:
 | 
						|
	aclsid[idx] = NO_SID;
 | 
						|
	if (is_samba)
 | 
						|
	  {
 | 
						|
	    uint32_t smb_uid;
 | 
						|
	    cygsid *smb_sid;
 | 
						|
 | 
						|
	    smb_uid = cygheap->ugid_cache.reverse_get_uid (aclbufp[idx].a_id);
 | 
						|
	    if (smb_uid != ILLEGAL_UID)
 | 
						|
	      {
 | 
						|
		smb_sid = (cygsid *) alloca (sizeof (cygsid));
 | 
						|
		aclsid[idx] = smb_sid->create (22, 2, 1, smb_uid);
 | 
						|
	      }
 | 
						|
	  }
 | 
						|
	if (!aclsid[idx])
 | 
						|
	  aclsid[idx] = sidfromuid (aclbufp[idx].a_id, &cldap);
 | 
						|
	break;
 | 
						|
      case GROUP_OBJ:
 | 
						|
	aclsid[idx] = group;
 | 
						|
	break;
 | 
						|
      case DEF_GROUP_OBJ:
 | 
						|
	aclsid[idx] = !(attr & S_ISGID) ? (PSID) well_known_creator_group_sid
 | 
						|
					: group;
 | 
						|
	break;
 | 
						|
      case GROUP:
 | 
						|
      case DEF_GROUP:
 | 
						|
	aclsid[idx] = NO_SID;
 | 
						|
	if (is_samba)
 | 
						|
	  {
 | 
						|
	    uint32_t smb_gid;
 | 
						|
	    cygsid *smb_sid;
 | 
						|
 | 
						|
	    smb_gid = cygheap->ugid_cache.reverse_get_gid (aclbufp[idx].a_id);
 | 
						|
	    if (smb_gid != ILLEGAL_GID)
 | 
						|
	      {
 | 
						|
		smb_sid = (cygsid *) alloca (sizeof (cygsid));
 | 
						|
		aclsid[idx] = smb_sid->create (22, 2, 2, smb_gid);
 | 
						|
	      }
 | 
						|
	  }
 | 
						|
	if (!aclsid[idx])
 | 
						|
	  aclsid[idx] = sidfromgid (aclbufp[idx].a_id, &cldap);
 | 
						|
	break;
 | 
						|
      case CLASS_OBJ:
 | 
						|
      case DEF_CLASS_OBJ:
 | 
						|
	aclsid[idx] = well_known_null_sid;
 | 
						|
	break;
 | 
						|
      case OTHER_OBJ:
 | 
						|
      case DEF_OTHER_OBJ:
 | 
						|
	aclsid[idx] = well_known_world_sid;
 | 
						|
	break;
 | 
						|
      }
 | 
						|
 | 
						|
  /* Initialize ACL. */
 | 
						|
  acl = (PACL) tp.w_get ();
 | 
						|
  RtlCreateAcl (acl, ACL_MAXIMUM_SIZE, ACL_REVISION);
 | 
						|
 | 
						|
  /* This loop has two runs, the first handling the actual permission,
 | 
						|
     the second handling the default permissions. */
 | 
						|
  idx = 0;
 | 
						|
  for (int def = 0; def <= ACL_DEFAULT; def += ACL_DEFAULT)
 | 
						|
    {
 | 
						|
      DWORD inherit = def ? SUB_CONTAINERS_AND_OBJECTS_INHERIT | INHERIT_ONLY
 | 
						|
			  : NO_INHERITANCE;
 | 
						|
 | 
						|
      /* No default ACEs on files. */
 | 
						|
      if (def && !S_ISDIR (attr))
 | 
						|
	{
 | 
						|
	  /* Trying to set default ACEs on a non-directory is an error.
 | 
						|
	     The underlying functions on Linux return EACCES. */
 | 
						|
	  if (idx < nentries && aclbufp[idx].a_type & ACL_DEFAULT)
 | 
						|
	    {
 | 
						|
	      set_errno (EACCES);
 | 
						|
	      return NULL;
 | 
						|
	    }
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
 | 
						|
      /* To check if the NULL SID deny ACE is required we need user_obj.  */
 | 
						|
      tmp_idx = searchace (aclbufp, nentries, def | USER_OBJ);
 | 
						|
      /* No default entries present? */
 | 
						|
      if (tmp_idx < 0)
 | 
						|
	break;
 | 
						|
      user_obj = aclbufp[tmp_idx].a_perm;
 | 
						|
      /* To compute deny access masks, we need group_obj, other_obj and... */
 | 
						|
      tmp_idx = searchace (aclbufp, nentries, def | GROUP_OBJ);
 | 
						|
      group_obj = aclbufp[tmp_idx].a_perm;
 | 
						|
      tmp_idx = searchace (aclbufp, nentries, def | OTHER_OBJ);
 | 
						|
      other_obj = aclbufp[tmp_idx].a_perm;
 | 
						|
 | 
						|
      /* ... class_obj.  Create NULL deny ACE.  Only the S_ISGID attribute gets
 | 
						|
	 inherited.  For directories check if we are also going to generate
 | 
						|
	 default entries.  If not we have a problem.  We can't generate only a
 | 
						|
	 single, inheritable NULL SID ACE because that leads to (fixable, TODO)
 | 
						|
	 access problems when trying to create the matching child permissions.
 | 
						|
	 Therefore we remove the S_ISGID bit on the directory because having it
 | 
						|
	 set would be misleading. */
 | 
						|
      if (!def && S_ISDIR (attr) && (attr & S_ISGID))
 | 
						|
	{
 | 
						|
	  /* Check for a required entry per POSIX. */
 | 
						|
	  tmp_idx = searchace (aclbufp, nentries, DEF_USER_OBJ);
 | 
						|
	  if (tmp_idx < 0)
 | 
						|
	    attr &= ~S_ISGID;
 | 
						|
	}
 | 
						|
      access = CYG_ACE_ISBITS_TO_WIN (def ? attr & S_ISGID : attr)
 | 
						|
	       | CYG_ACE_NEW_STYLE;
 | 
						|
      tmp_idx = searchace (aclbufp, nentries, def | CLASS_OBJ);
 | 
						|
      if (tmp_idx >= 0)
 | 
						|
	{
 | 
						|
	  has_class_obj = true;
 | 
						|
	  class_obj = aclbufp[tmp_idx].a_perm;
 | 
						|
	  access |= CYG_ACE_MASK_TO_WIN (class_obj);
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  /* Setting class_obj to group_obj allows to write below code without
 | 
						|
	     additional checks for existence of a CLASS_OBJ. */
 | 
						|
	  has_class_obj = false;
 | 
						|
	  class_obj = group_obj;
 | 
						|
	}
 | 
						|
      /* Note that Windows filters the ACE Mask value so it only reflects
 | 
						|
	 the bit values supported by the object type.  The result is that
 | 
						|
	 we can't set a CLASS_OBJ value for ptys.  The get_posix_access
 | 
						|
	 function has to workaround that.
 | 
						|
 | 
						|
	 We also don't write the NULL SID ACE in case we have a simple POSIX
 | 
						|
	 permission ACL with the user perms >= group perms >= other perms and
 | 
						|
	 no special bits set.  In all other cases we either need the NULL SID
 | 
						|
	 ACE or we write it to avoid calls to AuthZ from get_posix_access. */
 | 
						|
      if (!S_ISCHR (attr)
 | 
						|
	  && (has_class_obj
 | 
						|
	      || access != CYG_ACE_NEW_STYLE
 | 
						|
	      || ((user_obj | group_obj | other_obj) != user_obj
 | 
						|
		  || (group_obj | other_obj) != group_obj))
 | 
						|
	  && !add_access_denied_ace (acl, access, well_known_null_sid, acl_len,
 | 
						|
				     inherit))
 | 
						|
	return NULL;
 | 
						|
 | 
						|
      /* Do we potentially chmod a file with owner SID == group SID?  If so,
 | 
						|
	 make sure the owner perms are always >= group perms. */
 | 
						|
      if (!def && owner_eq_group)
 | 
						|
	  aclbufp[0].a_perm |= group_obj & class_obj;
 | 
						|
 | 
						|
      /* This loop has two runs, the first w/ check_types == (USER_OBJ | USER),
 | 
						|
	 the second w/ check_types == (GROUP_OBJ | GROUP).  Each run creates
 | 
						|
	 first the deny, then the allow ACEs for the current types. */
 | 
						|
      for (int check_types = USER_OBJ | USER;
 | 
						|
	   check_types < CLASS_OBJ;
 | 
						|
	   check_types <<= 2)
 | 
						|
	{
 | 
						|
	  /* Create deny ACEs for users, then 1st run for groups.  For groups,
 | 
						|
	     only take CLASS_OBJ permissions into account.  Class permissions
 | 
						|
	     are handled in the 2nd deny loop below. */
 | 
						|
	  for (start_idx = idx;
 | 
						|
	       idx < nentries && aclbufp[idx].a_type & check_types;
 | 
						|
	       ++idx)
 | 
						|
	    {
 | 
						|
	      /* Avoid creating DENY ACEs for the second occurrence of
 | 
						|
		 accounts which show up twice, as USER_OBJ and USER, or
 | 
						|
		 GROUP_OBJ and GROUP. */
 | 
						|
	      if ((aclbufp[idx].a_type & USER && aclsid[idx] == owner)
 | 
						|
		  || (aclbufp[idx].a_type & GROUP && aclsid[idx] == group))
 | 
						|
		continue;
 | 
						|
	      /* For the rules how to construct the deny access mask, see the
 | 
						|
		 comment right at the start of this file. */
 | 
						|
	      if (aclbufp[idx].a_type & USER_OBJ)
 | 
						|
		deny = ~aclbufp[idx].a_perm & (class_obj | other_obj);
 | 
						|
	      else if (aclbufp[idx].a_type & USER)
 | 
						|
		deny = (aclbufp[idx].a_perm & ~class_obj)
 | 
						|
		       | (~aclbufp[idx].a_perm & other_obj);
 | 
						|
	      /* Accommodate Windows: Only generate deny masks for SYSTEM
 | 
						|
		 and the Administrators group in terms of the execute bit,
 | 
						|
		 if they are not the primary group. */
 | 
						|
	      else if (aclbufp[idx].a_type & GROUP
 | 
						|
		       && (aclsid[idx] == well_known_system_sid
 | 
						|
			   || aclsid[idx] == well_known_admins_sid))
 | 
						|
		deny = aclbufp[idx].a_perm & ~(class_obj | S_IROTH | S_IWOTH);
 | 
						|
	      else
 | 
						|
		deny = (aclbufp[idx].a_perm & ~class_obj);
 | 
						|
	      if (!deny)
 | 
						|
		continue;
 | 
						|
	      access = 0;
 | 
						|
	      if (deny & S_IROTH)
 | 
						|
		access |= FILE_DENY_READ;
 | 
						|
	      if (deny & S_IWOTH)
 | 
						|
		access |= (aclbufp[idx].a_type & USER_OBJ)
 | 
						|
			  ? FILE_DENY_WRITE_OWNER : FILE_DENY_WRITE;
 | 
						|
	      if (deny & S_IXOTH)
 | 
						|
		access |= FILE_DENY_EXEC;
 | 
						|
	      if (!add_access_denied_ace (acl, access, aclsid[idx], acl_len,
 | 
						|
					  inherit))
 | 
						|
		return NULL;
 | 
						|
	    }
 | 
						|
	  /* Create allow ACEs for users, then groups. */
 | 
						|
	  for (idx = start_idx;
 | 
						|
	       idx < nentries && aclbufp[idx].a_type & check_types;
 | 
						|
	       ++idx)
 | 
						|
	    {
 | 
						|
	      /* Don't set FILE_READ/WRITE_ATTRIBUTES unconditionally on Samba,
 | 
						|
		 otherwise it enforces read permissions. */
 | 
						|
	      access = STD_RIGHTS_OTHER | (is_samba ? 0 : FILE_READ_ATTRIBUTES);
 | 
						|
	      if (aclbufp[idx].a_type & USER_OBJ)
 | 
						|
		{
 | 
						|
		  access |= STD_RIGHTS_OWNER;
 | 
						|
		  if (!is_samba)
 | 
						|
		    access |= FILE_WRITE_ATTRIBUTES;
 | 
						|
		  /* Set FILE_DELETE_CHILD on files with "rwx" perms for the
 | 
						|
		     owner so that the owner gets "full control" (Duh). */
 | 
						|
		  if (aclbufp[idx].a_perm == S_IRWXO)
 | 
						|
		    access |= FILE_DELETE_CHILD;
 | 
						|
		}
 | 
						|
	      if (aclbufp[idx].a_perm & S_IROTH)
 | 
						|
		access |= FILE_ALLOW_READ;
 | 
						|
	      if (aclbufp[idx].a_perm & S_IWOTH)
 | 
						|
		access |= FILE_ALLOW_WRITE;
 | 
						|
	      if (aclbufp[idx].a_perm & S_IXOTH)
 | 
						|
		access |= FILE_ALLOW_EXEC;
 | 
						|
	      /* Handle S_ISVTX. */
 | 
						|
	      if (S_ISDIR (attr)
 | 
						|
		  && (aclbufp[idx].a_perm & (S_IWOTH | S_IXOTH))
 | 
						|
		     == (S_IWOTH | S_IXOTH)
 | 
						|
		  && (!(attr & S_ISVTX) || aclbufp[idx].a_type & USER_OBJ))
 | 
						|
		access |= FILE_DELETE_CHILD;
 | 
						|
	      /* For ptys, make sure the Administrators group has WRITE_DAC
 | 
						|
		 and WRITE_OWNER perms. */
 | 
						|
	      if (dev_has_admins && aclsid[idx] == well_known_admins_sid)
 | 
						|
		access |= STD_RIGHTS_OWNER;
 | 
						|
	      if (!add_access_allowed_ace (acl, access, aclsid[idx], acl_len,
 | 
						|
					   inherit))
 | 
						|
		return NULL;
 | 
						|
	    }
 | 
						|
	  /* 2nd deny loop: Create deny ACEs for groups when they have less
 | 
						|
	     permissions than OTHER_OBJ. */
 | 
						|
	  if (check_types == (GROUP_OBJ | GROUP))
 | 
						|
	    for (idx = start_idx;
 | 
						|
		 idx < nentries && aclbufp[idx].a_type & check_types;
 | 
						|
		 ++idx)
 | 
						|
	      {
 | 
						|
		if (aclbufp[idx].a_type & GROUP && aclsid[idx] == group)
 | 
						|
		  continue;
 | 
						|
		/* Only generate deny masks for SYSTEM and the Administrators
 | 
						|
		   group if they are the primary group. */
 | 
						|
		if (aclbufp[idx].a_type & GROUP
 | 
						|
		    && (aclsid[idx] == well_known_system_sid
 | 
						|
			|| aclsid[idx] == well_known_admins_sid))
 | 
						|
		  deny = 0;
 | 
						|
		else
 | 
						|
		  deny = (~aclbufp[idx].a_perm & other_obj);
 | 
						|
		if (!deny)
 | 
						|
		  continue;
 | 
						|
		access = 0;
 | 
						|
		if (deny & S_IROTH)
 | 
						|
		  access |= FILE_DENY_READ;
 | 
						|
		if (deny & S_IWOTH)
 | 
						|
		  access |= FILE_DENY_WRITE;
 | 
						|
		if (deny & S_IXOTH)
 | 
						|
		  access |= FILE_DENY_EXEC;
 | 
						|
		if (!add_access_denied_ace (acl, access, aclsid[idx], acl_len,
 | 
						|
					    inherit))
 | 
						|
		  return NULL;
 | 
						|
	      }
 | 
						|
	}
 | 
						|
      /* For ptys if the admins group isn't in the ACL, add an ACE to make
 | 
						|
	 sure the admins group has WRITE_DAC and WRITE_OWNER perms. */
 | 
						|
      if (S_ISCHR (attr) && !dev_has_admins
 | 
						|
	  && !add_access_allowed_ace (acl,
 | 
						|
				      STD_RIGHTS_OWNER | FILE_ALLOW_READ
 | 
						|
				      | FILE_ALLOW_WRITE,
 | 
						|
				      well_known_admins_sid, acl_len,
 | 
						|
				      NO_INHERITANCE))
 | 
						|
	return NULL;
 | 
						|
      /* Create allow ACE for other.  It's preceeded by class_obj if it exists.
 | 
						|
	 If so, skip it. */
 | 
						|
      if (aclbufp[idx].a_type & CLASS_OBJ)
 | 
						|
	++idx;
 | 
						|
      access = STD_RIGHTS_OTHER | (is_samba ? 0 : FILE_READ_ATTRIBUTES);
 | 
						|
      if (aclbufp[idx].a_perm & S_IROTH)
 | 
						|
	access |= FILE_ALLOW_READ;
 | 
						|
      if (aclbufp[idx].a_perm & S_IWOTH)
 | 
						|
	access |= FILE_ALLOW_WRITE;
 | 
						|
      if (aclbufp[idx].a_perm & S_IXOTH)
 | 
						|
	access |= FILE_ALLOW_EXEC;
 | 
						|
      /* Handle S_ISVTX. */
 | 
						|
      if (S_ISDIR (attr)
 | 
						|
	  && (aclbufp[idx].a_perm & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH)
 | 
						|
	  && !(attr & S_ISVTX))
 | 
						|
	access |= FILE_DELETE_CHILD;
 | 
						|
      if (!add_access_allowed_ace (acl, access, aclsid[idx++], acl_len,
 | 
						|
				   inherit))
 | 
						|
	return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Set AclSize to computed value. */
 | 
						|
  acl->AclSize = acl_len;
 | 
						|
  debug_printf ("ACL-Size: %u", acl_len);
 | 
						|
  /* Create DACL for local security descriptor. */
 | 
						|
  status = RtlSetDaclSecurityDescriptor (&sd, TRUE, acl, FALSE);
 | 
						|
  if (!NT_SUCCESS (status))
 | 
						|
    {
 | 
						|
      __seterrno_from_nt_status (status);
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
  /* Make self relative security descriptor in sd_ret. */
 | 
						|
  DWORD sd_size = 0;
 | 
						|
  RtlAbsoluteToSelfRelativeSD (&sd, sd_ret, &sd_size);
 | 
						|
  if (sd_size <= 0)
 | 
						|
    {
 | 
						|
      __seterrno ();
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
  if (!sd_ret.realloc (sd_size))
 | 
						|
    {
 | 
						|
      set_errno (ENOMEM);
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
  status = RtlAbsoluteToSelfRelativeSD (&sd, sd_ret, &sd_size);
 | 
						|
  if (!NT_SUCCESS (status))
 | 
						|
    {
 | 
						|
      __seterrno_from_nt_status (status);
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
  debug_printf ("Created SD-Size: %u", sd_ret.size ());
 | 
						|
  return sd_ret;
 | 
						|
}
 | 
						|
 | 
						|
/* This function *requires* a verified and sorted acl list! */
 | 
						|
int
 | 
						|
setacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp,
 | 
						|
	bool &writable)
 | 
						|
{
 | 
						|
  security_descriptor sd, sd_ret;
 | 
						|
  mode_t attr = pc.isdir () ? S_IFDIR : 0;
 | 
						|
  uid_t uid;
 | 
						|
  gid_t gid;
 | 
						|
 | 
						|
  if (get_file_sd (handle, pc, sd, false))
 | 
						|
    return -1;
 | 
						|
  if (get_posix_access (sd, &attr, &uid, &gid, NULL, 0) < 0)
 | 
						|
    return -1;
 | 
						|
  if (!set_posix_access (attr, uid, gid, aclbufp, nentries,
 | 
						|
			 sd_ret, pc.fs_is_samba ()))
 | 
						|
    return -1;
 | 
						|
  /* FIXME?  Caller needs to know if any write perms are set to allow removing
 | 
						|
     the DOS R/O bit. */
 | 
						|
  writable = true;
 | 
						|
  return set_file_sd (handle, pc, sd_ret, false);
 | 
						|
}
 | 
						|
 | 
						|
/* Temporary access denied bits used by getace and get_posix_access during
 | 
						|
   Windows ACL processing.  These bits get removed before the created POSIX
 | 
						|
   ACL gets published. */
 | 
						|
#define DENY_R 040000
 | 
						|
#define DENY_W 020000
 | 
						|
#define DENY_X 010000
 | 
						|
#define DENY_RWX (DENY_R | DENY_W | DENY_X)
 | 
						|
 | 
						|
/* New style ACL means, just read the bits and store them away.  Don't
 | 
						|
   create masked values on your own. */
 | 
						|
static void
 | 
						|
getace (aclent_t &acl, int type, int id, DWORD win_ace_mask,
 | 
						|
	DWORD win_ace_type, bool new_style)
 | 
						|
{
 | 
						|
  acl.a_type = type;
 | 
						|
  acl.a_id = id;
 | 
						|
 | 
						|
  if ((win_ace_mask & FILE_READ_BITS)
 | 
						|
      && (new_style || !(acl.a_perm & (S_IROTH | DENY_R))))
 | 
						|
    {
 | 
						|
      if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE)
 | 
						|
	acl.a_perm |= S_IROTH;
 | 
						|
      else if (win_ace_type == ACCESS_DENIED_ACE_TYPE)
 | 
						|
	acl.a_perm |= DENY_R;
 | 
						|
    }
 | 
						|
 | 
						|
  if ((win_ace_mask & FILE_WRITE_BITS)
 | 
						|
      && (new_style || !(acl.a_perm & (S_IWOTH | DENY_W))))
 | 
						|
    {
 | 
						|
      if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE)
 | 
						|
	acl.a_perm |= S_IWOTH;
 | 
						|
      else if (win_ace_type == ACCESS_DENIED_ACE_TYPE)
 | 
						|
	acl.a_perm |= DENY_W;
 | 
						|
    }
 | 
						|
 | 
						|
  if ((win_ace_mask & FILE_EXEC_BITS)
 | 
						|
      && (new_style || !(acl.a_perm & (S_IXOTH | DENY_X))))
 | 
						|
    {
 | 
						|
      if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE)
 | 
						|
	acl.a_perm |= S_IXOTH;
 | 
						|
      else if (win_ace_type == ACCESS_DENIED_ACE_TYPE)
 | 
						|
	acl.a_perm |= DENY_X;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* From the SECURITY_DESCRIPTOR given in psd, compute user, owner, posix
 | 
						|
   attributes, as well as the POSIX acl.  The function returns the number
 | 
						|
   of entries returned in aclbufp, or -1 in case of error.
 | 
						|
 | 
						|
   When called from chmod, it also returns the fact if the ACL is a "standard"
 | 
						|
   ACL.  A "standard" ACL is an ACL which only consists of ACEs for owner,
 | 
						|
   group, other, as well as (this is Windows) the Administrators group and
 | 
						|
   SYSTEM.  See fhandler_disk_file::fchmod for how this is used to fake
 | 
						|
   stock POSIX perms even if Administrators and SYSTEM is in the ACE. */
 | 
						|
int
 | 
						|
get_posix_access (PSECURITY_DESCRIPTOR psd,
 | 
						|
		  mode_t *attr_ret, uid_t *uid_ret, gid_t *gid_ret,
 | 
						|
		  aclent_t *aclbufp, int nentries, bool *std_acl)
 | 
						|
{
 | 
						|
  tmp_pathbuf tp;
 | 
						|
  NTSTATUS status;
 | 
						|
  BOOLEAN dummy, acl_exists;
 | 
						|
  SECURITY_DESCRIPTOR_CONTROL ctrl;
 | 
						|
  ULONG rev;
 | 
						|
  PACL acl;
 | 
						|
  PACCESS_ALLOWED_ACE ace;
 | 
						|
  cygpsid owner_sid, group_sid;
 | 
						|
  cyg_ldap cldap;
 | 
						|
  uid_t uid;
 | 
						|
  gid_t gid;
 | 
						|
  mode_t attr = 0;
 | 
						|
  aclent_t *lacl = NULL;
 | 
						|
  cygpsid ace_sid, *aclsid;
 | 
						|
  int pos, type, id, idx;
 | 
						|
 | 
						|
  bool owner_eq_group;
 | 
						|
  bool just_created = false;
 | 
						|
  bool standard_ACEs_only = true;
 | 
						|
  bool new_style = false;
 | 
						|
  bool saw_user_obj = false;
 | 
						|
  bool saw_group_obj = false;
 | 
						|
  bool saw_other_obj = false;
 | 
						|
  bool saw_def_user_obj = false;
 | 
						|
  bool saw_def_group_obj = false;
 | 
						|
  bool has_class_perm = false;
 | 
						|
  bool has_def_class_perm = false;
 | 
						|
 | 
						|
  mode_t class_perm = 0;
 | 
						|
  mode_t def_class_perm = 0;
 | 
						|
  int types_def = 0;
 | 
						|
  int def_pgrp_pos = -1;
 | 
						|
 | 
						|
  if (aclbufp && nentries < MIN_ACL_ENTRIES)
 | 
						|
    {
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  /* If reading the security descriptor failed, treat the object as
 | 
						|
     unreadable. */
 | 
						|
  if (!psd)
 | 
						|
    {
 | 
						|
      if (attr_ret)
 | 
						|
        *attr_ret &= S_IFMT;
 | 
						|
      if (uid_ret)
 | 
						|
        *uid_ret = ACL_UNDEFINED_ID;
 | 
						|
      if (gid_ret)
 | 
						|
        *gid_ret = ACL_UNDEFINED_ID;
 | 
						|
      if (aclbufp)
 | 
						|
	{
 | 
						|
	  aclbufp[0].a_type = USER_OBJ;
 | 
						|
	  aclbufp[0].a_id = ACL_UNDEFINED_ID;
 | 
						|
	  aclbufp[0].a_perm = 0;
 | 
						|
	  aclbufp[1].a_type = GROUP_OBJ;
 | 
						|
	  aclbufp[1].a_id = ACL_UNDEFINED_ID;
 | 
						|
	  aclbufp[1].a_perm = 0;
 | 
						|
	  aclbufp[2].a_type = OTHER_OBJ;
 | 
						|
	  aclbufp[2].a_id = ACL_UNDEFINED_ID;
 | 
						|
	  aclbufp[2].a_perm = 0;
 | 
						|
	  return MIN_ACL_ENTRIES;
 | 
						|
	}
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
  /* Fetch owner, group, and ACL from security descriptor. */
 | 
						|
  status = RtlGetOwnerSecurityDescriptor (psd, (PSID *) &owner_sid, &dummy);
 | 
						|
  if (!NT_SUCCESS (status))
 | 
						|
    {
 | 
						|
      __seterrno_from_nt_status (status);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  status = RtlGetGroupSecurityDescriptor (psd, (PSID *) &group_sid, &dummy);
 | 
						|
  if (!NT_SUCCESS (status))
 | 
						|
    {
 | 
						|
      __seterrno_from_nt_status (status);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  status = RtlGetDaclSecurityDescriptor (psd, &acl_exists, &acl, &dummy);
 | 
						|
  if (!NT_SUCCESS (status))
 | 
						|
    {
 | 
						|
      __seterrno_from_nt_status (status);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  /* Set uidret, gidret, and initalize attributes. */
 | 
						|
  uid = owner_sid.get_uid (&cldap);
 | 
						|
  gid = group_sid.get_gid (&cldap);
 | 
						|
  if (attr_ret)
 | 
						|
    {
 | 
						|
      attr = *attr_ret & S_IFMT;
 | 
						|
      just_created = *attr_ret & S_JUSTCREATED;
 | 
						|
    }
 | 
						|
  /* Remember the fact that owner and group are the same account. */
 | 
						|
  owner_eq_group = owner_sid == group_sid;
 | 
						|
 | 
						|
  /* Create and initialize local aclent_t array. */
 | 
						|
  lacl = (aclent_t *) tp.c_get ();
 | 
						|
  memset (lacl, 0, MAX_ACL_ENTRIES * sizeof (aclent_t *));
 | 
						|
  lacl[0].a_type = USER_OBJ;
 | 
						|
  lacl[0].a_id = uid;
 | 
						|
  lacl[1].a_type = GROUP_OBJ;
 | 
						|
  lacl[1].a_id = gid;
 | 
						|
  lacl[2].a_type = OTHER_OBJ;
 | 
						|
  lacl[2].a_id = ACL_UNDEFINED_ID;
 | 
						|
  /* Create array to collect SIDs of all entries in lacl. */
 | 
						|
  aclsid = (cygpsid *) tp.w_get ();
 | 
						|
  aclsid[0] = owner_sid;
 | 
						|
  aclsid[1] = group_sid;
 | 
						|
  aclsid[2] = well_known_world_sid;
 | 
						|
 | 
						|
  /* No ACEs?  Everybody has full access. */
 | 
						|
  if (!acl_exists || !acl || acl->AceCount == 0)
 | 
						|
    {
 | 
						|
      for (pos = 0; pos < MIN_ACL_ENTRIES; ++pos)
 | 
						|
	lacl[pos].a_perm = S_IROTH | S_IWOTH | S_IXOTH;
 | 
						|
      goto out;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Files and dirs are created with a NULL descriptor, so inheritence
 | 
						|
     rules kick in.  If no inheritable entries exist in the parent object,
 | 
						|
     Windows will create entries according to the user token's default DACL.
 | 
						|
     These entries are not desired and we ignore them at creation time.
 | 
						|
     We're just checking the SE_DACL_AUTO_INHERITED flag here, since that's
 | 
						|
     what we set in get_file_sd.  Read the longish comment there before
 | 
						|
     changing this test! */
 | 
						|
  if (just_created
 | 
						|
      && NT_SUCCESS (RtlGetControlSecurityDescriptor (psd, &ctrl, &rev))
 | 
						|
      && !(ctrl & SE_DACL_AUTO_INHERITED))
 | 
						|
    ;
 | 
						|
  else for (idx = 0; idx < acl->AceCount; ++idx)
 | 
						|
    {
 | 
						|
      if (!NT_SUCCESS (RtlGetAce (acl, idx, (PVOID *) &ace)))
 | 
						|
	continue;
 | 
						|
 | 
						|
      ace_sid = (PSID) &ace->SidStart;
 | 
						|
 | 
						|
      if (ace_sid == well_known_null_sid)
 | 
						|
	{
 | 
						|
	  /* Fetch special bits. */
 | 
						|
	  attr |= CYG_ACE_ISBITS_TO_POSIX (ace->Mask);
 | 
						|
	  if (ace->Header.AceType == ACCESS_DENIED_ACE_TYPE
 | 
						|
	      && ace->Mask & CYG_ACE_NEW_STYLE)
 | 
						|
	    {
 | 
						|
	      /* New-style ACL.  Note the fact that a mask value is present
 | 
						|
		 since that changes how getace fetches the information.  That's
 | 
						|
		 fine, because the NULL deny ACE is supposed to precede all
 | 
						|
		 USER, GROUP and GROUP_OBJ entries.  Any ACL not created that
 | 
						|
		 way has been rearranged by the Windows functionality to create
 | 
						|
		 the brain-dead "canonical" ACL order and is broken anyway. */
 | 
						|
	      new_style = true;
 | 
						|
	      attr |= CYG_ACE_ISBITS_TO_POSIX (ace->Mask);
 | 
						|
	      if (ace->Mask & CYG_ACE_MASK_VALID)
 | 
						|
		{
 | 
						|
		  if (!(ace->Header.AceFlags & INHERIT_ONLY))
 | 
						|
		    {
 | 
						|
		      if ((pos = searchace (lacl, MAX_ACL_ENTRIES, CLASS_OBJ))
 | 
						|
			  >= 0)
 | 
						|
			{
 | 
						|
			  lacl[pos].a_type = CLASS_OBJ;
 | 
						|
			  lacl[pos].a_id = ACL_UNDEFINED_ID;
 | 
						|
			  lacl[pos].a_perm = CYG_ACE_MASK_TO_POSIX (ace->Mask);
 | 
						|
			  aclsid[pos] = well_known_null_sid;
 | 
						|
			}
 | 
						|
		      has_class_perm = true;
 | 
						|
		      class_perm = lacl[pos].a_perm;
 | 
						|
		    }
 | 
						|
		  if (ace->Header.AceFlags & SUB_CONTAINERS_AND_OBJECTS_INHERIT)
 | 
						|
		    {
 | 
						|
		      if ((pos = searchace (lacl, MAX_ACL_ENTRIES,
 | 
						|
					    DEF_CLASS_OBJ)) >= 0)
 | 
						|
			{
 | 
						|
			  lacl[pos].a_type = DEF_CLASS_OBJ;
 | 
						|
			  lacl[pos].a_id = ACL_UNDEFINED_ID;
 | 
						|
			  lacl[pos].a_perm = CYG_ACE_MASK_TO_POSIX (ace->Mask);
 | 
						|
			  aclsid[pos] = well_known_null_sid;
 | 
						|
			}
 | 
						|
		      has_def_class_perm = true;
 | 
						|
		      def_class_perm = lacl[pos].a_perm;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	  continue;
 | 
						|
	}
 | 
						|
      if (ace_sid == owner_sid)
 | 
						|
	{
 | 
						|
	  type = USER_OBJ;
 | 
						|
	  id = uid;
 | 
						|
	}
 | 
						|
      else if (ace_sid == group_sid)
 | 
						|
	{
 | 
						|
	  type = GROUP_OBJ;
 | 
						|
	  id = gid;
 | 
						|
	}
 | 
						|
      else if (ace_sid == well_known_world_sid)
 | 
						|
	{
 | 
						|
	  type = OTHER_OBJ;
 | 
						|
	  id = ACL_UNDEFINED_ID;
 | 
						|
	  if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE
 | 
						|
	      && !(ace->Header.AceFlags & INHERIT_ONLY))
 | 
						|
	    saw_other_obj = true;
 | 
						|
	}
 | 
						|
      else if (ace_sid == well_known_creator_owner_sid)
 | 
						|
	{
 | 
						|
	  type = DEF_USER_OBJ;
 | 
						|
	  types_def |= type;
 | 
						|
	  id = ACL_UNDEFINED_ID;
 | 
						|
	  saw_def_user_obj = true;
 | 
						|
	}
 | 
						|
      else if (ace_sid == well_known_creator_group_sid)
 | 
						|
	{
 | 
						|
	  type = DEF_GROUP_OBJ;
 | 
						|
	  types_def |= type;
 | 
						|
	  id = ACL_UNDEFINED_ID;
 | 
						|
	  saw_def_group_obj = true;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  id = ace_sid.get_id (TRUE, &type, &cldap);
 | 
						|
	  if (!type)
 | 
						|
	    continue;
 | 
						|
	}
 | 
						|
      /* If the SGID attribute is set on a just created file or dir, the
 | 
						|
         first group in the ACL is the desired primary group of the new
 | 
						|
	 object.  Alternatively, the first repetition of the owner SID is
 | 
						|
	 the desired primary group, and we mark the object as owner_eq_group
 | 
						|
	 object. */
 | 
						|
      if (just_created && attr & S_ISGID && !saw_group_obj
 | 
						|
	  && (type == GROUP || (type == USER_OBJ && saw_user_obj)))
 | 
						|
	{
 | 
						|
	  type = GROUP_OBJ;
 | 
						|
	  lacl[1].a_id = gid = id;
 | 
						|
	  if (type == USER_OBJ)
 | 
						|
	    owner_eq_group = true;
 | 
						|
	}
 | 
						|
      if (!(ace->Header.AceFlags & INHERIT_ONLY || type & ACL_DEFAULT))
 | 
						|
	{
 | 
						|
	  if (type == USER_OBJ)
 | 
						|
	    {
 | 
						|
	      /* If we get a second entry for the owner SID, it's either a
 | 
						|
		 GROUP_OBJ entry for the same SID, if owner SID == group SID,
 | 
						|
		 or it's an additional USER entry.  The latter can happen
 | 
						|
		 when chown'ing a file. */
 | 
						|
	      if (saw_user_obj)
 | 
						|
		{
 | 
						|
		  if (owner_eq_group && !saw_group_obj)
 | 
						|
		    {
 | 
						|
		      type = GROUP_OBJ;
 | 
						|
		      /* Gid and uid are not necessarily the same even if the
 | 
						|
			 SID is the same: /etc/group is in use and the user got
 | 
						|
			 added to /etc/group using another gid than the uid.
 | 
						|
			 This is a border case but it happened and resetting id
 | 
						|
			 to gid is not much of a burden. */
 | 
						|
		      id = gid;
 | 
						|
		      if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
 | 
						|
			saw_group_obj = true;
 | 
						|
		    }
 | 
						|
		  else
 | 
						|
		    type = USER;
 | 
						|
		}
 | 
						|
	      else if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
 | 
						|
		saw_user_obj = true;
 | 
						|
	    }
 | 
						|
	  else if (type == GROUP_OBJ)
 | 
						|
	    {
 | 
						|
	      /* Same for the primary group. */
 | 
						|
	      if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
 | 
						|
		{
 | 
						|
		  if (saw_group_obj)
 | 
						|
		    type = GROUP;
 | 
						|
		  saw_group_obj = true;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	  if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0)
 | 
						|
	    {
 | 
						|
	      getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType,
 | 
						|
		      new_style && type & (USER | GROUP_OBJ | GROUP));
 | 
						|
	      aclsid[pos] = ace_sid;
 | 
						|
	      if (!new_style)
 | 
						|
		{
 | 
						|
		  /* Fix up CLASS_OBJ value. */
 | 
						|
		  if (type & (USER | GROUP))
 | 
						|
		    {
 | 
						|
		      has_class_perm = true;
 | 
						|
		      /* Accommodate Windows: Never add SYSTEM and Admins to
 | 
						|
			 CLASS_OBJ.  Unless (implicitly) if they are the
 | 
						|
			 GROUP_OBJ entry. */
 | 
						|
		      if (ace_sid != well_known_system_sid
 | 
						|
			  && ace_sid != well_known_admins_sid)
 | 
						|
			{
 | 
						|
			  class_perm |= lacl[pos].a_perm;
 | 
						|
			  standard_ACEs_only = false;
 | 
						|
			}
 | 
						|
		    }
 | 
						|
		}
 | 
						|
	      /* For a newly created file, we'd like to know if we're running
 | 
						|
		 with a standard ACL, one only consisting of POSIX perms, plus
 | 
						|
		 SYSTEM and Admins as maximum non-POSIX perms entries.  If it's
 | 
						|
		 a standard ACL, we apply umask.  That's not entirely correct,
 | 
						|
		 but it's probably the best we can do.  Chmod also wants to
 | 
						|
		 know this.  See there for the details. */
 | 
						|
	      else if (type & (USER | GROUP)
 | 
						|
		       && standard_ACEs_only
 | 
						|
		       && ace_sid != well_known_system_sid
 | 
						|
		       && ace_sid != well_known_admins_sid)
 | 
						|
		standard_ACEs_only = false;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      if ((ace->Header.AceFlags & SUB_CONTAINERS_AND_OBJECTS_INHERIT))
 | 
						|
	{
 | 
						|
	  if (type == USER_OBJ)
 | 
						|
	    {
 | 
						|
	      /* As above: If we get a second entry for the owner SID, it's
 | 
						|
		 a GROUP_OBJ entry for the same SID if owner SID == group SID,
 | 
						|
		 but this time only if the S_ISGID bit is set. Otherwise it's
 | 
						|
		 an additional USER entry. */
 | 
						|
	      if (saw_def_user_obj)
 | 
						|
		{
 | 
						|
		  if (owner_eq_group && !saw_def_group_obj && attr & S_ISGID)
 | 
						|
		    {
 | 
						|
		      /* Needs post-processing in the following GROUP_OBJ block.
 | 
						|
		         Set id to ACL_UNDEFINED_ID to play it safe. */
 | 
						|
		      type = GROUP_OBJ;
 | 
						|
		      id = ACL_UNDEFINED_ID;
 | 
						|
		    }
 | 
						|
		  else
 | 
						|
		    type = USER;
 | 
						|
		}
 | 
						|
	      else if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
 | 
						|
		saw_def_user_obj = true;
 | 
						|
	    }
 | 
						|
	  if (type == GROUP_OBJ)
 | 
						|
	    {
 | 
						|
	      /* If the SGID bit is set, the inheritable entry for the
 | 
						|
		 primary group is, in fact, the DEF_GROUP_OBJ entry,
 | 
						|
		 so don't change the type to GROUP in this case. */
 | 
						|
	      if (!new_style || saw_def_group_obj || !(attr & S_ISGID))
 | 
						|
		type = GROUP;
 | 
						|
	      else if (ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
 | 
						|
		saw_def_group_obj = true;
 | 
						|
	    }
 | 
						|
	  type |= ACL_DEFAULT;
 | 
						|
	  types_def |= type;
 | 
						|
	  if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0)
 | 
						|
	    {
 | 
						|
	      getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType,
 | 
						|
		      new_style && type & (USER | GROUP_OBJ | GROUP));
 | 
						|
	      aclsid[pos] = ace_sid;
 | 
						|
	      if (!new_style)
 | 
						|
		{
 | 
						|
		  /* Fix up DEF_CLASS_OBJ value. */
 | 
						|
		  if (type & (USER | GROUP))
 | 
						|
		    {
 | 
						|
		      has_def_class_perm = true;
 | 
						|
		      /* Accommodate Windows: Never add SYSTEM and Admins to
 | 
						|
			 CLASS_OBJ.  Unless (implicitly) if they are the
 | 
						|
			 GROUP_OBJ entry. */
 | 
						|
		      if (ace_sid != well_known_system_sid
 | 
						|
			  && ace_sid != well_known_admins_sid)
 | 
						|
		      def_class_perm |= lacl[pos].a_perm;
 | 
						|
		    }
 | 
						|
		  /* And note the position of the DEF_GROUP_OBJ entry. */
 | 
						|
		  if (type == DEF_GROUP_OBJ)
 | 
						|
		    def_pgrp_pos = pos;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
  /* If this is a just created file, and this is an ACL with only standard
 | 
						|
     entries, or if standard POSIX permissions are missing (probably no
 | 
						|
     inherited ACEs so created from a default DACL), assign the permissions
 | 
						|
     specified by the file creation mask.  The values get masked by the
 | 
						|
     actually requested permissions by the caller per POSIX 1003.1e draft 17. */
 | 
						|
  if (just_created)
 | 
						|
    {
 | 
						|
      mode_t perms = (S_IRWXU | S_IRWXG | S_IRWXO) & ~cygheap->umask;
 | 
						|
      if (standard_ACEs_only || !saw_user_obj)
 | 
						|
	lacl[0].a_perm = (perms >> 6) & S_IRWXO;
 | 
						|
      if (standard_ACEs_only || !saw_group_obj)
 | 
						|
	lacl[1].a_perm = (perms >> 3) & S_IRWXO;
 | 
						|
      if (standard_ACEs_only || !saw_other_obj)
 | 
						|
	lacl[2].a_perm = perms & S_IRWXO;
 | 
						|
    }
 | 
						|
  /* If this is an old-style or non-Cygwin ACL, and secondary user and group
 | 
						|
     entries exist in the ACL, fake a matching CLASS_OBJ entry. The CLASS_OBJ
 | 
						|
     permissions are the or'ed permissions of the primary group permissions
 | 
						|
     and all secondary user and group permissions. */
 | 
						|
  if (!new_style && has_class_perm
 | 
						|
      && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0)
 | 
						|
    {
 | 
						|
      lacl[pos].a_type = CLASS_OBJ;
 | 
						|
      lacl[pos].a_id = ACL_UNDEFINED_ID;
 | 
						|
      class_perm |= lacl[1].a_perm;
 | 
						|
      lacl[pos].a_perm = class_perm;
 | 
						|
      aclsid[pos] = well_known_null_sid;
 | 
						|
    }
 | 
						|
  /* For ptys, fake a mask if the admins group is neither owner nor group.
 | 
						|
     In that case we have an extra ACE for the admins group, and we need a
 | 
						|
     CLASS_OBJ to get a valid POSIX ACL.  However, Windows filters the ACE
 | 
						|
     Mask value so it only reflects the bit values supported by the object
 | 
						|
     type.  The result is that we can't set an explicit CLASS_OBJ value for
 | 
						|
     ptys in the NULL SID ACE. */
 | 
						|
  else if (S_ISCHR (attr) && owner_sid != well_known_admins_sid
 | 
						|
	   && group_sid != well_known_admins_sid
 | 
						|
	   && (pos = searchace (lacl, MAX_ACL_ENTRIES, CLASS_OBJ)) >= 0)
 | 
						|
    {
 | 
						|
      lacl[pos].a_type = CLASS_OBJ;
 | 
						|
      lacl[pos].a_id = ACL_UNDEFINED_ID;
 | 
						|
      lacl[pos].a_perm = lacl[1].a_perm; /* == group perms */
 | 
						|
      aclsid[pos] = well_known_null_sid;
 | 
						|
    }
 | 
						|
  /* Ensure that the default acl contains at least
 | 
						|
     DEF_(USER|GROUP|OTHER)_OBJ entries.  */
 | 
						|
  if (types_def && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0)
 | 
						|
    {
 | 
						|
      if (!(types_def & USER_OBJ))
 | 
						|
	{
 | 
						|
	  lacl[pos].a_type = DEF_USER_OBJ;
 | 
						|
	  lacl[pos].a_id = uid;
 | 
						|
	  lacl[pos].a_perm = lacl[0].a_perm;
 | 
						|
	  aclsid[pos] = well_known_creator_owner_sid;
 | 
						|
	  pos++;
 | 
						|
	}
 | 
						|
      if (!(types_def & GROUP_OBJ) && pos < MAX_ACL_ENTRIES)
 | 
						|
	{
 | 
						|
	  lacl[pos].a_type = DEF_GROUP_OBJ;
 | 
						|
	  lacl[pos].a_id = gid;
 | 
						|
	  lacl[pos].a_perm = lacl[1].a_perm;
 | 
						|
	  /* Note the position of the DEF_GROUP_OBJ entry. */
 | 
						|
	  def_pgrp_pos = pos;
 | 
						|
	  aclsid[pos] = well_known_creator_group_sid;
 | 
						|
	  pos++;
 | 
						|
	}
 | 
						|
      if (!(types_def & OTHER_OBJ) && pos < MAX_ACL_ENTRIES)
 | 
						|
	{
 | 
						|
	  lacl[pos].a_type = DEF_OTHER_OBJ;
 | 
						|
	  lacl[pos].a_id = ACL_UNDEFINED_ID;
 | 
						|
	  lacl[pos].a_perm = lacl[2].a_perm;
 | 
						|
	  aclsid[pos] = well_known_world_sid;
 | 
						|
	  pos++;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  /* If this is an old-style or non-Cygwin ACL, and secondary user default
 | 
						|
     and group default entries exist in the ACL, fake a matching DEF_CLASS_OBJ
 | 
						|
     entry. The DEF_CLASS_OBJ permissions are the or'ed permissions of the
 | 
						|
     primary group default permissions and all secondary user and group def.
 | 
						|
     permissions. */
 | 
						|
  if (!new_style && has_def_class_perm
 | 
						|
      && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0)
 | 
						|
    {
 | 
						|
      lacl[pos].a_type = DEF_CLASS_OBJ;
 | 
						|
      lacl[pos].a_id = ACL_UNDEFINED_ID;
 | 
						|
      lacl[pos].a_perm = def_class_perm;
 | 
						|
      if (def_pgrp_pos >= 0)
 | 
						|
	lacl[pos].a_perm |= lacl[def_pgrp_pos].a_perm;
 | 
						|
      aclsid[pos] = well_known_null_sid;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Make sure `pos' contains the number of used entries in lacl. */
 | 
						|
  if ((pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) < 0)
 | 
						|
    pos = MAX_ACL_ENTRIES;
 | 
						|
 | 
						|
  /* For old-style or non-Cygwin ACLs, check for merging permissions. */
 | 
						|
  if (!new_style)
 | 
						|
    for (idx = 0; idx < pos; ++idx)
 | 
						|
      {
 | 
						|
	if (lacl[idx].a_type & (USER_OBJ | USER)
 | 
						|
	    && !(lacl[idx].a_type & ACL_DEFAULT))
 | 
						|
	  {
 | 
						|
	    mode_t perm;
 | 
						|
 | 
						|
	    /* Don't merge if the user already has all permissions, or... */
 | 
						|
	    if (lacl[idx].a_perm == S_IRWXO)
 | 
						|
	      continue;
 | 
						|
	    /* ...if the sum of perms is less than or equal the user's perms. */
 | 
						|
	    perm = lacl[idx].a_perm
 | 
						|
		   | (has_class_perm ? class_perm : lacl[1].a_perm)
 | 
						|
		   | lacl[2].a_perm;
 | 
						|
	    if (perm == lacl[idx].a_perm)
 | 
						|
	      continue;
 | 
						|
	    /* Otherwise, if we use the Windows user DB, utilize Authz to make
 | 
						|
	       sure all user permissions are correctly reflecting the Windows
 | 
						|
	       permissions. */
 | 
						|
	    if (cygheap->pg.nss_pwd_db ()
 | 
						|
		&& authz_get_user_attribute (&perm, psd, aclsid[idx]))
 | 
						|
	      lacl[idx].a_perm = perm;
 | 
						|
	    /* Otherwise we only check the current user.  If the user entry
 | 
						|
	       has a deny ACE, don't check. */
 | 
						|
	    else if (lacl[idx].a_id == myself->uid
 | 
						|
		     && !(lacl[idx].a_perm & DENY_RWX))
 | 
						|
	      {
 | 
						|
		/* Sum up all permissions of groups the user is member of, plus
 | 
						|
		   everyone perms, and merge them to user perms.  */
 | 
						|
		BOOL ret;
 | 
						|
 | 
						|
		perm = lacl[2].a_perm & S_IRWXO;
 | 
						|
		for (int gidx = 1; gidx < pos; ++gidx)
 | 
						|
		  if (lacl[gidx].a_type & (GROUP_OBJ | GROUP)
 | 
						|
		      && CheckTokenMembership (cygheap->user.issetuid ()
 | 
						|
					       ? cygheap->user.imp_token () : NULL,
 | 
						|
					       aclsid[gidx], &ret)
 | 
						|
		      && ret)
 | 
						|
		    perm |= lacl[gidx].a_perm & S_IRWXO;
 | 
						|
		lacl[idx].a_perm |= perm;
 | 
						|
	      }
 | 
						|
	  }
 | 
						|
	/* For all groups, if everyone has more permissions, add everyone
 | 
						|
	   perms to group perms.  Skip groups with deny ACE. */
 | 
						|
	else if (lacl[idx].a_type & (GROUP_OBJ | GROUP)
 | 
						|
		 && !(lacl[idx].a_type & ACL_DEFAULT)
 | 
						|
		 && !(lacl[idx].a_perm & DENY_RWX))
 | 
						|
	  lacl[idx].a_perm |= lacl[2].a_perm & S_IRWXO;
 | 
						|
      }
 | 
						|
  /* If owner SID == group SID (Microsoft Accounts) merge group perms into
 | 
						|
     user perms but leave group perms intact.  That's a fake, but it allows
 | 
						|
     to keep track of the POSIX group perms without much effort. */
 | 
						|
  if (owner_eq_group)
 | 
						|
    lacl[0].a_perm |= lacl[1].a_perm;
 | 
						|
  /* Construct POSIX permission bits.  Fortunately we know exactly where
 | 
						|
     to fetch the affecting bits from, at least as long as the array
 | 
						|
     hasn't been sorted. */
 | 
						|
  attr |= (lacl[0].a_perm & S_IRWXO) << 6;
 | 
						|
  attr |= ((has_class_perm ? class_perm : lacl[1].a_perm) & S_IRWXO) << 3;
 | 
						|
  attr |= (lacl[2].a_perm & S_IRWXO);
 | 
						|
 | 
						|
out:
 | 
						|
  if (uid_ret)
 | 
						|
    *uid_ret = uid;
 | 
						|
  if (gid_ret)
 | 
						|
    *gid_ret = gid;
 | 
						|
  if (attr_ret)
 | 
						|
    *attr_ret = attr;
 | 
						|
  if (aclbufp)
 | 
						|
    {
 | 
						|
      if (pos > nentries)
 | 
						|
	{
 | 
						|
	  set_errno (ENOSPC);
 | 
						|
	  return -1;
 | 
						|
	}
 | 
						|
      memcpy (aclbufp, lacl, pos * sizeof (aclent_t));
 | 
						|
      for (idx = 0; idx < pos; ++idx)
 | 
						|
	aclbufp[idx].a_perm &= S_IRWXO;
 | 
						|
      aclsort32 (pos, 0, aclbufp);
 | 
						|
    }
 | 
						|
  if (std_acl)
 | 
						|
    *std_acl = standard_ACEs_only;
 | 
						|
  return pos;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
getacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp)
 | 
						|
{
 | 
						|
  security_descriptor sd;
 | 
						|
 | 
						|
  if (get_file_sd (handle, pc, sd, false))
 | 
						|
    return -1;
 | 
						|
  int pos = get_posix_access (sd, NULL, NULL, NULL, aclbufp, nentries);
 | 
						|
  syscall_printf ("%R = getacl(%S)", pos, pc.get_nt_native_path ());
 | 
						|
  return pos;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
acl32 (const char *path, int cmd, int nentries, aclent_t *aclbufp)
 | 
						|
{
 | 
						|
  int res = -1;
 | 
						|
 | 
						|
  fhandler_base *fh = build_fh_name (path, PC_SYM_FOLLOW | PC_KEEP_HANDLE,
 | 
						|
				     stat_suffixes);
 | 
						|
  if (!fh || !fh->exists ())
 | 
						|
    set_errno (ENOENT);
 | 
						|
  else if (fh->error ())
 | 
						|
    {
 | 
						|
      debug_printf ("got %d error from build_fh_name", fh->error ());
 | 
						|
      set_errno (fh->error ());
 | 
						|
    }
 | 
						|
  else
 | 
						|
    res = fh->facl (cmd, nentries, aclbufp);
 | 
						|
 | 
						|
  delete fh;
 | 
						|
  syscall_printf ("%R = acl(%s)", res, path);
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
#ifndef __x86_64__
 | 
						|
extern "C" int
 | 
						|
lacl32 (const char *path, int cmd, int nentries, aclent_t *aclbufp)
 | 
						|
{
 | 
						|
  /* This call was an accident.  Make it absolutely clear. */
 | 
						|
  set_errno (ENOSYS);
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
extern "C" int
 | 
						|
facl32 (int fd, int cmd, int nentries, aclent_t *aclbufp)
 | 
						|
{
 | 
						|
  cygheap_fdget cfd (fd);
 | 
						|
  if (cfd < 0)
 | 
						|
    {
 | 
						|
      syscall_printf ("-1 = facl (%d)", fd);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  int res = cfd->facl (cmd, nentries, aclbufp);
 | 
						|
  syscall_printf ("%R = facl(%s) )", res, cfd->get_name ());
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
__aclcheck (aclent_t *aclbufp, int nentries, int *which, bool posix)
 | 
						|
{
 | 
						|
  bool has_user_obj = false;
 | 
						|
  bool has_group_obj = false;
 | 
						|
  bool has_other_obj = false;
 | 
						|
  bool has_class_obj = false;
 | 
						|
  bool has_ug_objs = false;
 | 
						|
  bool has_def_objs = false;
 | 
						|
  bool has_def_user_obj = false;
 | 
						|
  bool has_def_group_obj = false;
 | 
						|
  bool has_def_other_obj = false;
 | 
						|
  bool has_def_class_obj = false;
 | 
						|
  bool has_def_ug_objs = false;
 | 
						|
  int pos2;
 | 
						|
 | 
						|
  for (int pos = 0; pos < nentries; ++pos)
 | 
						|
    {
 | 
						|
      /* POSIX ACLs may contain deleted entries.  Just ignore them. */
 | 
						|
      if (posix && aclbufp[pos].a_type == ACL_DELETED_TAG)
 | 
						|
	continue;
 | 
						|
      /* POSIX defines two sorts of ACLs, access and default, none of which
 | 
						|
	 is supposed to have the ACL_DEFAULT flag set. */
 | 
						|
      if (posix && (aclbufp[pos].a_type & ACL_DEFAULT))
 | 
						|
	{
 | 
						|
	  if (which)
 | 
						|
	    *which = pos;
 | 
						|
	  return ENTRY_ERROR;
 | 
						|
	}
 | 
						|
      switch (aclbufp[pos].a_type)
 | 
						|
	{
 | 
						|
	case USER_OBJ:
 | 
						|
	  if (has_user_obj)
 | 
						|
	    {
 | 
						|
	      if (which)
 | 
						|
		*which = pos;
 | 
						|
	      return USER_ERROR;
 | 
						|
	    }
 | 
						|
	  has_user_obj = true;
 | 
						|
	  break;
 | 
						|
	case GROUP_OBJ:
 | 
						|
	  if (has_group_obj)
 | 
						|
	    {
 | 
						|
	      if (which)
 | 
						|
		*which = pos;
 | 
						|
	      return GRP_ERROR;
 | 
						|
	    }
 | 
						|
	  has_group_obj = true;
 | 
						|
	  break;
 | 
						|
	case OTHER_OBJ:
 | 
						|
	  if (has_other_obj)
 | 
						|
	    {
 | 
						|
	      if (which)
 | 
						|
		*which = pos;
 | 
						|
	      return OTHER_ERROR;
 | 
						|
	    }
 | 
						|
	  has_other_obj = true;
 | 
						|
	  break;
 | 
						|
	case CLASS_OBJ:
 | 
						|
	  if (has_class_obj)
 | 
						|
	    {
 | 
						|
	      if (which)
 | 
						|
		*which = pos;
 | 
						|
	      return CLASS_ERROR;
 | 
						|
	    }
 | 
						|
	  has_class_obj = true;
 | 
						|
	  break;
 | 
						|
	case USER:
 | 
						|
	case GROUP:
 | 
						|
	  if ((pos2 = searchace (aclbufp + pos + 1, nentries - pos - 1,
 | 
						|
				 aclbufp[pos].a_type, aclbufp[pos].a_id)) >= 0)
 | 
						|
	    {
 | 
						|
	      if (which)
 | 
						|
		*which = pos2;
 | 
						|
	      return DUPLICATE_ERROR;
 | 
						|
	    }
 | 
						|
	  has_ug_objs = true;
 | 
						|
	  break;
 | 
						|
	case DEF_USER_OBJ:
 | 
						|
	  if (has_def_user_obj)
 | 
						|
	    {
 | 
						|
	      if (which)
 | 
						|
		*which = pos;
 | 
						|
	      return USER_ERROR;
 | 
						|
	    }
 | 
						|
	  has_def_objs = has_def_user_obj = true;
 | 
						|
	  break;
 | 
						|
	case DEF_GROUP_OBJ:
 | 
						|
	  if (has_def_group_obj)
 | 
						|
	    {
 | 
						|
	      if (which)
 | 
						|
		*which = pos;
 | 
						|
	      return GRP_ERROR;
 | 
						|
	    }
 | 
						|
	  has_def_objs = has_def_group_obj = true;
 | 
						|
	  break;
 | 
						|
	case DEF_OTHER_OBJ:
 | 
						|
	  if (has_def_other_obj)
 | 
						|
	    {
 | 
						|
	      if (which)
 | 
						|
		*which = pos;
 | 
						|
	      return OTHER_ERROR;
 | 
						|
	    }
 | 
						|
	  has_def_objs = has_def_other_obj = true;
 | 
						|
	  break;
 | 
						|
	case DEF_CLASS_OBJ:
 | 
						|
	  if (has_def_class_obj)
 | 
						|
	    {
 | 
						|
	      if (which)
 | 
						|
		*which = pos;
 | 
						|
	      return CLASS_ERROR;
 | 
						|
	    }
 | 
						|
	  has_def_objs = has_def_class_obj = true;
 | 
						|
	  break;
 | 
						|
	case DEF_USER:
 | 
						|
	case DEF_GROUP:
 | 
						|
	  if ((pos2 = searchace (aclbufp + pos + 1, nentries - pos - 1,
 | 
						|
				 aclbufp[pos].a_type, aclbufp[pos].a_id)) >= 0)
 | 
						|
	    {
 | 
						|
	      if (which)
 | 
						|
		*which = pos2;
 | 
						|
	      return DUPLICATE_ERROR;
 | 
						|
	    }
 | 
						|
	  has_def_objs = has_def_ug_objs = true;
 | 
						|
	  break;
 | 
						|
	default:
 | 
						|
	  if (which)
 | 
						|
	    *which = pos;
 | 
						|
	  return ENTRY_ERROR;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  if (!has_user_obj
 | 
						|
      || !has_group_obj
 | 
						|
      || !has_other_obj
 | 
						|
      || (has_ug_objs && !has_class_obj))
 | 
						|
    {
 | 
						|
      if (which)
 | 
						|
	*which = -1;
 | 
						|
      return MISS_ERROR;
 | 
						|
    }
 | 
						|
  /* Check for missing default entries only on Solaris ACLs. */
 | 
						|
  if (!posix &&
 | 
						|
      ((has_def_objs
 | 
						|
	&& !(has_def_user_obj && has_def_group_obj && has_def_other_obj))
 | 
						|
       || (has_def_ug_objs && !has_def_class_obj)))
 | 
						|
    {
 | 
						|
      if (which)
 | 
						|
	*which = -1;
 | 
						|
      return MISS_ERROR;
 | 
						|
    }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
aclcheck32 (aclent_t *aclbufp, int nentries, int *which)
 | 
						|
{
 | 
						|
  return __aclcheck (aclbufp, nentries, which, false);
 | 
						|
}
 | 
						|
 | 
						|
/* For the sake of acl_calc_mask, return -1 if the ACL doesn't need a mask
 | 
						|
   or if a mask entry already exists (__aclcalcmask sets the mask by itself).
 | 
						|
   Otherwise return the mask value so acl_calc_mask can create a mask entry.
 | 
						|
   This doesn't matter when called from aclsort. */
 | 
						|
mode_t
 | 
						|
__aclcalcmask (aclent_t *aclbufp, int nentries)
 | 
						|
{
 | 
						|
  mode_t mask = 0;
 | 
						|
  bool need_mask = false;
 | 
						|
  int mask_idx = -1;
 | 
						|
 | 
						|
  for (int idx = 0; idx < nentries; ++idx)
 | 
						|
    switch (aclbufp[idx].a_type)
 | 
						|
      {
 | 
						|
      case USER:
 | 
						|
      case GROUP:
 | 
						|
	need_mask = true;
 | 
						|
	/*FALLTHRU*/
 | 
						|
      case GROUP_OBJ:
 | 
						|
	mask |= aclbufp[idx].a_perm;
 | 
						|
	break;
 | 
						|
      case CLASS_OBJ:
 | 
						|
	mask_idx = idx;
 | 
						|
	break;
 | 
						|
      default:
 | 
						|
	break;
 | 
						|
      }
 | 
						|
  if (mask_idx != -1)
 | 
						|
    aclbufp[mask_idx].a_perm = mask;
 | 
						|
  if (need_mask && mask_idx == -1)
 | 
						|
    return mask;
 | 
						|
  return (acl_perm_t) -1;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
acecmp (const void *a1, const void *a2)
 | 
						|
{
 | 
						|
#define ace(i) ((const aclent_t *) a##i)
 | 
						|
  int ret = ace (1)->a_type - ace (2)->a_type;
 | 
						|
  if (!ret)
 | 
						|
    ret = ace (1)->a_id - ace (2)->a_id;
 | 
						|
  return ret;
 | 
						|
#undef ace
 | 
						|
}
 | 
						|
 | 
						|
/* Sorts any acl.  Called from sec_posixacl.cc. */
 | 
						|
int
 | 
						|
__aclsort (int nentries, aclent_t *aclbufp)
 | 
						|
{
 | 
						|
  if (!aclbufp || nentries < 0)
 | 
						|
    {
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  if (nentries > 0)
 | 
						|
    qsort ((void *) aclbufp, nentries, sizeof (aclent_t), acecmp);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
aclsort32 (int nentries, int calclass, aclent_t *aclbufp)
 | 
						|
{
 | 
						|
  if (!aclbufp || nentries < MIN_ACL_ENTRIES
 | 
						|
      || aclcheck32 (aclbufp, nentries, NULL))
 | 
						|
    {
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  qsort ((void *) aclbufp, nentries, sizeof (aclent_t), acecmp);
 | 
						|
  if (calclass)
 | 
						|
    __aclcalcmask (aclbufp, nentries);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
acltomode32 (aclent_t *aclbufp, int nentries, mode_t *modep)
 | 
						|
{
 | 
						|
  int pos;
 | 
						|
 | 
						|
  if (!aclbufp || nentries < 1 || !modep)
 | 
						|
    {
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  *modep = 0;
 | 
						|
  if ((pos = searchace (aclbufp, nentries, USER_OBJ)) < 0
 | 
						|
      || !aclbufp[pos].a_type)
 | 
						|
    {
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  *modep |= (aclbufp[pos].a_perm & S_IRWXO) << 6;
 | 
						|
  if ((pos = searchace (aclbufp, nentries, GROUP_OBJ)) < 0
 | 
						|
      || !aclbufp[pos].a_type)
 | 
						|
    {
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  *modep |= (aclbufp[pos].a_perm & S_IRWXO) << 3;
 | 
						|
  int cpos;
 | 
						|
  if ((cpos = searchace (aclbufp, nentries, CLASS_OBJ)) >= 0
 | 
						|
      && aclbufp[cpos].a_type == CLASS_OBJ)
 | 
						|
    *modep |= ((aclbufp[pos].a_perm & S_IRWXO) & aclbufp[cpos].a_perm) << 3;
 | 
						|
  if ((pos = searchace (aclbufp, nentries, OTHER_OBJ)) < 0
 | 
						|
      || !aclbufp[pos].a_type)
 | 
						|
    {
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  *modep |= aclbufp[pos].a_perm & S_IRWXO;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
aclfrommode32 (aclent_t *aclbufp, int nentries, mode_t *modep)
 | 
						|
{
 | 
						|
  int pos;
 | 
						|
 | 
						|
  if (!aclbufp || nentries < 1 || !modep)
 | 
						|
    {
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  if ((pos = searchace (aclbufp, nentries, USER_OBJ)) < 0
 | 
						|
      || !aclbufp[pos].a_type)
 | 
						|
    {
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  aclbufp[pos].a_perm = (*modep & S_IRWXU) >> 6;
 | 
						|
  if ((pos = searchace (aclbufp, nentries, GROUP_OBJ)) < 0
 | 
						|
      || !aclbufp[pos].a_type)
 | 
						|
    {
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  aclbufp[pos].a_perm = (*modep & S_IRWXG) >> 3;
 | 
						|
  if ((pos = searchace (aclbufp, nentries, CLASS_OBJ)) >= 0
 | 
						|
      && aclbufp[pos].a_type == CLASS_OBJ)
 | 
						|
    aclbufp[pos].a_perm = (*modep & S_IRWXG) >> 3;
 | 
						|
  if ((pos = searchace (aclbufp, nentries, OTHER_OBJ)) < 0
 | 
						|
      || !aclbufp[pos].a_type)
 | 
						|
    {
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  aclbufp[pos].a_perm = (*modep & S_IRWXO);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
acltopbits32 (aclent_t *aclbufp, int nentries, mode_t *pbitsp)
 | 
						|
{
 | 
						|
  return acltomode32 (aclbufp, nentries, pbitsp);
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
aclfrompbits32 (aclent_t *aclbufp, int nentries, mode_t *pbitsp)
 | 
						|
{
 | 
						|
  return aclfrommode32 (aclbufp, nentries, pbitsp);
 | 
						|
}
 | 
						|
 | 
						|
static char *
 | 
						|
permtostr (char *bufp, mode_t perm)
 | 
						|
{
 | 
						|
  *bufp++ = (perm & S_IROTH) ? 'r' : '-';
 | 
						|
  *bufp++ = (perm & S_IWOTH) ? 'w' : '-';
 | 
						|
  *bufp++ = (perm & S_IXOTH) ? 'x' : '-';
 | 
						|
  return bufp;
 | 
						|
}
 | 
						|
 | 
						|
#define _OPT(o) (options & (o))
 | 
						|
 | 
						|
#define _CHK(l) \
 | 
						|
		  if (bufp + (l) >= buf + 2 * NT_MAX_PATH - 1) \
 | 
						|
		    { \
 | 
						|
		      set_errno (ENOMEM); \
 | 
						|
		      return NULL; \
 | 
						|
		    }
 | 
						|
#define _CPY(s)	({ \
 | 
						|
		  const char *_s = (s); \
 | 
						|
		  _CHK (strlen (_s)); \
 | 
						|
		  bufp = stpcpy (bufp, _s); \
 | 
						|
		})
 | 
						|
#define _PTS(p) { \
 | 
						|
		  _CHK (3); \
 | 
						|
		  bufp = permtostr (bufp, p); \
 | 
						|
		}
 | 
						|
 | 
						|
#define _CMP(s)		(!strncmp (bufp, acl_part[s].str, acl_part[s].len))
 | 
						|
 | 
						|
struct _acl_part
 | 
						|
{
 | 
						|
  const char *str;
 | 
						|
  size_t len;
 | 
						|
};
 | 
						|
 | 
						|
static _acl_part acl_part_l[] =
 | 
						|
{
 | 
						|
  { "default:",	8 },
 | 
						|
  { "user:",	5 },
 | 
						|
  { "group:",	6 },
 | 
						|
  { "mask:",	5 },
 | 
						|
  { "other:",	6 }
 | 
						|
};
 | 
						|
 | 
						|
static _acl_part acl_part_s[] =
 | 
						|
{
 | 
						|
  { "d:",	2 },
 | 
						|
  { "u:",	2 },
 | 
						|
  { "g:",	2 },
 | 
						|
  { "m:",	2 },
 | 
						|
  { "o:",	2 }
 | 
						|
};
 | 
						|
 | 
						|
enum _acl_type {
 | 
						|
  default_s,
 | 
						|
  user_s,
 | 
						|
  group_s,
 | 
						|
  mask_s,
 | 
						|
  other_s,
 | 
						|
  none_s
 | 
						|
};
 | 
						|
 | 
						|
char *
 | 
						|
__acltotext (aclent_t *aclbufp, int aclcnt, const char *prefix, char separator,
 | 
						|
	     int options)
 | 
						|
{
 | 
						|
  if (!aclbufp || aclcnt < 1 || aclcnt > MAX_ACL_ENTRIES
 | 
						|
      || aclsort32 (aclcnt, 0, aclbufp))
 | 
						|
    {
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
  cyg_ldap cldap;
 | 
						|
  tmp_pathbuf tp;
 | 
						|
  char *buf = tp.t_get ();
 | 
						|
  char *bufp = buf;
 | 
						|
  char *entry_start;
 | 
						|
  bool first = true;
 | 
						|
  struct passwd *pw;
 | 
						|
  struct group *gr;
 | 
						|
  mode_t mask = S_IRWXO;
 | 
						|
  mode_t def_mask = S_IRWXO;
 | 
						|
  mode_t effective;
 | 
						|
  int pos;
 | 
						|
  _acl_part *acl_part = _OPT (TEXT_ABBREVIATE) ? acl_part_s : acl_part_l;
 | 
						|
 | 
						|
  *bufp = '\0';
 | 
						|
  /* If effective rights are requested, fetch mask values. */
 | 
						|
  if (_OPT (TEXT_SOME_EFFECTIVE | TEXT_ALL_EFFECTIVE))
 | 
						|
    {
 | 
						|
      if ((pos = searchace (aclbufp, aclcnt, CLASS_OBJ)) >= 0)
 | 
						|
	mask = aclbufp[pos].a_perm;
 | 
						|
      if ((pos = searchace (aclbufp, aclcnt, DEF_CLASS_OBJ)) >= 0)
 | 
						|
	def_mask = aclbufp[pos].a_perm;
 | 
						|
    }
 | 
						|
  for (pos = 0; pos < aclcnt; ++pos)
 | 
						|
    {
 | 
						|
      if (!first)
 | 
						|
	{
 | 
						|
	  _CHK (1);
 | 
						|
	  *bufp++ = separator;
 | 
						|
	}
 | 
						|
      first = false;
 | 
						|
      /* Rememeber start position of entry to compute TEXT_SMART_INDENT tabs. */
 | 
						|
      entry_start = bufp;
 | 
						|
      /* prefix */
 | 
						|
      if (prefix)
 | 
						|
	_CPY (prefix);
 | 
						|
      /* Solaris default acl? */
 | 
						|
      if (!_OPT (TEXT_IS_POSIX) && aclbufp[pos].a_type & ACL_DEFAULT)
 | 
						|
	_CPY (acl_part[default_s].str);
 | 
						|
      /* acl type */
 | 
						|
      switch (aclbufp[pos].a_type & ~ACL_DEFAULT)
 | 
						|
	{
 | 
						|
	case USER_OBJ:
 | 
						|
	case USER:
 | 
						|
	  _CPY (acl_part[user_s].str);
 | 
						|
	  break;
 | 
						|
	case GROUP_OBJ:
 | 
						|
	case GROUP:
 | 
						|
	  _CPY (acl_part[group_s].str);
 | 
						|
	  break;
 | 
						|
	case CLASS_OBJ:
 | 
						|
	  _CPY (acl_part[mask_s].str);
 | 
						|
	  break;
 | 
						|
	case OTHER_OBJ:
 | 
						|
	  _CPY (acl_part[other_s].str);
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
      /* id, if any  */
 | 
						|
      switch (aclbufp[pos].a_type & ~ACL_DEFAULT)
 | 
						|
	{
 | 
						|
	case USER:
 | 
						|
	  if (_OPT (TEXT_NUMERIC_IDS)
 | 
						|
	      || !(pw = internal_getpwuid (aclbufp[pos].a_id, &cldap)))
 | 
						|
	    {
 | 
						|
	      _CHK (11);
 | 
						|
	      bufp += __small_sprintf (bufp, "%u:", aclbufp[pos].a_id);
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      _CHK (strlen (pw->pw_name + 1));
 | 
						|
	      bufp += __small_sprintf (bufp, "%s:", pw->pw_name);
 | 
						|
	    }
 | 
						|
	  break;
 | 
						|
	case GROUP:
 | 
						|
	  if (_OPT (TEXT_NUMERIC_IDS)
 | 
						|
	      || !(gr = internal_getgrgid (aclbufp[pos].a_id, &cldap)))
 | 
						|
	    {
 | 
						|
	      _CHK (11);
 | 
						|
	      bufp += __small_sprintf (bufp, "%u:", aclbufp[pos].a_id);
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      _CHK (strlen (gr->gr_name));
 | 
						|
	      bufp += __small_sprintf (bufp, "%s:", gr->gr_name);
 | 
						|
	    }
 | 
						|
	  break;
 | 
						|
	default:
 | 
						|
	  _CPY (":");
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
      /* real permissions */
 | 
						|
      _PTS (aclbufp[pos].a_perm);
 | 
						|
      if (!_OPT (TEXT_SOME_EFFECTIVE | TEXT_ALL_EFFECTIVE))
 | 
						|
	continue;
 | 
						|
      /* effective permissions */
 | 
						|
      switch (aclbufp[pos].a_type)
 | 
						|
	{
 | 
						|
	case USER:
 | 
						|
	case GROUP_OBJ:
 | 
						|
	case GROUP:
 | 
						|
	  effective = aclbufp[pos].a_perm & mask;
 | 
						|
	  break;
 | 
						|
	case DEF_USER:
 | 
						|
	case DEF_GROUP_OBJ:
 | 
						|
	case DEF_GROUP:
 | 
						|
	  effective = aclbufp[pos].a_perm & def_mask;
 | 
						|
	  break;
 | 
						|
	default:
 | 
						|
	  continue;
 | 
						|
	}
 | 
						|
      if (_OPT (TEXT_ALL_EFFECTIVE) || effective != aclbufp[pos].a_perm)
 | 
						|
	{
 | 
						|
	  if (_OPT (TEXT_SMART_INDENT))
 | 
						|
	    {
 | 
						|
	      int tabs = 3 - (bufp - entry_start) / 8;
 | 
						|
	      if (tabs-- > 0)
 | 
						|
		{
 | 
						|
		  _CHK (tabs);
 | 
						|
		  while (tabs-- > 0)
 | 
						|
		    *bufp++ = '\t';
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	  _CPY ("\t#effective:");
 | 
						|
	  _PTS (effective);
 | 
						|
	}
 | 
						|
    }
 | 
						|
  if (_OPT (TEXT_END_SEPARATOR))
 | 
						|
    {
 | 
						|
      _CHK (1);
 | 
						|
      *bufp++ = separator;
 | 
						|
    }
 | 
						|
  *bufp = '\0';
 | 
						|
  return strdup (buf);
 | 
						|
}
 | 
						|
 | 
						|
extern "C" char *
 | 
						|
acltotext32 (aclent_t *aclbufp, int aclcnt)
 | 
						|
{
 | 
						|
  return __acltotext (aclbufp, aclcnt, NULL, ',', 0);
 | 
						|
}
 | 
						|
 | 
						|
static mode_t
 | 
						|
permfromstr (char *perm, bool posix_long)
 | 
						|
{
 | 
						|
  mode_t mode = 0;
 | 
						|
  int idx;
 | 
						|
 | 
						|
  if (perm[0] == 'r')
 | 
						|
    mode |= S_IROTH;
 | 
						|
  else if (perm[0] != '-')
 | 
						|
    return 01000;
 | 
						|
  if (perm[1] == 'w')
 | 
						|
    mode |= S_IWOTH;
 | 
						|
  else if (perm[1] != '-')
 | 
						|
    return 01000;
 | 
						|
  if (perm[2] == 'x')
 | 
						|
    mode |= S_IXOTH;
 | 
						|
  else if (perm[2] != '-')
 | 
						|
    return 01000;
 | 
						|
  idx = 3;
 | 
						|
  /* In posix long mode, only tabs up to a hash sign allowed. */
 | 
						|
  if (posix_long)
 | 
						|
    while (perm[idx] == '\t')
 | 
						|
      ++idx;
 | 
						|
  if (perm[idx] == '\0' || (posix_long && perm[idx] == '#'))
 | 
						|
    return mode;
 | 
						|
  return 01000;
 | 
						|
}
 | 
						|
 | 
						|
void *
 | 
						|
__aclfromtext (const char *acltextp, int *aclcnt, bool posix)
 | 
						|
{
 | 
						|
  if (!acltextp || strlen (acltextp) >= 2 * NT_MAX_PATH)
 | 
						|
    {
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
  cyg_ldap cldap;
 | 
						|
  tmp_pathbuf tp;
 | 
						|
  const char *delim;
 | 
						|
  _acl_part *acl_part;
 | 
						|
  char *bufp, *lasts, *qualifier;
 | 
						|
  int pos = 0;
 | 
						|
  int acl_type;
 | 
						|
 | 
						|
  aclent_t *lacl = (aclent_t *) tp.c_get ();
 | 
						|
  memset (lacl, 0, MAX_ACL_ENTRIES * sizeof (aclent_t *));
 | 
						|
  char *buf = tp.t_get ();
 | 
						|
  stpcpy (buf, acltextp);
 | 
						|
 | 
						|
  if (posix)
 | 
						|
    {
 | 
						|
      /* Posix long or short form.  Any \n in the string means long form. */
 | 
						|
      if (strchr (buf, '\n'))
 | 
						|
	{
 | 
						|
	  delim = "\n";
 | 
						|
	  acl_part = acl_part_l;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  delim = ",";
 | 
						|
	  acl_part = acl_part_s;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      /* Solaris aclfromtext format. */
 | 
						|
      delim = ",";
 | 
						|
      acl_part = acl_part_l;
 | 
						|
    }
 | 
						|
 | 
						|
  for (bufp = strtok_r (buf, delim, &lasts);
 | 
						|
       bufp;
 | 
						|
       bufp = strtok_r (NULL, delim, &lasts))
 | 
						|
    {
 | 
						|
      /* Handle default acl entries only for Solaris ACLs. */
 | 
						|
      if (!posix && _CMP (default_s))
 | 
						|
	{
 | 
						|
	  lacl[pos].a_type |= ACL_DEFAULT;
 | 
						|
	  bufp += acl_part[default_s].len;
 | 
						|
	}
 | 
						|
      lacl[pos].a_id = ACL_UNDEFINED_ID;
 | 
						|
      for (acl_type = user_s; acl_type < none_s; ++acl_type)
 | 
						|
	if (_CMP (acl_type))
 | 
						|
	  break;
 | 
						|
      if (acl_type == none_s)
 | 
						|
	{
 | 
						|
	  set_errno (EINVAL);
 | 
						|
	  return NULL;
 | 
						|
	}
 | 
						|
      bufp += acl_part[acl_type].len;
 | 
						|
      switch (acl_type)
 | 
						|
	{
 | 
						|
	case user_s:
 | 
						|
	case group_s:
 | 
						|
	  qualifier = bufp;
 | 
						|
	  bufp = strchrnul (bufp, ':');
 | 
						|
	  *bufp++ = '\0';
 | 
						|
	  /* No qualifier?  USER_OBJ or GROUP_OBJ */
 | 
						|
	  if (!*qualifier)
 | 
						|
	    {
 | 
						|
	      lacl[pos].a_type |= (acl_type == user_s) ? USER_OBJ : GROUP_OBJ;
 | 
						|
	      break;
 | 
						|
	    }
 | 
						|
	  /* Some qualifier, USER or GROUP */
 | 
						|
	  lacl[pos].a_type |= (acl_type == user_s) ? USER : GROUP;
 | 
						|
	  if (isdigit (*qualifier))
 | 
						|
	    {
 | 
						|
	      char *ep;
 | 
						|
 | 
						|
	      id_t id = strtol (qualifier, &ep, 10);
 | 
						|
	      if (*ep == '\0')
 | 
						|
		{
 | 
						|
		  lacl[pos].a_id = id;
 | 
						|
		  break;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	  if (acl_type == user_s)
 | 
						|
	    {
 | 
						|
	      struct passwd *pw = internal_getpwnam (qualifier, &cldap);
 | 
						|
	      if (pw)
 | 
						|
		lacl[pos].a_id = pw->pw_uid;
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      struct group *gr = internal_getgrnam (qualifier, &cldap);
 | 
						|
	      if (gr)
 | 
						|
		lacl[pos].a_id = gr->gr_gid;
 | 
						|
	    }
 | 
						|
	  if (lacl[pos].a_id == ACL_UNDEFINED_ID)
 | 
						|
	    {
 | 
						|
	      set_errno (EINVAL);
 | 
						|
	      return NULL;
 | 
						|
	    }
 | 
						|
	  break;
 | 
						|
	case mask_s:
 | 
						|
	case other_s:
 | 
						|
	  if (*bufp++ != ':')
 | 
						|
	    {
 | 
						|
	      set_errno (EINVAL);
 | 
						|
	      return NULL;
 | 
						|
	    }
 | 
						|
	  lacl[pos].a_type |= (acl_type == mask_s) ? CLASS_OBJ : OTHER_OBJ;
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
      /* In posix long mode, the next char after the permissions may be a tab
 | 
						|
	 followed by effective permissions we can ignore here. */
 | 
						|
      if ((lacl[pos].a_perm = permfromstr (bufp, *delim == '\n')) == 01000)
 | 
						|
	{
 | 
						|
	  set_errno (EINVAL);
 | 
						|
	  return NULL;
 | 
						|
	}
 | 
						|
      ++pos;
 | 
						|
    }
 | 
						|
  if (posix)
 | 
						|
    {
 | 
						|
      acl_t acl = (acl_t) acl_init (pos);
 | 
						|
      if (acl)
 | 
						|
	{
 | 
						|
	  memcpy (acl->entry, lacl, pos * sizeof (aclent_t));
 | 
						|
	  acl->count = pos;
 | 
						|
	  if (aclcnt)
 | 
						|
	    *aclcnt = pos;
 | 
						|
	}
 | 
						|
      return (void *) acl;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      aclent_t *aclp = (aclent_t *) malloc (pos * sizeof (aclent_t));
 | 
						|
      if (aclp)
 | 
						|
	{
 | 
						|
	  memcpy (aclp, lacl, pos * sizeof (aclent_t));
 | 
						|
	  if (aclcnt)
 | 
						|
	    *aclcnt = pos;
 | 
						|
	}
 | 
						|
      return (void *) aclp;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
extern "C" aclent_t *
 | 
						|
aclfromtext32 (char *acltextp, int *aclcnt)
 | 
						|
{
 | 
						|
  return (aclent_t *) __aclfromtext (acltextp, aclcnt, false);
 | 
						|
}
 | 
						|
 | 
						|
#ifdef __x86_64__
 | 
						|
EXPORT_ALIAS (acl32, acl)
 | 
						|
EXPORT_ALIAS (facl32, facl)
 | 
						|
EXPORT_ALIAS (aclcheck32, aclcheck)
 | 
						|
EXPORT_ALIAS (aclsort32, aclsort)
 | 
						|
EXPORT_ALIAS (acltomode32, acltomode)
 | 
						|
EXPORT_ALIAS (aclfrommode32, aclfrommode)
 | 
						|
EXPORT_ALIAS (acltopbits32, acltopbits)
 | 
						|
EXPORT_ALIAS (aclfrompbits32, aclfrompbits)
 | 
						|
EXPORT_ALIAS (acltotext32, acltotext)
 | 
						|
EXPORT_ALIAS (aclfromtext32, aclfromtext)
 | 
						|
#else
 | 
						|
typedef struct __acl16 {
 | 
						|
    int          a_type;
 | 
						|
    __uid16_t    a_id;
 | 
						|
    mode_t       a_perm;
 | 
						|
} __aclent16_t;
 | 
						|
 | 
						|
/* __aclent16_t and aclent_t have same size and same member offsets */
 | 
						|
static aclent_t *
 | 
						|
acl16to32 (__aclent16_t *aclbufp, int nentries)
 | 
						|
{
 | 
						|
  aclent_t *aclbufp32 = (aclent_t *) aclbufp;
 | 
						|
  if (aclbufp32)
 | 
						|
    for (int i = 0; i < nentries; i++)
 | 
						|
      aclbufp32[i].a_id &= USHRT_MAX;
 | 
						|
  return aclbufp32;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
acl (const char *path, int cmd, int nentries, __aclent16_t *aclbufp)
 | 
						|
{
 | 
						|
  return acl32 (path, cmd, nentries, acl16to32 (aclbufp, nentries));
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
facl (int fd, int cmd, int nentries, __aclent16_t *aclbufp)
 | 
						|
{
 | 
						|
  return facl32 (fd, cmd, nentries, acl16to32 (aclbufp, nentries));
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
lacl (const char *path, int cmd, int nentries, __aclent16_t *aclbufp)
 | 
						|
{
 | 
						|
  /* This call was an accident.  Make it absolutely clear. */
 | 
						|
  set_errno (ENOSYS);
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
aclcheck (__aclent16_t *aclbufp, int nentries, int *which)
 | 
						|
{
 | 
						|
  return aclcheck32 (acl16to32 (aclbufp, nentries), nentries, which);
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
aclsort (int nentries, int i, __aclent16_t *aclbufp)
 | 
						|
{
 | 
						|
  return aclsort32 (nentries, i, acl16to32 (aclbufp, nentries));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
extern "C" int
 | 
						|
acltomode (__aclent16_t *aclbufp, int nentries, mode_t *modep)
 | 
						|
{
 | 
						|
  return acltomode32 (acl16to32 (aclbufp, nentries), nentries, modep);
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
aclfrommode (__aclent16_t *aclbufp, int nentries, mode_t *modep)
 | 
						|
{
 | 
						|
  return aclfrommode32 ((aclent_t *)aclbufp, nentries, modep);
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
acltopbits (__aclent16_t *aclbufp, int nentries, mode_t *pbitsp)
 | 
						|
{
 | 
						|
  return acltopbits32 (acl16to32 (aclbufp, nentries), nentries, pbitsp);
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
aclfrompbits (__aclent16_t *aclbufp, int nentries, mode_t *pbitsp)
 | 
						|
{
 | 
						|
  return aclfrompbits32 ((aclent_t *)aclbufp, nentries, pbitsp);
 | 
						|
}
 | 
						|
 | 
						|
extern "C" char *
 | 
						|
acltotext (__aclent16_t *aclbufp, int aclcnt)
 | 
						|
{
 | 
						|
  return acltotext32 (acl16to32 (aclbufp, aclcnt), aclcnt);
 | 
						|
}
 | 
						|
 | 
						|
extern "C" __aclent16_t *
 | 
						|
aclfromtext (char *acltextp, int *aclcnt)
 | 
						|
{
 | 
						|
  return (__aclent16_t *) aclfromtext32 (acltextp, aclcnt);
 | 
						|
}
 | 
						|
#endif /* !__x86_64__ */
 |