| /* security.cc: NT file access control functions |
| |
| 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 <cygwin/acl.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; |
| } |
| } |
| /* We have a security descriptor now. Unfortunately, if you want to know |
| if an ACE is inherited from the parent object, this isn't sufficient. |
| |
| 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 we |
| know the DACL has been inherited. |
| |
| If none of these flags is set in the SD, the information whether |
| or not an ACE has been inherited is not available in the DACL of the |
| object. In this case GetSecurityInfo fetches the SD from the parent |
| directory and tests if the object's SD contains inherited ACEs from the |
| parent. |
| |
| Note that we're not testing the SE_DACL_AUTO_INHERITED and |
| SE_DACL_PROTECTED flags here because we know the state the file's SD |
| is in. Since we're creating all files with a NULL descriptor, the DACL |
| is either inherited from the parent, or it's the default DACL. In |
| neither case, one of these flags is set. |
| |
| For speed, we're not calling RtlConvertToAutoInheritSecurityObject |
| anymore (but keep the code here for reference). Rather we just test |
| if one of the parent's ACEs is inheritable. If so, we know we inherited |
| it and set the SE_DACL_AUTO_INHERITED flag. If not, we may assume our |
| object's DACL is the default DACL. |
| |
| 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. */ |
| if (justcreated) |
| { |
| PACL dacl; |
| BOOLEAN exists, def; |
| ACCESS_ALLOWED_ACE *ace; |
| UNICODE_STRING dirname; |
| PSECURITY_DESCRIPTOR psd; |
| tmp_pathbuf tp; |
| |
| /* 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; |
| } |
| #if 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. */ |
| PSECURITY_DESCRIPTOR nsd; |
| 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); |
| #else |
| /* ... and check the parent descriptor for inheritable ACEs matching |
| our current object type (file/dir). The simple truth in our case |
| is, either the parent dir had inheritable ACEs and all our ACEs are |
| inherited, or the parent dir didn't have inheritable ACEs and all |
| our ACEs are taken from the default DACL. */ |
| bool inherited = false; |
| BYTE search_flags = pc.isdir () ? SUB_CONTAINERS_AND_OBJECTS_INHERIT |
| : SUB_OBJECTS_ONLY_INHERIT; |
| if (NT_SUCCESS (RtlGetDaclSecurityDescriptor (psd, &exists, &dacl, &def)) |
| && exists && dacl) |
| for (ULONG idx = 0; idx < dacl->AceCount; ++idx) |
| if (NT_SUCCESS (RtlGetAce (dacl, idx, (PVOID *) &ace)) |
| && (ace->Header.AceFlags & search_flags)) |
| { |
| inherited = true; |
| break; |
| } |
| /* Then, if the parent descriptor contained inheritable ACEs, we mark |
| the SD as SE_DACL_AUTO_INHERITED. Note that this requires the |
| matching check in get_posix_access. If we ever revert to |
| RtlConvertToAutoInheritSecurityObject, the check in get_posix_access |
| has to test every single ACE for the INHERITED_ACE flag again. */ |
| if (inherited |
| && NT_SUCCESS (RtlGetDaclSecurityDescriptor (sd, &exists, &dacl, |
| &def)) |
| && exists && dacl) |
| RtlSetControlSecurityDescriptor (sd, SE_DACL_AUTO_INHERITED, |
| SE_DACL_AUTO_INHERITED); |
| #endif |
| } |
| 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 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_posix_access (sd, attribute, uidret, gidret, NULL, 0); |
| 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_posix_access (sd, attribute, uidret, gidret, NULL, 0); |
| 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, 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, 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; |
| } |
| |
| 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 = set_posix_access (attribute, geteuid32 (), |
| getegid32 (), NULL, 0, |
| sd, false); |
| } |
| |
| 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; |
| return get_posix_access (sd, attribute, uidret, gidret, NULL, 0) |
| >= 0 ? 0 : -1; |
| } |
| |
| int |
| create_object_sd_from_attribute (uid_t uid, gid_t gid, mode_t attribute, |
| security_descriptor &sd) |
| { |
| return set_posix_access (attribute, uid, gid, NULL, 0, sd, false) |
| ? 0 : -1; |
| } |
| |
| 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 (uid, gid, attribute, sd) |
| || set_object_sd (handle, sd, uid != ILLEGAL_UID || gid != ILLEGAL_GID)) |
| return -1; |
| return 0; |
| } |
| |
| int |
| set_created_file_access (HANDLE handle, path_conv &pc, mode_t attr) |
| { |
| int ret = -1; |
| security_descriptor sd, sd_ret; |
| mode_t attr_rd; |
| uid_t uid; |
| gid_t gid; |
| tmp_pathbuf tp; |
| aclent_t *aclp; |
| int nentries, idx; |
| bool std_acl; |
| |
| if (!get_file_sd (handle, pc, sd, true)) |
| { |
| attr |= S_JUSTCREATED; |
| if (pc.isdir ()) |
| attr |= S_IFDIR; |
| attr_rd = attr; |
| aclp = (aclent_t *) tp.c_get (); |
| if ((nentries = get_posix_access (sd, &attr_rd, &uid, &gid, aclp, |
| MAX_ACL_ENTRIES, &std_acl)) >= 0) |
| { |
| if (S_ISLNK (attr)) |
| { |
| /* Symlinks always get the request POSIX perms. */ |
| aclp[0].a_perm = (attr >> 6) & S_IRWXO; |
| if ((idx = searchace (aclp, nentries, GROUP_OBJ)) >= 0) |
| aclp[idx].a_perm = (attr >> 3) & S_IRWXO; |
| if ((idx = searchace (aclp, nentries, CLASS_OBJ)) >= 0) |
| aclp[idx].a_perm = (attr >> 3) & S_IRWXO; |
| if ((idx = searchace (aclp, nentries, OTHER_OBJ)) >= 0) |
| aclp[idx].a_perm = attr & S_IRWXO; |
| } |
| else |
| { |
| /* Overwrite ACL permissions as required by POSIX 1003.1e |
| draft 17. */ |
| aclp[0].a_perm &= (attr >> 6) & S_IRWXO; |
| if ((idx = searchace (aclp, nentries, CLASS_OBJ)) >= 0) |
| aclp[idx].a_perm &= (attr >> 3) & S_IRWXO; |
| if (std_acl |
| && (idx = searchace (aclp, nentries, GROUP_OBJ)) >= 0) |
| aclp[idx].a_perm &= (attr >> 3) & S_IRWXO; |
| if ((idx = searchace (aclp, nentries, OTHER_OBJ)) >= 0) |
| aclp[idx].a_perm &= attr & S_IRWXO; |
| } |
| /* Construct appropriate inherit attribute for new directories. |
| Basically we do this only for the sake of non-Cygwin applications. |
| Cygwin applications don't need these. Additionally, if the |
| S_ISGID bit is set, propagate it. */ |
| if (S_ISDIR (attr)) |
| { |
| if (searchace (aclp, nentries, DEF_USER_OBJ) < 0) |
| { |
| aclp[nentries].a_type = DEF_USER_OBJ; |
| aclp[nentries].a_id = ILLEGAL_UID; |
| aclp[nentries++].a_perm = (attr >> 6) & S_IRWXO; |
| } |
| if (searchace (aclp, nentries, DEF_GROUP_OBJ) < 0) |
| { |
| aclp[nentries].a_type = DEF_GROUP_OBJ; |
| aclp[nentries].a_id = ILLEGAL_GID; |
| aclp[nentries++].a_perm = (attr >> 3) & S_IRWXO; |
| } |
| if (searchace (aclp, nentries, DEF_OTHER_OBJ) < 0) |
| { |
| aclp[nentries].a_type = DEF_OTHER_OBJ; |
| aclp[nentries].a_id = ILLEGAL_UID; |
| aclp[nentries++].a_perm = attr & S_IRWXO; |
| } |
| if (attr_rd & S_ISGID) |
| attr |= S_ISGID; |
| } |
| if (set_posix_access (attr, uid, gid, aclp, nentries, sd_ret, |
| pc.fs_is_samba ())) |
| ret = set_file_sd (handle, pc, sd_ret, attr_rd & S_ISGID); |
| } |
| } |
| 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 if (sid_sub_auth (ace_sid, 0) == 2) /* 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, 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; |
| } |