diff --git a/modules/rostests/apitests/ntdll/CMakeLists.txt b/modules/rostests/apitests/ntdll/CMakeLists.txt index 85515a2efaf..b6ce3a1cf4d 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 + NtAccessCheck.c NtAccessCheckByType.c NtAccessCheckByTypeResultList.c NtAdjustGroupsToken.c diff --git a/modules/rostests/apitests/ntdll/NtAccessCheck.c b/modules/rostests/apitests/ntdll/NtAccessCheck.c new file mode 100644 index 00000000000..c987a445b13 --- /dev/null +++ b/modules/rostests/apitests/ntdll/NtAccessCheck.c @@ -0,0 +1,188 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Tests for the NtAccessCheck API + * COPYRIGHT: Copyright 2023 George Bișoc + */ + +#include "precomp.h" + +static +HANDLE +GetToken(VOID) +{ + 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 = SecurityImpersonation; + Sqos.ContextTrackingMode = 0; + Sqos.EffectiveOnly = FALSE; + + InitializeObjectAttributes(&ObjectAttributes, + NULL, + 0, + NULL, + NULL); + ObjectAttributes.SecurityQualityOfService = &Sqos; + + Status = NtDuplicateToken(Token, + TOKEN_QUERY | TOKEN_DUPLICATE, + &ObjectAttributes, + FALSE, + TokenImpersonation, + &DuplicatedToken); + if (!NT_SUCCESS(Status)) + { + trace("Failed to duplicate token (Status 0x%08lx)\n", Status); + NtClose(Token); + return NULL; + } + + return DuplicatedToken; +} + +static +VOID +AccessCheckEmptyMappingTest(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; + PSID WorldSid = NULL; + static SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY}; + static GENERIC_MAPPING EmptyMapping = {0, 0, 0, 0}; + + /* 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, + &WorldSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create World SID, skipping tests\n"); + goto Quit; + } + + Token = GetToken(); + 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(WorldSid); + Dacl = RtlAllocateHeap(RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + DaclSize); + if (Dacl == NULL) + { + skip("Failed to allocate memory for DACL, skipping tests\n"); + goto Quit; + } + + /* Setup a ACL and give full access to everyone */ + Status = RtlCreateAcl(Dacl, + DaclSize, + ACL_REVISION); + if (!NT_SUCCESS(Status)) + { + skip("Failed to create DACL, skipping tests\n"); + goto Quit; + } + + Status = RtlAddAccessAllowedAce(Dacl, + ACL_REVISION, + GENERIC_ALL, + WorldSid); + if (!NT_SUCCESS(Status)) + { + skip("Failed to add allowed ACE for World SID, skipping tests\n"); + goto Quit; + } + + /* Setup the descriptor */ + RtlSetGroupSecurityDescriptor(&Sd, WorldSid, FALSE); + RtlSetOwnerSecurityDescriptor(&Sd, WorldSid, FALSE); + RtlSetDaclSecurityDescriptor(&Sd, TRUE, Dacl, FALSE); + + /* Do an access check with empty mapping */ + Status = NtAccessCheck(&Sd, + Token, + MAXIMUM_ALLOWED, + &EmptyMapping, + PrivilegeSet, + &PrivilegeSetLength, + &GrantedAccess, + &AccessStatus); + ok_hex(Status, STATUS_SUCCESS); + ok(AccessStatus == STATUS_SUCCESS, "Expected a success status but got 0x%08lx\n", AccessStatus); + trace("GrantedAccess == 0x%08lx\n", GrantedAccess); + +Quit: + if (Dacl) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl); + } + + if (Token) + { + NtClose(Token); + } + + if (WorldSid) + { + RtlFreeSid(WorldSid); + } + + if (PrivilegeSet) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, PrivilegeSet); + } +} + +START_TEST(NtAccessCheck) +{ + AccessCheckEmptyMappingTest(); +} diff --git a/modules/rostests/apitests/ntdll/testlist.c b/modules/rostests/apitests/ntdll/testlist.c index 2be58938238..438cc56a172 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_NtAccessCheck(void); extern void func_NtAccessCheckByType(void); extern void func_NtAccessCheckByTypeResultList(void); extern void func_NtAdjustGroupsToken(void); @@ -102,6 +103,7 @@ const struct test winetest_testlist[] = { "LdrEnumResources", func_LdrEnumResources }, { "load_notifications", func_load_notifications }, { "NtAcceptConnectPort", func_NtAcceptConnectPort }, + { "NtAccessCheck", func_NtAccessCheck }, { "NtAccessCheckByType", func_NtAccessCheckByType }, { "NtAccessCheckByTypeResultList", func_NtAccessCheckByTypeResultList }, { "NtAdjustGroupsToken", func_NtAdjustGroupsToken },