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