https://git.reactos.org/?p=reactos.git;a=commitdiff;h=fd2e4437a8c9cc1aba7d52...
commit fd2e4437a8c9cc1aba7d526d2a6103ce4fe2520d Author: George Bișoc george.bisoc@reactos.org AuthorDate: Wed Jun 21 17:57:23 2023 +0200 Commit: unknown george.bisoc@reactos.org CommitDate: Tue Aug 22 17:54:19 2023 +0200
[NTDLL_APITEST] Write tests for NtAccessCheckByType --- modules/rostests/apitests/ntdll/CMakeLists.txt | 1 + .../rostests/apitests/ntdll/NtAccessCheckByType.c | 1383 ++++++++++++++++++++ modules/rostests/apitests/ntdll/testlist.c | 2 + 3 files changed, 1386 insertions(+)
diff --git a/modules/rostests/apitests/ntdll/CMakeLists.txt b/modules/rostests/apitests/ntdll/CMakeLists.txt index 380b76ab310..4cad9ae218e 100644 --- a/modules/rostests/apitests/ntdll/CMakeLists.txt +++ b/modules/rostests/apitests/ntdll/CMakeLists.txt @@ -10,6 +10,7 @@ list(APPEND SOURCE load_notifications.c locale.c NtAcceptConnectPort.c + NtAccessCheckByType.c NtAdjustGroupsToken.c NtAdjustPrivilegesToken.c NtAllocateVirtualMemory.c diff --git a/modules/rostests/apitests/ntdll/NtAccessCheckByType.c b/modules/rostests/apitests/ntdll/NtAccessCheckByType.c new file mode 100644 index 00000000000..bbd072dd9f4 --- /dev/null +++ b/modules/rostests/apitests/ntdll/NtAccessCheckByType.c @@ -0,0 +1,1383 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Tests for the NtAccessCheckByType API + * COPYRIGHT: Copyright 2023 George Bișoc george.bisoc@reactos.org + */ + +#include "precomp.h" + +static GENERIC_MAPPING RegMapping = {KEY_READ, KEY_WRITE, KEY_EXECUTE, KEY_ALL_ACCESS}; +static GUID ObjectType = {0x12345678, 0x1234, 0x5678, {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}}; +static GUID ChildObjectType = {0x23456789, 0x2345, 0x6786, {0x2, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99}}; +static SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY}; +static SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY}; + +static +HANDLE +GetTokenProcess( + _In_ BOOLEAN WantImpersonateLevel, + _In_ BOOLEAN WantImpersonateType) +{ + NTSTATUS Status; + HANDLE Token; + HANDLE DuplicatedToken; + OBJECT_ATTRIBUTES ObjectAttributes; + SECURITY_QUALITY_OF_SERVICE Sqos; + + Status = NtOpenProcessToken(NtCurrentProcess(), + TOKEN_QUERY | TOKEN_DUPLICATE, + &Token); + if (!NT_SUCCESS(Status)) + { + trace("Failed to get current process token (Status 0x%08lx)\n", Status); + return NULL; + } + + Sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); + Sqos.ImpersonationLevel = WantImpersonateLevel ? SecurityImpersonation : SecurityAnonymous; + Sqos.ContextTrackingMode = 0; + Sqos.EffectiveOnly = FALSE; + + InitializeObjectAttributes(&ObjectAttributes, + NULL, + 0, + NULL, + NULL); + ObjectAttributes.SecurityQualityOfService = &Sqos; + + Status = NtDuplicateToken(Token, + TOKEN_QUERY | TOKEN_DUPLICATE, + &ObjectAttributes, + FALSE, + WantImpersonateType ? TokenImpersonation : TokenPrimary, + &DuplicatedToken); + if (!NT_SUCCESS(Status)) + { + trace("Failed to duplicate token (Status 0x%08lx)\n", Status); + NtClose(Token); + return NULL; + } + + return DuplicatedToken; +} + +static +VOID +ParamsValidationTests(VOID) +{ + NTSTATUS Status; + NTSTATUS AccessStatus; + ACCESS_MASK GrantedAccess; + PPRIVILEGE_SET PrivilegeSet = NULL; + ULONG PrivilegeSetLength; + HANDLE Token = NULL; + SECURITY_DESCRIPTOR Sd; + OBJECT_TYPE_LIST ObjTypeList[2]; + + /* Everything is NULL */ + Status = NtAccessCheckByType(NULL, + NULL, + NULL, + 0, + NULL, + 0, + NULL, + NULL, + 0, + NULL, + NULL); + ok_hex(Status, STATUS_ACCESS_VIOLATION); + + /* Allocate a buffer for privileges set */ + PrivilegeSetLength = FIELD_OFFSET(PRIVILEGE_SET, Privilege[16]); + PrivilegeSet = RtlAllocateHeap(RtlGetProcessHeap(), 0, PrivilegeSetLength); + if (PrivilegeSet == NULL) + { + skip("Failed to allocate PrivilegeSet, skipping tests\n"); + goto Quit; + } + + /* No token given */ + Status = NtAccessCheckByType(NULL, + NULL, + NULL, + MAXIMUM_ALLOWED, + NULL, + 0, + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_INVALID_HANDLE); + + /* Get a token now */ + Token = GetTokenProcess(FALSE, FALSE); + if (Token == NULL) + { + skip("Failed to get token, skipping tests\n"); + goto Quit; + } + + /* Token given but it's not an impersonation one */ + Status = NtAccessCheckByType(NULL, + NULL, + Token, + MAXIMUM_ALLOWED, + NULL, + 0, + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_NO_IMPERSONATION_TOKEN); + + /* Get a token but this time with an invalid impersonation level */ + NtClose(Token); + Token = GetTokenProcess(FALSE, TRUE); + if (Token == NULL) + { + skip("Failed to get token, skipping tests\n"); + goto Quit; + } + + /* Token given but it doesn't have the right impersonation level */ + Status = NtAccessCheckByType(NULL, + NULL, + Token, + MAXIMUM_ALLOWED, + NULL, + 0, + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_BAD_IMPERSONATION_LEVEL); + + /* A generic access right is given */ + Status = NtAccessCheckByType(NULL, + NULL, + Token, + GENERIC_WRITE, + NULL, + 0, + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_GENERIC_NOT_MAPPED); + + /* Close the token, get a valid one that we want */ + NtClose(Token); + Token = GetTokenProcess(TRUE, TRUE); + if (Token == NULL) + { + skip("Failed to get token, skipping tests\n"); + goto Quit; + } + + /* No security descriptor is given */ + Status = NtAccessCheckByType(NULL, + NULL, + Token, + MAXIMUM_ALLOWED, + NULL, + 0, + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_INVALID_SECURITY_DESCR); + + /* The function expects a privilege set */ + Status = NtAccessCheckByType(NULL, + NULL, + Token, + MAXIMUM_ALLOWED, + NULL, + 0, + &RegMapping, + NULL, + 0, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_ACCESS_VIOLATION); + + /* Create a security descriptor */ + Status = RtlCreateSecurityDescriptor(&Sd, SECURITY_DESCRIPTOR_REVISION); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create a security descriptor, skipping tests\n"); + goto Quit; + } + + /* This descriptor will have no group, owner and DACL */ + RtlSetGroupSecurityDescriptor(&Sd, NULL, FALSE); + RtlSetOwnerSecurityDescriptor(&Sd, NULL, FALSE); + RtlSetDaclSecurityDescriptor(&Sd, FALSE, NULL, FALSE); + + /* Give the invalid descriptor */ + Status = NtAccessCheckByType(&Sd, + NULL, + Token, + MAXIMUM_ALLOWED, + NULL, + 0, + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_INVALID_SECURITY_DESCR); + + /* Give a bogus principal SID */ + Status = NtAccessCheckByType(&Sd, + (PSID)1, + Token, + MAXIMUM_ALLOWED, + NULL, + 0, + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + + /* + * On newer versions of Windows such as 10 the principal SID is validated + * after the security descriptor so it will always trigger an invalid + * descriptor case. On Windows Server 2003 the principal SID is captured + * before the security descriptor. ReactOS currently follows the Windows + * 10's behavior. + */ + ok(Status == STATUS_ACCESS_VIOLATION || Status == STATUS_INVALID_SECURITY_DESCR, + "STATUS_ACCESS_VIOLATION or STATUS_INVALID_SECURITY_DESCR expected, got 0x%lx\n", Status); + + /* The first object element is not the root */ + ObjTypeList[0].Level = ACCESS_PROPERTY_SET_GUID; + ObjTypeList[0].Sbz = 0; + ObjTypeList[0].ObjectType = &ObjectType; + + /* Give a bogus list */ + Status = NtAccessCheckByType(&Sd, + (PSID)1, + Token, + MAXIMUM_ALLOWED, + ObjTypeList, + RTL_NUMBER_OF(ObjTypeList), + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_INVALID_PARAMETER); + + /* This list has two roots */ + ObjTypeList[0].Level = ACCESS_OBJECT_GUID; + ObjTypeList[0].Sbz = 0; + ObjTypeList[0].ObjectType = &ObjectType; + + ObjTypeList[1].Level = ACCESS_OBJECT_GUID; + ObjTypeList[1].Sbz = 0; + ObjTypeList[1].ObjectType = &ChildObjectType; + + /* Give a bogus list */ + Status = NtAccessCheckByType(&Sd, + (PSID)1, + Token, + MAXIMUM_ALLOWED, + ObjTypeList, + RTL_NUMBER_OF(ObjTypeList), + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_INVALID_PARAMETER); + + /* This list has an object whose level is invalid */ + ObjTypeList[0].Level = 0xa; + ObjTypeList[0].Sbz = 0; + ObjTypeList[0].ObjectType = &ObjectType; + + /* Give a bogus list */ + Status = NtAccessCheckByType(&Sd, + (PSID)1, + Token, + MAXIMUM_ALLOWED, + ObjTypeList, + RTL_NUMBER_OF(ObjTypeList), + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_INVALID_PARAMETER); + + /* This list doesn't have consistent object levels */ + ObjTypeList[0].Level = ACCESS_OBJECT_GUID; + ObjTypeList[0].Sbz = 0; + ObjTypeList[0].ObjectType = &ObjectType; + + ObjTypeList[1].Level = ACCESS_PROPERTY_GUID; + ObjTypeList[1].Sbz = 0; + ObjTypeList[1].ObjectType = &ChildObjectType; + + /* Give a bogus list */ + Status = NtAccessCheckByType(&Sd, + (PSID)1, + Token, + MAXIMUM_ALLOWED, + ObjTypeList, + RTL_NUMBER_OF(ObjTypeList), + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_INVALID_PARAMETER); + +Quit: + if (Token) + { + NtClose(Token); + } + + if (PrivilegeSet) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, PrivilegeSet); + } +} + +static +VOID +AccessGrantedNoDaclTests(VOID) +{ + NTSTATUS Status; + NTSTATUS AccessStatus; + ACCESS_MASK GrantedAccess; + PPRIVILEGE_SET PrivilegeSet = NULL; + ULONG PrivilegeSetLength; + HANDLE Token = NULL; + SECURITY_DESCRIPTOR Sd; + OBJECT_TYPE_LIST ObjTypeList[2]; + PSID AdminSid = NULL, UsersSid = NULL; + + /* Allocate all the stuff we need */ + PrivilegeSetLength = FIELD_OFFSET(PRIVILEGE_SET, Privilege[16]); + PrivilegeSet = RtlAllocateHeap(RtlGetProcessHeap(), 0, PrivilegeSetLength); + if (PrivilegeSet == NULL) + { + skip("Failed to allocate PrivilegeSet, skipping tests\n"); + return; + } + + Status = RtlAllocateAndInitializeSid(&NtAuthority, + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, + 0, + 0, + 0, + 0, + 0, + &AdminSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create Admins SID, skipping tests\n"); + goto Quit; + } + + Status = RtlAllocateAndInitializeSid(&NtAuthority, + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_USERS, + 0, + 0, + 0, + 0, + 0, + 0, + &UsersSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create User SID, skipping tests\n"); + goto Quit; + } + + Token = GetTokenProcess(TRUE, TRUE); + if (Token == NULL) + { + skip("Failed to get token, skipping tests\n"); + goto Quit; + } + + Status = RtlCreateSecurityDescriptor(&Sd, SECURITY_DESCRIPTOR_REVISION); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create a security descriptor, skipping tests\n"); + goto Quit; + } + + /* Setup the descriptor with no DACL */ + RtlSetGroupSecurityDescriptor(&Sd, UsersSid, FALSE); + RtlSetOwnerSecurityDescriptor(&Sd, AdminSid, FALSE); + RtlSetDaclSecurityDescriptor(&Sd, FALSE, NULL, FALSE); + + /* Setup the object type list */ + ObjTypeList[0].Level = ACCESS_OBJECT_GUID; + ObjTypeList[0].Sbz = 0; + ObjTypeList[0].ObjectType = &ObjectType; + + ObjTypeList[1].Level = ACCESS_PROPERTY_SET_GUID; + ObjTypeList[1].Sbz = 0; + ObjTypeList[1].ObjectType = &ChildObjectType; + + /* + * Evaluate the access -- a SD with no DACL is always a granted + * access as no ACL is enforced to protect the object. + */ + Status = NtAccessCheckByType(&Sd, + NULL, + Token, + KEY_READ, + ObjTypeList, + RTL_NUMBER_OF(ObjTypeList), + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_SUCCESS); + ok(GrantedAccess == KEY_READ, "Expected KEY_READ as granted right but got 0x%08lx\n", GrantedAccess); + ok(AccessStatus == STATUS_SUCCESS, "Expected a success status but got 0x%08lx\n", AccessStatus); + ok(PrivilegeSet != NULL, "PrivilegeSet is NULL when it mustn't be!\n"); + ok(PrivilegeSetLength != 0, "PrivilegeSetLength mustn't be 0!\n"); + + /* Evaluate access for MAXIMUM_ALLOWED as well */ + Status = NtAccessCheckByType(&Sd, + NULL, + Token, + MAXIMUM_ALLOWED, + ObjTypeList, + RTL_NUMBER_OF(ObjTypeList), + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_SUCCESS); + ok(GrantedAccess == KEY_ALL_ACCESS, "Expected KEY_ALL_ACCESS as granted right but got 0x%08lx\n", GrantedAccess); + ok(AccessStatus == STATUS_SUCCESS, "Expected a success status but got 0x%08lx\n", AccessStatus); + ok(PrivilegeSet != NULL, "PrivilegeSet is NULL when it mustn't be!\n"); + ok(PrivilegeSetLength != 0, "PrivilegeSetLength mustn't be 0!\n"); + + /* + * Evaluate access but with no object type list. + * NtAccessCheckByType will be treated as a normal NtAccessCheck. + */ + Status = NtAccessCheckByType(&Sd, + NULL, + Token, + KEY_READ, + NULL, + 0, + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_SUCCESS); + ok(GrantedAccess == KEY_READ, "Expected KEY_READ as granted right but got 0x%08lx\n", GrantedAccess); + ok(AccessStatus == STATUS_SUCCESS, "Expected a success status but got 0x%08lx\n", AccessStatus); + ok(PrivilegeSet != NULL, "PrivilegeSet is NULL when it mustn't be!\n"); + ok(PrivilegeSetLength != 0, "PrivilegeSetLength mustn't be 0!\n"); + +Quit: + if (Token) + { + NtClose(Token); + } + + if (UsersSid) + { + RtlFreeSid(UsersSid); + } + + if (AdminSid) + { + RtlFreeSid(AdminSid); + } + + if (PrivilegeSet) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, PrivilegeSet); + } +} + +static +VOID +AccessGrantedTests(VOID) +{ + NTSTATUS Status; + NTSTATUS AccessStatus; + ACCESS_MASK GrantedAccess; + PPRIVILEGE_SET PrivilegeSet = NULL; + ULONG PrivilegeSetLength; + HANDLE Token = NULL; + PACL Dacl = NULL; + ULONG DaclSize; + SECURITY_DESCRIPTOR Sd; + OBJECT_TYPE_LIST ObjTypeList[2]; + PSID EveryoneSid = NULL, AdminSid = NULL, UsersSid = NULL; + + /* Allocate all the stuff we need */ + PrivilegeSetLength = FIELD_OFFSET(PRIVILEGE_SET, Privilege[16]); + PrivilegeSet = RtlAllocateHeap(RtlGetProcessHeap(), 0, PrivilegeSetLength); + if (PrivilegeSet == NULL) + { + skip("Failed to allocate PrivilegeSet, skipping tests\n"); + return; + } + + Status = RtlAllocateAndInitializeSid(&WorldAuthority, + 1, + SECURITY_WORLD_RID, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + &EveryoneSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create Everyone SID, skipping tests\n"); + goto Quit; + } + + Status = RtlAllocateAndInitializeSid(&NtAuthority, + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, + 0, + 0, + 0, + 0, + 0, + &AdminSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create Admins SID, skipping tests\n"); + goto Quit; + } + + Status = RtlAllocateAndInitializeSid(&NtAuthority, + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_USERS, + 0, + 0, + 0, + 0, + 0, + 0, + &UsersSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create User SID, skipping tests\n"); + goto Quit; + } + + Token = GetTokenProcess(TRUE, TRUE); + if (Token == NULL) + { + skip("Failed to get token, skipping tests\n"); + goto Quit; + } + + Status = RtlCreateSecurityDescriptor(&Sd, SECURITY_DESCRIPTOR_REVISION); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create a security descriptor, skipping tests\n"); + goto Quit; + } + + DaclSize = sizeof(ACL) + + sizeof(ACCESS_ALLOWED_OBJECT_ACE) + RtlLengthSid(EveryoneSid) + + sizeof(ACCESS_ALLOWED_OBJECT_ACE) + RtlLengthSid(AdminSid); + Dacl = RtlAllocateHeap(RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + DaclSize); + if (Dacl == NULL) + { + skip("Failed to allocate memory for DACL, skipping tests\n"); + goto Quit; + } + + /* + * Create an ordinary ACL with an old revision. Object types are supported + * starting with Revision 4, so adding ACEs to it will fail. + */ + Status = RtlCreateAcl(Dacl, + DaclSize, + ACL_REVISION); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create DACL, skipping tests\n"); + goto Quit; + } + + Status = RtlAddAccessAllowedObjectAce(Dacl, + ACL_REVISION, + 0, + KEY_READ, + &ObjectType, + NULL, + EveryoneSid); + ok_hex(Status, STATUS_REVISION_MISMATCH); + + /* Free the ACL and create a proper one with correct revision */ + RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl); + Dacl = RtlAllocateHeap(RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + DaclSize); + if (Dacl == NULL) + { + skip("Failed to allocate memory for DACL, skipping tests\n"); + goto Quit; + } + + Status = RtlCreateAcl(Dacl, + DaclSize, + ACL_REVISION4); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create DACL, skipping tests\n"); + goto Quit; + } + + /* Setup ACEs -- READ for everyone and READ/WRITE for admins */ + Status = RtlAddAccessAllowedObjectAce(Dacl, + ACL_REVISION4, + 0, + KEY_READ, + &ObjectType, + NULL, + EveryoneSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to add allowed object ACE for Everyone SID, skipping tests\n"); + goto Quit; + } + + Status = RtlAddAccessAllowedObjectAce(Dacl, + ACL_REVISION4, + 0, + KEY_READ | KEY_WRITE, + &ObjectType, + NULL, + AdminSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to add allowed object ACE for Admins SID, skipping tests\n"); + goto Quit; + } + + /* Setup the descriptor */ + RtlSetGroupSecurityDescriptor(&Sd, UsersSid, FALSE); + RtlSetOwnerSecurityDescriptor(&Sd, AdminSid, FALSE); + RtlSetDaclSecurityDescriptor(&Sd, TRUE, Dacl, FALSE); + + /* Setup the object type list */ + ObjTypeList[0].Level = ACCESS_OBJECT_GUID; + ObjTypeList[0].Sbz = 0; + ObjTypeList[0].ObjectType = &ObjectType; + + ObjTypeList[1].Level = ACCESS_PROPERTY_SET_GUID; + ObjTypeList[1].Sbz = 0; + ObjTypeList[1].ObjectType = &ChildObjectType; + + /* Evaluate access -- KEY_READ has to be granted */ + Status = NtAccessCheckByType(&Sd, + NULL, + Token, + KEY_READ, + ObjTypeList, + RTL_NUMBER_OF(ObjTypeList), + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_SUCCESS); + ok(GrantedAccess == KEY_READ, "Expected KEY_READ as granted right but got 0x%08lx\n", GrantedAccess); + ok(AccessStatus == STATUS_SUCCESS, "Expected a success status but got 0x%08lx\n", AccessStatus); + ok(PrivilegeSet != NULL, "PrivilegeSet is NULL when it mustn't be!\n"); + ok(PrivilegeSetLength != 0, "PrivilegeSetLength mustn't be 0!\n"); + + /* Admins should be granted WRITE access */ + Status = NtAccessCheckByType(&Sd, + NULL, + Token, + KEY_WRITE, + ObjTypeList, + RTL_NUMBER_OF(ObjTypeList), + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_SUCCESS); + ok(GrantedAccess == KEY_WRITE, "Expected KEY_WRITE as granted right but got 0x%08lx\n", GrantedAccess); + ok(AccessStatus == STATUS_SUCCESS, "Expected a success status but got 0x%08lx\n", AccessStatus); + ok(PrivilegeSet != NULL, "PrivilegeSet is NULL when it mustn't be!\n"); + ok(PrivilegeSetLength != 0, "PrivilegeSetLength mustn't be 0!\n"); + +Quit: + if (Dacl) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl); + } + + if (Token) + { + NtClose(Token); + } + + if (UsersSid) + { + RtlFreeSid(UsersSid); + } + + if (AdminSid) + { + RtlFreeSid(AdminSid); + } + + if (EveryoneSid) + { + RtlFreeSid(EveryoneSid); + } + + if (PrivilegeSet) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, PrivilegeSet); + } +} + +static +VOID +AccessGrantedMultipleObjectsTests(VOID) +{ + NTSTATUS Status; + NTSTATUS AccessStatus; + ACCESS_MASK GrantedAccess; + PPRIVILEGE_SET PrivilegeSet = NULL; + ULONG PrivilegeSetLength; + HANDLE Token = NULL; + PACL Dacl = NULL; + ULONG DaclSize; + SECURITY_DESCRIPTOR Sd; + OBJECT_TYPE_LIST ObjTypeList[6]; + PSID EveryoneSid = NULL, AdminSid = NULL, UsersSid = NULL; + GUID ChildObjectType2 = {0x34578901, 0x3456, 0x7896, {0x3, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00}}; + GUID ChildObjectType3 = {0x45678901, 0x4567, 0x1122, {0x4, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x01}}; + GUID ChildObjectType4 = {0x56788901, 0x1111, 0x2222, {0x5, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x02}}; + GUID ChildObjectType5 = {0x67901234, 0x2222, 0x3333, {0x4, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x03}}; + + /* Allocate all the stuff we need */ + PrivilegeSetLength = FIELD_OFFSET(PRIVILEGE_SET, Privilege[16]); + PrivilegeSet = RtlAllocateHeap(RtlGetProcessHeap(), 0, PrivilegeSetLength); + if (PrivilegeSet == NULL) + { + skip("Failed to allocate PrivilegeSet, skipping tests\n"); + return; + } + + Status = RtlAllocateAndInitializeSid(&WorldAuthority, + 1, + SECURITY_WORLD_RID, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + &EveryoneSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create Everyone SID, skipping tests\n"); + goto Quit; + } + + Status = RtlAllocateAndInitializeSid(&NtAuthority, + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, + 0, + 0, + 0, + 0, + 0, + &AdminSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create Admins SID, skipping tests\n"); + goto Quit; + } + + Status = RtlAllocateAndInitializeSid(&NtAuthority, + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_USERS, + 0, + 0, + 0, + 0, + 0, + 0, + &UsersSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create User SID, skipping tests\n"); + goto Quit; + } + + Token = GetTokenProcess(TRUE, TRUE); + if (Token == NULL) + { + skip("Failed to get token, skipping tests\n"); + goto Quit; + } + + Status = RtlCreateSecurityDescriptor(&Sd, SECURITY_DESCRIPTOR_REVISION); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create a security descriptor, skipping tests\n"); + goto Quit; + } + + DaclSize = sizeof(ACL) + + sizeof(ACCESS_ALLOWED_OBJECT_ACE) + RtlLengthSid(AdminSid) + + sizeof(ACCESS_ALLOWED_OBJECT_ACE) + RtlLengthSid(EveryoneSid) + + sizeof(ACCESS_ALLOWED_OBJECT_ACE) + RtlLengthSid(EveryoneSid) + + sizeof(ACCESS_ALLOWED_OBJECT_ACE) + RtlLengthSid(EveryoneSid) + + sizeof(ACCESS_ALLOWED_OBJECT_ACE) + RtlLengthSid(EveryoneSid) + + sizeof(ACCESS_ALLOWED_OBJECT_ACE) + RtlLengthSid(EveryoneSid); + Dacl = RtlAllocateHeap(RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + DaclSize); + if (Dacl == NULL) + { + skip("Failed to allocate memory for DACL, skipping tests\n"); + goto Quit; + } + + Status = RtlCreateAcl(Dacl, + DaclSize, + ACL_REVISION4); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create DACL, skipping tests\n"); + goto Quit; + } + + /* Setup some rights for these objects, admins are granted everything */ + Status = RtlAddAccessAllowedObjectAce(Dacl, + ACL_REVISION4, + 0, + KEY_ALL_ACCESS, + &ObjectType, + NULL, + AdminSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to add allowed object ACE for Admins SID, skipping tests\n"); + goto Quit; + } + + Status = RtlAddAccessAllowedObjectAce(Dacl, + ACL_REVISION4, + 0, + KEY_READ, + &ChildObjectType, + NULL, + EveryoneSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to add allowed object ACE for Everyone SID, skipping tests\n"); + goto Quit; + } + + Status = RtlAddAccessAllowedObjectAce(Dacl, + ACL_REVISION4, + 0, + KEY_WRITE, + &ChildObjectType2, + NULL, + EveryoneSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to add allowed object ACE for Everyone SID, skipping tests\n"); + goto Quit; + } + + Status = RtlAddAccessAllowedObjectAce(Dacl, + ACL_REVISION4, + 0, + KEY_EXECUTE, + &ChildObjectType3, + NULL, + EveryoneSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to add allowed object ACE for Everyone SID, skipping tests\n"); + goto Quit; + } + + Status = RtlAddAccessAllowedObjectAce(Dacl, + ACL_REVISION4, + 0, + KEY_SET_VALUE, + &ChildObjectType4, + NULL, + EveryoneSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to add allowed object ACE for Everyone SID, skipping tests\n"); + goto Quit; + } + + Status = RtlAddAccessAllowedObjectAce(Dacl, + ACL_REVISION4, + 0, + KEY_QUERY_VALUE, + &ChildObjectType5, + NULL, + EveryoneSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to add allowed object ACE for Everyone SID, skipping tests\n"); + goto Quit; + } + + /* Setup the descriptor */ + RtlSetGroupSecurityDescriptor(&Sd, UsersSid, FALSE); + RtlSetOwnerSecurityDescriptor(&Sd, AdminSid, FALSE); + RtlSetDaclSecurityDescriptor(&Sd, TRUE, Dacl, FALSE); + + /* Setup the object type list */ + ObjTypeList[0].Level = ACCESS_OBJECT_GUID; + ObjTypeList[0].Sbz = 0; + ObjTypeList[0].ObjectType = &ObjectType; + + ObjTypeList[1].Level = ACCESS_PROPERTY_SET_GUID; + ObjTypeList[1].Sbz = 0; + ObjTypeList[1].ObjectType = &ChildObjectType; + + ObjTypeList[2].Level = ACCESS_PROPERTY_GUID; + ObjTypeList[2].Sbz = 0; + ObjTypeList[2].ObjectType = &ChildObjectType2; + + ObjTypeList[3].Level = ACCESS_PROPERTY_GUID; + ObjTypeList[3].Sbz = 0; + ObjTypeList[3].ObjectType = &ChildObjectType3; + + ObjTypeList[4].Level = ACCESS_PROPERTY_SET_GUID; + ObjTypeList[4].Sbz = 0; + ObjTypeList[4].ObjectType = &ChildObjectType4; + + ObjTypeList[5].Level = ACCESS_PROPERTY_GUID; + ObjTypeList[5].Sbz = 0; + ObjTypeList[5].ObjectType = &ChildObjectType5; + + /* Evaluate access for each object case */ + Status = NtAccessCheckByType(&Sd, + NULL, + Token, + MAXIMUM_ALLOWED, + ObjTypeList, + RTL_NUMBER_OF(ObjTypeList), + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_SUCCESS); + ok(GrantedAccess == KEY_ALL_ACCESS, "Expected KEY_ALL_ACCESS as granted right but got 0x%08lx\n", GrantedAccess); + ok(AccessStatus == STATUS_SUCCESS, "Expected a success status but got 0x%08lx\n", AccessStatus); + + Status = NtAccessCheckByType(&Sd, + NULL, + Token, + KEY_READ, + ObjTypeList, + RTL_NUMBER_OF(ObjTypeList), + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_SUCCESS); + ok(GrantedAccess == KEY_READ, "Expected KEY_READ as granted right but got 0x%08lx\n", GrantedAccess); + ok(AccessStatus == STATUS_SUCCESS, "Expected a success status but got 0x%08lx\n", AccessStatus); + + Status = NtAccessCheckByType(&Sd, + NULL, + Token, + KEY_WRITE, + ObjTypeList, + RTL_NUMBER_OF(ObjTypeList), + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_SUCCESS); + ok(GrantedAccess == KEY_WRITE, "Expected KEY_WRITE as granted right but got 0x%08lx\n", GrantedAccess); + ok(AccessStatus == STATUS_SUCCESS, "Expected a success status but got 0x%08lx\n", AccessStatus); + + Status = NtAccessCheckByType(&Sd, + NULL, + Token, + KEY_EXECUTE, + ObjTypeList, + RTL_NUMBER_OF(ObjTypeList), + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_SUCCESS); + ok(GrantedAccess == KEY_EXECUTE, "Expected KEY_EXECUTE as granted right but got 0x%08lx\n", GrantedAccess); + ok(AccessStatus == STATUS_SUCCESS, "Expected a success status but got 0x%08lx\n", AccessStatus); + + Status = NtAccessCheckByType(&Sd, + NULL, + Token, + KEY_SET_VALUE, + ObjTypeList, + RTL_NUMBER_OF(ObjTypeList), + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_SUCCESS); + ok(GrantedAccess == KEY_SET_VALUE, "Expected KEY_SET_VALUE as granted right but got 0x%08lx\n", GrantedAccess); + ok(AccessStatus == STATUS_SUCCESS, "Expected a success status but got 0x%08lx\n", AccessStatus); + + Status = NtAccessCheckByType(&Sd, + NULL, + Token, + KEY_QUERY_VALUE, + ObjTypeList, + RTL_NUMBER_OF(ObjTypeList), + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_SUCCESS); + ok(GrantedAccess == KEY_QUERY_VALUE, "Expected KEY_QUERY_VALUE as granted right but got 0x%08lx\n", GrantedAccess); + ok(AccessStatus == STATUS_SUCCESS, "Expected a success status but got 0x%08lx\n", AccessStatus); + +Quit: + if (Dacl) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl); + } + + if (Token) + { + NtClose(Token); + } + + if (UsersSid) + { + RtlFreeSid(UsersSid); + } + + if (AdminSid) + { + RtlFreeSid(AdminSid); + } + + if (EveryoneSid) + { + RtlFreeSid(EveryoneSid); + } + + if (PrivilegeSet) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, PrivilegeSet); + } +} + +static +VOID +DenyAccessTests(VOID) +{ + NTSTATUS Status; + NTSTATUS AccessStatus; + ACCESS_MASK GrantedAccess; + PPRIVILEGE_SET PrivilegeSet = NULL; + ULONG PrivilegeSetLength; + HANDLE Token = NULL; + PACL Dacl = NULL; + ULONG DaclSize; + SECURITY_DESCRIPTOR Sd; + OBJECT_TYPE_LIST ObjTypeList[2]; + PSID EveryoneSid = NULL, AdminSid = NULL, UsersSid = NULL; + + /* Allocate all the stuff we need */ + PrivilegeSetLength = FIELD_OFFSET(PRIVILEGE_SET, Privilege[16]); + PrivilegeSet = RtlAllocateHeap(RtlGetProcessHeap(), 0, PrivilegeSetLength); + if (PrivilegeSet == NULL) + { + skip("Failed to allocate PrivilegeSet, skipping tests\n"); + return; + } + + Status = RtlAllocateAndInitializeSid(&WorldAuthority, + 1, + SECURITY_WORLD_RID, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + &EveryoneSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create Everyone SID, skipping tests\n"); + goto Quit; + } + + Status = RtlAllocateAndInitializeSid(&NtAuthority, + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, + 0, + 0, + 0, + 0, + 0, + &AdminSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create Admins SID, skipping tests\n"); + goto Quit; + } + + Status = RtlAllocateAndInitializeSid(&NtAuthority, + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_USERS, + 0, + 0, + 0, + 0, + 0, + 0, + &UsersSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create User SID, skipping tests\n"); + goto Quit; + } + + Token = GetTokenProcess(TRUE, TRUE); + if (Token == NULL) + { + skip("Failed to get token, skipping tests\n"); + goto Quit; + } + + Status = RtlCreateSecurityDescriptor(&Sd, SECURITY_DESCRIPTOR_REVISION); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create a security descriptor, skipping tests\n"); + goto Quit; + } + + DaclSize = sizeof(ACL) + + sizeof(ACCESS_DENIED_OBJECT_ACE) + RtlLengthSid(AdminSid) + + sizeof(ACCESS_DENIED_OBJECT_ACE) + RtlLengthSid(EveryoneSid) + + sizeof(ACCESS_ALLOWED_OBJECT_ACE) + RtlLengthSid(EveryoneSid); + Dacl = RtlAllocateHeap(RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + DaclSize); + if (Dacl == NULL) + { + skip("Failed to allocate memory for DACL, skipping tests\n"); + goto Quit; + } + + Status = RtlCreateAcl(Dacl, + DaclSize, + ACL_REVISION4); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create DACL, skipping tests\n"); + goto Quit; + } + + /* + * Admins can't query values from keys and everyone else is denied writing to + * keys to child sub-object but can enumerate subkeys from the object itself. + */ + Status = RtlAddAccessDeniedObjectAce(Dacl, + ACL_REVISION4, + 0, + KEY_QUERY_VALUE, + &ObjectType, + NULL, + AdminSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to add deny object ACE for Admins SID, skipping tests\n"); + goto Quit; + } + + Status = RtlAddAccessDeniedObjectAce(Dacl, + ACL_REVISION4, + 0, + KEY_WRITE, + &ChildObjectType, + NULL, + EveryoneSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to add deny object ACE for Everyone SID, skipping tests\n"); + goto Quit; + } + + Status = RtlAddAccessAllowedObjectAce(Dacl, + ACL_REVISION4, + 0, + KEY_ENUMERATE_SUB_KEYS, + &ObjectType, + NULL, + EveryoneSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to add allowed object ACE for Everyone SID, skipping tests\n"); + goto Quit; + } + + /* Setup the descriptor */ + RtlSetGroupSecurityDescriptor(&Sd, UsersSid, FALSE); + RtlSetOwnerSecurityDescriptor(&Sd, AdminSid, FALSE); + RtlSetDaclSecurityDescriptor(&Sd, TRUE, Dacl, FALSE); + + /* Setup the object type list */ + ObjTypeList[0].Level = ACCESS_OBJECT_GUID; + ObjTypeList[0].Sbz = 0; + ObjTypeList[0].ObjectType = &ObjectType; + + ObjTypeList[1].Level = ACCESS_PROPERTY_SET_GUID; + ObjTypeList[1].Sbz = 0; + ObjTypeList[1].ObjectType = &ChildObjectType; + + /* Evaluate access -- they should be denied */ + Status = NtAccessCheckByType(&Sd, + NULL, + Token, + KEY_QUERY_VALUE, + ObjTypeList, + RTL_NUMBER_OF(ObjTypeList), + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_SUCCESS); + ok(GrantedAccess == 0, "Expected no access rights but got 0x%08lx\n", GrantedAccess); + ok(AccessStatus == STATUS_ACCESS_DENIED, "Expected access denied as status but got 0x%08lx\n", AccessStatus); + + Status = NtAccessCheckByType(&Sd, + NULL, + Token, + KEY_WRITE, + ObjTypeList, + RTL_NUMBER_OF(ObjTypeList), + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_SUCCESS); + ok(GrantedAccess == 0, "Expected no access rights but got 0x%08lx\n", GrantedAccess); + ok(AccessStatus == STATUS_ACCESS_DENIED, "Expected access denied as status but got 0x%08lx\n", AccessStatus); + + /* Everyone else should be granted only enumerate subkey access */ + Status = NtAccessCheckByType(&Sd, + NULL, + Token, + KEY_ENUMERATE_SUB_KEYS, + ObjTypeList, + RTL_NUMBER_OF(ObjTypeList), + &RegMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_SUCCESS); + ok(GrantedAccess == KEY_ENUMERATE_SUB_KEYS, "Expected KEY_ENUMERATE_SUB_KEYS as granted right but got 0x%08lx\n", GrantedAccess); + ok(AccessStatus == STATUS_SUCCESS, "Expected a success status but got 0x%08lx\n", AccessStatus); + +Quit: + if (Dacl) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl); + } + + if (Token) + { + NtClose(Token); + } + + if (UsersSid) + { + RtlFreeSid(UsersSid); + } + + if (AdminSid) + { + RtlFreeSid(AdminSid); + } + + if (EveryoneSid) + { + RtlFreeSid(EveryoneSid); + } + + if (PrivilegeSet) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, PrivilegeSet); + } +} + +START_TEST(NtAccessCheckByType) +{ + ParamsValidationTests(); + AccessGrantedNoDaclTests(); + AccessGrantedTests(); + AccessGrantedMultipleObjectsTests(); + DenyAccessTests(); +} diff --git a/modules/rostests/apitests/ntdll/testlist.c b/modules/rostests/apitests/ntdll/testlist.c index 03857e1852a..321b8cddf4b 100644 --- a/modules/rostests/apitests/ntdll/testlist.c +++ b/modules/rostests/apitests/ntdll/testlist.c @@ -6,6 +6,7 @@ extern void func_LdrEnumResources(void); extern void func_load_notifications(void); extern void func_NtAcceptConnectPort(void); +extern void func_NtAccessCheckByType(void); extern void func_NtAdjustGroupsToken(void); extern void func_NtAdjustPrivilegesToken(void); extern void func_NtAllocateVirtualMemory(void); @@ -99,6 +100,7 @@ const struct test winetest_testlist[] = { "LdrEnumResources", func_LdrEnumResources }, { "load_notifications", func_load_notifications }, { "NtAcceptConnectPort", func_NtAcceptConnectPort }, + { "NtAccessCheckByType", func_NtAccessCheckByType }, { "NtAdjustGroupsToken", func_NtAdjustGroupsToken }, { "NtAdjustPrivilegesToken", func_NtAdjustPrivilegesToken }, { "NtAllocateVirtualMemory", func_NtAllocateVirtualMemory },