Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorinna Vinschen <corinna@vinschen.de>2022-08-04 17:58:50 +0300
committerCorinna Vinschen <corinna@vinschen.de>2022-08-05 13:02:11 +0300
commit007e23d6390af11582e55453269b7a51c723d2dd (patch)
tree8e8cff3ca23f5e56d9766a5ee6c6abb366611b07 /winsup/cygwin/sec_acl.cc
parent1e428bee1c5ef7c76ba4e46e6693b913edc9bbf3 (diff)
Cygwin: Reorganize cygwin source dir
Create subdirs and move files accordingly: - DevDocs: doc files - fhandler: fhandler sources, split fhandler.cc into base.cc and null.cc - local_includes: local include files - scripts: scripts called during build - sec: security sources Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
Diffstat (limited to 'winsup/cygwin/sec_acl.cc')
-rw-r--r--winsup/cygwin/sec_acl.cc1966
1 files changed, 0 insertions, 1966 deletions
diff --git a/winsup/cygwin/sec_acl.cc b/winsup/cygwin/sec_acl.cc
deleted file mode 100644
index 03dbb8050..000000000
--- a/winsup/cygwin/sec_acl.cc
+++ /dev/null
@@ -1,1966 +0,0 @@
-/* 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 (S_ISDIR (attr)
- && (ace->Header.AceFlags & SUB_CONTAINERS_AND_OBJECTS_INHERIT) != 0)
- {
- 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;
- id = ACL_UNDEFINED_ID;
- saw_def_user_obj = true;
- }
- else if (ace_sid == well_known_creator_group_sid)
- {
- type = DEF_GROUP_OBJ;
- 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 (S_ISDIR (attr)
- && (ace->Header.AceFlags & SUB_CONTAINERS_AND_OBJECTS_INHERIT) != 0)
- {
- 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;
- aclsort (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;
- mode_t attr = pc.isdir () ? S_IFDIR : 0;
-
- if (get_file_sd (handle, pc, sd, false))
- return -1;
- int pos = get_posix_access (sd, &attr, NULL, NULL, aclbufp, nentries);
- syscall_printf ("%R = getacl(%S)", pos, pc.get_nt_native_path ());
- return pos;
-}
-
-extern "C" int
-acl (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;
-}
-
-extern "C" int
-facl (int fd, int cmd, int nentries, aclent_t *aclbufp)
-{
- cygheap_fdget cfd (fd);
- if (cfd < 0)
- {
- syscall_printf ("-1 = facl (%d)", fd);
- return -1;
- }
- if (cfd->get_flags () & O_PATH)
- {
- set_errno (EBADF);
- 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
-aclcheck (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;
- fallthrough;
- 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
-aclsort (int nentries, int calclass, aclent_t *aclbufp)
-{
- if (!aclbufp || nentries < MIN_ACL_ENTRIES
- || aclcheck (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
-acltomode (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
-aclfrommode (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
-acltopbits (aclent_t *aclbufp, int nentries, mode_t *pbitsp)
-{
- return acltomode (aclbufp, nentries, pbitsp);
-}
-
-extern "C" int
-aclfrompbits (aclent_t *aclbufp, int nentries, mode_t *pbitsp)
-{
- return aclfrommode (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 < 0 || aclcnt > MAX_ACL_ENTRIES
- || (aclcnt > 0 && aclsort (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 *
-acltotext (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 *
-aclfromtext (char *acltextp, int *aclcnt)
-{
- return (aclent_t *) __aclfromtext (acltextp, aclcnt, false);
-}