1 /*
2  * PROJECT:         ReactOS API tests
3  * LICENSE:         GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:         Tests for the NtDuplicateToken API
5  * COPYRIGHT:       Copyright 2021 George Bișoc <george.bisoc@reactos.org>
6  */
7 
8 #include "precomp.h"
9 
10 static
11 HANDLE
12 OpenTokenFromProcess(VOID)
13 {
14     BOOL Success;
15     HANDLE Token;
16 
17     Success = OpenProcessToken(GetCurrentProcess(),
18                                TOKEN_READ | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_DUPLICATE | TOKEN_QUERY,
19                                &Token);
20     if (!Success)
21     {
22         skip("OpenProcessToken() has failed to get the process' token (error code: %lu)!\n", GetLastError());
23         return NULL;
24     }
25 
26     return Token;
27 }
28 
29 static
30 VOID
31 DisablePrivilege(
32     _In_ HANDLE Token,
33     _In_ LPCWSTR PrivilegeName)
34 {
35     TOKEN_PRIVILEGES TokenPriv;
36     LUID PrivLuid;
37     BOOL Success;
38 
39     Success = LookupPrivilegeValueW(NULL, PrivilegeName, &PrivLuid);
40     if (!Success)
41     {
42         skip("LookupPrivilegeValueW() has failed to locate the privilege value (error code: %lu)!\n", GetLastError());
43         return;
44     }
45 
46     TokenPriv.PrivilegeCount = 1;
47     TokenPriv.Privileges[0].Luid = PrivLuid;
48     TokenPriv.Privileges[0].Attributes = 0;
49 
50     Success = AdjustTokenPrivileges(Token,
51                                     FALSE,
52                                     &TokenPriv,
53                                     0,
54                                     NULL,
55                                     NULL);
56     if (!Success)
57     {
58         skip("AdjustTokenPrivileges() has failed to adjust privileges of token (error code: %lu)!\n", GetLastError());
59         return;
60     }
61 }
62 
63 static
64 VOID
65 DuplicateTokenAsEffective(VOID)
66 {
67     NTSTATUS Status;
68     ULONG Size;
69     HANDLE TokenHandle;
70     HANDLE DuplicatedTokenHandle;
71     OBJECT_ATTRIBUTES ObjectAttributes;
72     PTOKEN_STATISTICS TokenStats;
73 
74     /* Initialize the object attributes for token duplication */
75     InitializeObjectAttributes(&ObjectAttributes,
76                                NULL,
77                                0,
78                                NULL,
79                                NULL);
80 
81     /* Get the token from process and begin the tests */
82     TokenHandle = OpenTokenFromProcess();
83 
84     /* We give a bogus invalid handle */
85     Status = NtDuplicateToken(NULL,
86                               0,
87                               NULL,
88                               TRUE,
89                               TokenPrimary,
90                               NULL);
91     ok_hex(Status, STATUS_ACCESS_VIOLATION);
92 
93     /*
94      * Disable a privilege, the impersonation privilege for example.
95      * Why we're doing this is because such privilege is enabled
96      * by default and we'd want to know what the kernel does
97      * at the moment of removing disabled privileges during making
98      * the token effective, with this potential privilege being
99      * disabled by ourselves.
100      */
101     DisablePrivilege(TokenHandle, L"SeImpersonatePrivilege");
102 
103     /* Query the total size of the token statistics structure */
104     Status = NtQueryInformationToken(TokenHandle, TokenStatistics, NULL, 0, &Size);
105     if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL)
106     {
107         skip("Failed to query the total size for token statistics structure! (Status -> 0x%lx)\n", Status);
108         return;
109     }
110 
111     /* Total size queried, time to allocate our buffer based on that size */
112     TokenStats = RtlAllocateHeap(RtlGetProcessHeap(), 0, Size);
113     if (TokenStats == NULL)
114     {
115         skip("Failed to allocate our token statistics buffer!\n");
116         return;
117     }
118 
119     /* Time to query our token statistics, prior duplicating the token as effective */
120     Status = NtQueryInformationToken(TokenHandle, TokenStatistics, TokenStats, Size, &Size);
121     if (!NT_SUCCESS(Status))
122     {
123         skip("Failed to query the token statistics! (Status -> 0x%lx)\n", Status);
124         return;
125     }
126 
127     trace("Number of privileges of regular token -- %lu\n", TokenStats->PrivilegeCount);
128     trace("Number of groups of regular token -- %lu\n", TokenStats->GroupCount);
129 
130     /* Duplicate the token as effective only */
131     Status = NtDuplicateToken(TokenHandle,
132                               0,
133                               &ObjectAttributes,
134                               TRUE,
135                               TokenPrimary,
136                               &DuplicatedTokenHandle);
137     ok_hex(Status, STATUS_SUCCESS);
138 
139     /*
140      * Query the token statistics again, but now this time of
141      * the duplicated effective token. On this moment this token
142      * should have the disabled privileges (including the one we
143      * disabled ourselves) removed as well as the disabled groups
144      * that the duplicated token includes, whatever that is.
145      */
146     Status = NtQueryInformationToken(DuplicatedTokenHandle, TokenStatistics, TokenStats, Size, &Size);
147     if (!NT_SUCCESS(Status))
148     {
149         skip("Failed to query the token statistics! (Status -> 0x%lx)\n", Status);
150         return;
151     }
152 
153     trace("Number of privileges of effective only token -- %lu\n", TokenStats->PrivilegeCount);
154     trace("Number of groups of effective only token -- %lu\n", TokenStats->GroupCount);
155 
156     /*
157      * We finished our tests, free the memory
158      * block and close the handles now.
159      */
160     RtlFreeHeap(RtlGetProcessHeap(), 0, TokenStats);
161     CloseHandle(TokenHandle),
162     CloseHandle(DuplicatedTokenHandle);
163 }
164 
165 START_TEST(NtDuplicateToken)
166 {
167     DuplicateTokenAsEffective();
168 }
169