From f97612978a32fd1015242f3e10072f40e6a510e5 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Thu, 28 Apr 2011 07:27:51 +0000 Subject: [PATCH] * autoload.cc (GetSecurityInfo): Remove. * ntdll.h (RtlConvertToAutoInheritSecurityObject): Declare. (RtlDeleteSecurityObject): Declare. (RtlGetControlSecurityDescriptor): Declare. (RtlLengthSecurityDescriptor): Declare. * security.cc (file_mapping): New global variable. (get_file_sd): Rewrite. Clean up code. Get rid of GetSecurityInfo call. (alloc_sd): Call RtlSetControlSecurityDescriptor to set SE_DACL_PROTECTED flag. (check_file_access): Remove mapping. Use file_mapping instead. (check_registry_access): Rename mapping to reg_mapping. * wincap.cc: Througout, drop use_get_sec_info_on_dirs, * wincap.h (struct wincaps): Drop use_get_sec_info_on_dirs. --- winsup/cygwin/ChangeLog | 17 +++ winsup/cygwin/autoload.cc | 1 - winsup/cygwin/ntdll.h | 14 ++- winsup/cygwin/security.cc | 246 ++++++++++++++++++++++++-------------- winsup/cygwin/wincap.cc | 8 -- winsup/cygwin/wincap.h | 2 - 6 files changed, 185 insertions(+), 103 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 836a59544..fcb6895a9 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,20 @@ +2011-04-28 Corinna Vinschen + + * autoload.cc (GetSecurityInfo): Remove. + * ntdll.h (RtlConvertToAutoInheritSecurityObject): Declare. + (RtlDeleteSecurityObject): Declare. + (RtlGetControlSecurityDescriptor): Declare. + (RtlLengthSecurityDescriptor): Declare. + * security.cc (file_mapping): New global variable. + (get_file_sd): Rewrite. Clean up code. Get rid of GetSecurityInfo + call. + (alloc_sd): Call RtlSetControlSecurityDescriptor to set + SE_DACL_PROTECTED flag. + (check_file_access): Remove mapping. Use file_mapping instead. + (check_registry_access): Rename mapping to reg_mapping. + * wincap.cc: Througout, drop use_get_sec_info_on_dirs, + * wincap.h (struct wincaps): Drop use_get_sec_info_on_dirs. + 2011-04-24 Corinna Vinschen * include/fenv.h: Add missing _FENV_H_ define. diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc index a469663c3..693566790 100644 --- a/winsup/cygwin/autoload.cc +++ b/winsup/cygwin/autoload.cc @@ -357,7 +357,6 @@ LoadDLLfunc (CryptAcquireContextW, 20, advapi32) LoadDLLfunc (CryptGenRandom, 12, advapi32) LoadDLLfunc (CryptReleaseContext, 8, advapi32) LoadDLLfunc (DeregisterEventSource, 4, advapi32) -LoadDLLfunc (GetSecurityInfo, 32, advapi32) LoadDLLfunc (LogonUserW, 24, advapi32) LoadDLLfunc (LookupAccountNameW, 28, advapi32) LoadDLLfunc (LookupAccountSidW, 28, advapi32) diff --git a/winsup/cygwin/ntdll.h b/winsup/cygwin/ntdll.h index 2937da224..8f13e1c6c 100644 --- a/winsup/cygwin/ntdll.h +++ b/winsup/cygwin/ntdll.h @@ -1,7 +1,7 @@ /* ntdll.h. Contains ntdll specific stuff not defined elsewhere. Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, - 2009, 2010 Red Hat, Inc. + 2009, 2010, 2011 Red Hat, Inc. This file is part of Cygwin. @@ -1105,12 +1105,18 @@ extern "C" LONG NTAPI RtlCompareUnicodeString (PUNICODE_STRING, PUNICODE_STRING, BOOLEAN); NTSTATUS NTAPI RtlConvertSidToUnicodeString (PUNICODE_STRING, PSID, BOOLEAN); - VOID NTAPI RtlCopyUnicodeString (PUNICODE_STRING, PUNICODE_STRING); + NTSTATUS NTAPI RtlConvertToAutoInheritSecurityObject (PSECURITY_DESCRIPTOR, + PSECURITY_DESCRIPTOR, + PSECURITY_DESCRIPTOR *, + GUID *, BOOLEAN, + PGENERIC_MAPPING); NTSTATUS NTAPI RtlCopySid (ULONG, PSID, PSID); + VOID NTAPI RtlCopyUnicodeString (PUNICODE_STRING, PUNICODE_STRING); NTSTATUS NTAPI RtlCreateAcl (PACL, ULONG, ULONG); NTSTATUS NTAPI RtlCreateRegistryKey (ULONG, PCWSTR); NTSTATUS NTAPI RtlCreateSecurityDescriptor (PSECURITY_DESCRIPTOR, ULONG); BOOLEAN NTAPI RtlCreateUnicodeStringFromAsciiz (PUNICODE_STRING, PCSTR); + NTSTATUS NTAPI RtlDeleteSecurityObject (PSECURITY_DESCRIPTOR *); NTSTATUS NTAPI RtlDowncaseUnicodeString (PUNICODE_STRING, PUNICODE_STRING, BOOLEAN); NTSTATUS NTAPI RtlEnterCriticalSection (PRTL_CRITICAL_SECTION); @@ -1124,6 +1130,9 @@ extern "C" VOID NTAPI RtlFreeUnicodeString (PUNICODE_STRING); BOOLEAN NTAPI RtlFirstFreeAce (PACL, PVOID *); NTSTATUS NTAPI RtlGetAce (PACL, ULONG, PVOID); + NTSTATUS NTAPI RtlGetControlSecurityDescriptor (PSECURITY_DESCRIPTOR, + PSECURITY_DESCRIPTOR_CONTROL, + PULONG); HANDLE NTAPI RtlGetCurrentTransaction (); NTSTATUS NTAPI RtlGetDaclSecurityDescriptor (PSECURITY_DESCRIPTOR, PBOOLEAN, PACL *, PBOOLEAN); @@ -1138,6 +1147,7 @@ extern "C" NTSTATUS NTAPI RtlIntegerToUnicodeString (ULONG, ULONG, PUNICODE_STRING); ULONG NTAPI RtlIsDosDeviceName_U (PCWSTR); NTSTATUS NTAPI RtlLeaveCriticalSection (PRTL_CRITICAL_SECTION); + ULONG NTAPI RtlLengthSecurityDescriptor (PSECURITY_DESCRIPTOR); ULONG NTAPI RtlLengthSid (PSID); ULONG NTAPI RtlNtStatusToDosError (NTSTATUS); NTSTATUS NTAPI RtlOemStringToUnicodeString (PUNICODE_STRING, POEM_STRING, diff --git a/winsup/cygwin/security.cc b/winsup/cygwin/security.cc index 2c6ef1a9c..25f6a6b72 100644 --- a/winsup/cygwin/security.cc +++ b/winsup/cygwin/security.cc @@ -31,94 +31,164 @@ details. */ | GROUP_SECURITY_INFORMATION \ | OWNER_SECURITY_INFORMATION) +static NO_COPY GENERIC_MAPPING 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) { - DWORD error = ERROR_SUCCESS; - int retry = 0; - int res = -1; + NTSTATUS status; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + ULONG len = SD_MAXIMUM_SIZE, rlen; - for (; retry < 2; ++retry) + /* Allocate space for the security descriptor. */ + if (!sd.malloc (len)) { - if (fh) + 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)) { - /* Amazing but true. If you want to know if an ACE is inherited - from the parent object, you can't use the NtQuerySecurityObject - function. In the DACL returned by this functions, the - INHERITED_ACE flag is never set. Only by calling GetSecurityInfo - you get this information. - - 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. In all other cases we - rather call NtQuerySecurityObject directly... - - ...except that there's a problem on 5.1 and 5.2 kernels. The - GetSecurityInfo call on a file sometimes returns with - ERROR_INVALID_ADDRESS if a former request for the SD of the - parent directory (or one of the parent directories?) used the - NtQuerySecurityObject call, rather than GetSecurityInfo as well. - As soon as all directory SDs are fetched using GetSecurityInfo, - the problem disappears. */ - if (justcreated - || (pc.isdir () && wincap.use_get_sec_info_on_dirs ())) - { - PSECURITY_DESCRIPTOR psd; - error = GetSecurityInfo (fh, SE_FILE_OBJECT, - ALL_SECURITY_INFORMATION, - NULL, NULL, NULL, NULL, &psd); - if (error == ERROR_SUCCESS) - { - sd = psd; - res = 0; - break; - } - } - else - { - NTSTATUS status; - ULONG len = SD_MAXIMUM_SIZE; - - if (!sd.malloc (len)) - { - set_errno (ENOMEM); - break; - } - status = NtQuerySecurityObject (fh, ALL_SECURITY_INFORMATION, - sd, len, &len); - if (NT_SUCCESS (status)) - { - res = 0; - break; - } - error = RtlNtStatusToDosError (status); - } - } - if (!retry) - { - OBJECT_ATTRIBUTES attr; - IO_STATUS_BLOCK io; - NTSTATUS status; - - status = NtOpenFile (&fh, READ_CONTROL, - pc.get_object_attr (attr, sec_none_nih), - &io, FILE_SHARE_VALID_FLAGS, - FILE_OPEN_FOR_BACKUP_INTENT); - if (!NT_SUCCESS (status)) - { - fh = NULL; - error = RtlNtStatusToDosError (status); - break; - } + debug_printf ("NtQuerySecurityObject (%S), status %p", + pc.get_nt_native_path (), status); + fh = NULL; } } - if (retry && fh) - NtClose (fh); - if (error != ERROR_SUCCESS) - __seterrno_from_win_error (error); - return res; + /* 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) + { + status = NtOpenFile (&fh, READ_CONTROL, + 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 could be 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 (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 %p", &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 %p", + &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 %p", + &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 @@ -482,7 +552,7 @@ alloc_sd (path_conv &pc, __uid32_t uid, __gid32_t gid, int attribute, /* We set the SE_DACL_PROTECTED flag here to prevent the DACL from being modified by inheritable ACEs. */ - sd.Control |= SE_DACL_PROTECTED; + RtlSetControlSecurityDescriptor (&sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED); /* Create owner for local security descriptor. */ if (!SetSecurityDescriptorOwner (&sd, owner_sid, FALSE)) @@ -974,10 +1044,6 @@ check_file_access (path_conv &pc, int flags, bool effective) { security_descriptor sd; int ret = -1; - static GENERIC_MAPPING NO_COPY mapping = { FILE_GENERIC_READ, - FILE_GENERIC_WRITE, - FILE_GENERIC_EXECUTE, - FILE_ALL_ACCESS }; DWORD desired = 0; if (flags & R_OK) desired |= FILE_READ_DATA; @@ -986,7 +1052,7 @@ check_file_access (path_conv &pc, int flags, bool effective) if (flags & X_OK) desired |= FILE_EXECUTE; if (!get_file_sd (NULL, pc, sd, false)) - ret = check_access (sd, mapping, desired, flags, effective); + ret = check_access (sd, file_mapping, desired, flags, effective); debug_printf ("flags %x, ret %d", flags, ret); return ret; } @@ -996,10 +1062,10 @@ check_registry_access (HANDLE hdl, int flags, bool effective) { security_descriptor sd; int ret = -1; - static GENERIC_MAPPING NO_COPY mapping = { KEY_READ, - KEY_WRITE, - KEY_EXECUTE, - KEY_ALL_ACCESS }; + static GENERIC_MAPPING NO_COPY reg_mapping = { KEY_READ, + KEY_WRITE, + KEY_EXECUTE, + KEY_ALL_ACCESS }; DWORD desired = 0; if (flags & R_OK) desired |= KEY_ENUMERATE_SUB_KEYS; @@ -1008,7 +1074,7 @@ check_registry_access (HANDLE hdl, int flags, bool effective) if (flags & X_OK) desired |= KEY_QUERY_VALUE; if (!get_reg_sd (hdl, sd)) - ret = check_access (sd, mapping, desired, flags, effective); + ret = check_access (sd, reg_mapping, desired, flags, effective); /* As long as we can't write the registry... */ if (flags & W_OK) { diff --git a/winsup/cygwin/wincap.cc b/winsup/cygwin/wincap.cc index af1acd520..4a7cc3e23 100644 --- a/winsup/cygwin/wincap.cc +++ b/winsup/cygwin/wincap.cc @@ -53,7 +53,6 @@ wincaps wincap_2000 __attribute__((section (".cygwin_dll_common"), shared)) = { has_fast_cwd:false, has_restricted_raw_disk_access:false, use_dont_resolve_hack:false, - use_get_sec_info_on_dirs:false, }; wincaps wincap_2000sp4 __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -86,7 +85,6 @@ wincaps wincap_2000sp4 __attribute__((section (".cygwin_dll_common"), shared)) = has_fast_cwd:false, has_restricted_raw_disk_access:false, use_dont_resolve_hack:false, - use_get_sec_info_on_dirs:false, }; wincaps wincap_xp __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -119,7 +117,6 @@ wincaps wincap_xp __attribute__((section (".cygwin_dll_common"), shared)) = { has_fast_cwd:false, has_restricted_raw_disk_access:false, use_dont_resolve_hack:true, - use_get_sec_info_on_dirs:true, }; wincaps wincap_xpsp1 __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -152,7 +149,6 @@ wincaps wincap_xpsp1 __attribute__((section (".cygwin_dll_common"), shared)) = { has_fast_cwd:false, has_restricted_raw_disk_access:false, use_dont_resolve_hack:true, - use_get_sec_info_on_dirs:true, }; wincaps wincap_xpsp2 __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -185,7 +181,6 @@ wincaps wincap_xpsp2 __attribute__((section (".cygwin_dll_common"), shared)) = { has_fast_cwd:false, has_restricted_raw_disk_access:false, use_dont_resolve_hack:true, - use_get_sec_info_on_dirs:true, }; wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -218,7 +213,6 @@ wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = { has_fast_cwd:false, has_restricted_raw_disk_access:false, use_dont_resolve_hack:true, - use_get_sec_info_on_dirs:true, }; wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -251,7 +245,6 @@ wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = { has_fast_cwd:true, has_restricted_raw_disk_access:true, use_dont_resolve_hack:false, - use_get_sec_info_on_dirs:false, }; wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = { @@ -284,7 +277,6 @@ wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = { has_fast_cwd:true, has_restricted_raw_disk_access:true, use_dont_resolve_hack:false, - use_get_sec_info_on_dirs:false, }; wincapc wincap __attribute__((section (".cygwin_dll_common"), shared)); diff --git a/winsup/cygwin/wincap.h b/winsup/cygwin/wincap.h index dc3312784..631cf30ae 100644 --- a/winsup/cygwin/wincap.h +++ b/winsup/cygwin/wincap.h @@ -43,7 +43,6 @@ struct wincaps unsigned has_fast_cwd : 1; unsigned has_restricted_raw_disk_access : 1; unsigned use_dont_resolve_hack : 1; - unsigned use_get_sec_info_on_dirs : 1; }; class wincapc @@ -92,7 +91,6 @@ public: bool IMPLEMENT (has_fast_cwd) bool IMPLEMENT (has_restricted_raw_disk_access) bool IMPLEMENT (use_dont_resolve_hack) - bool IMPLEMENT (use_get_sec_info_on_dirs) #undef IMPLEMENT };