Preliminary read side implementation of new permission handling.

* acl.h (MAX_ACL_ENTRIES): Raise to 2730.  Add comment to explain.
	* sec_acl.cc:  Add leading comment to explain new ACL style.
	Add definitions and macros to use for bits in new Cygwin ACL.
	(DENY_RWX): New mask value for all temporary deny bits.
	(getace): Add bool parameter to decide when leaving all bits intact,
	rather than filtering them per the already set bits.
	(get_posix_access): New function, taking over functionality to read
	POSIX ACL from SECURITY_DESCRIPTOR.
	(getacl): Just call get_posix_access.
	* sec_helper.cc (well_known_cygwin_sid): Define.
	* security.cc (get_attribute_from_acl): Remove.
	(get_info_from_sd): Remove.
	(get_reg_sd): Call get_posix_access instead of get_info_from_sd.
	(get_file_attribute): Ditto.
	(get_object_attribute): Ditto.
	* security.h (well_known_cygwin_sid): Declare.
	(get_posix_access): Add prototype.

	* Throughout, use simpler ACE macros from Windows' accctrl.h.

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2015-03-18 17:49:12 +01:00
parent 4a9636b1d6
commit 52f01a0ff8
No known key found for this signature in database
GPG Key ID: F536069DAE444FA0
6 changed files with 399 additions and 360 deletions

View File

@ -1,3 +1,26 @@
2015-03-18 Corinna Vinschen <corinna@vinschen.de>
Preliminary read side implementation of new permission handling.
* acl.h (MAX_ACL_ENTRIES): Raise to 2730. Add comment to explain.
* sec_acl.cc: Add leading comment to explain new ACL style.
Add definitions and macros to use for bits in new Cygwin ACL.
(DENY_RWX): New mask value for all temporary deny bits.
(getace): Add bool parameter to decide when leaving all bits intact,
rather than filtering them per the already set bits.
(get_posix_access): New function, taking over functionality to read
POSIX ACL from SECURITY_DESCRIPTOR.
(getacl): Just call get_posix_access.
* sec_helper.cc (well_known_cygwin_sid): Define.
* security.cc (get_attribute_from_acl): Remove.
(get_info_from_sd): Remove.
(get_reg_sd): Call get_posix_access instead of get_info_from_sd.
(get_file_attribute): Ditto.
(get_object_attribute): Ditto.
* security.h (well_known_cygwin_sid): Declare.
(get_posix_access): Add prototype.
* Throughout, use simpler ACE macros from Windows' accctrl.h.
2015-03-18 Corinna Vinschen <corinna@vinschen.de> 2015-03-18 Corinna Vinschen <corinna@vinschen.de>
* grp.cc (pwdgrp::parse_group): Call cygsid::getfromgr_passwd. * grp.cc (pwdgrp::parse_group): Call cygsid::getfromgr_passwd.

View File

@ -1,6 +1,6 @@
/* cygwin/acl.h header file for Cygwin. /* cygwin/acl.h header file for Cygwin.
Copyright 1999, 2000, 2001, 2002, 2010, 2014 Red Hat, Inc. Copyright 1999, 2000, 2001, 2002, 2010, 2014, 2015 Red Hat, Inc.
Written by C. Vinschen. Written by C. Vinschen.
This file is part of Cygwin. This file is part of Cygwin.
@ -25,8 +25,16 @@ extern "C" {
#define GETACL (0x1) #define GETACL (0x1)
#define GETACLCNT (0x2) #define GETACLCNT (0x2)
/* Windows ACLs have a maximum size of 64K. Counting the most pessimistic way,
the maximum number of ACEs is 3276. Technet claims "approximately 1820",
which uses the length of normal user and group SIDs for the computation.
We're now going with 2730, the number of aclent_t entries matching a 32K
buffer.
On one hand, there are only a limited number of SIDs shorter than the normal
user/group SIDs, on the other hand there are no deny aclent_t entries, so we
should be fine with 32K aclbuf_t buffers provided by the caller. */
#define MIN_ACL_ENTRIES (3) // minimal acl entries from GETACLCNT #define MIN_ACL_ENTRIES (3) // minimal acl entries from GETACLCNT
#define MAX_ACL_ENTRIES (256) // max entries of each type #define MAX_ACL_ENTRIES (2730) // max entries of each type
// Return values of aclcheck(3) in case of error */ // Return values of aclcheck(3) in case of error */
#define GRP_ERROR (0x1) #define GRP_ERROR (0x1)

View File

@ -24,6 +24,64 @@ details. */
#include "ntdll.h" #include "ntdll.h"
#include "tls_pbuf.h" #include "tls_pbuf.h"
/* How does a correctly constructed new-style Windows ACL claiming to be a
POSIX ACL look like?
- Cygwin ACE (special bits, CLASS_OBJ).
- If a USER entry has more permissions than any group, Everyone, or if it
has more permissions than allowed by the CLASS_OBJ entry:
USER deny ACEs == POSIX allow
& ^(mask | all group allows | Everyone allow)
- USER_OBJ allow ACE
- USER allow ACEs
The POSIX permissions returned for a USER entry are the allow bits alone!
- If a GROUP entry has more permissions than Everyone, or if it has more
permissions than allowed by the CLASS_OBJ entry:
GROUP deny ACEs == POSIX allow & ^(mask | Everyone allow)
- GROUP_OBJ allow ACE
- GROUP allow ACEs
The POSIX permissions returned for a GROUP entry are the allow bits alone!
- OTHER_OBJ allow ACE
Rinse and repeat for default ACEs with INHERIT flags set.
- Default Cygwin ACE (S_ISGID, CLASS_OBJ). */
/* POSIX <-> Win32 */
/* Historically, these bits are stored in a NULL SID ACE. To distinguish
the new ACL style from the old one, we're now using an invented SID, the
Cygwin SID (S-1-0-1132029815). The new ACEs can exist twice in an ACL,
the "normal one" containg 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_IS_GID)) >> 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)
static int static int
searchace (aclent_t *aclp, int nentries, int type, uid_t id = ILLEGAL_UID) searchace (aclent_t *aclp, int nentries, int type, uid_t id = ILLEGAL_UID)
{ {
@ -235,8 +293,7 @@ setacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp,
allow |= FILE_DELETE_CHILD; allow |= FILE_DELETE_CHILD;
/* Set inherit property. */ /* Set inherit property. */
DWORD inheritance = (aclbufp[i].a_type & ACL_DEFAULT) DWORD inheritance = (aclbufp[i].a_type & ACL_DEFAULT)
? (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE ? (SUB_CONTAINERS_AND_OBJECTS_INHERIT | INHERIT_ONLY)
| INHERIT_ONLY_ACE)
: NO_INHERITANCE; : NO_INHERITANCE;
/* /*
* If a specific acl contains a corresponding default entry with * If a specific acl contains a corresponding default entry with
@ -251,7 +308,7 @@ setacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp,
? aclbufp[i].a_id : ILLEGAL_UID)) >= 0 ? aclbufp[i].a_id : ILLEGAL_UID)) >= 0
&& aclbufp[i].a_perm == aclbufp[i + 1 + pos].a_perm) && aclbufp[i].a_perm == aclbufp[i + 1 + pos].a_perm)
{ {
inheritance = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE; inheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
/* invalidate the corresponding default entry. */ /* invalidate the corresponding default entry. */
invalid[i + 1 + pos] = true; invalid[i + 1 + pos] = true;
} }
@ -337,15 +394,19 @@ setacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp,
#define DENY_R 040000 #define DENY_R 040000
#define DENY_W 020000 #define DENY_W 020000
#define DENY_X 010000 #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 static void
getace (aclent_t &acl, int type, int id, DWORD win_ace_mask, getace (aclent_t &acl, int type, int id, DWORD win_ace_mask,
DWORD win_ace_type) DWORD win_ace_type, bool new_style)
{ {
acl.a_type = type; acl.a_type = type;
acl.a_id = id; acl.a_id = id;
if ((win_ace_mask & FILE_READ_BITS) && !(acl.a_perm & (S_IROTH | DENY_R))) if ((win_ace_mask & FILE_READ_BITS)
&& (new_style || !(acl.a_perm & (S_IROTH | DENY_R))))
{ {
if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE) if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE)
acl.a_perm |= S_IROTH; acl.a_perm |= S_IROTH;
@ -353,7 +414,8 @@ getace (aclent_t &acl, int type, int id, DWORD win_ace_mask,
acl.a_perm |= DENY_R; acl.a_perm |= DENY_R;
} }
if ((win_ace_mask & FILE_WRITE_BITS) && !(acl.a_perm & (S_IWOTH | DENY_W))) if ((win_ace_mask & FILE_WRITE_BITS)
&& (new_style || !(acl.a_perm & (S_IWOTH | DENY_W))))
{ {
if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE) if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE)
acl.a_perm |= S_IWOTH; acl.a_perm |= S_IWOTH;
@ -361,7 +423,8 @@ getace (aclent_t &acl, int type, int id, DWORD win_ace_mask,
acl.a_perm |= DENY_W; acl.a_perm |= DENY_W;
} }
if ((win_ace_mask & FILE_EXEC_BITS) && !(acl.a_perm & (S_IXOTH | DENY_X))) if ((win_ace_mask & FILE_EXEC_BITS)
&& (new_style || !(acl.a_perm & (S_IXOTH | DENY_X))))
{ {
if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE) if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE)
acl.a_perm |= S_IXOTH; acl.a_perm |= S_IXOTH;
@ -370,40 +433,92 @@ getace (aclent_t &acl, int type, int id, DWORD win_ace_mask,
} }
} }
/* 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. */
int int
getacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp) get_posix_access (PSECURITY_DESCRIPTOR psd,
mode_t *attr_ret, uid_t *uid_ret, gid_t *gid_ret,
aclent_t *aclbufp, int nentries)
{ {
security_descriptor sd; tmp_pathbuf tp;
if (get_file_sd (handle, pc, sd, false))
return -1;
cygpsid owner_sid;
cygpsid group_sid;
NTSTATUS status; NTSTATUS status;
BOOLEAN dummy; BOOLEAN dummy, acl_exists;
PACL acl;
PACCESS_ALLOWED_ACE ace;
cygpsid owner_sid, group_sid;
cyg_ldap cldap;
uid_t uid; uid_t uid;
gid_t gid; gid_t gid;
cyg_ldap cldap; mode_t attr = 0;
aclent_t *lacl = NULL;
cygpsid ace_sid;
int pos, type, id;
status = RtlGetOwnerSecurityDescriptor (sd, (PSID *) &owner_sid, &dummy); bool new_style = false;
int types_def = 0;
int def_pgrp_pos = -1;
bool has_class_perm = false, has_def_class_perm = false;
mode_t class_perm = 0, def_class_perm = 0;
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 = ILLEGAL_UID;
if (gid_ret)
*gid_ret = ILLEGAL_GID;
if (aclbufp)
{
aclbufp[0].a_type = USER_OBJ;
aclbufp[0].a_id = ILLEGAL_UID;
aclbufp[0].a_perm = 0;
aclbufp[1].a_type = GROUP_OBJ;
aclbufp[1].a_id = ILLEGAL_GID;
aclbufp[1].a_perm = 0;
aclbufp[2].a_type = OTHER_OBJ;
aclbufp[2].a_id = ILLEGAL_GID;
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)) if (!NT_SUCCESS (status))
{ {
__seterrno_from_nt_status (status); __seterrno_from_nt_status (status);
return -1; 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); uid = owner_sid.get_uid (&cldap);
status = RtlGetGroupSecurityDescriptor (sd, (PSID *) &group_sid, &dummy);
if (!NT_SUCCESS (status))
{
__seterrno_from_nt_status (status);
return -1;
}
gid = group_sid.get_gid (&cldap); gid = group_sid.get_gid (&cldap);
if (attr_ret)
attr |= (*attr_ret & S_IFMT);
aclent_t lacl[MAX_ACL_ENTRIES]; /* Create and initialize local aclent_t array. */
memset (&lacl, 0, MAX_ACL_ENTRIES * sizeof (aclent_t)); lacl = (aclent_t *) tp.c_get ();
memset (lacl, 0, MAX_ACL_ENTRIES * sizeof (aclent_t *));
lacl[0].a_type = USER_OBJ; lacl[0].a_type = USER_OBJ;
lacl[0].a_id = uid; lacl[0].a_id = uid;
lacl[1].a_type = GROUP_OBJ; lacl[1].a_type = GROUP_OBJ;
@ -411,46 +526,60 @@ getacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp)
lacl[2].a_type = OTHER_OBJ; lacl[2].a_type = OTHER_OBJ;
lacl[2].a_id = ILLEGAL_GID; lacl[2].a_id = ILLEGAL_GID;
PACL acl; /* No ACEs? Everybody has full access. */
BOOLEAN acl_exists; if (!acl_exists || !acl || acl->AceCount == 0)
status = RtlGetDaclSecurityDescriptor (sd, &acl_exists, &acl, &dummy);
if (!NT_SUCCESS (status))
{ {
__seterrno_from_nt_status (status); for (pos = 0; pos < MIN_ACL_ENTRIES; ++pos)
return -1; lacl[pos].a_perm = S_IROTH | S_IWOTH | S_IXOTH;
goto out;
} }
int pos, i, types_def = 0; for (int i = 0; i < acl->AceCount; ++i)
int pgrp_pos = 1, def_pgrp_pos = -1;
bool has_class_perm = false, has_def_class_perm = false;
mode_t class_perm = 0, def_class_perm = 0;
if (!acl_exists || !acl)
for (pos = 0; pos < 3; ++pos)
lacl[pos].a_perm = S_IROTH | S_IWOTH | S_IXOTH;
else
{ {
for (i = 0; i < acl->AceCount; ++i)
{
ACCESS_ALLOWED_ACE *ace;
if (!NT_SUCCESS (RtlGetAce (acl, i, (PVOID *) &ace))) if (!NT_SUCCESS (RtlGetAce (acl, i, (PVOID *) &ace)))
continue; continue;
cygpsid ace_sid ((PSID) &ace->SidStart); ace_sid = (PSID) &ace->SidStart;
int id;
int type = 0;
if (ace_sid == well_known_null_sid) if (ace_sid == well_known_null_sid)
{ {
/* Simply ignore. */ /* Old-style or non-Cygwin ACL. Fetch only the special bits. */
attr |= CYG_ACE_ISBITS_TO_POSIX (ace->Mask);
continue; continue;
} }
if (ace_sid == well_known_world_sid) if (ace_sid == well_known_cygwin_sid)
{ {
type = OTHER_OBJ; /* New-style ACL. Note the fact that a mask value is present since
id = ILLEGAL_GID; that changes how getace fetches the information. That's fine,
because the Cygwin SID 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. */
attr |= CYG_ACE_ISBITS_TO_POSIX (ace->Mask);
if (ace->Mask & CYG_ACE_MASK_VALID)
{
new_style = true;
type = (ace->Header.AceFlags & SUB_CONTAINERS_AND_OBJECTS_INHERIT)
? DEF_CLASS_OBJ : CLASS_OBJ;
if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, ILLEGAL_GID))
>= 0)
{
lacl[pos].a_type = type;
lacl[pos].a_id = ILLEGAL_GID;
lacl[pos].a_perm = CYG_ACE_MASK_TO_POSIX (ace->Mask);
}
if (type == CLASS_OBJ) /* Needed for POSIX permissions. */
{
has_class_perm = true;
class_perm = lacl[pos].a_perm;
}
else
{
has_def_class_perm = true;
def_class_perm = lacl[pos].a_perm;
}
}
continue;
} }
else if (ace_sid == owner_sid) else if (ace_sid == owner_sid)
{ {
@ -462,10 +591,9 @@ getacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp)
type = GROUP_OBJ; type = GROUP_OBJ;
id = gid; id = gid;
} }
else if (ace_sid == well_known_creator_group_sid) else if (ace_sid == well_known_world_sid)
{ {
type = DEF_GROUP_OBJ; type = OTHER_OBJ;
types_def |= type;
id = ILLEGAL_GID; id = ILLEGAL_GID;
} }
else if (ace_sid == well_known_creator_owner_sid) else if (ace_sid == well_known_creator_owner_sid)
@ -474,27 +602,36 @@ getacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp)
types_def |= type; types_def |= type;
id = ILLEGAL_GID; id = ILLEGAL_GID;
} }
else if (ace_sid == well_known_creator_group_sid)
{
type = DEF_GROUP_OBJ;
types_def |= type;
id = ILLEGAL_GID;
}
else else
{
id = ace_sid.get_id (TRUE, &type, &cldap); id = ace_sid.get_id (TRUE, &type, &cldap);
if (!type) if (!type)
continue; continue;
if (!(ace->Header.AceFlags & INHERIT_ONLY_ACE || type & ACL_DEFAULT)) }
if (!(ace->Header.AceFlags & INHERIT_ONLY || type & ACL_DEFAULT))
{ {
if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0) if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0)
{ {
getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType); getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType,
new_style && type & (USER | GROUP));
if (!new_style)
{
/* Fix up CLASS_OBJ value. */ /* Fix up CLASS_OBJ value. */
if (type == USER || type == GROUP) if (type & (USER | GROUP))
{ {
has_class_perm = true; has_class_perm = true;
class_perm |= lacl[pos].a_perm; class_perm |= lacl[pos].a_perm;
} }
} }
} }
if ((ace->Header.AceFlags }
& (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE)) if ((ace->Header.AceFlags & SUB_CONTAINERS_AND_OBJECTS_INHERIT))
&& pc.isdir ())
{ {
if (type == USER_OBJ) if (type == USER_OBJ)
type = USER; type = USER;
@ -504,9 +641,12 @@ getacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp)
types_def |= type; types_def |= type;
if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0) if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0)
{ {
getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType); getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType,
new_style && type & (USER | GROUP));
if (!new_style)
{
/* Fix up DEF_CLASS_OBJ value. */ /* Fix up DEF_CLASS_OBJ value. */
if (type == DEF_USER || type == DEF_GROUP) if (type & (USER | GROUP))
{ {
has_def_class_perm = true; has_def_class_perm = true;
def_class_perm |= lacl[pos].a_perm; def_class_perm |= lacl[pos].a_perm;
@ -517,15 +657,17 @@ getacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp)
} }
} }
} }
/* If secondary user and group entries exist in the ACL, fake a matching }
CLASS_OBJ entry. The CLASS_OBJ permissions are the or'ed permissions /* If this is an old-style or non-Cygwin ACL, and secondary user and group
of the primary group permissions and all secondary user and group entries exist in the ACL, fake a matching CLASS_OBJ entry. The CLASS_OBJ
permissions. */ permissions are the or'ed permissions of the primary group permissions
if (has_class_perm && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0) 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_type = CLASS_OBJ;
lacl[pos].a_id = ILLEGAL_GID; lacl[pos].a_id = ILLEGAL_GID;
lacl[pos].a_perm = class_perm | lacl[pgrp_pos].a_perm; lacl[pos].a_perm = class_perm | lacl[1].a_perm;
} }
/* Ensure that the default acl contains at least /* Ensure that the default acl contains at least
DEF_(USER|GROUP|OTHER)_OBJ entries. */ DEF_(USER|GROUP|OTHER)_OBJ entries. */
@ -555,11 +697,12 @@ getacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp)
pos++; pos++;
} }
} }
/* If secondary user default and group default entries exist in the ACL, /* If this is an old-style or non-Cygwin ACL, and secondary user default
fake a matching DEF_CLASS_OBJ entry. The DEF_CLASS_OBJ permissions are and group default entries exist in the ACL, fake a matching DEF_CLASS_OBJ
the or'ed permissions of the primary group default permissions and all entry. The DEF_CLASS_OBJ permissions are the or'ed permissions of the
secondary user and group default permissions. */ primary group default permissions and all secondary user and group def.
if (has_def_class_perm permissions. */
if (!new_style && has_def_class_perm
&& (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0) && (pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) >= 0)
{ {
lacl[pos].a_type = DEF_CLASS_OBJ; lacl[pos].a_type = DEF_CLASS_OBJ;
@ -568,29 +711,80 @@ getacl (HANDLE handle, path_conv &pc, int nentries, aclent_t *aclbufp)
if (def_pgrp_pos >= 0) if (def_pgrp_pos >= 0)
lacl[pos].a_perm |= lacl[def_pgrp_pos].a_perm; lacl[pos].a_perm |= lacl[def_pgrp_pos].a_perm;
} }
}
/* Make sure `pos' contains the number of used entries in lacl. */
if ((pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) < 0) if ((pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) < 0)
pos = MAX_ACL_ENTRIES; pos = MAX_ACL_ENTRIES;
/* For old-style or non-Cygwin ACLs, check for merging permissions. */
if (!new_style)
for (int i = 0; i < pos; ++i)
{
/* Current user? If the user entry has a deny ACE, don't check. */
if (lacl[i].a_id == myself->uid
&& lacl[i].a_type & (USER_OBJ | USER)
&& !(lacl[i].a_type & ACL_DEFAULT)
&& !(lacl[i].a_perm & DENY_RWX))
{
int gpos;
gid_t grps[NGROUPS_MAX];
cyg_ldap cldap;
/* Sum up all permissions of groups the user is member of, plus
everyone perms, and merge them to user perms. */
mode_t grp_perm = lacl[2].a_perm & S_IRWXO;
int gnum = internal_getgroups (NGROUPS_MAX, grps, &cldap);
for (int g = 0; g < gnum && grp_perm != S_IRWXO; ++g)
if ((gpos = 1, grps[g] == lacl[gpos].a_id)
|| (gpos = searchace (lacl, MAX_ACL_ENTRIES, GROUP, grps[g]))
>= 0)
grp_perm |= lacl[gpos].a_perm & S_IRWXO;
lacl[i].a_perm |= grp_perm;
}
/* For all groups, if everyone has more permissions, add everyone
perms to group perms. Skip groups with deny ACE. */
else if (lacl[i].a_id & (GROUP_OBJ | GROUP)
&& !(lacl[i].a_type & ACL_DEFAULT)
&& !(lacl[i].a_perm & DENY_RWX))
lacl[i].a_perm |= lacl[2].a_perm & S_IRWXO;
}
/* 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 (aclbufp)
{ {
#if 0
/* Disable owner/group permissions equivalence if owner SID == group SID.
It's technically not quite correct, but it helps in case a security
conscious application checks if a file has too open permissions. In
fact, since owner == group, there's no security issue here. */
if (owner_sid == group_sid)
lacl[1].a_perm = lacl[0].a_perm;
#endif
if (pos > nentries) if (pos > nentries)
{ {
set_errno (ENOSPC); set_errno (ENOSPC);
return -1; return -1;
} }
memcpy (aclbufp, lacl, pos * sizeof (aclent_t)); memcpy (aclbufp, lacl, pos * sizeof (aclent_t));
for (i = 0; i < pos; ++i) for (int i = 0; i < pos; ++i)
aclbufp[i].a_perm &= ~(DENY_R | DENY_W | DENY_X); aclbufp[i].a_perm &= S_IRWXO;
aclsort32 (pos, 0, aclbufp); aclsort32 (pos, 0, aclbufp);
} }
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 ()); syscall_printf ("%R = getacl(%S)", pos, pc.get_nt_native_path ());
return pos; return pos;
} }

View File

@ -40,6 +40,8 @@ SECURITY_ATTRIBUTES NO_COPY_RO sec_all_nih =
MKSID (well_known_null_sid, "S-1-0-0", MKSID (well_known_null_sid, "S-1-0-0",
SECURITY_NULL_SID_AUTHORITY, 1, SECURITY_NULL_RID); SECURITY_NULL_SID_AUTHORITY, 1, SECURITY_NULL_RID);
MKSID (well_known_cygwin_sid, "S-1-0-1132029815",
SECURITY_NULL_SID_AUTHORITY, 1, 0x43796777); /* "Cygw" */
MKSID (well_known_world_sid, "S-1-1-0", MKSID (well_known_world_sid, "S-1-1-0",
SECURITY_WORLD_SID_AUTHORITY, 1, SECURITY_WORLD_RID); SECURITY_WORLD_SID_AUTHORITY, 1, SECURITY_WORLD_RID);
MKSID (well_known_local_sid, "S-1-2-0", MKSID (well_known_local_sid, "S-1-2-0",
@ -616,22 +618,21 @@ _recycler_sd (void *buf, bool users, bool dir)
pre-Vista permissions the same way as on Vista and later. */ pre-Vista permissions the same way as on Vista and later. */
RtlCreateAcl (dacl, MAX_DACL_LEN (3), ACL_REVISION); RtlCreateAcl (dacl, MAX_DACL_LEN (3), ACL_REVISION);
RtlAddAccessAllowedAceEx (dacl, ACL_REVISION, RtlAddAccessAllowedAceEx (dacl, ACL_REVISION,
dir ? CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE dir ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
: NO_INHERITANCE, : NO_INHERITANCE,
FILE_ALL_ACCESS, well_known_admins_sid); FILE_ALL_ACCESS, well_known_admins_sid);
RtlAddAccessAllowedAceEx (dacl, ACL_REVISION, RtlAddAccessAllowedAceEx (dacl, ACL_REVISION,
dir ? CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE dir ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
: NO_INHERITANCE, : NO_INHERITANCE,
FILE_ALL_ACCESS, well_known_system_sid); FILE_ALL_ACCESS, well_known_system_sid);
if (users) if (users)
RtlAddAccessAllowedAceEx (dacl, ACL_REVISION, NO_PROPAGATE_INHERIT_ACE, RtlAddAccessAllowedAceEx (dacl, ACL_REVISION, INHERIT_NO_PROPAGATE,
FILE_GENERIC_READ | FILE_GENERIC_EXECUTE FILE_GENERIC_READ | FILE_GENERIC_EXECUTE
| FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES, | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES,
well_known_users_sid); well_known_users_sid);
else else
RtlAddAccessAllowedAceEx (dacl, ACL_REVISION, RtlAddAccessAllowedAceEx (dacl, ACL_REVISION,
dir ? CONTAINER_INHERIT_ACE dir ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
| OBJECT_INHERIT_ACE
: NO_INHERITANCE, : NO_INHERITANCE,
FILE_ALL_ACCESS, cygheap->user.sid ()); FILE_ALL_ACCESS, cygheap->user.sid ());
LPVOID ace; LPVOID ace;

View File

@ -234,194 +234,6 @@ set_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd, bool is_chown)
return res; return res;
} }
static void
get_attribute_from_acl (mode_t *attribute, PACL acl, PSID owner_sid,
PSID group_sid, bool grp_member)
{
ACCESS_ALLOWED_ACE *ace;
mode_t allow = 0;
mode_t deny = 0;
mode_t *flags, *anti;
bool isownergroup = RtlEqualSid (owner_sid, group_sid);
for (DWORD i = 0; i < acl->AceCount; ++i)
{
if (!NT_SUCCESS (RtlGetAce (acl, i, (PVOID *) &ace)))
continue;
if (ace->Header.AceFlags & INHERIT_ONLY_ACE)
continue;
switch (ace->Header.AceType)
{
case ACCESS_ALLOWED_ACE_TYPE:
flags = &allow;
anti = &deny;
break;
case ACCESS_DENIED_ACE_TYPE:
flags = &deny;
anti = &allow;
break;
default:
continue;
}
cygpsid ace_sid ((PSID) &ace->SidStart);
if (ace_sid == well_known_world_sid)
{
if (ace->Mask & FILE_READ_BITS)
*flags |= ((!(*anti & S_IROTH)) ? S_IROTH : 0)
| ((!isownergroup && !(*anti & S_IRGRP)) ? S_IRGRP : 0)
| ((!(*anti & S_IRUSR)) ? S_IRUSR : 0);
if (ace->Mask & FILE_WRITE_BITS)
*flags |= ((!(*anti & S_IWOTH)) ? S_IWOTH : 0)
| ((!isownergroup && !(*anti & S_IWGRP)) ? S_IWGRP : 0)
| ((!(*anti & S_IWUSR)) ? S_IWUSR : 0);
if (ace->Mask & FILE_EXEC_BITS)
*flags |= ((!(*anti & S_IXOTH)) ? S_IXOTH : 0)
| ((!isownergroup && !(*anti & S_IXGRP)) ? S_IXGRP : 0)
| ((!(*anti & S_IXUSR)) ? S_IXUSR : 0);
if ((S_ISDIR (*attribute)) &&
(ace->Mask & (FILE_WRITE_DATA | FILE_EXECUTE | FILE_DELETE_CHILD))
== (FILE_WRITE_DATA | FILE_EXECUTE))
*flags |= S_ISVTX;
}
else if (ace_sid == well_known_null_sid)
{
/* Read SUID, SGID and VTX bits from NULL ACE. */
if (ace->Mask & FILE_READ_DATA)
*flags |= S_ISVTX;
if (ace->Mask & FILE_WRITE_DATA)
*flags |= S_ISGID;
if (ace->Mask & FILE_APPEND_DATA)
*flags |= S_ISUID;
}
else if (ace_sid == owner_sid)
{
if (ace->Mask & FILE_READ_BITS)
*flags |= ((!(*anti & S_IRUSR)) ? S_IRUSR : 0);
if (ace->Mask & FILE_WRITE_BITS)
*flags |= ((!(*anti & S_IWUSR)) ? S_IWUSR : 0);
if (ace->Mask & FILE_EXEC_BITS)
*flags |= ((!(*anti & S_IXUSR)) ? S_IXUSR : 0);
/* Apply deny mask to group if group SID == owner SID. */
if (group_sid && isownergroup
&& ace->Header.AceType == ACCESS_DENIED_ACE_TYPE)
{
if (ace->Mask & FILE_READ_BITS)
*flags |= ((!(*anti & S_IRUSR)) ? S_IRGRP : 0);
if (ace->Mask & FILE_WRITE_BITS)
*flags |= ((!(*anti & S_IWUSR)) ? S_IWGRP : 0);
if (ace->Mask & FILE_EXEC_BITS)
*flags |= ((!(*anti & S_IXUSR)) ? S_IXGRP : 0);
}
}
else if (ace_sid == group_sid)
{
if (ace->Mask & FILE_READ_BITS)
*flags |= ((!(*anti & S_IRGRP)) ? S_IRGRP : 0)
| ((grp_member && !(*anti & S_IRUSR)) ? S_IRUSR : 0);
if (ace->Mask & FILE_WRITE_BITS)
*flags |= ((!(*anti & S_IWGRP)) ? S_IWGRP : 0)
| ((grp_member && !(*anti & S_IWUSR)) ? S_IWUSR : 0);
if (ace->Mask & FILE_EXEC_BITS)
*flags |= ((!(*anti & S_IXGRP)) ? S_IXGRP : 0)
| ((grp_member && !(*anti & S_IXUSR)) ? S_IXUSR : 0);
}
else if (flags == &allow)
{
/* Simplified computation of additional group permissions based on
the CLASS_OBJ value. CLASS_OBJ represents the or'ed value of
the primary group permissions and all secondary user and group
permissions. FIXME: This only takes ACCESS_ALLOWED_ACEs into
account. The computation with additional ACCESS_DENIED_ACE
handling is much more complicated. */
if (ace->Mask & FILE_READ_BITS)
*flags |= S_IRGRP;
if (ace->Mask & FILE_WRITE_BITS)
*flags |= S_IWGRP;
if (ace->Mask & FILE_EXEC_BITS)
*flags |= S_IXGRP;
}
}
*attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX | S_ISGID | S_ISUID);
#if 0
/* Disable owner/group permissions equivalence if owner SID == group SID.
It's technically not quite correct, but it helps in case a security
conscious application checks if a file has too open permissions. In
fact, since owner == group, there's no security issue here. */
if (owner_sid && group_sid && RtlEqualSid (owner_sid, group_sid)
/* FIXME: temporary exception for /var/empty */
&& well_known_system_sid != group_sid)
{
allow &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
allow |= (((allow & S_IRUSR) ? S_IRGRP : 0)
| ((allow & S_IWUSR) ? S_IWGRP : 0)
| ((allow & S_IXUSR) ? S_IXGRP : 0));
}
#endif
*attribute |= allow;
}
static void
get_info_from_sd (PSECURITY_DESCRIPTOR psd, mode_t *attribute,
uid_t *uidret, gid_t *gidret)
{
if (!psd)
{
/* If reading the security descriptor failed, treat the object
as unreadable. */
if (attribute)
*attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO);
if (uidret)
*uidret = ILLEGAL_UID;
if (gidret)
*gidret = ILLEGAL_GID;
return;
}
cygpsid owner_sid;
cygpsid group_sid;
NTSTATUS status;
BOOLEAN dummy;
status = RtlGetOwnerSecurityDescriptor (psd, (PSID *) &owner_sid, &dummy);
if (!NT_SUCCESS (status))
debug_printf ("RtlGetOwnerSecurityDescriptor: %y", status);
status = RtlGetGroupSecurityDescriptor (psd, (PSID *) &group_sid, &dummy);
if (!NT_SUCCESS (status))
debug_printf ("RtlGetGroupSecurityDescriptor: %y", status);
uid_t uid;
gid_t gid;
bool grp_member = get_sids_info (owner_sid, group_sid, &uid, &gid);
if (uidret)
*uidret = uid;
if (gidret)
*gidret = gid;
if (!attribute)
{
syscall_printf ("uid %u, gid %u", uid, gid);
return;
}
PACL acl;
BOOLEAN acl_exists;
status = RtlGetDaclSecurityDescriptor (psd, &acl_exists, &acl, &dummy);
if (!NT_SUCCESS (status))
{
__seterrno_from_nt_status (status);
*attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO);
}
else if (!acl_exists || !acl)
*attribute |= S_IRWXU | S_IRWXG | S_IRWXO;
else
get_attribute_from_acl (attribute, acl, owner_sid, group_sid, grp_member);
syscall_printf ("%sACL %y, uid %u, gid %u",
(!acl_exists || !acl)?"NO ":"", *attribute, uid, gid);
}
static int static int
get_reg_sd (HANDLE handle, security_descriptor &sd_ret) get_reg_sd (HANDLE handle, security_descriptor &sd_ret)
{ {
@ -454,7 +266,7 @@ get_reg_attribute (HKEY hkey, mode_t *attribute, uid_t *uidret,
if (!get_reg_sd (hkey, sd)) if (!get_reg_sd (hkey, sd))
{ {
get_info_from_sd (sd, attribute, uidret, gidret); get_posix_access (sd, attribute, uidret, gidret, NULL, 0);
return 0; return 0;
} }
/* The entries are already set to default values */ /* The entries are already set to default values */
@ -471,7 +283,7 @@ get_file_attribute (HANDLE handle, path_conv &pc,
if (!get_file_sd (handle, pc, sd, false)) if (!get_file_sd (handle, pc, sd, false))
{ {
get_info_from_sd (sd, attribute, uidret, gidret); get_posix_access (sd, attribute, uidret, gidret, NULL, 0);
return 0; return 0;
} }
/* ENOSYS is returned by get_file_sd if fetching the DACL from a remote /* ENOSYS is returned by get_file_sd if fetching the DACL from a remote
@ -773,10 +585,10 @@ alloc_sd (path_conv &pc, uid_t uid, gid_t gid, int attribute,
{ {
if ((S_ISDIR (attribute) && (attribute & S_JUSTCREATED)) if ((S_ISDIR (attribute) && (attribute & S_JUSTCREATED))
|| (ace->Header.AceFlags || (ace->Header.AceFlags
& (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE)) == 0) & (SUB_CONTAINERS_AND_OBJECTS_INHERIT)) == 0)
continue; continue;
else else
ace->Header.AceFlags |= INHERIT_ONLY_ACE; ace->Header.AceFlags |= INHERIT_ONLY;
} }
if (attribute & S_JUSTCREATED) if (attribute & S_JUSTCREATED)
{ {
@ -797,8 +609,7 @@ alloc_sd (path_conv &pc, uid_t uid, gid_t gid, int attribute,
it inherits the default ACL from its parent, so mark it inherits the default ACL from its parent, so mark
all unrelated, inherited ACEs inheritable. */ all unrelated, inherited ACEs inheritable. */
if (S_ISDIR (attribute)) if (S_ISDIR (attribute))
ace->Header.AceFlags |= CONTAINER_INHERIT_ACE ace->Header.AceFlags |= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
| OBJECT_INHERIT_ACE;
} }
else if (uid == ILLEGAL_UID && gid == ILLEGAL_UID else if (uid == ILLEGAL_UID && gid == ILLEGAL_UID
&& ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE && ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE
@ -835,8 +646,7 @@ alloc_sd (path_conv &pc, uid_t uid, gid_t gid, int attribute,
Cygwin applications don't need this. */ Cygwin applications don't need this. */
if (S_ISDIR (attribute) && (attribute & S_JUSTCREATED)) if (S_ISDIR (attribute) && (attribute & S_JUSTCREATED))
{ {
const DWORD inherit = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE const DWORD inherit = SUB_CONTAINERS_AND_OBJECTS_INHERIT | INHERIT_ONLY;
| INHERIT_ONLY_ACE;
/* Set allow ACE for owner. */ /* Set allow ACE for owner. */
if (!add_access_allowed_ace (acl, ace_off++, owner_allow, if (!add_access_allowed_ace (acl, ace_off++, owner_allow,
well_known_creator_owner_sid, acl_len, well_known_creator_owner_sid, acl_len,
@ -936,7 +746,7 @@ get_object_attribute (HANDLE handle, uid_t *uidret, gid_t *gidret,
if (get_object_sd (handle, sd)) if (get_object_sd (handle, sd))
return -1; return -1;
get_info_from_sd (sd, attribute, uidret, gidret); get_posix_access (sd, attribute, uidret, gidret, NULL, 0);
return 0; return 0;
} }

View File

@ -393,6 +393,7 @@ public:
}; };
extern cygpsid well_known_null_sid; extern cygpsid well_known_null_sid;
extern cygpsid well_known_cygwin_sid;
extern cygpsid well_known_world_sid; extern cygpsid well_known_world_sid;
extern cygpsid well_known_local_sid; extern cygpsid well_known_local_sid;
extern cygpsid well_known_console_logon_sid; extern cygpsid well_known_console_logon_sid;
@ -463,6 +464,8 @@ bool get_sids_info (cygpsid, cygpsid, uid_t * , gid_t *);
struct acl; struct acl;
extern "C" int aclsort32 (int, int, struct acl *); extern "C" int aclsort32 (int, int, struct acl *);
extern "C" int acl32 (const char *, int, int, struct acl *); extern "C" int acl32 (const char *, int, int, struct acl *);
int get_posix_access (PSECURITY_DESCRIPTOR, mode_t *, uid_t *, gid_t *,
struct acl *, int);
int getacl (HANDLE, path_conv &, int, struct acl *); int getacl (HANDLE, path_conv &, int, struct acl *);
int setacl (HANDLE, path_conv &, int, struct acl *, bool &); int setacl (HANDLE, path_conv &, int, struct acl *, bool &);