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 NtQueryInformationToken API
5  * COPYRIGHT:       Copyright 2022 George Bișoc <george.bisoc@reactos.org>
6  */
7 
8 #include "precomp.h"
9 
10 static
11 HANDLE
12 OpenCurrentToken(VOID)
13 {
14     BOOL Success;
15     HANDLE Token;
16 
17     Success = OpenProcessToken(GetCurrentProcess(),
18                                TOKEN_READ | TOKEN_QUERY_SOURCE | TOKEN_DUPLICATE,
19                                &Token);
20     if (!Success)
21     {
22         ok(FALSE, "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 QueryTokenUserTests(
32     _In_ HANDLE Token)
33 {
34     NTSTATUS Status;
35     PTOKEN_USER UserToken;
36     ULONG BufferLength;
37     UNICODE_STRING SidString;
38 
39     /*
40      * Query the exact buffer length to hold
41      * our stuff, STATUS_BUFFER_TOO_SMALL must
42      * be expected here.
43      */
44     Status = NtQueryInformationToken(Token,
45                                      TokenUser,
46                                      NULL,
47                                      0,
48                                      &BufferLength);
49     ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL);
50 
51     /* Allocate the buffer based on the size we got */
52     UserToken = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
53     if (!UserToken)
54     {
55         ok(FALSE, "Failed to allocate from heap for token user (required buffer length %lu)!\n", BufferLength);
56         return;
57     }
58 
59     /* Now do the actual query */
60     Status = NtQueryInformationToken(Token,
61                                      TokenUser,
62                                      UserToken,
63                                      BufferLength,
64                                      &BufferLength);
65     ok_ntstatus(Status, STATUS_SUCCESS);
66 
67     RtlConvertSidToUnicodeString(&SidString, UserToken->User.Sid, TRUE);
68     trace("=============== TokenUser ===============\n");
69     trace("The SID of current token user is: %s\n", wine_dbgstr_w(SidString.Buffer));
70     trace("=========================================\n\n");
71     RtlFreeUnicodeString(&SidString);
72 
73     RtlFreeHeap(RtlGetProcessHeap(), 0, UserToken);
74 }
75 
76 static
77 VOID
78 QueryTokenGroupsTests(
79     _In_ HANDLE Token)
80 {
81     NTSTATUS Status;
82     PTOKEN_GROUPS Groups;
83     ULONG BufferLength;
84 
85     /*
86      * Query the exact buffer length to hold
87      * our stuff, STATUS_BUFFER_TOO_SMALL must
88      * be expected here.
89      */
90     Status = NtQueryInformationToken(Token,
91                                      TokenGroups,
92                                      NULL,
93                                      0,
94                                      &BufferLength);
95     ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL);
96 
97     /* Allocate the buffer based on the size we got */
98     Groups = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
99     if (!Groups)
100     {
101         ok(FALSE, "Failed to allocate from heap for token groups (required buffer length %lu)!\n", BufferLength);
102         return;
103     }
104 
105     /*
106      * Now do the actual query and validate the
107      * number of groups.
108      */
109     Status = NtQueryInformationToken(Token,
110                                      TokenGroups,
111                                      Groups,
112                                      BufferLength,
113                                      &BufferLength);
114     ok_ntstatus(Status, STATUS_SUCCESS);
115     ok(Groups->GroupCount == 10, "The number of groups must be 10 (current number %lu)!\n", Groups->GroupCount);
116 
117     RtlFreeHeap(RtlGetProcessHeap(), 0, Groups);
118 }
119 
120 static
121 VOID
122 QueryTokenPrivilegesTests(
123     _In_ HANDLE Token)
124 {
125     NTSTATUS Status;
126     PTOKEN_PRIVILEGES Privileges;
127     ULONG BufferLength;
128 
129     /*
130      * Query the exact buffer length to hold
131      * our stuff, STATUS_BUFFER_TOO_SMALL must
132      * be expected here.
133      */
134     Status = NtQueryInformationToken(Token,
135                                      TokenPrivileges,
136                                      NULL,
137                                      0,
138                                      &BufferLength);
139     ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL);
140 
141     /* Allocate the buffer based on the size we got */
142     Privileges = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
143     if (!Privileges)
144     {
145         ok(FALSE, "Failed to allocate from heap for token privileges (required buffer length %lu)!\n", BufferLength);
146         return;
147     }
148 
149     /*
150      * Now do the actual query and validate the
151      * number of privileges.
152      */
153     Status = NtQueryInformationToken(Token,
154                                      TokenPrivileges,
155                                      Privileges,
156                                      BufferLength,
157                                      &BufferLength);
158     ok_ntstatus(Status, STATUS_SUCCESS);
159     ok(Privileges->PrivilegeCount == 20, "The number of privileges must be 20 (current number %lu)!\n", Privileges->PrivilegeCount);
160 
161     RtlFreeHeap(RtlGetProcessHeap(), 0, Privileges);
162 }
163 
164 static
165 VOID
166 QueryTokenOwnerTests(
167     _In_ HANDLE Token)
168 {
169     NTSTATUS Status;
170     PTOKEN_OWNER Owner;
171     ULONG BufferLength;
172     UNICODE_STRING SidString;
173 
174     /*
175      * Query the exact buffer length to hold
176      * our stuff, STATUS_BUFFER_TOO_SMALL must
177      * be expected here.
178      */
179     Status = NtQueryInformationToken(Token,
180                                      TokenOwner,
181                                      NULL,
182                                      0,
183                                      &BufferLength);
184     ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL);
185 
186     /* Allocate the buffer based on the size we got */
187     Owner = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
188     if (!Owner)
189     {
190         ok(FALSE, "Failed to allocate from heap for token owner (required buffer length %lu)!\n", BufferLength);
191         return;
192     }
193 
194     /*
195      * Now do the actual query and validate the
196      * token owner (must be the local admin).
197      */
198     Status = NtQueryInformationToken(Token,
199                                      TokenOwner,
200                                      Owner,
201                                      BufferLength,
202                                      &BufferLength);
203     ok_ntstatus(Status, STATUS_SUCCESS);
204 
205     RtlConvertSidToUnicodeString(&SidString, Owner->Owner, TRUE);
206     ok_wstr(SidString.Buffer, L"S-1-5-32-544");
207     RtlFreeUnicodeString(&SidString);
208 
209     RtlFreeHeap(RtlGetProcessHeap(), 0, Owner);
210 }
211 
212 static
213 VOID
214 QueryTokenPrimaryGroupTests(
215     _In_ HANDLE Token)
216 {
217     NTSTATUS Status;
218     PTOKEN_PRIMARY_GROUP PrimaryGroup;
219     ULONG BufferLength;
220     UNICODE_STRING SidString;
221 
222     /*
223      * Query the exact buffer length to hold
224      * our stuff, STATUS_BUFFER_TOO_SMALL must
225      * be expected here.
226      */
227     Status = NtQueryInformationToken(Token,
228                                      TokenPrimaryGroup,
229                                      NULL,
230                                      0,
231                                      &BufferLength);
232     ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL);
233 
234     /* Allocate the buffer based on the size we got */
235     PrimaryGroup = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
236     if (!PrimaryGroup)
237     {
238         ok(FALSE, "Failed to allocate from heap for token primary group (required buffer length %lu)!\n", BufferLength);
239         return;
240     }
241 
242     /* Now do the actual query */
243     Status = NtQueryInformationToken(Token,
244                                      TokenPrimaryGroup,
245                                      PrimaryGroup,
246                                      BufferLength,
247                                      &BufferLength);
248     ok_ntstatus(Status, STATUS_SUCCESS);
249 
250     RtlConvertSidToUnicodeString(&SidString, PrimaryGroup->PrimaryGroup, TRUE);
251     trace("=============== TokenPrimaryGroup ===============\n");
252     trace("The primary group SID of current token is: %s\n", wine_dbgstr_w(SidString.Buffer));
253     trace("=========================================\n\n");
254     RtlFreeUnicodeString(&SidString);
255 
256     RtlFreeHeap(RtlGetProcessHeap(), 0, PrimaryGroup);
257 }
258 
259 static
260 VOID
261 QueryTokenDefaultDaclTests(
262     _In_ HANDLE Token)
263 {
264     NTSTATUS Status;
265     PTOKEN_DEFAULT_DACL Dacl;
266     ULONG BufferLength;
267 
268     /*
269      * Query the exact buffer length to hold
270      * our stuff, STATUS_BUFFER_TOO_SMALL must
271      * be expected here.
272      */
273     Status = NtQueryInformationToken(Token,
274                                      TokenDefaultDacl,
275                                      NULL,
276                                      0,
277                                      &BufferLength);
278     ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL);
279 
280     /* Allocate the buffer based on the size we got */
281     Dacl = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
282     if (!Dacl)
283     {
284         ok(FALSE, "Failed to allocate from heap for token default DACL (required buffer length %lu)!\n", BufferLength);
285         return;
286     }
287 
288     /*
289      * Now do the actual query and validate the
290      * ACL revision and number count of ACEs.
291      */
292     Status = NtQueryInformationToken(Token,
293                                      TokenDefaultDacl,
294                                      Dacl,
295                                      BufferLength,
296                                      &BufferLength);
297     ok_ntstatus(Status, STATUS_SUCCESS);
298     ok(Dacl->DefaultDacl->AclRevision == 2, "The ACL revision of token default DACL must be 2 (current revision %u)!\n", Dacl->DefaultDacl->AclRevision);
299     ok(Dacl->DefaultDacl->AceCount == 2, "The ACL's ACE count must be 2 (current ACE count %u)!\n", Dacl->DefaultDacl->AceCount);
300 
301     RtlFreeHeap(RtlGetProcessHeap(), 0, Dacl);
302 }
303 
304 static
305 VOID
306 QueryTokenSourceTests(
307     _In_ HANDLE Token)
308 {
309     NTSTATUS Status;
310     PTOKEN_SOURCE Source;
311     ULONG BufferLength;
312     CHAR SourceName[8];
313 
314     /*
315      * Query the exact buffer length to hold
316      * our stuff, STATUS_BUFFER_TOO_SMALL must
317      * be expected here.
318      */
319     Status = NtQueryInformationToken(Token,
320                                      TokenSource,
321                                      NULL,
322                                      0,
323                                      &BufferLength);
324     ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL);
325 
326     /* Allocate the buffer based on the size we got */
327     Source = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
328     if (!Source)
329     {
330         ok(FALSE, "Failed to allocate from heap for token source (required buffer length %lu)!\n", BufferLength);
331         return;
332     }
333 
334     /* Now do the actual query */
335     Status = NtQueryInformationToken(Token,
336                                      TokenSource,
337                                      Source,
338                                      BufferLength,
339                                      &BufferLength);
340     ok_ntstatus(Status, STATUS_SUCCESS);
341 
342     /*
343      * Subtract the source name from the queried buffer
344      * and compare it. The source name in question must be
345      * "User32" as the primary token of the current calling
346      * process is generated when the user has successfully
347      * logged in and he's into the desktop.
348      */
349     SourceName[0] = Source->SourceName[0];
350     SourceName[1] = Source->SourceName[1];
351     SourceName[2] = Source->SourceName[2];
352     SourceName[3] = Source->SourceName[3];
353     SourceName[4] = Source->SourceName[4];
354     SourceName[5] = Source->SourceName[5];
355     SourceName[6] = '\0';
356     ok_str(SourceName, "User32");
357 
358     RtlFreeHeap(RtlGetProcessHeap(), 0, Source);
359 }
360 
361 static
362 VOID
363 QueryTokenTypeTests(
364     _In_ HANDLE Token)
365 {
366     NTSTATUS Status;
367     TOKEN_TYPE Type;
368     ULONG BufferLength;
369 
370     /*
371      * Query the token type. The token of the
372      * current calling process must be primary
373      * since we aren't impersonating the security
374      * context of a client.
375      */
376     Status = NtQueryInformationToken(Token,
377                                      TokenType,
378                                      &Type,
379                                      sizeof(TOKEN_TYPE),
380                                      &BufferLength);
381     ok_ntstatus(Status, STATUS_SUCCESS);
382     ok(Type == TokenPrimary, "The current token is not primary!\n");
383 }
384 
385 static
386 VOID
387 QueryTokenImpersonationTests(
388     _In_ HANDLE Token)
389 {
390     NTSTATUS Status;
391     SECURITY_IMPERSONATION_LEVEL Level;
392     ULONG BufferLength;
393     HANDLE DupToken;
394     OBJECT_ATTRIBUTES ObjectAttributes;
395 
396     /*
397      * Windows throws STATUS_INVALID_INFO_CLASS here
398      * because one cannot simply query the impersonation
399      * level of a primary token.
400      */
401     Status = NtQueryInformationToken(Token,
402                                      TokenImpersonationLevel,
403                                      &Level,
404                                      sizeof(SECURITY_IMPERSONATION_LEVEL),
405                                      &BufferLength);
406     ok_ntstatus(Status, STATUS_INVALID_INFO_CLASS);
407 
408     /*
409      * Initialize the object attribute and duplicate
410      * the token into an actual impersonation one.
411      */
412     InitializeObjectAttributes(&ObjectAttributes,
413                                NULL,
414                                0,
415                                NULL,
416                                NULL);
417 
418     Status = NtDuplicateToken(Token,
419                               TOKEN_QUERY,
420                               &ObjectAttributes,
421                               FALSE,
422                               TokenImpersonation,
423                               &DupToken);
424     if (!NT_SUCCESS(Status))
425     {
426         ok(FALSE, "Failed to duplicate token (Status code %lx)!\n", Status);
427         return;
428     }
429 
430     /* Now do the actual query */
431     Status = NtQueryInformationToken(DupToken,
432                                      TokenImpersonationLevel,
433                                      &Level,
434                                      sizeof(SECURITY_IMPERSONATION_LEVEL),
435                                      &BufferLength);
436     ok_ntstatus(Status, STATUS_SUCCESS);
437     ok(Level == SecurityAnonymous, "The current token impersonation level is not anonymous!\n");
438     NtClose(DupToken);
439 }
440 
441 static
442 VOID
443 QueryTokenStatisticsTests(
444     _In_ HANDLE Token)
445 {
446     NTSTATUS Status;
447     PTOKEN_STATISTICS Statistics;
448     ULONG BufferLength;
449 
450     /*
451      * Query the exact buffer length to hold
452      * our stuff, STATUS_BUFFER_TOO_SMALL must
453      * be expected here.
454      */
455     Status = NtQueryInformationToken(Token,
456                                      TokenStatistics,
457                                      NULL,
458                                      0,
459                                      &BufferLength);
460     ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL);
461 
462     /* Allocate the buffer based on the size we got */
463     Statistics = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
464     if (!Statistics)
465     {
466         ok(FALSE, "Failed to allocate from heap for token statistics (required buffer length %lu)!\n", BufferLength);
467         return;
468     }
469 
470     /* Do the actual query */
471     Status = NtQueryInformationToken(Token,
472                                      TokenStatistics,
473                                      Statistics,
474                                      BufferLength,
475                                      &BufferLength);
476     ok_ntstatus(Status, STATUS_SUCCESS);
477 
478     trace("=============== TokenStatistics ===============\n");
479     trace("Token ID: %lu %lu\n", Statistics->TokenId.LowPart, Statistics->TokenId.HighPart);
480     trace("Authentication ID: %lu %lu\n", Statistics->AuthenticationId.LowPart, Statistics->AuthenticationId.HighPart);
481     trace("Dynamic Charged: %lu\n", Statistics->DynamicCharged);
482     trace("Dynamic Available: %lu\n", Statistics->DynamicAvailable);
483     trace("Modified ID: %lu %lu\n", Statistics->ModifiedId.LowPart, Statistics->ModifiedId.HighPart);
484     trace("=========================================\n\n");
485 
486     RtlFreeHeap(RtlGetProcessHeap(), 0, Statistics);
487 }
488 
489 static
490 VOID
491 QueryTokenPrivilegesAndGroupsTests(
492     _In_ HANDLE Token)
493 {
494     NTSTATUS Status;
495     PTOKEN_GROUPS_AND_PRIVILEGES PrivsAndGroups;
496     TOKEN_GROUPS SidToRestrict;
497     HANDLE FilteredToken;
498     PSID WorldSid;
499     ULONG BufferLength;
500     static SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY};
501 
502     /*
503      * Create a World SID and filter the token
504      * by adding a restricted SID.
505      */
506     Status = RtlAllocateAndInitializeSid(&WorldAuthority,
507                                          1,
508                                          SECURITY_WORLD_RID,
509                                          0, 0, 0, 0, 0, 0, 0,
510                                          &WorldSid);
511     if (!NT_SUCCESS(Status))
512     {
513         ok(FALSE, "Failed to allocate World SID (Status code %lx)!\n", Status);
514         return;
515     }
516 
517     SidToRestrict.GroupCount = 1;
518     SidToRestrict.Groups[0].Attributes = 0;
519     SidToRestrict.Groups[0].Sid = WorldSid;
520 
521     Status = NtFilterToken(Token,
522                            0,
523                            NULL,
524                            NULL,
525                            &SidToRestrict,
526                            &FilteredToken);
527     if (!NT_SUCCESS(Status))
528     {
529         ok(FALSE, "Failed to filter the current token (Status code %lx)!\n", Status);
530         RtlFreeHeap(RtlGetProcessHeap(), 0, WorldSid);
531         return;
532     }
533 
534     /*
535      * Query the exact buffer length to hold
536      * our stuff, STATUS_BUFFER_TOO_SMALL must
537      * be expected here.
538      */
539     Status = NtQueryInformationToken(FilteredToken,
540                                      TokenGroupsAndPrivileges,
541                                      NULL,
542                                      0,
543                                      &BufferLength);
544     ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL);
545 
546     /* Allocate the buffer based on the size we got */
547     PrivsAndGroups = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
548     if (!PrivsAndGroups)
549     {
550         ok(FALSE, "Failed to allocate from heap for token privileges and groups (required buffer length %lu)!\n", BufferLength);
551         RtlFreeHeap(RtlGetProcessHeap(), 0, WorldSid);
552         NtClose(FilteredToken);
553         return;
554     }
555 
556     /* Do the actual query */
557     Status = NtQueryInformationToken(FilteredToken,
558                                      TokenGroupsAndPrivileges,
559                                      PrivsAndGroups,
560                                      BufferLength,
561                                      &BufferLength);
562     ok_ntstatus(Status, STATUS_SUCCESS);
563 
564     trace("=============== TokenGroupsAndPrivileges ===============\n");
565     trace("SID count: %lu\n", PrivsAndGroups->SidCount);
566     trace("SID length: %lu\n", PrivsAndGroups->SidLength);
567     trace("Restricted SID count: %lu\n", PrivsAndGroups->RestrictedSidCount);
568     trace("Restricted SID length: %lu\n", PrivsAndGroups->RestrictedSidLength);
569     trace("Privilege count: %lu\n", PrivsAndGroups->PrivilegeCount);
570     trace("Privilege length: %lu\n", PrivsAndGroups->PrivilegeLength);
571     trace("Authentication ID: %lu %lu\n", PrivsAndGroups->AuthenticationId.LowPart, PrivsAndGroups->AuthenticationId.HighPart);
572     trace("=========================================\n\n");
573 
574     RtlFreeHeap(RtlGetProcessHeap(), 0, PrivsAndGroups);
575     RtlFreeHeap(RtlGetProcessHeap(), 0, WorldSid);
576     NtClose(FilteredToken);
577 }
578 
579 static
580 VOID
581 QueryTokenRestrictedSidsTest(
582     _In_ HANDLE Token)
583 {
584     NTSTATUS Status;
585     PTOKEN_GROUPS RestrictedGroups;
586     TOKEN_GROUPS SidToRestrict;
587     ULONG BufferLength;
588     HANDLE FilteredToken;
589     PSID WorldSid;
590     static SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY};
591 
592     /*
593      * Query the exact buffer length to hold
594      * our stuff, STATUS_BUFFER_TOO_SMALL must
595      * be expected here.
596      */
597     Status = NtQueryInformationToken(Token,
598                                      TokenRestrictedSids,
599                                      NULL,
600                                      0,
601                                      &BufferLength);
602     ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL);
603 
604     /* Allocate the buffer based on the size we got */
605     RestrictedGroups = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
606     if (!RestrictedGroups)
607     {
608         ok(FALSE, "Failed to allocate from heap for restricted SIDs (required buffer length %lu)!\n", BufferLength);
609         return;
610     }
611 
612     /*
613      * Query the number of restricted SIDs. Originally the token
614      * doesn't have any restricted SIDs inserted.
615      */
616     Status = NtQueryInformationToken(Token,
617                                      TokenRestrictedSids,
618                                      RestrictedGroups,
619                                      BufferLength,
620                                      &BufferLength);
621     ok_ntstatus(Status, STATUS_SUCCESS);
622     ok(RestrictedGroups->GroupCount == 0, "There mustn't be any restricted SIDs before filtering (number of restricted SIDs %lu)!\n", RestrictedGroups->GroupCount);
623 
624     RtlFreeHeap(RtlGetProcessHeap(), 0, RestrictedGroups);
625     RestrictedGroups = NULL;
626 
627     Status = RtlAllocateAndInitializeSid(&WorldAuthority,
628                                          1,
629                                          SECURITY_WORLD_RID,
630                                          0, 0, 0, 0, 0, 0, 0,
631                                          &WorldSid);
632     if (!NT_SUCCESS(Status))
633     {
634         ok(FALSE, "Failed to allocate World SID (Status code %lx)!\n", Status);
635         return;
636     }
637 
638     SidToRestrict.GroupCount = 1;
639     SidToRestrict.Groups[0].Attributes = 0;
640     SidToRestrict.Groups[0].Sid = WorldSid;
641 
642     Status = NtFilterToken(Token,
643                            0,
644                            NULL,
645                            NULL,
646                            &SidToRestrict,
647                            &FilteredToken);
648     if (!NT_SUCCESS(Status))
649     {
650         ok(FALSE, "Failed to filter the current token (Status code %lx)!\n", Status);
651         RtlFreeHeap(RtlGetProcessHeap(), 0, WorldSid);
652         return;
653     }
654 
655     Status = NtQueryInformationToken(FilteredToken,
656                                      TokenRestrictedSids,
657                                      NULL,
658                                      0,
659                                      &BufferLength);
660     ok_ntstatus(Status, STATUS_BUFFER_TOO_SMALL);
661 
662     RestrictedGroups = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
663     if (!RestrictedGroups)
664     {
665         ok(FALSE, "Failed to allocate from heap for restricted SIDs (required buffer length %lu)!\n", BufferLength);
666         RtlFreeHeap(RtlGetProcessHeap(), 0, WorldSid);
667         return;
668     }
669 
670     /*
671      * Do a query again, this time we must have a
672      * restricted SID inserted into the token.
673      */
674     Status = NtQueryInformationToken(FilteredToken,
675                                      TokenRestrictedSids,
676                                      RestrictedGroups,
677                                      BufferLength,
678                                      &BufferLength);
679     ok_ntstatus(Status, STATUS_SUCCESS);
680     ok(RestrictedGroups->GroupCount == 1, "There must be only one restricted SID added in token (number of restricted SIDs %lu)!\n", RestrictedGroups->GroupCount);
681 
682     RtlFreeHeap(RtlGetProcessHeap(), 0, RestrictedGroups);
683     RtlFreeHeap(RtlGetProcessHeap(), 0, WorldSid);
684     NtClose(FilteredToken);
685 }
686 
687 static
688 VOID
689 QueryTokenSessionIdTests(
690     _In_ HANDLE Token)
691 {
692     NTSTATUS Status;
693     ULONG SessionId;
694     ULONG BufferLength;
695 
696     /*
697      * Query the session ID. Generally the current
698      * process token is not under any terminal service
699      * so the ID must be 0.
700      */
701     Status = NtQueryInformationToken(Token,
702                                      TokenSessionId,
703                                      &SessionId,
704                                      sizeof(ULONG),
705                                      &BufferLength);
706     ok_ntstatus(Status, STATUS_SUCCESS);
707     ok(SessionId == 0, "The session ID of current token must be 0 (current session %lu)!\n", SessionId);
708 }
709 
710 static
711 VOID
712 QueryTokenIsSandboxInert(
713     _In_ HANDLE Token)
714 {
715     NTSTATUS Status;
716     ULONG IsTokenInert;
717     ULONG BufferLength;
718     HANDLE FilteredToken;
719 
720     /*
721      * Query the sandbox inert token information,
722      * it must not be inert.
723      */
724     Status = NtQueryInformationToken(Token,
725                                      TokenSandBoxInert,
726                                      &IsTokenInert,
727                                      sizeof(ULONG),
728                                      &BufferLength);
729     ok_ntstatus(Status, STATUS_SUCCESS);
730     ok(IsTokenInert == FALSE, "The token must not be a sandbox inert one!\n");
731 
732     /*
733      * Try to turn the token into an inert
734      * one by filtering it.
735      */
736     Status = NtFilterToken(Token,
737                            SANDBOX_INERT,
738                            NULL,
739                            NULL,
740                            NULL,
741                            &FilteredToken);
742     if (!NT_SUCCESS(Status))
743     {
744         ok(FALSE, "Failed to filter the current token (Status code %lx)!\n", Status);
745         return;
746     }
747 
748     /*
749      * Now do a query again, this time
750      * the token should be inert.
751      */
752     Status = NtQueryInformationToken(FilteredToken,
753                                      TokenSandBoxInert,
754                                      &IsTokenInert,
755                                      sizeof(ULONG),
756                                      &BufferLength);
757     ok_ntstatus(Status, STATUS_SUCCESS);
758     ok(IsTokenInert == TRUE, "The token must be a sandbox inert one after filtering!\n");
759 
760     NtClose(FilteredToken);
761 }
762 
763 static
764 VOID
765 QueryTokenOriginTests(
766     _In_ HANDLE Token)
767 {
768     NTSTATUS Status;
769     TOKEN_ORIGIN Origin;
770     ULONG BufferLength;
771 
772     /* Query the token origin */
773     Status = NtQueryInformationToken(Token,
774                                      TokenOrigin,
775                                      &Origin,
776                                      sizeof(TOKEN_ORIGIN),
777                                      &BufferLength);
778     ok_ntstatus(Status, STATUS_SUCCESS);
779     ok(Origin.OriginatingLogonSession.LowPart == 0x3e7, "The LowPart field of the originating logon session must be SYSTEM_LUID (current value %lu)!\n",
780        Origin.OriginatingLogonSession.LowPart);
781     ok(Origin.OriginatingLogonSession.HighPart == 0x0, "The HighPart field of the logon session must be 0 (current value %lu)!\n",
782        Origin.OriginatingLogonSession.HighPart);
783 }
784 
785 START_TEST(NtQueryInformationToken)
786 {
787     NTSTATUS Status;
788     HANDLE Token;
789     PVOID Dummy;
790     ULONG DummyReturnLength;
791 
792     /* ReturnLength is NULL */
793     Status = NtQueryInformationToken(NULL,
794                                      TokenUser,
795                                      NULL,
796                                      0,
797                                      NULL);
798     ok_ntstatus(Status, STATUS_ACCESS_VIOLATION);
799 
800     /* We don't give any token here */
801     Status = NtQueryInformationToken(NULL,
802                                      TokenUser,
803                                      &Dummy,
804                                      0,
805                                      &DummyReturnLength);
806     ok_ntstatus(Status, STATUS_INVALID_HANDLE);
807 
808     Token = OpenCurrentToken();
809 
810     /* Class 0 is unused on Windows */
811     Status = NtQueryInformationToken(Token,
812                                      0,
813                                      &Dummy,
814                                      0,
815                                      &DummyReturnLength);
816     ok_ntstatus(Status, STATUS_INVALID_INFO_CLASS);
817 
818     /* We give a bogus info class */
819     Status = NtQueryInformationToken(Token,
820                                      0xa0a,
821                                      &Dummy,
822                                      0,
823                                      &DummyReturnLength);
824     ok_ntstatus(Status, STATUS_INVALID_INFO_CLASS);
825 
826     /* Now perform tests for each class */
827     QueryTokenUserTests(Token);
828     QueryTokenGroupsTests(Token);
829     QueryTokenPrivilegesTests(Token);
830     QueryTokenOwnerTests(Token);
831     QueryTokenPrimaryGroupTests(Token);
832     QueryTokenDefaultDaclTests(Token);
833     QueryTokenSourceTests(Token);
834     QueryTokenTypeTests(Token);
835     QueryTokenImpersonationTests(Token);
836     QueryTokenStatisticsTests(Token);
837     QueryTokenPrivilegesAndGroupsTests(Token);
838     QueryTokenRestrictedSidsTest(Token);
839     QueryTokenSessionIdTests(Token);
840     QueryTokenIsSandboxInert(Token);
841     QueryTokenOriginTests(Token);
842 
843     NtClose(Token);
844 }
845