xref: /reactos/ntoskrnl/se/token.c (revision 40ee59d6)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:         Security token implementation support
5  * COPYRIGHT:       Copyright David Welch <welch@cwcom.net>
6  *                  Copyright 2021 George Bișoc <george.bisoc@reactos.org>
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 #include <ntlsa.h>
16 
17 /* GLOBALS ********************************************************************/
18 
19 POBJECT_TYPE SeTokenObjectType = NULL;
20 
21 TOKEN_SOURCE SeSystemTokenSource = {"*SYSTEM*", {0}};
22 LUID SeSystemAuthenticationId = SYSTEM_LUID;
23 LUID SeAnonymousAuthenticationId = ANONYMOUS_LOGON_LUID;
24 
25 static GENERIC_MAPPING SepTokenMapping = {
26     TOKEN_READ,
27     TOKEN_WRITE,
28     TOKEN_EXECUTE,
29     TOKEN_ALL_ACCESS
30 };
31 
32 static const INFORMATION_CLASS_INFO SeTokenInformationClass[] = {
33 
34     /* Class 0 not used, blame MS! */
35     IQS_NONE,
36 
37     /* TokenUser */
38     IQS_SAME(TOKEN_USER, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
39     /* TokenGroups */
40     IQS_SAME(TOKEN_GROUPS, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
41     /* TokenPrivileges */
42     IQS_SAME(TOKEN_PRIVILEGES, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
43     /* TokenOwner */
44     IQS_SAME(TOKEN_OWNER, ULONG, ICIF_QUERY | ICIF_SET | ICIF_SIZE_VARIABLE),
45     /* TokenPrimaryGroup */
46     IQS_SAME(TOKEN_PRIMARY_GROUP, ULONG, ICIF_QUERY | ICIF_SET | ICIF_SIZE_VARIABLE),
47     /* TokenDefaultDacl */
48     IQS_SAME(TOKEN_DEFAULT_DACL, ULONG, ICIF_QUERY | ICIF_SET | ICIF_SIZE_VARIABLE),
49     /* TokenSource */
50     IQS_SAME(TOKEN_SOURCE, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
51     /* TokenType */
52     IQS_SAME(TOKEN_TYPE, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
53     /* TokenImpersonationLevel */
54     IQS_SAME(SECURITY_IMPERSONATION_LEVEL, ULONG, ICIF_QUERY),
55     /* TokenStatistics */
56     IQS_SAME(TOKEN_STATISTICS, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
57     /* TokenRestrictedSids */
58     IQS_SAME(TOKEN_GROUPS, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
59     /* TokenSessionId */
60     IQS_SAME(ULONG, ULONG, ICIF_QUERY | ICIF_SET),
61     /* TokenGroupsAndPrivileges */
62     IQS_SAME(TOKEN_GROUPS_AND_PRIVILEGES, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
63     /* TokenSessionReference */
64     IQS_SAME(ULONG, ULONG, ICIF_SET),
65     /* TokenSandBoxInert */
66     IQS_SAME(ULONG, ULONG, ICIF_QUERY),
67     /* TokenAuditPolicy */
68     IQS_SAME(TOKEN_AUDIT_POLICY_INFORMATION, ULONG, ICIF_SET | ICIF_SET_SIZE_VARIABLE),
69     /* TokenOrigin */
70     IQS_SAME(TOKEN_ORIGIN, ULONG, ICIF_QUERY | ICIF_SET),
71 };
72 
73 /* FUNCTIONS *****************************************************************/
74 
75 /**
76  * @brief
77  * Creates a lock for the token.
78  *
79  * @param[in,out] Token
80  * A token which lock has to be created.
81  *
82  * @return
83  * STATUS_SUCCESS if the pool allocation and resource initialisation have
84  * completed successfully, otherwise STATUS_INSUFFICIENT_RESOURCES on a
85  * pool allocation failure.
86  */
87 static
88 NTSTATUS
89 SepCreateTokenLock(
90     _Inout_ PTOKEN Token)
91 {
92     PAGED_CODE();
93 
94     Token->TokenLock = ExAllocatePoolWithTag(NonPagedPool,
95                                              sizeof(ERESOURCE),
96                                              TAG_SE_TOKEN_LOCK);
97     if (Token->TokenLock == NULL)
98     {
99         DPRINT1("SepCreateTokenLock(): Failed to allocate memory!\n");
100         return STATUS_INSUFFICIENT_RESOURCES;
101     }
102 
103     ExInitializeResourceLite(Token->TokenLock);
104     return STATUS_SUCCESS;
105 }
106 
107 /**
108  * @brief
109  * Deletes a lock of a token.
110  *
111  * @param[in,out] Token
112  * A token which contains the lock.
113  *
114  * @return
115  * Nothing.
116  */
117 static
118 VOID
119 SepDeleteTokenLock(
120     _Inout_ PTOKEN Token)
121 {
122     PAGED_CODE();
123 
124     ExDeleteResourceLite(Token->TokenLock);
125     ExFreePoolWithTag(Token->TokenLock, TAG_SE_TOKEN_LOCK);
126 }
127 
128 /**
129  * @brief
130  * Compares the elements of SID arrays provided by tokens.
131  * The elements that are being compared for equality are
132  * the SIDs and their attributes.
133  *
134  * @param[in] SidArrayToken1
135  * SID array from the first token.
136  *
137  * @param[in] CountSidArray1
138  * SID count array from the first token.
139  *
140  * @param[in] SidArrayToken2
141  * SID array from the second token.
142  *
143  * @param[in] CountSidArray2
144  * SID count array from the second token.
145  *
146  * @return
147  * Returns TRUE if the elements match from either arrays,
148  * FALSE otherwise.
149  */
150 static
151 BOOLEAN
152 SepCompareSidAndAttributesFromTokens(
153     _In_ PSID_AND_ATTRIBUTES SidArrayToken1,
154     _In_ ULONG CountSidArray1,
155     _In_ PSID_AND_ATTRIBUTES SidArrayToken2,
156     _In_ ULONG CountSidArray2)
157 {
158     ULONG FirstCount, SecondCount;
159     PSID_AND_ATTRIBUTES FirstSidArray, SecondSidArray;
160     PAGED_CODE();
161 
162     /* Bail out if index counters provided are not equal */
163     if (CountSidArray1 != CountSidArray2)
164     {
165         DPRINT("SepCompareSidAndAttributesFromTokens(): Index counters are not the same!\n");
166         return FALSE;
167     }
168 
169     /* Loop over the SID arrays and compare them */
170     for (FirstCount = 0; FirstCount < CountSidArray1; FirstCount++)
171     {
172         for (SecondCount = 0; SecondCount < CountSidArray2; SecondCount++)
173         {
174             FirstSidArray = &SidArrayToken1[FirstCount];
175             SecondSidArray = &SidArrayToken2[SecondCount];
176 
177             if (RtlEqualSid(FirstSidArray->Sid, SecondSidArray->Sid) &&
178                 FirstSidArray->Attributes == SecondSidArray->Attributes)
179             {
180                 break;
181             }
182         }
183 
184         /* We've exhausted the array of the second token without finding this one */
185         if (SecondCount == CountSidArray2)
186         {
187             DPRINT("SepCompareSidAndAttributesFromTokens(): No matching elements could be found in either token!\n");
188             return FALSE;
189         }
190     }
191 
192     return TRUE;
193 }
194 
195 /**
196  * @brief
197  * Compares the elements of privilege arrays provided by tokens.
198  * The elements that are being compared for equality are
199  * the privileges and their attributes.
200  *
201  * @param[in] PrivArrayToken1
202  * Privilege array from the first token.
203  *
204  * @param[in] CountPrivArray1
205  * Privilege count array from the first token.
206  *
207  * @param[in] PrivArrayToken2
208  * Privilege array from the second token.
209  *
210  * @param[in] CountPrivArray2
211  * Privilege count array from the second token.
212  *
213  * @return
214  * Returns TRUE if the elements match from either arrays,
215  * FALSE otherwise.
216  */
217 static
218 BOOLEAN
219 SepComparePrivilegeAndAttributesFromTokens(
220     _In_ PLUID_AND_ATTRIBUTES PrivArrayToken1,
221     _In_ ULONG CountPrivArray1,
222     _In_ PLUID_AND_ATTRIBUTES PrivArrayToken2,
223     _In_ ULONG CountPrivArray2)
224 {
225     ULONG FirstCount, SecondCount;
226     PLUID_AND_ATTRIBUTES FirstPrivArray, SecondPrivArray;
227     PAGED_CODE();
228 
229     /* Bail out if index counters provided are not equal */
230     if (CountPrivArray1 != CountPrivArray2)
231     {
232         DPRINT("SepComparePrivilegeAndAttributesFromTokens(): Index counters are not the same!\n");
233         return FALSE;
234     }
235 
236     /* Loop over the privilege arrays and compare them */
237     for (FirstCount = 0; FirstCount < CountPrivArray1; FirstCount++)
238     {
239         for (SecondCount = 0; SecondCount < CountPrivArray2; SecondCount++)
240         {
241             FirstPrivArray = &PrivArrayToken1[FirstCount];
242             SecondPrivArray = &PrivArrayToken2[SecondCount];
243 
244             if (RtlEqualLuid(&FirstPrivArray->Luid, &SecondPrivArray->Luid) &&
245                 FirstPrivArray->Attributes == SecondPrivArray->Attributes)
246             {
247                 break;
248             }
249         }
250 
251         /* We've exhausted the array of the second token without finding this one */
252         if (SecondCount == CountPrivArray2)
253         {
254             DPRINT("SepComparePrivilegeAndAttributesFromTokens(): No matching elements could be found in either token!\n");
255             return FALSE;
256         }
257     }
258 
259     return TRUE;
260 }
261 
262 /**
263  * @brief
264  * Compares tokens if they're equal based on all the following properties. If all
265  * of the said conditions are met then the tokens are deemed as equal.
266  *
267  * - Every SID that is present in either token is also present in the other one.
268  * - Both or none of the tokens are restricted.
269  * - If both tokens are restricted, every SID that is restricted in either token is
270  *   also restricted in the other one.
271  * - Every privilege present in either token is also present in the other one.
272  *
273  * @param[in] FirstToken
274  * The first token.
275  *
276  * @param[in] SecondToken
277  * The second token.
278  *
279  * @param[out] Equal
280  * The retrieved value which determines if the tokens are
281  * equal or not.
282  *
283  * @return
284  * Returns STATUS_SUCCESS.
285  */
286 static
287 NTSTATUS
288 SepCompareTokens(
289     _In_ PTOKEN FirstToken,
290     _In_ PTOKEN SecondToken,
291     _Out_ PBOOLEAN Equal)
292 {
293     BOOLEAN Restricted, IsEqual = FALSE;
294     PAGED_CODE();
295 
296     ASSERT(FirstToken != SecondToken);
297 
298     /* Lock the tokens */
299     SepAcquireTokenLockShared(FirstToken);
300     SepAcquireTokenLockShared(SecondToken);
301 
302     /* Check if every SID that is present in either token is also present in the other one */
303     if (!SepCompareSidAndAttributesFromTokens(FirstToken->UserAndGroups,
304                                               FirstToken->UserAndGroupCount,
305                                               SecondToken->UserAndGroups,
306                                               SecondToken->UserAndGroupCount))
307     {
308         goto Quit;
309     }
310 
311     /* Is one token restricted but the other isn't? */
312     Restricted = SeTokenIsRestricted(FirstToken);
313     if (Restricted != SeTokenIsRestricted(SecondToken))
314     {
315         /* If that's the case then bail out */
316         goto Quit;
317     }
318 
319     /*
320      * If both tokens are restricted check if every SID
321      * that is restricted in either token is also restricted
322      * in the other one.
323      */
324     if (Restricted)
325     {
326         if (!SepCompareSidAndAttributesFromTokens(FirstToken->RestrictedSids,
327                                                   FirstToken->RestrictedSidCount,
328                                                   SecondToken->RestrictedSids,
329                                                   SecondToken->RestrictedSidCount))
330         {
331             goto Quit;
332         }
333     }
334 
335     /* Check if every privilege present in either token is also present in the other one */
336     if (!SepComparePrivilegeAndAttributesFromTokens(FirstToken->Privileges,
337                                                     FirstToken->PrivilegeCount,
338                                                     SecondToken->Privileges,
339                                                     SecondToken->PrivilegeCount))
340     {
341         goto Quit;
342     }
343 
344     /* If we're here then the tokens are equal */
345     IsEqual = TRUE;
346     DPRINT("SepCompareTokens(): Tokens are equal!\n");
347 
348 Quit:
349     /* Unlock the tokens */
350     SepReleaseTokenLock(SecondToken);
351     SepReleaseTokenLock(FirstToken);
352 
353     *Equal = IsEqual;
354     return STATUS_SUCCESS;
355 }
356 
357 /**
358  * @brief
359  * Private function that impersonates the system's anonymous logon token.
360  * The major bulk of the impersonation procedure is done here.
361  *
362  * @param[in] Thread
363  * The executive thread object that is to impersonate the client.
364  *
365  * @param[in] PreviousMode
366  * The access processor mode, indicating if the call is executed
367  * in kernel or user mode.
368  *
369  * @return
370  * Returns STATUS_SUCCESS if the impersonation has succeeded.
371  * STATUS_UNSUCCESSFUL is returned if the primary token couldn't be
372  * obtained from the current process to perform additional tasks.
373  * STATUS_ACCESS_DENIED is returned if the process' primary token is
374  * restricted, which for this matter we cannot impersonate onto a
375  * restricted process. Otherwise a failure NTSTATUS code is returned.
376  */
377 static
378 NTSTATUS
379 SepImpersonateAnonymousToken(
380     _In_ PETHREAD Thread,
381     _In_ KPROCESSOR_MODE PreviousMode)
382 {
383     NTSTATUS Status;
384     PTOKEN TokenToImpersonate, ProcessToken;
385     ULONG IncludeEveryoneValueData;
386     PAGED_CODE();
387 
388     /*
389      * We must check first which kind of token
390      * shall we assign for the thread to impersonate,
391      * the one with Everyone Group SID or the other
392      * without. Invoke the registry helper to
393      * return the data value for us.
394      */
395     Status = SepRegQueryHelper(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Lsa",
396                                L"EveryoneIncludesAnonymous",
397                                REG_DWORD,
398                                sizeof(IncludeEveryoneValueData),
399                                &IncludeEveryoneValueData);
400     if (!NT_SUCCESS(Status))
401     {
402         DPRINT1("SepRegQueryHelper(): Failed to query the registry value (Status 0x%lx)\n", Status);
403         return Status;
404     }
405 
406     if (IncludeEveryoneValueData == 0)
407     {
408         DPRINT("SepImpersonateAnonymousToken(): Assigning the token not including the Everyone Group SID...\n");
409         TokenToImpersonate = SeAnonymousLogonTokenNoEveryone;
410     }
411     else
412     {
413         DPRINT("SepImpersonateAnonymousToken(): Assigning the token including the Everyone Group SID...\n");
414         TokenToImpersonate = SeAnonymousLogonToken;
415     }
416 
417     /*
418      * Tell the object manager that we're going to use this token
419      * object now by incrementing the reference count.
420     */
421     Status = ObReferenceObjectByPointer(TokenToImpersonate,
422                                         TOKEN_IMPERSONATE,
423                                         SeTokenObjectType,
424                                         PreviousMode);
425     if (!NT_SUCCESS(Status))
426     {
427         DPRINT1("SepImpersonateAnonymousToken(): Couldn't be able to use the token, bail out...\n");
428         return Status;
429     }
430 
431     /*
432      * Reference the primary token of the current process that the anonymous
433      * logon token impersonation procedure is being performed. We'll be going
434      * to use the process' token to figure out if the process is actually
435      * restricted or not.
436      */
437     ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess());
438     if (!ProcessToken)
439     {
440         DPRINT1("SepImpersonateAnonymousToken(): Couldn't be able to get the process' primary token, bail out...\n");
441         ObDereferenceObject(TokenToImpersonate);
442         return STATUS_UNSUCCESSFUL;
443     }
444 
445     /* Now, is the token from the current process restricted? */
446     if (SeTokenIsRestricted(ProcessToken))
447     {
448         DPRINT1("SepImpersonateAnonymousToken(): The process is restricted, can't do anything. Bail out...\n");
449         PsDereferencePrimaryToken(ProcessToken);
450         ObDereferenceObject(TokenToImpersonate);
451         return STATUS_ACCESS_DENIED;
452     }
453 
454     /*
455      * Finally it's time to impersonate! But first, fast dereference the
456      * process' primary token as we no longer need it.
457      */
458     ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken);
459     Status = PsImpersonateClient(Thread, TokenToImpersonate, TRUE, FALSE, SecurityImpersonation);
460     if (!NT_SUCCESS(Status))
461     {
462         DPRINT1("SepImpersonateAnonymousToken(): Failed to impersonate, bail out...\n");
463         ObDereferenceObject(TokenToImpersonate);
464         return Status;
465     }
466 
467     return Status;
468 }
469 
470 /**
471  * @brief
472  * Updates the token's flags based upon the privilege that the token
473  * has been granted. The flag can either be taken out or given to the token
474  * if the attributes of the specified privilege is enabled or not.
475  *
476  * @param[in,out] Token
477  * The token where the flags are to be changed.
478  *
479  * @param[in] Index
480  * The index count which represents the total sum of privileges. The count in question
481  * MUST NOT exceed the expected privileges count of the token.
482  *
483  * @return
484  * Nothing.
485  */
486 static
487 VOID
488 SepUpdateSinglePrivilegeFlagToken(
489     _Inout_ PTOKEN Token,
490     _In_ ULONG Index)
491 {
492     ULONG TokenFlag;
493     ASSERT(Index < Token->PrivilegeCount);
494 
495     /* The high part of all values we are interested in is 0 */
496     if (Token->Privileges[Index].Luid.HighPart != 0)
497     {
498         return;
499     }
500 
501     /* Check for certain privileges to update flags */
502     if (Token->Privileges[Index].Luid.LowPart == SE_CHANGE_NOTIFY_PRIVILEGE)
503     {
504         TokenFlag = TOKEN_HAS_TRAVERSE_PRIVILEGE;
505     }
506     else if (Token->Privileges[Index].Luid.LowPart == SE_BACKUP_PRIVILEGE)
507     {
508         TokenFlag = TOKEN_HAS_BACKUP_PRIVILEGE;
509     }
510     else if (Token->Privileges[Index].Luid.LowPart == SE_RESTORE_PRIVILEGE)
511     {
512         TokenFlag = TOKEN_HAS_RESTORE_PRIVILEGE;
513     }
514     else if (Token->Privileges[Index].Luid.LowPart == SE_IMPERSONATE_PRIVILEGE)
515     {
516         TokenFlag = TOKEN_HAS_IMPERSONATE_PRIVILEGE;
517     }
518     else
519     {
520         /* Nothing to do */
521         return;
522     }
523 
524     /* Check if the specified privilege is enabled */
525     if (Token->Privileges[Index].Attributes & SE_PRIVILEGE_ENABLED)
526     {
527         /* It is enabled, so set the flag */
528         Token->TokenFlags |= TokenFlag;
529     }
530     else
531     {
532         /* Is is disabled, so remove the flag */
533         Token->TokenFlags &= ~TokenFlag;
534     }
535 }
536 
537 /**
538  * @brief
539  * Updates the token's flags based upon the privilege that the token
540  * has been granted. The function uses the private helper, SepUpdateSinglePrivilegeFlagToken,
541  * in order to update the flags of a token.
542  *
543  * @param[in,out] Token
544  * The token where the flags are to be changed.
545  *
546  * @return
547  * Nothing.
548  */
549 static
550 VOID
551 SepUpdatePrivilegeFlagsToken(
552     _Inout_ PTOKEN Token)
553 {
554     ULONG i;
555 
556     /* Loop all privileges */
557     for (i = 0; i < Token->PrivilegeCount; i++)
558     {
559         /* Updates the flags for this privilege */
560         SepUpdateSinglePrivilegeFlagToken(Token, i);
561     }
562 }
563 
564 /**
565  * @brief
566  * Removes a privilege from the token.
567  *
568  * @param[in,out] Token
569  * The token where the privilege is to be removed.
570  *
571  * @param[in] Index
572  * The index count which represents the number position of the privilege
573  * we want to remove.
574  *
575  * @return
576  * Nothing.
577  */
578 static
579 VOID
580 SepRemovePrivilegeToken(
581     _Inout_ PTOKEN Token,
582     _In_ ULONG Index)
583 {
584     ULONG MoveCount;
585     ASSERT(Index < Token->PrivilegeCount);
586 
587     /* Calculate the number of trailing privileges */
588     MoveCount = Token->PrivilegeCount - Index - 1;
589     if (MoveCount != 0)
590     {
591         /* Move them one location ahead */
592         RtlMoveMemory(&Token->Privileges[Index],
593                       &Token->Privileges[Index + 1],
594                       MoveCount * sizeof(LUID_AND_ATTRIBUTES));
595     }
596 
597     /* Update privilege count */
598     Token->PrivilegeCount--;
599 }
600 
601 /**
602  * @brief
603  * Removes a group from the token.
604  *
605  * @param[in,out] Token
606  * The token where the group is to be removed.
607  *
608  * @param[in] Index
609  * The index count which represents the number position of the group
610  * we want to remove.
611  *
612  * @return
613  * Nothing.
614  */
615 static
616 VOID
617 SepRemoveUserGroupToken(
618     _Inout_ PTOKEN Token,
619     _In_ ULONG Index)
620 {
621     ULONG MoveCount;
622     ASSERT(Index < Token->UserAndGroupCount);
623 
624     /* Calculate the number of trailing groups */
625     MoveCount = Token->UserAndGroupCount - Index - 1;
626     if (MoveCount != 0)
627     {
628         /* Time to remove the group by moving one location ahead */
629         RtlMoveMemory(&Token->UserAndGroups[Index],
630                       &Token->UserAndGroups[Index + 1],
631                       MoveCount * sizeof(SID_AND_ATTRIBUTES));
632     }
633 
634     /* Remove one group count */
635     Token->UserAndGroupCount--;
636 }
637 
638 /**
639  * @unimplemented
640  * @brief
641  * Frees (de-allocates) the proxy data memory block of a token.
642  *
643  * @param[in,out] ProxyData
644  * The proxy data to be freed.
645  *
646  * @return
647  * Nothing.
648  */
649 VOID
650 NTAPI
651 SepFreeProxyData(
652     _Inout_ PVOID ProxyData)
653 {
654     UNIMPLEMENTED;
655 }
656 
657 /**
658  * @unimplemented
659  * @brief
660  * Copies the proxy data from the source into the destination of a token.
661  *
662  * @param[out] Dest
663  * The destination path where the proxy data is to be copied to.
664  *
665  * @param[in] Src
666  * The source path where the proxy data is be copied from.
667  *
668  * @return
669  * To be added...
670  */
671 NTSTATUS
672 NTAPI
673 SepCopyProxyData(
674     _Out_ PVOID* Dest,
675     _In_ PVOID Src)
676 {
677     UNIMPLEMENTED;
678     return STATUS_NOT_IMPLEMENTED;
679 }
680 
681 /**
682  * @brief
683  * Replaces the old access token of a process (pointed by the EPROCESS kernel structure) with a
684  * new access token. The new access token must be a primary token for use.
685  *
686  * @param[in] Process
687  * The process instance where its access token is about to be replaced.
688  *
689  * @param[in] NewAccessToken
690  * The new token that it's going to replace the old one.
691  *
692  * @param[out] OldAccessToken
693  * The returned old token that's been replaced, which the caller can do anything.
694  *
695  * @return
696  * Returns STATUS_SUCCESS if the exchange operation between tokens has completed successfully.
697  * STATUS_BAD_TOKEN_TYPE is returned if the new token is not a primary one so that we cannot
698  * exchange it with the old one from the process. STATUS_TOKEN_ALREADY_IN_USE is returned if
699  * both tokens aren't equal which means one of them has different properties (groups, privileges, etc.)
700  * and as such one of them is currently in use. A failure NTSTATUS code is returned otherwise.
701  */
702 NTSTATUS
703 NTAPI
704 SeExchangePrimaryToken(
705     _In_ PEPROCESS Process,
706     _In_ PACCESS_TOKEN NewAccessToken,
707     _Out_ PACCESS_TOKEN* OldAccessToken)
708 {
709     PTOKEN OldToken;
710     PTOKEN NewToken = (PTOKEN)NewAccessToken;
711 
712     PAGED_CODE();
713 
714     if (NewToken->TokenType != TokenPrimary)
715         return STATUS_BAD_TOKEN_TYPE;
716 
717     if (NewToken->TokenInUse)
718     {
719         BOOLEAN IsEqual;
720         NTSTATUS Status;
721 
722         /* Maybe we're trying to set the same token */
723         OldToken = PsReferencePrimaryToken(Process);
724         if (OldToken == NewToken)
725         {
726             /* So it's a nop. */
727             *OldAccessToken = OldToken;
728             return STATUS_SUCCESS;
729         }
730 
731         Status = SepCompareTokens(OldToken, NewToken, &IsEqual);
732         if (!NT_SUCCESS(Status))
733         {
734             PsDereferencePrimaryToken(OldToken);
735             *OldAccessToken = NULL;
736             return Status;
737         }
738 
739         if (!IsEqual)
740         {
741             PsDereferencePrimaryToken(OldToken);
742             *OldAccessToken = NULL;
743             return STATUS_TOKEN_ALREADY_IN_USE;
744         }
745         /* Silently return STATUS_SUCCESS but do not set the new token,
746          * as it's already in use elsewhere. */
747         *OldAccessToken = OldToken;
748         return STATUS_SUCCESS;
749     }
750 
751     /* Lock the new token */
752     SepAcquireTokenLockExclusive(NewToken);
753 
754     /* Mark new token in use */
755     NewToken->TokenInUse = TRUE;
756 
757     /* Set the session ID for the new token */
758     NewToken->SessionId = MmGetSessionId(Process);
759 
760     /* Unlock the new token */
761     SepReleaseTokenLock(NewToken);
762 
763     /* Reference the new token */
764     ObReferenceObject(NewToken);
765 
766     /* Replace the old with the new */
767     OldToken = ObFastReplaceObject(&Process->Token, NewToken);
768 
769     /* Lock the old token */
770     SepAcquireTokenLockExclusive(OldToken);
771 
772     /* Mark the old token as free */
773     OldToken->TokenInUse = FALSE;
774 
775     /* Unlock the old token */
776     SepReleaseTokenLock(OldToken);
777 
778     *OldAccessToken = (PACCESS_TOKEN)OldToken;
779     return STATUS_SUCCESS;
780 }
781 
782 /**
783  * @brief
784  * Removes the primary token of a process.
785  *
786  * @param[in,out] Process
787  * The process instance with the access token to be removed.
788  *
789  * @return
790  * Nothing.
791  */
792 VOID
793 NTAPI
794 SeDeassignPrimaryToken(
795     _Inout_ PEPROCESS Process)
796 {
797     PTOKEN OldToken;
798 
799     /* Remove the Token */
800     OldToken = ObFastReplaceObject(&Process->Token, NULL);
801 
802     /* Mark the Old Token as free */
803     OldToken->TokenInUse = FALSE;
804 
805     /* Dereference the Token */
806     ObDereferenceObject(OldToken);
807 }
808 
809 /**
810  * @brief
811  * Computes the length size of a SID.
812  *
813  * @param[in] Count
814  * Total count of entries that have SIDs in them (that being PSID_AND_ATTRIBUTES in this context).
815  *
816  * @param[in] Src
817  * Source that points to the attributes and SID entry structure.
818  *
819  * @return
820  * Returns the total length of a SID size.
821  */
822 static ULONG
823 RtlLengthSidAndAttributes(
824     _In_ ULONG Count,
825     _In_ PSID_AND_ATTRIBUTES Src)
826 {
827     ULONG i;
828     ULONG uLength;
829 
830     PAGED_CODE();
831 
832     uLength = Count * sizeof(SID_AND_ATTRIBUTES);
833     for (i = 0; i < Count; i++)
834         uLength += RtlLengthSid(Src[i].Sid);
835 
836     return uLength;
837 }
838 
839 /**
840  * @brief
841  * Finds the primary group and default owner entity based on the submitted primary group instance
842  * and an access token.
843  *
844  * @param[in] Token
845  * Access token to begin the search query of primary group and default owner.
846  *
847  * @param[in] PrimaryGroup
848  * A primary group SID to be used for search query, determining if user & groups of a token
849  * and the submitted primary group do match.
850  *
851  * @param[in] DefaultOwner
852  * The default owner. If specified, it's used to determine if the token belongs to the actual user,
853  * that is, being the owner himself.
854  *
855  * @param[out] PrimaryGroupIndex
856  * Returns the primary group index.
857  *
858  * @param[out] DefaultOwnerIndex
859  * Returns the default owner index.
860  *
861  * @return
862  * Returns STATUS_SUCCESS if the find query operation has completed successfully and that at least one
863  * search result is requested by the caller. STATUS_INVALID_PARAMETER is returned if the caller hasn't requested
864  * any search result. STATUS_INVALID_OWNER is returned if the specified default user owner does not match with the other
865  * user from the token. STATUS_INVALID_PRIMARY_GROUP is returned if the specified default primary group does not match with the
866  * other group from the token.
867  */
868 static NTSTATUS
869 SepFindPrimaryGroupAndDefaultOwner(
870     _In_ PTOKEN Token,
871     _In_ PSID PrimaryGroup,
872     _In_opt_ PSID DefaultOwner,
873     _Out_opt_ PULONG PrimaryGroupIndex,
874     _Out_opt_ PULONG DefaultOwnerIndex)
875 {
876     ULONG i;
877 
878     /* We should return at least a search result */
879     if (!PrimaryGroupIndex && !DefaultOwnerIndex)
880         return STATUS_INVALID_PARAMETER;
881 
882     if (PrimaryGroupIndex)
883     {
884         /* Initialize with an invalid index */
885         // Token->PrimaryGroup = NULL;
886         *PrimaryGroupIndex = Token->UserAndGroupCount;
887     }
888 
889     if (DefaultOwnerIndex)
890     {
891         if (DefaultOwner)
892         {
893             /* An owner is specified: check whether this is actually the user */
894             if (RtlEqualSid(Token->UserAndGroups[0].Sid, DefaultOwner))
895             {
896                 /*
897                  * It's the user (first element in array): set it
898                  * as the owner and stop the search for it.
899                  */
900                 *DefaultOwnerIndex = 0;
901                 DefaultOwnerIndex = NULL;
902             }
903             else
904             {
905                 /* An owner is specified: initialize with an invalid index */
906                 *DefaultOwnerIndex = Token->UserAndGroupCount;
907             }
908         }
909         else
910         {
911             /*
912              * No owner specified: set the user (first element in array)
913              * as the owner and stop the search for it.
914              */
915             *DefaultOwnerIndex = 0;
916             DefaultOwnerIndex = NULL;
917         }
918     }
919 
920     /* Validate and set the primary group and default owner indices */
921     for (i = 0; i < Token->UserAndGroupCount; i++)
922     {
923         /* Stop the search if we have found what we searched for */
924         if (!PrimaryGroupIndex && !DefaultOwnerIndex)
925             break;
926 
927         if (DefaultOwnerIndex && DefaultOwner &&
928             RtlEqualSid(Token->UserAndGroups[i].Sid, DefaultOwner) &&
929             (Token->UserAndGroups[i].Attributes & SE_GROUP_OWNER))
930         {
931             /* Owner is found, stop the search for it */
932             *DefaultOwnerIndex = i;
933             DefaultOwnerIndex = NULL;
934         }
935 
936         if (PrimaryGroupIndex &&
937             RtlEqualSid(Token->UserAndGroups[i].Sid, PrimaryGroup))
938         {
939             /* Primary group is found, stop the search for it */
940             // Token->PrimaryGroup = Token->UserAndGroups[i].Sid;
941             *PrimaryGroupIndex = i;
942             PrimaryGroupIndex = NULL;
943         }
944     }
945 
946     if (DefaultOwnerIndex)
947     {
948         if (*DefaultOwnerIndex == Token->UserAndGroupCount)
949             return STATUS_INVALID_OWNER;
950     }
951 
952     if (PrimaryGroupIndex)
953     {
954         if (*PrimaryGroupIndex == Token->UserAndGroupCount)
955         // if (Token->PrimaryGroup == NULL)
956             return STATUS_INVALID_PRIMARY_GROUP;
957     }
958 
959     return STATUS_SUCCESS;
960 }
961 
962 /**
963  * @brief
964  * Duplicates an access token, from an existing valid token.
965  *
966  * @param[in] Token
967  * Access token to duplicate.
968  *
969  * @param[in] ObjectAttributes
970  * Object attributes for the new token.
971  *
972  * @param[in] EffectiveOnly
973  * If set to TRUE, the function removes all the disabled privileges and groups of the token
974  * to duplicate.
975  *
976  * @param[in] TokenType
977  * Type of token.
978  *
979  * @param[in] Level
980  * Security impersonation level of a token.
981  *
982  * @param[in] PreviousMode
983  * The processor request level mode.
984  *
985  * @param[out] NewAccessToken
986  * The duplicated token.
987  *
988  * @return
989  * Returns STATUS_SUCCESS if the token has been duplicated. STATUS_INSUFFICIENT_RESOURCES is returned
990  * if memory pool allocation of the dynamic part of the token for duplication has failed due to the lack
991  * of memory resources. A failure NTSTATUS code is returned otherwise.
992  */
993 NTSTATUS
994 NTAPI
995 SepDuplicateToken(
996     _In_ PTOKEN Token,
997     _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
998     _In_ BOOLEAN EffectiveOnly,
999     _In_ TOKEN_TYPE TokenType,
1000     _In_ SECURITY_IMPERSONATION_LEVEL Level,
1001     _In_ KPROCESSOR_MODE PreviousMode,
1002     _Out_ PTOKEN* NewAccessToken)
1003 {
1004     NTSTATUS Status;
1005     PTOKEN AccessToken;
1006     PVOID EndMem;
1007     ULONG VariableLength;
1008     ULONG TotalSize;
1009     ULONG PrivilegesIndex, GroupsIndex;
1010 
1011     PAGED_CODE();
1012 
1013     /* Compute how much size we need to allocate for the token */
1014     VariableLength = Token->VariableLength;
1015     TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
1016 
1017     Status = ObCreateObject(PreviousMode,
1018                             SeTokenObjectType,
1019                             ObjectAttributes,
1020                             PreviousMode,
1021                             NULL,
1022                             TotalSize,
1023                             0,
1024                             0,
1025                             (PVOID*)&AccessToken);
1026     if (!NT_SUCCESS(Status))
1027     {
1028         DPRINT1("ObCreateObject() failed (Status 0x%lx)\n", Status);
1029         return Status;
1030     }
1031 
1032     /* Zero out the buffer and initialize the token */
1033     RtlZeroMemory(AccessToken, TotalSize);
1034 
1035     ExAllocateLocallyUniqueId(&AccessToken->TokenId);
1036 
1037     AccessToken->TokenType = TokenType;
1038     AccessToken->ImpersonationLevel = Level;
1039 
1040     /* Initialise the lock for the access token */
1041     Status = SepCreateTokenLock(AccessToken);
1042     if (!NT_SUCCESS(Status))
1043     {
1044         ObDereferenceObject(AccessToken);
1045         return Status;
1046     }
1047 
1048     /* Copy the immutable fields */
1049     RtlCopyLuid(&AccessToken->TokenSource.SourceIdentifier,
1050                 &Token->TokenSource.SourceIdentifier);
1051     RtlCopyMemory(AccessToken->TokenSource.SourceName,
1052                   Token->TokenSource.SourceName,
1053                   sizeof(Token->TokenSource.SourceName));
1054 
1055     AccessToken->AuthenticationId = Token->AuthenticationId;
1056     AccessToken->ParentTokenId = Token->ParentTokenId;
1057     AccessToken->ExpirationTime = Token->ExpirationTime;
1058     AccessToken->OriginatingLogonSession = Token->OriginatingLogonSession;
1059 
1060     /* Lock the source token and copy the mutable fields */
1061     SepAcquireTokenLockExclusive(Token);
1062 
1063     AccessToken->SessionId = Token->SessionId;
1064     RtlCopyLuid(&AccessToken->ModifiedId, &Token->ModifiedId);
1065 
1066     AccessToken->TokenFlags = Token->TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
1067 
1068     /* Reference the logon session */
1069     Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
1070     if (!NT_SUCCESS(Status))
1071     {
1072         /* No logon session could be found, bail out */
1073         DPRINT1("SepRmReferenceLogonSession() failed (Status 0x%lx)\n", Status);
1074         /* Set the flag for proper cleanup by the delete procedure */
1075         AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED;
1076         goto Quit;
1077     }
1078 
1079     /* Insert the referenced logon session into the token */
1080     Status = SepRmInsertLogonSessionIntoToken(AccessToken);
1081     if (!NT_SUCCESS(Status))
1082     {
1083         /* Failed to insert the logon session into the token, bail out */
1084         DPRINT1("SepRmInsertLogonSessionIntoToken() failed (Status 0x%lx)\n", Status);
1085         goto Quit;
1086     }
1087 
1088     /* Assign the data that reside in the TOKEN's variable information area */
1089     AccessToken->VariableLength = VariableLength;
1090     EndMem = (PVOID)&AccessToken->VariablePart;
1091 
1092     /* Copy the privileges */
1093     AccessToken->PrivilegeCount = 0;
1094     AccessToken->Privileges = NULL;
1095     if (Token->Privileges && (Token->PrivilegeCount > 0))
1096     {
1097         ULONG PrivilegesLength = Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
1098         PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID));
1099 
1100         ASSERT(VariableLength >= PrivilegesLength);
1101 
1102         AccessToken->PrivilegeCount = Token->PrivilegeCount;
1103         AccessToken->Privileges = EndMem;
1104         EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength);
1105         VariableLength -= PrivilegesLength;
1106 
1107         RtlCopyMemory(AccessToken->Privileges,
1108                       Token->Privileges,
1109                       AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
1110     }
1111 
1112     /* Copy the user and groups */
1113     AccessToken->UserAndGroupCount = 0;
1114     AccessToken->UserAndGroups = NULL;
1115     if (Token->UserAndGroups && (Token->UserAndGroupCount > 0))
1116     {
1117         AccessToken->UserAndGroupCount = Token->UserAndGroupCount;
1118         AccessToken->UserAndGroups = EndMem;
1119         EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
1120         VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups);
1121 
1122         Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount,
1123                                               Token->UserAndGroups,
1124                                               VariableLength,
1125                                               AccessToken->UserAndGroups,
1126                                               EndMem,
1127                                               &EndMem,
1128                                               &VariableLength);
1129         if (!NT_SUCCESS(Status))
1130         {
1131             DPRINT1("RtlCopySidAndAttributesArray(UserAndGroups) failed (Status 0x%lx)\n", Status);
1132             goto Quit;
1133         }
1134     }
1135 
1136 #if 1
1137     {
1138     ULONG PrimaryGroupIndex;
1139 
1140     /* Find the token primary group */
1141     Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken,
1142                                                 Token->PrimaryGroup,
1143                                                 NULL,
1144                                                 &PrimaryGroupIndex,
1145                                                 NULL);
1146     if (!NT_SUCCESS(Status))
1147     {
1148         DPRINT1("SepFindPrimaryGroupAndDefaultOwner failed (Status 0x%lx)\n", Status);
1149         goto Quit;
1150     }
1151     AccessToken->PrimaryGroup = AccessToken->UserAndGroups[PrimaryGroupIndex].Sid;
1152     }
1153 #else
1154     AccessToken->PrimaryGroup = (PVOID)((ULONG_PTR)AccessToken + (ULONG_PTR)Token->PrimaryGroup - (ULONG_PTR)Token->UserAndGroups);
1155 #endif
1156     AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex;
1157 
1158     /* Copy the restricted SIDs */
1159     AccessToken->RestrictedSidCount = 0;
1160     AccessToken->RestrictedSids = NULL;
1161     if (Token->RestrictedSids && (Token->RestrictedSidCount > 0))
1162     {
1163         AccessToken->RestrictedSidCount = Token->RestrictedSidCount;
1164         AccessToken->RestrictedSids = EndMem;
1165         EndMem = &AccessToken->RestrictedSids[AccessToken->RestrictedSidCount];
1166         VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->RestrictedSids);
1167 
1168         Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount,
1169                                               Token->RestrictedSids,
1170                                               VariableLength,
1171                                               AccessToken->RestrictedSids,
1172                                               EndMem,
1173                                               &EndMem,
1174                                               &VariableLength);
1175         if (!NT_SUCCESS(Status))
1176         {
1177             DPRINT1("RtlCopySidAndAttributesArray(RestrictedSids) failed (Status 0x%lx)\n", Status);
1178             goto Quit;
1179         }
1180     }
1181 
1182     /*
1183      * Filter the token by removing the disabled privileges
1184      * and groups if the caller wants to duplicate an access
1185      * token as effective only.
1186      */
1187     if (EffectiveOnly)
1188     {
1189         /* Begin querying the groups and search for disabled ones */
1190         for (GroupsIndex = 0; GroupsIndex < AccessToken->UserAndGroupCount; GroupsIndex++)
1191         {
1192             /*
1193              * A group or user is considered disabled if its attributes is either
1194              * 0 or SE_GROUP_ENABLED is not included in the attributes flags list.
1195              * That is because a certain user and/or group can have several attributes
1196              * that bear no influence on whether a user/group is enabled or not
1197              * (SE_GROUP_ENABLED_BY_DEFAULT for example which is a mere indicator
1198              * that the group has just been enabled by default). A mandatory
1199              * group (that is, the group has SE_GROUP_MANDATORY attribute)
1200              * by standards it's always enabled and no one can disable it.
1201              */
1202             if (AccessToken->UserAndGroups[GroupsIndex].Attributes == 0 ||
1203                 (AccessToken->UserAndGroups[GroupsIndex].Attributes & SE_GROUP_ENABLED) == 0)
1204             {
1205                 /*
1206                  * A group is not enabled, it's time to remove
1207                  * from the token and update the groups index
1208                  * accordingly and continue with the next group.
1209                  */
1210                 SepRemoveUserGroupToken(AccessToken, GroupsIndex);
1211                 GroupsIndex--;
1212             }
1213         }
1214 
1215         /* Begin querying the privileges and search for disabled ones */
1216         for (PrivilegesIndex = 0; PrivilegesIndex < AccessToken->PrivilegeCount; PrivilegesIndex++)
1217         {
1218             /*
1219              * A privilege is considered disabled if its attributes is either
1220              * 0 or SE_PRIVILEGE_ENABLED is not included in the attributes flags list.
1221              * That is because a certain privilege can have several attributes
1222              * that bear no influence on whether a privilege is enabled or not
1223              * (SE_PRIVILEGE_ENABLED_BY_DEFAULT for example which is a mere indicator
1224              * that the privilege has just been enabled by default).
1225              */
1226             if (AccessToken->Privileges[PrivilegesIndex].Attributes == 0 ||
1227                 (AccessToken->Privileges[PrivilegesIndex].Attributes & SE_PRIVILEGE_ENABLED) == 0)
1228             {
1229                 /*
1230                  * A privilege is not enabled, therefor it's time
1231                  * to strip it from the token and continue with the next
1232                  * privilege. Of course we must also want to update the
1233                  * privileges index accordingly.
1234                  */
1235                 SepRemovePrivilegeToken(AccessToken, PrivilegesIndex);
1236                 PrivilegesIndex--;
1237             }
1238         }
1239     }
1240 
1241     //
1242     // NOTE: So far our dynamic area only contains
1243     // the default dacl, so this makes the following
1244     // code pretty simple. The day where it stores
1245     // other data, the code will require adaptations.
1246     //
1247 
1248     /* Now allocate the TOKEN's dynamic information area and set the data */
1249     AccessToken->DynamicAvailable = 0; // Unused memory in the dynamic area.
1250     AccessToken->DynamicPart = NULL;
1251     if (Token->DynamicPart && Token->DefaultDacl)
1252     {
1253         AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
1254                                                          Token->DefaultDacl->AclSize,
1255                                                          TAG_TOKEN_DYNAMIC);
1256         if (AccessToken->DynamicPart == NULL)
1257         {
1258             Status = STATUS_INSUFFICIENT_RESOURCES;
1259             goto Quit;
1260         }
1261         EndMem = (PVOID)AccessToken->DynamicPart;
1262 
1263         AccessToken->DefaultDacl = EndMem;
1264 
1265         RtlCopyMemory(AccessToken->DefaultDacl,
1266                       Token->DefaultDacl,
1267                       Token->DefaultDacl->AclSize);
1268     }
1269 
1270     /* Unlock the source token */
1271     SepReleaseTokenLock(Token);
1272 
1273     /* Return the token */
1274     *NewAccessToken = AccessToken;
1275     Status = STATUS_SUCCESS;
1276 
1277 Quit:
1278     if (!NT_SUCCESS(Status))
1279     {
1280         /* Unlock the source token */
1281         SepReleaseTokenLock(Token);
1282 
1283         /* Dereference the token, the delete procedure will clean it up */
1284         ObDereferenceObject(AccessToken);
1285     }
1286 
1287     return Status;
1288 }
1289 
1290 /**
1291  * @brief
1292  * Subtracts a token in exchange of duplicating a new one.
1293  *
1294  * @param[in] ParentToken
1295  * The parent access token for duplication.
1296  *
1297  * @param[out] Token
1298  * The new duplicated token.
1299  *
1300  * @param[in] InUse
1301  * Set this to TRUE if the token is about to be used immediately after the call execution
1302  * of this function, FALSE otherwise.
1303  *
1304  * @param[in] SessionId
1305  * Session ID for the token to be assigned.
1306  *
1307  * @return
1308  * Returns STATUS_SUCCESS if token subtracting and duplication have completed successfully.
1309  * A failure NTSTATUS code is returned otherwise.
1310  */
1311 NTSTATUS
1312 NTAPI
1313 SeSubProcessToken(
1314     _In_ PTOKEN ParentToken,
1315     _Out_ PTOKEN *Token,
1316     _In_ BOOLEAN InUse,
1317     _In_ ULONG SessionId)
1318 {
1319     PTOKEN NewToken;
1320     OBJECT_ATTRIBUTES ObjectAttributes;
1321     NTSTATUS Status;
1322 
1323     /* Initialize the attributes and duplicate it */
1324     InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
1325     Status = SepDuplicateToken(ParentToken,
1326                                &ObjectAttributes,
1327                                FALSE,
1328                                TokenPrimary,
1329                                ParentToken->ImpersonationLevel,
1330                                KernelMode,
1331                                &NewToken);
1332     if (NT_SUCCESS(Status))
1333     {
1334         /* Insert it */
1335         Status = ObInsertObject(NewToken,
1336                                 NULL,
1337                                 0,
1338                                 0,
1339                                 NULL,
1340                                 NULL);
1341         if (NT_SUCCESS(Status))
1342         {
1343             /* Set the session ID */
1344             NewToken->SessionId = SessionId;
1345             NewToken->TokenInUse = InUse;
1346 
1347             /* Return the token */
1348             *Token = NewToken;
1349         }
1350     }
1351 
1352     /* Return status */
1353     return Status;
1354 }
1355 
1356 /**
1357  * @brief
1358  * Checks if the token is a child of the other token
1359  * of the current process that the calling thread is invoking this function.
1360  *
1361  * @param[in] Token
1362  * An access token to determine if it's a child or not.
1363  *
1364  * @param[out] IsChild
1365  * The returned boolean result.
1366  *
1367  * @return
1368  * Returns STATUS_SUCCESS when the function finishes its operation. STATUS_UNSUCCESSFUL is
1369  * returned if primary token of the current calling process couldn't be referenced otherwise.
1370  */
1371 NTSTATUS
1372 NTAPI
1373 SeIsTokenChild(
1374     _In_ PTOKEN Token,
1375     _Out_ PBOOLEAN IsChild)
1376 {
1377     PTOKEN ProcessToken;
1378     LUID ProcessTokenId, CallerParentId;
1379 
1380     /* Assume failure */
1381     *IsChild = FALSE;
1382 
1383     /* Reference the process token */
1384     ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess());
1385     if (!ProcessToken)
1386         return STATUS_UNSUCCESSFUL;
1387 
1388     /* Get its token ID */
1389     ProcessTokenId = ProcessToken->TokenId;
1390 
1391     /* Dereference the token */
1392     ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken);
1393 
1394     /* Get our parent token ID */
1395     CallerParentId = Token->ParentTokenId;
1396 
1397     /* Compare the token IDs */
1398     if (RtlEqualLuid(&CallerParentId, &ProcessTokenId))
1399         *IsChild = TRUE;
1400 
1401     /* Return success */
1402     return STATUS_SUCCESS;
1403 }
1404 
1405 /**
1406  * @brief
1407  * Checks if the token is a sibling of the other token of
1408  * the current process that the calling thread is invoking this function.
1409  *
1410  * @param[in] Token
1411  * An access token to determine if it's a sibling or not.
1412  *
1413  * @param[out] IsSibling
1414  * The returned boolean result.
1415  *
1416  * @return
1417  * Returns STATUS_SUCCESS when the function finishes its operation. STATUS_UNSUCCESSFUL is
1418  * returned if primary token of the current calling process couldn't be referenced otherwise.
1419  */
1420 NTSTATUS
1421 NTAPI
1422 SeIsTokenSibling(
1423     _In_ PTOKEN Token,
1424     _Out_ PBOOLEAN IsSibling)
1425 {
1426     PTOKEN ProcessToken;
1427     LUID ProcessParentId, ProcessAuthId;
1428     LUID CallerParentId, CallerAuthId;
1429 
1430     /* Assume failure */
1431     *IsSibling = FALSE;
1432 
1433     /* Reference the process token */
1434     ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess());
1435     if (!ProcessToken)
1436         return STATUS_UNSUCCESSFUL;
1437 
1438     /* Get its parent and authentication IDs */
1439     ProcessParentId = ProcessToken->ParentTokenId;
1440     ProcessAuthId = ProcessToken->AuthenticationId;
1441 
1442     /* Dereference the token */
1443     ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken);
1444 
1445     /* Get our parent and authentication IDs */
1446     CallerParentId = Token->ParentTokenId;
1447     CallerAuthId = Token->AuthenticationId;
1448 
1449     /* Compare the token IDs */
1450     if (RtlEqualLuid(&CallerParentId, &ProcessParentId) &&
1451         RtlEqualLuid(&CallerAuthId, &ProcessAuthId))
1452     {
1453         *IsSibling = TRUE;
1454     }
1455 
1456     /* Return success */
1457     return STATUS_SUCCESS;
1458 }
1459 
1460 /**
1461  * @brief
1462  * Copies an existing access token (technically duplicating a new one).
1463  *
1464  * @param[in] Token
1465  * Token to copy.
1466  *
1467  * @param[in] Level
1468  * Impersonation security level to assign to the newly copied token.
1469  *
1470  * @param[in] PreviousMode
1471  * Processor request level mode.
1472  *
1473  * @param[out] NewToken
1474  * The newly copied token.
1475  *
1476  * @return
1477  * Returns STATUS_SUCCESS when token copying has finished successfully. A failure
1478  * NTSTATUS code is returned otherwise.
1479  */
1480 NTSTATUS
1481 NTAPI
1482 SeCopyClientToken(
1483     _In_ PACCESS_TOKEN Token,
1484     _In_ SECURITY_IMPERSONATION_LEVEL Level,
1485     _In_ KPROCESSOR_MODE PreviousMode,
1486     _Out_ PACCESS_TOKEN* NewToken)
1487 {
1488     NTSTATUS Status;
1489     OBJECT_ATTRIBUTES ObjectAttributes;
1490 
1491     PAGED_CODE();
1492 
1493     InitializeObjectAttributes(&ObjectAttributes,
1494                                NULL,
1495                                0,
1496                                NULL,
1497                                NULL);
1498 
1499     Status = SepDuplicateToken(Token,
1500                                &ObjectAttributes,
1501                                FALSE,
1502                                TokenImpersonation,
1503                                Level,
1504                                PreviousMode,
1505                                (PTOKEN*)NewToken);
1506 
1507     return Status;
1508 }
1509 
1510 /**
1511  * @brief
1512  * Internal function that deals with access token object destruction and deletion.
1513  * The function is used solely by the object manager mechanism that handles the life
1514  * management of a token object.
1515  *
1516  * @param[in] ObjectBody
1517  * The object body that represents an access token object.
1518  *
1519  * @return
1520  * Nothing.
1521  */
1522 VOID
1523 NTAPI
1524 SepDeleteToken(
1525     _In_ PVOID ObjectBody)
1526 {
1527     NTSTATUS Status;
1528     PTOKEN AccessToken = (PTOKEN)ObjectBody;
1529 
1530     DPRINT("SepDeleteToken()\n");
1531 
1532     /* Remove the referenced logon session from token */
1533     if (AccessToken->LogonSession)
1534     {
1535         Status = SepRmRemoveLogonSessionFromToken(AccessToken);
1536         if (!NT_SUCCESS(Status))
1537         {
1538             /* Something seriously went wrong */
1539             DPRINT1("SepDeleteToken(): Failed to remove the logon session from token (Status: 0x%lx)\n", Status);
1540             return;
1541         }
1542     }
1543 
1544     /* Dereference the logon session */
1545     if ((AccessToken->TokenFlags & TOKEN_SESSION_NOT_REFERENCED) == 0)
1546         SepRmDereferenceLogonSession(&AccessToken->AuthenticationId);
1547 
1548     /* Delete the token lock */
1549     if (AccessToken->TokenLock)
1550         SepDeleteTokenLock(AccessToken);
1551 
1552     /* Delete the dynamic information area */
1553     if (AccessToken->DynamicPart)
1554         ExFreePoolWithTag(AccessToken->DynamicPart, TAG_TOKEN_DYNAMIC);
1555 }
1556 
1557 /**
1558  * @brief
1559  * Internal function that initializes critical kernel data for access
1560  * token implementation in SRM.
1561  *
1562  * @return
1563  * Nothing.
1564  */
1565 CODE_SEG("INIT")
1566 VOID
1567 NTAPI
1568 SepInitializeTokenImplementation(VOID)
1569 {
1570     UNICODE_STRING Name;
1571     OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
1572 
1573     DPRINT("Creating Token Object Type\n");
1574 
1575     /* Initialize the Token type */
1576     RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
1577     RtlInitUnicodeString(&Name, L"Token");
1578     ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
1579     ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
1580     ObjectTypeInitializer.SecurityRequired = TRUE;
1581     ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(TOKEN);
1582     ObjectTypeInitializer.GenericMapping = SepTokenMapping;
1583     ObjectTypeInitializer.PoolType = PagedPool;
1584     ObjectTypeInitializer.ValidAccessMask = TOKEN_ALL_ACCESS;
1585     ObjectTypeInitializer.UseDefaultObject = TRUE;
1586     ObjectTypeInitializer.DeleteProcedure = SepDeleteToken;
1587     ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &SeTokenObjectType);
1588 }
1589 
1590 /**
1591  * @brief
1592  * Assigns a primary access token to a given process.
1593  *
1594  * @param[in] Process
1595  * Process where the token is about to be assigned.
1596  *
1597  * @param[in] Token
1598  * The token to be assigned.
1599  *
1600  * @return
1601  * Nothing.
1602  */
1603 VOID
1604 NTAPI
1605 SeAssignPrimaryToken(
1606     _In_ PEPROCESS Process,
1607     _In_ PTOKEN Token)
1608 {
1609     PAGED_CODE();
1610 
1611     /* Sanity checks */
1612     ASSERT(Token->TokenType == TokenPrimary);
1613     ASSERT(!Token->TokenInUse);
1614 
1615     /* Clean any previous token */
1616     if (Process->Token.Object) SeDeassignPrimaryToken(Process);
1617 
1618     /* Set the new token */
1619     ObReferenceObject(Token);
1620     Token->TokenInUse = TRUE;
1621     ObInitializeFastReference(&Process->Token, Token);
1622 }
1623 
1624 /**
1625  * @brief
1626  * Internal function responsible for access token object creation in the kernel.
1627  * A fully created token objected is inserted into the token handle, thus the handle
1628  * becoming a valid handle to an access token object and ready for use.
1629  *
1630  * @param[out] TokenHandle
1631  * Valid token handle that's ready for use after token creation and object insertion.
1632  *
1633  * @param[in] PreviousMode
1634  * Processor request level mode.
1635  *
1636  * @param[in] DesiredAccess
1637  * Desired access right for the token object to be granted. This kind of access right
1638  * impacts how the token can be used and who.
1639  *
1640  * @param[in] ObjectAttributes
1641  * Object attributes for the token to be created.
1642  *
1643  * @param[in] TokenType
1644  * Type of token to assign upon creation.
1645  *
1646  * @param[in] ImpersonationLevel
1647  * Security impersonation level of token to assign upon creation.
1648  *
1649  * @param[in] AuthenticationId
1650  * Authentication ID that represents the authentication information of the token.
1651  *
1652  * @param[in] ExpirationTime
1653  * Expiration time of the token to assign. A value of -1 means that the token never
1654  * expires and its life depends upon the amount of references this token object has.
1655  *
1656  * @param[in] User
1657  * User entry to assign to the token.
1658  *
1659  * @param[in] GroupCount
1660  * The total number of groups count for the token.
1661  *
1662  * @param[in] Groups
1663  * The group entries for the token.
1664  *
1665  * @param[in] GroupsLength
1666  * The length size of the groups array, pointed by the Groups parameter.
1667  *
1668  * @param[in] PrivilegeCount
1669  * The total number of priivleges that the newly created token has.
1670  *
1671  * @param[in] Privileges
1672  * The privileges for the token.
1673  *
1674  * @param[in] Owner
1675  * The main user (or also owner) that represents the token that we create.
1676  *
1677  * @param[in] PrimaryGroup
1678  * The main group that represents the token that we create.
1679  *
1680  * @param[in] DefaultDacl
1681  * A discretionary access control list for the token.
1682  *
1683  * @param[in] TokenSource
1684  * Source (or the origin) of the access token that creates it.
1685  *
1686  * @param[in] SystemToken
1687  * If set to TRUE, the newly created token is a system token and only in charge
1688  * by the internal system. The function directly returns a pointer to the
1689  * created token object for system kernel use. Otherwise if set to FALSE, the
1690  * function inserts the object to a handle making it a regular access token.
1691  *
1692  * @return
1693  * Returns STATUS_SUCCESS if token creation has completed successfully.
1694  * STATUS_INSUFFICIENT_RESOURCES is returned if the dynamic area of memory of the
1695  * token hasn't been allocated because of lack of memory resources. A failure
1696  * NTSTATUS code is returned otherwise.
1697  */
1698 NTSTATUS
1699 NTAPI
1700 SepCreateToken(
1701     _Out_ PHANDLE TokenHandle,
1702     _In_ KPROCESSOR_MODE PreviousMode,
1703     _In_ ACCESS_MASK DesiredAccess,
1704     _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
1705     _In_ TOKEN_TYPE TokenType,
1706     _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
1707     _In_ PLUID AuthenticationId,
1708     _In_ PLARGE_INTEGER ExpirationTime,
1709     _In_ PSID_AND_ATTRIBUTES User,
1710     _In_ ULONG GroupCount,
1711     _In_ PSID_AND_ATTRIBUTES Groups,
1712     _In_ ULONG GroupsLength,
1713     _In_ ULONG PrivilegeCount,
1714     _In_ PLUID_AND_ATTRIBUTES Privileges,
1715     _In_opt_ PSID Owner,
1716     _In_ PSID PrimaryGroup,
1717     _In_opt_ PACL DefaultDacl,
1718     _In_ PTOKEN_SOURCE TokenSource,
1719     _In_ BOOLEAN SystemToken)
1720 {
1721     NTSTATUS Status;
1722     PTOKEN AccessToken;
1723     ULONG TokenFlags = 0;
1724     ULONG PrimaryGroupIndex, DefaultOwnerIndex;
1725     LUID TokenId;
1726     LUID ModifiedId;
1727     PVOID EndMem;
1728     ULONG PrivilegesLength;
1729     ULONG UserGroupsLength;
1730     ULONG VariableLength;
1731     ULONG TotalSize;
1732     ULONG i;
1733 
1734     PAGED_CODE();
1735 
1736     /* Loop all groups */
1737     for (i = 0; i < GroupCount; i++)
1738     {
1739         /* Check for mandatory groups */
1740         if (Groups[i].Attributes & SE_GROUP_MANDATORY)
1741         {
1742             /* Force them to be enabled */
1743             Groups[i].Attributes |= (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT);
1744         }
1745 
1746         /* Check of the group is an admin group */
1747         if (RtlEqualSid(SeAliasAdminsSid, Groups[i].Sid))
1748         {
1749             /* Remember this so we can optimize queries later */
1750             TokenFlags |= TOKEN_HAS_ADMIN_GROUP;
1751         }
1752     }
1753 
1754     /* Allocate unique IDs for the token */
1755     ExAllocateLocallyUniqueId(&TokenId);
1756     ExAllocateLocallyUniqueId(&ModifiedId);
1757 
1758     /* Compute how much size we need to allocate for the token */
1759 
1760     /* Privileges size */
1761     PrivilegesLength = PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
1762     PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID));
1763 
1764     /* User and groups size */
1765     UserGroupsLength = (1 + GroupCount) * sizeof(SID_AND_ATTRIBUTES);
1766     UserGroupsLength += RtlLengthSid(User->Sid);
1767     for (i = 0; i < GroupCount; i++)
1768     {
1769         UserGroupsLength += RtlLengthSid(Groups[i].Sid);
1770     }
1771     UserGroupsLength = ALIGN_UP_BY(UserGroupsLength, sizeof(PVOID));
1772 
1773     /* Add the additional groups array length */
1774     UserGroupsLength += ALIGN_UP_BY(GroupsLength, sizeof(PVOID));
1775 
1776     VariableLength = PrivilegesLength + UserGroupsLength;
1777     TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
1778 
1779     Status = ObCreateObject(PreviousMode,
1780                             SeTokenObjectType,
1781                             ObjectAttributes,
1782                             PreviousMode,
1783                             NULL,
1784                             TotalSize,
1785                             0,
1786                             0,
1787                             (PVOID*)&AccessToken);
1788     if (!NT_SUCCESS(Status))
1789     {
1790         DPRINT1("ObCreateObject() failed (Status 0x%lx)\n", Status);
1791         return Status;
1792     }
1793 
1794     /* Zero out the buffer and initialize the token */
1795     RtlZeroMemory(AccessToken, TotalSize);
1796 
1797     RtlCopyLuid(&AccessToken->TokenId, &TokenId);
1798 
1799     AccessToken->TokenType = TokenType;
1800     AccessToken->ImpersonationLevel = ImpersonationLevel;
1801 
1802     /* Initialise the lock for the access token */
1803     Status = SepCreateTokenLock(AccessToken);
1804     if (!NT_SUCCESS(Status))
1805         goto Quit;
1806 
1807     RtlCopyLuid(&AccessToken->TokenSource.SourceIdentifier,
1808                 &TokenSource->SourceIdentifier);
1809     RtlCopyMemory(AccessToken->TokenSource.SourceName,
1810                   TokenSource->SourceName,
1811                   sizeof(TokenSource->SourceName));
1812 
1813     AccessToken->ExpirationTime = *ExpirationTime;
1814     RtlCopyLuid(&AccessToken->ModifiedId, &ModifiedId);
1815 
1816     AccessToken->TokenFlags = TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
1817 
1818     /* Copy and reference the logon session */
1819     RtlCopyLuid(&AccessToken->AuthenticationId, AuthenticationId);
1820     Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
1821     if (!NT_SUCCESS(Status))
1822     {
1823         /* No logon session could be found, bail out */
1824         DPRINT1("SepRmReferenceLogonSession() failed (Status 0x%lx)\n", Status);
1825         /* Set the flag for proper cleanup by the delete procedure */
1826         AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED;
1827         goto Quit;
1828     }
1829 
1830     /* Insert the referenced logon session into the token */
1831     Status = SepRmInsertLogonSessionIntoToken(AccessToken);
1832     if (!NT_SUCCESS(Status))
1833     {
1834         /* Failed to insert the logon session into the token, bail out */
1835         DPRINT1("SepRmInsertLogonSessionIntoToken() failed (Status 0x%lx)\n", Status);
1836         goto Quit;
1837     }
1838 
1839     /* Assign the data that reside in the TOKEN's variable information area */
1840     AccessToken->VariableLength = VariableLength;
1841     EndMem = (PVOID)&AccessToken->VariablePart;
1842 
1843     /* Copy the privileges */
1844     AccessToken->PrivilegeCount = PrivilegeCount;
1845     AccessToken->Privileges = NULL;
1846     if (PrivilegeCount > 0)
1847     {
1848         AccessToken->Privileges = EndMem;
1849         EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength);
1850         VariableLength -= PrivilegesLength;
1851 
1852         if (PreviousMode != KernelMode)
1853         {
1854             _SEH2_TRY
1855             {
1856                 RtlCopyMemory(AccessToken->Privileges,
1857                               Privileges,
1858                               PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
1859             }
1860             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1861             {
1862                 Status = _SEH2_GetExceptionCode();
1863             }
1864             _SEH2_END;
1865         }
1866         else
1867         {
1868             RtlCopyMemory(AccessToken->Privileges,
1869                           Privileges,
1870                           PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
1871         }
1872 
1873         if (!NT_SUCCESS(Status))
1874             goto Quit;
1875     }
1876 
1877     /* Update the privilege flags */
1878     SepUpdatePrivilegeFlagsToken(AccessToken);
1879 
1880     /* Copy the user and groups */
1881     AccessToken->UserAndGroupCount = 1 + GroupCount;
1882     AccessToken->UserAndGroups = EndMem;
1883     EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
1884     VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups);
1885 
1886     Status = RtlCopySidAndAttributesArray(1,
1887                                           User,
1888                                           VariableLength,
1889                                           &AccessToken->UserAndGroups[0],
1890                                           EndMem,
1891                                           &EndMem,
1892                                           &VariableLength);
1893     if (!NT_SUCCESS(Status))
1894         goto Quit;
1895 
1896     Status = RtlCopySidAndAttributesArray(GroupCount,
1897                                           Groups,
1898                                           VariableLength,
1899                                           &AccessToken->UserAndGroups[1],
1900                                           EndMem,
1901                                           &EndMem,
1902                                           &VariableLength);
1903     if (!NT_SUCCESS(Status))
1904         goto Quit;
1905 
1906     /* Find the token primary group and default owner */
1907     Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken,
1908                                                 PrimaryGroup,
1909                                                 Owner,
1910                                                 &PrimaryGroupIndex,
1911                                                 &DefaultOwnerIndex);
1912     if (!NT_SUCCESS(Status))
1913     {
1914         DPRINT1("SepFindPrimaryGroupAndDefaultOwner failed (Status 0x%lx)\n", Status);
1915         goto Quit;
1916     }
1917 
1918     AccessToken->PrimaryGroup = AccessToken->UserAndGroups[PrimaryGroupIndex].Sid;
1919     AccessToken->DefaultOwnerIndex = DefaultOwnerIndex;
1920 
1921     /* Now allocate the TOKEN's dynamic information area and set the data */
1922     AccessToken->DynamicAvailable = 0; // Unused memory in the dynamic area.
1923     AccessToken->DynamicPart = NULL;
1924     if (DefaultDacl != NULL)
1925     {
1926         AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
1927                                                          DefaultDacl->AclSize,
1928                                                          TAG_TOKEN_DYNAMIC);
1929         if (AccessToken->DynamicPart == NULL)
1930         {
1931             Status = STATUS_INSUFFICIENT_RESOURCES;
1932             goto Quit;
1933         }
1934         EndMem = (PVOID)AccessToken->DynamicPart;
1935 
1936         AccessToken->DefaultDacl = EndMem;
1937 
1938         RtlCopyMemory(AccessToken->DefaultDacl,
1939                       DefaultDacl,
1940                       DefaultDacl->AclSize);
1941     }
1942 
1943     /* Insert the token only if it's not the system token, otherwise return it directly */
1944     if (!SystemToken)
1945     {
1946         Status = ObInsertObject(AccessToken,
1947                                 NULL,
1948                                 DesiredAccess,
1949                                 0,
1950                                 NULL,
1951                                 TokenHandle);
1952         if (!NT_SUCCESS(Status))
1953         {
1954             DPRINT1("ObInsertObject() failed (Status 0x%lx)\n", Status);
1955         }
1956     }
1957     else
1958     {
1959         /* Return pointer instead of handle */
1960         *TokenHandle = (HANDLE)AccessToken;
1961     }
1962 
1963 Quit:
1964     if (!NT_SUCCESS(Status))
1965     {
1966         /* Dereference the token, the delete procedure will clean it up */
1967         ObDereferenceObject(AccessToken);
1968     }
1969 
1970     return Status;
1971 }
1972 
1973 /**
1974  * @brief
1975  * Creates the system process token.
1976  *
1977  * @return
1978  * Returns the system process token if the operations have
1979  * completed successfully.
1980  */
1981 CODE_SEG("INIT")
1982 PTOKEN
1983 NTAPI
1984 SepCreateSystemProcessToken(VOID)
1985 {
1986     LUID_AND_ATTRIBUTES Privileges[25];
1987     ULONG GroupAttributes, OwnerAttributes;
1988     SID_AND_ATTRIBUTES Groups[32];
1989     LARGE_INTEGER Expiration;
1990     SID_AND_ATTRIBUTES UserSid;
1991     ULONG GroupsLength;
1992     PSID PrimaryGroup;
1993     OBJECT_ATTRIBUTES ObjectAttributes;
1994     PSID Owner;
1995     ULONG i;
1996     PTOKEN Token;
1997     NTSTATUS Status;
1998 
1999     /* Don't ever expire */
2000     Expiration.QuadPart = -1;
2001 
2002     /* All groups mandatory and enabled */
2003     GroupAttributes = SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT;
2004     OwnerAttributes = SE_GROUP_ENABLED | SE_GROUP_OWNER | SE_GROUP_ENABLED_BY_DEFAULT;
2005 
2006     /* User is Local System */
2007     UserSid.Sid = SeLocalSystemSid;
2008     UserSid.Attributes = 0;
2009 
2010     /* Primary group is Local System */
2011     PrimaryGroup = SeLocalSystemSid;
2012 
2013     /* Owner is Administrators */
2014     Owner = SeAliasAdminsSid;
2015 
2016     /* Groups are Administrators, World, and Authenticated Users */
2017     Groups[0].Sid = SeAliasAdminsSid;
2018     Groups[0].Attributes = OwnerAttributes;
2019     Groups[1].Sid = SeWorldSid;
2020     Groups[1].Attributes = GroupAttributes;
2021     Groups[2].Sid = SeAuthenticatedUsersSid;
2022     Groups[2].Attributes = GroupAttributes;
2023     GroupsLength = sizeof(SID_AND_ATTRIBUTES) +
2024                    SeLengthSid(Groups[0].Sid) +
2025                    SeLengthSid(Groups[1].Sid) +
2026                    SeLengthSid(Groups[2].Sid);
2027     ASSERT(GroupsLength <= sizeof(Groups));
2028 
2029     /* Setup the privileges */
2030     i = 0;
2031     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
2032     Privileges[i++].Luid = SeTcbPrivilege;
2033 
2034     Privileges[i].Attributes = 0;
2035     Privileges[i++].Luid = SeCreateTokenPrivilege;
2036 
2037     Privileges[i].Attributes = 0;
2038     Privileges[i++].Luid = SeTakeOwnershipPrivilege;
2039 
2040     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
2041     Privileges[i++].Luid = SeCreatePagefilePrivilege;
2042 
2043     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
2044     Privileges[i++].Luid = SeLockMemoryPrivilege;
2045 
2046     Privileges[i].Attributes = 0;
2047     Privileges[i++].Luid = SeAssignPrimaryTokenPrivilege;
2048 
2049     Privileges[i].Attributes = 0;
2050     Privileges[i++].Luid = SeIncreaseQuotaPrivilege;
2051 
2052     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
2053     Privileges[i++].Luid = SeIncreaseBasePriorityPrivilege;
2054 
2055     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
2056     Privileges[i++].Luid = SeCreatePermanentPrivilege;
2057 
2058     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
2059     Privileges[i++].Luid = SeDebugPrivilege;
2060 
2061     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
2062     Privileges[i++].Luid = SeAuditPrivilege;
2063 
2064     Privileges[i].Attributes = 0;
2065     Privileges[i++].Luid = SeSecurityPrivilege;
2066 
2067     Privileges[i].Attributes = 0;
2068     Privileges[i++].Luid = SeSystemEnvironmentPrivilege;
2069 
2070     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
2071     Privileges[i++].Luid = SeChangeNotifyPrivilege;
2072 
2073     Privileges[i].Attributes = 0;
2074     Privileges[i++].Luid = SeBackupPrivilege;
2075 
2076     Privileges[i].Attributes = 0;
2077     Privileges[i++].Luid = SeRestorePrivilege;
2078 
2079     Privileges[i].Attributes = 0;
2080     Privileges[i++].Luid = SeShutdownPrivilege;
2081 
2082     Privileges[i].Attributes = 0;
2083     Privileges[i++].Luid = SeLoadDriverPrivilege;
2084 
2085     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
2086     Privileges[i++].Luid = SeProfileSingleProcessPrivilege;
2087 
2088     Privileges[i].Attributes = 0;
2089     Privileges[i++].Luid = SeSystemtimePrivilege;
2090     ASSERT(i == 20);
2091 
2092     /* Setup the object attributes */
2093     InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
2094     ASSERT(SeSystemDefaultDacl != NULL);
2095 
2096     /* Create the token */
2097     Status = SepCreateToken((PHANDLE)&Token,
2098                             KernelMode,
2099                             0,
2100                             &ObjectAttributes,
2101                             TokenPrimary,
2102                             SecurityAnonymous,
2103                             &SeSystemAuthenticationId,
2104                             &Expiration,
2105                             &UserSid,
2106                             3,
2107                             Groups,
2108                             GroupsLength,
2109                             20,
2110                             Privileges,
2111                             Owner,
2112                             PrimaryGroup,
2113                             SeSystemDefaultDacl,
2114                             &SeSystemTokenSource,
2115                             TRUE);
2116     ASSERT(Status == STATUS_SUCCESS);
2117 
2118     /* Return the token */
2119     return Token;
2120 }
2121 
2122 /**
2123  * @brief
2124  * Creates the anonymous logon token for the system. The difference between this
2125  * token and the other one is the inclusion of everyone SID group (being SeWorldSid).
2126  * The other token lacks such group.
2127  *
2128  * @return
2129  * Returns the system's anonymous logon token if the operations have
2130  * completed successfully.
2131  */
2132 CODE_SEG("INIT")
2133 PTOKEN
2134 SepCreateSystemAnonymousLogonToken(VOID)
2135 {
2136     SID_AND_ATTRIBUTES Groups[32], UserSid;
2137     PSID PrimaryGroup;
2138     PTOKEN Token;
2139     ULONG GroupsLength;
2140     LARGE_INTEGER Expiration;
2141     OBJECT_ATTRIBUTES ObjectAttributes;
2142     NTSTATUS Status;
2143 
2144     /* The token never expires */
2145     Expiration.QuadPart = -1;
2146 
2147     /* The user is the anonymous logon */
2148     UserSid.Sid = SeAnonymousLogonSid;
2149     UserSid.Attributes = 0;
2150 
2151     /* The primary group is also the anonymous logon */
2152     PrimaryGroup = SeAnonymousLogonSid;
2153 
2154     /* The only group for the token is the World */
2155     Groups[0].Sid = SeWorldSid;
2156     Groups[0].Attributes = SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT;
2157     GroupsLength = sizeof(SID_AND_ATTRIBUTES) +
2158                    SeLengthSid(Groups[0].Sid);
2159     ASSERT(GroupsLength <= sizeof(Groups));
2160 
2161     /* Initialise the object attributes for the token */
2162     InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
2163     ASSERT(SeSystemAnonymousLogonDacl != NULL);
2164 
2165     /* Create token */
2166     Status = SepCreateToken((PHANDLE)&Token,
2167                             KernelMode,
2168                             0,
2169                             &ObjectAttributes,
2170                             TokenPrimary,
2171                             SecurityAnonymous,
2172                             &SeAnonymousAuthenticationId,
2173                             &Expiration,
2174                             &UserSid,
2175                             1,
2176                             Groups,
2177                             GroupsLength,
2178                             0,
2179                             NULL,
2180                             NULL,
2181                             PrimaryGroup,
2182                             SeSystemAnonymousLogonDacl,
2183                             &SeSystemTokenSource,
2184                             TRUE);
2185     ASSERT(Status == STATUS_SUCCESS);
2186 
2187     /* Return the anonymous logon token */
2188     return Token;
2189 }
2190 
2191 /**
2192  * @brief
2193  * Creates the anonymous logon token for the system. This kind of token
2194  * doesn't include the everyone SID group (being SeWorldSid).
2195  *
2196  * @return
2197  * Returns the system's anonymous logon token if the operations have
2198  * completed successfully.
2199  */
2200 CODE_SEG("INIT")
2201 PTOKEN
2202 SepCreateSystemAnonymousLogonTokenNoEveryone(VOID)
2203 {
2204     SID_AND_ATTRIBUTES UserSid;
2205     PSID PrimaryGroup;
2206     PTOKEN Token;
2207     LARGE_INTEGER Expiration;
2208     OBJECT_ATTRIBUTES ObjectAttributes;
2209     NTSTATUS Status;
2210 
2211     /* The token never expires */
2212     Expiration.QuadPart = -1;
2213 
2214     /* The user is the anonymous logon */
2215     UserSid.Sid = SeAnonymousLogonSid;
2216     UserSid.Attributes = 0;
2217 
2218     /* The primary group is also the anonymous logon */
2219     PrimaryGroup = SeAnonymousLogonSid;
2220 
2221     /* Initialise the object attributes for the token */
2222     InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
2223     ASSERT(SeSystemAnonymousLogonDacl != NULL);
2224 
2225     /* Create token */
2226     Status = SepCreateToken((PHANDLE)&Token,
2227                             KernelMode,
2228                             0,
2229                             &ObjectAttributes,
2230                             TokenPrimary,
2231                             SecurityAnonymous,
2232                             &SeAnonymousAuthenticationId,
2233                             &Expiration,
2234                             &UserSid,
2235                             0,
2236                             NULL,
2237                             0,
2238                             0,
2239                             NULL,
2240                             NULL,
2241                             PrimaryGroup,
2242                             SeSystemAnonymousLogonDacl,
2243                             &SeSystemTokenSource,
2244                             TRUE);
2245     ASSERT(Status == STATUS_SUCCESS);
2246 
2247     /* Return the anonymous (not including everyone) logon token */
2248     return Token;
2249 }
2250 
2251 /* PUBLIC FUNCTIONS ***********************************************************/
2252 
2253 /**
2254  * @unimplemented
2255  * @brief
2256  * Filters an access token from an existing token, making it more restricted
2257  * than the previous one.
2258  *
2259  * @param[in] ExistingToken
2260  * An existing token for filtering.
2261  *
2262  * @param[in] Flags
2263  * Privilege flag options. This parameter argument influences how the token
2264  * is filtered. Such parameter can be 0.
2265  *
2266  * @param[in] SidsToDisable
2267  * Array of SIDs to disable.
2268  *
2269  * @param[in] PrivilegesToDelete
2270  * Array of privileges to delete.
2271  *
2272  * @param[in] RestrictedSids
2273  * An array of restricted SIDs for the new filtered token.
2274  *
2275  * @param[out] FilteredToken
2276  * The newly filtered token, returned to the caller.
2277  *
2278  * @return
2279  * To be added...
2280  */
2281 NTSTATUS
2282 NTAPI
2283 SeFilterToken(
2284     _In_ PACCESS_TOKEN ExistingToken,
2285     _In_ ULONG Flags,
2286     _In_opt_ PTOKEN_GROUPS SidsToDisable,
2287     _In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete,
2288     _In_opt_ PTOKEN_GROUPS RestrictedSids,
2289     _Out_ PACCESS_TOKEN * FilteredToken)
2290 {
2291     UNIMPLEMENTED;
2292     return STATUS_NOT_IMPLEMENTED;
2293 }
2294 
2295 /**
2296  * @brief
2297  * Queries information details about the given token to the call. The difference
2298  * between NtQueryInformationToken and this routine is that the system call has
2299  * user mode buffer data probing and additional protection checks whereas this
2300  * routine doesn't have any of these. The routine is used exclusively in kernel
2301  * mode.
2302  *
2303  * @param[in] AccessToken
2304  * An access token to be given.
2305  *
2306  * @param[in] TokenInformationClass
2307  * Token information class.
2308  *
2309  * @param[out] TokenInformation
2310  * Buffer with retrieved information. Such information is arbitrary, depending
2311  * on the requested information class.
2312  *
2313  * @return
2314  * Returns STATUS_SUCCESS if the operation to query the desired information
2315  * has completed successfully. STATUS_INSUFFICIENT_RESOURCES is returned if
2316  * pool memory allocation has failed to satisfy an operation. Otherwise
2317  * STATUS_INVALID_INFO_CLASS is returned indicating that the information
2318  * class provided is not supported by the routine.
2319  *
2320  * @remarks
2321  * Only certain information classes are not implemented in this function and
2322  * these are TokenOrigin, TokenGroupsAndPrivileges, TokenRestrictedSids and
2323  * TokenSandBoxInert. The following classes are implemented in NtQueryInformationToken
2324  * only.
2325  */
2326 NTSTATUS
2327 NTAPI
2328 SeQueryInformationToken(
2329     _In_ PACCESS_TOKEN AccessToken,
2330     _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
2331     _Outptr_result_buffer_(_Inexpressible_(token-dependent)) PVOID *TokenInformation)
2332 {
2333     NTSTATUS Status;
2334     PTOKEN Token = (PTOKEN)AccessToken;
2335     ULONG RequiredLength;
2336     union
2337     {
2338         PSID PSid;
2339         ULONG Ulong;
2340     } Unused;
2341 
2342     PAGED_CODE();
2343 
2344     /* Lock the token */
2345     SepAcquireTokenLockShared(Token);
2346 
2347     switch (TokenInformationClass)
2348     {
2349         case TokenUser:
2350         {
2351             PTOKEN_USER tu;
2352 
2353             DPRINT("SeQueryInformationToken(TokenUser)\n");
2354             RequiredLength = sizeof(TOKEN_USER) +
2355                 RtlLengthSid(Token->UserAndGroups[0].Sid);
2356 
2357             /* Allocate the output buffer */
2358             tu = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
2359             if (tu == NULL)
2360             {
2361                 Status = STATUS_INSUFFICIENT_RESOURCES;
2362                 break;
2363             }
2364 
2365             Status = RtlCopySidAndAttributesArray(1,
2366                                                   &Token->UserAndGroups[0],
2367                                                   RequiredLength - sizeof(TOKEN_USER),
2368                                                   &tu->User,
2369                                                   (PSID)(tu + 1),
2370                                                   &Unused.PSid,
2371                                                   &Unused.Ulong);
2372 
2373             /* Return the structure */
2374             *TokenInformation = tu;
2375             Status = STATUS_SUCCESS;
2376             break;
2377         }
2378 
2379         case TokenGroups:
2380         {
2381             PTOKEN_GROUPS tg;
2382             ULONG SidLen;
2383             PSID Sid;
2384 
2385             DPRINT("SeQueryInformationToken(TokenGroups)\n");
2386             RequiredLength = sizeof(tg->GroupCount) +
2387                 RtlLengthSidAndAttributes(Token->UserAndGroupCount - 1, &Token->UserAndGroups[1]);
2388 
2389             SidLen = RequiredLength - sizeof(tg->GroupCount) -
2390                 ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES));
2391 
2392             /* Allocate the output buffer */
2393             tg = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
2394             if (tg == NULL)
2395             {
2396                 Status = STATUS_INSUFFICIENT_RESOURCES;
2397                 break;
2398             }
2399 
2400             Sid = (PSID)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
2401                          ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES)));
2402 
2403             tg->GroupCount = Token->UserAndGroupCount - 1;
2404             Status = RtlCopySidAndAttributesArray(Token->UserAndGroupCount - 1,
2405                                                   &Token->UserAndGroups[1],
2406                                                   SidLen,
2407                                                   &tg->Groups[0],
2408                                                   Sid,
2409                                                   &Unused.PSid,
2410                                                   &Unused.Ulong);
2411 
2412             /* Return the structure */
2413             *TokenInformation = tg;
2414             Status = STATUS_SUCCESS;
2415             break;
2416         }
2417 
2418         case TokenPrivileges:
2419         {
2420             PTOKEN_PRIVILEGES tp;
2421 
2422             DPRINT("SeQueryInformationToken(TokenPrivileges)\n");
2423             RequiredLength = sizeof(tp->PrivilegeCount) +
2424                 (Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
2425 
2426             /* Allocate the output buffer */
2427             tp = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
2428             if (tp == NULL)
2429             {
2430                 Status = STATUS_INSUFFICIENT_RESOURCES;
2431                 break;
2432             }
2433 
2434             tp->PrivilegeCount = Token->PrivilegeCount;
2435             RtlCopyLuidAndAttributesArray(Token->PrivilegeCount,
2436                                           Token->Privileges,
2437                                           &tp->Privileges[0]);
2438 
2439             /* Return the structure */
2440             *TokenInformation = tp;
2441             Status = STATUS_SUCCESS;
2442             break;
2443         }
2444 
2445         case TokenOwner:
2446         {
2447             PTOKEN_OWNER to;
2448             ULONG SidLen;
2449 
2450             DPRINT("SeQueryInformationToken(TokenOwner)\n");
2451             SidLen = RtlLengthSid(Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
2452             RequiredLength = sizeof(TOKEN_OWNER) + SidLen;
2453 
2454             /* Allocate the output buffer */
2455             to = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
2456             if (to == NULL)
2457             {
2458                 Status = STATUS_INSUFFICIENT_RESOURCES;
2459                 break;
2460             }
2461 
2462             to->Owner = (PSID)(to + 1);
2463             Status = RtlCopySid(SidLen,
2464                                 to->Owner,
2465                                 Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
2466 
2467             /* Return the structure */
2468             *TokenInformation = to;
2469             Status = STATUS_SUCCESS;
2470             break;
2471         }
2472 
2473         case TokenPrimaryGroup:
2474         {
2475             PTOKEN_PRIMARY_GROUP tpg;
2476             ULONG SidLen;
2477 
2478             DPRINT("SeQueryInformationToken(TokenPrimaryGroup)\n");
2479             SidLen = RtlLengthSid(Token->PrimaryGroup);
2480             RequiredLength = sizeof(TOKEN_PRIMARY_GROUP) + SidLen;
2481 
2482             /* Allocate the output buffer */
2483             tpg = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
2484             if (tpg == NULL)
2485             {
2486                 Status = STATUS_INSUFFICIENT_RESOURCES;
2487                 break;
2488             }
2489 
2490             tpg->PrimaryGroup = (PSID)(tpg + 1);
2491             Status = RtlCopySid(SidLen,
2492                                 tpg->PrimaryGroup,
2493                                 Token->PrimaryGroup);
2494 
2495             /* Return the structure */
2496             *TokenInformation = tpg;
2497             Status = STATUS_SUCCESS;
2498             break;
2499         }
2500 
2501         case TokenDefaultDacl:
2502         {
2503             PTOKEN_DEFAULT_DACL tdd;
2504 
2505             DPRINT("SeQueryInformationToken(TokenDefaultDacl)\n");
2506             RequiredLength = sizeof(TOKEN_DEFAULT_DACL);
2507 
2508             if (Token->DefaultDacl != NULL)
2509                 RequiredLength += Token->DefaultDacl->AclSize;
2510 
2511             /* Allocate the output buffer */
2512             tdd = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
2513             if (tdd == NULL)
2514             {
2515                 Status = STATUS_INSUFFICIENT_RESOURCES;
2516                 break;
2517             }
2518 
2519             if (Token->DefaultDacl != NULL)
2520             {
2521                 tdd->DefaultDacl = (PACL)(tdd + 1);
2522                 RtlCopyMemory(tdd->DefaultDacl,
2523                               Token->DefaultDacl,
2524                               Token->DefaultDacl->AclSize);
2525             }
2526             else
2527             {
2528                 tdd->DefaultDacl = NULL;
2529             }
2530 
2531             /* Return the structure */
2532             *TokenInformation = tdd;
2533             Status = STATUS_SUCCESS;
2534             break;
2535         }
2536 
2537         case TokenSource:
2538         {
2539             PTOKEN_SOURCE ts;
2540 
2541             DPRINT("SeQueryInformationToken(TokenSource)\n");
2542             RequiredLength = sizeof(TOKEN_SOURCE);
2543 
2544             /* Allocate the output buffer */
2545             ts = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
2546             if (ts == NULL)
2547             {
2548                 Status = STATUS_INSUFFICIENT_RESOURCES;
2549                 break;
2550             }
2551 
2552             *ts = Token->TokenSource;
2553 
2554             /* Return the structure */
2555             *TokenInformation = ts;
2556             Status = STATUS_SUCCESS;
2557             break;
2558         }
2559 
2560         case TokenType:
2561         {
2562             PTOKEN_TYPE tt;
2563 
2564             DPRINT("SeQueryInformationToken(TokenType)\n");
2565             RequiredLength = sizeof(TOKEN_TYPE);
2566 
2567             /* Allocate the output buffer */
2568             tt = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
2569             if (tt == NULL)
2570             {
2571                 Status = STATUS_INSUFFICIENT_RESOURCES;
2572                 break;
2573             }
2574 
2575             *tt = Token->TokenType;
2576 
2577             /* Return the structure */
2578             *TokenInformation = tt;
2579             Status = STATUS_SUCCESS;
2580             break;
2581         }
2582 
2583         case TokenImpersonationLevel:
2584         {
2585             PSECURITY_IMPERSONATION_LEVEL sil;
2586 
2587             DPRINT("SeQueryInformationToken(TokenImpersonationLevel)\n");
2588             RequiredLength = sizeof(SECURITY_IMPERSONATION_LEVEL);
2589 
2590             /* Fail if the token is not an impersonation token */
2591             if (Token->TokenType != TokenImpersonation)
2592             {
2593                 Status = STATUS_INVALID_INFO_CLASS;
2594                 break;
2595             }
2596 
2597             /* Allocate the output buffer */
2598             sil = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
2599             if (sil == NULL)
2600             {
2601                 Status = STATUS_INSUFFICIENT_RESOURCES;
2602                 break;
2603             }
2604 
2605             *sil = Token->ImpersonationLevel;
2606 
2607             /* Return the structure */
2608             *TokenInformation = sil;
2609             Status = STATUS_SUCCESS;
2610             break;
2611         }
2612 
2613         case TokenStatistics:
2614         {
2615             PTOKEN_STATISTICS ts;
2616 
2617             DPRINT("SeQueryInformationToken(TokenStatistics)\n");
2618             RequiredLength = sizeof(TOKEN_STATISTICS);
2619 
2620             /* Allocate the output buffer */
2621             ts = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
2622             if (ts == NULL)
2623             {
2624                 Status = STATUS_INSUFFICIENT_RESOURCES;
2625                 break;
2626             }
2627 
2628             ts->TokenId = Token->TokenId;
2629             ts->AuthenticationId = Token->AuthenticationId;
2630             ts->ExpirationTime = Token->ExpirationTime;
2631             ts->TokenType = Token->TokenType;
2632             ts->ImpersonationLevel = Token->ImpersonationLevel;
2633             ts->DynamicCharged = Token->DynamicCharged;
2634             ts->DynamicAvailable = Token->DynamicAvailable;
2635             ts->GroupCount = Token->UserAndGroupCount - 1;
2636             ts->PrivilegeCount = Token->PrivilegeCount;
2637             ts->ModifiedId = Token->ModifiedId;
2638 
2639             /* Return the structure */
2640             *TokenInformation = ts;
2641             Status = STATUS_SUCCESS;
2642             break;
2643         }
2644 
2645         case TokenSessionId:
2646         {
2647             DPRINT("SeQueryInformationToken(TokenSessionId)\n");
2648             Status = SeQuerySessionIdToken(Token, (PULONG)TokenInformation);
2649             break;
2650         }
2651 
2652         default:
2653             DPRINT1("SeQueryInformationToken(%d) invalid information class\n", TokenInformationClass);
2654             Status = STATUS_INVALID_INFO_CLASS;
2655             break;
2656     }
2657 
2658     /* Release the lock of the token */
2659     SepReleaseTokenLock(Token);
2660 
2661     return Status;
2662 }
2663 
2664 /**
2665  * @brief
2666  * Queries the session ID of an access token.
2667  *
2668  * @param[in] Token
2669  * A valid access token where the session ID has to be gathered.
2670  *
2671  * @param[out] pSessionId
2672  * The returned pointer to a session ID to the caller.
2673  *
2674  * @return
2675  * Returns STATUS_SUCCESS.
2676  */
2677 NTSTATUS
2678 NTAPI
2679 SeQuerySessionIdToken(
2680     _In_ PACCESS_TOKEN Token,
2681     _Out_ PULONG pSessionId)
2682 {
2683     PAGED_CODE();
2684 
2685     /* Lock the token */
2686     SepAcquireTokenLockShared(Token);
2687 
2688     *pSessionId = ((PTOKEN)Token)->SessionId;
2689 
2690     /* Unlock the token */
2691     SepReleaseTokenLock(Token);
2692 
2693     return STATUS_SUCCESS;
2694 }
2695 
2696 /**
2697  * @brief
2698  * Queries the authentication ID of an access token.
2699  *
2700  * @param[in] Token
2701  * A valid access token where the authentication ID has to be gathered.
2702  *
2703  * @param[out] pSessionId
2704  * The returned pointer to an authentication ID to the caller.
2705  *
2706  * @return
2707  * Returns STATUS_SUCCESS.
2708  */
2709 NTSTATUS
2710 NTAPI
2711 SeQueryAuthenticationIdToken(
2712     _In_ PACCESS_TOKEN Token,
2713     _Out_ PLUID LogonId)
2714 {
2715     PAGED_CODE();
2716 
2717     *LogonId = ((PTOKEN)Token)->AuthenticationId;
2718 
2719     return STATUS_SUCCESS;
2720 }
2721 
2722 /**
2723  * @brief
2724  * Gathers the security impersonation level of an access token.
2725  *
2726  * @param[in] Token
2727  * A valid access token where the impersonation level has to be gathered.
2728  *
2729  * @return
2730  * Returns the security impersonation level from a valid token.
2731  */
2732 SECURITY_IMPERSONATION_LEVEL
2733 NTAPI
2734 SeTokenImpersonationLevel(
2735     _In_ PACCESS_TOKEN Token)
2736 {
2737     PAGED_CODE();
2738 
2739     return ((PTOKEN)Token)->ImpersonationLevel;
2740 }
2741 
2742 /**
2743  * @brief
2744  * Gathers the token type of an access token. A token ca be either
2745  * a primary token or impersonation token.
2746  *
2747  * @param[in] Token
2748  * A valid access token where the token type has to be gathered.
2749  *
2750  * @return
2751  * Returns the token type from a valid token.
2752  */
2753 TOKEN_TYPE
2754 NTAPI
2755 SeTokenType(
2756     _In_ PACCESS_TOKEN Token)
2757 {
2758     PAGED_CODE();
2759 
2760     return ((PTOKEN)Token)->TokenType;
2761 }
2762 
2763 /**
2764  * @brief
2765  * Determines if a token is either an admin token or not. Such
2766  * condition is checked based upon TOKEN_HAS_ADMIN_GROUP flag,
2767  * which means if the respective access token belongs to an
2768  * administrator group or not.
2769  *
2770  * @param[in] Token
2771  * A valid access token to determine if such token is admin or not.
2772  *
2773  * @return
2774  * Returns TRUE if the token is an admin one, FALSE otherwise.
2775  */
2776 BOOLEAN
2777 NTAPI
2778 SeTokenIsAdmin(
2779     _In_ PACCESS_TOKEN Token)
2780 {
2781     PAGED_CODE();
2782 
2783     // NOTE: Win7+ instead really checks the list of groups in the token
2784     // (since TOKEN_HAS_ADMIN_GROUP == TOKEN_WRITE_RESTRICTED ...)
2785     return (((PTOKEN)Token)->TokenFlags & TOKEN_HAS_ADMIN_GROUP) != 0;
2786 }
2787 
2788 /**
2789  * @brief
2790  * Determines if a token is restricted or not, based upon the token
2791  * flags.
2792  *
2793  * @param[in] Token
2794  * A valid access token to determine if such token is restricted.
2795  *
2796  * @return
2797  * Returns TRUE if the token is restricted, FALSE otherwise.
2798  */
2799 BOOLEAN
2800 NTAPI
2801 SeTokenIsRestricted(
2802     _In_ PACCESS_TOKEN Token)
2803 {
2804     PAGED_CODE();
2805 
2806     return (((PTOKEN)Token)->TokenFlags & TOKEN_IS_RESTRICTED) != 0;
2807 }
2808 
2809 /**
2810  * @brief
2811  * Determines if a token is write restricted, that is, nobody can write anything
2812  * to it.
2813  *
2814  * @param[in] Token
2815  * A valid access token to determine if such token is write restricted.
2816  *
2817  * @return
2818  * Returns TRUE if the token is write restricted, FALSE otherwise.
2819  *
2820  * @remarks
2821  * First introduced in NT 5.1 SP2 x86 (5.1.2600.2622), absent in NT 5.2,
2822  * then finally re-introduced in Vista+.
2823  */
2824 BOOLEAN
2825 NTAPI
2826 SeTokenIsWriteRestricted(
2827     _In_ PACCESS_TOKEN Token)
2828 {
2829     PAGED_CODE();
2830 
2831     // NOTE: NT 5.1 SP2 x86 checks the SE_BACKUP_PRIVILEGES_CHECKED flag
2832     // while Vista+ checks the TOKEN_WRITE_RESTRICTED flag as one expects.
2833     return (((PTOKEN)Token)->TokenFlags & SE_BACKUP_PRIVILEGES_CHECKED) != 0;
2834 }
2835 
2836 /**
2837  * @brief
2838  * Ensures that client impersonation can occur by checking if the token
2839  * we're going to assign as the impersonation token can be actually impersonated
2840  * in the first place. The routine is used primarily by PsImpersonateClient.
2841  *
2842  * @param[in] ProcessToken
2843  * Token from a process.
2844  *
2845  * @param[in] TokenToImpersonate
2846  * Token that we are going to impersonate.
2847  *
2848  * @param[in] ImpersonationLevel
2849  * Security impersonation level grade.
2850  *
2851  * @return
2852  * Returns TRUE if the conditions checked are met for token impersonation,
2853  * FALSE otherwise.
2854  */
2855 BOOLEAN
2856 NTAPI
2857 SeTokenCanImpersonate(
2858     _In_ PTOKEN ProcessToken,
2859     _In_ PTOKEN TokenToImpersonate,
2860     _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel)
2861 {
2862     BOOLEAN CanImpersonate;
2863     PAGED_CODE();
2864 
2865     /*
2866      * SecurityAnonymous and SecurityIdentification levels do not
2867      * allow impersonation. If we get such levels from the call
2868      * then something's seriously wrong.
2869      */
2870     ASSERT(ImpersonationLevel != SecurityAnonymous ||
2871            ImpersonationLevel != SecurityIdentification);
2872 
2873     /* Time to lock our tokens */
2874     SepAcquireTokenLockShared(ProcessToken);
2875     SepAcquireTokenLockShared(TokenToImpersonate);
2876 
2877     /* What kind of authentication ID does the token have? */
2878     if (RtlEqualLuid(&TokenToImpersonate->AuthenticationId,
2879                      &SeAnonymousAuthenticationId))
2880     {
2881         /*
2882          * OK, it looks like the token has an anonymous
2883          * authentication. Is that token created by the system?
2884          */
2885         if (TokenToImpersonate->TokenSource.SourceName != SeSystemTokenSource.SourceName &&
2886             !RtlEqualLuid(&TokenToImpersonate->TokenSource.SourceIdentifier, &SeSystemTokenSource.SourceIdentifier))
2887         {
2888             /* It isn't, we can't impersonate regular tokens */
2889             DPRINT("SeTokenCanImpersonate(): Token has an anonymous authentication ID, can't impersonate!\n");
2890             CanImpersonate = FALSE;
2891             goto Quit;
2892         }
2893     }
2894 
2895     /* Are the SID values from both tokens equal? */
2896     if (!RtlEqualSid(ProcessToken->UserAndGroups->Sid,
2897                      TokenToImpersonate->UserAndGroups->Sid))
2898     {
2899         /* They aren't, bail out */
2900         DPRINT("SeTokenCanImpersonate(): Tokens SIDs are not equal!\n");
2901         CanImpersonate = FALSE;
2902         goto Quit;
2903     }
2904 
2905     /*
2906      * Make sure the tokens aren't diverged in terms of
2907      * restrictions, that is, one token is restricted
2908      * but the other one isn't.
2909      */
2910     if (SeTokenIsRestricted(ProcessToken) !=
2911         SeTokenIsRestricted(TokenToImpersonate))
2912     {
2913         /*
2914          * One token is restricted so we cannot
2915          * continue further at this point, bail out.
2916          */
2917         DPRINT("SeTokenCanImpersonate(): One token is restricted, can't continue!\n");
2918         CanImpersonate = FALSE;
2919         goto Quit;
2920     }
2921 
2922     /* If we've reached that far then we can impersonate! */
2923     DPRINT("SeTokenCanImpersonate(): We can impersonate.\n");
2924     CanImpersonate = TRUE;
2925 
2926 Quit:
2927     /* We're done, unlock the tokens now */
2928     SepReleaseTokenLock(ProcessToken);
2929     SepReleaseTokenLock(TokenToImpersonate);
2930 
2931     return CanImpersonate;
2932 }
2933 
2934 /* SYSTEM CALLS ***************************************************************/
2935 
2936 /**
2937  * @brief
2938  * Queries a specific type of information in regard of an access token based upon
2939  * the information class. The calling thread must have specific access rights in order
2940  * to obtain specific information about the token.
2941  *
2942  * @param[in] TokenHandle
2943  * A handle of a token where information is to be gathered.
2944  *
2945  * @param[in] TokenInformationClass
2946  * Token information class.
2947  *
2948  * @param[out] TokenInformation
2949  * A returned output buffer with token information, which information is arbitrarily upon
2950  * the information class chosen.
2951  *
2952  * @param[in] TokenInformationLength
2953  * Length of the token information buffer, in bytes.
2954  *
2955  * @param[out] ReturnLength
2956  * If specified in the call, the function returns the total length size of the token
2957  * information buffer..
2958  *
2959  * @return
2960  * Returns STATUS_SUCCESS if information querying has completed successfully.
2961  * STATUS_BUFFER_TOO_SMALL is returned if the information length that represents
2962  * the token information buffer is not greater than the required length.
2963  * STATUS_INVALID_HANDLE is returned if the token handle is not a valid one.
2964  * STATUS_INVALID_INFO_CLASS is returned if the information class is not a valid
2965  * one (that is, the class doesn't belong to TOKEN_INFORMATION_CLASS). A failure
2966  * NTSTATUS code is returned otherwise.
2967  */
2968 _Must_inspect_result_
2969 __kernel_entry
2970 NTSTATUS
2971 NTAPI
2972 NtQueryInformationToken(
2973     _In_ HANDLE TokenHandle,
2974     _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
2975     _Out_writes_bytes_to_opt_(TokenInformationLength, *ReturnLength)
2976         PVOID TokenInformation,
2977     _In_ ULONG TokenInformationLength,
2978     _Out_ PULONG ReturnLength)
2979 {
2980     NTSTATUS Status;
2981     KPROCESSOR_MODE PreviousMode;
2982     PTOKEN Token;
2983     ULONG RequiredLength;
2984     union
2985     {
2986         PSID PSid;
2987         ULONG Ulong;
2988     } Unused;
2989 
2990     PAGED_CODE();
2991 
2992     PreviousMode = ExGetPreviousMode();
2993 
2994     /* Check buffers and class validity */
2995     Status = DefaultQueryInfoBufferCheck(TokenInformationClass,
2996                                          SeTokenInformationClass,
2997                                          RTL_NUMBER_OF(SeTokenInformationClass),
2998                                          TokenInformation,
2999                                          TokenInformationLength,
3000                                          ReturnLength,
3001                                          NULL,
3002                                          PreviousMode,
3003                                          TRUE);
3004     if (!NT_SUCCESS(Status))
3005     {
3006         DPRINT("NtQueryInformationToken() failed, Status: 0x%x\n", Status);
3007         return Status;
3008     }
3009 
3010     Status = ObReferenceObjectByHandle(TokenHandle,
3011                                        (TokenInformationClass == TokenSource) ? TOKEN_QUERY_SOURCE : TOKEN_QUERY,
3012                                        SeTokenObjectType,
3013                                        PreviousMode,
3014                                        (PVOID*)&Token,
3015                                        NULL);
3016     if (NT_SUCCESS(Status))
3017     {
3018         /* Lock the token */
3019         SepAcquireTokenLockShared(Token);
3020 
3021         switch (TokenInformationClass)
3022         {
3023             case TokenUser:
3024             {
3025                 PTOKEN_USER tu = (PTOKEN_USER)TokenInformation;
3026 
3027                 DPRINT("NtQueryInformationToken(TokenUser)\n");
3028                 RequiredLength = sizeof(TOKEN_USER) +
3029                     RtlLengthSid(Token->UserAndGroups[0].Sid);
3030 
3031                 _SEH2_TRY
3032                 {
3033                     if (TokenInformationLength >= RequiredLength)
3034                     {
3035                         Status = RtlCopySidAndAttributesArray(1,
3036                                                               &Token->UserAndGroups[0],
3037                                                               RequiredLength - sizeof(TOKEN_USER),
3038                                                               &tu->User,
3039                                                               (PSID)(tu + 1),
3040                                                               &Unused.PSid,
3041                                                               &Unused.Ulong);
3042                     }
3043                     else
3044                     {
3045                         Status = STATUS_BUFFER_TOO_SMALL;
3046                     }
3047 
3048                     if (ReturnLength != NULL)
3049                     {
3050                         *ReturnLength = RequiredLength;
3051                     }
3052                 }
3053                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3054                 {
3055                     Status = _SEH2_GetExceptionCode();
3056                 }
3057                 _SEH2_END;
3058 
3059                 break;
3060             }
3061 
3062             case TokenGroups:
3063             {
3064                 PTOKEN_GROUPS tg = (PTOKEN_GROUPS)TokenInformation;
3065 
3066                 DPRINT("NtQueryInformationToken(TokenGroups)\n");
3067                 RequiredLength = sizeof(tg->GroupCount) +
3068                     RtlLengthSidAndAttributes(Token->UserAndGroupCount - 1, &Token->UserAndGroups[1]);
3069 
3070                 _SEH2_TRY
3071                 {
3072                     if (TokenInformationLength >= RequiredLength)
3073                     {
3074                         ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) -
3075                             ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES));
3076                         PSID Sid = (PSID_AND_ATTRIBUTES)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
3077                                                          ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES)));
3078 
3079                         tg->GroupCount = Token->UserAndGroupCount - 1;
3080                         Status = RtlCopySidAndAttributesArray(Token->UserAndGroupCount - 1,
3081                                                               &Token->UserAndGroups[1],
3082                                                               SidLen,
3083                                                               &tg->Groups[0],
3084                                                               Sid,
3085                                                               &Unused.PSid,
3086                                                               &Unused.Ulong);
3087                     }
3088                     else
3089                     {
3090                         Status = STATUS_BUFFER_TOO_SMALL;
3091                     }
3092 
3093                     if (ReturnLength != NULL)
3094                     {
3095                         *ReturnLength = RequiredLength;
3096                     }
3097                 }
3098                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3099                 {
3100                     Status = _SEH2_GetExceptionCode();
3101                 }
3102                 _SEH2_END;
3103 
3104                 break;
3105             }
3106 
3107             case TokenPrivileges:
3108             {
3109                 PTOKEN_PRIVILEGES tp = (PTOKEN_PRIVILEGES)TokenInformation;
3110 
3111                 DPRINT("NtQueryInformationToken(TokenPrivileges)\n");
3112                 RequiredLength = sizeof(tp->PrivilegeCount) +
3113                     (Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
3114 
3115                 _SEH2_TRY
3116                 {
3117                     if (TokenInformationLength >= RequiredLength)
3118                     {
3119                         tp->PrivilegeCount = Token->PrivilegeCount;
3120                         RtlCopyLuidAndAttributesArray(Token->PrivilegeCount,
3121                                                       Token->Privileges,
3122                                                       &tp->Privileges[0]);
3123                     }
3124                     else
3125                     {
3126                         Status = STATUS_BUFFER_TOO_SMALL;
3127                     }
3128 
3129                     if (ReturnLength != NULL)
3130                     {
3131                         *ReturnLength = RequiredLength;
3132                     }
3133                 }
3134                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3135                 {
3136                     Status = _SEH2_GetExceptionCode();
3137                 }
3138                 _SEH2_END;
3139 
3140                 break;
3141             }
3142 
3143             case TokenOwner:
3144             {
3145                 PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation;
3146                 ULONG SidLen;
3147 
3148                 DPRINT("NtQueryInformationToken(TokenOwner)\n");
3149                 SidLen = RtlLengthSid(Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
3150                 RequiredLength = sizeof(TOKEN_OWNER) + SidLen;
3151 
3152                 _SEH2_TRY
3153                 {
3154                     if (TokenInformationLength >= RequiredLength)
3155                     {
3156                         to->Owner = (PSID)(to + 1);
3157                         Status = RtlCopySid(SidLen,
3158                                             to->Owner,
3159                                             Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
3160                     }
3161                     else
3162                     {
3163                         Status = STATUS_BUFFER_TOO_SMALL;
3164                     }
3165 
3166                     if (ReturnLength != NULL)
3167                     {
3168                         *ReturnLength = RequiredLength;
3169                     }
3170                 }
3171                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3172                 {
3173                     Status = _SEH2_GetExceptionCode();
3174                 }
3175                 _SEH2_END;
3176 
3177                 break;
3178             }
3179 
3180             case TokenPrimaryGroup:
3181             {
3182                 PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation;
3183                 ULONG SidLen;
3184 
3185                 DPRINT("NtQueryInformationToken(TokenPrimaryGroup)\n");
3186                 SidLen = RtlLengthSid(Token->PrimaryGroup);
3187                 RequiredLength = sizeof(TOKEN_PRIMARY_GROUP) + SidLen;
3188 
3189                 _SEH2_TRY
3190                 {
3191                     if (TokenInformationLength >= RequiredLength)
3192                     {
3193                         tpg->PrimaryGroup = (PSID)(tpg + 1);
3194                         Status = RtlCopySid(SidLen,
3195                                             tpg->PrimaryGroup,
3196                                             Token->PrimaryGroup);
3197                     }
3198                     else
3199                     {
3200                         Status = STATUS_BUFFER_TOO_SMALL;
3201                     }
3202 
3203                     if (ReturnLength != NULL)
3204                     {
3205                         *ReturnLength = RequiredLength;
3206                     }
3207                 }
3208                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3209                 {
3210                     Status = _SEH2_GetExceptionCode();
3211                 }
3212                 _SEH2_END;
3213 
3214                 break;
3215             }
3216 
3217             case TokenDefaultDacl:
3218             {
3219                 PTOKEN_DEFAULT_DACL tdd = (PTOKEN_DEFAULT_DACL)TokenInformation;
3220 
3221                 DPRINT("NtQueryInformationToken(TokenDefaultDacl)\n");
3222                 RequiredLength = sizeof(TOKEN_DEFAULT_DACL);
3223 
3224                 if (Token->DefaultDacl != NULL)
3225                     RequiredLength += Token->DefaultDacl->AclSize;
3226 
3227                 _SEH2_TRY
3228                 {
3229                     if (TokenInformationLength >= RequiredLength)
3230                     {
3231                         if (Token->DefaultDacl != NULL)
3232                         {
3233                             tdd->DefaultDacl = (PACL)(tdd + 1);
3234                             RtlCopyMemory(tdd->DefaultDacl,
3235                                           Token->DefaultDacl,
3236                                           Token->DefaultDacl->AclSize);
3237                         }
3238                         else
3239                         {
3240                             tdd->DefaultDacl = NULL;
3241                         }
3242                     }
3243                     else
3244                     {
3245                         Status = STATUS_BUFFER_TOO_SMALL;
3246                     }
3247 
3248                     if (ReturnLength != NULL)
3249                     {
3250                         *ReturnLength = RequiredLength;
3251                     }
3252                 }
3253                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3254                 {
3255                     Status = _SEH2_GetExceptionCode();
3256                 }
3257                 _SEH2_END;
3258 
3259                 break;
3260             }
3261 
3262             case TokenSource:
3263             {
3264                 PTOKEN_SOURCE ts = (PTOKEN_SOURCE)TokenInformation;
3265 
3266                 DPRINT("NtQueryInformationToken(TokenSource)\n");
3267                 RequiredLength = sizeof(TOKEN_SOURCE);
3268 
3269                 _SEH2_TRY
3270                 {
3271                     if (TokenInformationLength >= RequiredLength)
3272                     {
3273                         *ts = Token->TokenSource;
3274                     }
3275                     else
3276                     {
3277                         Status = STATUS_BUFFER_TOO_SMALL;
3278                     }
3279 
3280                     if (ReturnLength != NULL)
3281                     {
3282                         *ReturnLength = RequiredLength;
3283                     }
3284                 }
3285                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3286                 {
3287                     Status = _SEH2_GetExceptionCode();
3288                 }
3289                 _SEH2_END;
3290 
3291                 break;
3292             }
3293 
3294             case TokenType:
3295             {
3296                 PTOKEN_TYPE tt = (PTOKEN_TYPE)TokenInformation;
3297 
3298                 DPRINT("NtQueryInformationToken(TokenType)\n");
3299                 RequiredLength = sizeof(TOKEN_TYPE);
3300 
3301                 _SEH2_TRY
3302                 {
3303                     if (TokenInformationLength >= RequiredLength)
3304                     {
3305                         *tt = Token->TokenType;
3306                     }
3307                     else
3308                     {
3309                         Status = STATUS_BUFFER_TOO_SMALL;
3310                     }
3311 
3312                     if (ReturnLength != NULL)
3313                     {
3314                         *ReturnLength = RequiredLength;
3315                     }
3316                 }
3317                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3318                 {
3319                     Status = _SEH2_GetExceptionCode();
3320                 }
3321                 _SEH2_END;
3322 
3323                 break;
3324             }
3325 
3326             case TokenImpersonationLevel:
3327             {
3328                 PSECURITY_IMPERSONATION_LEVEL sil = (PSECURITY_IMPERSONATION_LEVEL)TokenInformation;
3329 
3330                 DPRINT("NtQueryInformationToken(TokenImpersonationLevel)\n");
3331 
3332                 /* Fail if the token is not an impersonation token */
3333                 if (Token->TokenType != TokenImpersonation)
3334                 {
3335                     Status = STATUS_INVALID_INFO_CLASS;
3336                     break;
3337                 }
3338 
3339                 RequiredLength = sizeof(SECURITY_IMPERSONATION_LEVEL);
3340 
3341                 _SEH2_TRY
3342                 {
3343                     if (TokenInformationLength >= RequiredLength)
3344                     {
3345                         *sil = Token->ImpersonationLevel;
3346                     }
3347                     else
3348                     {
3349                         Status = STATUS_BUFFER_TOO_SMALL;
3350                     }
3351 
3352                     if (ReturnLength != NULL)
3353                     {
3354                         *ReturnLength = RequiredLength;
3355                     }
3356                 }
3357                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3358                 {
3359                     Status = _SEH2_GetExceptionCode();
3360                 }
3361                 _SEH2_END;
3362 
3363                 break;
3364             }
3365 
3366             case TokenStatistics:
3367             {
3368                 PTOKEN_STATISTICS ts = (PTOKEN_STATISTICS)TokenInformation;
3369 
3370                 DPRINT("NtQueryInformationToken(TokenStatistics)\n");
3371                 RequiredLength = sizeof(TOKEN_STATISTICS);
3372 
3373                 _SEH2_TRY
3374                 {
3375                     if (TokenInformationLength >= RequiredLength)
3376                     {
3377                         ts->TokenId = Token->TokenId;
3378                         ts->AuthenticationId = Token->AuthenticationId;
3379                         ts->ExpirationTime = Token->ExpirationTime;
3380                         ts->TokenType = Token->TokenType;
3381                         ts->ImpersonationLevel = Token->ImpersonationLevel;
3382                         ts->DynamicCharged = Token->DynamicCharged;
3383                         ts->DynamicAvailable = Token->DynamicAvailable;
3384                         ts->GroupCount = Token->UserAndGroupCount - 1;
3385                         ts->PrivilegeCount = Token->PrivilegeCount;
3386                         ts->ModifiedId = Token->ModifiedId;
3387                     }
3388                     else
3389                     {
3390                         Status = STATUS_BUFFER_TOO_SMALL;
3391                     }
3392 
3393                     if (ReturnLength != NULL)
3394                     {
3395                         *ReturnLength = RequiredLength;
3396                     }
3397                 }
3398                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3399                 {
3400                     Status = _SEH2_GetExceptionCode();
3401                 }
3402                 _SEH2_END;
3403 
3404                 break;
3405             }
3406 
3407             case TokenOrigin:
3408             {
3409                 PTOKEN_ORIGIN to = (PTOKEN_ORIGIN)TokenInformation;
3410 
3411                 DPRINT("NtQueryInformationToken(TokenOrigin)\n");
3412                 RequiredLength = sizeof(TOKEN_ORIGIN);
3413 
3414                 _SEH2_TRY
3415                 {
3416                     if (TokenInformationLength >= RequiredLength)
3417                     {
3418                         RtlCopyLuid(&to->OriginatingLogonSession,
3419                                     &Token->AuthenticationId);
3420                     }
3421                     else
3422                     {
3423                         Status = STATUS_BUFFER_TOO_SMALL;
3424                     }
3425 
3426                     if (ReturnLength != NULL)
3427                     {
3428                         *ReturnLength = RequiredLength;
3429                     }
3430                 }
3431                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3432                 {
3433                     Status = _SEH2_GetExceptionCode();
3434                 }
3435                 _SEH2_END;
3436 
3437                 break;
3438             }
3439 
3440             case TokenGroupsAndPrivileges:
3441                 DPRINT1("NtQueryInformationToken(TokenGroupsAndPrivileges) not implemented\n");
3442                 Status = STATUS_NOT_IMPLEMENTED;
3443                 break;
3444 
3445             case TokenRestrictedSids:
3446             {
3447                 PTOKEN_GROUPS tg = (PTOKEN_GROUPS)TokenInformation;
3448 
3449                 DPRINT("NtQueryInformationToken(TokenRestrictedSids)\n");
3450                 RequiredLength = sizeof(tg->GroupCount) +
3451                 RtlLengthSidAndAttributes(Token->RestrictedSidCount, Token->RestrictedSids);
3452 
3453                 _SEH2_TRY
3454                 {
3455                     if (TokenInformationLength >= RequiredLength)
3456                     {
3457                         ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) -
3458                             (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES));
3459                         PSID Sid = (PSID)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
3460                                           (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES)));
3461 
3462                         tg->GroupCount = Token->RestrictedSidCount;
3463                         Status = RtlCopySidAndAttributesArray(Token->RestrictedSidCount,
3464                                                               Token->RestrictedSids,
3465                                                               SidLen,
3466                                                               &tg->Groups[0],
3467                                                               Sid,
3468                                                               &Unused.PSid,
3469                                                               &Unused.Ulong);
3470                     }
3471                     else
3472                     {
3473                         Status = STATUS_BUFFER_TOO_SMALL;
3474                     }
3475 
3476                     if (ReturnLength != NULL)
3477                     {
3478                         *ReturnLength = RequiredLength;
3479                     }
3480                 }
3481                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3482                 {
3483                     Status = _SEH2_GetExceptionCode();
3484                 }
3485                 _SEH2_END;
3486 
3487                 break;
3488             }
3489 
3490             case TokenSandBoxInert:
3491                 DPRINT1("NtQueryInformationToken(TokenSandboxInert) not implemented\n");
3492                 Status = STATUS_NOT_IMPLEMENTED;
3493                 break;
3494 
3495             case TokenSessionId:
3496             {
3497                 ULONG SessionId = 0;
3498 
3499                 DPRINT("NtQueryInformationToken(TokenSessionId)\n");
3500 
3501                 Status = SeQuerySessionIdToken(Token, &SessionId);
3502                 if (NT_SUCCESS(Status))
3503                 {
3504                     _SEH2_TRY
3505                     {
3506                         /* Buffer size was already verified, no need to check here again */
3507                         *(PULONG)TokenInformation = SessionId;
3508 
3509                         if (ReturnLength != NULL)
3510                         {
3511                             *ReturnLength = sizeof(ULONG);
3512                         }
3513                     }
3514                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3515                     {
3516                         Status = _SEH2_GetExceptionCode();
3517                     }
3518                     _SEH2_END;
3519                 }
3520 
3521                 break;
3522             }
3523 
3524             default:
3525                 DPRINT1("NtQueryInformationToken(%d) invalid information class\n", TokenInformationClass);
3526                 Status = STATUS_INVALID_INFO_CLASS;
3527                 break;
3528         }
3529 
3530         /* Unlock and dereference the token */
3531         SepReleaseTokenLock(Token);
3532         ObDereferenceObject(Token);
3533     }
3534 
3535     return Status;
3536 }
3537 
3538 /**
3539  * @unimplemented
3540  * @brief
3541  * Sets (modifies) some specific information in regard of an access token. The
3542  * calling thread must have specific access rights in order to modify token's
3543  * information data.
3544  *
3545  * @param[in] TokenHandle
3546  * A handle of a token where information is to be modified.
3547  *
3548  * @param[in] TokenInformationClass
3549  * Token information class.
3550  *
3551  * @param[in] TokenInformation
3552  * An arbitrary pointer to a buffer with token information to set. Such
3553  * arbitrary buffer depends on the information class chosen that the caller
3554  * wants to modify such information data of a token.
3555  *
3556  * @param[in] TokenInformationLength
3557  * Length of the token information buffer, in bytes.
3558  *
3559  * @return
3560  * Returns STATUS_SUCCESS if information setting has completed successfully.
3561  * STATUS_INFO_LENGTH_MISMATCH is returned if the information length of the
3562  * buffer is less than the required length. STATUS_INSUFFICIENT_RESOURCES is
3563  * returned if memory pool allocation has failed. STATUS_PRIVILEGE_NOT_HELD
3564  * is returned if the calling thread hasn't the required privileges to perform
3565  * the operation in question. A failure NTSTATUS code is returned otherwise.
3566  *
3567  * @remarks
3568  * The function is partly implemented, mainly TokenOrigin and TokenDefaultDacl.
3569  */
3570 _Must_inspect_result_
3571 __kernel_entry
3572 NTSTATUS
3573 NTAPI
3574 NtSetInformationToken(
3575     _In_ HANDLE TokenHandle,
3576     _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
3577     _In_reads_bytes_(TokenInformationLength) PVOID TokenInformation,
3578     _In_ ULONG TokenInformationLength)
3579 {
3580     NTSTATUS Status;
3581     PTOKEN Token;
3582     KPROCESSOR_MODE PreviousMode;
3583     ULONG NeededAccess = TOKEN_ADJUST_DEFAULT;
3584 
3585     PAGED_CODE();
3586 
3587     PreviousMode = ExGetPreviousMode();
3588 
3589     Status = DefaultSetInfoBufferCheck(TokenInformationClass,
3590                                        SeTokenInformationClass,
3591                                        RTL_NUMBER_OF(SeTokenInformationClass),
3592                                        TokenInformation,
3593                                        TokenInformationLength,
3594                                        PreviousMode);
3595     if (!NT_SUCCESS(Status))
3596     {
3597         /* Invalid buffers */
3598         DPRINT("NtSetInformationToken() failed, Status: 0x%x\n", Status);
3599         return Status;
3600     }
3601 
3602     if (TokenInformationClass == TokenSessionId)
3603     {
3604         NeededAccess |= TOKEN_ADJUST_SESSIONID;
3605     }
3606 
3607     Status = ObReferenceObjectByHandle(TokenHandle,
3608                                        NeededAccess,
3609                                        SeTokenObjectType,
3610                                        PreviousMode,
3611                                        (PVOID*)&Token,
3612                                        NULL);
3613     if (NT_SUCCESS(Status))
3614     {
3615         switch (TokenInformationClass)
3616         {
3617             case TokenOwner:
3618             {
3619                 if (TokenInformationLength >= sizeof(TOKEN_OWNER))
3620                 {
3621                     PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation;
3622                     PSID InputSid = NULL, CapturedSid;
3623                     ULONG DefaultOwnerIndex;
3624 
3625                     _SEH2_TRY
3626                     {
3627                         InputSid = to->Owner;
3628                     }
3629                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3630                     {
3631                         Status = _SEH2_GetExceptionCode();
3632                         _SEH2_YIELD(goto Cleanup);
3633                     }
3634                     _SEH2_END;
3635 
3636                     Status = SepCaptureSid(InputSid,
3637                                            PreviousMode,
3638                                            PagedPool,
3639                                            FALSE,
3640                                            &CapturedSid);
3641                     if (NT_SUCCESS(Status))
3642                     {
3643                         /* Lock the token */
3644                         SepAcquireTokenLockExclusive(Token);
3645 
3646                         /* Find the owner amongst the existing token user and groups */
3647                         Status = SepFindPrimaryGroupAndDefaultOwner(Token,
3648                                                                     NULL,
3649                                                                     CapturedSid,
3650                                                                     NULL,
3651                                                                     &DefaultOwnerIndex);
3652                         if (NT_SUCCESS(Status))
3653                         {
3654                             /* Found it */
3655                             Token->DefaultOwnerIndex = DefaultOwnerIndex;
3656                             ExAllocateLocallyUniqueId(&Token->ModifiedId);
3657                         }
3658 
3659                         /* Unlock the token */
3660                         SepReleaseTokenLock(Token);
3661 
3662                         SepReleaseSid(CapturedSid,
3663                                       PreviousMode,
3664                                       FALSE);
3665                     }
3666                 }
3667                 else
3668                 {
3669                     Status = STATUS_INFO_LENGTH_MISMATCH;
3670                 }
3671                 break;
3672             }
3673 
3674             case TokenPrimaryGroup:
3675             {
3676                 if (TokenInformationLength >= sizeof(TOKEN_PRIMARY_GROUP))
3677                 {
3678                     PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation;
3679                     PSID InputSid = NULL, CapturedSid;
3680                     ULONG PrimaryGroupIndex;
3681 
3682                     _SEH2_TRY
3683                     {
3684                         InputSid = tpg->PrimaryGroup;
3685                     }
3686                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3687                     {
3688                         Status = _SEH2_GetExceptionCode();
3689                         _SEH2_YIELD(goto Cleanup);
3690                     }
3691                     _SEH2_END;
3692 
3693                     Status = SepCaptureSid(InputSid,
3694                                            PreviousMode,
3695                                            PagedPool,
3696                                            FALSE,
3697                                            &CapturedSid);
3698                     if (NT_SUCCESS(Status))
3699                     {
3700                         /* Lock the token */
3701                         SepAcquireTokenLockExclusive(Token);
3702 
3703                         /* Find the primary group amongst the existing token user and groups */
3704                         Status = SepFindPrimaryGroupAndDefaultOwner(Token,
3705                                                                     CapturedSid,
3706                                                                     NULL,
3707                                                                     &PrimaryGroupIndex,
3708                                                                     NULL);
3709                         if (NT_SUCCESS(Status))
3710                         {
3711                             /* Found it */
3712                             Token->PrimaryGroup = Token->UserAndGroups[PrimaryGroupIndex].Sid;
3713                             ExAllocateLocallyUniqueId(&Token->ModifiedId);
3714                         }
3715 
3716                         /* Unlock the token */
3717                         SepReleaseTokenLock(Token);
3718 
3719                         SepReleaseSid(CapturedSid,
3720                                       PreviousMode,
3721                                       FALSE);
3722                     }
3723                 }
3724                 else
3725                 {
3726                     Status = STATUS_INFO_LENGTH_MISMATCH;
3727                 }
3728                 break;
3729             }
3730 
3731             case TokenDefaultDacl:
3732             {
3733                 if (TokenInformationLength >= sizeof(TOKEN_DEFAULT_DACL))
3734                 {
3735                     PTOKEN_DEFAULT_DACL tdd = (PTOKEN_DEFAULT_DACL)TokenInformation;
3736                     PACL InputAcl = NULL;
3737 
3738                     _SEH2_TRY
3739                     {
3740                         InputAcl = tdd->DefaultDacl;
3741                     }
3742                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3743                     {
3744                         Status = _SEH2_GetExceptionCode();
3745                         _SEH2_YIELD(goto Cleanup);
3746                     }
3747                     _SEH2_END;
3748 
3749                     if (InputAcl != NULL)
3750                     {
3751                         PACL CapturedAcl;
3752 
3753                         /* Capture and copy the dacl */
3754                         Status = SepCaptureAcl(InputAcl,
3755                                                PreviousMode,
3756                                                PagedPool,
3757                                                TRUE,
3758                                                &CapturedAcl);
3759                         if (NT_SUCCESS(Status))
3760                         {
3761                             ULONG DynamicLength;
3762 
3763                             /* Lock the token */
3764                             SepAcquireTokenLockExclusive(Token);
3765 
3766                             //
3767                             // NOTE: So far our dynamic area only contains
3768                             // the default dacl, so this makes the following
3769                             // code pretty simple. The day where it stores
3770                             // other data, the code will require adaptations.
3771                             //
3772 
3773                             DynamicLength = Token->DynamicAvailable;
3774                             // Add here any other data length present in the dynamic area...
3775                             if (Token->DefaultDacl)
3776                                 DynamicLength += Token->DefaultDacl->AclSize;
3777 
3778                             /* Reallocate the dynamic area if it is too small */
3779                             Status = STATUS_SUCCESS;
3780                             if ((DynamicLength < CapturedAcl->AclSize) ||
3781                                 (Token->DynamicPart == NULL))
3782                             {
3783                                 PVOID NewDynamicPart;
3784 
3785                                 NewDynamicPart = ExAllocatePoolWithTag(PagedPool,
3786                                                                        CapturedAcl->AclSize,
3787                                                                        TAG_TOKEN_DYNAMIC);
3788                                 if (NewDynamicPart == NULL)
3789                                 {
3790                                     Status = STATUS_INSUFFICIENT_RESOURCES;
3791                                 }
3792                                 else
3793                                 {
3794                                     if (Token->DynamicPart != NULL)
3795                                     {
3796                                         // RtlCopyMemory(NewDynamicPart, Token->DynamicPart, DynamicLength);
3797                                         ExFreePoolWithTag(Token->DynamicPart, TAG_TOKEN_DYNAMIC);
3798                                     }
3799                                     Token->DynamicPart = NewDynamicPart;
3800                                     Token->DynamicAvailable = 0;
3801                                 }
3802                             }
3803                             else
3804                             {
3805                                 Token->DynamicAvailable = DynamicLength - CapturedAcl->AclSize;
3806                             }
3807 
3808                             if (NT_SUCCESS(Status))
3809                             {
3810                                 /* Set the new dacl */
3811                                 Token->DefaultDacl = (PVOID)Token->DynamicPart;
3812                                 RtlCopyMemory(Token->DefaultDacl,
3813                                               CapturedAcl,
3814                                               CapturedAcl->AclSize);
3815 
3816                                 ExAllocateLocallyUniqueId(&Token->ModifiedId);
3817                             }
3818 
3819                             /* Unlock the token */
3820                             SepReleaseTokenLock(Token);
3821 
3822                             ExFreePoolWithTag(CapturedAcl, TAG_ACL);
3823                         }
3824                     }
3825                     else
3826                     {
3827                         /* Lock the token */
3828                         SepAcquireTokenLockExclusive(Token);
3829 
3830                         /* Clear the default dacl if present */
3831                         if (Token->DefaultDacl != NULL)
3832                         {
3833                             Token->DynamicAvailable += Token->DefaultDacl->AclSize;
3834                             RtlZeroMemory(Token->DefaultDacl, Token->DefaultDacl->AclSize);
3835                             Token->DefaultDacl = NULL;
3836 
3837                             ExAllocateLocallyUniqueId(&Token->ModifiedId);
3838                         }
3839 
3840                         /* Unlock the token */
3841                         SepReleaseTokenLock(Token);
3842                     }
3843                 }
3844                 else
3845                 {
3846                     Status = STATUS_INFO_LENGTH_MISMATCH;
3847                 }
3848                 break;
3849             }
3850 
3851             case TokenSessionId:
3852             {
3853                 ULONG SessionId = 0;
3854 
3855                 _SEH2_TRY
3856                 {
3857                     /* Buffer size was already verified, no need to check here again */
3858                     SessionId = *(PULONG)TokenInformation;
3859                 }
3860                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3861                 {
3862                     Status = _SEH2_GetExceptionCode();
3863                     _SEH2_YIELD(goto Cleanup);
3864                 }
3865                 _SEH2_END;
3866 
3867                 /* Check for TCB privilege */
3868                 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
3869                 {
3870                     Status = STATUS_PRIVILEGE_NOT_HELD;
3871                     break;
3872                 }
3873 
3874                 /* Lock the token */
3875                 SepAcquireTokenLockExclusive(Token);
3876 
3877                 Token->SessionId = SessionId;
3878                 ExAllocateLocallyUniqueId(&Token->ModifiedId);
3879 
3880                 /* Unlock the token */
3881                 SepReleaseTokenLock(Token);
3882 
3883                 break;
3884             }
3885 
3886             case TokenSessionReference:
3887             {
3888                 ULONG SessionReference;
3889 
3890                 _SEH2_TRY
3891                 {
3892                     /* Buffer size was already verified, no need to check here again */
3893                     SessionReference = *(PULONG)TokenInformation;
3894                 }
3895                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3896                 {
3897                     Status = _SEH2_GetExceptionCode();
3898                     _SEH2_YIELD(goto Cleanup);
3899                 }
3900                 _SEH2_END;
3901 
3902                 /* Check for TCB privilege */
3903                 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
3904                 {
3905                     Status = STATUS_PRIVILEGE_NOT_HELD;
3906                     goto Cleanup;
3907                 }
3908 
3909                 /* Check if it is 0 */
3910                 if (SessionReference == 0)
3911                 {
3912                     ULONG OldTokenFlags;
3913 
3914                     /* Lock the token */
3915                     SepAcquireTokenLockExclusive(Token);
3916 
3917                     /* Atomically set the flag in the token */
3918                     OldTokenFlags = RtlInterlockedSetBits(&Token->TokenFlags,
3919                                                           TOKEN_SESSION_NOT_REFERENCED);
3920                     /*
3921                      * If the flag was already set, do not dereference again
3922                      * the logon session. Use SessionReference as an indicator
3923                      * to know whether to really dereference the session.
3924                      */
3925                     if (OldTokenFlags == Token->TokenFlags)
3926                         SessionReference = ULONG_MAX;
3927 
3928                     /*
3929                      * Otherwise if the flag was never set but just for this first time then
3930                      * remove the referenced logon session data from the token and dereference
3931                      * the logon session when needed.
3932                      */
3933                     if (SessionReference == 0)
3934                     {
3935                         SepRmRemoveLogonSessionFromToken(Token);
3936                         SepRmDereferenceLogonSession(&Token->AuthenticationId);
3937                     }
3938 
3939                     /* Unlock the token */
3940                     SepReleaseTokenLock(Token);
3941                 }
3942                 break;
3943             }
3944 
3945             case TokenAuditPolicy:
3946             {
3947                 PTOKEN_AUDIT_POLICY_INFORMATION PolicyInformation =
3948                     (PTOKEN_AUDIT_POLICY_INFORMATION)TokenInformation;
3949                 SEP_AUDIT_POLICY AuditPolicy;
3950                 ULONG i;
3951 
3952                 _SEH2_TRY
3953                 {
3954                     ProbeForRead(PolicyInformation,
3955                                  FIELD_OFFSET(TOKEN_AUDIT_POLICY_INFORMATION,
3956                                               Policies[PolicyInformation->PolicyCount]),
3957                                  sizeof(ULONG));
3958 
3959                     /* Loop all policies in the structure */
3960                     for (i = 0; i < PolicyInformation->PolicyCount; i++)
3961                     {
3962                         /* Set the corresponding bits in the packed structure */
3963                         switch (PolicyInformation->Policies[i].Category)
3964                         {
3965                             case AuditCategorySystem:
3966                                 AuditPolicy.PolicyElements.System = PolicyInformation->Policies[i].Value;
3967                                 break;
3968 
3969                             case AuditCategoryLogon:
3970                                 AuditPolicy.PolicyElements.Logon = PolicyInformation->Policies[i].Value;
3971                                 break;
3972 
3973                             case AuditCategoryObjectAccess:
3974                                 AuditPolicy.PolicyElements.ObjectAccess = PolicyInformation->Policies[i].Value;
3975                                 break;
3976 
3977                             case AuditCategoryPrivilegeUse:
3978                                 AuditPolicy.PolicyElements.PrivilegeUse = PolicyInformation->Policies[i].Value;
3979                                 break;
3980 
3981                             case AuditCategoryDetailedTracking:
3982                                 AuditPolicy.PolicyElements.DetailedTracking = PolicyInformation->Policies[i].Value;
3983                                 break;
3984 
3985                             case AuditCategoryPolicyChange:
3986                                 AuditPolicy.PolicyElements.PolicyChange = PolicyInformation->Policies[i].Value;
3987                                 break;
3988 
3989                             case AuditCategoryAccountManagement:
3990                                 AuditPolicy.PolicyElements.AccountManagement = PolicyInformation->Policies[i].Value;
3991                                 break;
3992 
3993                             case AuditCategoryDirectoryServiceAccess:
3994                                 AuditPolicy.PolicyElements.DirectoryServiceAccess = PolicyInformation->Policies[i].Value;
3995                                 break;
3996 
3997                             case AuditCategoryAccountLogon:
3998                                 AuditPolicy.PolicyElements.AccountLogon = PolicyInformation->Policies[i].Value;
3999                                 break;
4000                         }
4001                     }
4002                 }
4003                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4004                 {
4005                     Status = _SEH2_GetExceptionCode();
4006                     _SEH2_YIELD(goto Cleanup);
4007                 }
4008                 _SEH2_END;
4009 
4010                 /* Check for TCB privilege */
4011                 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
4012                 {
4013                     Status = STATUS_PRIVILEGE_NOT_HELD;
4014                     break;
4015                 }
4016 
4017                 /* Lock the token */
4018                 SepAcquireTokenLockExclusive(Token);
4019 
4020                 /* Set the new audit policy */
4021                 Token->AuditPolicy = AuditPolicy;
4022                 ExAllocateLocallyUniqueId(&Token->ModifiedId);
4023 
4024                 /* Unlock the token */
4025                 SepReleaseTokenLock(Token);
4026 
4027                 break;
4028             }
4029 
4030             case TokenOrigin:
4031             {
4032                 TOKEN_ORIGIN TokenOrigin;
4033 
4034                 _SEH2_TRY
4035                 {
4036                     /* Copy the token origin */
4037                     TokenOrigin = *(PTOKEN_ORIGIN)TokenInformation;
4038                 }
4039                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4040                 {
4041                     Status = _SEH2_GetExceptionCode();
4042                     _SEH2_YIELD(goto Cleanup);
4043                 }
4044                 _SEH2_END;
4045 
4046                 /* Check for TCB privilege */
4047                 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
4048                 {
4049                     Status = STATUS_PRIVILEGE_NOT_HELD;
4050                     break;
4051                 }
4052 
4053                 /* Lock the token */
4054                 SepAcquireTokenLockExclusive(Token);
4055 
4056                 /* Check if there is no token origin set yet */
4057                 if (RtlIsZeroLuid(&Token->OriginatingLogonSession))
4058                 {
4059                     /* Set the token origin */
4060                     Token->OriginatingLogonSession =
4061                         TokenOrigin.OriginatingLogonSession;
4062 
4063                     ExAllocateLocallyUniqueId(&Token->ModifiedId);
4064                 }
4065 
4066                 /* Unlock the token */
4067                 SepReleaseTokenLock(Token);
4068 
4069                 break;
4070             }
4071 
4072             default:
4073             {
4074                 DPRINT1("Invalid TokenInformationClass: 0x%lx\n",
4075                         TokenInformationClass);
4076                 Status = STATUS_INVALID_INFO_CLASS;
4077                 break;
4078             }
4079         }
4080 Cleanup:
4081         ObDereferenceObject(Token);
4082     }
4083 
4084     if (!NT_SUCCESS(Status))
4085     {
4086         DPRINT1("NtSetInformationToken failed with Status 0x%lx\n", Status);
4087     }
4088 
4089     return Status;
4090 }
4091 
4092 /**
4093  * @brief
4094  * Duplicates a token.
4095  *
4096  * @param[in] ExistingTokenHandle
4097  * An existing token to duplicate.
4098  *
4099  * @param[in] DesiredAccess
4100  * The desired access rights for the new duplicated token.
4101  *
4102  * @param[in] ObjectAttributes
4103  * Object attributes for the new duplicated token.
4104  *
4105  * @param[in] EffectiveOnly
4106  * If set to TRUE, the function removes all the disabled privileges and groups
4107  * of the token to duplicate.
4108  *
4109  * @param[in] TokenType
4110  * Type of token to assign to the duplicated token.
4111  *
4112  * @param[out] NewTokenHandle
4113  * The returned duplicated token handle.
4114  *
4115  * @return
4116  * STATUS_SUCCESS is returned if token duplication has completed successfully.
4117  * STATUS_BAD_IMPERSONATION_LEVEL is returned if the caller erroneously wants
4118  * to raise the impersonation level even though the conditions do not permit
4119  * it. A failure NTSTATUS code is returned otherwise.
4120  *
4121  * @remarks
4122  * Some sources claim 4th param is ImpersonationLevel, but on W2K
4123  * this is certainly NOT true, although I can't say for sure that EffectiveOnly
4124  * is correct either. -Gunnar
4125  * This is true. EffectiveOnly overrides SQOS.EffectiveOnly. - IAI
4126  * NOTE for readers: http://hex.pp.ua/nt/NtDuplicateToken.php is therefore
4127  * wrong in that regard, while MSDN documentation is correct.
4128  */
4129 _Must_inspect_result_
4130 __kernel_entry
4131 NTSTATUS
4132 NTAPI
4133 NtDuplicateToken(
4134     _In_ HANDLE ExistingTokenHandle,
4135     _In_ ACCESS_MASK DesiredAccess,
4136     _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
4137     _In_ BOOLEAN EffectiveOnly,
4138     _In_ TOKEN_TYPE TokenType,
4139     _Out_ PHANDLE NewTokenHandle)
4140 {
4141     KPROCESSOR_MODE PreviousMode;
4142     HANDLE hToken;
4143     PTOKEN Token;
4144     PTOKEN NewToken;
4145     PSECURITY_QUALITY_OF_SERVICE CapturedSecurityQualityOfService;
4146     BOOLEAN QoSPresent;
4147     OBJECT_HANDLE_INFORMATION HandleInformation;
4148     NTSTATUS Status;
4149 
4150     PAGED_CODE();
4151 
4152     if (TokenType != TokenImpersonation &&
4153         TokenType != TokenPrimary)
4154     {
4155         return STATUS_INVALID_PARAMETER;
4156     }
4157 
4158     PreviousMode = KeGetPreviousMode();
4159 
4160     if (PreviousMode != KernelMode)
4161     {
4162         _SEH2_TRY
4163         {
4164             ProbeForWriteHandle(NewTokenHandle);
4165         }
4166         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4167         {
4168             /* Return the exception code */
4169             _SEH2_YIELD(return _SEH2_GetExceptionCode());
4170         }
4171         _SEH2_END;
4172     }
4173 
4174     Status = SepCaptureSecurityQualityOfService(ObjectAttributes,
4175                                                 PreviousMode,
4176                                                 PagedPool,
4177                                                 FALSE,
4178                                                 &CapturedSecurityQualityOfService,
4179                                                 &QoSPresent);
4180     if (!NT_SUCCESS(Status))
4181     {
4182         DPRINT1("NtDuplicateToken() failed to capture QoS! Status: 0x%x\n", Status);
4183         return Status;
4184     }
4185 
4186     Status = ObReferenceObjectByHandle(ExistingTokenHandle,
4187                                        TOKEN_DUPLICATE,
4188                                        SeTokenObjectType,
4189                                        PreviousMode,
4190                                        (PVOID*)&Token,
4191                                        &HandleInformation);
4192     if (!NT_SUCCESS(Status))
4193     {
4194         DPRINT1("Failed to reference token (Status 0x%lx)\n", Status);
4195         SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
4196                                            PreviousMode,
4197                                            FALSE);
4198         return Status;
4199     }
4200 
4201     /*
4202      * Fail, if the original token is an impersonation token and the caller
4203      * tries to raise the impersonation level of the new token above the
4204      * impersonation level of the original token.
4205      */
4206     if (Token->TokenType == TokenImpersonation)
4207     {
4208         if (QoSPresent &&
4209             CapturedSecurityQualityOfService->ImpersonationLevel >Token->ImpersonationLevel)
4210         {
4211             ObDereferenceObject(Token);
4212             SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
4213                                                PreviousMode,
4214                                                FALSE);
4215             return STATUS_BAD_IMPERSONATION_LEVEL;
4216         }
4217     }
4218 
4219     /*
4220      * Fail, if a primary token is to be created from an impersonation token
4221      * and and the impersonation level of the impersonation token is below SecurityImpersonation.
4222      */
4223     if (Token->TokenType == TokenImpersonation &&
4224         TokenType == TokenPrimary &&
4225         Token->ImpersonationLevel < SecurityImpersonation)
4226     {
4227         ObDereferenceObject(Token);
4228         SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
4229                                            PreviousMode,
4230                                            FALSE);
4231         return STATUS_BAD_IMPERSONATION_LEVEL;
4232     }
4233 
4234     Status = SepDuplicateToken(Token,
4235                                ObjectAttributes,
4236                                EffectiveOnly,
4237                                TokenType,
4238                                (QoSPresent ? CapturedSecurityQualityOfService->ImpersonationLevel : SecurityAnonymous),
4239                                PreviousMode,
4240                                &NewToken);
4241 
4242     ObDereferenceObject(Token);
4243 
4244     if (NT_SUCCESS(Status))
4245     {
4246         Status = ObInsertObject(NewToken,
4247                                 NULL,
4248                                 (DesiredAccess ? DesiredAccess : HandleInformation.GrantedAccess),
4249                                 0,
4250                                 NULL,
4251                                 &hToken);
4252         if (NT_SUCCESS(Status))
4253         {
4254             _SEH2_TRY
4255             {
4256                 *NewTokenHandle = hToken;
4257             }
4258             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4259             {
4260                 Status = _SEH2_GetExceptionCode();
4261             }
4262             _SEH2_END;
4263         }
4264     }
4265 
4266     /* Free the captured structure */
4267     SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
4268                                        PreviousMode,
4269                                        FALSE);
4270 
4271     return Status;
4272 }
4273 
4274 /**
4275  * @brief
4276  * Changes the groups list of SIDs of a token.
4277  *
4278  * @param[in] TokenHandle
4279  * Token handle where the list of groups SIDs are to be adjusted.
4280  *
4281  * @param[in] ResetToDefault
4282  * If set to TRUE, the function resets the list of groups SIDs to default.
4283  * All the rest of parameters are ignored.
4284  *
4285  * @param[in] NewState
4286  * A new list of groups SIDs that the function will use it accordingly to
4287  * modify the current list of groups SIDs of a token.
4288  *
4289  * @param[in] BufferLength
4290  * The length size of the buffer that is pointed by the NewState parameter
4291  * argument, in bytes.
4292  *
4293  * @param[out] PreviousState
4294  * If specified, the function will return to the caller the old list of groups
4295  * SIDs.
4296  *
4297  * @param[out] ReturnLength
4298  * If specified, the function will return the total size length of the old list
4299  * of groups SIDs, in bytes.
4300  *
4301  * @return
4302  * To be added...
4303  */
4304 NTSTATUS
4305 NTAPI
4306 NtAdjustGroupsToken(
4307     _In_ HANDLE TokenHandle,
4308     _In_ BOOLEAN ResetToDefault,
4309     _In_ PTOKEN_GROUPS NewState,
4310     _In_ ULONG BufferLength,
4311     _Out_opt_ PTOKEN_GROUPS PreviousState,
4312     _Out_ PULONG ReturnLength)
4313 {
4314     UNIMPLEMENTED;
4315     return STATUS_NOT_IMPLEMENTED;
4316 }
4317 
4318 /**
4319  * @brief
4320  * Removes a certain amount of privileges of a token based upon the request
4321  * by the caller.
4322  *
4323  * @param[in,out] Token
4324  * Token handle where the privileges are about to be modified.
4325  *
4326  * @param[in] DisableAllPrivileges
4327  * If set to TRUE, the function disables all the privileges.
4328  *
4329  * @param[in] NewState
4330  * A new list of privileges that the function will use it accordingly to
4331  * either disable or enable the said privileges and change them.
4332  *
4333  * @param[in] NewStateCount
4334  * The new total number count of privileges.
4335  *
4336  * @param[out] PreviousState
4337  * If specified, the function will return the previous state list of privileges.
4338  *
4339  * @param[in] ApplyChanges
4340  * If set to TRUE, the function will immediatelly apply the changes onto the
4341  * token's privileges.
4342  *
4343  * @param[out] ChangedPrivileges
4344  * The returned count number of changed privileges.
4345  *
4346  * @param[out] ChangesMade
4347  * If TRUE, the function has made changes to the token's privileges. FALSE
4348  * otherwise.
4349  *
4350  * @return
4351  * Returns STATUS_SUCCESS if the function has successfully changed the list
4352  * of privileges. STATUS_NOT_ALL_ASSIGNED is returned if not every privilege
4353  * has been changed.
4354  */
4355 static
4356 NTSTATUS
4357 SepAdjustPrivileges(
4358     _Inout_ PTOKEN Token,
4359     _In_ BOOLEAN DisableAllPrivileges,
4360     _In_opt_ PLUID_AND_ATTRIBUTES NewState,
4361     _In_ ULONG NewStateCount,
4362     _Out_opt_ PTOKEN_PRIVILEGES PreviousState,
4363     _In_ BOOLEAN ApplyChanges,
4364     _Out_ PULONG ChangedPrivileges,
4365     _Out_ PBOOLEAN ChangesMade)
4366 {
4367     ULONG i, j, PrivilegeCount, ChangeCount, NewAttributes;
4368 
4369     /* Count the found privileges and those that need to be changed */
4370     PrivilegeCount = 0;
4371     ChangeCount = 0;
4372     *ChangesMade = FALSE;
4373 
4374     /* Loop all privileges in the token */
4375     for (i = 0; i < Token->PrivilegeCount; i++)
4376     {
4377         /* Shall all of them be disabled? */
4378         if (DisableAllPrivileges)
4379         {
4380             /* The new attributes are the old ones, but disabled */
4381             NewAttributes = Token->Privileges[i].Attributes & ~SE_PRIVILEGE_ENABLED;
4382         }
4383         else
4384         {
4385             /* Otherwise loop all provided privileges */
4386             for (j = 0; j < NewStateCount; j++)
4387             {
4388                 /* Check if this is the LUID we are looking for */
4389                 if (RtlEqualLuid(&Token->Privileges[i].Luid, &NewState[j].Luid))
4390                 {
4391                     DPRINT("Found privilege\n");
4392 
4393                     /* Copy SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_REMOVED */
4394                     NewAttributes = NewState[j].Attributes;
4395                     NewAttributes &= (SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_REMOVED);
4396                     NewAttributes |= Token->Privileges[i].Attributes & ~SE_PRIVILEGE_ENABLED;
4397 
4398                     /* Stop looking */
4399                     break;
4400                 }
4401             }
4402 
4403             /* Check if we didn't find the privilege */
4404             if (j == NewStateCount)
4405             {
4406                 /* Continue with the token's next privilege */
4407                 continue;
4408             }
4409         }
4410 
4411         /* We found a privilege, count it */
4412         PrivilegeCount++;
4413 
4414         /* Does the privilege need to be changed? */
4415         if (Token->Privileges[i].Attributes != NewAttributes)
4416         {
4417             /* Does the caller want the old privileges? */
4418             if (PreviousState != NULL)
4419             {
4420                 /* Copy the old privilege */
4421                 PreviousState->Privileges[ChangeCount] = Token->Privileges[i];
4422             }
4423 
4424             /* Does the caller want to apply the changes? */
4425             if (ApplyChanges)
4426             {
4427                 /* Shall we remove the privilege? */
4428                 if (NewAttributes & SE_PRIVILEGE_REMOVED)
4429                 {
4430                     /* Set the token as disabled and update flags for it */
4431                     Token->Privileges[i].Attributes &= ~SE_PRIVILEGE_ENABLED;
4432                     SepUpdateSinglePrivilegeFlagToken(Token, i);
4433 
4434                     /* Remove the privilege */
4435                     SepRemovePrivilegeToken(Token, i);
4436 
4437                     *ChangesMade = TRUE;
4438 
4439                     /* Fix the running index and continue with next one */
4440                     i--;
4441                     continue;
4442                 }
4443 
4444                 /* Set the new attributes and update flags */
4445                 Token->Privileges[i].Attributes = NewAttributes;
4446                 SepUpdateSinglePrivilegeFlagToken(Token, i);
4447                 *ChangesMade = TRUE;
4448             }
4449 
4450             /* Increment the change count */
4451             ChangeCount++;
4452         }
4453     }
4454 
4455     /* Set the number of saved privileges */
4456     if (PreviousState != NULL)
4457         PreviousState->PrivilegeCount = ChangeCount;
4458 
4459     /* Return the number of changed privileges */
4460     *ChangedPrivileges = ChangeCount;
4461 
4462     /* Check if we missed some */
4463     if (!DisableAllPrivileges && (PrivilegeCount < NewStateCount))
4464     {
4465         return STATUS_NOT_ALL_ASSIGNED;
4466     }
4467 
4468     return STATUS_SUCCESS;
4469 }
4470 
4471 /**
4472  * @brief
4473  * Removes a certain amount of privileges of a token based upon the request
4474  * by the caller.
4475  *
4476  * @param[in,out] Token
4477  * Token handle where the privileges are about to be modified.
4478  *
4479  * @param[in] DisableAllPrivileges
4480  * If set to TRUE, the function disables all the privileges.
4481  *
4482  * @param[in] NewState
4483  * A new list of privileges that the function will use it accordingly to
4484  * either disable or enable the said privileges and change them.
4485  *
4486  * @param[in] NewStateCount
4487  * The new total number count of privileges.
4488  *
4489  * @param[out] PreviousState
4490  * If specified, the function will return the previous state list of privileges.
4491  *
4492  * @param[in] ApplyChanges
4493  * If set to TRUE, the function will immediatelly apply the changes onto the
4494  * token's privileges.
4495  *
4496  * @param[out] ChangedPrivileges
4497  * The returned count number of changed privileges.
4498  *
4499  * @param[out] ChangesMade
4500  * If TRUE, the function has made changes to the token's privileges. FALSE
4501  * otherwise.
4502  *
4503  * @return
4504  * Returns STATUS_SUCCESS if the function has successfully changed the list
4505  * of privileges. STATUS_NOT_ALL_ASSIGNED is returned if not every privilege
4506  * has been changed.
4507  */
4508 _Must_inspect_result_
4509 __kernel_entry
4510 NTSTATUS
4511 NTAPI
4512 NtAdjustPrivilegesToken(
4513     _In_ HANDLE TokenHandle,
4514     _In_ BOOLEAN DisableAllPrivileges,
4515     _In_opt_ PTOKEN_PRIVILEGES NewState,
4516     _In_ ULONG BufferLength,
4517     _Out_writes_bytes_to_opt_(BufferLength,*ReturnLength)
4518         PTOKEN_PRIVILEGES PreviousState,
4519     _When_(PreviousState!=NULL, _Out_) PULONG ReturnLength)
4520 {
4521     NTSTATUS Status;
4522     KPROCESSOR_MODE PreviousMode;
4523     PTOKEN Token;
4524     PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
4525     ULONG CapturedCount = 0;
4526     ULONG CapturedLength = 0;
4527     ULONG NewStateSize = 0;
4528     ULONG ChangeCount;
4529     ULONG RequiredLength;
4530     BOOLEAN ChangesMade = FALSE;
4531 
4532     PAGED_CODE();
4533 
4534     DPRINT("NtAdjustPrivilegesToken() called\n");
4535 
4536     /* Fail, if we do not disable all privileges but NewState is NULL */
4537     if (DisableAllPrivileges == FALSE && NewState == NULL)
4538         return STATUS_INVALID_PARAMETER;
4539 
4540     PreviousMode = KeGetPreviousMode();
4541     if (PreviousMode != KernelMode)
4542     {
4543         _SEH2_TRY
4544         {
4545             /* Probe NewState */
4546             if (DisableAllPrivileges == FALSE)
4547             {
4548                 /* First probe the header */
4549                 ProbeForRead(NewState, sizeof(TOKEN_PRIVILEGES), sizeof(ULONG));
4550 
4551                 CapturedCount = NewState->PrivilegeCount;
4552                 NewStateSize = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[CapturedCount]);
4553 
4554                 ProbeForRead(NewState, NewStateSize, sizeof(ULONG));
4555             }
4556 
4557             /* Probe PreviousState and ReturnLength */
4558             if (PreviousState != NULL)
4559             {
4560                 ProbeForWrite(PreviousState, BufferLength, sizeof(ULONG));
4561                 ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG));
4562             }
4563         }
4564         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4565         {
4566             /* Return the exception code */
4567             _SEH2_YIELD(return _SEH2_GetExceptionCode());
4568         }
4569         _SEH2_END;
4570     }
4571     else
4572     {
4573         /* This is kernel mode, we trust the caller */
4574         if (DisableAllPrivileges == FALSE)
4575             CapturedCount = NewState->PrivilegeCount;
4576     }
4577 
4578     /* Do we need to capture the new state? */
4579     if (DisableAllPrivileges == FALSE)
4580     {
4581         _SEH2_TRY
4582         {
4583             /* Capture the new state array of privileges */
4584             Status = SeCaptureLuidAndAttributesArray(NewState->Privileges,
4585                                                      CapturedCount,
4586                                                      PreviousMode,
4587                                                      NULL,
4588                                                      0,
4589                                                      PagedPool,
4590                                                      TRUE,
4591                                                      &CapturedPrivileges,
4592                                                      &CapturedLength);
4593         }
4594         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4595         {
4596             /* Return the exception code */
4597             Status = _SEH2_GetExceptionCode();
4598         }
4599         _SEH2_END;
4600 
4601         if (!NT_SUCCESS(Status))
4602             return Status;
4603     }
4604 
4605     /* Reference the token */
4606     Status = ObReferenceObjectByHandle(TokenHandle,
4607                                        TOKEN_ADJUST_PRIVILEGES | (PreviousState != NULL ? TOKEN_QUERY : 0),
4608                                        SeTokenObjectType,
4609                                        PreviousMode,
4610                                        (PVOID*)&Token,
4611                                        NULL);
4612     if (!NT_SUCCESS(Status))
4613     {
4614         DPRINT1("Failed to reference token (Status 0x%lx)\n", Status);
4615 
4616         /* Release the captured privileges */
4617         if (CapturedPrivileges != NULL)
4618         {
4619             SeReleaseLuidAndAttributesArray(CapturedPrivileges,
4620                                             PreviousMode,
4621                                             TRUE);
4622         }
4623 
4624         return Status;
4625     }
4626 
4627     /* Lock the token */
4628     SepAcquireTokenLockExclusive(Token);
4629 
4630     /* Count the privileges that need to be changed, do not apply them yet */
4631     Status = SepAdjustPrivileges(Token,
4632                                  DisableAllPrivileges,
4633                                  CapturedPrivileges,
4634                                  CapturedCount,
4635                                  NULL,
4636                                  FALSE,
4637                                  &ChangeCount,
4638                                  &ChangesMade);
4639 
4640     /* Check if the caller asked for the previous state */
4641     if (PreviousState != NULL)
4642     {
4643         /* Calculate the required length */
4644         RequiredLength = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[ChangeCount]);
4645 
4646         /* Try to return the required buffer length */
4647         _SEH2_TRY
4648         {
4649             *ReturnLength = RequiredLength;
4650         }
4651         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4652         {
4653             /* Do cleanup and return the exception code */
4654             Status = _SEH2_GetExceptionCode();
4655             _SEH2_YIELD(goto Cleanup);
4656         }
4657         _SEH2_END;
4658 
4659         /* Fail, if the buffer length is smaller than the required length */
4660         if (BufferLength < RequiredLength)
4661         {
4662             Status = STATUS_BUFFER_TOO_SMALL;
4663             goto Cleanup;
4664         }
4665     }
4666 
4667     /* Now enter SEH, since we might return the old privileges */
4668     _SEH2_TRY
4669     {
4670         /* This time apply the changes */
4671         Status = SepAdjustPrivileges(Token,
4672                                      DisableAllPrivileges,
4673                                      CapturedPrivileges,
4674                                      CapturedCount,
4675                                      PreviousState,
4676                                      TRUE,
4677                                      &ChangeCount,
4678                                      &ChangesMade);
4679     }
4680     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4681     {
4682         /* Do cleanup and return the exception code */
4683         Status = _SEH2_GetExceptionCode();
4684         ChangesMade = TRUE; // Force write.
4685         _SEH2_YIELD(goto Cleanup);
4686     }
4687     _SEH2_END;
4688 
4689 Cleanup:
4690     /* Touch the token if we made changes */
4691     if (ChangesMade)
4692         ExAllocateLocallyUniqueId(&Token->ModifiedId);
4693 
4694     /* Unlock and dereference the token */
4695     SepReleaseTokenLock(Token);
4696     ObDereferenceObject(Token);
4697 
4698     /* Release the captured privileges */
4699     if (CapturedPrivileges != NULL)
4700     {
4701         SeReleaseLuidAndAttributesArray(CapturedPrivileges,
4702                                         PreviousMode,
4703                                         TRUE);
4704     }
4705 
4706     DPRINT ("NtAdjustPrivilegesToken() done\n");
4707     return Status;
4708 }
4709 
4710 /**
4711  * @brief
4712  * Creates an access token.
4713  *
4714  * @param[out] TokenHandle
4715  * The returned created token handle to the caller.
4716  *
4717  * @param[in] DesiredAccess
4718  * The desired access rights for the token that we're creating.
4719  *
4720  * @param[in] ObjectAttributes
4721  * The object attributes for the token object that we're creating.
4722  *
4723  * @param[in] TokenType
4724  * The type of token to assign for the newly created token.
4725  *
4726  * @param[in] AuthenticationId
4727  * Authentication ID that represents the token's identity.
4728  *
4729  * @param[in] ExpirationTime
4730  * Expiration time for the token. If set to -1, the token never expires.
4731  *
4732  * @param[in] TokenUser
4733  * The main user entity for the token to assign.
4734  *
4735  * @param[in] TokenGroups
4736  * Group list of SIDs for the token to assign.
4737  *
4738  * @param[in] TokenPrivileges
4739  * Privileges for the token.
4740  *
4741  * @param[in] TokenOwner
4742  * The main user that owns the newly created token.
4743  *
4744  * @param[in] TokenPrimaryGroup
4745  * The primary group that represents as the main group of the token.
4746  *
4747  * @param[in] TokenDefaultDacl
4748  * Discretionary access control list for the token. This limits on how
4749  * the token can be used, accessed and used by whom.
4750  *
4751  * @param[in] TokenSource
4752  * The source origin of the token who creates it.
4753  *
4754  * @return
4755  * Returns STATUS_SUCCESS if the function has successfully created the token.
4756  * A failure NTSTATUS code is returned otherwise.
4757  */
4758 __kernel_entry
4759 NTSTATUS
4760 NTAPI
4761 NtCreateToken(
4762     _Out_ PHANDLE TokenHandle,
4763     _In_ ACCESS_MASK DesiredAccess,
4764     _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
4765     _In_ TOKEN_TYPE TokenType,
4766     _In_ PLUID AuthenticationId,
4767     _In_ PLARGE_INTEGER ExpirationTime,
4768     _In_ PTOKEN_USER TokenUser,
4769     _In_ PTOKEN_GROUPS TokenGroups,
4770     _In_ PTOKEN_PRIVILEGES TokenPrivileges,
4771     _In_opt_ PTOKEN_OWNER TokenOwner,
4772     _In_ PTOKEN_PRIMARY_GROUP TokenPrimaryGroup,
4773     _In_opt_ PTOKEN_DEFAULT_DACL TokenDefaultDacl,
4774     _In_ PTOKEN_SOURCE TokenSource)
4775 {
4776     HANDLE hToken;
4777     KPROCESSOR_MODE PreviousMode;
4778     ULONG PrivilegeCount, GroupCount;
4779     PSID OwnerSid, PrimaryGroupSid;
4780     PACL DefaultDacl;
4781     LARGE_INTEGER LocalExpirationTime = {{0, 0}};
4782     LUID LocalAuthenticationId;
4783     TOKEN_SOURCE LocalTokenSource;
4784     SECURITY_QUALITY_OF_SERVICE LocalSecurityQos;
4785     PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
4786     PSID_AND_ATTRIBUTES CapturedUser = NULL;
4787     PSID_AND_ATTRIBUTES CapturedGroups = NULL;
4788     PSID CapturedOwnerSid = NULL;
4789     PSID CapturedPrimaryGroupSid = NULL;
4790     PACL CapturedDefaultDacl = NULL;
4791     ULONG PrivilegesLength, UserLength, GroupsLength;
4792     NTSTATUS Status;
4793 
4794     PAGED_CODE();
4795 
4796     PreviousMode = ExGetPreviousMode();
4797 
4798     if (PreviousMode != KernelMode)
4799     {
4800         _SEH2_TRY
4801         {
4802             ProbeForWriteHandle(TokenHandle);
4803 
4804             if (ObjectAttributes != NULL)
4805             {
4806                 ProbeForRead(ObjectAttributes,
4807                              sizeof(OBJECT_ATTRIBUTES),
4808                              sizeof(ULONG));
4809                 LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService;
4810             }
4811 
4812             ProbeForRead(AuthenticationId,
4813                          sizeof(LUID),
4814                          sizeof(ULONG));
4815             LocalAuthenticationId = *AuthenticationId;
4816 
4817             LocalExpirationTime = ProbeForReadLargeInteger(ExpirationTime);
4818 
4819             ProbeForRead(TokenUser,
4820                          sizeof(TOKEN_USER),
4821                          sizeof(ULONG));
4822 
4823             ProbeForRead(TokenGroups,
4824                          sizeof(TOKEN_GROUPS),
4825                          sizeof(ULONG));
4826             GroupCount = TokenGroups->GroupCount;
4827 
4828             ProbeForRead(TokenPrivileges,
4829                          sizeof(TOKEN_PRIVILEGES),
4830                          sizeof(ULONG));
4831             PrivilegeCount = TokenPrivileges->PrivilegeCount;
4832 
4833             if (TokenOwner != NULL)
4834             {
4835                 ProbeForRead(TokenOwner,
4836                              sizeof(TOKEN_OWNER),
4837                              sizeof(ULONG));
4838                 OwnerSid = TokenOwner->Owner;
4839             }
4840             else
4841             {
4842                 OwnerSid = NULL;
4843             }
4844 
4845             ProbeForRead(TokenPrimaryGroup,
4846                          sizeof(TOKEN_PRIMARY_GROUP),
4847                          sizeof(ULONG));
4848             PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup;
4849 
4850             if (TokenDefaultDacl != NULL)
4851             {
4852                 ProbeForRead(TokenDefaultDacl,
4853                              sizeof(TOKEN_DEFAULT_DACL),
4854                              sizeof(ULONG));
4855                 DefaultDacl = TokenDefaultDacl->DefaultDacl;
4856             }
4857             else
4858             {
4859                 DefaultDacl = NULL;
4860             }
4861 
4862             ProbeForRead(TokenSource,
4863                          sizeof(TOKEN_SOURCE),
4864                          sizeof(ULONG));
4865             LocalTokenSource = *TokenSource;
4866         }
4867         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4868         {
4869             /* Return the exception code */
4870             _SEH2_YIELD(return _SEH2_GetExceptionCode());
4871         }
4872         _SEH2_END;
4873     }
4874     else
4875     {
4876         if (ObjectAttributes != NULL)
4877             LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService;
4878         LocalAuthenticationId = *AuthenticationId;
4879         LocalExpirationTime = *ExpirationTime;
4880         GroupCount = TokenGroups->GroupCount;
4881         PrivilegeCount = TokenPrivileges->PrivilegeCount;
4882         OwnerSid = TokenOwner ? TokenOwner->Owner : NULL;
4883         PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup;
4884         DefaultDacl = TokenDefaultDacl ? TokenDefaultDacl->DefaultDacl : NULL;
4885         LocalTokenSource = *TokenSource;
4886     }
4887 
4888     /* Check token type */
4889     if ((TokenType < TokenPrimary) ||
4890         (TokenType > TokenImpersonation))
4891     {
4892         return STATUS_BAD_TOKEN_TYPE;
4893     }
4894 
4895     /* Check for token creation privilege */
4896     if (!SeSinglePrivilegeCheck(SeCreateTokenPrivilege, PreviousMode))
4897     {
4898         return STATUS_PRIVILEGE_NOT_HELD;
4899     }
4900 
4901     /* Capture the user SID and attributes */
4902     Status = SeCaptureSidAndAttributesArray(&TokenUser->User,
4903                                             1,
4904                                             PreviousMode,
4905                                             NULL,
4906                                             0,
4907                                             PagedPool,
4908                                             FALSE,
4909                                             &CapturedUser,
4910                                             &UserLength);
4911     if (!NT_SUCCESS(Status))
4912     {
4913         goto Cleanup;
4914     }
4915 
4916     /* Capture the groups SID and attributes array */
4917     Status = SeCaptureSidAndAttributesArray(&TokenGroups->Groups[0],
4918                                             GroupCount,
4919                                             PreviousMode,
4920                                             NULL,
4921                                             0,
4922                                             PagedPool,
4923                                             FALSE,
4924                                             &CapturedGroups,
4925                                             &GroupsLength);
4926     if (!NT_SUCCESS(Status))
4927     {
4928         goto Cleanup;
4929     }
4930 
4931     /* Capture privileges */
4932     Status = SeCaptureLuidAndAttributesArray(&TokenPrivileges->Privileges[0],
4933                                              PrivilegeCount,
4934                                              PreviousMode,
4935                                              NULL,
4936                                              0,
4937                                              PagedPool,
4938                                              FALSE,
4939                                              &CapturedPrivileges,
4940                                              &PrivilegesLength);
4941     if (!NT_SUCCESS(Status))
4942     {
4943         goto Cleanup;
4944     }
4945 
4946     /* Capture the token owner SID */
4947     if (TokenOwner != NULL)
4948     {
4949         Status = SepCaptureSid(OwnerSid,
4950                                PreviousMode,
4951                                PagedPool,
4952                                FALSE,
4953                                &CapturedOwnerSid);
4954         if (!NT_SUCCESS(Status))
4955         {
4956             goto Cleanup;
4957         }
4958     }
4959 
4960     /* Capture the token primary group SID */
4961     Status = SepCaptureSid(PrimaryGroupSid,
4962                            PreviousMode,
4963                            PagedPool,
4964                            FALSE,
4965                            &CapturedPrimaryGroupSid);
4966     if (!NT_SUCCESS(Status))
4967     {
4968         goto Cleanup;
4969     }
4970 
4971     /* Capture DefaultDacl */
4972     if (DefaultDacl != NULL)
4973     {
4974         Status = SepCaptureAcl(DefaultDacl,
4975                                PreviousMode,
4976                                NonPagedPool,
4977                                FALSE,
4978                                &CapturedDefaultDacl);
4979         if (!NT_SUCCESS(Status))
4980         {
4981             goto Cleanup;
4982         }
4983     }
4984 
4985     /* Call the internal function */
4986     Status = SepCreateToken(&hToken,
4987                             PreviousMode,
4988                             DesiredAccess,
4989                             ObjectAttributes,
4990                             TokenType,
4991                             LocalSecurityQos.ImpersonationLevel,
4992                             &LocalAuthenticationId,
4993                             &LocalExpirationTime,
4994                             CapturedUser,
4995                             GroupCount,
4996                             CapturedGroups,
4997                             GroupsLength,
4998                             PrivilegeCount,
4999                             CapturedPrivileges,
5000                             CapturedOwnerSid,
5001                             CapturedPrimaryGroupSid,
5002                             CapturedDefaultDacl,
5003                             &LocalTokenSource,
5004                             FALSE);
5005     if (NT_SUCCESS(Status))
5006     {
5007         _SEH2_TRY
5008         {
5009             *TokenHandle = hToken;
5010         }
5011         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5012         {
5013             Status = _SEH2_GetExceptionCode();
5014         }
5015         _SEH2_END;
5016     }
5017 
5018 Cleanup:
5019 
5020     /* Release what we captured */
5021     SeReleaseSidAndAttributesArray(CapturedUser, PreviousMode, FALSE);
5022     SeReleaseSidAndAttributesArray(CapturedGroups, PreviousMode, FALSE);
5023     SeReleaseLuidAndAttributesArray(CapturedPrivileges, PreviousMode, FALSE);
5024     SepReleaseSid(CapturedOwnerSid, PreviousMode, FALSE);
5025     SepReleaseSid(CapturedPrimaryGroupSid, PreviousMode, FALSE);
5026     SepReleaseAcl(CapturedDefaultDacl, PreviousMode, FALSE);
5027 
5028     return Status;
5029 }
5030 
5031 /**
5032  * @brief
5033  * Opens a token that is tied to a thread handle.
5034  *
5035  * @param[out] ThreadHandle
5036  * Thread handle where the token is about to be opened.
5037  *
5038  * @param[in] DesiredAccess
5039  * The request access right for the token.
5040  *
5041  * @param[in] OpenAsSelf
5042  * If set to TRUE, the access check will be made with the security context
5043  * of the process of the calling thread (opening as self). Otherwise the access
5044  * check will be made with the security context of the calling thread instead.
5045  *
5046  * @param[in] HandleAttributes
5047  * Handle attributes for the opened thread token handle.
5048  *
5049  * @param[out] TokenHandle
5050  * The opened token handle returned to the caller for use.
5051  *
5052  * @return
5053  * Returns STATUS_SUCCESS if the function has successfully opened the thread
5054  * token. STATUS_CANT_OPEN_ANONYMOUS is returned if a token has SecurityAnonymous
5055  * as impersonation level and we cannot open it. A failure NTSTATUS code is returned
5056  * otherwise.
5057  */
5058 NTSTATUS
5059 NTAPI
5060 NtOpenThreadTokenEx(
5061     _In_ HANDLE ThreadHandle,
5062     _In_ ACCESS_MASK DesiredAccess,
5063     _In_ BOOLEAN OpenAsSelf,
5064     _In_ ULONG HandleAttributes,
5065     _Out_ PHANDLE TokenHandle)
5066 {
5067     PETHREAD Thread;
5068     HANDLE hToken;
5069     PTOKEN Token, NewToken = NULL, PrimaryToken;
5070     BOOLEAN CopyOnOpen, EffectiveOnly;
5071     SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
5072     SE_IMPERSONATION_STATE ImpersonationState;
5073     OBJECT_ATTRIBUTES ObjectAttributes;
5074     SECURITY_DESCRIPTOR SecurityDescriptor;
5075     PACL Dacl = NULL;
5076     KPROCESSOR_MODE PreviousMode;
5077     NTSTATUS Status;
5078     BOOLEAN RestoreImpersonation = FALSE;
5079 
5080     PAGED_CODE();
5081 
5082     PreviousMode = ExGetPreviousMode();
5083 
5084     if (PreviousMode != KernelMode)
5085     {
5086         _SEH2_TRY
5087         {
5088             ProbeForWriteHandle(TokenHandle);
5089         }
5090         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5091         {
5092             /* Return the exception code */
5093             _SEH2_YIELD(return _SEH2_GetExceptionCode());
5094         }
5095         _SEH2_END;
5096     }
5097 
5098     /* Validate object attributes */
5099     HandleAttributes = ObpValidateAttributes(HandleAttributes, PreviousMode);
5100 
5101     /*
5102      * At first open the thread token for information access and verify
5103      * that the token associated with thread is valid.
5104      */
5105 
5106     Status = ObReferenceObjectByHandle(ThreadHandle, THREAD_QUERY_INFORMATION,
5107                                        PsThreadType, PreviousMode, (PVOID*)&Thread,
5108                                        NULL);
5109     if (!NT_SUCCESS(Status))
5110     {
5111         return Status;
5112     }
5113 
5114     Token = PsReferenceImpersonationToken(Thread, &CopyOnOpen, &EffectiveOnly,
5115                                           &ImpersonationLevel);
5116     if (Token == NULL)
5117     {
5118         ObDereferenceObject(Thread);
5119         return STATUS_NO_TOKEN;
5120     }
5121 
5122     if (ImpersonationLevel == SecurityAnonymous)
5123     {
5124         PsDereferenceImpersonationToken(Token);
5125         ObDereferenceObject(Thread);
5126         return STATUS_CANT_OPEN_ANONYMOUS;
5127     }
5128 
5129     /*
5130      * Revert to self if OpenAsSelf is specified.
5131      */
5132 
5133     if (OpenAsSelf)
5134     {
5135         RestoreImpersonation = PsDisableImpersonation(PsGetCurrentThread(),
5136                                                       &ImpersonationState);
5137     }
5138 
5139     if (CopyOnOpen)
5140     {
5141         PrimaryToken = PsReferencePrimaryToken(Thread->ThreadsProcess);
5142 
5143         Status = SepCreateImpersonationTokenDacl(Token, PrimaryToken, &Dacl);
5144 
5145         ObFastDereferenceObject(&Thread->ThreadsProcess->Token, PrimaryToken);
5146 
5147         if (NT_SUCCESS(Status))
5148         {
5149             if (Dacl)
5150             {
5151                 Status = RtlCreateSecurityDescriptor(&SecurityDescriptor,
5152                                                      SECURITY_DESCRIPTOR_REVISION);
5153                 if (!NT_SUCCESS(Status))
5154                 {
5155                     DPRINT1("NtOpenThreadTokenEx(): Failed to create a security descriptor (Status 0x%lx)\n", Status);
5156                 }
5157 
5158                 Status = RtlSetDaclSecurityDescriptor(&SecurityDescriptor, TRUE, Dacl,
5159                                                       FALSE);
5160                 if (!NT_SUCCESS(Status))
5161                 {
5162                     DPRINT1("NtOpenThreadTokenEx(): Failed to set a DACL to the security descriptor (Status 0x%lx)\n", Status);
5163                 }
5164             }
5165 
5166             InitializeObjectAttributes(&ObjectAttributes, NULL, HandleAttributes,
5167                                        NULL, Dacl ? &SecurityDescriptor : NULL);
5168 
5169             Status = SepDuplicateToken(Token, &ObjectAttributes, EffectiveOnly,
5170                                        TokenImpersonation, ImpersonationLevel,
5171                                        KernelMode, &NewToken);
5172             if (!NT_SUCCESS(Status))
5173             {
5174                 DPRINT1("NtOpenThreadTokenEx(): Failed to duplicate the token (Status 0x%lx)\n");
5175             }
5176 
5177             ObReferenceObject(NewToken);
5178             Status = ObInsertObject(NewToken, NULL, DesiredAccess, 0, NULL,
5179                                     &hToken);
5180             if (!NT_SUCCESS(Status))
5181             {
5182                 DPRINT1("NtOpenThreadTokenEx(): Failed to insert the token object (Status 0x%lx)\n", Status);
5183             }
5184         }
5185         else
5186         {
5187             DPRINT1("NtOpenThreadTokenEx(): Failed to impersonate token from DACL (Status 0x%lx)\n", Status);
5188         }
5189     }
5190     else
5191     {
5192         Status = ObOpenObjectByPointer(Token, HandleAttributes,
5193                                        NULL, DesiredAccess, SeTokenObjectType,
5194                                        PreviousMode, &hToken);
5195         if (!NT_SUCCESS(Status))
5196         {
5197             DPRINT1("NtOpenThreadTokenEx(): Failed to open the object (Status 0x%lx)\n", Status);
5198         }
5199     }
5200 
5201     if (Dacl) ExFreePoolWithTag(Dacl, TAG_ACL);
5202 
5203     if (RestoreImpersonation)
5204     {
5205         PsRestoreImpersonation(PsGetCurrentThread(), &ImpersonationState);
5206     }
5207 
5208     ObDereferenceObject(Token);
5209 
5210     if (NT_SUCCESS(Status) && CopyOnOpen)
5211     {
5212         Status = PsImpersonateClient(Thread, NewToken, FALSE, EffectiveOnly, ImpersonationLevel);
5213         if (!NT_SUCCESS(Status))
5214         {
5215             DPRINT1("NtOpenThreadTokenEx(): Failed to impersonate the client (Status 0x%lx)\n");
5216         }
5217     }
5218 
5219     if (NewToken) ObDereferenceObject(NewToken);
5220 
5221     ObDereferenceObject(Thread);
5222 
5223     if (NT_SUCCESS(Status))
5224     {
5225         _SEH2_TRY
5226         {
5227             *TokenHandle = hToken;
5228         }
5229         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5230         {
5231             Status = _SEH2_GetExceptionCode();
5232         }
5233         _SEH2_END;
5234     }
5235 
5236     return Status;
5237 }
5238 
5239 /**
5240  * @brief
5241  * Opens a token that is tied to a thread handle.
5242  *
5243  * @param[out] ThreadHandle
5244  * Thread handle where the token is about to be opened.
5245  *
5246  * @param[in] DesiredAccess
5247  * The request access right for the token.
5248  *
5249  * @param[in] OpenAsSelf
5250  * If set to TRUE, the access check will be made with the security context
5251  * of the process of the calling thread (opening as self). Otherwise the access
5252  * check will be made with the security context of the calling thread instead.
5253  *
5254  * @param[out] TokenHandle
5255  * The opened token handle returned to the caller for use.
5256  *
5257  * @return
5258  * See NtOpenThreadTokenEx.
5259  */
5260 NTSTATUS
5261 NTAPI
5262 NtOpenThreadToken(
5263     _In_ HANDLE ThreadHandle,
5264     _In_ ACCESS_MASK DesiredAccess,
5265     _In_ BOOLEAN OpenAsSelf,
5266     _Out_ PHANDLE TokenHandle)
5267 {
5268     return NtOpenThreadTokenEx(ThreadHandle, DesiredAccess, OpenAsSelf, 0,
5269                                TokenHandle);
5270 }
5271 
5272 /**
5273  * @brief
5274  * Compares tokens if they're equal or not.
5275  *
5276  * @param[in] FirstToken
5277  * The first token.
5278  *
5279  * @param[in] SecondToken
5280  * The second token.
5281  *
5282  * @param[out] Equal
5283  * The retrieved value which determines if the tokens are
5284  * equal or not.
5285  *
5286  * @return
5287  * Returns STATUS_SUCCESS, otherwise it returns a failure NTSTATUS code.
5288  */
5289 NTSTATUS
5290 NTAPI
5291 NtCompareTokens(
5292     _In_ HANDLE FirstTokenHandle,
5293     _In_ HANDLE SecondTokenHandle,
5294     _Out_ PBOOLEAN Equal)
5295 {
5296     KPROCESSOR_MODE PreviousMode;
5297     PTOKEN FirstToken, SecondToken;
5298     BOOLEAN IsEqual;
5299     NTSTATUS Status;
5300 
5301     PAGED_CODE();
5302 
5303     PreviousMode = ExGetPreviousMode();
5304 
5305     if (PreviousMode != KernelMode)
5306     {
5307         _SEH2_TRY
5308         {
5309             ProbeForWriteBoolean(Equal);
5310         }
5311         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5312         {
5313             /* Return the exception code */
5314             _SEH2_YIELD(return _SEH2_GetExceptionCode());
5315         }
5316         _SEH2_END;
5317     }
5318 
5319     Status = ObReferenceObjectByHandle(FirstTokenHandle,
5320                                        TOKEN_QUERY,
5321                                        SeTokenObjectType,
5322                                        PreviousMode,
5323                                        (PVOID*)&FirstToken,
5324                                        NULL);
5325     if (!NT_SUCCESS(Status))
5326     {
5327         DPRINT1("ObReferenceObjectByHandle() failed (Status 0x%lx)\n", Status);
5328         return Status;
5329     }
5330 
5331     Status = ObReferenceObjectByHandle(SecondTokenHandle,
5332                                        TOKEN_QUERY,
5333                                        SeTokenObjectType,
5334                                        PreviousMode,
5335                                        (PVOID*)&SecondToken,
5336                                        NULL);
5337     if (!NT_SUCCESS(Status))
5338     {
5339         DPRINT1("ObReferenceObjectByHandle() failed (Status 0x%lx)\n", Status);
5340         ObDereferenceObject(FirstToken);
5341         return Status;
5342     }
5343 
5344     if (FirstToken != SecondToken)
5345     {
5346         Status = SepCompareTokens(FirstToken,
5347                                   SecondToken,
5348                                   &IsEqual);
5349     }
5350     else
5351     {
5352         IsEqual = TRUE;
5353     }
5354 
5355     ObDereferenceObject(SecondToken);
5356     ObDereferenceObject(FirstToken);
5357 
5358     if (NT_SUCCESS(Status))
5359     {
5360         _SEH2_TRY
5361         {
5362             *Equal = IsEqual;
5363         }
5364         _SEH2_EXCEPT(ExSystemExceptionFilter())
5365         {
5366             Status = _SEH2_GetExceptionCode();
5367         }
5368         _SEH2_END;
5369     }
5370 
5371     return Status;
5372 }
5373 
5374 /**
5375  * @unimplemented
5376  * @brief
5377  * Opens a token that is tied to a thread handle.
5378  *
5379  * @param[in] ExistingTokenHandle
5380  * An existing token for filtering.
5381  *
5382  * @param[in] Flags
5383  * Privilege flag options. This parameter argument influences how the token
5384  * is filtered. Such parameter can be 0.
5385  *
5386  * @param[in] SidsToDisable
5387  * Array of SIDs to disable.
5388  *
5389  * @param[in] PrivilegesToDelete
5390  * Array of privileges to delete.
5391  *
5392  * @param[in] RestrictedSids
5393  * An array of restricted SIDs for the new filtered token.
5394  *
5395  * @param[out] NewTokenHandle
5396  * The newly filtered token, returned to the caller.
5397  *
5398  * @return
5399  * To be added...
5400  */
5401 NTSTATUS
5402 NTAPI
5403 NtFilterToken(
5404     _In_ HANDLE ExistingTokenHandle,
5405     _In_ ULONG Flags,
5406     _In_opt_ PTOKEN_GROUPS SidsToDisable,
5407     _In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete,
5408     _In_opt_ PTOKEN_GROUPS RestrictedSids,
5409     _Out_ PHANDLE NewTokenHandle)
5410 {
5411     UNIMPLEMENTED;
5412     return STATUS_NOT_IMPLEMENTED;
5413 }
5414 
5415 /**
5416  * @brief
5417  * Allows the calling thread to impersonate the system's anonymous
5418  * logon token.
5419  *
5420  * @param[in] ThreadHandle
5421  * A handle to the thread to start the procedure of logon token
5422  * impersonation. The thread must have the THREAD_IMPERSONATE
5423  * access right.
5424  *
5425  * @return
5426  * Returns STATUS_SUCCESS if the thread has successfully impersonated the
5427  * anonymous logon token, otherwise a failure NTSTATUS code is returned.
5428  *
5429  * @remarks
5430  * By default the system gives the opportunity to the caller to impersonate
5431  * the anonymous logon token without including the Everyone Group SID.
5432  * In cases where the caller wants to impersonate the token including such
5433  * group, the EveryoneIncludesAnonymous registry value setting has to be set
5434  * to 1, from HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa registry
5435  * path. The calling thread must invoke PsRevertToSelf when impersonation
5436  * is no longer needed or RevertToSelf if the calling execution is done
5437  * in user mode.
5438  */
5439 NTSTATUS
5440 NTAPI
5441 NtImpersonateAnonymousToken(
5442     _In_ HANDLE ThreadHandle)
5443 {
5444     PETHREAD Thread;
5445     KPROCESSOR_MODE PreviousMode;
5446     NTSTATUS Status;
5447     PAGED_CODE();
5448 
5449     PreviousMode = ExGetPreviousMode();
5450 
5451     /* Obtain the thread object from the handle */
5452     Status = ObReferenceObjectByHandle(ThreadHandle,
5453                                        THREAD_IMPERSONATE,
5454                                        PsThreadType,
5455                                        PreviousMode,
5456                                        (PVOID*)&Thread,
5457                                        NULL);
5458     if (!NT_SUCCESS(Status))
5459     {
5460         DPRINT1("NtImpersonateAnonymousToken(): Failed to reference the object (Status 0x%lx)\n", Status);
5461         return Status;
5462     }
5463 
5464     /* Call the private routine to impersonate the token */
5465     Status = SepImpersonateAnonymousToken(Thread, PreviousMode);
5466     if (!NT_SUCCESS(Status))
5467     {
5468         DPRINT1("NtImpersonateAnonymousToken(): Failed to impersonate the token (Status 0x%lx)\n", Status);
5469     }
5470 
5471     ObDereferenceObject(Thread);
5472     return Status;
5473 }
5474 
5475 /* EOF */
5476