diff options
Diffstat (limited to 'winsup/cygwin/security.cc')
-rw-r--r-- | winsup/cygwin/security.cc | 1208 |
1 files changed, 0 insertions, 1208 deletions
diff --git a/winsup/cygwin/security.cc b/winsup/cygwin/security.cc deleted file mode 100644 index 4c46e05d9..000000000 --- a/winsup/cygwin/security.cc +++ /dev/null @@ -1,1208 +0,0 @@ -/* security.cc: NT file access control functions - - Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, - 2008, 2009, 2010, 2011, 2012, 2013, 2014 Red Hat, Inc. - - Originaly written by Gunther Ebert, gunther.ebert@ixos-leipzig.de - Completely rewritten by Corinna Vinschen <corinna@vinschen.de> - -This file is part of Cygwin. - -This software is a copyrighted work licensed under the terms of the -Cygwin license. Please consult the file "CYGWIN_LICENSE" for -details. */ - -#include "winsup.h" -#include <unistd.h> -#include <stdlib.h> -#include "cygerrno.h" -#include "security.h" -#include "path.h" -#include "fhandler.h" -#include "dtable.h" -#include "pinfo.h" -#include "cygheap.h" -#include "ntdll.h" -#include "tls_pbuf.h" -#include <aclapi.h> - -#define ALL_SECURITY_INFORMATION (DACL_SECURITY_INFORMATION \ - | GROUP_SECURITY_INFORMATION \ - | OWNER_SECURITY_INFORMATION) - -static GENERIC_MAPPING NO_COPY_RO file_mapping = { FILE_GENERIC_READ, - FILE_GENERIC_WRITE, - FILE_GENERIC_EXECUTE, - FILE_ALL_ACCESS }; - -LONG -get_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd, - bool justcreated) -{ - NTSTATUS status = STATUS_SUCCESS; - OBJECT_ATTRIBUTES attr; - IO_STATUS_BLOCK io; - ULONG len = SD_MAXIMUM_SIZE, rlen; - - /* Allocate space for the security descriptor. */ - if (!sd.malloc (len)) - { - set_errno (ENOMEM); - return -1; - } - /* Try to fetch the security descriptor if the handle is valid. */ - if (fh) - { - status = NtQuerySecurityObject (fh, ALL_SECURITY_INFORMATION, - sd, len, &rlen); - if (!NT_SUCCESS (status)) - debug_printf ("NtQuerySecurityObject (%S), status %y", - pc.get_nt_native_path (), status); - } - /* If the handle was NULL, or fetching with the original handle didn't work, - try to reopen the file with READ_CONTROL and fetch the security descriptor - using that handle. */ - if (!fh || !NT_SUCCESS (status)) - { - status = NtOpenFile (&fh, READ_CONTROL, - fh ? pc.init_reopen_attr (attr, fh) - : pc.get_object_attr (attr, sec_none_nih), - &io, FILE_SHARE_VALID_FLAGS, - FILE_OPEN_FOR_BACKUP_INTENT); - if (!NT_SUCCESS (status)) - { - sd.free (); - __seterrno_from_nt_status (status); - return -1; - } - status = NtQuerySecurityObject (fh, ALL_SECURITY_INFORMATION, - sd, len, &rlen); - NtClose (fh); - if (!NT_SUCCESS (status)) - { - sd.free (); - __seterrno_from_nt_status (status); - return -1; - } - } - /* Ok, so we have a security descriptor now. Unfortunately, if you want - to know if an ACE is inherited from the parent object, you can't just - call NtQuerySecurityObject once. The problem is this: - - In the simple case, the SDs control word contains one of the - SE_DACL_AUTO_INHERITED or SE_DACL_PROTECTED flags, or at least one of - the ACEs has the INHERITED_ACE flag set. In all of these cases the - GetSecurityInfo function calls NtQuerySecurityObject only once, too, - apparently because it figures that the DACL is self-sufficient, which - it usually is. Windows Explorer, for instance, takes great care to - set these flags in a security descriptor if you change the ACL in the - GUI property dialog. - - The tricky case is if none of these flags is set in the SD. That means - the information whether or not an ACE has been inherited is not available - in the DACL of the object. In this case GetSecurityInfo also fetches the - SD from the parent directory and tests if the object's SD contains - inherited ACEs from the parent. The below code is closly emulating the - behaviour of GetSecurityInfo so we can get rid of this advapi32 dependency. - - However, this functionality is slow, and the extra information is only - required when the file has been created and the permissions are about - to be set to POSIX permissions. Therefore we only use it in case the - file just got created. - - Note that GetSecurityInfo has a problem on 5.1 and 5.2 kernels. Sometimes - it returns ERROR_INVALID_ADDRESS if a former request for the parent - directories' SD used NtQuerySecurityObject, rather than GetSecurityInfo - as well. See http://cygwin.com/ml/cygwin-developers/2011-03/msg00027.html - for the solution. This problem does not occur with the below code, so - the workaround has been removed. */ - if (justcreated) - { - SECURITY_DESCRIPTOR_CONTROL ctrl; - ULONG dummy; - PACL dacl; - BOOLEAN exists, def; - ACCESS_ALLOWED_ACE *ace; - UNICODE_STRING dirname; - PSECURITY_DESCRIPTOR psd, nsd; - tmp_pathbuf tp; - - /* Check SDs control flags. If SE_DACL_AUTO_INHERITED or - SE_DACL_PROTECTED is set we're done. */ - RtlGetControlSecurityDescriptor (sd, &ctrl, &dummy); - if (ctrl & (SE_DACL_AUTO_INHERITED | SE_DACL_PROTECTED)) - return 0; - /* Otherwise iterate over the ACEs and see if any one of them has the - INHERITED_ACE flag set. If so, we're done. */ - if (NT_SUCCESS (RtlGetDaclSecurityDescriptor (sd, &exists, &dacl, &def)) - && exists && dacl) - for (ULONG idx = 0; idx < dacl->AceCount; ++idx) - if (NT_SUCCESS (RtlGetAce (dacl, idx, (PVOID *) &ace)) - && (ace->Header.AceFlags & INHERITED_ACE)) - return 0; - /* Otherwise, open the parent directory with READ_CONTROL... */ - RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, NULL); - InitializeObjectAttributes (&attr, &dirname, pc.objcaseinsensitive (), - NULL, NULL); - status = NtOpenFile (&fh, READ_CONTROL, &attr, &io, - FILE_SHARE_VALID_FLAGS, - FILE_OPEN_FOR_BACKUP_INTENT - | FILE_OPEN_REPARSE_POINT); - if (!NT_SUCCESS (status)) - { - debug_printf ("NtOpenFile (%S), status %y", &dirname, status); - return 0; - } - /* ... fetch the parent's security descriptor ... */ - psd = (PSECURITY_DESCRIPTOR) tp.w_get (); - status = NtQuerySecurityObject (fh, ALL_SECURITY_INFORMATION, - psd, len, &rlen); - NtClose (fh); - if (!NT_SUCCESS (status)) - { - debug_printf ("NtQuerySecurityObject (%S), status %y", - &dirname, status); - return 0; - } - /* ... and create a new security descriptor in which all inherited ACEs - are marked with the INHERITED_ACE flag. For a description of the - undocumented RtlConvertToAutoInheritSecurityObject function from - ntdll.dll see the MSDN man page for the advapi32 function - ConvertToAutoInheritPrivateObjectSecurity. Fortunately the latter - is just a shim. */ - status = RtlConvertToAutoInheritSecurityObject (psd, sd, &nsd, NULL, - pc.isdir (), - &file_mapping); - if (!NT_SUCCESS (status)) - { - debug_printf ("RtlConvertToAutoInheritSecurityObject (%S), status %y", - &dirname, status); - return 0; - } - /* Eventually copy the new security descriptor into sd and delete the - original one created by RtlConvertToAutoInheritSecurityObject from - the heap. */ - len = RtlLengthSecurityDescriptor (nsd); - memcpy ((PSECURITY_DESCRIPTOR) sd, nsd, len); - RtlDeleteSecurityObject (&nsd); - } - return 0; -} - -LONG -set_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd, bool is_chown) -{ - NTSTATUS status = STATUS_SUCCESS; - int retry = 0; - int res = -1; - - for (; retry < 2; ++retry) - { - if (fh) - { - status = NtSetSecurityObject (fh, - is_chown ? ALL_SECURITY_INFORMATION - : DACL_SECURITY_INFORMATION, - sd); - if (NT_SUCCESS (status)) - { - res = 0; - break; - } - } - if (!retry) - { - OBJECT_ATTRIBUTES attr; - IO_STATUS_BLOCK io; - status = NtOpenFile (&fh, (is_chown ? WRITE_OWNER : 0) | WRITE_DAC, - fh ? pc.init_reopen_attr (attr, fh) - : pc.get_object_attr (attr, sec_none_nih), - &io, - FILE_SHARE_VALID_FLAGS, - FILE_OPEN_FOR_BACKUP_INTENT); - if (!NT_SUCCESS (status)) - { - fh = NULL; - break; - } - } - } - if (retry && fh) - NtClose (fh); - if (!NT_SUCCESS (status)) - __seterrno_from_nt_status (status); - return res; -} - -static void -get_attribute_from_acl (mode_t *attribute, PACL acl, PSID owner_sid, - PSID group_sid, bool grp_member) -{ - ACCESS_ALLOWED_ACE *ace; - int allow = 0; - int deny = 0; - int *flags, *anti; - - for (DWORD i = 0; i < acl->AceCount; ++i) - { - if (!NT_SUCCESS (RtlGetAce (acl, i, (PVOID *) &ace))) - continue; - if (ace->Header.AceFlags & INHERIT_ONLY_ACE) - continue; - switch (ace->Header.AceType) - { - case ACCESS_ALLOWED_ACE_TYPE: - flags = &allow; - anti = &deny; - break; - case ACCESS_DENIED_ACE_TYPE: - flags = &deny; - anti = &allow; - break; - default: - continue; - } - - cygpsid ace_sid ((PSID) &ace->SidStart); - if (ace_sid == well_known_world_sid) - { - if (ace->Mask & FILE_READ_BITS) - *flags |= ((!(*anti & S_IROTH)) ? S_IROTH : 0) - | ((!(*anti & S_IRGRP)) ? S_IRGRP : 0) - | ((!(*anti & S_IRUSR)) ? S_IRUSR : 0); - if (ace->Mask & FILE_WRITE_BITS) - *flags |= ((!(*anti & S_IWOTH)) ? S_IWOTH : 0) - | ((!(*anti & S_IWGRP)) ? S_IWGRP : 0) - | ((!(*anti & S_IWUSR)) ? S_IWUSR : 0); - if (ace->Mask & FILE_EXEC_BITS) - *flags |= ((!(*anti & S_IXOTH)) ? S_IXOTH : 0) - | ((!(*anti & S_IXGRP)) ? S_IXGRP : 0) - | ((!(*anti & S_IXUSR)) ? S_IXUSR : 0); - if ((S_ISDIR (*attribute)) && - (ace->Mask & (FILE_WRITE_DATA | FILE_EXECUTE | FILE_DELETE_CHILD)) - == (FILE_WRITE_DATA | FILE_EXECUTE)) - *flags |= S_ISVTX; - } - else if (ace_sid == well_known_null_sid) - { - /* Read SUID, SGID and VTX bits from NULL ACE. */ - if (ace->Mask & FILE_READ_DATA) - *flags |= S_ISVTX; - if (ace->Mask & FILE_WRITE_DATA) - *flags |= S_ISGID; - if (ace->Mask & FILE_APPEND_DATA) - *flags |= S_ISUID; - } - else if (ace_sid == owner_sid) - { - if (ace->Mask & FILE_READ_BITS) - *flags |= ((!(*anti & S_IRUSR)) ? S_IRUSR : 0); - if (ace->Mask & FILE_WRITE_BITS) - *flags |= ((!(*anti & S_IWUSR)) ? S_IWUSR : 0); - if (ace->Mask & FILE_EXEC_BITS) - *flags |= ((!(*anti & S_IXUSR)) ? S_IXUSR : 0); - } - else if (ace_sid == group_sid) - { - if (ace->Mask & FILE_READ_BITS) - *flags |= ((!(*anti & S_IRGRP)) ? S_IRGRP : 0) - | ((grp_member && !(*anti & S_IRUSR)) ? S_IRUSR : 0); - if (ace->Mask & FILE_WRITE_BITS) - *flags |= ((!(*anti & S_IWGRP)) ? S_IWGRP : 0) - | ((grp_member && !(*anti & S_IWUSR)) ? S_IWUSR : 0); - if (ace->Mask & FILE_EXEC_BITS) - *flags |= ((!(*anti & S_IXGRP)) ? S_IXGRP : 0) - | ((grp_member && !(*anti & S_IXUSR)) ? S_IXUSR : 0); - } - else if (flags == &allow) - { - /* Simplified computation of additional group permissions based on - the CLASS_OBJ value. CLASS_OBJ represents the or'ed value of - the primary group permissions and all secondary user and group - permissions. FIXME: This only takes ACCESS_ALLOWED_ACEs into - account. The computation with additional ACCESS_DENIED_ACE - handling is much more complicated. */ - if (ace->Mask & FILE_READ_BITS) - *flags |= S_IRGRP; - if (ace->Mask & FILE_WRITE_BITS) - *flags |= S_IWGRP; - if (ace->Mask & FILE_EXEC_BITS) - *flags |= S_IXGRP; - } - } - *attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX | S_ISGID | S_ISUID); - if (owner_sid && group_sid && RtlEqualSid (owner_sid, group_sid) - /* FIXME: temporary exception for /var/empty */ - && well_known_system_sid != group_sid) - { - allow &= ~(S_IRGRP | S_IWGRP | S_IXGRP); - allow |= (((allow & S_IRUSR) ? S_IRGRP : 0) - | ((allow & S_IWUSR) ? S_IWGRP : 0) - | ((allow & S_IXUSR) ? S_IXGRP : 0)); - } - *attribute |= allow; -} - -static void -get_info_from_sd (PSECURITY_DESCRIPTOR psd, mode_t *attribute, - uid_t *uidret, gid_t *gidret) -{ - if (!psd) - { - /* If reading the security descriptor failed, treat the object - as unreadable. */ - if (attribute) - *attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO); - if (uidret) - *uidret = ILLEGAL_UID; - if (gidret) - *gidret = ILLEGAL_GID; - return; - } - - cygpsid owner_sid; - cygpsid group_sid; - NTSTATUS status; - BOOLEAN dummy; - - status = RtlGetOwnerSecurityDescriptor (psd, (PSID *) &owner_sid, &dummy); - if (!NT_SUCCESS (status)) - debug_printf ("RtlGetOwnerSecurityDescriptor: %y", status); - status = RtlGetGroupSecurityDescriptor (psd, (PSID *) &group_sid, &dummy); - if (!NT_SUCCESS (status)) - debug_printf ("RtlGetGroupSecurityDescriptor: %y", status); - - uid_t uid; - gid_t gid; - bool grp_member = get_sids_info (owner_sid, group_sid, &uid, &gid); - if (uidret) - *uidret = uid; - if (gidret) - *gidret = gid; - - if (!attribute) - { - syscall_printf ("uid %u, gid %u", uid, gid); - return; - } - - PACL acl; - BOOLEAN acl_exists; - - status = RtlGetDaclSecurityDescriptor (psd, &acl_exists, &acl, &dummy); - if (!NT_SUCCESS (status)) - { - __seterrno_from_nt_status (status); - *attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO); - } - else if (!acl_exists || !acl) - *attribute |= S_IRWXU | S_IRWXG | S_IRWXO; - else - get_attribute_from_acl (attribute, acl, owner_sid, group_sid, grp_member); - - syscall_printf ("%sACL %y, uid %u, gid %u", - (!acl_exists || !acl)?"NO ":"", *attribute, uid, gid); -} - -static int -get_reg_sd (HANDLE handle, security_descriptor &sd_ret) -{ - LONG ret; - DWORD len = 0; - - ret = RegGetKeySecurity ((HKEY) handle, ALL_SECURITY_INFORMATION, - sd_ret, &len); - if (ret == ERROR_INSUFFICIENT_BUFFER) - { - if (!sd_ret.malloc (len)) - set_errno (ENOMEM); - else - ret = RegGetKeySecurity ((HKEY) handle, ALL_SECURITY_INFORMATION, - sd_ret, &len); - } - if (ret != ERROR_SUCCESS) - { - __seterrno (); - return -1; - } - return 0; -} - -int -get_reg_attribute (HKEY hkey, mode_t *attribute, uid_t *uidret, - gid_t *gidret) -{ - security_descriptor sd; - - if (!get_reg_sd (hkey, sd)) - { - get_info_from_sd (sd, attribute, uidret, gidret); - return 0; - } - /* The entries are already set to default values */ - return -1; -} - -int -get_file_attribute (HANDLE handle, path_conv &pc, - mode_t *attribute, uid_t *uidret, gid_t *gidret) -{ - if (pc.has_acls ()) - { - security_descriptor sd; - - if (!get_file_sd (handle, pc, sd, false)) - { - get_info_from_sd (sd, attribute, uidret, gidret); - return 0; - } - /* ENOSYS is returned by get_file_sd if fetching the DACL from a remote - share returns STATUS_INVALID_NETWORK_RESPONSE, which in turn is - converted to ERROR_BAD_NET_RESP. This potentially occurs when trying - to fetch DACLs from a NT4 machine which is not part of the domain of - the requesting machine. */ - else if (get_errno () != ENOSYS) - { - if (uidret) - *uidret = ILLEGAL_UID; - if (gidret) - *gidret = ILLEGAL_GID; - - return -1; - } - } - - if (uidret) - *uidret = myself->uid; - if (gidret) - *gidret = myself->gid; - - return -1; -} - -bool -add_access_allowed_ace (PACL acl, int offset, DWORD attributes, - PSID sid, size_t &len_add, DWORD inherit) -{ - NTSTATUS status = RtlAddAccessAllowedAceEx (acl, ACL_REVISION, inherit, - attributes, sid); - if (!NT_SUCCESS (status)) - { - __seterrno_from_nt_status (status); - return false; - } - len_add += sizeof (ACCESS_ALLOWED_ACE) - sizeof (DWORD) + RtlLengthSid (sid); - return true; -} - -bool -add_access_denied_ace (PACL acl, int offset, DWORD attributes, - PSID sid, size_t &len_add, DWORD inherit) -{ - NTSTATUS status = RtlAddAccessDeniedAceEx (acl, ACL_REVISION, inherit, - attributes, sid); - if (!NT_SUCCESS (status)) - { - __seterrno_from_nt_status (status); - return false; - } - len_add += sizeof (ACCESS_DENIED_ACE) - sizeof (DWORD) + RtlLengthSid (sid); - return true; -} - -static PSECURITY_DESCRIPTOR -alloc_sd (path_conv &pc, uid_t uid, gid_t gid, int attribute, - security_descriptor &sd_ret) -{ - NTSTATUS status; - BOOLEAN dummy; - tmp_pathbuf tp; - - /* NOTE: If the high bit of attribute is set, we have just created - a file or directory. See below for an explanation. */ - - debug_printf("uid %u, gid %u, attribute 0%o", uid, gid, attribute); - - /* Get owner and group from current security descriptor. */ - PSID cur_owner_sid = NULL; - PSID cur_group_sid = NULL; - status = RtlGetOwnerSecurityDescriptor (sd_ret, &cur_owner_sid, &dummy); - if (!NT_SUCCESS (status)) - debug_printf ("RtlGetOwnerSecurityDescriptor: %y", status); - status = RtlGetGroupSecurityDescriptor (sd_ret, &cur_group_sid, &dummy); - if (!NT_SUCCESS (status)) - debug_printf ("RtlGetGroupSecurityDescriptor: %y", status); - - /* Get SID of owner. */ - cygsid owner_sid; - /* Check for current user first */ - if (uid == myself->uid) - owner_sid = cygheap->user.sid (); - else if (uid == ILLEGAL_UID) - owner_sid = cur_owner_sid; - else if (!owner_sid.getfrompw (internal_getpwuid (uid))) - { - set_errno (EINVAL); - return NULL; - } - owner_sid.debug_print ("alloc_sd: owner SID ="); - - /* Get SID of new group. */ - cygsid group_sid; - /* Check for current user first */ - if (gid == myself->gid) - group_sid = cygheap->user.groups.pgsid; - else if (gid == ILLEGAL_GID) - group_sid = cur_group_sid; - else if (!group_sid.getfromgr (internal_getgrgid (gid))) - { - set_errno (EINVAL); - return NULL; - } - group_sid.debug_print ("alloc_sd: group SID ="); - - /* Initialize local security descriptor. */ - SECURITY_DESCRIPTOR sd; - RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION); - - /* We set the SE_DACL_PROTECTED flag here to prevent the DACL from being - modified by inheritable ACEs. */ - RtlSetControlSecurityDescriptor (&sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED); - - /* Create owner for local security descriptor. */ - status = RtlSetOwnerSecurityDescriptor (&sd, owner_sid, FALSE); - if (!NT_SUCCESS (status)) - { - __seterrno_from_nt_status (status); - return NULL; - } - - /* Create group for local security descriptor. */ - status = RtlSetGroupSecurityDescriptor (&sd, group_sid, FALSE); - if (!NT_SUCCESS (status)) - { - __seterrno_from_nt_status (status); - return NULL; - } - - /* Initialize local access control list. */ - PACL acl = (PACL) tp.w_get (); - RtlCreateAcl (acl, ACL_MAXIMUM_SIZE, ACL_REVISION); - - /* From here fill ACL. */ - size_t acl_len = sizeof (ACL); - int ace_off = 0; - /* Only used for sync objects (for ttys). The admins group should - always have the right to manipulate the ACL, so we have to make sure - that the ACL gives the admins group STANDARD_RIGHTS_ALL access. */ - bool saw_admins = false; - - /* Construct allow attribute for owner. - Don't set FILE_READ/WRITE_ATTRIBUTES unconditionally on Samba, otherwise - it enforces read permissions. Same for other's below. */ - DWORD owner_allow = STANDARD_RIGHTS_ALL - | (pc.fs_is_samba () - ? 0 : (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES)); - if (attribute & S_IRUSR) - owner_allow |= FILE_GENERIC_READ; - if (attribute & S_IWUSR) - owner_allow |= FILE_GENERIC_WRITE; - if (attribute & S_IXUSR) - owner_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES; - if (S_ISDIR (attribute) - && (attribute & (S_IWUSR | S_IXUSR)) == (S_IWUSR | S_IXUSR)) - owner_allow |= FILE_DELETE_CHILD; - /* For sync objects note that the owner is admin. */ - if (S_ISCHR (attribute) && owner_sid == well_known_admins_sid) - saw_admins = true; - - /* Construct allow attribute for group. */ - DWORD group_allow = STANDARD_RIGHTS_READ | SYNCHRONIZE - | (pc.fs_is_samba () ? 0 : FILE_READ_ATTRIBUTES); - if (attribute & S_IRGRP) - group_allow |= FILE_GENERIC_READ; - if (attribute & S_IWGRP) - group_allow |= FILE_GENERIC_WRITE; - if (attribute & S_IXGRP) - group_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES; - if (S_ISDIR (attribute) - && (attribute & (S_IWGRP | S_IXGRP)) == (S_IWGRP | S_IXGRP) - && !(attribute & S_ISVTX)) - group_allow |= FILE_DELETE_CHILD; - /* For sync objects, add STANDARD_RIGHTS_ALL for admins group. */ - if (S_ISCHR (attribute) && group_sid == well_known_admins_sid) - { - group_allow |= STANDARD_RIGHTS_ALL; - saw_admins = true; - } - - /* Construct allow attribute for everyone. */ - DWORD other_allow = STANDARD_RIGHTS_READ | SYNCHRONIZE - | (pc.fs_is_samba () ? 0 : FILE_READ_ATTRIBUTES); - if (attribute & S_IROTH) - other_allow |= FILE_GENERIC_READ; - if (attribute & S_IWOTH) - other_allow |= FILE_GENERIC_WRITE; - if (attribute & S_IXOTH) - other_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES; - if (S_ISDIR (attribute) - && (attribute & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH) - && !(attribute & S_ISVTX)) - other_allow |= FILE_DELETE_CHILD; - - /* Construct SUID, SGID and VTX bits in NULL ACE. */ - DWORD null_allow = 0L; - if (attribute & (S_ISUID | S_ISGID | S_ISVTX)) - { - if (attribute & S_ISUID) - null_allow |= FILE_APPEND_DATA; - if (attribute & S_ISGID) - null_allow |= FILE_WRITE_DATA; - if (attribute & S_ISVTX) - null_allow |= FILE_READ_DATA; - } - - /* Add owner and group permissions if SIDs are equal - and construct deny attributes for group and owner. */ - bool isownergroup; - if ((isownergroup = (owner_sid == group_sid))) - owner_allow |= group_allow; - - DWORD owner_deny = ~owner_allow & (group_allow | other_allow); - owner_deny &= ~(STANDARD_RIGHTS_READ - | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES); - - DWORD group_deny = ~group_allow & other_allow; - group_deny &= ~(STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES); - - /* Set deny ACE for owner. */ - if (owner_deny - && !add_access_denied_ace (acl, ace_off++, owner_deny, - owner_sid, acl_len, NO_INHERITANCE)) - return NULL; - /* Set deny ACE for group here to respect the canonical order, - if this does not impact owner */ - if (group_deny && !(group_deny & owner_allow) && !isownergroup - && !add_access_denied_ace (acl, ace_off++, group_deny, - group_sid, acl_len, NO_INHERITANCE)) - return NULL; - /* Set allow ACE for owner. */ - if (!add_access_allowed_ace (acl, ace_off++, owner_allow, - owner_sid, acl_len, NO_INHERITANCE)) - return NULL; - /* Set deny ACE for group, if still needed. */ - if (group_deny & owner_allow && !isownergroup - && !add_access_denied_ace (acl, ace_off++, group_deny, - group_sid, acl_len, NO_INHERITANCE)) - return NULL; - /* Set allow ACE for group. */ - if (!isownergroup - && !add_access_allowed_ace (acl, ace_off++, group_allow, - group_sid, acl_len, NO_INHERITANCE)) - return NULL; - - /* For sync objects, if we didn't see the admins group so far, add entry - with STANDARD_RIGHTS_ALL access. */ - if (S_ISCHR (attribute) && !saw_admins) - { - if (!add_access_allowed_ace (acl, ace_off++, STANDARD_RIGHTS_ALL, - well_known_admins_sid, acl_len, - NO_INHERITANCE)) - return NULL; - saw_admins = true; - } - - /* Set allow ACE for everyone. */ - if (!add_access_allowed_ace (acl, ace_off++, other_allow, - well_known_world_sid, acl_len, NO_INHERITANCE)) - return NULL; - /* Set null ACE for special bits. */ - if (null_allow - && !add_access_allowed_ace (acl, ace_off++, null_allow, - well_known_null_sid, acl_len, NO_INHERITANCE)) - return NULL; - - /* Fill ACL with unrelated ACEs from current security descriptor. */ - PACL oacl; - BOOLEAN acl_exists = FALSE; - ACCESS_ALLOWED_ACE *ace; - - status = RtlGetDaclSecurityDescriptor (sd_ret, &acl_exists, &oacl, &dummy); - if (NT_SUCCESS (status) && acl_exists && oacl) - for (DWORD i = 0; i < oacl->AceCount; ++i) - if (NT_SUCCESS (RtlGetAce (oacl, i, (PVOID *) &ace))) - { - cygpsid ace_sid ((PSID) &ace->SidStart); - - /* Always skip NULL SID as well as admins SID on virtual device files - in /proc/sys. */ - if (ace_sid == well_known_null_sid - || (S_ISCHR (attribute) && ace_sid == well_known_admins_sid)) - continue; - /* Check for ACEs which are always created in the preceding code - and check for the default inheritence ACEs which will be created - for just created directories. Skip them for just created - directories or if they are not inherited. If they are inherited, - make sure they are *only* inherited, so they don't collide with - the permissions set in this function. */ - if ((ace_sid == cur_owner_sid) - || (ace_sid == owner_sid) - || (ace_sid == cur_group_sid) - || (ace_sid == group_sid) - || (ace_sid == well_known_creator_owner_sid) - || (ace_sid == well_known_creator_group_sid) - || (ace_sid == well_known_world_sid)) - { - if ((S_ISDIR (attribute) && (attribute & S_JUSTCREATED)) - || (ace->Header.AceFlags - & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE)) == 0) - continue; - else - ace->Header.AceFlags |= INHERIT_ONLY_ACE; - } - if (attribute & S_JUSTCREATED) - { - /* Since files and dirs are created with a NULL descriptor, - inheritence rules kick in. If no inheritable entries exist - in the parent object, Windows will create entries from the - user token's default DACL in the file DACL. These entries - are not desired and we drop them silently. */ - if (!(ace->Header.AceFlags & INHERITED_ACE)) - continue; - /* Remove the INHERITED_ACE flag since on POSIX systems - inheritance is settled when the file has been created. - This also avoids error messages in Windows Explorer when - opening a file's security tab. Explorer complains if - inheritable ACEs are preceding non-inheritable ACEs. */ - ace->Header.AceFlags &= ~INHERITED_ACE; - } - /* - * Add unrelated ACCESS_DENIED_ACE to the beginning but - * behind the owner_deny, ACCESS_ALLOWED_ACE to the end. - * FIXME: this would break the order of the inherit-only ACEs - */ - status = RtlAddAce (acl, ACL_REVISION, - ace->Header.AceType == ACCESS_DENIED_ACE_TYPE - ? (owner_deny ? 1 : 0) : MAXDWORD, - (LPVOID) ace, ace->Header.AceSize); - if (!NT_SUCCESS (status)) - { - __seterrno_from_nt_status (status); - return NULL; - } - ace_off++; - acl_len += ace->Header.AceSize; - } - - /* Construct appropriate inherit attribute for new directories. Keep in - mind that we do this only for the sake of non-Cygwin applications. - Cygwin applications don't need this. */ - if (S_ISDIR (attribute) && (attribute & S_JUSTCREATED)) - { - const DWORD inherit = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE - | INHERIT_ONLY_ACE; -#if 0 /* FIXME: Not done currently as this breaks the canonical order */ - /* Set deny ACE for owner. */ - if (owner_deny - && !add_access_denied_ace (acl, ace_off++, owner_deny, - well_known_creator_owner_sid, acl_len, inherit)) - return NULL; - /* Set deny ACE for group here to respect the canonical order, - if this does not impact owner */ - if (group_deny && !(group_deny & owner_allow) - && !add_access_denied_ace (acl, ace_off++, group_deny, - well_known_creator_group_sid, acl_len, inherit)) - return NULL; -#endif - /* Set allow ACE for owner. */ - if (!add_access_allowed_ace (acl, ace_off++, owner_allow, - well_known_creator_owner_sid, acl_len, - inherit)) - return NULL; -#if 0 /* FIXME: Not done currently as this breaks the canonical order and - won't be preserved on chown and chmod */ - /* Set deny ACE for group, conflicting with owner_allow. */ - if (group_deny & owner_allow - && !add_access_denied_ace (acl, ace_off++, group_deny, - well_known_creator_group_sid, acl_len, inherit)) - return NULL; -#endif - /* Set allow ACE for group. */ - if (!add_access_allowed_ace (acl, ace_off++, group_allow, - well_known_creator_group_sid, acl_len, - inherit)) - return NULL; - /* Set allow ACE for everyone. */ - if (!add_access_allowed_ace (acl, ace_off++, other_allow, - well_known_world_sid, acl_len, inherit)) - return NULL; - } - - /* Set AclSize to computed value. */ - acl->AclSize = acl_len; - debug_printf ("ACL-Size: %d", 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. */ - DWORD sd_size = 0; - RtlAbsoluteToSelfRelativeSD (&sd, sd_ret, &sd_size); - if (sd_size <= 0) - { - __seterrno (); - return NULL; - } - if (!sd_ret.malloc (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; -} - -void -set_security_attribute (path_conv &pc, int attribute, PSECURITY_ATTRIBUTES psa, - security_descriptor &sd) -{ - psa->lpSecurityDescriptor = sd.malloc (SECURITY_DESCRIPTOR_MIN_LENGTH); - RtlCreateSecurityDescriptor ((PSECURITY_DESCRIPTOR) psa->lpSecurityDescriptor, - SECURITY_DESCRIPTOR_REVISION); - psa->lpSecurityDescriptor = alloc_sd (pc, geteuid32 (), getegid32 (), - attribute, sd); -} - -int -get_object_sd (HANDLE handle, security_descriptor &sd) -{ - ULONG len = 0; - NTSTATUS status; - - status = NtQuerySecurityObject (handle, ALL_SECURITY_INFORMATION, - sd, len, &len); - if (status != STATUS_BUFFER_TOO_SMALL) - { - __seterrno_from_nt_status (status); - return -1; - } - if (!sd.malloc (len)) - { - set_errno (ENOMEM); - return -1; - } - status = NtQuerySecurityObject (handle, ALL_SECURITY_INFORMATION, - sd, len, &len); - if (!NT_SUCCESS (status)) - { - __seterrno_from_nt_status (status); - return -1; - } - return 0; -} - -int -get_object_attribute (HANDLE handle, uid_t *uidret, gid_t *gidret, - mode_t *attribute) -{ - security_descriptor sd; - - if (get_object_sd (handle, sd)) - return -1; - get_info_from_sd (sd, attribute, uidret, gidret); - return 0; -} - -int -create_object_sd_from_attribute (HANDLE handle, uid_t uid, gid_t gid, - mode_t attribute, security_descriptor &sd) -{ - path_conv pc; - if ((handle && get_object_sd (handle, sd)) - || !alloc_sd (pc, uid, gid, attribute, sd)) - return -1; - return 0; -} - -int -set_object_sd (HANDLE handle, security_descriptor &sd, bool chown) -{ - NTSTATUS status; - status = NtSetSecurityObject (handle, chown ? ALL_SECURITY_INFORMATION - : DACL_SECURITY_INFORMATION, sd); - if (!NT_SUCCESS (status)) - { - __seterrno_from_nt_status (status); - return -1; - } - return 0; -} - -int -set_object_attribute (HANDLE handle, uid_t uid, gid_t gid, - mode_t attribute) -{ - security_descriptor sd; - - if (create_object_sd_from_attribute (handle, uid, gid, attribute, sd) - || set_object_sd (handle, sd, uid != ILLEGAL_UID || gid != ILLEGAL_GID)) - return -1; - return 0; -} - -int -set_file_attribute (HANDLE handle, path_conv &pc, - uid_t uid, gid_t gid, mode_t attribute) -{ - int ret = -1; - - if (pc.has_acls ()) - { - security_descriptor sd; - - if (!get_file_sd (handle, pc, sd, (bool)(attribute & S_JUSTCREATED)) - && alloc_sd (pc, uid, gid, attribute, sd)) - ret = set_file_sd (handle, pc, sd, - uid != ILLEGAL_UID || gid != ILLEGAL_GID); - } - else - ret = 0; - syscall_printf ("%d = set_file_attribute(%S, %d, %d, 0%o)", - ret, pc.get_nt_native_path (), uid, gid, attribute); - return ret; -} - -static int -check_access (security_descriptor &sd, GENERIC_MAPPING &mapping, - ACCESS_MASK desired, int flags, bool effective) -{ - int ret = -1; - NTSTATUS status, allow; - ACCESS_MASK granted; - DWORD plen = sizeof (PRIVILEGE_SET) + 3 * sizeof (LUID_AND_ATTRIBUTES); - PPRIVILEGE_SET pset = (PPRIVILEGE_SET) alloca (plen); - HANDLE tok = ((effective && cygheap->user.issetuid ()) - ? cygheap->user.imp_token () - : hProcImpToken); - - if (!tok) - { - if (!DuplicateTokenEx (hProcToken, MAXIMUM_ALLOWED, NULL, - SecurityImpersonation, TokenImpersonation, - &hProcImpToken)) - { - __seterrno (); - return ret; - } - tok = hProcImpToken; - } - - status = NtAccessCheck (sd, tok, desired, &mapping, pset, &plen, &granted, - &allow); - if (!NT_SUCCESS (status)) - __seterrno (); - else if (!NT_SUCCESS (allow)) - { - /* CV, 2006-10-16: Now, that's really weird. Imagine a user who has no - standard access to a file, but who has backup and restore privileges - and these privileges are enabled in the access token. One would - expect that the AccessCheck function takes this into consideration - when returning the access status. Otherwise, why bother with the - pset parameter, right? - But not so. AccessCheck actually returns a status of "false" here, - even though opening a file with backup resp. restore intent - naturally succeeds for this user. This definitely spoils the results - of access(2) for administrative users or the SYSTEM account. So, in - case the access check fails, another check against the user's - backup/restore privileges has to be made. Sigh. */ - int granted_flags = 0; - BOOLEAN has_priv; - - if (flags & R_OK) - { - pset->PrivilegeCount = 1; - pset->Control = 0; - pset->Privilege[0].Luid.HighPart = 0L; - pset->Privilege[0].Luid.LowPart = SE_BACKUP_PRIVILEGE; - pset->Privilege[0].Attributes = 0; - status = NtPrivilegeCheck (tok, pset, &has_priv); - if (NT_SUCCESS (status) && has_priv) - granted_flags |= R_OK; - } - if (flags & W_OK) - { - pset->PrivilegeCount = 1; - pset->Control = 0; - pset->Privilege[0].Luid.HighPart = 0L; - pset->Privilege[0].Luid.LowPart = SE_RESTORE_PRIVILEGE; - pset->Privilege[0].Attributes = 0; - status = NtPrivilegeCheck (tok, pset, &has_priv); - if (NT_SUCCESS (status) && has_priv) - granted_flags |= W_OK; - } - if (granted_flags == flags) - ret = 0; - else - set_errno (EACCES); - } - else - ret = 0; - return ret; -} - -/* Samba override. Check security descriptor for Samba UNIX user and group - accounts and check if we have an RFC 2307 mapping to a Windows account. - Create a new security descriptor with all of the UNIX accounts with - valid mapping replaced with their Windows counterpart. */ -static void -convert_samba_sd (security_descriptor &sd_ret) -{ - NTSTATUS status; - BOOLEAN dummy; - PSID sid; - cygsid owner; - cygsid group; - SECURITY_DESCRIPTOR sd; - cyg_ldap cldap; - tmp_pathbuf tp; - PACL acl, oacl; - size_t acl_len; - PACCESS_ALLOWED_ACE ace; - - if (!NT_SUCCESS (RtlGetOwnerSecurityDescriptor (sd_ret, &sid, &dummy))) - return; - owner = sid; - if (!NT_SUCCESS (RtlGetGroupSecurityDescriptor (sd_ret, &sid, &dummy))) - return; - group = sid; - - if (sid_id_auth (owner) == 22) - { - struct passwd *pwd; - uid_t uid = owner.get_uid (&cldap); - if (uid < UNIX_POSIX_OFFSET && (pwd = internal_getpwuid (uid))) - owner.getfrompw (pwd); - } - if (sid_id_auth (group) == 22) - { - struct group *grp; - gid_t gid = group.get_gid (&cldap); - if (gid < UNIX_POSIX_OFFSET && (grp = internal_getgrgid (gid))) - group.getfromgr (grp); - } - - if (!NT_SUCCESS (RtlGetDaclSecurityDescriptor (sd_ret, &dummy, - &oacl, &dummy))) - return; - acl = (PACL) tp.w_get (); - RtlCreateAcl (acl, ACL_MAXIMUM_SIZE, ACL_REVISION); - acl_len = sizeof (ACL); - - for (DWORD i = 0; i < oacl->AceCount; ++i) - if (NT_SUCCESS (RtlGetAce (oacl, i, (PVOID *) &ace))) - { - cygsid ace_sid ((PSID) &ace->SidStart); - if (sid_id_auth (ace_sid) == 22) - { - if (sid_sub_auth (ace_sid, 0) == 1) /* user */ - { - struct passwd *pwd; - uid_t uid = ace_sid.get_uid (&cldap); - if (uid < UNIX_POSIX_OFFSET && (pwd = internal_getpwuid (uid))) - ace_sid.getfrompw (pwd); - } - else /* group */ - { - struct group *grp; - gid_t gid = ace_sid.get_gid (&cldap); - if (gid < UNIX_POSIX_OFFSET && (grp = internal_getgrgid (gid))) - ace_sid.getfromgr (grp); - } - if (!add_access_allowed_ace (acl, i, ace->Mask, ace_sid, acl_len, - ace->Header.AceFlags)) - return; - } - } - acl->AclSize = acl_len; - - RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION); - RtlSetControlSecurityDescriptor (&sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED); - RtlSetOwnerSecurityDescriptor (&sd, owner, FALSE); - RtlSetGroupSecurityDescriptor (&sd, group, FALSE); - - status = RtlSetDaclSecurityDescriptor (&sd, TRUE, acl, FALSE); - if (!NT_SUCCESS (status)) - return; - DWORD sd_size = 0; - status = RtlAbsoluteToSelfRelativeSD (&sd, sd_ret, &sd_size); - if (sd_size > 0 && sd_ret.malloc (sd_size)) - RtlAbsoluteToSelfRelativeSD (&sd, sd_ret, &sd_size); -} - -int -check_file_access (path_conv &pc, int flags, bool effective) -{ - security_descriptor sd; - int ret = -1; - ACCESS_MASK desired = 0; - if (flags & R_OK) - desired |= FILE_READ_DATA; - if (flags & W_OK) - desired |= FILE_WRITE_DATA; - if (flags & X_OK) - desired |= FILE_EXECUTE; - if (!get_file_sd (pc.handle (), pc, sd, false)) - { - /* Tweak Samba security descriptor as necessary. */ - if (pc.fs_is_samba ()) - convert_samba_sd (sd); - ret = check_access (sd, file_mapping, desired, flags, effective); - } - debug_printf ("flags %y, ret %d", flags, ret); - return ret; -} - -int -check_registry_access (HANDLE hdl, int flags, bool effective) -{ - security_descriptor sd; - int ret = -1; - static GENERIC_MAPPING NO_COPY_RO reg_mapping = { KEY_READ, - KEY_WRITE, - KEY_EXECUTE, - KEY_ALL_ACCESS }; - ACCESS_MASK desired = 0; - if (flags & R_OK) - desired |= KEY_ENUMERATE_SUB_KEYS; - if (flags & W_OK) - desired |= KEY_SET_VALUE; - if (flags & X_OK) - desired |= KEY_QUERY_VALUE; - - if ((HKEY) hdl == HKEY_PERFORMANCE_DATA) - /* RegGetKeySecurity() always fails with ERROR_INVALID_HANDLE. */ - ret = 0; - else if (!get_reg_sd (hdl, sd)) - ret = check_access (sd, reg_mapping, desired, flags, effective); - - /* As long as we can't write the registry... */ - if (flags & W_OK) - { - set_errno (EROFS); - ret = -1; - } - debug_printf ("flags %y, ret %d", flags, ret); - return ret; -} |