From 6fe1b387c6f5a582fedeac7c17d84440a1df74d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20Bi=C8=99oc?= Date: Tue, 19 Oct 2021 11:36:02 +0200 Subject: [PATCH] [NTDLL_APITEST] Implement NtFilterToken testcase --- .../rostests/apitests/ntdll/CMakeLists.txt | 1 + .../rostests/apitests/ntdll/NtFilterToken.c | 137 ++++++++++++++++++ modules/rostests/apitests/ntdll/testlist.c | 2 + 3 files changed, 140 insertions(+) create mode 100644 modules/rostests/apitests/ntdll/NtFilterToken.c diff --git a/modules/rostests/apitests/ntdll/CMakeLists.txt b/modules/rostests/apitests/ntdll/CMakeLists.txt index 403641119fb..1e69ecf4900 100644 --- a/modules/rostests/apitests/ntdll/CMakeLists.txt +++ b/modules/rostests/apitests/ntdll/CMakeLists.txt @@ -22,6 +22,7 @@ list(APPEND SOURCE NtDeleteKey.c NtDuplicateObject.c NtDuplicateToken.c + NtFilterToken.c NtFreeVirtualMemory.c NtImpersonateAnonymousToken.c NtLoadUnloadKey.c diff --git a/modules/rostests/apitests/ntdll/NtFilterToken.c b/modules/rostests/apitests/ntdll/NtFilterToken.c new file mode 100644 index 00000000000..60453c9bf87 --- /dev/null +++ b/modules/rostests/apitests/ntdll/NtFilterToken.c @@ -0,0 +1,137 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Tests for the NtFilterToken API + * COPYRIGHT: Copyright 2021 George Bișoc + */ + +#include "precomp.h" + +static +HANDLE +GetTokenProcess(VOID) +{ + BOOL Success; + HANDLE Token; + + Success = OpenProcessToken(GetCurrentProcess(), + TOKEN_DUPLICATE | TOKEN_QUERY, + &Token); + if (!Success) + { + skip("GetTokenProcess() has failed to get the process' token (error code: %lu)!\n", GetLastError()); + return NULL; + } + + return Token; +} + +START_TEST(NtFilterToken) +{ + NTSTATUS Status; + HANDLE FilteredToken, Token; + TOKEN_PRIVILEGES Priv; + LUID PrivLuid; + ULONG Size; + PTOKEN_STATISTICS TokenStats; + + /* We don't give a token */ + Status = NtFilterToken(NULL, + 0, + NULL, + NULL, + NULL, + &FilteredToken); + ok_hex(Status, STATUS_INVALID_HANDLE); + + /* Get the token from process now */ + Token = GetTokenProcess(); + + /* We don't give any privileges to delete */ + Status = NtFilterToken(Token, + 0, + NULL, + NULL, + NULL, + &FilteredToken); + ok_hex(Status, STATUS_SUCCESS); + + /* Query the total size to hold the statistics */ + Status = NtQueryInformationToken(Token, TokenStatistics, NULL, 0, &Size); + if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) + { + skip("Failed to query the total size for token statistics structure! (Status -> 0x%lx)\n", Status); + return; + } + + /* Total size queried, time to allocate our buffer based on that size */ + TokenStats = RtlAllocateHeap(RtlGetProcessHeap(), 0, Size); + if (TokenStats == NULL) + { + skip("Failed to allocate our token statistics buffer!\n"); + return; + } + + /* Time to query our token statistics, prior disabling token's privileges */ + Status = NtQueryInformationToken(Token, TokenStatistics, TokenStats, Size, &Size); + if (!NT_SUCCESS(Status)) + { + skip("Failed to query the token statistics! (Status -> 0x%lx)\n", Status); + return; + } + + trace("Number of privileges before token filtering -- %lu\n\n", TokenStats->PrivilegeCount); + + /* Disable the privileges and make the token a safer inert one */ + Status = NtFilterToken(Token, + DISABLE_MAX_PRIVILEGE | SANDBOX_INERT, + NULL, + NULL, + NULL, + &FilteredToken); + ok_hex(Status, STATUS_SUCCESS); + + /* We've disabled privileges, query the stats again */ + Status = NtQueryInformationToken(FilteredToken, TokenStatistics, TokenStats, Size, &Size); + if (!NT_SUCCESS(Status)) + { + skip("Failed to query the token statistics! (Status -> 0x%lx)\n", Status); + return; + } + + trace("Number of privileges after token filtering (privileges disabled with DISABLE_MAX_PRIVILEGE) -- %lu\n\n", TokenStats->PrivilegeCount); + + /* Close the filtered token and do another test */ + CloseHandle(FilteredToken); + + /* Fill in a privilege to delete */ + Priv.PrivilegeCount = 1; + + ConvertPrivLongToLuid(SE_BACKUP_PRIVILEGE, &PrivLuid); + Priv.Privileges[0].Luid = PrivLuid; + Priv.Privileges[0].Attributes = 0; + + /* Delete the privileges */ + Status = NtFilterToken(Token, + 0, + NULL, + &Priv, + NULL, + &FilteredToken); + ok_hex(Status, STATUS_SUCCESS); + + /* We've deleted a privilege, query the stats again */ + Status = NtQueryInformationToken(FilteredToken, TokenStatistics, TokenStats, Size, &Size); + if (!NT_SUCCESS(Status)) + { + skip("Failed to query the token statistics! (Status -> 0x%lx)\n", Status); + return; + } + + trace("Number of privileges after token filtering (manually deleted privilege) -- %lu\n\n", TokenStats->PrivilegeCount); + + /* We're done */ + RtlFreeHeap(RtlGetProcessHeap(), 0, TokenStats); + CloseHandle(Token); + CloseHandle(FilteredToken); +} diff --git a/modules/rostests/apitests/ntdll/testlist.c b/modules/rostests/apitests/ntdll/testlist.c index 9642602fd77..aff61fa3091 100644 --- a/modules/rostests/apitests/ntdll/testlist.c +++ b/modules/rostests/apitests/ntdll/testlist.c @@ -18,6 +18,7 @@ extern void func_NtCreateThread(void); extern void func_NtDeleteKey(void); extern void func_NtDuplicateObject(void); extern void func_NtDuplicateToken(void); +extern void func_NtFilterToken(void); extern void func_NtFreeVirtualMemory(void); extern void func_NtImpersonateAnonymousToken(void); extern void func_NtLoadUnloadKey(void); @@ -105,6 +106,7 @@ const struct test winetest_testlist[] = { "NtDeleteKey", func_NtDeleteKey }, { "NtDuplicateObject", func_NtDuplicateObject }, { "NtDuplicateToken", func_NtDuplicateToken }, + { "NtFilterToken", func_NtFilterToken }, { "NtFreeVirtualMemory", func_NtFreeVirtualMemory }, { "NtImpersonateAnonymousToken", func_NtImpersonateAnonymousToken }, { "NtLoadUnloadKey", func_NtLoadUnloadKey },