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/base.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/base.cc')
-rw-r--r-- | winsup/cygwin/sec/base.cc | 748 |
1 files changed, 748 insertions, 0 deletions
diff --git a/winsup/cygwin/sec/base.cc b/winsup/cygwin/sec/base.cc new file mode 100644 index 000000000..dc85ca72a --- /dev/null +++ b/winsup/cygwin/sec/base.cc @@ -0,0 +1,748 @@ +/* sec/base.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 + | pc.is_known_reparse_point () + ? FILE_OPEN_REPARSE_POINT : 0); + 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 + | pc.is_known_reparse_point () + ? FILE_OPEN_REPARSE_POINT : 0); + 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, geteuid (), + getegid (), 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; +} |