From bac1bce60549b0a1e5dc5b77c53ea9d2cc1e430e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20Bi=C8=99oc?= Date: Sun, 5 Jun 2022 23:24:06 +0200 Subject: [PATCH] [NTDLL_APITEST] Write tests for NtQueryInformationToken and NtSetInformationToken --- .../rostests/apitests/ntdll/CMakeLists.txt | 2 + .../apitests/ntdll/NtQueryInformationToken.c | 699 ++++++++++++++++++ .../apitests/ntdll/NtSetInformationToken.c | 247 +++++++ modules/rostests/apitests/ntdll/testlist.c | 4 + 4 files changed, 952 insertions(+) create mode 100644 modules/rostests/apitests/ntdll/NtQueryInformationToken.c create mode 100644 modules/rostests/apitests/ntdll/NtSetInformationToken.c diff --git a/modules/rostests/apitests/ntdll/CMakeLists.txt b/modules/rostests/apitests/ntdll/CMakeLists.txt index a50b1898a76..39296e4d8f2 100644 --- a/modules/rostests/apitests/ntdll/CMakeLists.txt +++ b/modules/rostests/apitests/ntdll/CMakeLists.txt @@ -35,6 +35,7 @@ list(APPEND SOURCE NtQueryInformationFile.c NtQueryInformationProcess.c NtQueryInformationThread.c + NtQueryInformationToken.c NtQueryKey.c NtQuerySystemEnvironmentValue.c NtQuerySystemInformation.c @@ -45,6 +46,7 @@ list(APPEND SOURCE NtSetInformationFile.c NtSetInformationProcess.c NtSetInformationThread.c + NtSetInformationToken.c NtSetValueKey.c NtSetVolumeInformationFile.c NtUnloadDriver.c diff --git a/modules/rostests/apitests/ntdll/NtQueryInformationToken.c b/modules/rostests/apitests/ntdll/NtQueryInformationToken.c new file mode 100644 index 00000000000..18e8001f99b --- /dev/null +++ b/modules/rostests/apitests/ntdll/NtQueryInformationToken.c @@ -0,0 +1,699 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Tests for the NtQueryInformationToken API + * COPYRIGHT: Copyright 2022 George Bișoc + */ + +#include "precomp.h" + +static +HANDLE +OpenCurrentToken(VOID) +{ + BOOL Success; + HANDLE Token; + + Success = OpenProcessToken(GetCurrentProcess(), + TOKEN_READ | TOKEN_QUERY_SOURCE | TOKEN_DUPLICATE, + &Token); + if (!Success) + { + ok(0, "OpenProcessToken() has failed to get the process' token (error code: %lu)!\n", GetLastError()); + return NULL; + } + + return Token; +} + +static +VOID +QueryTokenUserTests( + _In_ HANDLE Token) +{ + NTSTATUS Status; + PTOKEN_USER UserToken; + ULONG BufferLength; + UNICODE_STRING SidString; + + /* + * Query the exact buffer length to hold + * our stuff, STATUS_BUFFER_TOO_SMALL must + * be expected here. + */ + Status = NtQueryInformationToken(Token, + TokenUser, + NULL, + 0, + &BufferLength); + ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL); + + /* Allocate the buffer based on the size we got */ + UserToken = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); + if (!UserToken) + { + ok(0, "Failed to allocate from heap for token user (required buffer length %lu)!\n", BufferLength); + return; + } + + /* Now do the actual query */ + Status = NtQueryInformationToken(Token, + TokenUser, + UserToken, + BufferLength, + &BufferLength); + ok_ntstatus(Status, STATUS_SUCCESS); + + RtlConvertSidToUnicodeString(&SidString, UserToken->User.Sid, TRUE); + trace("=============== TokenUser ===============\n"); + trace("The SID of current token user is: %s\n", wine_dbgstr_w(SidString.Buffer)); + trace("=========================================\n\n"); + RtlFreeUnicodeString(&SidString); + + RtlFreeHeap(RtlGetProcessHeap(), 0, UserToken); +} + +static +VOID +QueryTokenGroupsTests( + _In_ HANDLE Token) +{ + NTSTATUS Status; + PTOKEN_GROUPS Groups; + ULONG BufferLength; + + /* + * Query the exact buffer length to hold + * our stuff, STATUS_BUFFER_TOO_SMALL must + * be expected here. + */ + Status = NtQueryInformationToken(Token, + TokenGroups, + NULL, + 0, + &BufferLength); + ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL); + + /* Allocate the buffer based on the size we got */ + Groups = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); + if (!Groups) + { + ok(0, "Failed to allocate from heap for token groups (required buffer length %lu)!\n", BufferLength); + return; + } + + /* + * Now do the actual query and validate the + * number of groups. + */ + Status = NtQueryInformationToken(Token, + TokenGroups, + Groups, + BufferLength, + &BufferLength); + ok_ntstatus(Status, STATUS_SUCCESS); + ok(Groups->GroupCount == 10, "The number of groups must be 10 (current number %lu)!\n", Groups->GroupCount); + + RtlFreeHeap(RtlGetProcessHeap(), 0, Groups); +} + +static +VOID +QueryTokenPrivilegesTests( + _In_ HANDLE Token) +{ + NTSTATUS Status; + PTOKEN_PRIVILEGES Privileges; + ULONG BufferLength; + + /* + * Query the exact buffer length to hold + * our stuff, STATUS_BUFFER_TOO_SMALL must + * be expected here. + */ + Status = NtQueryInformationToken(Token, + TokenPrivileges, + NULL, + 0, + &BufferLength); + ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL); + + /* Allocate the buffer based on the size we got */ + Privileges = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); + if (!Privileges) + { + ok(0, "Failed to allocate from heap for token privileges (required buffer length %lu)!\n", BufferLength); + return; + } + + /* + * Now do the actual query and validate the + * number of privileges. + */ + Status = NtQueryInformationToken(Token, + TokenPrivileges, + Privileges, + BufferLength, + &BufferLength); + ok_ntstatus(Status, STATUS_SUCCESS); + ok(Privileges->PrivilegeCount == 20, "The number of privileges must be 20 (current number %lu)!\n", Privileges->PrivilegeCount); + + RtlFreeHeap(RtlGetProcessHeap(), 0, Privileges); +} + +static +VOID +QueryTokenOwnerTests( + _In_ HANDLE Token) +{ + NTSTATUS Status; + PTOKEN_OWNER Owner; + ULONG BufferLength; + UNICODE_STRING SidString; + + /* + * Query the exact buffer length to hold + * our stuff, STATUS_BUFFER_TOO_SMALL must + * be expected here. + */ + Status = NtQueryInformationToken(Token, + TokenOwner, + NULL, + 0, + &BufferLength); + ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL); + + /* Allocate the buffer based on the size we got */ + Owner = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); + if (!Owner) + { + ok(0, "Failed to allocate from heap for token owner (required buffer length %lu)!\n", BufferLength); + return; + } + + /* + * Now do the actual query and validate the + * token owner (must be the local admin). + */ + Status = NtQueryInformationToken(Token, + TokenOwner, + Owner, + BufferLength, + &BufferLength); + ok_ntstatus(Status, STATUS_SUCCESS); + + RtlConvertSidToUnicodeString(&SidString, Owner->Owner, TRUE); + ok_wstr(SidString.Buffer, L"S-1-5-32-544"); + RtlFreeUnicodeString(&SidString); + + RtlFreeHeap(RtlGetProcessHeap(), 0, Owner); +} + +static +VOID +QueryTokenPrimaryGroupTests( + _In_ HANDLE Token) +{ + NTSTATUS Status; + PTOKEN_PRIMARY_GROUP PrimaryGroup; + ULONG BufferLength; + UNICODE_STRING SidString; + + /* + * Query the exact buffer length to hold + * our stuff, STATUS_BUFFER_TOO_SMALL must + * be expected here. + */ + Status = NtQueryInformationToken(Token, + TokenPrimaryGroup, + NULL, + 0, + &BufferLength); + ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL); + + /* Allocate the buffer based on the size we got */ + PrimaryGroup = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); + if (!PrimaryGroup) + { + ok(0, "Failed to allocate from heap for token primary group (required buffer length %lu)!\n", BufferLength); + return; + } + + /* Now do the actual query */ + Status = NtQueryInformationToken(Token, + TokenPrimaryGroup, + PrimaryGroup, + BufferLength, + &BufferLength); + ok_ntstatus(Status, STATUS_SUCCESS); + + RtlConvertSidToUnicodeString(&SidString, PrimaryGroup->PrimaryGroup, TRUE); + trace("=============== TokenPrimaryGroup ===============\n"); + trace("The primary group SID of current token is: %s\n", wine_dbgstr_w(SidString.Buffer)); + trace("=========================================\n\n"); + RtlFreeUnicodeString(&SidString); + + RtlFreeHeap(RtlGetProcessHeap(), 0, PrimaryGroup); +} + +static +VOID +QueryTokenDefaultDaclTests( + _In_ HANDLE Token) +{ + NTSTATUS Status; + PTOKEN_DEFAULT_DACL Dacl; + ULONG BufferLength; + + /* + * Query the exact buffer length to hold + * our stuff, STATUS_BUFFER_TOO_SMALL must + * be expected here. + */ + Status = NtQueryInformationToken(Token, + TokenDefaultDacl, + NULL, + 0, + &BufferLength); + ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL); + + /* Allocate the buffer based on the size we got */ + Dacl = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); + if (!Dacl) + { + ok(0, "Failed to allocate from heap for token default DACL (required buffer length %lu)!\n", BufferLength); + return; + } + + /* + * Now do the actual query and validate the + * ACL revision and number count of ACEs. + */ + Status = NtQueryInformationToken(Token, + TokenDefaultDacl, + Dacl, + BufferLength, + &BufferLength); + ok_ntstatus(Status, STATUS_SUCCESS); + ok(Dacl->DefaultDacl->AclRevision == 2, "The ACL revision of token default DACL must be 2 (current revision %u)!\n", Dacl->DefaultDacl->AclRevision); + ok(Dacl->DefaultDacl->AceCount == 2, "The ACL's ACE count must be 2 (current ACE count %u)!\n", Dacl->DefaultDacl->AceCount); + + RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl); +} + +static +VOID +QueryTokenSourceTests( + _In_ HANDLE Token) +{ + NTSTATUS Status; + PTOKEN_SOURCE Source; + ULONG BufferLength; + CHAR SourceName[8]; + + /* + * Query the exact buffer length to hold + * our stuff, STATUS_BUFFER_TOO_SMALL must + * be expected here. + */ + Status = NtQueryInformationToken(Token, + TokenSource, + NULL, + 0, + &BufferLength); + ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL); + + /* Allocate the buffer based on the size we got */ + Source = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); + if (!Source) + { + ok(0, "Failed to allocate from heap for token source (required buffer length %lu)!\n", BufferLength); + return; + } + + /* Now do the actual query */ + Status = NtQueryInformationToken(Token, + TokenSource, + Source, + BufferLength, + &BufferLength); + ok_ntstatus(Status, STATUS_SUCCESS); + + /* + * Subtract the source name from the queried buffer + * and compare it. The source name in question must be + * "User32" as the primary token of the current calling + * process is generated when the user has successfully + * logged in and he's into the desktop. + */ + SourceName[0] = Source->SourceName[0]; + SourceName[1] = Source->SourceName[1]; + SourceName[2] = Source->SourceName[2]; + SourceName[3] = Source->SourceName[3]; + SourceName[4] = Source->SourceName[4]; + SourceName[5] = Source->SourceName[5]; + SourceName[6] = '\0'; + ok_str(SourceName, "User32"); + + RtlFreeHeap(RtlGetProcessHeap(), 0, Source); +} + +static +VOID +QueryTokenTypeTests( + _In_ HANDLE Token) +{ + NTSTATUS Status; + TOKEN_TYPE Type; + ULONG BufferLength; + + /* + * Query the token type. The token of the + * current calling process must be primary + * since we aren't impersonating the security + * context of a client. + */ + Status = NtQueryInformationToken(Token, + TokenType, + &Type, + sizeof(TOKEN_TYPE), + &BufferLength); + ok_ntstatus(Status, STATUS_SUCCESS); + ok(Type == TokenPrimary, "The current token is not primary!\n"); +} + +static +VOID +QueryTokenImpersonationTests( + _In_ HANDLE Token) +{ + NTSTATUS Status; + SECURITY_IMPERSONATION_LEVEL Level; + ULONG BufferLength; + HANDLE DupToken; + OBJECT_ATTRIBUTES ObjectAttributes; + + /* + * Windows throws STATUS_INVALID_INFO_CLASS here + * because one cannot simply query the impersonation + * level of a primary token. + */ + Status = NtQueryInformationToken(Token, + TokenImpersonationLevel, + &Level, + sizeof(SECURITY_IMPERSONATION_LEVEL), + &BufferLength); + ok_ntstatus(Status, STATUS_INVALID_INFO_CLASS); + + /* + * Initialize the object attribute and duplicate + * the token into an actual impersonation one. + */ + InitializeObjectAttributes(&ObjectAttributes, + NULL, + 0, + NULL, + NULL); + + Status = NtDuplicateToken(Token, + TOKEN_QUERY, + &ObjectAttributes, + FALSE, + TokenImpersonation, + &DupToken); + if (!NT_SUCCESS(Status)) + { + ok(0, "Failed to duplicate token (Status code %lx)!\n", Status); + return; + } + + /* Now do the actual query */ + Status = NtQueryInformationToken(DupToken, + TokenImpersonationLevel, + &Level, + sizeof(SECURITY_IMPERSONATION_LEVEL), + &BufferLength); + ok_ntstatus(Status, STATUS_SUCCESS); + ok(Level == SecurityAnonymous, "The current token impersonation level is not anonymous!\n"); + CloseHandle(DupToken); +} + +static +VOID +QueryTokenStatisticsTests( + _In_ HANDLE Token) +{ + NTSTATUS Status; + PTOKEN_STATISTICS Statistics; + ULONG BufferLength; + + /* + * Query the exact buffer length to hold + * our stuff, STATUS_BUFFER_TOO_SMALL must + * be expected here. + */ + Status = NtQueryInformationToken(Token, + TokenStatistics, + NULL, + 0, + &BufferLength); + ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL); + + /* Allocate the buffer based on the size we got */ + Statistics = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); + if (!Statistics) + { + skip("Failed to allocate heap for token statistics!\n"); + return; + } + + /* Do the actual query */ + Status = NtQueryInformationToken(Token, + TokenStatistics, + Statistics, + BufferLength, + &BufferLength); + ok_ntstatus(Status, STATUS_SUCCESS); + + trace("=============== TokenStatistics ===============\n"); + trace("Token ID: %lu %lu\n", Statistics->TokenId.LowPart, Statistics->TokenId.HighPart); + trace("Authentication ID: %lu %lu\n", Statistics->AuthenticationId.LowPart, Statistics->AuthenticationId.HighPart); + trace("Dynamic Charged: %lu\n", Statistics->DynamicCharged); + trace("Dynamic Available: %lu\n", Statistics->DynamicAvailable); + trace("Modified ID: %lu %lu\n", Statistics->ModifiedId.LowPart, Statistics->ModifiedId.HighPart); + trace("=========================================\n\n"); + + RtlFreeHeap(RtlGetProcessHeap(), 0, Statistics); +} + +static +VOID +QueryTokenRestrictedSidsTest( + _In_ HANDLE Token) +{ + NTSTATUS Status; + PTOKEN_GROUPS RestrictedGroups; + TOKEN_GROUPS SidToRestrict; + ULONG BufferLength; + HANDLE FilteredToken; + PSID WorldSid; + static SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY}; + + /* + * Query the exact buffer length to hold + * our stuff, STATUS_BUFFER_TOO_SMALL must + * be expected here. + */ + Status = NtQueryInformationToken(Token, + TokenRestrictedSids, + NULL, + 0, + &BufferLength); + ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL); + + /* Allocate the buffer based on the size we got */ + RestrictedGroups = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); + if (!RestrictedGroups) + { + ok(0, "Failed to allocate from heap for restricted SIDs (required buffer length %lu)!\n", BufferLength); + return; + } + + /* + * Query the number of restricted SIDs. Originally the token + * doesn't have any restricted SIDs inserted. + */ + Status = NtQueryInformationToken(Token, + TokenRestrictedSids, + RestrictedGroups, + BufferLength, + &BufferLength); + ok_ntstatus(Status, STATUS_SUCCESS); + ok(RestrictedGroups->GroupCount == 0, "There mustn't be any restricted SIDs before filtering (number of restricted SIDs %lu)!\n", RestrictedGroups->GroupCount); + + RtlFreeHeap(RtlGetProcessHeap(), 0, RestrictedGroups); + RestrictedGroups = NULL; + + Status = RtlAllocateAndInitializeSid(&WorldAuthority, + 1, + SECURITY_WORLD_RID, + 0, 0, 0, 0, 0, 0, 0, + &WorldSid); + if (!NT_SUCCESS(Status)) + { + ok(0, "Failed to allocate World SID (Status code %lx)!\n", Status); + return; + } + + SidToRestrict.GroupCount = 1; + SidToRestrict.Groups[0].Attributes = 0; + SidToRestrict.Groups[0].Sid = WorldSid; + + Status = NtFilterToken(Token, + 0, + NULL, + NULL, + &SidToRestrict, + &FilteredToken); + if (!NT_SUCCESS(Status)) + { + ok(0, "Failed to filter the current token (Status code %lx)!\n", Status); + RtlFreeHeap(RtlGetProcessHeap(), 0, WorldSid); + return; + } + + Status = NtQueryInformationToken(FilteredToken, + TokenRestrictedSids, + NULL, + 0, + &BufferLength); + ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL); + + RestrictedGroups = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); + if (!RestrictedGroups) + { + ok(0, "Failed to allocate from heap for restricted SIDs (required buffer length %lu)!\n", BufferLength); + RtlFreeHeap(RtlGetProcessHeap(), 0, WorldSid); + return; + } + + /* + * Do a query again, this time we must have a + * restricted SID inserted into the token. + */ + Status = NtQueryInformationToken(FilteredToken, + TokenRestrictedSids, + RestrictedGroups, + BufferLength, + &BufferLength); + ok_ntstatus(Status, STATUS_SUCCESS); + ok(RestrictedGroups->GroupCount == 1, "There must be only one restricted SID added in token (number of restricted SIDs %lu)!\n", RestrictedGroups->GroupCount); + + RtlFreeHeap(RtlGetProcessHeap(), 0, RestrictedGroups); + RtlFreeHeap(RtlGetProcessHeap(), 0, WorldSid); + CloseHandle(FilteredToken); +} + +static +VOID +QueryTokenSessionIdTests( + _In_ HANDLE Token) +{ + NTSTATUS Status; + ULONG SessionId; + ULONG BufferLength; + + /* + * Query the session ID. Generally the current + * process token is not under any terminal service + * so the ID must be 0. + */ + Status = NtQueryInformationToken(Token, + TokenSessionId, + &SessionId, + sizeof(ULONG), + &BufferLength); + ok_ntstatus(Status, STATUS_SUCCESS); + ok(SessionId == 0, "The session ID of current token must be 0 (current session %lu)!\n", SessionId); +} + +static +VOID +QueryTokenOriginTests( + _In_ HANDLE Token) +{ + NTSTATUS Status; + TOKEN_ORIGIN Origin; + ULONG BufferLength; + + /* Query the token origin */ + Status = NtQueryInformationToken(Token, + TokenOrigin, + &Origin, + sizeof(TOKEN_ORIGIN), + &BufferLength); + ok_ntstatus(Status, STATUS_SUCCESS); + ok(Origin.OriginatingLogonSession.LowPart == 0x3e7, "The LowPart field of the originating logon session must be SYSTEM_LUID (current value %lu)!\n", + Origin.OriginatingLogonSession.LowPart); + ok(Origin.OriginatingLogonSession.HighPart == 0x0, "The HighPart field of the logon session must be 0 (current value %lu)!\n", + Origin.OriginatingLogonSession.HighPart); +} + +START_TEST(NtQueryInformationToken) +{ + NTSTATUS Status; + HANDLE Token; + PVOID Dummy; + ULONG DummyReturnLength; + + /* ReturnLength is NULL */ + Status = NtQueryInformationToken(NULL, + TokenUser, + NULL, + 0, + NULL); + ok_ntstatus(Status, STATUS_ACCESS_VIOLATION); + + /* We don't give any token here */ + Status = NtQueryInformationToken(NULL, + TokenUser, + &Dummy, + 0, + &DummyReturnLength); + ok_ntstatus(Status, STATUS_INVALID_HANDLE); + + Token = OpenCurrentToken(); + + /* Class 0 is unused on Windows */ + Status = NtQueryInformationToken(Token, + 0, + &Dummy, + 0, + &DummyReturnLength); + ok_ntstatus(Status, STATUS_INVALID_INFO_CLASS); + + /* We give a bogus info class */ + Status = NtQueryInformationToken(Token, + 0xa0a, + &Dummy, + 0, + &DummyReturnLength); + ok_ntstatus(Status, STATUS_INVALID_INFO_CLASS); + + /* Now perform tests for each class */ + QueryTokenUserTests(Token); + QueryTokenGroupsTests(Token); + QueryTokenPrivilegesTests(Token); + QueryTokenOwnerTests(Token); + QueryTokenPrimaryGroupTests(Token); + QueryTokenDefaultDaclTests(Token); + QueryTokenSourceTests(Token); + QueryTokenTypeTests(Token); + QueryTokenImpersonationTests(Token); + QueryTokenStatisticsTests(Token); + QueryTokenRestrictedSidsTest(Token); + QueryTokenSessionIdTests(Token); + QueryTokenOriginTests(Token); + + CloseHandle(Token); +} diff --git a/modules/rostests/apitests/ntdll/NtSetInformationToken.c b/modules/rostests/apitests/ntdll/NtSetInformationToken.c new file mode 100644 index 00000000000..cd51189345b --- /dev/null +++ b/modules/rostests/apitests/ntdll/NtSetInformationToken.c @@ -0,0 +1,247 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Tests for the NtSetInformationToken API + * COPYRIGHT: Copyright 2022 George Bișoc + */ + +#include "precomp.h" + +static +HANDLE +OpenCurrentToken(VOID) +{ + BOOL Success; + HANDLE Token; + + Success = OpenProcessToken(GetCurrentProcess(), + TOKEN_READ | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID, + &Token); + if (!Success) + { + ok(0, "OpenProcessToken() has failed to get the process' token (error code: %lu)!\n", GetLastError()); + return NULL; + } + + return Token; +} + +static +PTOKEN_DEFAULT_DACL +QueryOriginalDefaultDacl( + _In_ HANDLE Token, + _Out_ PULONG DaclLength) +{ + NTSTATUS Status; + PTOKEN_DEFAULT_DACL Dacl; + ULONG BufferLength; + + *DaclLength = 0; + + Status = NtQueryInformationToken(Token, + TokenDefaultDacl, + NULL, + 0, + &BufferLength); + if (!NT_SUCCESS(Status) && (Status != STATUS_BUFFER_TOO_SMALL)) + { + ok(0, "Failed to query buffer length, STATUS_BUFFER_TOO_SMALL has to be expected (Status code %lx)!\n", Status); + return NULL; + } + + Dacl = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength); + if (!Dacl) + { + ok(0, "Failed to allocate from heap for token default DACL (required buffer length %lu)!\n", BufferLength); + return NULL; + } + + Status = NtQueryInformationToken(Token, + TokenDefaultDacl, + Dacl, + BufferLength, + &BufferLength); + if (!NT_SUCCESS(Status)) + { + ok(0, "Failed to query default DACL (Status code %lx)!\n", Status); + RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl); + return NULL; + } + + *DaclLength = BufferLength; + return Dacl; +} + +static +PACL +CreateNewDefaultDacl( + _Out_ PULONG DaclLength) +{ + NTSTATUS Status; + PACL Dacl; + ULONG Length; + PSID LocalSystemSid; + static SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY}; + + *DaclLength = 0; + + Status = RtlAllocateAndInitializeSid(&NtAuthority, + 1, + SECURITY_LOCAL_SYSTEM_RID, + 0, 0, 0, 0, 0, 0, 0, + &LocalSystemSid); + if (!NT_SUCCESS(Status)) + { + ok(0, "Failed to allocate Local System SID (Status code %lx)!\n", Status); + return NULL; + } + + Length = sizeof(ACL) + + sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(LocalSystemSid); + + Dacl = RtlAllocateHeap(RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + Length); + if (!Dacl) + { + ok(0, "Failed to allocate from heap for DACL!\n"); + RtlFreeHeap(RtlGetProcessHeap(), 0, LocalSystemSid); + return NULL; + } + + Status = RtlCreateAcl(Dacl, + Length, + ACL_REVISION); + if (!NT_SUCCESS(Status)) + { + ok(0, "Failed to create ACL (Status code %lx)!\n", Status); + RtlFreeHeap(RtlGetProcessHeap(), 0, LocalSystemSid); + RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl); + return NULL; + } + + Status = RtlAddAccessAllowedAce(Dacl, + ACL_REVISION, + GENERIC_ALL, + LocalSystemSid); + if (!NT_SUCCESS(Status)) + { + ok(0, "Failed to add access allowed ACE (Status code %lx)!\n", Status); + RtlFreeHeap(RtlGetProcessHeap(), 0, LocalSystemSid); + RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl); + return NULL; + } + + *DaclLength = Length; + RtlFreeHeap(RtlGetProcessHeap(), 0, LocalSystemSid); + return Dacl; +} + +static +VOID +SetTokenDefaultDaclTests( + _In_ HANDLE Token) +{ + NTSTATUS Status; + PACL NewDacl; + TOKEN_DEFAULT_DACL NewDefaultDacl; + PTOKEN_DEFAULT_DACL DefaultDacl; + ULONG OriginalDaclLength, NewDaclLength; + + /* + * Query the original DACL of the token first, + * we don't want to leave the token tampered + * later on. + */ + DefaultDacl = QueryOriginalDefaultDacl(Token, &OriginalDaclLength); + if (!DefaultDacl) + { + ok(0, "Failed to query token's default DACL!\n"); + return; + } + + /* Allocate new DACL */ + NewDacl = CreateNewDefaultDacl(&NewDaclLength); + if (!DefaultDacl) + { + ok(0, "Failed to allocate buffer for new DACL!\n"); + RtlFreeHeap(RtlGetProcessHeap(), 0, DefaultDacl); + return; + } + + NewDefaultDacl.DefaultDacl = NewDacl; + + /* + * Set a new DACL for the token. + */ + Status = NtSetInformationToken(Token, + TokenDefaultDacl, + &NewDefaultDacl, + NewDaclLength); + ok_ntstatus(Status, STATUS_SUCCESS); + + /* Now set the original DACL */ + Status = NtSetInformationToken(Token, + TokenDefaultDacl, + DefaultDacl, + OriginalDaclLength); + ok_ntstatus(Status, STATUS_SUCCESS); + + RtlFreeHeap(RtlGetProcessHeap(), 0, DefaultDacl); + RtlFreeHeap(RtlGetProcessHeap(), 0, NewDacl); +} + +static +VOID +SetTokenSessionIdTests( + _In_ HANDLE Token) +{ + NTSTATUS Status; + ULONG SessionId = 1; + + /* + * We're not allowed to set a session ID + * because we don't have the TCB privilege. + */ + Status = NtSetInformationToken(Token, + TokenSessionId, + &SessionId, + sizeof(ULONG)); + ok_ntstatus(Status, STATUS_PRIVILEGE_NOT_HELD); +} + +START_TEST(NtSetInformationToken) +{ + NTSTATUS Status; + ULONG DummyReturnLength = 0; + HANDLE Token; + + /* Everything else is NULL */ + Status = NtSetInformationToken(NULL, + TokenOwner, + NULL, + 0); + ok_ntstatus(Status, STATUS_INVALID_HANDLE); + + /* We don't give a token */ + Status = NtSetInformationToken(NULL, + TokenOwner, + NULL, + DummyReturnLength); + ok_ntstatus(Status, STATUS_INVALID_HANDLE); + + Token = OpenCurrentToken(); + + /* We give a bogus token class */ + Status = NtSetInformationToken(Token, + 0xa0a, + NULL, + DummyReturnLength); + ok_ntstatus(Status, STATUS_INVALID_INFO_CLASS); + + /* Now perform tests for each class */ + SetTokenDefaultDaclTests(Token); + SetTokenSessionIdTests(Token); + + CloseHandle(Token); +} diff --git a/modules/rostests/apitests/ntdll/testlist.c b/modules/rostests/apitests/ntdll/testlist.c index aff61fa3091..3b88411cfb6 100644 --- a/modules/rostests/apitests/ntdll/testlist.c +++ b/modules/rostests/apitests/ntdll/testlist.c @@ -31,6 +31,7 @@ extern void func_NtProtectVirtualMemory(void); extern void func_NtQueryInformationFile(void); extern void func_NtQueryInformationProcess(void); extern void func_NtQueryInformationThread(void); +extern void func_NtQueryInformationToken(void); extern void func_NtQueryKey(void); extern void func_NtQuerySystemEnvironmentValue(void); extern void func_NtQuerySystemInformation(void); @@ -41,6 +42,7 @@ extern void func_NtSaveKey(void); extern void func_NtSetInformationFile(void); extern void func_NtSetInformationProcess(void); extern void func_NtSetInformationThread(void); +extern void func_NtSetInformationToken(void); extern void func_NtSetValueKey(void); extern void func_NtSetVolumeInformationFile(void); extern void func_NtSystemInformation(void); @@ -119,6 +121,7 @@ const struct test winetest_testlist[] = { "NtQueryInformationFile", func_NtQueryInformationFile }, { "NtQueryInformationProcess", func_NtQueryInformationProcess }, { "NtQueryInformationThread", func_NtQueryInformationThread }, + { "NtQueryInformationToken", func_NtQueryInformationToken }, { "NtQueryKey", func_NtQueryKey }, { "NtQuerySystemEnvironmentValue", func_NtQuerySystemEnvironmentValue }, { "NtQuerySystemInformation", func_NtQuerySystemInformation }, @@ -129,6 +132,7 @@ const struct test winetest_testlist[] = { "NtSetInformationFile", func_NtSetInformationFile }, { "NtSetInformationProcess", func_NtSetInformationProcess }, { "NtSetInformationThread", func_NtSetInformationThread }, + { "NtSetInformationToken", func_NtSetInformationToken }, { "NtSetValueKey", func_NtSetValueKey}, { "NtSetVolumeInformationFile", func_NtSetVolumeInformationFile }, { "NtSystemInformation", func_NtSystemInformation },