xref: /reactos/ntoskrnl/se/token.c (revision 7e22dc05)
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-2022 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 PrimaryGroupIndex;
1008     ULONG VariableLength;
1009     ULONG TotalSize;
1010     ULONG PrivilegesIndex, GroupsIndex;
1011 
1012     PAGED_CODE();
1013 
1014     /* Compute how much size we need to allocate for the token */
1015     VariableLength = Token->VariableLength;
1016     TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
1017 
1018     Status = ObCreateObject(PreviousMode,
1019                             SeTokenObjectType,
1020                             ObjectAttributes,
1021                             PreviousMode,
1022                             NULL,
1023                             TotalSize,
1024                             0,
1025                             0,
1026                             (PVOID*)&AccessToken);
1027     if (!NT_SUCCESS(Status))
1028     {
1029         DPRINT1("ObCreateObject() failed (Status 0x%lx)\n", Status);
1030         return Status;
1031     }
1032 
1033     /* Zero out the buffer and initialize the token */
1034     RtlZeroMemory(AccessToken, TotalSize);
1035 
1036     ExAllocateLocallyUniqueId(&AccessToken->TokenId);
1037 
1038     AccessToken->TokenType = TokenType;
1039     AccessToken->ImpersonationLevel = Level;
1040 
1041     /* Initialise the lock for the access token */
1042     Status = SepCreateTokenLock(AccessToken);
1043     if (!NT_SUCCESS(Status))
1044     {
1045         ObDereferenceObject(AccessToken);
1046         return Status;
1047     }
1048 
1049     /* Copy the immutable fields */
1050     RtlCopyLuid(&AccessToken->TokenSource.SourceIdentifier,
1051                 &Token->TokenSource.SourceIdentifier);
1052     RtlCopyMemory(AccessToken->TokenSource.SourceName,
1053                   Token->TokenSource.SourceName,
1054                   sizeof(Token->TokenSource.SourceName));
1055 
1056     AccessToken->AuthenticationId = Token->AuthenticationId;
1057     AccessToken->ParentTokenId = Token->ParentTokenId;
1058     AccessToken->ExpirationTime = Token->ExpirationTime;
1059     AccessToken->OriginatingLogonSession = Token->OriginatingLogonSession;
1060 
1061     /* Lock the source token and copy the mutable fields */
1062     SepAcquireTokenLockExclusive(Token);
1063 
1064     AccessToken->SessionId = Token->SessionId;
1065     RtlCopyLuid(&AccessToken->ModifiedId, &Token->ModifiedId);
1066 
1067     AccessToken->TokenFlags = Token->TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
1068 
1069     /* Reference the logon session */
1070     Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
1071     if (!NT_SUCCESS(Status))
1072     {
1073         /* No logon session could be found, bail out */
1074         DPRINT1("SepRmReferenceLogonSession() failed (Status 0x%lx)\n", Status);
1075         /* Set the flag for proper cleanup by the delete procedure */
1076         AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED;
1077         goto Quit;
1078     }
1079 
1080     /* Insert the referenced logon session into the token */
1081     Status = SepRmInsertLogonSessionIntoToken(AccessToken);
1082     if (!NT_SUCCESS(Status))
1083     {
1084         /* Failed to insert the logon session into the token, bail out */
1085         DPRINT1("SepRmInsertLogonSessionIntoToken() failed (Status 0x%lx)\n", Status);
1086         goto Quit;
1087     }
1088 
1089     /* Fill in token debug information */
1090 #if DBG
1091     RtlCopyMemory(AccessToken->ImageFileName,
1092                   PsGetCurrentProcess()->ImageFileName,
1093                   min(sizeof(AccessToken->ImageFileName), sizeof(PsGetCurrentProcess()->ImageFileName)));
1094 
1095     AccessToken->ProcessCid = PsGetCurrentProcessId();
1096     AccessToken->ThreadCid = PsGetCurrentThreadId();
1097     AccessToken->CreateMethod = TOKEN_DUPLICATE_METHOD;
1098 #endif
1099 
1100     /* Assign the data that reside in the TOKEN's variable information area */
1101     AccessToken->VariableLength = VariableLength;
1102     EndMem = (PVOID)&AccessToken->VariablePart;
1103 
1104     /* Copy the privileges */
1105     AccessToken->PrivilegeCount = 0;
1106     AccessToken->Privileges = NULL;
1107     if (Token->Privileges && (Token->PrivilegeCount > 0))
1108     {
1109         ULONG PrivilegesLength = Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
1110         PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID));
1111 
1112         ASSERT(VariableLength >= PrivilegesLength);
1113 
1114         AccessToken->PrivilegeCount = Token->PrivilegeCount;
1115         AccessToken->Privileges = EndMem;
1116         EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength);
1117         VariableLength -= PrivilegesLength;
1118 
1119         RtlCopyMemory(AccessToken->Privileges,
1120                       Token->Privileges,
1121                       AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
1122     }
1123 
1124     /* Copy the user and groups */
1125     AccessToken->UserAndGroupCount = 0;
1126     AccessToken->UserAndGroups = NULL;
1127     if (Token->UserAndGroups && (Token->UserAndGroupCount > 0))
1128     {
1129         AccessToken->UserAndGroupCount = Token->UserAndGroupCount;
1130         AccessToken->UserAndGroups = EndMem;
1131         EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
1132         VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups);
1133 
1134         Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount,
1135                                               Token->UserAndGroups,
1136                                               VariableLength,
1137                                               AccessToken->UserAndGroups,
1138                                               EndMem,
1139                                               &EndMem,
1140                                               &VariableLength);
1141         if (!NT_SUCCESS(Status))
1142         {
1143             DPRINT1("RtlCopySidAndAttributesArray(UserAndGroups) failed (Status 0x%lx)\n", Status);
1144             goto Quit;
1145         }
1146     }
1147 
1148     /* Find the token primary group */
1149     Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken,
1150                                                 Token->PrimaryGroup,
1151                                                 NULL,
1152                                                 &PrimaryGroupIndex,
1153                                                 NULL);
1154     if (!NT_SUCCESS(Status))
1155     {
1156         DPRINT1("SepFindPrimaryGroupAndDefaultOwner failed (Status 0x%lx)\n", Status);
1157         goto Quit;
1158     }
1159 
1160     AccessToken->PrimaryGroup = AccessToken->UserAndGroups[PrimaryGroupIndex].Sid;
1161     AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex;
1162 
1163     /* Copy the restricted SIDs */
1164     AccessToken->RestrictedSidCount = 0;
1165     AccessToken->RestrictedSids = NULL;
1166     if (Token->RestrictedSids && (Token->RestrictedSidCount > 0))
1167     {
1168         AccessToken->RestrictedSidCount = Token->RestrictedSidCount;
1169         AccessToken->RestrictedSids = EndMem;
1170         EndMem = &AccessToken->RestrictedSids[AccessToken->RestrictedSidCount];
1171         VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->RestrictedSids);
1172 
1173         Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount,
1174                                               Token->RestrictedSids,
1175                                               VariableLength,
1176                                               AccessToken->RestrictedSids,
1177                                               EndMem,
1178                                               &EndMem,
1179                                               &VariableLength);
1180         if (!NT_SUCCESS(Status))
1181         {
1182             DPRINT1("RtlCopySidAndAttributesArray(RestrictedSids) failed (Status 0x%lx)\n", Status);
1183             goto Quit;
1184         }
1185     }
1186 
1187     /*
1188      * Filter the token by removing the disabled privileges
1189      * and groups if the caller wants to duplicate an access
1190      * token as effective only.
1191      */
1192     if (EffectiveOnly)
1193     {
1194         /* Begin querying the groups and search for disabled ones */
1195         for (GroupsIndex = 0; GroupsIndex < AccessToken->UserAndGroupCount; GroupsIndex++)
1196         {
1197             /*
1198              * A group or user is considered disabled if its attributes is either
1199              * 0 or SE_GROUP_ENABLED is not included in the attributes flags list.
1200              * That is because a certain user and/or group can have several attributes
1201              * that bear no influence on whether a user/group is enabled or not
1202              * (SE_GROUP_ENABLED_BY_DEFAULT for example which is a mere indicator
1203              * that the group has just been enabled by default). A mandatory
1204              * group (that is, the group has SE_GROUP_MANDATORY attribute)
1205              * by standards it's always enabled and no one can disable it.
1206              */
1207             if (AccessToken->UserAndGroups[GroupsIndex].Attributes == 0 ||
1208                 (AccessToken->UserAndGroups[GroupsIndex].Attributes & SE_GROUP_ENABLED) == 0)
1209             {
1210                 /*
1211                  * If this group is an administrators group
1212                  * and the token belongs to such group,
1213                  * we've to take away TOKEN_HAS_ADMIN_GROUP
1214                  * for the fact that's not enabled and as
1215                  * such the token no longer belongs to
1216                  * this group.
1217                  */
1218                 if (RtlEqualSid(SeAliasAdminsSid,
1219                                 &AccessToken->UserAndGroups[GroupsIndex].Sid))
1220                 {
1221                     AccessToken->TokenFlags &= ~TOKEN_HAS_ADMIN_GROUP;
1222                 }
1223 
1224                 /*
1225                  * A group is not enabled, it's time to remove
1226                  * from the token and update the groups index
1227                  * accordingly and continue with the next group.
1228                  */
1229                 SepRemoveUserGroupToken(AccessToken, GroupsIndex);
1230                 GroupsIndex--;
1231             }
1232         }
1233 
1234         /* Begin querying the privileges and search for disabled ones */
1235         for (PrivilegesIndex = 0; PrivilegesIndex < AccessToken->PrivilegeCount; PrivilegesIndex++)
1236         {
1237             /*
1238              * A privilege is considered disabled if its attributes is either
1239              * 0 or SE_PRIVILEGE_ENABLED is not included in the attributes flags list.
1240              * That is because a certain privilege can have several attributes
1241              * that bear no influence on whether a privilege is enabled or not
1242              * (SE_PRIVILEGE_ENABLED_BY_DEFAULT for example which is a mere indicator
1243              * that the privilege has just been enabled by default).
1244              */
1245             if (AccessToken->Privileges[PrivilegesIndex].Attributes == 0 ||
1246                 (AccessToken->Privileges[PrivilegesIndex].Attributes & SE_PRIVILEGE_ENABLED) == 0)
1247             {
1248                 /*
1249                  * A privilege is not enabled, therefor it's time
1250                  * to strip it from the token and continue with the next
1251                  * privilege. Of course we must also want to update the
1252                  * privileges index accordingly.
1253                  */
1254                 SepRemovePrivilegeToken(AccessToken, PrivilegesIndex);
1255                 PrivilegesIndex--;
1256             }
1257         }
1258     }
1259 
1260     //
1261     // NOTE: So far our dynamic area only contains
1262     // the default dacl, so this makes the following
1263     // code pretty simple. The day where it stores
1264     // other data, the code will require adaptations.
1265     //
1266 
1267     /* Now allocate the TOKEN's dynamic information area and set the data */
1268     AccessToken->DynamicAvailable = 0; // Unused memory in the dynamic area.
1269     AccessToken->DynamicPart = NULL;
1270     if (Token->DynamicPart && Token->DefaultDacl)
1271     {
1272         AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
1273                                                          Token->DefaultDacl->AclSize,
1274                                                          TAG_TOKEN_DYNAMIC);
1275         if (AccessToken->DynamicPart == NULL)
1276         {
1277             Status = STATUS_INSUFFICIENT_RESOURCES;
1278             goto Quit;
1279         }
1280         EndMem = (PVOID)AccessToken->DynamicPart;
1281 
1282         AccessToken->DefaultDacl = EndMem;
1283 
1284         RtlCopyMemory(AccessToken->DefaultDacl,
1285                       Token->DefaultDacl,
1286                       Token->DefaultDacl->AclSize);
1287     }
1288 
1289     /* Unlock the source token */
1290     SepReleaseTokenLock(Token);
1291 
1292     /* Return the token */
1293     *NewAccessToken = AccessToken;
1294     Status = STATUS_SUCCESS;
1295 
1296 Quit:
1297     if (!NT_SUCCESS(Status))
1298     {
1299         /* Unlock the source token */
1300         SepReleaseTokenLock(Token);
1301 
1302         /* Dereference the token, the delete procedure will clean it up */
1303         ObDereferenceObject(AccessToken);
1304     }
1305 
1306     return Status;
1307 }
1308 
1309 /**
1310  * @brief
1311  * Subtracts a token in exchange of duplicating a new one.
1312  *
1313  * @param[in] ParentToken
1314  * The parent access token for duplication.
1315  *
1316  * @param[out] Token
1317  * The new duplicated token.
1318  *
1319  * @param[in] InUse
1320  * Set this to TRUE if the token is about to be used immediately after the call execution
1321  * of this function, FALSE otherwise.
1322  *
1323  * @param[in] SessionId
1324  * Session ID for the token to be assigned.
1325  *
1326  * @return
1327  * Returns STATUS_SUCCESS if token subtracting and duplication have completed successfully.
1328  * A failure NTSTATUS code is returned otherwise.
1329  */
1330 NTSTATUS
1331 NTAPI
1332 SeSubProcessToken(
1333     _In_ PTOKEN ParentToken,
1334     _Out_ PTOKEN *Token,
1335     _In_ BOOLEAN InUse,
1336     _In_ ULONG SessionId)
1337 {
1338     PTOKEN NewToken;
1339     OBJECT_ATTRIBUTES ObjectAttributes;
1340     NTSTATUS Status;
1341 
1342     /* Initialize the attributes and duplicate it */
1343     InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
1344     Status = SepDuplicateToken(ParentToken,
1345                                &ObjectAttributes,
1346                                FALSE,
1347                                TokenPrimary,
1348                                ParentToken->ImpersonationLevel,
1349                                KernelMode,
1350                                &NewToken);
1351     if (NT_SUCCESS(Status))
1352     {
1353         /* Insert it */
1354         Status = ObInsertObject(NewToken,
1355                                 NULL,
1356                                 0,
1357                                 0,
1358                                 NULL,
1359                                 NULL);
1360         if (NT_SUCCESS(Status))
1361         {
1362             /* Set the session ID */
1363             NewToken->SessionId = SessionId;
1364             NewToken->TokenInUse = InUse;
1365 
1366             /* Return the token */
1367             *Token = NewToken;
1368         }
1369     }
1370 
1371     /* Return status */
1372     return Status;
1373 }
1374 
1375 /**
1376  * @brief
1377  * Checks if the token is a child of the other token
1378  * of the current process that the calling thread is invoking this function.
1379  *
1380  * @param[in] Token
1381  * An access token to determine if it's a child or not.
1382  *
1383  * @param[out] IsChild
1384  * The returned boolean result.
1385  *
1386  * @return
1387  * Returns STATUS_SUCCESS when the function finishes its operation. STATUS_UNSUCCESSFUL is
1388  * returned if primary token of the current calling process couldn't be referenced otherwise.
1389  */
1390 NTSTATUS
1391 NTAPI
1392 SeIsTokenChild(
1393     _In_ PTOKEN Token,
1394     _Out_ PBOOLEAN IsChild)
1395 {
1396     PTOKEN ProcessToken;
1397     LUID ProcessTokenId, CallerParentId;
1398 
1399     /* Assume failure */
1400     *IsChild = FALSE;
1401 
1402     /* Reference the process token */
1403     ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess());
1404     if (!ProcessToken)
1405         return STATUS_UNSUCCESSFUL;
1406 
1407     /* Get its token ID */
1408     ProcessTokenId = ProcessToken->TokenId;
1409 
1410     /* Dereference the token */
1411     ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken);
1412 
1413     /* Get our parent token ID */
1414     CallerParentId = Token->ParentTokenId;
1415 
1416     /* Compare the token IDs */
1417     if (RtlEqualLuid(&CallerParentId, &ProcessTokenId))
1418         *IsChild = TRUE;
1419 
1420     /* Return success */
1421     return STATUS_SUCCESS;
1422 }
1423 
1424 /**
1425  * @brief
1426  * Checks if the token is a sibling of the other token of
1427  * the current process that the calling thread is invoking this function.
1428  *
1429  * @param[in] Token
1430  * An access token to determine if it's a sibling or not.
1431  *
1432  * @param[out] IsSibling
1433  * The returned boolean result.
1434  *
1435  * @return
1436  * Returns STATUS_SUCCESS when the function finishes its operation. STATUS_UNSUCCESSFUL is
1437  * returned if primary token of the current calling process couldn't be referenced otherwise.
1438  */
1439 NTSTATUS
1440 NTAPI
1441 SeIsTokenSibling(
1442     _In_ PTOKEN Token,
1443     _Out_ PBOOLEAN IsSibling)
1444 {
1445     PTOKEN ProcessToken;
1446     LUID ProcessParentId, ProcessAuthId;
1447     LUID CallerParentId, CallerAuthId;
1448 
1449     /* Assume failure */
1450     *IsSibling = FALSE;
1451 
1452     /* Reference the process token */
1453     ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess());
1454     if (!ProcessToken)
1455         return STATUS_UNSUCCESSFUL;
1456 
1457     /* Get its parent and authentication IDs */
1458     ProcessParentId = ProcessToken->ParentTokenId;
1459     ProcessAuthId = ProcessToken->AuthenticationId;
1460 
1461     /* Dereference the token */
1462     ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken);
1463 
1464     /* Get our parent and authentication IDs */
1465     CallerParentId = Token->ParentTokenId;
1466     CallerAuthId = Token->AuthenticationId;
1467 
1468     /* Compare the token IDs */
1469     if (RtlEqualLuid(&CallerParentId, &ProcessParentId) &&
1470         RtlEqualLuid(&CallerAuthId, &ProcessAuthId))
1471     {
1472         *IsSibling = TRUE;
1473     }
1474 
1475     /* Return success */
1476     return STATUS_SUCCESS;
1477 }
1478 
1479 /**
1480  * @brief
1481  * Copies an existing access token (technically duplicating a new one).
1482  *
1483  * @param[in] Token
1484  * Token to copy.
1485  *
1486  * @param[in] Level
1487  * Impersonation security level to assign to the newly copied token.
1488  *
1489  * @param[in] PreviousMode
1490  * Processor request level mode.
1491  *
1492  * @param[out] NewToken
1493  * The newly copied token.
1494  *
1495  * @return
1496  * Returns STATUS_SUCCESS when token copying has finished successfully. A failure
1497  * NTSTATUS code is returned otherwise.
1498  */
1499 NTSTATUS
1500 NTAPI
1501 SeCopyClientToken(
1502     _In_ PACCESS_TOKEN Token,
1503     _In_ SECURITY_IMPERSONATION_LEVEL Level,
1504     _In_ KPROCESSOR_MODE PreviousMode,
1505     _Out_ PACCESS_TOKEN* NewToken)
1506 {
1507     NTSTATUS Status;
1508     OBJECT_ATTRIBUTES ObjectAttributes;
1509 
1510     PAGED_CODE();
1511 
1512     InitializeObjectAttributes(&ObjectAttributes,
1513                                NULL,
1514                                0,
1515                                NULL,
1516                                NULL);
1517 
1518     Status = SepDuplicateToken(Token,
1519                                &ObjectAttributes,
1520                                FALSE,
1521                                TokenImpersonation,
1522                                Level,
1523                                PreviousMode,
1524                                (PTOKEN*)NewToken);
1525 
1526     return Status;
1527 }
1528 
1529 /**
1530  * @brief
1531  * Internal function that deals with access token object destruction and deletion.
1532  * The function is used solely by the object manager mechanism that handles the life
1533  * management of a token object.
1534  *
1535  * @param[in] ObjectBody
1536  * The object body that represents an access token object.
1537  *
1538  * @return
1539  * Nothing.
1540  */
1541 VOID
1542 NTAPI
1543 SepDeleteToken(
1544     _In_ PVOID ObjectBody)
1545 {
1546     NTSTATUS Status;
1547     PTOKEN AccessToken = (PTOKEN)ObjectBody;
1548 
1549     DPRINT("SepDeleteToken()\n");
1550 
1551     /* Remove the referenced logon session from token */
1552     if (AccessToken->LogonSession)
1553     {
1554         Status = SepRmRemoveLogonSessionFromToken(AccessToken);
1555         if (!NT_SUCCESS(Status))
1556         {
1557             /* Something seriously went wrong */
1558             DPRINT1("SepDeleteToken(): Failed to remove the logon session from token (Status: 0x%lx)\n", Status);
1559             return;
1560         }
1561     }
1562 
1563     /* Dereference the logon session */
1564     if ((AccessToken->TokenFlags & TOKEN_SESSION_NOT_REFERENCED) == 0)
1565         SepRmDereferenceLogonSession(&AccessToken->AuthenticationId);
1566 
1567     /* Delete the token lock */
1568     if (AccessToken->TokenLock)
1569         SepDeleteTokenLock(AccessToken);
1570 
1571     /* Delete the dynamic information area */
1572     if (AccessToken->DynamicPart)
1573         ExFreePoolWithTag(AccessToken->DynamicPart, TAG_TOKEN_DYNAMIC);
1574 }
1575 
1576 /**
1577  * @brief
1578  * Internal function that initializes critical kernel data for access
1579  * token implementation in SRM.
1580  *
1581  * @return
1582  * Nothing.
1583  */
1584 CODE_SEG("INIT")
1585 VOID
1586 NTAPI
1587 SepInitializeTokenImplementation(VOID)
1588 {
1589     UNICODE_STRING Name;
1590     OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
1591 
1592     DPRINT("Creating Token Object Type\n");
1593 
1594     /* Initialize the Token type */
1595     RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
1596     RtlInitUnicodeString(&Name, L"Token");
1597     ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
1598     ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
1599     ObjectTypeInitializer.SecurityRequired = TRUE;
1600     ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(TOKEN);
1601     ObjectTypeInitializer.GenericMapping = SepTokenMapping;
1602     ObjectTypeInitializer.PoolType = PagedPool;
1603     ObjectTypeInitializer.ValidAccessMask = TOKEN_ALL_ACCESS;
1604     ObjectTypeInitializer.UseDefaultObject = TRUE;
1605     ObjectTypeInitializer.DeleteProcedure = SepDeleteToken;
1606     ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &SeTokenObjectType);
1607 }
1608 
1609 /**
1610  * @brief
1611  * Assigns a primary access token to a given process.
1612  *
1613  * @param[in] Process
1614  * Process where the token is about to be assigned.
1615  *
1616  * @param[in] Token
1617  * The token to be assigned.
1618  *
1619  * @return
1620  * Nothing.
1621  */
1622 VOID
1623 NTAPI
1624 SeAssignPrimaryToken(
1625     _In_ PEPROCESS Process,
1626     _In_ PTOKEN Token)
1627 {
1628     PAGED_CODE();
1629 
1630     /* Sanity checks */
1631     ASSERT(Token->TokenType == TokenPrimary);
1632     ASSERT(!Token->TokenInUse);
1633 
1634     /* Clean any previous token */
1635     if (Process->Token.Object) SeDeassignPrimaryToken(Process);
1636 
1637     /* Set the new token */
1638     ObReferenceObject(Token);
1639     Token->TokenInUse = TRUE;
1640     ObInitializeFastReference(&Process->Token, Token);
1641 }
1642 
1643 /**
1644  * @brief
1645  * Internal function responsible for access token object creation in the kernel.
1646  * A fully created token objected is inserted into the token handle, thus the handle
1647  * becoming a valid handle to an access token object and ready for use.
1648  *
1649  * @param[out] TokenHandle
1650  * Valid token handle that's ready for use after token creation and object insertion.
1651  *
1652  * @param[in] PreviousMode
1653  * Processor request level mode.
1654  *
1655  * @param[in] DesiredAccess
1656  * Desired access right for the token object to be granted. This kind of access right
1657  * impacts how the token can be used and who.
1658  *
1659  * @param[in] ObjectAttributes
1660  * Object attributes for the token to be created.
1661  *
1662  * @param[in] TokenType
1663  * Type of token to assign upon creation.
1664  *
1665  * @param[in] ImpersonationLevel
1666  * Security impersonation level of token to assign upon creation.
1667  *
1668  * @param[in] AuthenticationId
1669  * Authentication ID that represents the authentication information of the token.
1670  *
1671  * @param[in] ExpirationTime
1672  * Expiration time of the token to assign. A value of -1 means that the token never
1673  * expires and its life depends upon the amount of references this token object has.
1674  *
1675  * @param[in] User
1676  * User entry to assign to the token.
1677  *
1678  * @param[in] GroupCount
1679  * The total number of groups count for the token.
1680  *
1681  * @param[in] Groups
1682  * The group entries for the token.
1683  *
1684  * @param[in] GroupsLength
1685  * The length size of the groups array, pointed by the Groups parameter.
1686  *
1687  * @param[in] PrivilegeCount
1688  * The total number of priivleges that the newly created token has.
1689  *
1690  * @param[in] Privileges
1691  * The privileges for the token.
1692  *
1693  * @param[in] Owner
1694  * The main user (or also owner) that represents the token that we create.
1695  *
1696  * @param[in] PrimaryGroup
1697  * The main group that represents the token that we create.
1698  *
1699  * @param[in] DefaultDacl
1700  * A discretionary access control list for the token.
1701  *
1702  * @param[in] TokenSource
1703  * Source (or the origin) of the access token that creates it.
1704  *
1705  * @param[in] SystemToken
1706  * If set to TRUE, the newly created token is a system token and only in charge
1707  * by the internal system. The function directly returns a pointer to the
1708  * created token object for system kernel use. Otherwise if set to FALSE, the
1709  * function inserts the object to a handle making it a regular access token.
1710  *
1711  * @return
1712  * Returns STATUS_SUCCESS if token creation has completed successfully.
1713  * STATUS_INSUFFICIENT_RESOURCES is returned if the dynamic area of memory of the
1714  * token hasn't been allocated because of lack of memory resources. A failure
1715  * NTSTATUS code is returned otherwise.
1716  */
1717 NTSTATUS
1718 NTAPI
1719 SepCreateToken(
1720     _Out_ PHANDLE TokenHandle,
1721     _In_ KPROCESSOR_MODE PreviousMode,
1722     _In_ ACCESS_MASK DesiredAccess,
1723     _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
1724     _In_ TOKEN_TYPE TokenType,
1725     _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
1726     _In_ PLUID AuthenticationId,
1727     _In_ PLARGE_INTEGER ExpirationTime,
1728     _In_ PSID_AND_ATTRIBUTES User,
1729     _In_ ULONG GroupCount,
1730     _In_ PSID_AND_ATTRIBUTES Groups,
1731     _In_ ULONG GroupsLength,
1732     _In_ ULONG PrivilegeCount,
1733     _In_ PLUID_AND_ATTRIBUTES Privileges,
1734     _In_opt_ PSID Owner,
1735     _In_ PSID PrimaryGroup,
1736     _In_opt_ PACL DefaultDacl,
1737     _In_ PTOKEN_SOURCE TokenSource,
1738     _In_ BOOLEAN SystemToken)
1739 {
1740     NTSTATUS Status;
1741     PTOKEN AccessToken;
1742     ULONG TokenFlags = 0;
1743     ULONG PrimaryGroupIndex, DefaultOwnerIndex;
1744     LUID TokenId;
1745     LUID ModifiedId;
1746     PVOID EndMem;
1747     ULONG PrivilegesLength;
1748     ULONG UserGroupsLength;
1749     ULONG VariableLength;
1750     ULONG TotalSize;
1751     ULONG i;
1752 
1753     PAGED_CODE();
1754 
1755     /* Loop all groups */
1756     for (i = 0; i < GroupCount; i++)
1757     {
1758         /* Check for mandatory groups */
1759         if (Groups[i].Attributes & SE_GROUP_MANDATORY)
1760         {
1761             /* Force them to be enabled */
1762             Groups[i].Attributes |= (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT);
1763         }
1764 
1765         /* Check of the group is an admin group */
1766         if (RtlEqualSid(SeAliasAdminsSid, Groups[i].Sid))
1767         {
1768             /* Remember this so we can optimize queries later */
1769             TokenFlags |= TOKEN_HAS_ADMIN_GROUP;
1770         }
1771     }
1772 
1773     /* Allocate unique IDs for the token */
1774     ExAllocateLocallyUniqueId(&TokenId);
1775     ExAllocateLocallyUniqueId(&ModifiedId);
1776 
1777     /* Compute how much size we need to allocate for the token */
1778 
1779     /* Privileges size */
1780     PrivilegesLength = PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
1781     PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID));
1782 
1783     /* User and groups size */
1784     UserGroupsLength = (1 + GroupCount) * sizeof(SID_AND_ATTRIBUTES);
1785     UserGroupsLength += RtlLengthSid(User->Sid);
1786     for (i = 0; i < GroupCount; i++)
1787     {
1788         UserGroupsLength += RtlLengthSid(Groups[i].Sid);
1789     }
1790     UserGroupsLength = ALIGN_UP_BY(UserGroupsLength, sizeof(PVOID));
1791 
1792     /* Add the additional groups array length */
1793     UserGroupsLength += ALIGN_UP_BY(GroupsLength, sizeof(PVOID));
1794 
1795     VariableLength = PrivilegesLength + UserGroupsLength;
1796     TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
1797 
1798     Status = ObCreateObject(PreviousMode,
1799                             SeTokenObjectType,
1800                             ObjectAttributes,
1801                             PreviousMode,
1802                             NULL,
1803                             TotalSize,
1804                             0,
1805                             0,
1806                             (PVOID*)&AccessToken);
1807     if (!NT_SUCCESS(Status))
1808     {
1809         DPRINT1("ObCreateObject() failed (Status 0x%lx)\n", Status);
1810         return Status;
1811     }
1812 
1813     /* Zero out the buffer and initialize the token */
1814     RtlZeroMemory(AccessToken, TotalSize);
1815 
1816     RtlCopyLuid(&AccessToken->TokenId, &TokenId);
1817 
1818     AccessToken->TokenType = TokenType;
1819     AccessToken->ImpersonationLevel = ImpersonationLevel;
1820 
1821     /* Initialise the lock for the access token */
1822     Status = SepCreateTokenLock(AccessToken);
1823     if (!NT_SUCCESS(Status))
1824         goto Quit;
1825 
1826     RtlCopyLuid(&AccessToken->TokenSource.SourceIdentifier,
1827                 &TokenSource->SourceIdentifier);
1828     RtlCopyMemory(AccessToken->TokenSource.SourceName,
1829                   TokenSource->SourceName,
1830                   sizeof(TokenSource->SourceName));
1831 
1832     AccessToken->ExpirationTime = *ExpirationTime;
1833     RtlCopyLuid(&AccessToken->ModifiedId, &ModifiedId);
1834 
1835     AccessToken->TokenFlags = TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
1836 
1837     /* Copy and reference the logon session */
1838     RtlCopyLuid(&AccessToken->AuthenticationId, AuthenticationId);
1839     Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
1840     if (!NT_SUCCESS(Status))
1841     {
1842         /* No logon session could be found, bail out */
1843         DPRINT1("SepRmReferenceLogonSession() failed (Status 0x%lx)\n", Status);
1844         /* Set the flag for proper cleanup by the delete procedure */
1845         AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED;
1846         goto Quit;
1847     }
1848 
1849     /* Insert the referenced logon session into the token */
1850     Status = SepRmInsertLogonSessionIntoToken(AccessToken);
1851     if (!NT_SUCCESS(Status))
1852     {
1853         /* Failed to insert the logon session into the token, bail out */
1854         DPRINT1("SepRmInsertLogonSessionIntoToken() failed (Status 0x%lx)\n", Status);
1855         goto Quit;
1856     }
1857 
1858     /* Fill in token debug information */
1859 #if DBG
1860     /*
1861      * We must determine ourselves that the current
1862      * process is not the initial CPU one. The initial
1863      * process is not a "real" process, that is, the
1864      * Process Manager has not yet been initialized and
1865      * as a matter of fact we are creating a token before
1866      * any process gets created by Ps. If it turns out
1867      * that the current process is the initial CPU process
1868      * where token creation execution takes place, don't
1869      * do anything.
1870      */
1871     if (PsGetCurrentProcess() != &KiInitialProcess)
1872     {
1873         RtlCopyMemory(AccessToken->ImageFileName,
1874                       PsGetCurrentProcess()->ImageFileName,
1875                       min(sizeof(AccessToken->ImageFileName), sizeof(PsGetCurrentProcess()->ImageFileName)));
1876 
1877         AccessToken->ProcessCid = PsGetCurrentProcessId();
1878         AccessToken->ThreadCid = PsGetCurrentThreadId();
1879     }
1880 
1881     AccessToken->CreateMethod = TOKEN_CREATE_METHOD;
1882 #endif
1883 
1884     /* Assign the data that reside in the TOKEN's variable information area */
1885     AccessToken->VariableLength = VariableLength;
1886     EndMem = (PVOID)&AccessToken->VariablePart;
1887 
1888     /* Copy the privileges */
1889     AccessToken->PrivilegeCount = PrivilegeCount;
1890     AccessToken->Privileges = NULL;
1891     if (PrivilegeCount > 0)
1892     {
1893         AccessToken->Privileges = EndMem;
1894         EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength);
1895         VariableLength -= PrivilegesLength;
1896 
1897         if (PreviousMode != KernelMode)
1898         {
1899             _SEH2_TRY
1900             {
1901                 RtlCopyMemory(AccessToken->Privileges,
1902                               Privileges,
1903                               PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
1904             }
1905             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1906             {
1907                 Status = _SEH2_GetExceptionCode();
1908             }
1909             _SEH2_END;
1910         }
1911         else
1912         {
1913             RtlCopyMemory(AccessToken->Privileges,
1914                           Privileges,
1915                           PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
1916         }
1917 
1918         if (!NT_SUCCESS(Status))
1919             goto Quit;
1920     }
1921 
1922     /* Update the privilege flags */
1923     SepUpdatePrivilegeFlagsToken(AccessToken);
1924 
1925     /* Copy the user and groups */
1926     AccessToken->UserAndGroupCount = 1 + GroupCount;
1927     AccessToken->UserAndGroups = EndMem;
1928     EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
1929     VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups);
1930 
1931     Status = RtlCopySidAndAttributesArray(1,
1932                                           User,
1933                                           VariableLength,
1934                                           &AccessToken->UserAndGroups[0],
1935                                           EndMem,
1936                                           &EndMem,
1937                                           &VariableLength);
1938     if (!NT_SUCCESS(Status))
1939         goto Quit;
1940 
1941     Status = RtlCopySidAndAttributesArray(GroupCount,
1942                                           Groups,
1943                                           VariableLength,
1944                                           &AccessToken->UserAndGroups[1],
1945                                           EndMem,
1946                                           &EndMem,
1947                                           &VariableLength);
1948     if (!NT_SUCCESS(Status))
1949         goto Quit;
1950 
1951     /* Find the token primary group and default owner */
1952     Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken,
1953                                                 PrimaryGroup,
1954                                                 Owner,
1955                                                 &PrimaryGroupIndex,
1956                                                 &DefaultOwnerIndex);
1957     if (!NT_SUCCESS(Status))
1958     {
1959         DPRINT1("SepFindPrimaryGroupAndDefaultOwner failed (Status 0x%lx)\n", Status);
1960         goto Quit;
1961     }
1962 
1963     AccessToken->PrimaryGroup = AccessToken->UserAndGroups[PrimaryGroupIndex].Sid;
1964     AccessToken->DefaultOwnerIndex = DefaultOwnerIndex;
1965 
1966     /* Now allocate the TOKEN's dynamic information area and set the data */
1967     AccessToken->DynamicAvailable = 0; // Unused memory in the dynamic area.
1968     AccessToken->DynamicPart = NULL;
1969     if (DefaultDacl != NULL)
1970     {
1971         AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
1972                                                          DefaultDacl->AclSize,
1973                                                          TAG_TOKEN_DYNAMIC);
1974         if (AccessToken->DynamicPart == NULL)
1975         {
1976             Status = STATUS_INSUFFICIENT_RESOURCES;
1977             goto Quit;
1978         }
1979         EndMem = (PVOID)AccessToken->DynamicPart;
1980 
1981         AccessToken->DefaultDacl = EndMem;
1982 
1983         RtlCopyMemory(AccessToken->DefaultDacl,
1984                       DefaultDacl,
1985                       DefaultDacl->AclSize);
1986     }
1987 
1988     /* Insert the token only if it's not the system token, otherwise return it directly */
1989     if (!SystemToken)
1990     {
1991         Status = ObInsertObject(AccessToken,
1992                                 NULL,
1993                                 DesiredAccess,
1994                                 0,
1995                                 NULL,
1996                                 TokenHandle);
1997         if (!NT_SUCCESS(Status))
1998         {
1999             DPRINT1("ObInsertObject() failed (Status 0x%lx)\n", Status);
2000         }
2001     }
2002     else
2003     {
2004         /* Return pointer instead of handle */
2005         *TokenHandle = (HANDLE)AccessToken;
2006     }
2007 
2008 Quit:
2009     if (!NT_SUCCESS(Status))
2010     {
2011         /* Dereference the token, the delete procedure will clean it up */
2012         ObDereferenceObject(AccessToken);
2013     }
2014 
2015     return Status;
2016 }
2017 
2018 /**
2019  * @brief
2020  * Private helper function responsible for creating a restricted access
2021  * token, that is, a filtered token from privileges and groups and with
2022  * restricted SIDs added into the token on demand by the caller.
2023  *
2024  * @param[in] Token
2025  * An existing and valid access token.
2026  *
2027  * @param[in] PrivilegesToBeDeleted
2028  * A list of privileges to be deleted within the token that's going
2029  * to be filtered. This parameter is ignored if the caller wants to disable
2030  * all the privileges by specifying DISABLE_MAX_PRIVILEGE in the flags
2031  * parameter.
2032  *
2033  * @param[in] SidsToBeDisabled
2034  * A list of group SIDs to be disabled within the token. This parameter
2035  * can be NULL.
2036  *
2037  * @param[in] RestrictedSidsIntoToken
2038  * A list of restricted SIDs to be added into the token. This parameter
2039  * can be NULL.
2040  *
2041  * @param[in] PrivilegesCount
2042  * The privilege count of the privileges list.
2043  *
2044  * @param[in] RegularGroupsSidCount
2045  * The SIDs count of the group SIDs list.
2046  *
2047  * @param[in] RestrictedSidsCount
2048  * The restricted SIDs count of restricted SIDs list.
2049  *
2050  * @param[in] PrivilegeFlags
2051  * Influences how the privileges should be filtered in an access
2052  * token. See NtFilterToken syscall for more information.
2053  *
2054  * @param[in] PreviousMode
2055  * Processor level access mode.
2056  *
2057  * @param[out] FilteredToken
2058  * The filtered token, returned to the caller.
2059  *
2060  * @return
2061  * Returns STATUS_SUCCESS if token token filtering has completed successfully.
2062  * STATUS_INVALID_PARAMETER is returned if one or more of the parameters
2063  * do not meet the conditions imposed by the function. A failure NTSTATUS
2064  * code is returned otherwise.
2065  *
2066  * @remarks
2067  * The final outcome of privileges and/or SIDs filtering is not always
2068  * deterministic. That is, any privileges or SIDs that aren't present
2069  * in the access token are ignored and the function continues with the
2070  * next privilege or SID to find for filtering. For a fully deterministic
2071  * outcome the caller is responsible for querying the information details
2072  * of privileges and SIDs present in the token and then afterwards use
2073  * such obtained information to do any kind of filtering to the token.
2074  */
2075 static
2076 NTSTATUS
2077 SepPerformTokenFiltering(
2078     _In_ PTOKEN Token,
2079     _In_opt_ PLUID_AND_ATTRIBUTES PrivilegesToBeDeleted,
2080     _In_opt_ PSID_AND_ATTRIBUTES SidsToBeDisabled,
2081     _In_opt_ PSID_AND_ATTRIBUTES RestrictedSidsIntoToken,
2082     _When_(PrivilegesToBeDeleted != NULL, _In_) ULONG PrivilegesCount,
2083     _When_(SidsToBeDisabled != NULL, _In_) ULONG RegularGroupsSidCount,
2084     _When_(RestrictedSidsIntoToken != NULL, _In_) ULONG RestrictedSidsCount,
2085     _In_ ULONG PrivilegeFlags,
2086     _In_ KPROCESSOR_MODE PreviousMode,
2087     _Out_ PTOKEN *FilteredToken)
2088 {
2089     PTOKEN AccessToken;
2090     NTSTATUS Status;
2091     PVOID EndMem;
2092     ULONG RestrictedSidsLength;
2093     ULONG PrivilegesLength;
2094     ULONG PrimaryGroupIndex;
2095     ULONG RestrictedSidsInList;
2096     ULONG RestrictedSidsInToken;
2097     ULONG VariableLength, TotalSize;
2098     ULONG PrivsInToken, PrivsInList;
2099     ULONG GroupsInToken, GroupsInList;
2100     BOOLEAN WantPrivilegesDisabled;
2101     BOOLEAN FoundPrivilege;
2102     BOOLEAN FoundGroup;
2103     PAGED_CODE();
2104 
2105     /* Ensure that the token we get is not garbage */
2106     ASSERT(Token);
2107 
2108     /* Assume the caller doesn't want privileges disabled */
2109     WantPrivilegesDisabled = FALSE;
2110 
2111     /* Assume we haven't found anything */
2112     FoundPrivilege = FALSE;
2113     FoundGroup = FALSE;
2114 
2115     /*
2116      * Take the size that we need for filtered token
2117      * allocation based upon the existing access token
2118      * we've been given.
2119      */
2120     VariableLength = Token->VariableLength;
2121 
2122     if (RestrictedSidsIntoToken != NULL)
2123     {
2124         /*
2125          * If the caller provided a list of restricted SIDs
2126          * to be added onto the filtered access token then
2127          * we must compute the size which is the total space
2128          * of the current token and the length of the restricted
2129          * SIDs for the filtered token.
2130          */
2131         RestrictedSidsLength = RestrictedSidsCount * sizeof(SID_AND_ATTRIBUTES);
2132         RestrictedSidsLength += RtlLengthSidAndAttributes(RestrictedSidsCount, RestrictedSidsIntoToken);
2133         RestrictedSidsLength = ALIGN_UP_BY(RestrictedSidsLength, sizeof(PVOID));
2134 
2135         /*
2136          * The variable length of the token is not just
2137          * the actual space length of the existing token
2138          * but also the sum of the restricted SIDs length.
2139          */
2140         VariableLength += RestrictedSidsLength;
2141         TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength + RestrictedSidsLength;
2142     }
2143     else
2144     {
2145         /* Otherwise the size is of the actual current token */
2146         TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
2147     }
2148 
2149     /* Set up a filtered token object */
2150     Status = ObCreateObject(PreviousMode,
2151                             SeTokenObjectType,
2152                             NULL,
2153                             PreviousMode,
2154                             NULL,
2155                             TotalSize,
2156                             0,
2157                             0,
2158                             (PVOID*)&AccessToken);
2159     if (!NT_SUCCESS(Status))
2160     {
2161         DPRINT1("SepPerformTokenFiltering(): Failed to create the filtered token object (Status 0x%lx)\n", Status);
2162         return Status;
2163     }
2164 
2165     /* Initialize the token and begin filling stuff to it */
2166     RtlZeroMemory(AccessToken, TotalSize);
2167 
2168     /* Set up a lock for the new token */
2169     Status = SepCreateTokenLock(AccessToken);
2170     if (!NT_SUCCESS(Status))
2171     {
2172         ObDereferenceObject(AccessToken);
2173         return Status;
2174     }
2175 
2176     /* Allocate new IDs for the token */
2177     ExAllocateLocallyUniqueId(&AccessToken->TokenId);
2178     ExAllocateLocallyUniqueId(&AccessToken->ModifiedId);
2179 
2180     /* Copy the type and impersonation level from the token */
2181     AccessToken->TokenType = Token->TokenType;
2182     AccessToken->ImpersonationLevel = Token->ImpersonationLevel;
2183 
2184     /* Copy the immutable fields */
2185     RtlCopyLuid(&AccessToken->TokenSource.SourceIdentifier,
2186                 &Token->TokenSource.SourceIdentifier);
2187     RtlCopyMemory(AccessToken->TokenSource.SourceName,
2188                   Token->TokenSource.SourceName,
2189                   sizeof(Token->TokenSource.SourceName));
2190 
2191     RtlCopyLuid(&AccessToken->AuthenticationId, &Token->AuthenticationId);
2192     RtlCopyLuid(&AccessToken->ParentTokenId, &Token->TokenId);
2193     RtlCopyLuid(&AccessToken->OriginatingLogonSession,
2194                 &Token->OriginatingLogonSession);
2195 
2196     AccessToken->ExpirationTime = Token->ExpirationTime;
2197 
2198     /* Copy the mutable fields */
2199     AccessToken->SessionId = Token->SessionId;
2200     AccessToken->TokenFlags = Token->TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
2201 
2202     /* Reference the logon session */
2203     Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
2204     if (!NT_SUCCESS(Status))
2205     {
2206         /* We failed, bail out*/
2207         DPRINT1("SepPerformTokenFiltering(): Failed to reference the logon session (Status 0x%lx)\n", Status);
2208         AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED;
2209         goto Quit;
2210     }
2211 
2212     /* Insert the referenced logon session into the token */
2213     Status = SepRmInsertLogonSessionIntoToken(AccessToken);
2214     if (!NT_SUCCESS(Status))
2215     {
2216         /* Failed to insert the logon session into the token, bail out */
2217         DPRINT1("SepPerformTokenFiltering(): Failed to insert the logon session into token (Status 0x%lx)\n", Status);
2218         goto Quit;
2219     }
2220 
2221     /* Fill in token debug information */
2222 #if DBG
2223     RtlCopyMemory(AccessToken->ImageFileName,
2224                   PsGetCurrentProcess()->ImageFileName,
2225                   min(sizeof(AccessToken->ImageFileName), sizeof(PsGetCurrentProcess()->ImageFileName)));
2226 
2227     AccessToken->ProcessCid = PsGetCurrentProcessId();
2228     AccessToken->ThreadCid = PsGetCurrentThreadId();
2229     AccessToken->CreateMethod = TOKEN_FILTER_METHOD;
2230 #endif
2231 
2232     /* Assign the data that reside in the token's variable information area */
2233     AccessToken->VariableLength = VariableLength;
2234     EndMem = (PVOID)&AccessToken->VariablePart;
2235 
2236     /* Copy the privileges from the existing token */
2237     AccessToken->PrivilegeCount = 0;
2238     AccessToken->Privileges = NULL;
2239     if (Token->Privileges && (Token->PrivilegeCount > 0))
2240     {
2241         PrivilegesLength = Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
2242         PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID));
2243 
2244         /*
2245          * Ensure that the token can actually hold all
2246          * the privileges from the existing token.
2247          * Otherwise something's seriously wrong and
2248          * we've to guard ourselves.
2249          */
2250         ASSERT(VariableLength >= PrivilegesLength);
2251 
2252         AccessToken->PrivilegeCount = Token->PrivilegeCount;
2253         AccessToken->Privileges = EndMem;
2254         EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength);
2255         VariableLength -= PrivilegesLength;
2256 
2257         if (PreviousMode != KernelMode)
2258         {
2259             _SEH2_TRY
2260             {
2261                 RtlCopyMemory(AccessToken->Privileges,
2262                               Token->Privileges,
2263                               AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
2264             }
2265             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2266             {
2267                 Status = _SEH2_GetExceptionCode();
2268                 _SEH2_YIELD(goto Quit);
2269             }
2270             _SEH2_END;
2271         }
2272         else
2273         {
2274             RtlCopyMemory(AccessToken->Privileges,
2275                           Token->Privileges,
2276                           AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
2277         }
2278     }
2279 
2280     /* Copy the user and groups */
2281     AccessToken->UserAndGroupCount = 0;
2282     AccessToken->UserAndGroups = NULL;
2283     if (Token->UserAndGroups && (Token->UserAndGroupCount > 0))
2284     {
2285         AccessToken->UserAndGroupCount = Token->UserAndGroupCount;
2286         AccessToken->UserAndGroups = EndMem;
2287         EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
2288         VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups);
2289 
2290         if (PreviousMode != KernelMode)
2291         {
2292             _SEH2_TRY
2293             {
2294                 Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount,
2295                                                       Token->UserAndGroups,
2296                                                       VariableLength,
2297                                                       AccessToken->UserAndGroups,
2298                                                       EndMem,
2299                                                       &EndMem,
2300                                                       &VariableLength);
2301             }
2302             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2303             {
2304                 Status = _SEH2_GetExceptionCode();
2305                 _SEH2_YIELD(goto Quit);
2306             }
2307             _SEH2_END;
2308         }
2309         else
2310         {
2311             Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount,
2312                                                   Token->UserAndGroups,
2313                                                   VariableLength,
2314                                                   AccessToken->UserAndGroups,
2315                                                   EndMem,
2316                                                   &EndMem,
2317                                                   &VariableLength);
2318             if (!NT_SUCCESS(Status))
2319             {
2320                 DPRINT1("SepPerformTokenFiltering(): Failed to copy the groups into token (Status 0x%lx)\n", Status);
2321                 goto Quit;
2322             }
2323         }
2324     }
2325 
2326     /* Copy the restricted SIDs */
2327     AccessToken->RestrictedSidCount = 0;
2328     AccessToken->RestrictedSids = NULL;
2329     if (Token->RestrictedSids && (Token->RestrictedSidCount > 0))
2330     {
2331         AccessToken->RestrictedSidCount = Token->RestrictedSidCount;
2332         AccessToken->RestrictedSids = EndMem;
2333         EndMem = &AccessToken->RestrictedSids[AccessToken->RestrictedSidCount];
2334         VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->RestrictedSids);
2335 
2336         if (PreviousMode != KernelMode)
2337         {
2338             _SEH2_TRY
2339             {
2340                 Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount,
2341                                                       Token->RestrictedSids,
2342                                                       VariableLength,
2343                                                       AccessToken->RestrictedSids,
2344                                                       EndMem,
2345                                                       &EndMem,
2346                                                       &VariableLength);
2347             }
2348             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2349             {
2350                 Status = _SEH2_GetExceptionCode();
2351                 _SEH2_YIELD(goto Quit);
2352             }
2353             _SEH2_END;
2354         }
2355         else
2356         {
2357             Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount,
2358                                                   Token->RestrictedSids,
2359                                                   VariableLength,
2360                                                   AccessToken->RestrictedSids,
2361                                                   EndMem,
2362                                                   &EndMem,
2363                                                   &VariableLength);
2364             if (!NT_SUCCESS(Status))
2365             {
2366                 DPRINT1("SepPerformTokenFiltering(): Failed to copy the restricted SIDs into token (Status 0x%lx)\n", Status);
2367                 goto Quit;
2368             }
2369         }
2370     }
2371 
2372     /* Search for the primary group */
2373     Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken,
2374                                                 Token->PrimaryGroup,
2375                                                 NULL,
2376                                                 &PrimaryGroupIndex,
2377                                                 NULL);
2378     if (!NT_SUCCESS(Status))
2379     {
2380         DPRINT1("SepPerformTokenFiltering(): Failed searching for the primary group (Status 0x%lx)\n", Status);
2381         goto Quit;
2382     }
2383 
2384     /* Assign the primary group and default owner index now */
2385     AccessToken->PrimaryGroup = AccessToken->UserAndGroups[PrimaryGroupIndex].Sid;
2386     AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex;
2387 
2388     /* Now allocate the token's dynamic information area and set the data */
2389     AccessToken->DynamicAvailable = 0;
2390     AccessToken->DynamicPart = NULL;
2391     if (Token->DynamicPart && Token->DefaultDacl)
2392     {
2393         AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
2394                                                          Token->DefaultDacl->AclSize,
2395                                                          TAG_TOKEN_DYNAMIC);
2396         if (AccessToken->DynamicPart == NULL)
2397         {
2398             Status = STATUS_INSUFFICIENT_RESOURCES;
2399             goto Quit;
2400         }
2401 
2402         EndMem = (PVOID)AccessToken->DynamicPart;
2403         AccessToken->DefaultDacl = EndMem;
2404 
2405         RtlCopyMemory(AccessToken->DefaultDacl,
2406                       Token->DefaultDacl,
2407                       Token->DefaultDacl->AclSize);
2408     }
2409 
2410     /*
2411      * Now figure out what does the caller
2412      * want with the privileges.
2413      */
2414     if (PrivilegeFlags & DISABLE_MAX_PRIVILEGE)
2415     {
2416         /*
2417          * The caller wants them disabled, cache this request
2418          * for later operations.
2419          */
2420         WantPrivilegesDisabled = TRUE;
2421     }
2422 
2423     if (PrivilegeFlags & SANDBOX_INERT)
2424     {
2425         /* The caller wants an inert token, store the TOKEN_SANDBOX_INERT flag now */
2426         AccessToken->TokenFlags |= TOKEN_SANDBOX_INERT;
2427     }
2428 
2429     /*
2430      * Now it's time to filter the token's privileges.
2431      * Loop all the privileges in the token.
2432      */
2433     for (PrivsInToken = 0; PrivsInToken < AccessToken->PrivilegeCount; PrivsInToken++)
2434     {
2435         if (WantPrivilegesDisabled)
2436         {
2437             /*
2438              * We got the acknowledgement that the caller wants
2439              * to disable all the privileges so let's just do it.
2440              * However, as per the general documentation is stated
2441              * that only SE_CHANGE_NOTIFY_PRIVILEGE must be kept
2442              * therefore in that case we must skip this privilege.
2443              */
2444             if (AccessToken->Privileges[PrivsInToken].Luid.LowPart == SE_CHANGE_NOTIFY_PRIVILEGE)
2445             {
2446                 continue;
2447             }
2448             else
2449             {
2450                 /*
2451                  * The act of disabling privileges actually means
2452                  * "deleting" them from the access token entirely.
2453                  * First we must disable them so that we can update
2454                  * token flags accordingly.
2455                  */
2456                 AccessToken->Privileges[PrivsInToken].Attributes &= ~SE_PRIVILEGE_ENABLED;
2457                 SepUpdateSinglePrivilegeFlagToken(AccessToken, PrivsInToken);
2458 
2459                 /* Remove the privileges now */
2460                 SepRemovePrivilegeToken(AccessToken, PrivsInToken);
2461                 PrivsInToken--;
2462             }
2463         }
2464         else
2465         {
2466             if (PrivilegesToBeDeleted != NULL)
2467             {
2468                 /* Loop the privileges we've got to delete */
2469                 for (PrivsInList = 0; PrivsInList < PrivilegesCount; PrivsInList++)
2470                 {
2471                     /* Does this privilege exist in the token? */
2472                     if (RtlEqualLuid(&AccessToken->Privileges[PrivsInToken].Luid,
2473                                      &PrivilegesToBeDeleted[PrivsInList].Luid))
2474                     {
2475                         /* Mark that we found it */
2476                         FoundPrivilege = TRUE;
2477                         break;
2478                     }
2479                 }
2480 
2481                 /* Did we find the privilege? */
2482                 if (PrivsInList == PrivilegesCount)
2483                 {
2484                     /* We didn't, continue with next one */
2485                     continue;
2486                 }
2487             }
2488         }
2489 
2490         /*
2491          * If we have found the target privilege in the token
2492          * based on the privileges list given by the caller
2493          * then begin deleting it.
2494          */
2495         if (FoundPrivilege)
2496         {
2497             /* Disable the privilege and update the flags */
2498             AccessToken->Privileges[PrivsInToken].Attributes &= ~SE_PRIVILEGE_ENABLED;
2499             SepUpdateSinglePrivilegeFlagToken(AccessToken, PrivsInToken);
2500 
2501             /* Delete the privilege */
2502             SepRemovePrivilegeToken(AccessToken, PrivsInToken);
2503 
2504             /*
2505              * Adjust the index and reset the FoundPrivilege indicator
2506              * so that we can continue with the next privilege to delete.
2507              */
2508             PrivsInToken--;
2509             FoundPrivilege = FALSE;
2510             continue;
2511         }
2512     }
2513 
2514     /*
2515      * Loop the group SIDs that we want to disable as
2516      * per on the request by the caller.
2517      */
2518     if (SidsToBeDisabled != NULL)
2519     {
2520         for (GroupsInToken = 0; GroupsInToken < AccessToken->UserAndGroupCount; GroupsInToken++)
2521         {
2522             for (GroupsInList = 0; GroupsInList < RegularGroupsSidCount; GroupsInList++)
2523             {
2524                 /* Does this group SID exist in the token? */
2525                 if (RtlEqualSid(&AccessToken->UserAndGroups[GroupsInToken].Sid,
2526                                 &SidsToBeDisabled[GroupsInList].Sid))
2527                 {
2528                     /* Mark that we found it */
2529                     FoundGroup = TRUE;
2530                     break;
2531                 }
2532             }
2533 
2534             /* Did we find the group? */
2535             if (GroupsInList == RegularGroupsSidCount)
2536             {
2537                 /* We didn't, continue with next one */
2538                 continue;
2539             }
2540 
2541             /* If we have found the group, disable it */
2542             if (FoundGroup)
2543             {
2544                 /*
2545                  * If the acess token belongs to the administrators
2546                  * group and this is the target group, we must take
2547                  * away TOKEN_HAS_ADMIN_GROUP flag from the token.
2548                  */
2549                 if (RtlEqualSid(SeAliasAdminsSid,
2550                                 &AccessToken->UserAndGroups[GroupsInToken].Sid))
2551                 {
2552                     AccessToken->TokenFlags &= ~TOKEN_HAS_ADMIN_GROUP;
2553                 }
2554 
2555                 /*
2556                  * If the target group that we have found it is the
2557                  * owner then from now on it no longer is but the user.
2558                  * Therefore assign the default owner index as the user.
2559                  */
2560                 if (AccessToken->DefaultOwnerIndex == GroupsInToken)
2561                 {
2562                     AccessToken->DefaultOwnerIndex = 0;
2563                 }
2564 
2565                 /*
2566                  * The principle of disabling a group SID is by
2567                  * taking away SE_GROUP_ENABLED_BY_DEFAULT and
2568                  * SE_GROUP_ENABLED attributes and assign
2569                  * SE_GROUP_USE_FOR_DENY_ONLY. This renders
2570                  * SID a "Deny only" SID.
2571                  */
2572                 AccessToken->UserAndGroups[GroupsInToken].Attributes &= ~(SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT);
2573                 AccessToken->UserAndGroups[GroupsInToken].Attributes |= SE_GROUP_USE_FOR_DENY_ONLY;
2574 
2575                 /* Adjust the index and continue with the next group */
2576                 GroupsInToken--;
2577                 FoundGroup = FALSE;
2578                 continue;
2579             }
2580         }
2581     }
2582 
2583     /*
2584      * Insert the restricted SIDs into the token on
2585      * the request by the caller.
2586      */
2587     if (RestrictedSidsIntoToken != NULL)
2588     {
2589         for (RestrictedSidsInList = 0; RestrictedSidsInList < RestrictedSidsCount; RestrictedSidsInList++)
2590         {
2591             /* Did the caller assign attributes to the restricted SIDs? */
2592             if (RestrictedSidsIntoToken[RestrictedSidsInList].Attributes != 0)
2593             {
2594                 /* There mustn't be any attributes, bail out */
2595                 DPRINT1("SepPerformTokenFiltering(): There mustn't be any attributes to restricted SIDs!\n");
2596                 Status = STATUS_INVALID_PARAMETER;
2597                 goto Quit;
2598             }
2599         }
2600 
2601         /*
2602          * Ensure that the token can hold the restricted SIDs
2603          * (the variable length is calculated at the beginning
2604          * of the routine call).
2605          */
2606         ASSERT(VariableLength >= RestrictedSidsLength);
2607 
2608         /*
2609          * Now let's begin inserting the restricted SIDs into the filtered
2610          * access token from the list the caller gave us.
2611          */
2612         AccessToken->RestrictedSidCount = RestrictedSidsCount;
2613         AccessToken->RestrictedSids = EndMem;
2614         EndMem = (PVOID)((ULONG_PTR)EndMem + RestrictedSidsLength);
2615         VariableLength -= RestrictedSidsLength;
2616 
2617         if (PreviousMode != KernelMode)
2618         {
2619             _SEH2_TRY
2620             {
2621                 RtlCopyMemory(AccessToken->RestrictedSids,
2622                               RestrictedSidsIntoToken,
2623                               AccessToken->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES));
2624             }
2625             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2626             {
2627                 Status = _SEH2_GetExceptionCode();
2628                 _SEH2_YIELD(goto Quit);
2629             }
2630             _SEH2_END;
2631         }
2632         else
2633         {
2634             RtlCopyMemory(AccessToken->RestrictedSids,
2635                           RestrictedSidsIntoToken,
2636                           AccessToken->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES));
2637         }
2638 
2639         /*
2640          * As we've copied the restricted SIDs into
2641          * the token, we must assign them the following
2642          * combination of attributes SE_GROUP_ENABLED,
2643          * SE_GROUP_ENABLED_BY_DEFAULT and SE_GROUP_MANDATORY.
2644          * With such attributes we estabilish that restricting
2645          * SIDs into the token are enabled for access checks.
2646          */
2647         for (RestrictedSidsInToken = 0; RestrictedSidsInToken < AccessToken->RestrictedSidCount; RestrictedSidsInToken++)
2648         {
2649             AccessToken->RestrictedSids[RestrictedSidsInToken].Attributes |= (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY);
2650         }
2651 
2652         /*
2653          * As we added restricted SIDs into the token, mark
2654          * it as restricted.
2655          */
2656         AccessToken->TokenFlags |= TOKEN_IS_RESTRICTED;
2657     }
2658 
2659     /* We've finally filtered the token, give it to the caller */
2660     *FilteredToken = AccessToken;
2661     Status = STATUS_SUCCESS;
2662     DPRINT("SepPerformTokenFiltering(): The token has been filtered!\n");
2663 
2664 Quit:
2665     if (!NT_SUCCESS(Status))
2666     {
2667         /* Dereference the token */
2668         ObDereferenceObject(AccessToken);
2669     }
2670 
2671     return Status;
2672 }
2673 
2674 /**
2675  * @brief
2676  * Creates the system process token.
2677  *
2678  * @return
2679  * Returns the system process token if the operations have
2680  * completed successfully.
2681  */
2682 CODE_SEG("INIT")
2683 PTOKEN
2684 NTAPI
2685 SepCreateSystemProcessToken(VOID)
2686 {
2687     LUID_AND_ATTRIBUTES Privileges[25];
2688     ULONG GroupAttributes, OwnerAttributes;
2689     SID_AND_ATTRIBUTES Groups[32];
2690     LARGE_INTEGER Expiration;
2691     SID_AND_ATTRIBUTES UserSid;
2692     ULONG GroupsLength;
2693     PSID PrimaryGroup;
2694     OBJECT_ATTRIBUTES ObjectAttributes;
2695     PSID Owner;
2696     ULONG i;
2697     PTOKEN Token;
2698     NTSTATUS Status;
2699 
2700     /* Don't ever expire */
2701     Expiration.QuadPart = -1;
2702 
2703     /* All groups mandatory and enabled */
2704     GroupAttributes = SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT;
2705     OwnerAttributes = SE_GROUP_ENABLED | SE_GROUP_OWNER | SE_GROUP_ENABLED_BY_DEFAULT;
2706 
2707     /* User is Local System */
2708     UserSid.Sid = SeLocalSystemSid;
2709     UserSid.Attributes = 0;
2710 
2711     /* Primary group is Local System */
2712     PrimaryGroup = SeLocalSystemSid;
2713 
2714     /* Owner is Administrators */
2715     Owner = SeAliasAdminsSid;
2716 
2717     /* Groups are Administrators, World, and Authenticated Users */
2718     Groups[0].Sid = SeAliasAdminsSid;
2719     Groups[0].Attributes = OwnerAttributes;
2720     Groups[1].Sid = SeWorldSid;
2721     Groups[1].Attributes = GroupAttributes;
2722     Groups[2].Sid = SeAuthenticatedUsersSid;
2723     Groups[2].Attributes = GroupAttributes;
2724     GroupsLength = sizeof(SID_AND_ATTRIBUTES) +
2725                    SeLengthSid(Groups[0].Sid) +
2726                    SeLengthSid(Groups[1].Sid) +
2727                    SeLengthSid(Groups[2].Sid);
2728     ASSERT(GroupsLength <= sizeof(Groups));
2729 
2730     /* Setup the privileges */
2731     i = 0;
2732     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
2733     Privileges[i++].Luid = SeTcbPrivilege;
2734 
2735     Privileges[i].Attributes = 0;
2736     Privileges[i++].Luid = SeCreateTokenPrivilege;
2737 
2738     Privileges[i].Attributes = 0;
2739     Privileges[i++].Luid = SeTakeOwnershipPrivilege;
2740 
2741     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
2742     Privileges[i++].Luid = SeCreatePagefilePrivilege;
2743 
2744     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
2745     Privileges[i++].Luid = SeLockMemoryPrivilege;
2746 
2747     Privileges[i].Attributes = 0;
2748     Privileges[i++].Luid = SeAssignPrimaryTokenPrivilege;
2749 
2750     Privileges[i].Attributes = 0;
2751     Privileges[i++].Luid = SeIncreaseQuotaPrivilege;
2752 
2753     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
2754     Privileges[i++].Luid = SeIncreaseBasePriorityPrivilege;
2755 
2756     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
2757     Privileges[i++].Luid = SeCreatePermanentPrivilege;
2758 
2759     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
2760     Privileges[i++].Luid = SeDebugPrivilege;
2761 
2762     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
2763     Privileges[i++].Luid = SeAuditPrivilege;
2764 
2765     Privileges[i].Attributes = 0;
2766     Privileges[i++].Luid = SeSecurityPrivilege;
2767 
2768     Privileges[i].Attributes = 0;
2769     Privileges[i++].Luid = SeSystemEnvironmentPrivilege;
2770 
2771     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
2772     Privileges[i++].Luid = SeChangeNotifyPrivilege;
2773 
2774     Privileges[i].Attributes = 0;
2775     Privileges[i++].Luid = SeBackupPrivilege;
2776 
2777     Privileges[i].Attributes = 0;
2778     Privileges[i++].Luid = SeRestorePrivilege;
2779 
2780     Privileges[i].Attributes = 0;
2781     Privileges[i++].Luid = SeShutdownPrivilege;
2782 
2783     Privileges[i].Attributes = 0;
2784     Privileges[i++].Luid = SeLoadDriverPrivilege;
2785 
2786     Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
2787     Privileges[i++].Luid = SeProfileSingleProcessPrivilege;
2788 
2789     Privileges[i].Attributes = 0;
2790     Privileges[i++].Luid = SeSystemtimePrivilege;
2791     ASSERT(i == 20);
2792 
2793     /* Setup the object attributes */
2794     InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
2795     ASSERT(SeSystemDefaultDacl != NULL);
2796 
2797     /* Create the token */
2798     Status = SepCreateToken((PHANDLE)&Token,
2799                             KernelMode,
2800                             0,
2801                             &ObjectAttributes,
2802                             TokenPrimary,
2803                             SecurityAnonymous,
2804                             &SeSystemAuthenticationId,
2805                             &Expiration,
2806                             &UserSid,
2807                             3,
2808                             Groups,
2809                             GroupsLength,
2810                             20,
2811                             Privileges,
2812                             Owner,
2813                             PrimaryGroup,
2814                             SeSystemDefaultDacl,
2815                             &SeSystemTokenSource,
2816                             TRUE);
2817     ASSERT(Status == STATUS_SUCCESS);
2818 
2819     /* Return the token */
2820     return Token;
2821 }
2822 
2823 /**
2824  * @brief
2825  * Creates the anonymous logon token for the system. The difference between this
2826  * token and the other one is the inclusion of everyone SID group (being SeWorldSid).
2827  * The other token lacks such group.
2828  *
2829  * @return
2830  * Returns the system's anonymous logon token if the operations have
2831  * completed successfully.
2832  */
2833 CODE_SEG("INIT")
2834 PTOKEN
2835 SepCreateSystemAnonymousLogonToken(VOID)
2836 {
2837     SID_AND_ATTRIBUTES Groups[32], UserSid;
2838     PSID PrimaryGroup;
2839     PTOKEN Token;
2840     ULONG GroupsLength;
2841     LARGE_INTEGER Expiration;
2842     OBJECT_ATTRIBUTES ObjectAttributes;
2843     NTSTATUS Status;
2844 
2845     /* The token never expires */
2846     Expiration.QuadPart = -1;
2847 
2848     /* The user is the anonymous logon */
2849     UserSid.Sid = SeAnonymousLogonSid;
2850     UserSid.Attributes = 0;
2851 
2852     /* The primary group is also the anonymous logon */
2853     PrimaryGroup = SeAnonymousLogonSid;
2854 
2855     /* The only group for the token is the World */
2856     Groups[0].Sid = SeWorldSid;
2857     Groups[0].Attributes = SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT;
2858     GroupsLength = sizeof(SID_AND_ATTRIBUTES) +
2859                    SeLengthSid(Groups[0].Sid);
2860     ASSERT(GroupsLength <= sizeof(Groups));
2861 
2862     /* Initialise the object attributes for the token */
2863     InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
2864     ASSERT(SeSystemAnonymousLogonDacl != NULL);
2865 
2866     /* Create token */
2867     Status = SepCreateToken((PHANDLE)&Token,
2868                             KernelMode,
2869                             0,
2870                             &ObjectAttributes,
2871                             TokenPrimary,
2872                             SecurityAnonymous,
2873                             &SeAnonymousAuthenticationId,
2874                             &Expiration,
2875                             &UserSid,
2876                             1,
2877                             Groups,
2878                             GroupsLength,
2879                             0,
2880                             NULL,
2881                             NULL,
2882                             PrimaryGroup,
2883                             SeSystemAnonymousLogonDacl,
2884                             &SeSystemTokenSource,
2885                             TRUE);
2886     ASSERT(Status == STATUS_SUCCESS);
2887 
2888     /* Return the anonymous logon token */
2889     return Token;
2890 }
2891 
2892 /**
2893  * @brief
2894  * Creates the anonymous logon token for the system. This kind of token
2895  * doesn't include the everyone SID group (being SeWorldSid).
2896  *
2897  * @return
2898  * Returns the system's anonymous logon token if the operations have
2899  * completed successfully.
2900  */
2901 CODE_SEG("INIT")
2902 PTOKEN
2903 SepCreateSystemAnonymousLogonTokenNoEveryone(VOID)
2904 {
2905     SID_AND_ATTRIBUTES UserSid;
2906     PSID PrimaryGroup;
2907     PTOKEN Token;
2908     LARGE_INTEGER Expiration;
2909     OBJECT_ATTRIBUTES ObjectAttributes;
2910     NTSTATUS Status;
2911 
2912     /* The token never expires */
2913     Expiration.QuadPart = -1;
2914 
2915     /* The user is the anonymous logon */
2916     UserSid.Sid = SeAnonymousLogonSid;
2917     UserSid.Attributes = 0;
2918 
2919     /* The primary group is also the anonymous logon */
2920     PrimaryGroup = SeAnonymousLogonSid;
2921 
2922     /* Initialise the object attributes for the token */
2923     InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
2924     ASSERT(SeSystemAnonymousLogonDacl != NULL);
2925 
2926     /* Create token */
2927     Status = SepCreateToken((PHANDLE)&Token,
2928                             KernelMode,
2929                             0,
2930                             &ObjectAttributes,
2931                             TokenPrimary,
2932                             SecurityAnonymous,
2933                             &SeAnonymousAuthenticationId,
2934                             &Expiration,
2935                             &UserSid,
2936                             0,
2937                             NULL,
2938                             0,
2939                             0,
2940                             NULL,
2941                             NULL,
2942                             PrimaryGroup,
2943                             SeSystemAnonymousLogonDacl,
2944                             &SeSystemTokenSource,
2945                             TRUE);
2946     ASSERT(Status == STATUS_SUCCESS);
2947 
2948     /* Return the anonymous (not including everyone) logon token */
2949     return Token;
2950 }
2951 
2952 /* PUBLIC FUNCTIONS ***********************************************************/
2953 
2954 /**
2955  * @brief
2956  * Filters an access token from an existing token, making it more restricted
2957  * than the previous one.
2958  *
2959  * @param[in] ExistingToken
2960  * An existing token for filtering.
2961  *
2962  * @param[in] Flags
2963  * Privilege flag options. This parameter argument influences how the token
2964  * is filtered. Such parameter can be 0. See NtFilterToken syscall for
2965  * more information.
2966  *
2967  * @param[in] SidsToDisable
2968  * Array of SIDs to disable. Such parameter can be NULL.
2969  *
2970  * @param[in] PrivilegesToDelete
2971  * Array of privileges to delete. If DISABLE_MAX_PRIVILEGE flag is specified
2972  * in the Flags parameter, PrivilegesToDelete is ignored.
2973  *
2974  * @param[in] RestrictedSids
2975  * An array of restricted SIDs for the new filtered token. Such parameter
2976  * can be NULL.
2977  *
2978  * @param[out] FilteredToken
2979  * The newly filtered token, returned to the caller.
2980  *
2981  * @return
2982  * Returns STATUS_SUCCESS if the function has successfully completed its
2983  * operations and that the access token has been filtered. STATUS_INVALID_PARAMETER
2984  * is returned if one or more of the parameter are not valid. A failure NTSTATUS code
2985  * is returned otherwise.
2986  *
2987  * @remarks
2988  * WARNING -- The caller IS RESPONSIBLE for locking the existing access token
2989  *            before attempting to do any kind of filtering operation into
2990  *            the token. The lock MUST BE RELEASED after this kernel routine
2991  *            has finished doing its work.
2992  */
2993 NTSTATUS
2994 NTAPI
2995 SeFilterToken(
2996     _In_ PACCESS_TOKEN ExistingToken,
2997     _In_ ULONG Flags,
2998     _In_opt_ PTOKEN_GROUPS SidsToDisable,
2999     _In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete,
3000     _In_opt_ PTOKEN_GROUPS RestrictedSids,
3001     _Out_ PACCESS_TOKEN *FilteredToken)
3002 {
3003     NTSTATUS Status;
3004     PTOKEN AccessToken;
3005     ULONG PrivilegesCount = 0;
3006     ULONG SidsCount = 0;
3007     ULONG RestrictedSidsCount = 0;
3008     PAGED_CODE();
3009 
3010     /* Begin copying the counters */
3011     if (SidsToDisable != NULL)
3012     {
3013         SidsCount = SidsToDisable->GroupCount;
3014     }
3015 
3016     if (PrivilegesToDelete != NULL)
3017     {
3018         PrivilegesCount = PrivilegesToDelete->PrivilegeCount;
3019     }
3020 
3021     if (RestrictedSids != NULL)
3022     {
3023         RestrictedSidsCount = RestrictedSids->GroupCount;
3024     }
3025 
3026     /* Call the internal API */
3027     Status = SepPerformTokenFiltering(ExistingToken,
3028                                       PrivilegesToDelete->Privileges,
3029                                       SidsToDisable->Groups,
3030                                       RestrictedSids->Groups,
3031                                       PrivilegesCount,
3032                                       SidsCount,
3033                                       RestrictedSidsCount,
3034                                       Flags,
3035                                       KernelMode,
3036                                       &AccessToken);
3037     if (!NT_SUCCESS(Status))
3038     {
3039         DPRINT1("SeFilterToken(): Failed to filter the token (Status 0x%lx)\n", Status);
3040         return Status;
3041     }
3042 
3043     /* Insert the filtered token */
3044     Status = ObInsertObject(AccessToken,
3045                             NULL,
3046                             0,
3047                             0,
3048                             NULL,
3049                             NULL);
3050     if (!NT_SUCCESS(Status))
3051     {
3052         DPRINT1("SeFilterToken(): Failed to insert the token (Status 0x%lx)\n", Status);
3053         return Status;
3054     }
3055 
3056     /* Give it to the caller */
3057     *FilteredToken = AccessToken;
3058     return Status;
3059 }
3060 
3061 /**
3062  * @brief
3063  * Queries information details about the given token to the call. The difference
3064  * between NtQueryInformationToken and this routine is that the system call has
3065  * user mode buffer data probing and additional protection checks whereas this
3066  * routine doesn't have any of these. The routine is used exclusively in kernel
3067  * mode.
3068  *
3069  * @param[in] AccessToken
3070  * An access token to be given.
3071  *
3072  * @param[in] TokenInformationClass
3073  * Token information class.
3074  *
3075  * @param[out] TokenInformation
3076  * Buffer with retrieved information. Such information is arbitrary, depending
3077  * on the requested information class.
3078  *
3079  * @return
3080  * Returns STATUS_SUCCESS if the operation to query the desired information
3081  * has completed successfully. STATUS_INSUFFICIENT_RESOURCES is returned if
3082  * pool memory allocation has failed to satisfy an operation. Otherwise
3083  * STATUS_INVALID_INFO_CLASS is returned indicating that the information
3084  * class provided is not supported by the routine.
3085  *
3086  * @remarks
3087  * Only certain information classes are not implemented in this function and
3088  * these are TokenOrigin, TokenGroupsAndPrivileges, TokenRestrictedSids and
3089  * TokenSandBoxInert. The following classes are implemented in NtQueryInformationToken
3090  * only.
3091  */
3092 NTSTATUS
3093 NTAPI
3094 SeQueryInformationToken(
3095     _In_ PACCESS_TOKEN AccessToken,
3096     _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
3097     _Outptr_result_buffer_(_Inexpressible_(token-dependent)) PVOID *TokenInformation)
3098 {
3099     NTSTATUS Status;
3100     PTOKEN Token = (PTOKEN)AccessToken;
3101     ULONG RequiredLength;
3102     union
3103     {
3104         PSID PSid;
3105         ULONG Ulong;
3106     } Unused;
3107 
3108     PAGED_CODE();
3109 
3110     /* Lock the token */
3111     SepAcquireTokenLockShared(Token);
3112 
3113     switch (TokenInformationClass)
3114     {
3115         case TokenUser:
3116         {
3117             PTOKEN_USER tu;
3118 
3119             DPRINT("SeQueryInformationToken(TokenUser)\n");
3120             RequiredLength = sizeof(TOKEN_USER) +
3121                 RtlLengthSid(Token->UserAndGroups[0].Sid);
3122 
3123             /* Allocate the output buffer */
3124             tu = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
3125             if (tu == NULL)
3126             {
3127                 Status = STATUS_INSUFFICIENT_RESOURCES;
3128                 break;
3129             }
3130 
3131             Status = RtlCopySidAndAttributesArray(1,
3132                                                   &Token->UserAndGroups[0],
3133                                                   RequiredLength - sizeof(TOKEN_USER),
3134                                                   &tu->User,
3135                                                   (PSID)(tu + 1),
3136                                                   &Unused.PSid,
3137                                                   &Unused.Ulong);
3138 
3139             /* Return the structure */
3140             *TokenInformation = tu;
3141             Status = STATUS_SUCCESS;
3142             break;
3143         }
3144 
3145         case TokenGroups:
3146         {
3147             PTOKEN_GROUPS tg;
3148             ULONG SidLen;
3149             PSID Sid;
3150 
3151             DPRINT("SeQueryInformationToken(TokenGroups)\n");
3152             RequiredLength = sizeof(tg->GroupCount) +
3153                 RtlLengthSidAndAttributes(Token->UserAndGroupCount - 1, &Token->UserAndGroups[1]);
3154 
3155             SidLen = RequiredLength - sizeof(tg->GroupCount) -
3156                 ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES));
3157 
3158             /* Allocate the output buffer */
3159             tg = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
3160             if (tg == NULL)
3161             {
3162                 Status = STATUS_INSUFFICIENT_RESOURCES;
3163                 break;
3164             }
3165 
3166             Sid = (PSID)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
3167                          ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES)));
3168 
3169             tg->GroupCount = Token->UserAndGroupCount - 1;
3170             Status = RtlCopySidAndAttributesArray(Token->UserAndGroupCount - 1,
3171                                                   &Token->UserAndGroups[1],
3172                                                   SidLen,
3173                                                   &tg->Groups[0],
3174                                                   Sid,
3175                                                   &Unused.PSid,
3176                                                   &Unused.Ulong);
3177 
3178             /* Return the structure */
3179             *TokenInformation = tg;
3180             Status = STATUS_SUCCESS;
3181             break;
3182         }
3183 
3184         case TokenPrivileges:
3185         {
3186             PTOKEN_PRIVILEGES tp;
3187 
3188             DPRINT("SeQueryInformationToken(TokenPrivileges)\n");
3189             RequiredLength = sizeof(tp->PrivilegeCount) +
3190                 (Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
3191 
3192             /* Allocate the output buffer */
3193             tp = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
3194             if (tp == NULL)
3195             {
3196                 Status = STATUS_INSUFFICIENT_RESOURCES;
3197                 break;
3198             }
3199 
3200             tp->PrivilegeCount = Token->PrivilegeCount;
3201             RtlCopyLuidAndAttributesArray(Token->PrivilegeCount,
3202                                           Token->Privileges,
3203                                           &tp->Privileges[0]);
3204 
3205             /* Return the structure */
3206             *TokenInformation = tp;
3207             Status = STATUS_SUCCESS;
3208             break;
3209         }
3210 
3211         case TokenOwner:
3212         {
3213             PTOKEN_OWNER to;
3214             ULONG SidLen;
3215 
3216             DPRINT("SeQueryInformationToken(TokenOwner)\n");
3217             SidLen = RtlLengthSid(Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
3218             RequiredLength = sizeof(TOKEN_OWNER) + SidLen;
3219 
3220             /* Allocate the output buffer */
3221             to = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
3222             if (to == NULL)
3223             {
3224                 Status = STATUS_INSUFFICIENT_RESOURCES;
3225                 break;
3226             }
3227 
3228             to->Owner = (PSID)(to + 1);
3229             Status = RtlCopySid(SidLen,
3230                                 to->Owner,
3231                                 Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
3232 
3233             /* Return the structure */
3234             *TokenInformation = to;
3235             Status = STATUS_SUCCESS;
3236             break;
3237         }
3238 
3239         case TokenPrimaryGroup:
3240         {
3241             PTOKEN_PRIMARY_GROUP tpg;
3242             ULONG SidLen;
3243 
3244             DPRINT("SeQueryInformationToken(TokenPrimaryGroup)\n");
3245             SidLen = RtlLengthSid(Token->PrimaryGroup);
3246             RequiredLength = sizeof(TOKEN_PRIMARY_GROUP) + SidLen;
3247 
3248             /* Allocate the output buffer */
3249             tpg = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
3250             if (tpg == NULL)
3251             {
3252                 Status = STATUS_INSUFFICIENT_RESOURCES;
3253                 break;
3254             }
3255 
3256             tpg->PrimaryGroup = (PSID)(tpg + 1);
3257             Status = RtlCopySid(SidLen,
3258                                 tpg->PrimaryGroup,
3259                                 Token->PrimaryGroup);
3260 
3261             /* Return the structure */
3262             *TokenInformation = tpg;
3263             Status = STATUS_SUCCESS;
3264             break;
3265         }
3266 
3267         case TokenDefaultDacl:
3268         {
3269             PTOKEN_DEFAULT_DACL tdd;
3270 
3271             DPRINT("SeQueryInformationToken(TokenDefaultDacl)\n");
3272             RequiredLength = sizeof(TOKEN_DEFAULT_DACL);
3273 
3274             if (Token->DefaultDacl != NULL)
3275                 RequiredLength += Token->DefaultDacl->AclSize;
3276 
3277             /* Allocate the output buffer */
3278             tdd = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
3279             if (tdd == NULL)
3280             {
3281                 Status = STATUS_INSUFFICIENT_RESOURCES;
3282                 break;
3283             }
3284 
3285             if (Token->DefaultDacl != NULL)
3286             {
3287                 tdd->DefaultDacl = (PACL)(tdd + 1);
3288                 RtlCopyMemory(tdd->DefaultDacl,
3289                               Token->DefaultDacl,
3290                               Token->DefaultDacl->AclSize);
3291             }
3292             else
3293             {
3294                 tdd->DefaultDacl = NULL;
3295             }
3296 
3297             /* Return the structure */
3298             *TokenInformation = tdd;
3299             Status = STATUS_SUCCESS;
3300             break;
3301         }
3302 
3303         case TokenSource:
3304         {
3305             PTOKEN_SOURCE ts;
3306 
3307             DPRINT("SeQueryInformationToken(TokenSource)\n");
3308             RequiredLength = sizeof(TOKEN_SOURCE);
3309 
3310             /* Allocate the output buffer */
3311             ts = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
3312             if (ts == NULL)
3313             {
3314                 Status = STATUS_INSUFFICIENT_RESOURCES;
3315                 break;
3316             }
3317 
3318             *ts = Token->TokenSource;
3319 
3320             /* Return the structure */
3321             *TokenInformation = ts;
3322             Status = STATUS_SUCCESS;
3323             break;
3324         }
3325 
3326         case TokenType:
3327         {
3328             PTOKEN_TYPE tt;
3329 
3330             DPRINT("SeQueryInformationToken(TokenType)\n");
3331             RequiredLength = sizeof(TOKEN_TYPE);
3332 
3333             /* Allocate the output buffer */
3334             tt = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
3335             if (tt == NULL)
3336             {
3337                 Status = STATUS_INSUFFICIENT_RESOURCES;
3338                 break;
3339             }
3340 
3341             *tt = Token->TokenType;
3342 
3343             /* Return the structure */
3344             *TokenInformation = tt;
3345             Status = STATUS_SUCCESS;
3346             break;
3347         }
3348 
3349         case TokenImpersonationLevel:
3350         {
3351             PSECURITY_IMPERSONATION_LEVEL sil;
3352 
3353             DPRINT("SeQueryInformationToken(TokenImpersonationLevel)\n");
3354             RequiredLength = sizeof(SECURITY_IMPERSONATION_LEVEL);
3355 
3356             /* Fail if the token is not an impersonation token */
3357             if (Token->TokenType != TokenImpersonation)
3358             {
3359                 Status = STATUS_INVALID_INFO_CLASS;
3360                 break;
3361             }
3362 
3363             /* Allocate the output buffer */
3364             sil = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
3365             if (sil == NULL)
3366             {
3367                 Status = STATUS_INSUFFICIENT_RESOURCES;
3368                 break;
3369             }
3370 
3371             *sil = Token->ImpersonationLevel;
3372 
3373             /* Return the structure */
3374             *TokenInformation = sil;
3375             Status = STATUS_SUCCESS;
3376             break;
3377         }
3378 
3379         case TokenStatistics:
3380         {
3381             PTOKEN_STATISTICS ts;
3382 
3383             DPRINT("SeQueryInformationToken(TokenStatistics)\n");
3384             RequiredLength = sizeof(TOKEN_STATISTICS);
3385 
3386             /* Allocate the output buffer */
3387             ts = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
3388             if (ts == NULL)
3389             {
3390                 Status = STATUS_INSUFFICIENT_RESOURCES;
3391                 break;
3392             }
3393 
3394             ts->TokenId = Token->TokenId;
3395             ts->AuthenticationId = Token->AuthenticationId;
3396             ts->ExpirationTime = Token->ExpirationTime;
3397             ts->TokenType = Token->TokenType;
3398             ts->ImpersonationLevel = Token->ImpersonationLevel;
3399             ts->DynamicCharged = Token->DynamicCharged;
3400             ts->DynamicAvailable = Token->DynamicAvailable;
3401             ts->GroupCount = Token->UserAndGroupCount - 1;
3402             ts->PrivilegeCount = Token->PrivilegeCount;
3403             ts->ModifiedId = Token->ModifiedId;
3404 
3405             /* Return the structure */
3406             *TokenInformation = ts;
3407             Status = STATUS_SUCCESS;
3408             break;
3409         }
3410 
3411         case TokenSessionId:
3412         {
3413             DPRINT("SeQueryInformationToken(TokenSessionId)\n");
3414             Status = SeQuerySessionIdToken(Token, (PULONG)TokenInformation);
3415             break;
3416         }
3417 
3418         default:
3419             DPRINT1("SeQueryInformationToken(%d) invalid information class\n", TokenInformationClass);
3420             Status = STATUS_INVALID_INFO_CLASS;
3421             break;
3422     }
3423 
3424     /* Release the lock of the token */
3425     SepReleaseTokenLock(Token);
3426 
3427     return Status;
3428 }
3429 
3430 /**
3431  * @brief
3432  * Queries the session ID of an access token.
3433  *
3434  * @param[in] Token
3435  * A valid access token where the session ID has to be gathered.
3436  *
3437  * @param[out] pSessionId
3438  * The returned pointer to a session ID to the caller.
3439  *
3440  * @return
3441  * Returns STATUS_SUCCESS.
3442  */
3443 NTSTATUS
3444 NTAPI
3445 SeQuerySessionIdToken(
3446     _In_ PACCESS_TOKEN Token,
3447     _Out_ PULONG pSessionId)
3448 {
3449     PAGED_CODE();
3450 
3451     /* Lock the token */
3452     SepAcquireTokenLockShared(Token);
3453 
3454     *pSessionId = ((PTOKEN)Token)->SessionId;
3455 
3456     /* Unlock the token */
3457     SepReleaseTokenLock(Token);
3458 
3459     return STATUS_SUCCESS;
3460 }
3461 
3462 /**
3463  * @brief
3464  * Queries the authentication ID of an access token.
3465  *
3466  * @param[in] Token
3467  * A valid access token where the authentication ID has to be gathered.
3468  *
3469  * @param[out] pSessionId
3470  * The returned pointer to an authentication ID to the caller.
3471  *
3472  * @return
3473  * Returns STATUS_SUCCESS.
3474  */
3475 NTSTATUS
3476 NTAPI
3477 SeQueryAuthenticationIdToken(
3478     _In_ PACCESS_TOKEN Token,
3479     _Out_ PLUID LogonId)
3480 {
3481     PAGED_CODE();
3482 
3483     *LogonId = ((PTOKEN)Token)->AuthenticationId;
3484 
3485     return STATUS_SUCCESS;
3486 }
3487 
3488 /**
3489  * @brief
3490  * Gathers the security impersonation level of an access token.
3491  *
3492  * @param[in] Token
3493  * A valid access token where the impersonation level has to be gathered.
3494  *
3495  * @return
3496  * Returns the security impersonation level from a valid token.
3497  */
3498 SECURITY_IMPERSONATION_LEVEL
3499 NTAPI
3500 SeTokenImpersonationLevel(
3501     _In_ PACCESS_TOKEN Token)
3502 {
3503     PAGED_CODE();
3504 
3505     return ((PTOKEN)Token)->ImpersonationLevel;
3506 }
3507 
3508 /**
3509  * @brief
3510  * Gathers the token type of an access token. A token ca be either
3511  * a primary token or impersonation token.
3512  *
3513  * @param[in] Token
3514  * A valid access token where the token type has to be gathered.
3515  *
3516  * @return
3517  * Returns the token type from a valid token.
3518  */
3519 TOKEN_TYPE
3520 NTAPI
3521 SeTokenType(
3522     _In_ PACCESS_TOKEN Token)
3523 {
3524     PAGED_CODE();
3525 
3526     return ((PTOKEN)Token)->TokenType;
3527 }
3528 
3529 /**
3530  * @brief
3531  * Determines if a token is either an admin token or not. Such
3532  * condition is checked based upon TOKEN_HAS_ADMIN_GROUP flag,
3533  * which means if the respective access token belongs to an
3534  * administrator group or not.
3535  *
3536  * @param[in] Token
3537  * A valid access token to determine if such token is admin or not.
3538  *
3539  * @return
3540  * Returns TRUE if the token is an admin one, FALSE otherwise.
3541  */
3542 BOOLEAN
3543 NTAPI
3544 SeTokenIsAdmin(
3545     _In_ PACCESS_TOKEN Token)
3546 {
3547     PAGED_CODE();
3548 
3549     // NOTE: Win7+ instead really checks the list of groups in the token
3550     // (since TOKEN_HAS_ADMIN_GROUP == TOKEN_WRITE_RESTRICTED ...)
3551     return (((PTOKEN)Token)->TokenFlags & TOKEN_HAS_ADMIN_GROUP) != 0;
3552 }
3553 
3554 /**
3555  * @brief
3556  * Determines if a token is restricted or not, based upon the token
3557  * flags.
3558  *
3559  * @param[in] Token
3560  * A valid access token to determine if such token is restricted.
3561  *
3562  * @return
3563  * Returns TRUE if the token is restricted, FALSE otherwise.
3564  */
3565 BOOLEAN
3566 NTAPI
3567 SeTokenIsRestricted(
3568     _In_ PACCESS_TOKEN Token)
3569 {
3570     PAGED_CODE();
3571 
3572     return (((PTOKEN)Token)->TokenFlags & TOKEN_IS_RESTRICTED) != 0;
3573 }
3574 
3575 /**
3576  * @brief
3577  * Determines if a token is write restricted, that is, nobody can write anything
3578  * to it.
3579  *
3580  * @param[in] Token
3581  * A valid access token to determine if such token is write restricted.
3582  *
3583  * @return
3584  * Returns TRUE if the token is write restricted, FALSE otherwise.
3585  *
3586  * @remarks
3587  * First introduced in NT 5.1 SP2 x86 (5.1.2600.2622), absent in NT 5.2,
3588  * then finally re-introduced in Vista+.
3589  */
3590 BOOLEAN
3591 NTAPI
3592 SeTokenIsWriteRestricted(
3593     _In_ PACCESS_TOKEN Token)
3594 {
3595     PAGED_CODE();
3596 
3597     // NOTE: NT 5.1 SP2 x86 checks the SE_BACKUP_PRIVILEGES_CHECKED flag
3598     // while Vista+ checks the TOKEN_WRITE_RESTRICTED flag as one expects.
3599     return (((PTOKEN)Token)->TokenFlags & SE_BACKUP_PRIVILEGES_CHECKED) != 0;
3600 }
3601 
3602 /**
3603  * @brief
3604  * Ensures that client impersonation can occur by checking if the token
3605  * we're going to assign as the impersonation token can be actually impersonated
3606  * in the first place. The routine is used primarily by PsImpersonateClient.
3607  *
3608  * @param[in] ProcessToken
3609  * Token from a process.
3610  *
3611  * @param[in] TokenToImpersonate
3612  * Token that we are going to impersonate.
3613  *
3614  * @param[in] ImpersonationLevel
3615  * Security impersonation level grade.
3616  *
3617  * @return
3618  * Returns TRUE if the conditions checked are met for token impersonation,
3619  * FALSE otherwise.
3620  */
3621 BOOLEAN
3622 NTAPI
3623 SeTokenCanImpersonate(
3624     _In_ PTOKEN ProcessToken,
3625     _In_ PTOKEN TokenToImpersonate,
3626     _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel)
3627 {
3628     BOOLEAN CanImpersonate;
3629     PAGED_CODE();
3630 
3631     /*
3632      * SecurityAnonymous and SecurityIdentification levels do not
3633      * allow impersonation.
3634      */
3635     if (ImpersonationLevel == SecurityAnonymous ||
3636         ImpersonationLevel == SecurityIdentification)
3637     {
3638         return FALSE;
3639     }
3640 
3641     /* Time to lock our tokens */
3642     SepAcquireTokenLockShared(ProcessToken);
3643     SepAcquireTokenLockShared(TokenToImpersonate);
3644 
3645     /* What kind of authentication ID does the token have? */
3646     if (RtlEqualLuid(&TokenToImpersonate->AuthenticationId,
3647                      &SeAnonymousAuthenticationId))
3648     {
3649         /*
3650          * OK, it looks like the token has an anonymous
3651          * authentication. Is that token created by the system?
3652          */
3653         if (TokenToImpersonate->TokenSource.SourceName != SeSystemTokenSource.SourceName &&
3654             !RtlEqualLuid(&TokenToImpersonate->TokenSource.SourceIdentifier, &SeSystemTokenSource.SourceIdentifier))
3655         {
3656             /* It isn't, we can't impersonate regular tokens */
3657             DPRINT("SeTokenCanImpersonate(): Token has an anonymous authentication ID, can't impersonate!\n");
3658             CanImpersonate = FALSE;
3659             goto Quit;
3660         }
3661     }
3662 
3663     /* Are the SID values from both tokens equal? */
3664     if (!RtlEqualSid(ProcessToken->UserAndGroups->Sid,
3665                      TokenToImpersonate->UserAndGroups->Sid))
3666     {
3667         /* They aren't, bail out */
3668         DPRINT("SeTokenCanImpersonate(): Tokens SIDs are not equal!\n");
3669         CanImpersonate = FALSE;
3670         goto Quit;
3671     }
3672 
3673     /*
3674      * Make sure the tokens aren't diverged in terms of
3675      * restrictions, that is, one token is restricted
3676      * but the other one isn't.
3677      */
3678     if (SeTokenIsRestricted(ProcessToken) !=
3679         SeTokenIsRestricted(TokenToImpersonate))
3680     {
3681         /*
3682          * One token is restricted so we cannot
3683          * continue further at this point, bail out.
3684          */
3685         DPRINT("SeTokenCanImpersonate(): One token is restricted, can't continue!\n");
3686         CanImpersonate = FALSE;
3687         goto Quit;
3688     }
3689 
3690     /* If we've reached that far then we can impersonate! */
3691     DPRINT("SeTokenCanImpersonate(): We can impersonate.\n");
3692     CanImpersonate = TRUE;
3693 
3694 Quit:
3695     /* We're done, unlock the tokens now */
3696     SepReleaseTokenLock(ProcessToken);
3697     SepReleaseTokenLock(TokenToImpersonate);
3698 
3699     return CanImpersonate;
3700 }
3701 
3702 /* SYSTEM CALLS ***************************************************************/
3703 
3704 /**
3705  * @brief
3706  * Queries a specific type of information in regard of an access token based upon
3707  * the information class. The calling thread must have specific access rights in order
3708  * to obtain specific information about the token.
3709  *
3710  * @param[in] TokenHandle
3711  * A handle of a token where information is to be gathered.
3712  *
3713  * @param[in] TokenInformationClass
3714  * Token information class.
3715  *
3716  * @param[out] TokenInformation
3717  * A returned output buffer with token information, which information is arbitrarily upon
3718  * the information class chosen.
3719  *
3720  * @param[in] TokenInformationLength
3721  * Length of the token information buffer, in bytes.
3722  *
3723  * @param[out] ReturnLength
3724  * If specified in the call, the function returns the total length size of the token
3725  * information buffer..
3726  *
3727  * @return
3728  * Returns STATUS_SUCCESS if information querying has completed successfully.
3729  * STATUS_BUFFER_TOO_SMALL is returned if the information length that represents
3730  * the token information buffer is not greater than the required length.
3731  * STATUS_INVALID_HANDLE is returned if the token handle is not a valid one.
3732  * STATUS_INVALID_INFO_CLASS is returned if the information class is not a valid
3733  * one (that is, the class doesn't belong to TOKEN_INFORMATION_CLASS). A failure
3734  * NTSTATUS code is returned otherwise.
3735  */
3736 _Must_inspect_result_
3737 __kernel_entry
3738 NTSTATUS
3739 NTAPI
3740 NtQueryInformationToken(
3741     _In_ HANDLE TokenHandle,
3742     _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
3743     _Out_writes_bytes_to_opt_(TokenInformationLength, *ReturnLength)
3744         PVOID TokenInformation,
3745     _In_ ULONG TokenInformationLength,
3746     _Out_ PULONG ReturnLength)
3747 {
3748     NTSTATUS Status;
3749     KPROCESSOR_MODE PreviousMode;
3750     PTOKEN Token;
3751     ULONG RequiredLength;
3752     union
3753     {
3754         PSID PSid;
3755         ULONG Ulong;
3756     } Unused;
3757 
3758     PAGED_CODE();
3759 
3760     PreviousMode = ExGetPreviousMode();
3761 
3762     /* Check buffers and class validity */
3763     Status = DefaultQueryInfoBufferCheck(TokenInformationClass,
3764                                          SeTokenInformationClass,
3765                                          RTL_NUMBER_OF(SeTokenInformationClass),
3766                                          TokenInformation,
3767                                          TokenInformationLength,
3768                                          ReturnLength,
3769                                          NULL,
3770                                          PreviousMode,
3771                                          TRUE);
3772     if (!NT_SUCCESS(Status))
3773     {
3774         DPRINT("NtQueryInformationToken() failed, Status: 0x%x\n", Status);
3775         return Status;
3776     }
3777 
3778     Status = ObReferenceObjectByHandle(TokenHandle,
3779                                        (TokenInformationClass == TokenSource) ? TOKEN_QUERY_SOURCE : TOKEN_QUERY,
3780                                        SeTokenObjectType,
3781                                        PreviousMode,
3782                                        (PVOID*)&Token,
3783                                        NULL);
3784     if (NT_SUCCESS(Status))
3785     {
3786         /* Lock the token */
3787         SepAcquireTokenLockShared(Token);
3788 
3789         switch (TokenInformationClass)
3790         {
3791             case TokenUser:
3792             {
3793                 PTOKEN_USER tu = (PTOKEN_USER)TokenInformation;
3794 
3795                 DPRINT("NtQueryInformationToken(TokenUser)\n");
3796                 RequiredLength = sizeof(TOKEN_USER) +
3797                     RtlLengthSid(Token->UserAndGroups[0].Sid);
3798 
3799                 _SEH2_TRY
3800                 {
3801                     if (TokenInformationLength >= RequiredLength)
3802                     {
3803                         Status = RtlCopySidAndAttributesArray(1,
3804                                                               &Token->UserAndGroups[0],
3805                                                               RequiredLength - sizeof(TOKEN_USER),
3806                                                               &tu->User,
3807                                                               (PSID)(tu + 1),
3808                                                               &Unused.PSid,
3809                                                               &Unused.Ulong);
3810                     }
3811                     else
3812                     {
3813                         Status = STATUS_BUFFER_TOO_SMALL;
3814                     }
3815 
3816                     if (ReturnLength != NULL)
3817                     {
3818                         *ReturnLength = RequiredLength;
3819                     }
3820                 }
3821                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3822                 {
3823                     Status = _SEH2_GetExceptionCode();
3824                 }
3825                 _SEH2_END;
3826 
3827                 break;
3828             }
3829 
3830             case TokenGroups:
3831             {
3832                 PTOKEN_GROUPS tg = (PTOKEN_GROUPS)TokenInformation;
3833 
3834                 DPRINT("NtQueryInformationToken(TokenGroups)\n");
3835                 RequiredLength = sizeof(tg->GroupCount) +
3836                     RtlLengthSidAndAttributes(Token->UserAndGroupCount - 1, &Token->UserAndGroups[1]);
3837 
3838                 _SEH2_TRY
3839                 {
3840                     if (TokenInformationLength >= RequiredLength)
3841                     {
3842                         ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) -
3843                             ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES));
3844                         PSID Sid = (PSID_AND_ATTRIBUTES)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
3845                                                          ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES)));
3846 
3847                         tg->GroupCount = Token->UserAndGroupCount - 1;
3848                         Status = RtlCopySidAndAttributesArray(Token->UserAndGroupCount - 1,
3849                                                               &Token->UserAndGroups[1],
3850                                                               SidLen,
3851                                                               &tg->Groups[0],
3852                                                               Sid,
3853                                                               &Unused.PSid,
3854                                                               &Unused.Ulong);
3855                     }
3856                     else
3857                     {
3858                         Status = STATUS_BUFFER_TOO_SMALL;
3859                     }
3860 
3861                     if (ReturnLength != NULL)
3862                     {
3863                         *ReturnLength = RequiredLength;
3864                     }
3865                 }
3866                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3867                 {
3868                     Status = _SEH2_GetExceptionCode();
3869                 }
3870                 _SEH2_END;
3871 
3872                 break;
3873             }
3874 
3875             case TokenPrivileges:
3876             {
3877                 PTOKEN_PRIVILEGES tp = (PTOKEN_PRIVILEGES)TokenInformation;
3878 
3879                 DPRINT("NtQueryInformationToken(TokenPrivileges)\n");
3880                 RequiredLength = sizeof(tp->PrivilegeCount) +
3881                     (Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
3882 
3883                 _SEH2_TRY
3884                 {
3885                     if (TokenInformationLength >= RequiredLength)
3886                     {
3887                         tp->PrivilegeCount = Token->PrivilegeCount;
3888                         RtlCopyLuidAndAttributesArray(Token->PrivilegeCount,
3889                                                       Token->Privileges,
3890                                                       &tp->Privileges[0]);
3891                     }
3892                     else
3893                     {
3894                         Status = STATUS_BUFFER_TOO_SMALL;
3895                     }
3896 
3897                     if (ReturnLength != NULL)
3898                     {
3899                         *ReturnLength = RequiredLength;
3900                     }
3901                 }
3902                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3903                 {
3904                     Status = _SEH2_GetExceptionCode();
3905                 }
3906                 _SEH2_END;
3907 
3908                 break;
3909             }
3910 
3911             case TokenOwner:
3912             {
3913                 PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation;
3914                 ULONG SidLen;
3915 
3916                 DPRINT("NtQueryInformationToken(TokenOwner)\n");
3917                 SidLen = RtlLengthSid(Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
3918                 RequiredLength = sizeof(TOKEN_OWNER) + SidLen;
3919 
3920                 _SEH2_TRY
3921                 {
3922                     if (TokenInformationLength >= RequiredLength)
3923                     {
3924                         to->Owner = (PSID)(to + 1);
3925                         Status = RtlCopySid(SidLen,
3926                                             to->Owner,
3927                                             Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
3928                     }
3929                     else
3930                     {
3931                         Status = STATUS_BUFFER_TOO_SMALL;
3932                     }
3933 
3934                     if (ReturnLength != NULL)
3935                     {
3936                         *ReturnLength = RequiredLength;
3937                     }
3938                 }
3939                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3940                 {
3941                     Status = _SEH2_GetExceptionCode();
3942                 }
3943                 _SEH2_END;
3944 
3945                 break;
3946             }
3947 
3948             case TokenPrimaryGroup:
3949             {
3950                 PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation;
3951                 ULONG SidLen;
3952 
3953                 DPRINT("NtQueryInformationToken(TokenPrimaryGroup)\n");
3954                 SidLen = RtlLengthSid(Token->PrimaryGroup);
3955                 RequiredLength = sizeof(TOKEN_PRIMARY_GROUP) + SidLen;
3956 
3957                 _SEH2_TRY
3958                 {
3959                     if (TokenInformationLength >= RequiredLength)
3960                     {
3961                         tpg->PrimaryGroup = (PSID)(tpg + 1);
3962                         Status = RtlCopySid(SidLen,
3963                                             tpg->PrimaryGroup,
3964                                             Token->PrimaryGroup);
3965                     }
3966                     else
3967                     {
3968                         Status = STATUS_BUFFER_TOO_SMALL;
3969                     }
3970 
3971                     if (ReturnLength != NULL)
3972                     {
3973                         *ReturnLength = RequiredLength;
3974                     }
3975                 }
3976                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3977                 {
3978                     Status = _SEH2_GetExceptionCode();
3979                 }
3980                 _SEH2_END;
3981 
3982                 break;
3983             }
3984 
3985             case TokenDefaultDacl:
3986             {
3987                 PTOKEN_DEFAULT_DACL tdd = (PTOKEN_DEFAULT_DACL)TokenInformation;
3988 
3989                 DPRINT("NtQueryInformationToken(TokenDefaultDacl)\n");
3990                 RequiredLength = sizeof(TOKEN_DEFAULT_DACL);
3991 
3992                 if (Token->DefaultDacl != NULL)
3993                     RequiredLength += Token->DefaultDacl->AclSize;
3994 
3995                 _SEH2_TRY
3996                 {
3997                     if (TokenInformationLength >= RequiredLength)
3998                     {
3999                         if (Token->DefaultDacl != NULL)
4000                         {
4001                             tdd->DefaultDacl = (PACL)(tdd + 1);
4002                             RtlCopyMemory(tdd->DefaultDacl,
4003                                           Token->DefaultDacl,
4004                                           Token->DefaultDacl->AclSize);
4005                         }
4006                         else
4007                         {
4008                             tdd->DefaultDacl = NULL;
4009                         }
4010                     }
4011                     else
4012                     {
4013                         Status = STATUS_BUFFER_TOO_SMALL;
4014                     }
4015 
4016                     if (ReturnLength != NULL)
4017                     {
4018                         *ReturnLength = RequiredLength;
4019                     }
4020                 }
4021                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4022                 {
4023                     Status = _SEH2_GetExceptionCode();
4024                 }
4025                 _SEH2_END;
4026 
4027                 break;
4028             }
4029 
4030             case TokenSource:
4031             {
4032                 PTOKEN_SOURCE ts = (PTOKEN_SOURCE)TokenInformation;
4033 
4034                 DPRINT("NtQueryInformationToken(TokenSource)\n");
4035                 RequiredLength = sizeof(TOKEN_SOURCE);
4036 
4037                 _SEH2_TRY
4038                 {
4039                     if (TokenInformationLength >= RequiredLength)
4040                     {
4041                         *ts = Token->TokenSource;
4042                     }
4043                     else
4044                     {
4045                         Status = STATUS_BUFFER_TOO_SMALL;
4046                     }
4047 
4048                     if (ReturnLength != NULL)
4049                     {
4050                         *ReturnLength = RequiredLength;
4051                     }
4052                 }
4053                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4054                 {
4055                     Status = _SEH2_GetExceptionCode();
4056                 }
4057                 _SEH2_END;
4058 
4059                 break;
4060             }
4061 
4062             case TokenType:
4063             {
4064                 PTOKEN_TYPE tt = (PTOKEN_TYPE)TokenInformation;
4065 
4066                 DPRINT("NtQueryInformationToken(TokenType)\n");
4067                 RequiredLength = sizeof(TOKEN_TYPE);
4068 
4069                 _SEH2_TRY
4070                 {
4071                     if (TokenInformationLength >= RequiredLength)
4072                     {
4073                         *tt = Token->TokenType;
4074                     }
4075                     else
4076                     {
4077                         Status = STATUS_BUFFER_TOO_SMALL;
4078                     }
4079 
4080                     if (ReturnLength != NULL)
4081                     {
4082                         *ReturnLength = RequiredLength;
4083                     }
4084                 }
4085                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4086                 {
4087                     Status = _SEH2_GetExceptionCode();
4088                 }
4089                 _SEH2_END;
4090 
4091                 break;
4092             }
4093 
4094             case TokenImpersonationLevel:
4095             {
4096                 PSECURITY_IMPERSONATION_LEVEL sil = (PSECURITY_IMPERSONATION_LEVEL)TokenInformation;
4097 
4098                 DPRINT("NtQueryInformationToken(TokenImpersonationLevel)\n");
4099 
4100                 /* Fail if the token is not an impersonation token */
4101                 if (Token->TokenType != TokenImpersonation)
4102                 {
4103                     Status = STATUS_INVALID_INFO_CLASS;
4104                     break;
4105                 }
4106 
4107                 RequiredLength = sizeof(SECURITY_IMPERSONATION_LEVEL);
4108 
4109                 _SEH2_TRY
4110                 {
4111                     if (TokenInformationLength >= RequiredLength)
4112                     {
4113                         *sil = Token->ImpersonationLevel;
4114                     }
4115                     else
4116                     {
4117                         Status = STATUS_BUFFER_TOO_SMALL;
4118                     }
4119 
4120                     if (ReturnLength != NULL)
4121                     {
4122                         *ReturnLength = RequiredLength;
4123                     }
4124                 }
4125                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4126                 {
4127                     Status = _SEH2_GetExceptionCode();
4128                 }
4129                 _SEH2_END;
4130 
4131                 break;
4132             }
4133 
4134             case TokenStatistics:
4135             {
4136                 PTOKEN_STATISTICS ts = (PTOKEN_STATISTICS)TokenInformation;
4137 
4138                 DPRINT("NtQueryInformationToken(TokenStatistics)\n");
4139                 RequiredLength = sizeof(TOKEN_STATISTICS);
4140 
4141                 _SEH2_TRY
4142                 {
4143                     if (TokenInformationLength >= RequiredLength)
4144                     {
4145                         ts->TokenId = Token->TokenId;
4146                         ts->AuthenticationId = Token->AuthenticationId;
4147                         ts->ExpirationTime = Token->ExpirationTime;
4148                         ts->TokenType = Token->TokenType;
4149                         ts->ImpersonationLevel = Token->ImpersonationLevel;
4150                         ts->DynamicCharged = Token->DynamicCharged;
4151                         ts->DynamicAvailable = Token->DynamicAvailable;
4152                         ts->GroupCount = Token->UserAndGroupCount - 1;
4153                         ts->PrivilegeCount = Token->PrivilegeCount;
4154                         ts->ModifiedId = Token->ModifiedId;
4155                     }
4156                     else
4157                     {
4158                         Status = STATUS_BUFFER_TOO_SMALL;
4159                     }
4160 
4161                     if (ReturnLength != NULL)
4162                     {
4163                         *ReturnLength = RequiredLength;
4164                     }
4165                 }
4166                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4167                 {
4168                     Status = _SEH2_GetExceptionCode();
4169                 }
4170                 _SEH2_END;
4171 
4172                 break;
4173             }
4174 
4175             case TokenOrigin:
4176             {
4177                 PTOKEN_ORIGIN to = (PTOKEN_ORIGIN)TokenInformation;
4178 
4179                 DPRINT("NtQueryInformationToken(TokenOrigin)\n");
4180                 RequiredLength = sizeof(TOKEN_ORIGIN);
4181 
4182                 _SEH2_TRY
4183                 {
4184                     if (TokenInformationLength >= RequiredLength)
4185                     {
4186                         RtlCopyLuid(&to->OriginatingLogonSession,
4187                                     &Token->AuthenticationId);
4188                     }
4189                     else
4190                     {
4191                         Status = STATUS_BUFFER_TOO_SMALL;
4192                     }
4193 
4194                     if (ReturnLength != NULL)
4195                     {
4196                         *ReturnLength = RequiredLength;
4197                     }
4198                 }
4199                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4200                 {
4201                     Status = _SEH2_GetExceptionCode();
4202                 }
4203                 _SEH2_END;
4204 
4205                 break;
4206             }
4207 
4208             case TokenGroupsAndPrivileges:
4209                 DPRINT1("NtQueryInformationToken(TokenGroupsAndPrivileges) not implemented\n");
4210                 Status = STATUS_NOT_IMPLEMENTED;
4211                 break;
4212 
4213             case TokenRestrictedSids:
4214             {
4215                 PTOKEN_GROUPS tg = (PTOKEN_GROUPS)TokenInformation;
4216 
4217                 DPRINT("NtQueryInformationToken(TokenRestrictedSids)\n");
4218                 RequiredLength = sizeof(tg->GroupCount) +
4219                 RtlLengthSidAndAttributes(Token->RestrictedSidCount, Token->RestrictedSids);
4220 
4221                 _SEH2_TRY
4222                 {
4223                     if (TokenInformationLength >= RequiredLength)
4224                     {
4225                         ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) -
4226                             (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES));
4227                         PSID Sid = (PSID)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
4228                                           (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES)));
4229 
4230                         tg->GroupCount = Token->RestrictedSidCount;
4231                         Status = RtlCopySidAndAttributesArray(Token->RestrictedSidCount,
4232                                                               Token->RestrictedSids,
4233                                                               SidLen,
4234                                                               &tg->Groups[0],
4235                                                               Sid,
4236                                                               &Unused.PSid,
4237                                                               &Unused.Ulong);
4238                     }
4239                     else
4240                     {
4241                         Status = STATUS_BUFFER_TOO_SMALL;
4242                     }
4243 
4244                     if (ReturnLength != NULL)
4245                     {
4246                         *ReturnLength = RequiredLength;
4247                     }
4248                 }
4249                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4250                 {
4251                     Status = _SEH2_GetExceptionCode();
4252                 }
4253                 _SEH2_END;
4254 
4255                 break;
4256             }
4257 
4258             case TokenSandBoxInert:
4259                 DPRINT1("NtQueryInformationToken(TokenSandboxInert) not implemented\n");
4260                 Status = STATUS_NOT_IMPLEMENTED;
4261                 break;
4262 
4263             case TokenSessionId:
4264             {
4265                 ULONG SessionId = 0;
4266 
4267                 DPRINT("NtQueryInformationToken(TokenSessionId)\n");
4268 
4269                 Status = SeQuerySessionIdToken(Token, &SessionId);
4270                 if (NT_SUCCESS(Status))
4271                 {
4272                     _SEH2_TRY
4273                     {
4274                         /* Buffer size was already verified, no need to check here again */
4275                         *(PULONG)TokenInformation = SessionId;
4276 
4277                         if (ReturnLength != NULL)
4278                         {
4279                             *ReturnLength = sizeof(ULONG);
4280                         }
4281                     }
4282                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4283                     {
4284                         Status = _SEH2_GetExceptionCode();
4285                     }
4286                     _SEH2_END;
4287                 }
4288 
4289                 break;
4290             }
4291 
4292             default:
4293                 DPRINT1("NtQueryInformationToken(%d) invalid information class\n", TokenInformationClass);
4294                 Status = STATUS_INVALID_INFO_CLASS;
4295                 break;
4296         }
4297 
4298         /* Unlock and dereference the token */
4299         SepReleaseTokenLock(Token);
4300         ObDereferenceObject(Token);
4301     }
4302 
4303     return Status;
4304 }
4305 
4306 /**
4307  * @unimplemented
4308  * @brief
4309  * Sets (modifies) some specific information in regard of an access token. The
4310  * calling thread must have specific access rights in order to modify token's
4311  * information data.
4312  *
4313  * @param[in] TokenHandle
4314  * A handle of a token where information is to be modified.
4315  *
4316  * @param[in] TokenInformationClass
4317  * Token information class.
4318  *
4319  * @param[in] TokenInformation
4320  * An arbitrary pointer to a buffer with token information to set. Such
4321  * arbitrary buffer depends on the information class chosen that the caller
4322  * wants to modify such information data of a token.
4323  *
4324  * @param[in] TokenInformationLength
4325  * Length of the token information buffer, in bytes.
4326  *
4327  * @return
4328  * Returns STATUS_SUCCESS if information setting has completed successfully.
4329  * STATUS_INFO_LENGTH_MISMATCH is returned if the information length of the
4330  * buffer is less than the required length. STATUS_INSUFFICIENT_RESOURCES is
4331  * returned if memory pool allocation has failed. STATUS_PRIVILEGE_NOT_HELD
4332  * is returned if the calling thread hasn't the required privileges to perform
4333  * the operation in question. A failure NTSTATUS code is returned otherwise.
4334  *
4335  * @remarks
4336  * The function is partly implemented, mainly TokenOrigin and TokenDefaultDacl.
4337  */
4338 _Must_inspect_result_
4339 __kernel_entry
4340 NTSTATUS
4341 NTAPI
4342 NtSetInformationToken(
4343     _In_ HANDLE TokenHandle,
4344     _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
4345     _In_reads_bytes_(TokenInformationLength) PVOID TokenInformation,
4346     _In_ ULONG TokenInformationLength)
4347 {
4348     NTSTATUS Status;
4349     PTOKEN Token;
4350     KPROCESSOR_MODE PreviousMode;
4351     ULONG NeededAccess = TOKEN_ADJUST_DEFAULT;
4352 
4353     PAGED_CODE();
4354 
4355     PreviousMode = ExGetPreviousMode();
4356 
4357     Status = DefaultSetInfoBufferCheck(TokenInformationClass,
4358                                        SeTokenInformationClass,
4359                                        RTL_NUMBER_OF(SeTokenInformationClass),
4360                                        TokenInformation,
4361                                        TokenInformationLength,
4362                                        PreviousMode);
4363     if (!NT_SUCCESS(Status))
4364     {
4365         /* Invalid buffers */
4366         DPRINT("NtSetInformationToken() failed, Status: 0x%x\n", Status);
4367         return Status;
4368     }
4369 
4370     if (TokenInformationClass == TokenSessionId)
4371     {
4372         NeededAccess |= TOKEN_ADJUST_SESSIONID;
4373     }
4374 
4375     Status = ObReferenceObjectByHandle(TokenHandle,
4376                                        NeededAccess,
4377                                        SeTokenObjectType,
4378                                        PreviousMode,
4379                                        (PVOID*)&Token,
4380                                        NULL);
4381     if (NT_SUCCESS(Status))
4382     {
4383         switch (TokenInformationClass)
4384         {
4385             case TokenOwner:
4386             {
4387                 if (TokenInformationLength >= sizeof(TOKEN_OWNER))
4388                 {
4389                     PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation;
4390                     PSID InputSid = NULL, CapturedSid;
4391                     ULONG DefaultOwnerIndex;
4392 
4393                     _SEH2_TRY
4394                     {
4395                         InputSid = to->Owner;
4396                     }
4397                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4398                     {
4399                         Status = _SEH2_GetExceptionCode();
4400                         _SEH2_YIELD(goto Cleanup);
4401                     }
4402                     _SEH2_END;
4403 
4404                     Status = SepCaptureSid(InputSid,
4405                                            PreviousMode,
4406                                            PagedPool,
4407                                            FALSE,
4408                                            &CapturedSid);
4409                     if (NT_SUCCESS(Status))
4410                     {
4411                         /* Lock the token */
4412                         SepAcquireTokenLockExclusive(Token);
4413 
4414                         /* Find the owner amongst the existing token user and groups */
4415                         Status = SepFindPrimaryGroupAndDefaultOwner(Token,
4416                                                                     NULL,
4417                                                                     CapturedSid,
4418                                                                     NULL,
4419                                                                     &DefaultOwnerIndex);
4420                         if (NT_SUCCESS(Status))
4421                         {
4422                             /* Found it */
4423                             Token->DefaultOwnerIndex = DefaultOwnerIndex;
4424                             ExAllocateLocallyUniqueId(&Token->ModifiedId);
4425                         }
4426 
4427                         /* Unlock the token */
4428                         SepReleaseTokenLock(Token);
4429 
4430                         SepReleaseSid(CapturedSid,
4431                                       PreviousMode,
4432                                       FALSE);
4433                     }
4434                 }
4435                 else
4436                 {
4437                     Status = STATUS_INFO_LENGTH_MISMATCH;
4438                 }
4439                 break;
4440             }
4441 
4442             case TokenPrimaryGroup:
4443             {
4444                 if (TokenInformationLength >= sizeof(TOKEN_PRIMARY_GROUP))
4445                 {
4446                     PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation;
4447                     PSID InputSid = NULL, CapturedSid;
4448                     ULONG PrimaryGroupIndex;
4449 
4450                     _SEH2_TRY
4451                     {
4452                         InputSid = tpg->PrimaryGroup;
4453                     }
4454                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4455                     {
4456                         Status = _SEH2_GetExceptionCode();
4457                         _SEH2_YIELD(goto Cleanup);
4458                     }
4459                     _SEH2_END;
4460 
4461                     Status = SepCaptureSid(InputSid,
4462                                            PreviousMode,
4463                                            PagedPool,
4464                                            FALSE,
4465                                            &CapturedSid);
4466                     if (NT_SUCCESS(Status))
4467                     {
4468                         /* Lock the token */
4469                         SepAcquireTokenLockExclusive(Token);
4470 
4471                         /* Find the primary group amongst the existing token user and groups */
4472                         Status = SepFindPrimaryGroupAndDefaultOwner(Token,
4473                                                                     CapturedSid,
4474                                                                     NULL,
4475                                                                     &PrimaryGroupIndex,
4476                                                                     NULL);
4477                         if (NT_SUCCESS(Status))
4478                         {
4479                             /* Found it */
4480                             Token->PrimaryGroup = Token->UserAndGroups[PrimaryGroupIndex].Sid;
4481                             ExAllocateLocallyUniqueId(&Token->ModifiedId);
4482                         }
4483 
4484                         /* Unlock the token */
4485                         SepReleaseTokenLock(Token);
4486 
4487                         SepReleaseSid(CapturedSid,
4488                                       PreviousMode,
4489                                       FALSE);
4490                     }
4491                 }
4492                 else
4493                 {
4494                     Status = STATUS_INFO_LENGTH_MISMATCH;
4495                 }
4496                 break;
4497             }
4498 
4499             case TokenDefaultDacl:
4500             {
4501                 if (TokenInformationLength >= sizeof(TOKEN_DEFAULT_DACL))
4502                 {
4503                     PTOKEN_DEFAULT_DACL tdd = (PTOKEN_DEFAULT_DACL)TokenInformation;
4504                     PACL InputAcl = NULL;
4505 
4506                     _SEH2_TRY
4507                     {
4508                         InputAcl = tdd->DefaultDacl;
4509                     }
4510                     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4511                     {
4512                         Status = _SEH2_GetExceptionCode();
4513                         _SEH2_YIELD(goto Cleanup);
4514                     }
4515                     _SEH2_END;
4516 
4517                     if (InputAcl != NULL)
4518                     {
4519                         PACL CapturedAcl;
4520 
4521                         /* Capture and copy the dacl */
4522                         Status = SepCaptureAcl(InputAcl,
4523                                                PreviousMode,
4524                                                PagedPool,
4525                                                TRUE,
4526                                                &CapturedAcl);
4527                         if (NT_SUCCESS(Status))
4528                         {
4529                             ULONG DynamicLength;
4530 
4531                             /* Lock the token */
4532                             SepAcquireTokenLockExclusive(Token);
4533 
4534                             //
4535                             // NOTE: So far our dynamic area only contains
4536                             // the default dacl, so this makes the following
4537                             // code pretty simple. The day where it stores
4538                             // other data, the code will require adaptations.
4539                             //
4540 
4541                             DynamicLength = Token->DynamicAvailable;
4542                             // Add here any other data length present in the dynamic area...
4543                             if (Token->DefaultDacl)
4544                                 DynamicLength += Token->DefaultDacl->AclSize;
4545 
4546                             /* Reallocate the dynamic area if it is too small */
4547                             Status = STATUS_SUCCESS;
4548                             if ((DynamicLength < CapturedAcl->AclSize) ||
4549                                 (Token->DynamicPart == NULL))
4550                             {
4551                                 PVOID NewDynamicPart;
4552 
4553                                 NewDynamicPart = ExAllocatePoolWithTag(PagedPool,
4554                                                                        CapturedAcl->AclSize,
4555                                                                        TAG_TOKEN_DYNAMIC);
4556                                 if (NewDynamicPart == NULL)
4557                                 {
4558                                     Status = STATUS_INSUFFICIENT_RESOURCES;
4559                                 }
4560                                 else
4561                                 {
4562                                     if (Token->DynamicPart != NULL)
4563                                     {
4564                                         // RtlCopyMemory(NewDynamicPart, Token->DynamicPart, DynamicLength);
4565                                         ExFreePoolWithTag(Token->DynamicPart, TAG_TOKEN_DYNAMIC);
4566                                     }
4567                                     Token->DynamicPart = NewDynamicPart;
4568                                     Token->DynamicAvailable = 0;
4569                                 }
4570                             }
4571                             else
4572                             {
4573                                 Token->DynamicAvailable = DynamicLength - CapturedAcl->AclSize;
4574                             }
4575 
4576                             if (NT_SUCCESS(Status))
4577                             {
4578                                 /* Set the new dacl */
4579                                 Token->DefaultDacl = (PVOID)Token->DynamicPart;
4580                                 RtlCopyMemory(Token->DefaultDacl,
4581                                               CapturedAcl,
4582                                               CapturedAcl->AclSize);
4583 
4584                                 ExAllocateLocallyUniqueId(&Token->ModifiedId);
4585                             }
4586 
4587                             /* Unlock the token */
4588                             SepReleaseTokenLock(Token);
4589 
4590                             ExFreePoolWithTag(CapturedAcl, TAG_ACL);
4591                         }
4592                     }
4593                     else
4594                     {
4595                         /* Lock the token */
4596                         SepAcquireTokenLockExclusive(Token);
4597 
4598                         /* Clear the default dacl if present */
4599                         if (Token->DefaultDacl != NULL)
4600                         {
4601                             Token->DynamicAvailable += Token->DefaultDacl->AclSize;
4602                             RtlZeroMemory(Token->DefaultDacl, Token->DefaultDacl->AclSize);
4603                             Token->DefaultDacl = NULL;
4604 
4605                             ExAllocateLocallyUniqueId(&Token->ModifiedId);
4606                         }
4607 
4608                         /* Unlock the token */
4609                         SepReleaseTokenLock(Token);
4610                     }
4611                 }
4612                 else
4613                 {
4614                     Status = STATUS_INFO_LENGTH_MISMATCH;
4615                 }
4616                 break;
4617             }
4618 
4619             case TokenSessionId:
4620             {
4621                 ULONG SessionId = 0;
4622 
4623                 _SEH2_TRY
4624                 {
4625                     /* Buffer size was already verified, no need to check here again */
4626                     SessionId = *(PULONG)TokenInformation;
4627                 }
4628                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4629                 {
4630                     Status = _SEH2_GetExceptionCode();
4631                     _SEH2_YIELD(goto Cleanup);
4632                 }
4633                 _SEH2_END;
4634 
4635                 /* Check for TCB privilege */
4636                 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
4637                 {
4638                     Status = STATUS_PRIVILEGE_NOT_HELD;
4639                     break;
4640                 }
4641 
4642                 /* Lock the token */
4643                 SepAcquireTokenLockExclusive(Token);
4644 
4645                 Token->SessionId = SessionId;
4646                 ExAllocateLocallyUniqueId(&Token->ModifiedId);
4647 
4648                 /* Unlock the token */
4649                 SepReleaseTokenLock(Token);
4650 
4651                 break;
4652             }
4653 
4654             case TokenSessionReference:
4655             {
4656                 ULONG SessionReference;
4657 
4658                 _SEH2_TRY
4659                 {
4660                     /* Buffer size was already verified, no need to check here again */
4661                     SessionReference = *(PULONG)TokenInformation;
4662                 }
4663                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4664                 {
4665                     Status = _SEH2_GetExceptionCode();
4666                     _SEH2_YIELD(goto Cleanup);
4667                 }
4668                 _SEH2_END;
4669 
4670                 /* Check for TCB privilege */
4671                 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
4672                 {
4673                     Status = STATUS_PRIVILEGE_NOT_HELD;
4674                     goto Cleanup;
4675                 }
4676 
4677                 /* Check if it is 0 */
4678                 if (SessionReference == 0)
4679                 {
4680                     ULONG OldTokenFlags;
4681 
4682                     /* Lock the token */
4683                     SepAcquireTokenLockExclusive(Token);
4684 
4685                     /* Atomically set the flag in the token */
4686                     OldTokenFlags = RtlInterlockedSetBits(&Token->TokenFlags,
4687                                                           TOKEN_SESSION_NOT_REFERENCED);
4688                     /*
4689                      * If the flag was already set, do not dereference again
4690                      * the logon session. Use SessionReference as an indicator
4691                      * to know whether to really dereference the session.
4692                      */
4693                     if (OldTokenFlags == Token->TokenFlags)
4694                         SessionReference = ULONG_MAX;
4695 
4696                     /*
4697                      * Otherwise if the flag was never set but just for this first time then
4698                      * remove the referenced logon session data from the token and dereference
4699                      * the logon session when needed.
4700                      */
4701                     if (SessionReference == 0)
4702                     {
4703                         SepRmRemoveLogonSessionFromToken(Token);
4704                         SepRmDereferenceLogonSession(&Token->AuthenticationId);
4705                     }
4706 
4707                     /* Unlock the token */
4708                     SepReleaseTokenLock(Token);
4709                 }
4710                 break;
4711             }
4712 
4713             case TokenAuditPolicy:
4714             {
4715                 PTOKEN_AUDIT_POLICY_INFORMATION PolicyInformation =
4716                     (PTOKEN_AUDIT_POLICY_INFORMATION)TokenInformation;
4717                 SEP_AUDIT_POLICY AuditPolicy;
4718                 ULONG i;
4719 
4720                 _SEH2_TRY
4721                 {
4722                     ProbeForRead(PolicyInformation,
4723                                  FIELD_OFFSET(TOKEN_AUDIT_POLICY_INFORMATION,
4724                                               Policies[PolicyInformation->PolicyCount]),
4725                                  sizeof(ULONG));
4726 
4727                     /* Loop all policies in the structure */
4728                     for (i = 0; i < PolicyInformation->PolicyCount; i++)
4729                     {
4730                         /* Set the corresponding bits in the packed structure */
4731                         switch (PolicyInformation->Policies[i].Category)
4732                         {
4733                             case AuditCategorySystem:
4734                                 AuditPolicy.PolicyElements.System = PolicyInformation->Policies[i].Value;
4735                                 break;
4736 
4737                             case AuditCategoryLogon:
4738                                 AuditPolicy.PolicyElements.Logon = PolicyInformation->Policies[i].Value;
4739                                 break;
4740 
4741                             case AuditCategoryObjectAccess:
4742                                 AuditPolicy.PolicyElements.ObjectAccess = PolicyInformation->Policies[i].Value;
4743                                 break;
4744 
4745                             case AuditCategoryPrivilegeUse:
4746                                 AuditPolicy.PolicyElements.PrivilegeUse = PolicyInformation->Policies[i].Value;
4747                                 break;
4748 
4749                             case AuditCategoryDetailedTracking:
4750                                 AuditPolicy.PolicyElements.DetailedTracking = PolicyInformation->Policies[i].Value;
4751                                 break;
4752 
4753                             case AuditCategoryPolicyChange:
4754                                 AuditPolicy.PolicyElements.PolicyChange = PolicyInformation->Policies[i].Value;
4755                                 break;
4756 
4757                             case AuditCategoryAccountManagement:
4758                                 AuditPolicy.PolicyElements.AccountManagement = PolicyInformation->Policies[i].Value;
4759                                 break;
4760 
4761                             case AuditCategoryDirectoryServiceAccess:
4762                                 AuditPolicy.PolicyElements.DirectoryServiceAccess = PolicyInformation->Policies[i].Value;
4763                                 break;
4764 
4765                             case AuditCategoryAccountLogon:
4766                                 AuditPolicy.PolicyElements.AccountLogon = PolicyInformation->Policies[i].Value;
4767                                 break;
4768                         }
4769                     }
4770                 }
4771                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4772                 {
4773                     Status = _SEH2_GetExceptionCode();
4774                     _SEH2_YIELD(goto Cleanup);
4775                 }
4776                 _SEH2_END;
4777 
4778                 /* Check for TCB privilege */
4779                 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
4780                 {
4781                     Status = STATUS_PRIVILEGE_NOT_HELD;
4782                     break;
4783                 }
4784 
4785                 /* Lock the token */
4786                 SepAcquireTokenLockExclusive(Token);
4787 
4788                 /* Set the new audit policy */
4789                 Token->AuditPolicy = AuditPolicy;
4790                 ExAllocateLocallyUniqueId(&Token->ModifiedId);
4791 
4792                 /* Unlock the token */
4793                 SepReleaseTokenLock(Token);
4794 
4795                 break;
4796             }
4797 
4798             case TokenOrigin:
4799             {
4800                 TOKEN_ORIGIN TokenOrigin;
4801 
4802                 _SEH2_TRY
4803                 {
4804                     /* Copy the token origin */
4805                     TokenOrigin = *(PTOKEN_ORIGIN)TokenInformation;
4806                 }
4807                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4808                 {
4809                     Status = _SEH2_GetExceptionCode();
4810                     _SEH2_YIELD(goto Cleanup);
4811                 }
4812                 _SEH2_END;
4813 
4814                 /* Check for TCB privilege */
4815                 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
4816                 {
4817                     Status = STATUS_PRIVILEGE_NOT_HELD;
4818                     break;
4819                 }
4820 
4821                 /* Lock the token */
4822                 SepAcquireTokenLockExclusive(Token);
4823 
4824                 /* Check if there is no token origin set yet */
4825                 if (RtlIsZeroLuid(&Token->OriginatingLogonSession))
4826                 {
4827                     /* Set the token origin */
4828                     Token->OriginatingLogonSession =
4829                         TokenOrigin.OriginatingLogonSession;
4830 
4831                     ExAllocateLocallyUniqueId(&Token->ModifiedId);
4832                 }
4833 
4834                 /* Unlock the token */
4835                 SepReleaseTokenLock(Token);
4836 
4837                 break;
4838             }
4839 
4840             default:
4841             {
4842                 DPRINT1("Invalid TokenInformationClass: 0x%lx\n",
4843                         TokenInformationClass);
4844                 Status = STATUS_INVALID_INFO_CLASS;
4845                 break;
4846             }
4847         }
4848 Cleanup:
4849         ObDereferenceObject(Token);
4850     }
4851 
4852     if (!NT_SUCCESS(Status))
4853     {
4854         DPRINT1("NtSetInformationToken failed with Status 0x%lx\n", Status);
4855     }
4856 
4857     return Status;
4858 }
4859 
4860 /**
4861  * @brief
4862  * Duplicates a token.
4863  *
4864  * @param[in] ExistingTokenHandle
4865  * An existing token to duplicate.
4866  *
4867  * @param[in] DesiredAccess
4868  * The desired access rights for the new duplicated token.
4869  *
4870  * @param[in] ObjectAttributes
4871  * Object attributes for the new duplicated token.
4872  *
4873  * @param[in] EffectiveOnly
4874  * If set to TRUE, the function removes all the disabled privileges and groups
4875  * of the token to duplicate.
4876  *
4877  * @param[in] TokenType
4878  * Type of token to assign to the duplicated token.
4879  *
4880  * @param[out] NewTokenHandle
4881  * The returned duplicated token handle.
4882  *
4883  * @return
4884  * STATUS_SUCCESS is returned if token duplication has completed successfully.
4885  * STATUS_BAD_IMPERSONATION_LEVEL is returned if the caller erroneously wants
4886  * to raise the impersonation level even though the conditions do not permit
4887  * it. A failure NTSTATUS code is returned otherwise.
4888  *
4889  * @remarks
4890  * Some sources claim 4th param is ImpersonationLevel, but on W2K
4891  * this is certainly NOT true, although I can't say for sure that EffectiveOnly
4892  * is correct either. -Gunnar
4893  * This is true. EffectiveOnly overrides SQOS.EffectiveOnly. - IAI
4894  * NOTE for readers: http://hex.pp.ua/nt/NtDuplicateToken.php is therefore
4895  * wrong in that regard, while MSDN documentation is correct.
4896  */
4897 _Must_inspect_result_
4898 __kernel_entry
4899 NTSTATUS
4900 NTAPI
4901 NtDuplicateToken(
4902     _In_ HANDLE ExistingTokenHandle,
4903     _In_ ACCESS_MASK DesiredAccess,
4904     _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
4905     _In_ BOOLEAN EffectiveOnly,
4906     _In_ TOKEN_TYPE TokenType,
4907     _Out_ PHANDLE NewTokenHandle)
4908 {
4909     KPROCESSOR_MODE PreviousMode;
4910     HANDLE hToken;
4911     PTOKEN Token;
4912     PTOKEN NewToken;
4913     PSECURITY_QUALITY_OF_SERVICE CapturedSecurityQualityOfService;
4914     BOOLEAN QoSPresent;
4915     OBJECT_HANDLE_INFORMATION HandleInformation;
4916     NTSTATUS Status;
4917 
4918     PAGED_CODE();
4919 
4920     if (TokenType != TokenImpersonation &&
4921         TokenType != TokenPrimary)
4922     {
4923         return STATUS_INVALID_PARAMETER;
4924     }
4925 
4926     PreviousMode = KeGetPreviousMode();
4927 
4928     if (PreviousMode != KernelMode)
4929     {
4930         _SEH2_TRY
4931         {
4932             ProbeForWriteHandle(NewTokenHandle);
4933         }
4934         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4935         {
4936             /* Return the exception code */
4937             _SEH2_YIELD(return _SEH2_GetExceptionCode());
4938         }
4939         _SEH2_END;
4940     }
4941 
4942     Status = SepCaptureSecurityQualityOfService(ObjectAttributes,
4943                                                 PreviousMode,
4944                                                 PagedPool,
4945                                                 FALSE,
4946                                                 &CapturedSecurityQualityOfService,
4947                                                 &QoSPresent);
4948     if (!NT_SUCCESS(Status))
4949     {
4950         DPRINT1("NtDuplicateToken() failed to capture QoS! Status: 0x%x\n", Status);
4951         return Status;
4952     }
4953 
4954     Status = ObReferenceObjectByHandle(ExistingTokenHandle,
4955                                        TOKEN_DUPLICATE,
4956                                        SeTokenObjectType,
4957                                        PreviousMode,
4958                                        (PVOID*)&Token,
4959                                        &HandleInformation);
4960     if (!NT_SUCCESS(Status))
4961     {
4962         DPRINT1("Failed to reference token (Status 0x%lx)\n", Status);
4963         SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
4964                                            PreviousMode,
4965                                            FALSE);
4966         return Status;
4967     }
4968 
4969     /*
4970      * Fail, if the original token is an impersonation token and the caller
4971      * tries to raise the impersonation level of the new token above the
4972      * impersonation level of the original token.
4973      */
4974     if (Token->TokenType == TokenImpersonation)
4975     {
4976         if (QoSPresent &&
4977             CapturedSecurityQualityOfService->ImpersonationLevel >Token->ImpersonationLevel)
4978         {
4979             ObDereferenceObject(Token);
4980             SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
4981                                                PreviousMode,
4982                                                FALSE);
4983             return STATUS_BAD_IMPERSONATION_LEVEL;
4984         }
4985     }
4986 
4987     /*
4988      * Fail, if a primary token is to be created from an impersonation token
4989      * and and the impersonation level of the impersonation token is below SecurityImpersonation.
4990      */
4991     if (Token->TokenType == TokenImpersonation &&
4992         TokenType == TokenPrimary &&
4993         Token->ImpersonationLevel < SecurityImpersonation)
4994     {
4995         ObDereferenceObject(Token);
4996         SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
4997                                            PreviousMode,
4998                                            FALSE);
4999         return STATUS_BAD_IMPERSONATION_LEVEL;
5000     }
5001 
5002     Status = SepDuplicateToken(Token,
5003                                ObjectAttributes,
5004                                EffectiveOnly,
5005                                TokenType,
5006                                (QoSPresent ? CapturedSecurityQualityOfService->ImpersonationLevel : SecurityAnonymous),
5007                                PreviousMode,
5008                                &NewToken);
5009 
5010     ObDereferenceObject(Token);
5011 
5012     if (NT_SUCCESS(Status))
5013     {
5014         Status = ObInsertObject(NewToken,
5015                                 NULL,
5016                                 (DesiredAccess ? DesiredAccess : HandleInformation.GrantedAccess),
5017                                 0,
5018                                 NULL,
5019                                 &hToken);
5020         if (NT_SUCCESS(Status))
5021         {
5022             _SEH2_TRY
5023             {
5024                 *NewTokenHandle = hToken;
5025             }
5026             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5027             {
5028                 Status = _SEH2_GetExceptionCode();
5029             }
5030             _SEH2_END;
5031         }
5032     }
5033 
5034     /* Free the captured structure */
5035     SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
5036                                        PreviousMode,
5037                                        FALSE);
5038 
5039     return Status;
5040 }
5041 
5042 /**
5043  * @brief
5044  * Private routine that iterates over the groups of an
5045  * access token to be adjusted as per on request by the
5046  * caller, where a group can be enabled or disabled.
5047  *
5048  * @param[in] Token
5049  * Access token where its groups are to be enabled or disabled.
5050  *
5051  * @param[in] NewState
5052  * A list of groups with new state attributes to be assigned to
5053  * the token.
5054  *
5055  * @param[in] NewStateCount
5056  * The captured count number of groups in the list.
5057  *
5058  * @param[in] ApplyChanges
5059  * If set to FALSE, the function will only iterate over the token's
5060  * groups without performing any kind of modification. If set to TRUE,
5061  * the changes will be applied immediately when the function has done
5062  * looping the groups.
5063  *
5064  * @param[in] ResetToDefaultStates
5065  * The function will reset the groups in an access token to default
5066  * states if set to TRUE. In such scenario the function ignores
5067  * NewState outright. Otherwise if set to FALSE, the function will
5068  * use NewState to assign the newly attributes to adjust the token's
5069  * groups. SE_GROUP_ENABLED_BY_DEFAULT is a flag indicator that is used
5070  * for such purpose.
5071  *
5072  * @param[out] ChangesMade
5073  * Returns TRUE if changes to token's groups have been made, otherwise
5074  * FALSE is returned. Bear in mind such changes aren't always deterministic.
5075  * See remarks for further details.
5076  *
5077  * @param[out] PreviousGroupsState
5078  * If requested by the caller, the function will return the previous state
5079  * of groups in an access token prior taking action on adjusting the token.
5080  * This is a UM (user mode) pointer and it's prone to raise exceptions
5081  * if such pointer address is not valid.
5082  *
5083  * @param[out] ChangedGroups
5084  * Returns the total number of changed groups in an access token. This
5085  * argument could also indicate the number of groups to be changed if
5086  * the calling thread hasn't chosen to apply the changes yet. A number
5087  * of 0 indicates no groups have been or to be changed because the groups'
5088  * attributes in a token are the same as the ones from NewState given by
5089  * the caller.
5090  *
5091  * @return
5092  * STATUS_SUCCESS is returned if the function has successfully completed
5093  * the operation of adjusting groups in a token. STATUS_CANT_DISABLE_MANDATORY
5094  * is returned if there was an attempt to disable a mandatory group which is
5095  * not possible. STATUS_CANT_ENABLE_DENY_ONLY is returned if there was an attempt
5096  * to enable a "use for Deny only" group which is not allowed, that is, a restricted
5097  * group. STATUS_NOT_ALL_ASSIGNED is returned if not all the groups are actually
5098  * assigned to the token.
5099  *
5100  * @remarks
5101  * Token groups adjusting can be judged to be deterministic or not based on the
5102  * NT status code value. That is, STATUS_SUCCESS indicates the function not only
5103  * has iterated over the whole groups in a token, it also has applied the changes
5104  * thoroughly without impediment and the results perfectly match with the request
5105  * desired by the caller. In this situation the condition is deemed deterministic.
5106  * In a different situation however, if the status code was STATUS_NOT_ALL_ASSIGNED,
5107  * the function would still continue looping the groups in a token and apply the
5108  * changes whenever possible where the respective groups actually exist in the
5109  * token. This kind of situation is deemed as indeterministic.
5110  * For STATUS_CANT_DISABLE_MANDATORY and STATUS_CANT_ENABLE_DENY_ONLY the scenario
5111  * is even more indeterministic as the iteration of groups comes to a halt thus
5112  * leaving all other possible groups to be adjusted.
5113  */
5114 static
5115 NTSTATUS
5116 SepAdjustGroups(
5117     _In_ PTOKEN Token,
5118     _In_opt_ PSID_AND_ATTRIBUTES NewState,
5119     _In_ ULONG NewStateCount,
5120     _In_ BOOLEAN ApplyChanges,
5121     _In_ BOOLEAN ResetToDefaultStates,
5122     _Out_ PBOOLEAN ChangesMade,
5123     _Out_opt_ PTOKEN_GROUPS PreviousGroupsState,
5124     _Out_ PULONG ChangedGroups)
5125 {
5126     ULONG GroupsInToken, GroupsInList;
5127     ULONG ChangeCount, GroupsCount, NewAttributes;
5128 
5129     PAGED_CODE();
5130 
5131     /* Ensure that the token we get is not plain garbage */
5132     ASSERT(Token);
5133 
5134     /* Initialize the counters and begin the work */
5135     *ChangesMade = FALSE;
5136     GroupsCount = 0;
5137     ChangeCount = 0;
5138 
5139     /* Begin looping all the groups in the token */
5140     for (GroupsInToken = 0; GroupsInToken < Token->UserAndGroupCount; GroupsInToken++)
5141     {
5142         /* Does the caller want to reset groups to default states? */
5143         if (ResetToDefaultStates)
5144         {
5145             /*
5146              * SE_GROUP_ENABLED_BY_DEFAULT is a special indicator that informs us
5147              * if a certain group has been enabled by default or not. In case
5148              * a group is enabled by default but it is not currently enabled then
5149              * at that point we must enable it back by default. For now just
5150              * assign the respective SE_GROUP_ENABLED attribute as we'll do the
5151              * eventual work later.
5152              */
5153             if ((Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_ENABLED_BY_DEFAULT) &&
5154                 (Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_ENABLED) == 0)
5155             {
5156                 NewAttributes = Token->UserAndGroups[GroupsInToken].Attributes |= SE_GROUP_ENABLED;
5157             }
5158 
5159             /*
5160              * Unlike the case above, a group that hasn't been enabled by
5161              * default but it's currently enabled then we must disable
5162              * it back.
5163              */
5164             if ((Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_ENABLED_BY_DEFAULT) == 0 &&
5165                 (Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_ENABLED))
5166             {
5167                 NewAttributes = Token->UserAndGroups[GroupsInToken].Attributes & ~SE_GROUP_ENABLED;
5168             }
5169         }
5170         else
5171         {
5172             /* Loop the provided groups in the list then */
5173             for (GroupsInList = 0; GroupsInList < NewStateCount; GroupsInList++)
5174             {
5175                 /* Does this group exist in the token? */
5176                 if (RtlEqualSid(&Token->UserAndGroups[GroupsInToken].Sid,
5177                                 &NewState[GroupsInList].Sid))
5178                 {
5179                     /*
5180                      * This is the group that we're looking for.
5181                      * However, it could be that the group is a
5182                      * mandatory group which we are not allowed
5183                      * and cannot disable it.
5184                      */
5185                     if ((Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_MANDATORY) &&
5186                         (NewState[GroupsInList].Attributes & SE_GROUP_ENABLED) == 0)
5187                     {
5188                         /* It is mandatory, forget about this group */
5189                         DPRINT1("SepAdjustGroups(): The SID group is mandatory!\n");
5190                         return STATUS_CANT_DISABLE_MANDATORY;
5191                     }
5192 
5193                     /*
5194                      * We've to ensure that apart the group mustn't be
5195                      * mandatory, it mustn't be a restricted group as
5196                      * well. That is, the group is marked with
5197                      * SE_GROUP_USE_FOR_DENY_ONLY flag and no one
5198                      * can enable it because it's for "deny" use only.
5199                      */
5200                     if ((Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_USE_FOR_DENY_ONLY) &&
5201                         (NewState[GroupsInList].Attributes & SE_GROUP_ENABLED))
5202                     {
5203                         /* This group is restricted, forget about it */
5204                         DPRINT1("SepAdjustGroups(): The SID group is for use deny only!\n");
5205                         return STATUS_CANT_ENABLE_DENY_ONLY;
5206                     }
5207 
5208                     /* Copy the attributes and stop searching */
5209                     NewAttributes = NewState[GroupsInList].Attributes;
5210                     NewAttributes &= SE_GROUP_ENABLED;
5211                     NewAttributes = Token->UserAndGroups[GroupsInToken].Attributes & ~SE_GROUP_ENABLED;
5212                     break;
5213                 }
5214 
5215                 /* Did we find the specific group we wanted? */
5216                 if (GroupsInList == NewStateCount)
5217                 {
5218                     /* We didn't, continue with the next token's group */
5219                     continue;
5220                 }
5221             }
5222 
5223             /* Count the group that we found it */
5224             GroupsCount++;
5225 
5226             /* Does the token have the same attributes as the caller requested them? */
5227             if (Token->UserAndGroups[GroupsInToken].Attributes != NewAttributes)
5228             {
5229                 /*
5230                  * No, then it's time to make some adjustment to the
5231                  * token's groups. Does the caller want the previous states
5232                  * of groups?
5233                  */
5234                 if (PreviousGroupsState != NULL)
5235                 {
5236                     PreviousGroupsState->Groups[ChangeCount] = Token->UserAndGroups[GroupsInToken];
5237                 }
5238 
5239                 /* Time to apply the changes now? */
5240                 if (ApplyChanges)
5241                 {
5242                     /* The caller gave us consent, apply and report that we made changes! */
5243                     Token->UserAndGroups[GroupsInToken].Attributes = NewAttributes;
5244                     *ChangesMade = TRUE;
5245                 }
5246 
5247                 /* Increment the count change */
5248                 ChangeCount++;
5249             }
5250         }
5251     }
5252 
5253     /* Report the number of previous saved groups */
5254     if (PreviousGroupsState != NULL)
5255     {
5256         PreviousGroupsState->GroupCount = ChangeCount;
5257     }
5258 
5259     /* Report the number of changed groups */
5260     *ChangedGroups = ChangeCount;
5261 
5262     /* Did we miss some groups? */
5263     if (!ResetToDefaultStates && (GroupsCount < NewStateCount))
5264     {
5265         /*
5266          * If we're at this stage then we are in a situation
5267          * where the adjust changes done to token's groups is
5268          * not deterministic as the caller might have wanted
5269          * as per NewState parameter.
5270          */
5271         DPRINT1("SepAdjustGroups(): The token hasn't all the groups assigned!\n");
5272         return STATUS_NOT_ALL_ASSIGNED;
5273     }
5274 
5275     return STATUS_SUCCESS;
5276 }
5277 
5278 /**
5279  * @brief
5280  * Changes the list of groups by enabling or disabling them
5281  * in an access token. Unlike NtAdjustPrivilegesToken,
5282  * this API routine does not remove groups.
5283  *
5284  * @param[in] TokenHandle
5285  * Token handle where the list of groups SID are to be adjusted.
5286  * The access token must have TOKEN_ADJUST_GROUPS access right
5287  * in order to change the groups in a token. The token must also
5288  * have TOKEN_QUERY access right if the caller requests the previous
5289  * states of groups list, that is, PreviousState is not NULL.
5290  *
5291  * @param[in] ResetToDefault
5292  * If set to TRUE, the function resets the list of groups to default
5293  * enabled and disabled states. NewState is ignored in this case.
5294  * Otherwise if the parameter is set to FALSE, the function expects
5295  * a new list of groups from NewState to be adjusted within the token.
5296  *
5297  * @param[in] NewState
5298  * A new list of groups SID that the function will use it accordingly to
5299  * modify the current list of groups SID of a token.
5300  *
5301  * @param[in] BufferLength
5302  * The length size of the buffer that is pointed by the NewState parameter
5303  * argument, in bytes.
5304  *
5305  * @param[out] PreviousState
5306  * If specified, the function will return to the caller the old list of groups
5307  * SID. If this parameter is NULL, ReturnLength must also be NULL.
5308  *
5309  * @param[out] ReturnLength
5310  * If specified, the function will return the total size length of the old list
5311  * of groups SIDs, in bytes.
5312  *
5313  * @return
5314  * STATUS_SUCCESS is returned if the function has successfully adjusted the
5315  * token's groups. STATUS_INVALID_PARAMETER is returned if the caller has
5316  * submitted one or more invalid parameters, that is, the caller didn't want
5317  * to reset the groups to default state but no NewState argument list has been
5318  * provided. STATUS_BUFFER_TOO_SMALL is returned if the buffer length given
5319  * by the caller is smaller than the required length size. A failure NTSTATUS
5320  * code is returned otherwise.
5321  */
5322 NTSTATUS
5323 NTAPI
5324 NtAdjustGroupsToken(
5325     _In_ HANDLE TokenHandle,
5326     _In_ BOOLEAN ResetToDefault,
5327     _In_ PTOKEN_GROUPS NewState,
5328     _In_ ULONG BufferLength,
5329     _Out_writes_bytes_to_opt_(BufferLength, *ReturnLength)
5330     PTOKEN_GROUPS PreviousState,
5331     _When_(PreviousState != NULL, _Out_) PULONG ReturnLength)
5332 {
5333     PTOKEN Token;
5334     NTSTATUS Status;
5335     KPROCESSOR_MODE PreviousMode;
5336     ULONG ChangeCount, RequiredLength;
5337     ULONG CapturedCount = 0;
5338     ULONG CapturedLength = 0;
5339     ULONG NewStateSize = 0;
5340     PSID_AND_ATTRIBUTES CapturedGroups = NULL;
5341     BOOLEAN ChangesMade = FALSE;
5342     BOOLEAN LockAndReferenceAcquired = FALSE;
5343 
5344     PAGED_CODE();
5345 
5346     /*
5347      * If the caller doesn't want to reset the groups of an
5348      * access token to default states then at least we must
5349      * expect a list of groups to be adjusted based on NewState
5350      * parameter. Otherwise bail out because the caller has
5351      * no idea what they're doing.
5352      */
5353     if (!ResetToDefault && !NewState)
5354     {
5355         DPRINT1("NtAdjustGroupsToken(): The caller hasn't provided any list of groups to adjust!\n");
5356         return STATUS_INVALID_PARAMETER;
5357     }
5358 
5359     PreviousMode = ExGetPreviousMode();
5360 
5361     if (PreviousMode != KernelMode)
5362     {
5363         _SEH2_TRY
5364         {
5365             /* Probe NewState */
5366             if (!ResetToDefault)
5367             {
5368                 /* Probe the header */
5369                 ProbeForRead(NewState, sizeof(*NewState), sizeof(ULONG));
5370 
5371                 CapturedCount = NewState->GroupCount;
5372                 NewStateSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedCount]);
5373 
5374                 ProbeForRead(NewState, NewStateSize, sizeof(ULONG));
5375             }
5376 
5377             if (PreviousState != NULL)
5378             {
5379                 ProbeForWrite(PreviousState, BufferLength, sizeof(ULONG));
5380                 ProbeForWrite(ReturnLength, sizeof(*ReturnLength), sizeof(ULONG));
5381             }
5382         }
5383         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5384         {
5385             /* Return the exception code */
5386             _SEH2_YIELD(return _SEH2_GetExceptionCode());
5387         }
5388         _SEH2_END;
5389     }
5390     else
5391     {
5392         /*
5393          * We're calling directly from the kernel, just retrieve
5394          * the number count of captured groups outright.
5395          */
5396         if (!ResetToDefault)
5397         {
5398             CapturedCount = NewState->GroupCount;
5399         }
5400     }
5401 
5402     /* Time to capture the NewState list */
5403     if (!ResetToDefault)
5404     {
5405         _SEH2_TRY
5406         {
5407             Status = SeCaptureSidAndAttributesArray(NewState->Groups,
5408                                                     CapturedCount,
5409                                                     PreviousMode,
5410                                                     NULL,
5411                                                     0,
5412                                                     PagedPool,
5413                                                     TRUE,
5414                                                     &CapturedGroups,
5415                                                     &CapturedLength);
5416         }
5417         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5418         {
5419             Status = _SEH2_GetExceptionCode();
5420         }
5421         _SEH2_END;
5422 
5423         if (!NT_SUCCESS(Status))
5424         {
5425             DPRINT1("NtAdjustGroupsToken(): Failed to capture the NewState list of groups (Status 0x%lx)\n", Status);
5426             return Status;
5427         }
5428     }
5429 
5430     /* Time to reference the token */
5431     Status = ObReferenceObjectByHandle(TokenHandle,
5432                                        TOKEN_ADJUST_GROUPS | (PreviousState != NULL ? TOKEN_QUERY : 0),
5433                                        SeTokenObjectType,
5434                                        PreviousMode,
5435                                        (PVOID*)&Token,
5436                                        NULL);
5437     if (!NT_SUCCESS(Status))
5438     {
5439         /* We couldn't reference the access token, bail out */
5440         DPRINT1("NtAdjustGroupsToken(): Failed to reference the token (Status 0x%lx)\n", Status);
5441 
5442         if (CapturedGroups != NULL)
5443         {
5444             SeReleaseSidAndAttributesArray(CapturedGroups,
5445                                            PreviousMode,
5446                                            TRUE);
5447         }
5448 
5449         goto Quit;
5450     }
5451 
5452     /* Lock the token */
5453     SepAcquireTokenLockExclusive(Token);
5454     LockAndReferenceAcquired = TRUE;
5455 
5456     /* Count the number of groups to be changed */
5457     Status = SepAdjustGroups(Token,
5458                              CapturedGroups,
5459                              CapturedCount,
5460                              FALSE,
5461                              ResetToDefault,
5462                              &ChangesMade,
5463                              NULL,
5464                              &ChangeCount);
5465 
5466     /* Does the caller want the previous state of groups? */
5467     if (PreviousState != NULL)
5468     {
5469         /* Calculate the required length */
5470         RequiredLength = FIELD_OFFSET(TOKEN_GROUPS, Groups[ChangeCount]);
5471 
5472         /* Return the required length to the caller */
5473         _SEH2_TRY
5474         {
5475             *ReturnLength = RequiredLength;
5476         }
5477         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5478         {
5479             /* Bail out and return the exception code */
5480             Status = _SEH2_GetExceptionCode();
5481             _SEH2_YIELD(goto Quit);
5482         }
5483         _SEH2_END;
5484 
5485         /* The buffer length provided is smaller than the required length, bail out */
5486         if (BufferLength < RequiredLength)
5487         {
5488             Status = STATUS_BUFFER_TOO_SMALL;
5489             goto Quit;
5490         }
5491     }
5492 
5493     /*
5494      * Now it's time to apply changes. Wrap the code
5495      * in SEH as we are returning the old groups state
5496      * list to the caller since PreviousState is a
5497      * UM pointer.
5498      */
5499     _SEH2_TRY
5500     {
5501         Status = SepAdjustGroups(Token,
5502                                  CapturedGroups,
5503                                  CapturedCount,
5504                                  TRUE,
5505                                  ResetToDefault,
5506                                  &ChangesMade,
5507                                  PreviousState,
5508                                  &ChangeCount);
5509     }
5510     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5511     {
5512         /* Bail out and return the exception code */
5513         Status = _SEH2_GetExceptionCode();
5514 
5515         /* Force the write as we touched the token still */
5516         ChangesMade = TRUE;
5517         _SEH2_YIELD(goto Quit);
5518     }
5519     _SEH2_END;
5520 
5521 Quit:
5522     /* Allocate a new ID for the token as we made changes */
5523     if (ChangesMade)
5524     {
5525         ExAllocateLocallyUniqueId(&Token->ModifiedId);
5526     }
5527 
5528     /* Have we successfully acquired the lock and referenced the token before? */
5529     if (LockAndReferenceAcquired)
5530     {
5531         /* Unlock and dereference the token */
5532         SepReleaseTokenLock(Token);
5533         ObDereferenceObject(Token);
5534     }
5535 
5536     /* Release the captured groups */
5537     if (CapturedGroups != NULL)
5538     {
5539         SeReleaseSidAndAttributesArray(CapturedGroups,
5540                                        PreviousMode,
5541                                        TRUE);
5542     }
5543 
5544     return Status;
5545 }
5546 
5547 /**
5548  * @brief
5549  * Removes a certain amount of privileges of a token based upon the request
5550  * by the caller.
5551  *
5552  * @param[in,out] Token
5553  * Token handle where the privileges are about to be modified.
5554  *
5555  * @param[in] DisableAllPrivileges
5556  * If set to TRUE, the function disables all the privileges.
5557  *
5558  * @param[in] NewState
5559  * A new list of privileges that the function will use it accordingly to
5560  * either disable or enable the said privileges and change them.
5561  *
5562  * @param[in] NewStateCount
5563  * The new total number count of privileges.
5564  *
5565  * @param[out] PreviousState
5566  * If specified, the function will return the previous state list of privileges.
5567  *
5568  * @param[in] ApplyChanges
5569  * If set to TRUE, the function will immediatelly apply the changes onto the
5570  * token's privileges.
5571  *
5572  * @param[out] ChangedPrivileges
5573  * The returned count number of changed privileges.
5574  *
5575  * @param[out] ChangesMade
5576  * If TRUE, the function has made changes to the token's privileges. FALSE
5577  * otherwise.
5578  *
5579  * @return
5580  * Returns STATUS_SUCCESS if the function has successfully changed the list
5581  * of privileges. STATUS_NOT_ALL_ASSIGNED is returned if not every privilege
5582  * has been changed.
5583  */
5584 static
5585 NTSTATUS
5586 SepAdjustPrivileges(
5587     _Inout_ PTOKEN Token,
5588     _In_ BOOLEAN DisableAllPrivileges,
5589     _In_opt_ PLUID_AND_ATTRIBUTES NewState,
5590     _In_ ULONG NewStateCount,
5591     _Out_opt_ PTOKEN_PRIVILEGES PreviousState,
5592     _In_ BOOLEAN ApplyChanges,
5593     _Out_ PULONG ChangedPrivileges,
5594     _Out_ PBOOLEAN ChangesMade)
5595 {
5596     ULONG i, j, PrivilegeCount, ChangeCount, NewAttributes;
5597 
5598     PAGED_CODE();
5599 
5600     /* Count the found privileges and those that need to be changed */
5601     PrivilegeCount = 0;
5602     ChangeCount = 0;
5603     *ChangesMade = FALSE;
5604 
5605     /* Loop all privileges in the token */
5606     for (i = 0; i < Token->PrivilegeCount; i++)
5607     {
5608         /* Shall all of them be disabled? */
5609         if (DisableAllPrivileges)
5610         {
5611             /* The new attributes are the old ones, but disabled */
5612             NewAttributes = Token->Privileges[i].Attributes & ~SE_PRIVILEGE_ENABLED;
5613         }
5614         else
5615         {
5616             /* Otherwise loop all provided privileges */
5617             for (j = 0; j < NewStateCount; j++)
5618             {
5619                 /* Check if this is the LUID we are looking for */
5620                 if (RtlEqualLuid(&Token->Privileges[i].Luid, &NewState[j].Luid))
5621                 {
5622                     DPRINT("Found privilege\n");
5623 
5624                     /* Copy SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_REMOVED */
5625                     NewAttributes = NewState[j].Attributes;
5626                     NewAttributes &= (SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_REMOVED);
5627                     NewAttributes |= Token->Privileges[i].Attributes & ~SE_PRIVILEGE_ENABLED;
5628 
5629                     /* Stop looking */
5630                     break;
5631                 }
5632             }
5633 
5634             /* Check if we didn't find the privilege */
5635             if (j == NewStateCount)
5636             {
5637                 /* Continue with the token's next privilege */
5638                 continue;
5639             }
5640         }
5641 
5642         /* We found a privilege, count it */
5643         PrivilegeCount++;
5644 
5645         /* Does the privilege need to be changed? */
5646         if (Token->Privileges[i].Attributes != NewAttributes)
5647         {
5648             /* Does the caller want the old privileges? */
5649             if (PreviousState != NULL)
5650             {
5651                 /* Copy the old privilege */
5652                 PreviousState->Privileges[ChangeCount] = Token->Privileges[i];
5653             }
5654 
5655             /* Does the caller want to apply the changes? */
5656             if (ApplyChanges)
5657             {
5658                 /* Shall we remove the privilege? */
5659                 if (NewAttributes & SE_PRIVILEGE_REMOVED)
5660                 {
5661                     /* Set the token as disabled and update flags for it */
5662                     Token->Privileges[i].Attributes &= ~SE_PRIVILEGE_ENABLED;
5663                     SepUpdateSinglePrivilegeFlagToken(Token, i);
5664 
5665                     /* Remove the privilege */
5666                     SepRemovePrivilegeToken(Token, i);
5667 
5668                     *ChangesMade = TRUE;
5669 
5670                     /* Fix the running index and continue with next one */
5671                     i--;
5672                     continue;
5673                 }
5674 
5675                 /* Set the new attributes and update flags */
5676                 Token->Privileges[i].Attributes = NewAttributes;
5677                 SepUpdateSinglePrivilegeFlagToken(Token, i);
5678                 *ChangesMade = TRUE;
5679             }
5680 
5681             /* Increment the change count */
5682             ChangeCount++;
5683         }
5684     }
5685 
5686     /* Set the number of saved privileges */
5687     if (PreviousState != NULL)
5688         PreviousState->PrivilegeCount = ChangeCount;
5689 
5690     /* Return the number of changed privileges */
5691     *ChangedPrivileges = ChangeCount;
5692 
5693     /* Check if we missed some */
5694     if (!DisableAllPrivileges && (PrivilegeCount < NewStateCount))
5695     {
5696         return STATUS_NOT_ALL_ASSIGNED;
5697     }
5698 
5699     return STATUS_SUCCESS;
5700 }
5701 
5702 /**
5703  * @brief
5704  * Removes a certain amount of privileges of a token based upon the request
5705  * by the caller.
5706  *
5707  * @param[in,out] Token
5708  * Token handle where the privileges are about to be modified.
5709  *
5710  * @param[in] DisableAllPrivileges
5711  * If set to TRUE, the function disables all the privileges.
5712  *
5713  * @param[in] NewState
5714  * A new list of privileges that the function will use it accordingly to
5715  * either disable or enable the said privileges and change them.
5716  *
5717  * @param[in] NewStateCount
5718  * The new total number count of privileges.
5719  *
5720  * @param[out] PreviousState
5721  * If specified, the function will return the previous state list of privileges.
5722  *
5723  * @param[in] ApplyChanges
5724  * If set to TRUE, the function will immediatelly apply the changes onto the
5725  * token's privileges.
5726  *
5727  * @param[out] ChangedPrivileges
5728  * The returned count number of changed privileges.
5729  *
5730  * @param[out] ChangesMade
5731  * If TRUE, the function has made changes to the token's privileges. FALSE
5732  * otherwise.
5733  *
5734  * @return
5735  * Returns STATUS_SUCCESS if the function has successfully changed the list
5736  * of privileges. STATUS_NOT_ALL_ASSIGNED is returned if not every privilege
5737  * has been changed.
5738  */
5739 _Must_inspect_result_
5740 __kernel_entry
5741 NTSTATUS
5742 NTAPI
5743 NtAdjustPrivilegesToken(
5744     _In_ HANDLE TokenHandle,
5745     _In_ BOOLEAN DisableAllPrivileges,
5746     _In_opt_ PTOKEN_PRIVILEGES NewState,
5747     _In_ ULONG BufferLength,
5748     _Out_writes_bytes_to_opt_(BufferLength,*ReturnLength)
5749         PTOKEN_PRIVILEGES PreviousState,
5750     _When_(PreviousState!=NULL, _Out_) PULONG ReturnLength)
5751 {
5752     NTSTATUS Status;
5753     KPROCESSOR_MODE PreviousMode;
5754     PTOKEN Token;
5755     PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
5756     ULONG CapturedCount = 0;
5757     ULONG CapturedLength = 0;
5758     ULONG NewStateSize = 0;
5759     ULONG ChangeCount;
5760     ULONG RequiredLength;
5761     BOOLEAN ChangesMade = FALSE;
5762 
5763     PAGED_CODE();
5764 
5765     DPRINT("NtAdjustPrivilegesToken() called\n");
5766 
5767     /* Fail, if we do not disable all privileges but NewState is NULL */
5768     if (DisableAllPrivileges == FALSE && NewState == NULL)
5769         return STATUS_INVALID_PARAMETER;
5770 
5771     PreviousMode = KeGetPreviousMode();
5772     if (PreviousMode != KernelMode)
5773     {
5774         _SEH2_TRY
5775         {
5776             /* Probe NewState */
5777             if (DisableAllPrivileges == FALSE)
5778             {
5779                 /* First probe the header */
5780                 ProbeForRead(NewState, sizeof(TOKEN_PRIVILEGES), sizeof(ULONG));
5781 
5782                 CapturedCount = NewState->PrivilegeCount;
5783                 NewStateSize = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[CapturedCount]);
5784 
5785                 ProbeForRead(NewState, NewStateSize, sizeof(ULONG));
5786             }
5787 
5788             /* Probe PreviousState and ReturnLength */
5789             if (PreviousState != NULL)
5790             {
5791                 ProbeForWrite(PreviousState, BufferLength, sizeof(ULONG));
5792                 ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG));
5793             }
5794         }
5795         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5796         {
5797             /* Return the exception code */
5798             _SEH2_YIELD(return _SEH2_GetExceptionCode());
5799         }
5800         _SEH2_END;
5801     }
5802     else
5803     {
5804         /* This is kernel mode, we trust the caller */
5805         if (DisableAllPrivileges == FALSE)
5806             CapturedCount = NewState->PrivilegeCount;
5807     }
5808 
5809     /* Do we need to capture the new state? */
5810     if (DisableAllPrivileges == FALSE)
5811     {
5812         _SEH2_TRY
5813         {
5814             /* Capture the new state array of privileges */
5815             Status = SeCaptureLuidAndAttributesArray(NewState->Privileges,
5816                                                      CapturedCount,
5817                                                      PreviousMode,
5818                                                      NULL,
5819                                                      0,
5820                                                      PagedPool,
5821                                                      TRUE,
5822                                                      &CapturedPrivileges,
5823                                                      &CapturedLength);
5824         }
5825         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5826         {
5827             /* Return the exception code */
5828             Status = _SEH2_GetExceptionCode();
5829         }
5830         _SEH2_END;
5831 
5832         if (!NT_SUCCESS(Status))
5833             return Status;
5834     }
5835 
5836     /* Reference the token */
5837     Status = ObReferenceObjectByHandle(TokenHandle,
5838                                        TOKEN_ADJUST_PRIVILEGES | (PreviousState != NULL ? TOKEN_QUERY : 0),
5839                                        SeTokenObjectType,
5840                                        PreviousMode,
5841                                        (PVOID*)&Token,
5842                                        NULL);
5843     if (!NT_SUCCESS(Status))
5844     {
5845         DPRINT1("Failed to reference token (Status 0x%lx)\n", Status);
5846 
5847         /* Release the captured privileges */
5848         if (CapturedPrivileges != NULL)
5849         {
5850             SeReleaseLuidAndAttributesArray(CapturedPrivileges,
5851                                             PreviousMode,
5852                                             TRUE);
5853         }
5854 
5855         return Status;
5856     }
5857 
5858     /* Lock the token */
5859     SepAcquireTokenLockExclusive(Token);
5860 
5861     /* Count the privileges that need to be changed, do not apply them yet */
5862     Status = SepAdjustPrivileges(Token,
5863                                  DisableAllPrivileges,
5864                                  CapturedPrivileges,
5865                                  CapturedCount,
5866                                  NULL,
5867                                  FALSE,
5868                                  &ChangeCount,
5869                                  &ChangesMade);
5870 
5871     /* Check if the caller asked for the previous state */
5872     if (PreviousState != NULL)
5873     {
5874         /* Calculate the required length */
5875         RequiredLength = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[ChangeCount]);
5876 
5877         /* Try to return the required buffer length */
5878         _SEH2_TRY
5879         {
5880             *ReturnLength = RequiredLength;
5881         }
5882         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5883         {
5884             /* Do cleanup and return the exception code */
5885             Status = _SEH2_GetExceptionCode();
5886             _SEH2_YIELD(goto Cleanup);
5887         }
5888         _SEH2_END;
5889 
5890         /* Fail, if the buffer length is smaller than the required length */
5891         if (BufferLength < RequiredLength)
5892         {
5893             Status = STATUS_BUFFER_TOO_SMALL;
5894             goto Cleanup;
5895         }
5896     }
5897 
5898     /* Now enter SEH, since we might return the old privileges */
5899     _SEH2_TRY
5900     {
5901         /* This time apply the changes */
5902         Status = SepAdjustPrivileges(Token,
5903                                      DisableAllPrivileges,
5904                                      CapturedPrivileges,
5905                                      CapturedCount,
5906                                      PreviousState,
5907                                      TRUE,
5908                                      &ChangeCount,
5909                                      &ChangesMade);
5910     }
5911     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5912     {
5913         /* Do cleanup and return the exception code */
5914         Status = _SEH2_GetExceptionCode();
5915         ChangesMade = TRUE; // Force write.
5916         _SEH2_YIELD(goto Cleanup);
5917     }
5918     _SEH2_END;
5919 
5920 Cleanup:
5921     /* Touch the token if we made changes */
5922     if (ChangesMade)
5923         ExAllocateLocallyUniqueId(&Token->ModifiedId);
5924 
5925     /* Unlock and dereference the token */
5926     SepReleaseTokenLock(Token);
5927     ObDereferenceObject(Token);
5928 
5929     /* Release the captured privileges */
5930     if (CapturedPrivileges != NULL)
5931     {
5932         SeReleaseLuidAndAttributesArray(CapturedPrivileges,
5933                                         PreviousMode,
5934                                         TRUE);
5935     }
5936 
5937     DPRINT ("NtAdjustPrivilegesToken() done\n");
5938     return Status;
5939 }
5940 
5941 /**
5942  * @brief
5943  * Creates an access token.
5944  *
5945  * @param[out] TokenHandle
5946  * The returned created token handle to the caller.
5947  *
5948  * @param[in] DesiredAccess
5949  * The desired access rights for the token that we're creating.
5950  *
5951  * @param[in] ObjectAttributes
5952  * The object attributes for the token object that we're creating.
5953  *
5954  * @param[in] TokenType
5955  * The type of token to assign for the newly created token.
5956  *
5957  * @param[in] AuthenticationId
5958  * Authentication ID that represents the token's identity.
5959  *
5960  * @param[in] ExpirationTime
5961  * Expiration time for the token. If set to -1, the token never expires.
5962  *
5963  * @param[in] TokenUser
5964  * The main user entity for the token to assign.
5965  *
5966  * @param[in] TokenGroups
5967  * Group list of SIDs for the token to assign.
5968  *
5969  * @param[in] TokenPrivileges
5970  * Privileges for the token.
5971  *
5972  * @param[in] TokenOwner
5973  * The main user that owns the newly created token.
5974  *
5975  * @param[in] TokenPrimaryGroup
5976  * The primary group that represents as the main group of the token.
5977  *
5978  * @param[in] TokenDefaultDacl
5979  * Discretionary access control list for the token. This limits on how
5980  * the token can be used, accessed and used by whom.
5981  *
5982  * @param[in] TokenSource
5983  * The source origin of the token who creates it.
5984  *
5985  * @return
5986  * Returns STATUS_SUCCESS if the function has successfully created the token.
5987  * A failure NTSTATUS code is returned otherwise.
5988  */
5989 __kernel_entry
5990 NTSTATUS
5991 NTAPI
5992 NtCreateToken(
5993     _Out_ PHANDLE TokenHandle,
5994     _In_ ACCESS_MASK DesiredAccess,
5995     _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
5996     _In_ TOKEN_TYPE TokenType,
5997     _In_ PLUID AuthenticationId,
5998     _In_ PLARGE_INTEGER ExpirationTime,
5999     _In_ PTOKEN_USER TokenUser,
6000     _In_ PTOKEN_GROUPS TokenGroups,
6001     _In_ PTOKEN_PRIVILEGES TokenPrivileges,
6002     _In_opt_ PTOKEN_OWNER TokenOwner,
6003     _In_ PTOKEN_PRIMARY_GROUP TokenPrimaryGroup,
6004     _In_opt_ PTOKEN_DEFAULT_DACL TokenDefaultDacl,
6005     _In_ PTOKEN_SOURCE TokenSource)
6006 {
6007     HANDLE hToken;
6008     KPROCESSOR_MODE PreviousMode;
6009     ULONG PrivilegeCount, GroupCount;
6010     PSID OwnerSid, PrimaryGroupSid;
6011     PACL DefaultDacl;
6012     LARGE_INTEGER LocalExpirationTime = {{0, 0}};
6013     LUID LocalAuthenticationId;
6014     TOKEN_SOURCE LocalTokenSource;
6015     SECURITY_QUALITY_OF_SERVICE LocalSecurityQos;
6016     PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
6017     PSID_AND_ATTRIBUTES CapturedUser = NULL;
6018     PSID_AND_ATTRIBUTES CapturedGroups = NULL;
6019     PSID CapturedOwnerSid = NULL;
6020     PSID CapturedPrimaryGroupSid = NULL;
6021     PACL CapturedDefaultDacl = NULL;
6022     ULONG PrivilegesLength, UserLength, GroupsLength;
6023     NTSTATUS Status;
6024 
6025     PAGED_CODE();
6026 
6027     PreviousMode = ExGetPreviousMode();
6028 
6029     if (PreviousMode != KernelMode)
6030     {
6031         _SEH2_TRY
6032         {
6033             ProbeForWriteHandle(TokenHandle);
6034 
6035             if (ObjectAttributes != NULL)
6036             {
6037                 ProbeForRead(ObjectAttributes,
6038                              sizeof(OBJECT_ATTRIBUTES),
6039                              sizeof(ULONG));
6040                 LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService;
6041             }
6042 
6043             ProbeForRead(AuthenticationId,
6044                          sizeof(LUID),
6045                          sizeof(ULONG));
6046             LocalAuthenticationId = *AuthenticationId;
6047 
6048             LocalExpirationTime = ProbeForReadLargeInteger(ExpirationTime);
6049 
6050             ProbeForRead(TokenUser,
6051                          sizeof(TOKEN_USER),
6052                          sizeof(ULONG));
6053 
6054             ProbeForRead(TokenGroups,
6055                          sizeof(TOKEN_GROUPS),
6056                          sizeof(ULONG));
6057             GroupCount = TokenGroups->GroupCount;
6058 
6059             ProbeForRead(TokenPrivileges,
6060                          sizeof(TOKEN_PRIVILEGES),
6061                          sizeof(ULONG));
6062             PrivilegeCount = TokenPrivileges->PrivilegeCount;
6063 
6064             if (TokenOwner != NULL)
6065             {
6066                 ProbeForRead(TokenOwner,
6067                              sizeof(TOKEN_OWNER),
6068                              sizeof(ULONG));
6069                 OwnerSid = TokenOwner->Owner;
6070             }
6071             else
6072             {
6073                 OwnerSid = NULL;
6074             }
6075 
6076             ProbeForRead(TokenPrimaryGroup,
6077                          sizeof(TOKEN_PRIMARY_GROUP),
6078                          sizeof(ULONG));
6079             PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup;
6080 
6081             if (TokenDefaultDacl != NULL)
6082             {
6083                 ProbeForRead(TokenDefaultDacl,
6084                              sizeof(TOKEN_DEFAULT_DACL),
6085                              sizeof(ULONG));
6086                 DefaultDacl = TokenDefaultDacl->DefaultDacl;
6087             }
6088             else
6089             {
6090                 DefaultDacl = NULL;
6091             }
6092 
6093             ProbeForRead(TokenSource,
6094                          sizeof(TOKEN_SOURCE),
6095                          sizeof(ULONG));
6096             LocalTokenSource = *TokenSource;
6097         }
6098         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6099         {
6100             /* Return the exception code */
6101             _SEH2_YIELD(return _SEH2_GetExceptionCode());
6102         }
6103         _SEH2_END;
6104     }
6105     else
6106     {
6107         if (ObjectAttributes != NULL)
6108             LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService;
6109         LocalAuthenticationId = *AuthenticationId;
6110         LocalExpirationTime = *ExpirationTime;
6111         GroupCount = TokenGroups->GroupCount;
6112         PrivilegeCount = TokenPrivileges->PrivilegeCount;
6113         OwnerSid = TokenOwner ? TokenOwner->Owner : NULL;
6114         PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup;
6115         DefaultDacl = TokenDefaultDacl ? TokenDefaultDacl->DefaultDacl : NULL;
6116         LocalTokenSource = *TokenSource;
6117     }
6118 
6119     /* Check token type */
6120     if ((TokenType < TokenPrimary) ||
6121         (TokenType > TokenImpersonation))
6122     {
6123         return STATUS_BAD_TOKEN_TYPE;
6124     }
6125 
6126     /* Check for token creation privilege */
6127     if (!SeSinglePrivilegeCheck(SeCreateTokenPrivilege, PreviousMode))
6128     {
6129         return STATUS_PRIVILEGE_NOT_HELD;
6130     }
6131 
6132     /* Capture the user SID and attributes */
6133     Status = SeCaptureSidAndAttributesArray(&TokenUser->User,
6134                                             1,
6135                                             PreviousMode,
6136                                             NULL,
6137                                             0,
6138                                             PagedPool,
6139                                             FALSE,
6140                                             &CapturedUser,
6141                                             &UserLength);
6142     if (!NT_SUCCESS(Status))
6143     {
6144         goto Cleanup;
6145     }
6146 
6147     /* Capture the groups SID and attributes array */
6148     Status = SeCaptureSidAndAttributesArray(&TokenGroups->Groups[0],
6149                                             GroupCount,
6150                                             PreviousMode,
6151                                             NULL,
6152                                             0,
6153                                             PagedPool,
6154                                             FALSE,
6155                                             &CapturedGroups,
6156                                             &GroupsLength);
6157     if (!NT_SUCCESS(Status))
6158     {
6159         goto Cleanup;
6160     }
6161 
6162     /* Capture privileges */
6163     Status = SeCaptureLuidAndAttributesArray(&TokenPrivileges->Privileges[0],
6164                                              PrivilegeCount,
6165                                              PreviousMode,
6166                                              NULL,
6167                                              0,
6168                                              PagedPool,
6169                                              FALSE,
6170                                              &CapturedPrivileges,
6171                                              &PrivilegesLength);
6172     if (!NT_SUCCESS(Status))
6173     {
6174         goto Cleanup;
6175     }
6176 
6177     /* Capture the token owner SID */
6178     if (TokenOwner != NULL)
6179     {
6180         Status = SepCaptureSid(OwnerSid,
6181                                PreviousMode,
6182                                PagedPool,
6183                                FALSE,
6184                                &CapturedOwnerSid);
6185         if (!NT_SUCCESS(Status))
6186         {
6187             goto Cleanup;
6188         }
6189     }
6190 
6191     /* Capture the token primary group SID */
6192     Status = SepCaptureSid(PrimaryGroupSid,
6193                            PreviousMode,
6194                            PagedPool,
6195                            FALSE,
6196                            &CapturedPrimaryGroupSid);
6197     if (!NT_SUCCESS(Status))
6198     {
6199         goto Cleanup;
6200     }
6201 
6202     /* Capture DefaultDacl */
6203     if (DefaultDacl != NULL)
6204     {
6205         Status = SepCaptureAcl(DefaultDacl,
6206                                PreviousMode,
6207                                NonPagedPool,
6208                                FALSE,
6209                                &CapturedDefaultDacl);
6210         if (!NT_SUCCESS(Status))
6211         {
6212             goto Cleanup;
6213         }
6214     }
6215 
6216     /* Call the internal function */
6217     Status = SepCreateToken(&hToken,
6218                             PreviousMode,
6219                             DesiredAccess,
6220                             ObjectAttributes,
6221                             TokenType,
6222                             LocalSecurityQos.ImpersonationLevel,
6223                             &LocalAuthenticationId,
6224                             &LocalExpirationTime,
6225                             CapturedUser,
6226                             GroupCount,
6227                             CapturedGroups,
6228                             GroupsLength,
6229                             PrivilegeCount,
6230                             CapturedPrivileges,
6231                             CapturedOwnerSid,
6232                             CapturedPrimaryGroupSid,
6233                             CapturedDefaultDacl,
6234                             &LocalTokenSource,
6235                             FALSE);
6236     if (NT_SUCCESS(Status))
6237     {
6238         _SEH2_TRY
6239         {
6240             *TokenHandle = hToken;
6241         }
6242         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6243         {
6244             Status = _SEH2_GetExceptionCode();
6245         }
6246         _SEH2_END;
6247     }
6248 
6249 Cleanup:
6250 
6251     /* Release what we captured */
6252     SeReleaseSidAndAttributesArray(CapturedUser, PreviousMode, FALSE);
6253     SeReleaseSidAndAttributesArray(CapturedGroups, PreviousMode, FALSE);
6254     SeReleaseLuidAndAttributesArray(CapturedPrivileges, PreviousMode, FALSE);
6255     SepReleaseSid(CapturedOwnerSid, PreviousMode, FALSE);
6256     SepReleaseSid(CapturedPrimaryGroupSid, PreviousMode, FALSE);
6257     SepReleaseAcl(CapturedDefaultDacl, PreviousMode, FALSE);
6258 
6259     return Status;
6260 }
6261 
6262 /**
6263  * @brief
6264  * Opens a token that is tied to a thread handle.
6265  *
6266  * @param[out] ThreadHandle
6267  * Thread handle where the token is about to be opened.
6268  *
6269  * @param[in] DesiredAccess
6270  * The request access right for the token.
6271  *
6272  * @param[in] OpenAsSelf
6273  * If set to TRUE, the access check will be made with the security context
6274  * of the process of the calling thread (opening as self). Otherwise the access
6275  * check will be made with the security context of the calling thread instead.
6276  *
6277  * @param[in] HandleAttributes
6278  * Handle attributes for the opened thread token handle.
6279  *
6280  * @param[out] TokenHandle
6281  * The opened token handle returned to the caller for use.
6282  *
6283  * @return
6284  * Returns STATUS_SUCCESS if the function has successfully opened the thread
6285  * token. STATUS_CANT_OPEN_ANONYMOUS is returned if a token has SecurityAnonymous
6286  * as impersonation level and we cannot open it. A failure NTSTATUS code is returned
6287  * otherwise.
6288  */
6289 NTSTATUS
6290 NTAPI
6291 NtOpenThreadTokenEx(
6292     _In_ HANDLE ThreadHandle,
6293     _In_ ACCESS_MASK DesiredAccess,
6294     _In_ BOOLEAN OpenAsSelf,
6295     _In_ ULONG HandleAttributes,
6296     _Out_ PHANDLE TokenHandle)
6297 {
6298     PETHREAD Thread;
6299     HANDLE hToken;
6300     PTOKEN Token, NewToken = NULL, PrimaryToken;
6301     BOOLEAN CopyOnOpen, EffectiveOnly;
6302     SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
6303     SE_IMPERSONATION_STATE ImpersonationState;
6304     OBJECT_ATTRIBUTES ObjectAttributes;
6305     SECURITY_DESCRIPTOR SecurityDescriptor;
6306     PACL Dacl = NULL;
6307     KPROCESSOR_MODE PreviousMode;
6308     NTSTATUS Status;
6309     BOOLEAN RestoreImpersonation = FALSE;
6310 
6311     PAGED_CODE();
6312 
6313     PreviousMode = ExGetPreviousMode();
6314 
6315     if (PreviousMode != KernelMode)
6316     {
6317         _SEH2_TRY
6318         {
6319             ProbeForWriteHandle(TokenHandle);
6320         }
6321         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6322         {
6323             /* Return the exception code */
6324             _SEH2_YIELD(return _SEH2_GetExceptionCode());
6325         }
6326         _SEH2_END;
6327     }
6328 
6329     /* Validate object attributes */
6330     HandleAttributes = ObpValidateAttributes(HandleAttributes, PreviousMode);
6331 
6332     /*
6333      * At first open the thread token for information access and verify
6334      * that the token associated with thread is valid.
6335      */
6336 
6337     Status = ObReferenceObjectByHandle(ThreadHandle, THREAD_QUERY_INFORMATION,
6338                                        PsThreadType, PreviousMode, (PVOID*)&Thread,
6339                                        NULL);
6340     if (!NT_SUCCESS(Status))
6341     {
6342         return Status;
6343     }
6344 
6345     Token = PsReferenceImpersonationToken(Thread, &CopyOnOpen, &EffectiveOnly,
6346                                           &ImpersonationLevel);
6347     if (Token == NULL)
6348     {
6349         ObDereferenceObject(Thread);
6350         return STATUS_NO_TOKEN;
6351     }
6352 
6353     if (ImpersonationLevel == SecurityAnonymous)
6354     {
6355         PsDereferenceImpersonationToken(Token);
6356         ObDereferenceObject(Thread);
6357         return STATUS_CANT_OPEN_ANONYMOUS;
6358     }
6359 
6360     /*
6361      * Revert to self if OpenAsSelf is specified.
6362      */
6363 
6364     if (OpenAsSelf)
6365     {
6366         RestoreImpersonation = PsDisableImpersonation(PsGetCurrentThread(),
6367                                                       &ImpersonationState);
6368     }
6369 
6370     if (CopyOnOpen)
6371     {
6372         PrimaryToken = PsReferencePrimaryToken(Thread->ThreadsProcess);
6373 
6374         Status = SepCreateImpersonationTokenDacl(Token, PrimaryToken, &Dacl);
6375 
6376         ObFastDereferenceObject(&Thread->ThreadsProcess->Token, PrimaryToken);
6377 
6378         if (NT_SUCCESS(Status))
6379         {
6380             if (Dacl)
6381             {
6382                 Status = RtlCreateSecurityDescriptor(&SecurityDescriptor,
6383                                                      SECURITY_DESCRIPTOR_REVISION);
6384                 if (!NT_SUCCESS(Status))
6385                 {
6386                     DPRINT1("NtOpenThreadTokenEx(): Failed to create a security descriptor (Status 0x%lx)\n", Status);
6387                 }
6388 
6389                 Status = RtlSetDaclSecurityDescriptor(&SecurityDescriptor, TRUE, Dacl,
6390                                                       FALSE);
6391                 if (!NT_SUCCESS(Status))
6392                 {
6393                     DPRINT1("NtOpenThreadTokenEx(): Failed to set a DACL to the security descriptor (Status 0x%lx)\n", Status);
6394                 }
6395             }
6396 
6397             InitializeObjectAttributes(&ObjectAttributes, NULL, HandleAttributes,
6398                                        NULL, Dacl ? &SecurityDescriptor : NULL);
6399 
6400             Status = SepDuplicateToken(Token, &ObjectAttributes, EffectiveOnly,
6401                                        TokenImpersonation, ImpersonationLevel,
6402                                        KernelMode, &NewToken);
6403             if (!NT_SUCCESS(Status))
6404             {
6405                 DPRINT1("NtOpenThreadTokenEx(): Failed to duplicate the token (Status 0x%lx)\n", Status);
6406             }
6407 
6408             ObReferenceObject(NewToken);
6409             Status = ObInsertObject(NewToken, NULL, DesiredAccess, 0, NULL,
6410                                     &hToken);
6411             if (!NT_SUCCESS(Status))
6412             {
6413                 DPRINT1("NtOpenThreadTokenEx(): Failed to insert the token object (Status 0x%lx)\n", Status);
6414             }
6415         }
6416         else
6417         {
6418             DPRINT1("NtOpenThreadTokenEx(): Failed to impersonate token from DACL (Status 0x%lx)\n", Status);
6419         }
6420     }
6421     else
6422     {
6423         Status = ObOpenObjectByPointer(Token, HandleAttributes,
6424                                        NULL, DesiredAccess, SeTokenObjectType,
6425                                        PreviousMode, &hToken);
6426         if (!NT_SUCCESS(Status))
6427         {
6428             DPRINT1("NtOpenThreadTokenEx(): Failed to open the object (Status 0x%lx)\n", Status);
6429         }
6430     }
6431 
6432     if (Dacl) ExFreePoolWithTag(Dacl, TAG_ACL);
6433 
6434     if (RestoreImpersonation)
6435     {
6436         PsRestoreImpersonation(PsGetCurrentThread(), &ImpersonationState);
6437     }
6438 
6439     ObDereferenceObject(Token);
6440 
6441     if (NT_SUCCESS(Status) && CopyOnOpen)
6442     {
6443         Status = PsImpersonateClient(Thread, NewToken, FALSE, EffectiveOnly, ImpersonationLevel);
6444         if (!NT_SUCCESS(Status))
6445         {
6446             DPRINT1("NtOpenThreadTokenEx(): Failed to impersonate the client (Status 0x%lx)\n", Status);
6447         }
6448     }
6449 
6450     if (NewToken) ObDereferenceObject(NewToken);
6451 
6452     ObDereferenceObject(Thread);
6453 
6454     if (NT_SUCCESS(Status))
6455     {
6456         _SEH2_TRY
6457         {
6458             *TokenHandle = hToken;
6459         }
6460         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6461         {
6462             Status = _SEH2_GetExceptionCode();
6463         }
6464         _SEH2_END;
6465     }
6466 
6467     return Status;
6468 }
6469 
6470 /**
6471  * @brief
6472  * Opens a token that is tied to a thread handle.
6473  *
6474  * @param[out] ThreadHandle
6475  * Thread handle where the token is about to be opened.
6476  *
6477  * @param[in] DesiredAccess
6478  * The request access right for the token.
6479  *
6480  * @param[in] OpenAsSelf
6481  * If set to TRUE, the access check will be made with the security context
6482  * of the process of the calling thread (opening as self). Otherwise the access
6483  * check will be made with the security context of the calling thread instead.
6484  *
6485  * @param[out] TokenHandle
6486  * The opened token handle returned to the caller for use.
6487  *
6488  * @return
6489  * See NtOpenThreadTokenEx.
6490  */
6491 NTSTATUS
6492 NTAPI
6493 NtOpenThreadToken(
6494     _In_ HANDLE ThreadHandle,
6495     _In_ ACCESS_MASK DesiredAccess,
6496     _In_ BOOLEAN OpenAsSelf,
6497     _Out_ PHANDLE TokenHandle)
6498 {
6499     return NtOpenThreadTokenEx(ThreadHandle, DesiredAccess, OpenAsSelf, 0,
6500                                TokenHandle);
6501 }
6502 
6503 /**
6504  * @brief
6505  * Compares tokens if they're equal or not.
6506  *
6507  * @param[in] FirstToken
6508  * The first token.
6509  *
6510  * @param[in] SecondToken
6511  * The second token.
6512  *
6513  * @param[out] Equal
6514  * The retrieved value which determines if the tokens are
6515  * equal or not.
6516  *
6517  * @return
6518  * Returns STATUS_SUCCESS, otherwise it returns a failure NTSTATUS code.
6519  */
6520 NTSTATUS
6521 NTAPI
6522 NtCompareTokens(
6523     _In_ HANDLE FirstTokenHandle,
6524     _In_ HANDLE SecondTokenHandle,
6525     _Out_ PBOOLEAN Equal)
6526 {
6527     KPROCESSOR_MODE PreviousMode;
6528     PTOKEN FirstToken, SecondToken;
6529     BOOLEAN IsEqual;
6530     NTSTATUS Status;
6531 
6532     PAGED_CODE();
6533 
6534     PreviousMode = ExGetPreviousMode();
6535 
6536     if (PreviousMode != KernelMode)
6537     {
6538         _SEH2_TRY
6539         {
6540             ProbeForWriteBoolean(Equal);
6541         }
6542         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6543         {
6544             /* Return the exception code */
6545             _SEH2_YIELD(return _SEH2_GetExceptionCode());
6546         }
6547         _SEH2_END;
6548     }
6549 
6550     Status = ObReferenceObjectByHandle(FirstTokenHandle,
6551                                        TOKEN_QUERY,
6552                                        SeTokenObjectType,
6553                                        PreviousMode,
6554                                        (PVOID*)&FirstToken,
6555                                        NULL);
6556     if (!NT_SUCCESS(Status))
6557     {
6558         DPRINT1("ObReferenceObjectByHandle() failed (Status 0x%lx)\n", Status);
6559         return Status;
6560     }
6561 
6562     Status = ObReferenceObjectByHandle(SecondTokenHandle,
6563                                        TOKEN_QUERY,
6564                                        SeTokenObjectType,
6565                                        PreviousMode,
6566                                        (PVOID*)&SecondToken,
6567                                        NULL);
6568     if (!NT_SUCCESS(Status))
6569     {
6570         DPRINT1("ObReferenceObjectByHandle() failed (Status 0x%lx)\n", Status);
6571         ObDereferenceObject(FirstToken);
6572         return Status;
6573     }
6574 
6575     if (FirstToken != SecondToken)
6576     {
6577         Status = SepCompareTokens(FirstToken,
6578                                   SecondToken,
6579                                   &IsEqual);
6580     }
6581     else
6582     {
6583         IsEqual = TRUE;
6584     }
6585 
6586     ObDereferenceObject(SecondToken);
6587     ObDereferenceObject(FirstToken);
6588 
6589     if (NT_SUCCESS(Status))
6590     {
6591         _SEH2_TRY
6592         {
6593             *Equal = IsEqual;
6594         }
6595         _SEH2_EXCEPT(ExSystemExceptionFilter())
6596         {
6597             Status = _SEH2_GetExceptionCode();
6598         }
6599         _SEH2_END;
6600     }
6601 
6602     return Status;
6603 }
6604 
6605 /**
6606  * @brief
6607  * Creates an access token in a restricted form
6608  * from the original existing token, that is, such
6609  * action is called filtering.
6610  *
6611  * @param[in] ExistingTokenHandle
6612  * A handle to an access token which is to be filtered.
6613  *
6614  * @param[in] Flags
6615  * Privilege flag options. This parameter argument influences how the
6616  * token's privileges are filtered. For further details see remarks.
6617  *
6618  * @param[in] SidsToDisable
6619  * Array of SIDs to disable. The action of doing so assigns the
6620  * SE_GROUP_USE_FOR_DENY_ONLY attribute to the respective group
6621  * SID and takes away SE_GROUP_ENABLED and SE_GROUP_ENABLED_BY_DEFAULT.
6622  * This parameter can be NULL. This can be a UM pointer.
6623  *
6624  * @param[in] PrivilegesToDelete
6625  * Array of privileges to delete. The function will walk within this
6626  * array to determine if the specified privileges do exist in the
6627  * access token. Any missing privileges gets ignored. This parameter
6628  * can be NULL. This can be a UM pointer.
6629  *
6630  * @param[in] RestrictedSids
6631  * An array list of restricted groups SID to be added in the access
6632  * token. A token that is already restricted the newly added restricted
6633  * SIDs are redundant information in addition to the existing restricted
6634  * SIDs in the token. This parameter can be NULL. This can be a UM pointer.
6635  *
6636  * @param[out] NewTokenHandle
6637  * A new handle to the restricted (filtered) access token. This can be a
6638  * UM pointer.
6639  *
6640  * @return
6641  * Returns STATUS_SUCCESS if the routine has successfully filtered the
6642  * access token. STATUS_INVALID_PARAMETER is returned if one or more
6643  * parameters are not valid (see SepPerformTokenFiltering routine call
6644  * for more information). A failure NTSTATUS code is returned otherwise.
6645  *
6646  * @remarks
6647  * The Flags parameter determines the final outcome of how the privileges
6648  * in an access token are filtered. This parameter can take these supported
6649  * values (these can be combined):
6650  *
6651  * 0 -- Filter the token's privileges in the usual way. The function expects
6652  *      that the caller MUST PROVIDE a valid array list of privileges to be
6653  *      deleted (that is, PrivilegesToDelete MUSTN'T BE NULL).
6654  *
6655  * DISABLE_MAX_PRIVILEGE -- Disables (deletes) all the privileges except SeChangeNotifyPrivilege
6656  *                          in the new access token. Bear in mind if this flag is specified
6657  *                          the routine ignores PrivilegesToDelete.
6658  *
6659  * SANDBOX_INERT -- Stores the TOKEN_SANDBOX_INERT token flag within the access token.
6660  *
6661  * LUA_TOKEN -- The newly filtered access token is a LUA token. This flag is not
6662  *              supported in Windows Server 2003.
6663  *
6664  * WRITE_RESTRICTED -- The newly filtered token has the restricted SIDs that are
6665  *                     considered only when evaluating write access onto the token.
6666  *                     This value is not supported in Windows Server 2003.
6667  */
6668 NTSTATUS
6669 NTAPI
6670 NtFilterToken(
6671     _In_ HANDLE ExistingTokenHandle,
6672     _In_ ULONG Flags,
6673     _In_opt_ PTOKEN_GROUPS SidsToDisable,
6674     _In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete,
6675     _In_opt_ PTOKEN_GROUPS RestrictedSids,
6676     _Out_ PHANDLE NewTokenHandle)
6677 {
6678     PTOKEN Token, FilteredToken;
6679     HANDLE FilteredTokenHandle;
6680     NTSTATUS Status;
6681     KPROCESSOR_MODE PreviousMode;
6682     OBJECT_HANDLE_INFORMATION HandleInfo;
6683     ULONG ResultLength;
6684     ULONG CapturedSidsCount = 0;
6685     ULONG CapturedPrivilegesCount = 0;
6686     ULONG CapturedRestrictedSidsCount = 0;
6687     ULONG ProbeSize = 0;
6688     PSID_AND_ATTRIBUTES CapturedSids = NULL;
6689     PSID_AND_ATTRIBUTES CapturedRestrictedSids = NULL;
6690     PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
6691 
6692     PAGED_CODE();
6693 
6694     PreviousMode = ExGetPreviousMode();
6695 
6696     _SEH2_TRY
6697     {
6698         /* Probe SidsToDisable */
6699         if (SidsToDisable != NULL)
6700         {
6701             /* Probe the header */
6702             ProbeForRead(SidsToDisable, sizeof(*SidsToDisable), sizeof(ULONG));
6703 
6704             CapturedSidsCount = SidsToDisable->GroupCount;
6705             ProbeSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedSidsCount]);
6706 
6707             ProbeForRead(SidsToDisable, ProbeSize, sizeof(ULONG));
6708         }
6709 
6710         /* Probe PrivilegesToDelete */
6711         if (PrivilegesToDelete != NULL)
6712         {
6713             /* Probe the header */
6714             ProbeForRead(PrivilegesToDelete, sizeof(*PrivilegesToDelete), sizeof(ULONG));
6715 
6716             CapturedPrivilegesCount = PrivilegesToDelete->PrivilegeCount;
6717             ProbeSize = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[CapturedPrivilegesCount]);
6718 
6719             ProbeForRead(PrivilegesToDelete, ProbeSize, sizeof(ULONG));
6720         }
6721 
6722         /* Probe RestrictedSids */
6723         if (RestrictedSids != NULL)
6724         {
6725             /* Probe the header */
6726             ProbeForRead(RestrictedSids, sizeof(*RestrictedSids), sizeof(ULONG));
6727 
6728             CapturedRestrictedSidsCount = RestrictedSids->GroupCount;
6729             ProbeSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedRestrictedSidsCount]);
6730 
6731             ProbeForRead(RestrictedSids, ProbeSize, sizeof(ULONG));
6732         }
6733 
6734         /* Probe the handle */
6735         ProbeForWriteHandle(NewTokenHandle);
6736     }
6737     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6738     {
6739         /* Return the exception code */
6740         _SEH2_YIELD(return _SEH2_GetExceptionCode());
6741     }
6742     _SEH2_END;
6743 
6744     /* Reference the token and do the job */
6745     Status = ObReferenceObjectByHandle(ExistingTokenHandle,
6746                                        TOKEN_DUPLICATE,
6747                                        SeTokenObjectType,
6748                                        PreviousMode,
6749                                        (PVOID*)&Token,
6750                                        &HandleInfo);
6751     if (!NT_SUCCESS(Status))
6752     {
6753         DPRINT1("NtFilterToken(): Failed to reference the token (Status 0x%lx)\n", Status);
6754         return Status;
6755     }
6756 
6757     /* Lock the token */
6758     SepAcquireTokenLockExclusive(Token);
6759 
6760     /* Capture the group SIDs */
6761     if (SidsToDisable != NULL)
6762     {
6763         Status = SeCaptureSidAndAttributesArray(SidsToDisable->Groups,
6764                                                 CapturedSidsCount,
6765                                                 PreviousMode,
6766                                                 NULL,
6767                                                 0,
6768                                                 PagedPool,
6769                                                 TRUE,
6770                                                 &CapturedSids,
6771                                                 &ResultLength);
6772         if (!NT_SUCCESS(Status))
6773         {
6774             DPRINT1("NtFilterToken(): Failed to capture the SIDs (Status 0x%lx)\n", Status);
6775             goto Quit;
6776         }
6777     }
6778 
6779     /* Capture the privileges */
6780     if (PrivilegesToDelete != NULL)
6781     {
6782         Status = SeCaptureLuidAndAttributesArray(PrivilegesToDelete->Privileges,
6783                                                  CapturedPrivilegesCount,
6784                                                  PreviousMode,
6785                                                  NULL,
6786                                                  0,
6787                                                  PagedPool,
6788                                                  TRUE,
6789                                                  &CapturedPrivileges,
6790                                                  &ResultLength);
6791         if (!NT_SUCCESS(Status))
6792         {
6793             DPRINT1("NtFilterToken(): Failed to capture the privileges (Status 0x%lx)\n", Status);
6794             goto Quit;
6795         }
6796     }
6797 
6798     /* Capture the restricted SIDs */
6799     if (RestrictedSids != NULL)
6800     {
6801         Status = SeCaptureSidAndAttributesArray(RestrictedSids->Groups,
6802                                                 CapturedRestrictedSidsCount,
6803                                                 PreviousMode,
6804                                                 NULL,
6805                                                 0,
6806                                                 PagedPool,
6807                                                 TRUE,
6808                                                 &CapturedRestrictedSids,
6809                                                 &ResultLength);
6810         if (!NT_SUCCESS(Status))
6811         {
6812             DPRINT1("NtFilterToken(): Failed to capture the restricted SIDs (Status 0x%lx)\n", Status);
6813             goto Quit;
6814         }
6815     }
6816 
6817     /* Call the internal API so that it can filter the token for us */
6818     Status = SepPerformTokenFiltering(Token,
6819                                       CapturedPrivileges,
6820                                       CapturedSids,
6821                                       CapturedRestrictedSids,
6822                                       CapturedPrivilegesCount,
6823                                       CapturedSidsCount,
6824                                       CapturedRestrictedSidsCount,
6825                                       Flags,
6826                                       PreviousMode,
6827                                       &FilteredToken);
6828     if (!NT_SUCCESS(Status))
6829     {
6830         DPRINT1("NtFilterToken(): Failed to filter the token (Status 0x%lx)\n", Status);
6831         goto Quit;
6832     }
6833 
6834     /* We got our filtered token, insert it to the handle */
6835     Status = ObInsertObject(FilteredToken,
6836                             NULL,
6837                             HandleInfo.GrantedAccess,
6838                             0,
6839                             NULL,
6840                             &FilteredTokenHandle);
6841     if (!NT_SUCCESS(Status))
6842     {
6843         DPRINT1("NtFilterToken(): Failed to insert the filtered token object into the handle (Status 0x%lx)\n", Status);
6844         goto Quit;
6845     }
6846 
6847     /* And give it to the caller once we're done */
6848     _SEH2_TRY
6849     {
6850         *NewTokenHandle = FilteredTokenHandle;
6851     }
6852     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
6853     {
6854         Status = _SEH2_GetExceptionCode();
6855         _SEH2_YIELD(goto Quit);
6856     }
6857     _SEH2_END;
6858 
6859 Quit:
6860     /* Unlock and dereference the token */
6861     SepReleaseTokenLock(Token);
6862     ObDereferenceObject(Token);
6863 
6864     /* Release all the stuff we've captured */
6865     if (CapturedSids != NULL)
6866     {
6867         SeReleaseSidAndAttributesArray(CapturedSids,
6868                                        PreviousMode,
6869                                        TRUE);
6870         CapturedSids = NULL;
6871     }
6872 
6873     if (CapturedPrivileges != NULL)
6874     {
6875         SeReleaseLuidAndAttributesArray(CapturedPrivileges,
6876                                         PreviousMode,
6877                                         TRUE);
6878         CapturedPrivileges = NULL;
6879     }
6880 
6881     if (CapturedRestrictedSids != NULL)
6882     {
6883         SeReleaseSidAndAttributesArray(CapturedRestrictedSids,
6884                                        PreviousMode,
6885                                        TRUE);
6886         CapturedRestrictedSids = NULL;
6887     }
6888 
6889     return Status;
6890 }
6891 
6892 /**
6893  * @brief
6894  * Allows the calling thread to impersonate the system's anonymous
6895  * logon token.
6896  *
6897  * @param[in] ThreadHandle
6898  * A handle to the thread to start the procedure of logon token
6899  * impersonation. The thread must have the THREAD_IMPERSONATE
6900  * access right.
6901  *
6902  * @return
6903  * Returns STATUS_SUCCESS if the thread has successfully impersonated the
6904  * anonymous logon token, otherwise a failure NTSTATUS code is returned.
6905  *
6906  * @remarks
6907  * By default the system gives the opportunity to the caller to impersonate
6908  * the anonymous logon token without including the Everyone Group SID.
6909  * In cases where the caller wants to impersonate the token including such
6910  * group, the EveryoneIncludesAnonymous registry value setting has to be set
6911  * to 1, from HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa registry
6912  * path. The calling thread must invoke PsRevertToSelf when impersonation
6913  * is no longer needed or RevertToSelf if the calling execution is done
6914  * in user mode.
6915  */
6916 NTSTATUS
6917 NTAPI
6918 NtImpersonateAnonymousToken(
6919     _In_ HANDLE ThreadHandle)
6920 {
6921     PETHREAD Thread;
6922     KPROCESSOR_MODE PreviousMode;
6923     NTSTATUS Status;
6924     PAGED_CODE();
6925 
6926     PreviousMode = ExGetPreviousMode();
6927 
6928     /* Obtain the thread object from the handle */
6929     Status = ObReferenceObjectByHandle(ThreadHandle,
6930                                        THREAD_IMPERSONATE,
6931                                        PsThreadType,
6932                                        PreviousMode,
6933                                        (PVOID*)&Thread,
6934                                        NULL);
6935     if (!NT_SUCCESS(Status))
6936     {
6937         DPRINT1("NtImpersonateAnonymousToken(): Failed to reference the object (Status 0x%lx)\n", Status);
6938         return Status;
6939     }
6940 
6941     /* Call the private routine to impersonate the token */
6942     Status = SepImpersonateAnonymousToken(Thread, PreviousMode);
6943     if (!NT_SUCCESS(Status))
6944     {
6945         DPRINT1("NtImpersonateAnonymousToken(): Failed to impersonate the token (Status 0x%lx)\n", Status);
6946     }
6947 
6948     ObDereferenceObject(Thread);
6949     return Status;
6950 }
6951 
6952 /* EOF */
6953