https://git.reactos.org/?p=reactos.git;a=commitdiff;h=bac1bce60549b0a1e5dc5b77c53ea9d2cc1e430e

commit bac1bce60549b0a1e5dc5b77c53ea9d2cc1e430e
Author:     George Bișoc <[email protected]>
AuthorDate: Sun Jun 5 23:24:06 2022 +0200
Commit:     George Bișoc <[email protected]>
CommitDate: Thu Jun 9 19:20:31 2022 +0200

    [NTDLL_APITEST] Write tests for NtQueryInformationToken and 
NtSetInformationToken
---
 modules/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(+)

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 <[email protected]>
+ */
+
+#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 <[email protected]>
+ */
+
+#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 },

Reply via email to