diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2022-08-04 17:58:50 +0300 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2022-08-05 13:02:11 +0300 |
commit | 007e23d6390af11582e55453269b7a51c723d2dd (patch) | |
tree | 8e8cff3ca23f5e56d9766a5ee6c6abb366611b07 /winsup/cygwin/sec_acl.cc | |
parent | 1e428bee1c5ef7c76ba4e46e6693b913edc9bbf3 (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.cc | 1966 |
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); -} |