xref: /reactos/ntoskrnl/se/token.c (revision 09bfd96f)
1 /*
2  * PROJECT:     ReactOS Kernel
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Security access token implementation base support routines
5  * COPYRIGHT:   Copyright David Welch <welch@cwcom.net>
6  *              Copyright 2021-2023 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 /* GLOBALS ********************************************************************/
16 
17 POBJECT_TYPE SeTokenObjectType = NULL;
18 
19 TOKEN_SOURCE SeSystemTokenSource = {"*SYSTEM*", {0}};
20 LUID SeSystemAuthenticationId = SYSTEM_LUID;
21 LUID SeAnonymousAuthenticationId = ANONYMOUS_LOGON_LUID;
22 
23 static GENERIC_MAPPING SepTokenMapping = {
24     TOKEN_READ,
25     TOKEN_WRITE,
26     TOKEN_EXECUTE,
27     TOKEN_ALL_ACCESS
28 };
29 
30 /* PRIVATE FUNCTIONS *****************************************************************/
31 
32 /**
33  * @brief
34  * Creates a lock for the token.
35  *
36  * @param[in,out] Token
37  * A token which lock has to be created.
38  *
39  * @return
40  * STATUS_SUCCESS if the pool allocation and resource initialisation have
41  * completed successfully, otherwise STATUS_INSUFFICIENT_RESOURCES on a
42  * pool allocation failure.
43  */
44 NTSTATUS
SepCreateTokenLock(_Inout_ PTOKEN Token)45 SepCreateTokenLock(
46     _Inout_ PTOKEN Token)
47 {
48     PAGED_CODE();
49 
50     Token->TokenLock = ExAllocatePoolWithTag(NonPagedPool,
51                                              sizeof(ERESOURCE),
52                                              TAG_SE_TOKEN_LOCK);
53     if (Token->TokenLock == NULL)
54     {
55         DPRINT1("SepCreateTokenLock(): Failed to allocate memory!\n");
56         return STATUS_INSUFFICIENT_RESOURCES;
57     }
58 
59     ExInitializeResourceLite(Token->TokenLock);
60     return STATUS_SUCCESS;
61 }
62 
63 /**
64  * @brief
65  * Deletes a lock of a token.
66  *
67  * @param[in,out] Token
68  * A token which contains the lock.
69  *
70  * @return
71  * Nothing.
72  */
73 VOID
SepDeleteTokenLock(_Inout_ PTOKEN Token)74 SepDeleteTokenLock(
75     _Inout_ PTOKEN Token)
76 {
77     PAGED_CODE();
78 
79     ExDeleteResourceLite(Token->TokenLock);
80     ExFreePoolWithTag(Token->TokenLock, TAG_SE_TOKEN_LOCK);
81 }
82 
83 /**
84  * @brief
85  * Compares the elements of SID arrays provided by tokens.
86  * The elements that are being compared for equality are
87  * the SIDs and their attributes.
88  *
89  * @param[in] SidArrayToken1
90  * SID array from the first token.
91  *
92  * @param[in] CountSidArray1
93  * SID count array from the first token.
94  *
95  * @param[in] SidArrayToken2
96  * SID array from the second token.
97  *
98  * @param[in] CountSidArray2
99  * SID count array from the second token.
100  *
101  * @return
102  * Returns TRUE if the elements match from either arrays,
103  * FALSE otherwise.
104  */
105 static
106 BOOLEAN
SepCompareSidAndAttributesFromTokens(_In_ PSID_AND_ATTRIBUTES SidArrayToken1,_In_ ULONG CountSidArray1,_In_ PSID_AND_ATTRIBUTES SidArrayToken2,_In_ ULONG CountSidArray2)107 SepCompareSidAndAttributesFromTokens(
108     _In_ PSID_AND_ATTRIBUTES SidArrayToken1,
109     _In_ ULONG CountSidArray1,
110     _In_ PSID_AND_ATTRIBUTES SidArrayToken2,
111     _In_ ULONG CountSidArray2)
112 {
113     ULONG FirstCount, SecondCount;
114     PSID_AND_ATTRIBUTES FirstSidArray, SecondSidArray;
115     PAGED_CODE();
116 
117     /* Bail out if index counters provided are not equal */
118     if (CountSidArray1 != CountSidArray2)
119     {
120         DPRINT("SepCompareSidAndAttributesFromTokens(): Index counters are not the same!\n");
121         return FALSE;
122     }
123 
124     /* Loop over the SID arrays and compare them */
125     for (FirstCount = 0; FirstCount < CountSidArray1; FirstCount++)
126     {
127         for (SecondCount = 0; SecondCount < CountSidArray2; SecondCount++)
128         {
129             FirstSidArray = &SidArrayToken1[FirstCount];
130             SecondSidArray = &SidArrayToken2[SecondCount];
131 
132             if (RtlEqualSid(FirstSidArray->Sid, SecondSidArray->Sid) &&
133                 FirstSidArray->Attributes == SecondSidArray->Attributes)
134             {
135                 break;
136             }
137         }
138 
139         /* We've exhausted the array of the second token without finding this one */
140         if (SecondCount == CountSidArray2)
141         {
142             DPRINT("SepCompareSidAndAttributesFromTokens(): No matching elements could be found in either token!\n");
143             return FALSE;
144         }
145     }
146 
147     return TRUE;
148 }
149 
150 /**
151  * @brief
152  * Compares the elements of privilege arrays provided by tokens.
153  * The elements that are being compared for equality are
154  * the privileges and their attributes.
155  *
156  * @param[in] PrivArrayToken1
157  * Privilege array from the first token.
158  *
159  * @param[in] CountPrivArray1
160  * Privilege count array from the first token.
161  *
162  * @param[in] PrivArrayToken2
163  * Privilege array from the second token.
164  *
165  * @param[in] CountPrivArray2
166  * Privilege count array from the second token.
167  *
168  * @return
169  * Returns TRUE if the elements match from either arrays,
170  * FALSE otherwise.
171  */
172 static
173 BOOLEAN
SepComparePrivilegeAndAttributesFromTokens(_In_ PLUID_AND_ATTRIBUTES PrivArrayToken1,_In_ ULONG CountPrivArray1,_In_ PLUID_AND_ATTRIBUTES PrivArrayToken2,_In_ ULONG CountPrivArray2)174 SepComparePrivilegeAndAttributesFromTokens(
175     _In_ PLUID_AND_ATTRIBUTES PrivArrayToken1,
176     _In_ ULONG CountPrivArray1,
177     _In_ PLUID_AND_ATTRIBUTES PrivArrayToken2,
178     _In_ ULONG CountPrivArray2)
179 {
180     ULONG FirstCount, SecondCount;
181     PLUID_AND_ATTRIBUTES FirstPrivArray, SecondPrivArray;
182     PAGED_CODE();
183 
184     /* Bail out if index counters provided are not equal */
185     if (CountPrivArray1 != CountPrivArray2)
186     {
187         DPRINT("SepComparePrivilegeAndAttributesFromTokens(): Index counters are not the same!\n");
188         return FALSE;
189     }
190 
191     /* Loop over the privilege arrays and compare them */
192     for (FirstCount = 0; FirstCount < CountPrivArray1; FirstCount++)
193     {
194         for (SecondCount = 0; SecondCount < CountPrivArray2; SecondCount++)
195         {
196             FirstPrivArray = &PrivArrayToken1[FirstCount];
197             SecondPrivArray = &PrivArrayToken2[SecondCount];
198 
199             if (RtlEqualLuid(&FirstPrivArray->Luid, &SecondPrivArray->Luid) &&
200                 FirstPrivArray->Attributes == SecondPrivArray->Attributes)
201             {
202                 break;
203             }
204         }
205 
206         /* We've exhausted the array of the second token without finding this one */
207         if (SecondCount == CountPrivArray2)
208         {
209             DPRINT("SepComparePrivilegeAndAttributesFromTokens(): No matching elements could be found in either token!\n");
210             return FALSE;
211         }
212     }
213 
214     return TRUE;
215 }
216 
217 /**
218  * @brief
219  * Compares tokens if they're equal based on all the following properties. If all
220  * of the said conditions are met then the tokens are deemed as equal.
221  *
222  * - Every SID that is present in either token is also present in the other one.
223  * - Both or none of the tokens are restricted.
224  * - If both tokens are restricted, every SID that is restricted in either token is
225  *   also restricted in the other one.
226  * - Every privilege present in either token is also present in the other one.
227  *
228  * @param[in] FirstToken
229  * The first token.
230  *
231  * @param[in] SecondToken
232  * The second token.
233  *
234  * @param[out] Equal
235  * The retrieved value which determines if the tokens are
236  * equal or not.
237  *
238  * @return
239  * Returns STATUS_SUCCESS.
240  */
241 static
242 NTSTATUS
SepCompareTokens(_In_ PTOKEN FirstToken,_In_ PTOKEN SecondToken,_Out_ PBOOLEAN Equal)243 SepCompareTokens(
244     _In_ PTOKEN FirstToken,
245     _In_ PTOKEN SecondToken,
246     _Out_ PBOOLEAN Equal)
247 {
248     BOOLEAN Restricted, IsEqual = FALSE;
249     PAGED_CODE();
250 
251     ASSERT(FirstToken != SecondToken);
252 
253     /* Lock the tokens */
254     SepAcquireTokenLockShared(FirstToken);
255     SepAcquireTokenLockShared(SecondToken);
256 
257     /* Check if every SID that is present in either token is also present in the other one */
258     if (!SepCompareSidAndAttributesFromTokens(FirstToken->UserAndGroups,
259                                               FirstToken->UserAndGroupCount,
260                                               SecondToken->UserAndGroups,
261                                               SecondToken->UserAndGroupCount))
262     {
263         goto Quit;
264     }
265 
266     /* Is one token restricted but the other isn't? */
267     Restricted = SeTokenIsRestricted(FirstToken);
268     if (Restricted != SeTokenIsRestricted(SecondToken))
269     {
270         /* If that's the case then bail out */
271         goto Quit;
272     }
273 
274     /*
275      * If both tokens are restricted check if every SID
276      * that is restricted in either token is also restricted
277      * in the other one.
278      */
279     if (Restricted)
280     {
281         if (!SepCompareSidAndAttributesFromTokens(FirstToken->RestrictedSids,
282                                                   FirstToken->RestrictedSidCount,
283                                                   SecondToken->RestrictedSids,
284                                                   SecondToken->RestrictedSidCount))
285         {
286             goto Quit;
287         }
288     }
289 
290     /* Check if every privilege present in either token is also present in the other one */
291     if (!SepComparePrivilegeAndAttributesFromTokens(FirstToken->Privileges,
292                                                     FirstToken->PrivilegeCount,
293                                                     SecondToken->Privileges,
294                                                     SecondToken->PrivilegeCount))
295     {
296         goto Quit;
297     }
298 
299     /* If we're here then the tokens are equal */
300     IsEqual = TRUE;
301     DPRINT("SepCompareTokens(): Tokens are equal!\n");
302 
303 Quit:
304     /* Unlock the tokens */
305     SepReleaseTokenLock(SecondToken);
306     SepReleaseTokenLock(FirstToken);
307 
308     *Equal = IsEqual;
309     return STATUS_SUCCESS;
310 }
311 
312 /**
313  * @brief
314  * Private function that impersonates the system's anonymous logon token.
315  * The major bulk of the impersonation procedure is done here.
316  *
317  * @param[in] Thread
318  * The executive thread object that is to impersonate the client.
319  *
320  * @param[in] PreviousMode
321  * The access processor mode, indicating if the call is executed
322  * in kernel or user mode.
323  *
324  * @return
325  * Returns STATUS_SUCCESS if the impersonation has succeeded.
326  * STATUS_UNSUCCESSFUL is returned if the primary token couldn't be
327  * obtained from the current process to perform additional tasks.
328  * STATUS_ACCESS_DENIED is returned if the process' primary token is
329  * restricted, which for this matter we cannot impersonate onto a
330  * restricted process. Otherwise a failure NTSTATUS code is returned.
331  */
332 static
333 NTSTATUS
SepImpersonateAnonymousToken(_In_ PETHREAD Thread,_In_ KPROCESSOR_MODE PreviousMode)334 SepImpersonateAnonymousToken(
335     _In_ PETHREAD Thread,
336     _In_ KPROCESSOR_MODE PreviousMode)
337 {
338     NTSTATUS Status;
339     PTOKEN TokenToImpersonate, ProcessToken;
340     ULONG IncludeEveryoneValueData;
341     PAGED_CODE();
342 
343     /*
344      * We must check first which kind of token
345      * shall we assign for the thread to impersonate,
346      * the one with Everyone Group SID or the other
347      * without. Invoke the registry helper to
348      * return the data value for us.
349      */
350     Status = SepRegQueryHelper(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Lsa",
351                                L"EveryoneIncludesAnonymous",
352                                REG_DWORD,
353                                sizeof(IncludeEveryoneValueData),
354                                &IncludeEveryoneValueData);
355     if (!NT_SUCCESS(Status))
356     {
357         DPRINT1("SepRegQueryHelper(): Failed to query the registry value (Status 0x%lx)\n", Status);
358         return Status;
359     }
360 
361     if (IncludeEveryoneValueData == 0)
362     {
363         DPRINT("SepImpersonateAnonymousToken(): Assigning the token not including the Everyone Group SID...\n");
364         TokenToImpersonate = SeAnonymousLogonTokenNoEveryone;
365     }
366     else
367     {
368         DPRINT("SepImpersonateAnonymousToken(): Assigning the token including the Everyone Group SID...\n");
369         TokenToImpersonate = SeAnonymousLogonToken;
370     }
371 
372     /*
373      * Tell the object manager that we're going to use this token
374      * object now by incrementing the reference count.
375     */
376     Status = ObReferenceObjectByPointer(TokenToImpersonate,
377                                         TOKEN_IMPERSONATE,
378                                         SeTokenObjectType,
379                                         PreviousMode);
380     if (!NT_SUCCESS(Status))
381     {
382         DPRINT1("SepImpersonateAnonymousToken(): Couldn't be able to use the token, bail out...\n");
383         return Status;
384     }
385 
386     /*
387      * Reference the primary token of the current process that the anonymous
388      * logon token impersonation procedure is being performed. We'll be going
389      * to use the process' token to figure out if the process is actually
390      * restricted or not.
391      */
392     ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess());
393     if (!ProcessToken)
394     {
395         DPRINT1("SepImpersonateAnonymousToken(): Couldn't be able to get the process' primary token, bail out...\n");
396         ObDereferenceObject(TokenToImpersonate);
397         return STATUS_UNSUCCESSFUL;
398     }
399 
400     /* Now, is the token from the current process restricted? */
401     if (SeTokenIsRestricted(ProcessToken))
402     {
403         DPRINT1("SepImpersonateAnonymousToken(): The process is restricted, can't do anything. Bail out...\n");
404         PsDereferencePrimaryToken(ProcessToken);
405         ObDereferenceObject(TokenToImpersonate);
406         return STATUS_ACCESS_DENIED;
407     }
408 
409     /*
410      * Finally it's time to impersonate! But first, fast dereference the
411      * process' primary token as we no longer need it.
412      */
413     ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken);
414     Status = PsImpersonateClient(Thread, TokenToImpersonate, TRUE, FALSE, SecurityImpersonation);
415     if (!NT_SUCCESS(Status))
416     {
417         DPRINT1("SepImpersonateAnonymousToken(): Failed to impersonate, bail out...\n");
418         ObDereferenceObject(TokenToImpersonate);
419         return Status;
420     }
421 
422     return Status;
423 }
424 
425 /**
426  * @brief
427  * Updates the token's flags based upon the privilege that the token
428  * has been granted. The flag can either be taken out or given to the token
429  * if the attributes of the specified privilege is enabled or not.
430  *
431  * @param[in,out] Token
432  * The token where the flags are to be changed.
433  *
434  * @param[in] Index
435  * The index count which represents the total sum of privileges. The count in question
436  * MUST NOT exceed the expected privileges count of the token.
437  *
438  * @return
439  * Nothing.
440  */
441 VOID
SepUpdateSinglePrivilegeFlagToken(_Inout_ PTOKEN Token,_In_ ULONG Index)442 SepUpdateSinglePrivilegeFlagToken(
443     _Inout_ PTOKEN Token,
444     _In_ ULONG Index)
445 {
446     ULONG TokenFlag;
447     ASSERT(Index < Token->PrivilegeCount);
448 
449     /* The high part of all values we are interested in is 0 */
450     if (Token->Privileges[Index].Luid.HighPart != 0)
451     {
452         return;
453     }
454 
455     /* Check for certain privileges to update flags */
456     if (Token->Privileges[Index].Luid.LowPart == SE_CHANGE_NOTIFY_PRIVILEGE)
457     {
458         TokenFlag = TOKEN_HAS_TRAVERSE_PRIVILEGE;
459     }
460     else if (Token->Privileges[Index].Luid.LowPart == SE_BACKUP_PRIVILEGE)
461     {
462         TokenFlag = TOKEN_HAS_BACKUP_PRIVILEGE;
463     }
464     else if (Token->Privileges[Index].Luid.LowPart == SE_RESTORE_PRIVILEGE)
465     {
466         TokenFlag = TOKEN_HAS_RESTORE_PRIVILEGE;
467     }
468     else if (Token->Privileges[Index].Luid.LowPart == SE_IMPERSONATE_PRIVILEGE)
469     {
470         TokenFlag = TOKEN_HAS_IMPERSONATE_PRIVILEGE;
471     }
472     else
473     {
474         /* Nothing to do */
475         return;
476     }
477 
478     /* Check if the specified privilege is enabled */
479     if (Token->Privileges[Index].Attributes & SE_PRIVILEGE_ENABLED)
480     {
481         /* It is enabled, so set the flag */
482         Token->TokenFlags |= TokenFlag;
483     }
484     else
485     {
486         /* Is is disabled, so remove the flag */
487         Token->TokenFlags &= ~TokenFlag;
488     }
489 }
490 
491 /**
492  * @brief
493  * Checks if a token belongs to the main user, being the owner.
494  *
495  * @param[in] _Token
496  * A valid token object.
497  *
498  * @param[in] SecurityDescriptor
499  * A security descriptor where the owner is to be found.
500  *
501  * @param[in] TokenLocked
502  * If set to TRUE, the token has been already locked and there's
503  * no need to lock it again. Otherwise the function will acquire
504  * the lock.
505  *
506  * @return
507  * Returns TRUE if the token belongs to a owner, FALSE otherwise.
508  */
509 BOOLEAN
510 NTAPI
SepTokenIsOwner(_In_ PACCESS_TOKEN _Token,_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,_In_ BOOLEAN TokenLocked)511 SepTokenIsOwner(
512     _In_ PACCESS_TOKEN _Token,
513     _In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
514     _In_ BOOLEAN TokenLocked)
515 {
516     PSID Sid;
517     BOOLEAN Result;
518     PTOKEN Token = _Token;
519 
520     /* Get the owner SID */
521     Sid = SepGetOwnerFromDescriptor(SecurityDescriptor);
522     ASSERT(Sid != NULL);
523 
524     /* Lock the token if needed */
525     if (!TokenLocked) SepAcquireTokenLockShared(Token);
526 
527     /* Check if the owner SID is found, handling restricted case as well */
528     Result = SepSidInToken(Token, Sid);
529     if ((Result) && (Token->TokenFlags & TOKEN_IS_RESTRICTED))
530     {
531         Result = SepSidInTokenEx(Token, NULL, Sid, FALSE, TRUE);
532     }
533 
534     /* Release the lock if we had acquired it */
535     if (!TokenLocked) SepReleaseTokenLock(Token);
536 
537     /* Return the result */
538     return Result;
539 }
540 
541 /**
542  * @brief
543  * Updates the token's flags based upon the privilege that the token
544  * has been granted. The function uses the private helper, SepUpdateSinglePrivilegeFlagToken,
545  * in order to update the flags of a token.
546  *
547  * @param[in,out] Token
548  * The token where the flags are to be changed.
549  *
550  * @return
551  * Nothing.
552  */
553 VOID
SepUpdatePrivilegeFlagsToken(_Inout_ PTOKEN Token)554 SepUpdatePrivilegeFlagsToken(
555     _Inout_ PTOKEN Token)
556 {
557     ULONG i;
558 
559     /* Loop all privileges */
560     for (i = 0; i < Token->PrivilegeCount; i++)
561     {
562         /* Updates the flags for this privilege */
563         SepUpdateSinglePrivilegeFlagToken(Token, i);
564     }
565 }
566 
567 /**
568  * @brief
569  * Removes a privilege from the token.
570  *
571  * @param[in,out] Token
572  * The token where the privilege is to be removed.
573  *
574  * @param[in] Index
575  * The index count which represents the number position of the privilege
576  * we want to remove.
577  *
578  * @return
579  * Nothing.
580  */
581 VOID
SepRemovePrivilegeToken(_Inout_ PTOKEN Token,_In_ ULONG Index)582 SepRemovePrivilegeToken(
583     _Inout_ PTOKEN Token,
584     _In_ ULONG Index)
585 {
586     ULONG MoveCount;
587     ASSERT(Index < Token->PrivilegeCount);
588 
589     /* Calculate the number of trailing privileges */
590     MoveCount = Token->PrivilegeCount - Index - 1;
591     if (MoveCount != 0)
592     {
593         /* Move them one location ahead */
594         RtlMoveMemory(&Token->Privileges[Index],
595                       &Token->Privileges[Index + 1],
596                       MoveCount * sizeof(LUID_AND_ATTRIBUTES));
597     }
598 
599     /* Update privilege count */
600     Token->PrivilegeCount--;
601 }
602 
603 /**
604  * @brief
605  * Removes a group from the token.
606  *
607  * @param[in,out] Token
608  * The token where the group is to be removed.
609  *
610  * @param[in] Index
611  * The index count which represents the number position of the group
612  * we want to remove.
613  *
614  * @return
615  * Nothing.
616  */
617 VOID
SepRemoveUserGroupToken(_Inout_ PTOKEN Token,_In_ ULONG Index)618 SepRemoveUserGroupToken(
619     _Inout_ PTOKEN Token,
620     _In_ ULONG Index)
621 {
622     ULONG MoveCount;
623     ASSERT(Index < Token->UserAndGroupCount);
624 
625     /* Calculate the number of trailing groups */
626     MoveCount = Token->UserAndGroupCount - Index - 1;
627     if (MoveCount != 0)
628     {
629         /* Time to remove the group by moving one location ahead */
630         RtlMoveMemory(&Token->UserAndGroups[Index],
631                       &Token->UserAndGroups[Index + 1],
632                       MoveCount * sizeof(SID_AND_ATTRIBUTES));
633     }
634 
635     /* Remove one group count */
636     Token->UserAndGroupCount--;
637 }
638 
639 /**
640  * @brief
641  * Computes the exact available dynamic area of an access
642  * token whilst querying token statistics.
643  *
644  * @param[in] DynamicCharged
645  * The current charged dynamic area of an access token.
646  * This must not be 0!
647  *
648  * @param[in] PrimaryGroup
649  * A pointer to a primary group SID.
650  *
651  * @param[in] DefaultDacl
652  * If provided, this pointer points to a default DACL of an
653  * access token.
654  *
655  * @return
656  * Returns the calculated available dynamic area.
657  */
658 ULONG
SepComputeAvailableDynamicSpace(_In_ ULONG DynamicCharged,_In_ PSID PrimaryGroup,_In_opt_ PACL DefaultDacl)659 SepComputeAvailableDynamicSpace(
660     _In_ ULONG DynamicCharged,
661     _In_ PSID PrimaryGroup,
662     _In_opt_ PACL DefaultDacl)
663 {
664     ULONG DynamicAvailable;
665 
666     PAGED_CODE();
667 
668     /* A token's dynamic area is always charged */
669     ASSERT(DynamicCharged != 0);
670 
671     /*
672      * Take into account the default DACL if
673      * the token has one. Otherwise the occupied
674      * space is just the present primary group.
675      */
676     DynamicAvailable = DynamicCharged - RtlLengthSid(PrimaryGroup);
677     if (DefaultDacl)
678     {
679         DynamicAvailable -= DefaultDacl->AclSize;
680     }
681 
682     return DynamicAvailable;
683 }
684 
685 /**
686  * @brief
687  * Re-builds the dynamic part area of an access token
688  * during an a default DACL or primary group replacement
689  * within the said token if the said dynamic area can't
690  * hold the new security content.
691  *
692  * @param[in] AccessToken
693  * A pointer to an access token where its dynamic part
694  * is to be re-built and expanded based upon the new
695  * dynamic part size provided by the caller. Dynamic
696  * part expansion is not always guaranteed. See Remarks
697  * for further information.
698  *
699  * @param[in] NewDynamicPartSize
700  * The new dynamic part size.
701  *
702  * @return
703  * Returns STATUS_SUCCESS if the function has completed its
704  * operations successfully. STATUS_INSUFFICIENT_RESOURCES
705  * is returned if the new dynamic part could not be allocated.
706  *
707  * @remarks
708  * STATUS_SUCCESS does not indicate if the function has re-built
709  * the dynamic part of a token. If the current dynamic area size
710  * suffices the new dynamic area length provided by the caller
711  * then the dynamic area can hold the new security content buffer
712  * so dynamic part expansion is not necessary.
713  */
714 NTSTATUS
SepRebuildDynamicPartOfToken(_Inout_ PTOKEN AccessToken,_In_ ULONG NewDynamicPartSize)715 SepRebuildDynamicPartOfToken(
716     _Inout_ PTOKEN AccessToken,
717     _In_ ULONG NewDynamicPartSize)
718 {
719     PVOID NewDynamicPart;
720     PVOID PreviousDynamicPart;
721     ULONG CurrentDynamicLength;
722 
723     PAGED_CODE();
724 
725     /* Sanity checks */
726     ASSERT(AccessToken);
727     ASSERT(NewDynamicPartSize != 0);
728 
729     /*
730      * Compute the exact length of the available
731      * dynamic part of the access token.
732      */
733     CurrentDynamicLength = AccessToken->DynamicAvailable + RtlLengthSid(AccessToken->PrimaryGroup);
734     if (AccessToken->DefaultDacl)
735     {
736         CurrentDynamicLength += AccessToken->DefaultDacl->AclSize;
737     }
738 
739     /*
740      * Figure out if the current dynamic part is too small
741      * to fit new contents inside the said dynamic part.
742      * Rebuild the dynamic area and expand it if necessary.
743      */
744     if (CurrentDynamicLength < NewDynamicPartSize)
745     {
746         NewDynamicPart = ExAllocatePoolWithTag(PagedPool,
747                                                NewDynamicPartSize,
748                                                TAG_TOKEN_DYNAMIC);
749         if (NewDynamicPart == NULL)
750         {
751             DPRINT1("SepRebuildDynamicPartOfToken(): Insufficient resources to allocate new dynamic part!\n");
752             return STATUS_INSUFFICIENT_RESOURCES;
753         }
754 
755         /* Copy the existing dynamic part */
756         PreviousDynamicPart = AccessToken->DynamicPart;
757         RtlCopyMemory(NewDynamicPart, PreviousDynamicPart, CurrentDynamicLength);
758 
759         /* Update the available dynamic area and assign new dynamic */
760         AccessToken->DynamicAvailable += NewDynamicPartSize - CurrentDynamicLength;
761         AccessToken->DynamicPart = NewDynamicPart;
762 
763         /* Move the contents (primary group and default DACL) addresses as well */
764         AccessToken->PrimaryGroup = (PSID)((ULONG_PTR)AccessToken->DynamicPart +
765                                     ((ULONG_PTR)AccessToken->PrimaryGroup - (ULONG_PTR)PreviousDynamicPart));
766         if (AccessToken->DefaultDacl != NULL)
767         {
768             AccessToken->DefaultDacl = (PACL)((ULONG_PTR)AccessToken->DynamicPart +
769                                        ((ULONG_PTR)AccessToken->DefaultDacl - (ULONG_PTR)PreviousDynamicPart));
770         }
771 
772         /* And discard the previous dynamic part */
773         DPRINT("SepRebuildDynamicPartOfToken(): The dynamic part has been re-built with success!\n");
774         ExFreePoolWithTag(PreviousDynamicPart, TAG_TOKEN_DYNAMIC);
775     }
776 
777     return STATUS_SUCCESS;
778 }
779 
780 /**
781  * @unimplemented
782  * @brief
783  * Frees (de-allocates) the proxy data memory block of a token.
784  *
785  * @param[in,out] ProxyData
786  * The proxy data to be freed.
787  *
788  * @return
789  * Nothing.
790  */
791 VOID
792 NTAPI
SepFreeProxyData(_Inout_ PVOID ProxyData)793 SepFreeProxyData(
794     _Inout_ PVOID ProxyData)
795 {
796     UNIMPLEMENTED;
797 }
798 
799 /**
800  * @unimplemented
801  * @brief
802  * Copies the proxy data from the source into the destination of a token.
803  *
804  * @param[out] Dest
805  * The destination path where the proxy data is to be copied to.
806  *
807  * @param[in] Src
808  * The source path where the proxy data is be copied from.
809  *
810  * @return
811  * To be added...
812  */
813 NTSTATUS
814 NTAPI
SepCopyProxyData(_Out_ PVOID * Dest,_In_ PVOID Src)815 SepCopyProxyData(
816     _Out_ PVOID* Dest,
817     _In_ PVOID Src)
818 {
819     UNIMPLEMENTED;
820     return STATUS_NOT_IMPLEMENTED;
821 }
822 
823 /**
824  * @brief
825  * Replaces the old access token of a process (pointed by the EPROCESS kernel structure) with a
826  * new access token. The new access token must be a primary token for use.
827  *
828  * @param[in] Process
829  * The process instance where its access token is about to be replaced.
830  *
831  * @param[in] NewAccessToken
832  * The new token that it's going to replace the old one.
833  *
834  * @param[out] OldAccessToken
835  * The returned old token that's been replaced, which the caller can do anything.
836  *
837  * @return
838  * Returns STATUS_SUCCESS if the exchange operation between tokens has completed successfully.
839  * STATUS_BAD_TOKEN_TYPE is returned if the new token is not a primary one so that we cannot
840  * exchange it with the old one from the process. STATUS_TOKEN_ALREADY_IN_USE is returned if
841  * both tokens aren't equal which means one of them has different properties (groups, privileges, etc.)
842  * and as such one of them is currently in use. A failure NTSTATUS code is returned otherwise.
843  */
844 NTSTATUS
845 NTAPI
SeExchangePrimaryToken(_In_ PEPROCESS Process,_In_ PACCESS_TOKEN NewAccessToken,_Out_ PACCESS_TOKEN * OldAccessToken)846 SeExchangePrimaryToken(
847     _In_ PEPROCESS Process,
848     _In_ PACCESS_TOKEN NewAccessToken,
849     _Out_ PACCESS_TOKEN* OldAccessToken)
850 {
851     PTOKEN OldToken;
852     PTOKEN NewToken = (PTOKEN)NewAccessToken;
853 
854     PAGED_CODE();
855 
856     if (NewToken->TokenType != TokenPrimary)
857         return STATUS_BAD_TOKEN_TYPE;
858 
859     if (NewToken->TokenInUse)
860     {
861         BOOLEAN IsEqual;
862         NTSTATUS Status;
863 
864         /* Maybe we're trying to set the same token */
865         OldToken = PsReferencePrimaryToken(Process);
866         if (OldToken == NewToken)
867         {
868             /* So it's a nop. */
869             *OldAccessToken = OldToken;
870             return STATUS_SUCCESS;
871         }
872 
873         Status = SepCompareTokens(OldToken, NewToken, &IsEqual);
874         if (!NT_SUCCESS(Status))
875         {
876             PsDereferencePrimaryToken(OldToken);
877             *OldAccessToken = NULL;
878             return Status;
879         }
880 
881         if (!IsEqual)
882         {
883             PsDereferencePrimaryToken(OldToken);
884             *OldAccessToken = NULL;
885             return STATUS_TOKEN_ALREADY_IN_USE;
886         }
887         /* Silently return STATUS_SUCCESS but do not set the new token,
888          * as it's already in use elsewhere. */
889         *OldAccessToken = OldToken;
890         return STATUS_SUCCESS;
891     }
892 
893     /* Lock the new token */
894     SepAcquireTokenLockExclusive(NewToken);
895 
896     /* Mark new token in use */
897     NewToken->TokenInUse = TRUE;
898 
899     /* Set the session ID for the new token */
900     NewToken->SessionId = MmGetSessionId(Process);
901 
902     /* Unlock the new token */
903     SepReleaseTokenLock(NewToken);
904 
905     /* Reference the new token */
906     ObReferenceObject(NewToken);
907 
908     /* Replace the old with the new */
909     OldToken = ObFastReplaceObject(&Process->Token, NewToken);
910 
911     /* Lock the old token */
912     SepAcquireTokenLockExclusive(OldToken);
913 
914     /* Mark the old token as free */
915     OldToken->TokenInUse = FALSE;
916 
917     /* Unlock the old token */
918     SepReleaseTokenLock(OldToken);
919 
920     *OldAccessToken = (PACCESS_TOKEN)OldToken;
921     return STATUS_SUCCESS;
922 }
923 
924 /**
925  * @brief
926  * Removes the primary token of a process.
927  *
928  * @param[in,out] Process
929  * The process instance with the access token to be removed.
930  *
931  * @return
932  * Nothing.
933  */
934 VOID
935 NTAPI
SeDeassignPrimaryToken(_Inout_ PEPROCESS Process)936 SeDeassignPrimaryToken(
937     _Inout_ PEPROCESS Process)
938 {
939     PTOKEN OldToken;
940 
941     /* Remove the Token */
942     OldToken = ObFastReplaceObject(&Process->Token, NULL);
943 
944     /* Mark the Old Token as free */
945     OldToken->TokenInUse = FALSE;
946 
947     /* Dereference the Token */
948     ObDereferenceObject(OldToken);
949 }
950 
951 /**
952  * @brief
953  * Computes the length size of a SID.
954  *
955  * @param[in] Count
956  * Total count of entries that have SIDs in them (that being PSID_AND_ATTRIBUTES in this context).
957  *
958  * @param[in] Src
959  * Source that points to the attributes and SID entry structure.
960  *
961  * @return
962  * Returns the total length of a SID size.
963  */
964 ULONG
RtlLengthSidAndAttributes(_In_ ULONG Count,_In_ PSID_AND_ATTRIBUTES Src)965 RtlLengthSidAndAttributes(
966     _In_ ULONG Count,
967     _In_ PSID_AND_ATTRIBUTES Src)
968 {
969     ULONG i;
970     ULONG uLength;
971 
972     PAGED_CODE();
973 
974     uLength = Count * sizeof(SID_AND_ATTRIBUTES);
975     for (i = 0; i < Count; i++)
976         uLength += RtlLengthSid(Src[i].Sid);
977 
978     return uLength;
979 }
980 
981 /**
982  * @brief
983  * Finds the primary group and default owner entity based on the submitted primary group instance
984  * and an access token.
985  *
986  * @param[in] Token
987  * Access token to begin the search query of primary group and default owner.
988  *
989  * @param[in] PrimaryGroup
990  * A primary group SID to be used for search query, determining if user & groups of a token
991  * and the submitted primary group do match.
992  *
993  * @param[in] DefaultOwner
994  * The default owner. If specified, it's used to determine if the token belongs to the actual user,
995  * that is, being the owner himself.
996  *
997  * @param[out] PrimaryGroupIndex
998  * Returns the primary group index.
999  *
1000  * @param[out] DefaultOwnerIndex
1001  * Returns the default owner index.
1002  *
1003  * @return
1004  * Returns STATUS_SUCCESS if the find query operation has completed successfully and that at least one
1005  * search result is requested by the caller. STATUS_INVALID_PARAMETER is returned if the caller hasn't requested
1006  * any search result. STATUS_INVALID_OWNER is returned if the specified default user owner does not match with the other
1007  * user from the token. STATUS_INVALID_PRIMARY_GROUP is returned if the specified default primary group does not match with the
1008  * other group from the token.
1009  */
1010 NTSTATUS
SepFindPrimaryGroupAndDefaultOwner(_In_ PTOKEN Token,_In_ PSID PrimaryGroup,_In_opt_ PSID DefaultOwner,_Out_opt_ PULONG PrimaryGroupIndex,_Out_opt_ PULONG DefaultOwnerIndex)1011 SepFindPrimaryGroupAndDefaultOwner(
1012     _In_ PTOKEN Token,
1013     _In_ PSID PrimaryGroup,
1014     _In_opt_ PSID DefaultOwner,
1015     _Out_opt_ PULONG PrimaryGroupIndex,
1016     _Out_opt_ PULONG DefaultOwnerIndex)
1017 {
1018     ULONG i;
1019 
1020     /* We should return at least a search result */
1021     if (!PrimaryGroupIndex && !DefaultOwnerIndex)
1022         return STATUS_INVALID_PARAMETER;
1023 
1024     if (PrimaryGroupIndex)
1025     {
1026         /* Initialize with an invalid index */
1027         // Token->PrimaryGroup = NULL;
1028         *PrimaryGroupIndex = Token->UserAndGroupCount;
1029     }
1030 
1031     if (DefaultOwnerIndex)
1032     {
1033         if (DefaultOwner)
1034         {
1035             /* An owner is specified: check whether this is actually the user */
1036             if (RtlEqualSid(Token->UserAndGroups[0].Sid, DefaultOwner))
1037             {
1038                 /*
1039                  * It's the user (first element in array): set it
1040                  * as the owner and stop the search for it.
1041                  */
1042                 *DefaultOwnerIndex = 0;
1043                 DefaultOwnerIndex = NULL;
1044             }
1045             else
1046             {
1047                 /* An owner is specified: initialize with an invalid index */
1048                 *DefaultOwnerIndex = Token->UserAndGroupCount;
1049             }
1050         }
1051         else
1052         {
1053             /*
1054              * No owner specified: set the user (first element in array)
1055              * as the owner and stop the search for it.
1056              */
1057             *DefaultOwnerIndex = 0;
1058             DefaultOwnerIndex = NULL;
1059         }
1060     }
1061 
1062     /* Validate and set the primary group and default owner indices */
1063     for (i = 0; i < Token->UserAndGroupCount; i++)
1064     {
1065         /* Stop the search if we have found what we searched for */
1066         if (!PrimaryGroupIndex && !DefaultOwnerIndex)
1067             break;
1068 
1069         if (DefaultOwnerIndex && DefaultOwner &&
1070             RtlEqualSid(Token->UserAndGroups[i].Sid, DefaultOwner) &&
1071             (Token->UserAndGroups[i].Attributes & SE_GROUP_OWNER))
1072         {
1073             /* Owner is found, stop the search for it */
1074             *DefaultOwnerIndex = i;
1075             DefaultOwnerIndex = NULL;
1076         }
1077 
1078         if (PrimaryGroupIndex &&
1079             RtlEqualSid(Token->UserAndGroups[i].Sid, PrimaryGroup))
1080         {
1081             /* Primary group is found, stop the search for it */
1082             // Token->PrimaryGroup = Token->UserAndGroups[i].Sid;
1083             *PrimaryGroupIndex = i;
1084             PrimaryGroupIndex = NULL;
1085         }
1086     }
1087 
1088     if (DefaultOwnerIndex)
1089     {
1090         if (*DefaultOwnerIndex == Token->UserAndGroupCount)
1091             return STATUS_INVALID_OWNER;
1092     }
1093 
1094     if (PrimaryGroupIndex)
1095     {
1096         if (*PrimaryGroupIndex == Token->UserAndGroupCount)
1097         // if (Token->PrimaryGroup == NULL)
1098             return STATUS_INVALID_PRIMARY_GROUP;
1099     }
1100 
1101     return STATUS_SUCCESS;
1102 }
1103 
1104 /**
1105  * @brief
1106  * Internal private function that returns an opened handle
1107  * of an access token associated with a thread.
1108  *
1109  * @param[in] Thread
1110  * A pointer to a Executive thread. This parameter is used to
1111  * validate that the newly obtained thread in this function
1112  * hasn't diverged. This could potentially lead to a scenario
1113  * that we might get an access token from a different token
1114  * which is not what we want. The validation is performed
1115  * if the token has to copied and can't be opened directly.
1116  *
1117  * @param[in] ThreadHandle
1118  * A handle to a thread, of which an access token is to be opened
1119  * and given from that thread.
1120  *
1121  * @param[in] ThreadToken
1122  * A pointer to an access token associated with the specific thread.
1123  * The function assumes that the token is an impersonation one
1124  * prior the calling of this function.
1125  *
1126  * @param[in] DesiredAccess
1127  * The desired access rights for the access token.
1128  *
1129  * @param[in] HandleAttributes
1130  * Handle attributes of which they are used for the newly creation
1131  * of the opened thread token. The function assumes that they have
1132  * been validated prior the calling of this function.
1133  *
1134  * @param[in] EffectiveOnly
1135  * If set to TRUE, the function will copy a new access token with
1136  * privileges and groups that are effectively enabled. Any disabled
1137  * privilege or group is removed from the copied token. Otherwise
1138  * if set to FALSE, the function retains all the enabled and disabled
1139  * privielges and groups.
1140  *
1141  * @param[in] CopyOnOpen
1142  * If set to TRUE, it tells the function that the access token cannot
1143  * be directly opened due to the security impersonation info of the
1144  * associated thread being enforced. In this case the function will
1145  * make a copy of the said token by duplicating it. Otherwise if set
1146  * to FALSE, the function will just open the access token directly.
1147  *
1148  * @param[in] ImpersonationLevel
1149  * The security impersonation level, at which it is allowed to
1150  * access the token.
1151  *
1152  * @param[in] PreviousMode
1153  * The processor request level mode.
1154  *
1155  * @param[out] OpenedTokenHandle
1156  * A pointer to an opened access token handle associated with the
1157  * specific thread, returned to the caller. Initially this parameter
1158  * is set to NULL and if the function fails to open the thread's token,
1159  * it will stay NULL.
1160  *
1161  * @return
1162  * Returns STATUS_SUCCESS if the function has successfully opened the thread's
1163  * token. STATUS_OBJECT_TYPE_MISMATCH is returned if the obtained thread object
1164  * no longer matches with the other thread that has been obtained previously.
1165  * STATUS_NO_TOKEN is returned if the associated thread's process has no
1166  * primary access token. A failure NTSTATUS code is returned otherwise.
1167  */
1168 static
1169 NTSTATUS
SepOpenThreadToken(_In_ PETHREAD Thread,_In_ HANDLE ThreadHandle,_In_ PTOKEN ThreadToken,_In_ ACCESS_MASK DesiredAccess,_In_ ULONG HandleAttributes,_In_ BOOLEAN EffectiveOnly,_In_ BOOLEAN CopyOnOpen,_In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,_In_ KPROCESSOR_MODE PreviousMode,_Out_ PHANDLE OpenedTokenHandle)1170 SepOpenThreadToken(
1171     _In_ PETHREAD Thread,
1172     _In_ HANDLE ThreadHandle,
1173     _In_ PTOKEN ThreadToken,
1174     _In_ ACCESS_MASK DesiredAccess,
1175     _In_ ULONG HandleAttributes,
1176     _In_ BOOLEAN EffectiveOnly,
1177     _In_ BOOLEAN CopyOnOpen,
1178     _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
1179     _In_ KPROCESSOR_MODE PreviousMode,
1180     _Out_ PHANDLE OpenedTokenHandle)
1181 {
1182     NTSTATUS Status;
1183     HANDLE TokenHandle;
1184     PETHREAD Thread2;
1185     OBJECT_ATTRIBUTES ObjectAttributes;
1186     PTOKEN NewToken, PrimaryToken;
1187     SECURITY_DESCRIPTOR SecurityDescriptor;
1188     PACL Dacl;
1189 
1190     PAGED_CODE();
1191 
1192     /* Assume no opened token handle at first */
1193     *OpenedTokenHandle = NULL;
1194 
1195     /* Check if we have to do a copy of the token on open or not */
1196     if (!CopyOnOpen)
1197     {
1198         /* Just open the thread's token directly */
1199         Status = ObOpenObjectByPointer(ThreadToken,
1200                                        HandleAttributes,
1201                                        NULL,
1202                                        DesiredAccess,
1203                                        SeTokenObjectType,
1204                                        PreviousMode,
1205                                        &TokenHandle);
1206         if (!NT_SUCCESS(Status))
1207         {
1208             DPRINT1("Failed to open the thread's token object (Status 0x%lx)\n", Status);
1209             return Status;
1210         }
1211 
1212         /* Give it to caller */
1213         *OpenedTokenHandle = TokenHandle;
1214         return STATUS_SUCCESS;
1215     }
1216 
1217     /*
1218      * The caller asks to do a copy of that token whilst it's opened.
1219      * Obtain a thread object again but this time we have to obtain
1220      * it in our side, kernel mode, and request all the access needed
1221      * to do a copy of the token because the original thread only has
1222      * query access needed for access token validation.
1223      */
1224     Status = ObReferenceObjectByHandle(ThreadHandle,
1225                                        THREAD_ALL_ACCESS,
1226                                        PsThreadType,
1227                                        KernelMode,
1228                                        (PVOID*)&Thread2,
1229                                        NULL);
1230     if (!NT_SUCCESS(Status))
1231     {
1232         DPRINT1("Failed to reference the object thread (Status 0x%lx)\n", Status);
1233         return Status;
1234     }
1235 
1236     /* Check that one of the threads hasn't diverged */
1237     if (Thread != Thread2)
1238     {
1239         DPRINT1("One of the threads aren't the same (original thread 0x%p, thread 0x%p)\n", Thread, Thread2);
1240         ObDereferenceObject(Thread2);
1241         return STATUS_OBJECT_TYPE_MISMATCH;
1242     }
1243 
1244     /* Reference the primary token of the process' thread */
1245     PrimaryToken = PsReferencePrimaryToken(Thread2->ThreadsProcess);
1246     if (!PrimaryToken)
1247     {
1248         DPRINT1("Failed to reference the primary token of thread\n");
1249         ObDereferenceObject(Thread2);
1250         return STATUS_NO_TOKEN;
1251     }
1252 
1253     /* Create an impersonation DACL from the tokens we got */
1254     Status = SepCreateImpersonationTokenDacl(ThreadToken, PrimaryToken, &Dacl);
1255     ObFastDereferenceObject(&Thread2->ThreadsProcess->Token, PrimaryToken);
1256     if (!NT_SUCCESS(Status))
1257     {
1258         DPRINT1("Failed to create an impersonation token DACL (Status 0x%lx)\n", Status);
1259         ObDereferenceObject(Thread2);
1260         return Status;
1261     }
1262 
1263     /* Create a security descriptor with the DACL we got */
1264     Status = RtlCreateSecurityDescriptor(&SecurityDescriptor,
1265                                          SECURITY_DESCRIPTOR_REVISION);
1266     if (!NT_SUCCESS(Status))
1267     {
1268         DPRINT1("Failed to create a security descriptor (Status 0x%lx)\n", Status);
1269         ExFreePoolWithTag(Dacl, TAG_ACL);
1270         ObDereferenceObject(Thread2);
1271         return Status;
1272     }
1273 
1274     /* Attach the DACL to that security descriptor */
1275     Status = RtlSetDaclSecurityDescriptor(&SecurityDescriptor,
1276                                           TRUE,
1277                                           Dacl,
1278                                           FALSE);
1279     if (!NT_SUCCESS(Status))
1280     {
1281         DPRINT1("Failed to set the DACL to the security descriptor (Status 0x%lx)\n", Status);
1282         ExFreePoolWithTag(Dacl, TAG_ACL);
1283         ObDereferenceObject(Thread2);
1284         return Status;
1285     }
1286 
1287     /*
1288      * Initialize the object attributes for the token we
1289      * are going to duplicate.
1290      */
1291     InitializeObjectAttributes(&ObjectAttributes,
1292                                NULL,
1293                                HandleAttributes,
1294                                NULL,
1295                                &SecurityDescriptor);
1296 
1297     /* Duplicate (copy) it now */
1298     Status = SepDuplicateToken(ThreadToken,
1299                                &ObjectAttributes,
1300                                EffectiveOnly,
1301                                TokenImpersonation,
1302                                ImpersonationLevel,
1303                                KernelMode,
1304                                &NewToken);
1305     if (!NT_SUCCESS(Status))
1306     {
1307         DPRINT1("Failed to duplicate the token (Status 0x%lx)\n", Status);
1308         ExFreePoolWithTag(Dacl, TAG_ACL);
1309         ObDereferenceObject(Thread2);
1310         return Status;
1311     }
1312 
1313     /* Insert that copied token into the handle now */
1314     ObReferenceObject(NewToken);
1315     Status = ObInsertObject(NewToken,
1316                             NULL,
1317                             DesiredAccess,
1318                             0,
1319                             NULL,
1320                             &TokenHandle);
1321     if (!NT_SUCCESS(Status))
1322     {
1323         DPRINT1("Failed to insert the token object (Status 0x%lx)\n", Status);
1324         ExFreePoolWithTag(Dacl, TAG_ACL);
1325         ObDereferenceObject(NewToken);
1326         ObDereferenceObject(Thread2);
1327         return Status;
1328     }
1329 
1330     /* We're almost done, free the DACL if we got one */
1331     ExFreePoolWithTag(Dacl, TAG_ACL);
1332 
1333     /* Impersonate the client finally */
1334     Status = PsImpersonateClient(Thread, NewToken, FALSE, EffectiveOnly, ImpersonationLevel);
1335     if (!NT_SUCCESS(Status))
1336     {
1337         DPRINT1("Failed to impersonate the client (Status 0x%lx)\n", Status);
1338         ObDereferenceObject(NewToken);
1339         ObDereferenceObject(Thread2);
1340         return Status;
1341     }
1342 
1343     /* Give the newly opened token handle to caller */
1344     *OpenedTokenHandle = TokenHandle;
1345     ObDereferenceObject(NewToken);
1346     ObDereferenceObject(Thread2);
1347     return Status;
1348 }
1349 
1350 /**
1351  * @brief
1352  * Subtracts a token in exchange of duplicating a new one.
1353  *
1354  * @param[in] ParentToken
1355  * The parent access token for duplication.
1356  *
1357  * @param[out] Token
1358  * The new duplicated token.
1359  *
1360  * @param[in] InUse
1361  * Set this to TRUE if the token is about to be used immediately after the call execution
1362  * of this function, FALSE otherwise.
1363  *
1364  * @param[in] SessionId
1365  * Session ID for the token to be assigned.
1366  *
1367  * @return
1368  * Returns STATUS_SUCCESS if token subtracting and duplication have completed successfully.
1369  * A failure NTSTATUS code is returned otherwise.
1370  */
1371 NTSTATUS
1372 NTAPI
SeSubProcessToken(_In_ PTOKEN ParentToken,_Out_ PTOKEN * Token,_In_ BOOLEAN InUse,_In_ ULONG SessionId)1373 SeSubProcessToken(
1374     _In_ PTOKEN ParentToken,
1375     _Out_ PTOKEN *Token,
1376     _In_ BOOLEAN InUse,
1377     _In_ ULONG SessionId)
1378 {
1379     PTOKEN NewToken;
1380     OBJECT_ATTRIBUTES ObjectAttributes;
1381     NTSTATUS Status;
1382 
1383     /* Initialize the attributes and duplicate it */
1384     InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
1385     Status = SepDuplicateToken(ParentToken,
1386                                &ObjectAttributes,
1387                                FALSE,
1388                                TokenPrimary,
1389                                ParentToken->ImpersonationLevel,
1390                                KernelMode,
1391                                &NewToken);
1392     if (NT_SUCCESS(Status))
1393     {
1394         /* Insert it */
1395         Status = ObInsertObject(NewToken,
1396                                 NULL,
1397                                 0,
1398                                 0,
1399                                 NULL,
1400                                 NULL);
1401         if (NT_SUCCESS(Status))
1402         {
1403             /* Set the session ID */
1404             NewToken->SessionId = SessionId;
1405             NewToken->TokenInUse = InUse;
1406 
1407             /* Return the token */
1408             *Token = NewToken;
1409         }
1410     }
1411 
1412     /* Return status */
1413     return Status;
1414 }
1415 
1416 /**
1417  * @brief
1418  * Checks if the token is a child of the other token
1419  * of the current process that the calling thread is invoking this function.
1420  *
1421  * @param[in] Token
1422  * An access token to determine if it's a child or not.
1423  *
1424  * @param[out] IsChild
1425  * The returned boolean result.
1426  *
1427  * @return
1428  * Returns STATUS_SUCCESS when the function finishes its operation. STATUS_UNSUCCESSFUL is
1429  * returned if primary token of the current calling process couldn't be referenced otherwise.
1430  */
1431 NTSTATUS
1432 NTAPI
SeIsTokenChild(_In_ PTOKEN Token,_Out_ PBOOLEAN IsChild)1433 SeIsTokenChild(
1434     _In_ PTOKEN Token,
1435     _Out_ PBOOLEAN IsChild)
1436 {
1437     PTOKEN ProcessToken;
1438     LUID ProcessTokenId, CallerParentId;
1439 
1440     /* Assume failure */
1441     *IsChild = FALSE;
1442 
1443     /* Reference the process token */
1444     ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess());
1445     if (!ProcessToken)
1446         return STATUS_UNSUCCESSFUL;
1447 
1448     /* Get its token ID */
1449     ProcessTokenId = ProcessToken->TokenId;
1450 
1451     /* Dereference the token */
1452     ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken);
1453 
1454     /* Get our parent token ID */
1455     CallerParentId = Token->ParentTokenId;
1456 
1457     /* Compare the token IDs */
1458     if (RtlEqualLuid(&CallerParentId, &ProcessTokenId))
1459         *IsChild = TRUE;
1460 
1461     /* Return success */
1462     return STATUS_SUCCESS;
1463 }
1464 
1465 /**
1466  * @brief
1467  * Checks if the token is a sibling of the other token of
1468  * the current process that the calling thread is invoking this function.
1469  *
1470  * @param[in] Token
1471  * An access token to determine if it's a sibling or not.
1472  *
1473  * @param[out] IsSibling
1474  * The returned boolean result.
1475  *
1476  * @return
1477  * Returns STATUS_SUCCESS when the function finishes its operation. STATUS_UNSUCCESSFUL is
1478  * returned if primary token of the current calling process couldn't be referenced otherwise.
1479  */
1480 NTSTATUS
1481 NTAPI
SeIsTokenSibling(_In_ PTOKEN Token,_Out_ PBOOLEAN IsSibling)1482 SeIsTokenSibling(
1483     _In_ PTOKEN Token,
1484     _Out_ PBOOLEAN IsSibling)
1485 {
1486     PTOKEN ProcessToken;
1487     LUID ProcessParentId, ProcessAuthId;
1488     LUID CallerParentId, CallerAuthId;
1489 
1490     /* Assume failure */
1491     *IsSibling = FALSE;
1492 
1493     /* Reference the process token */
1494     ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess());
1495     if (!ProcessToken)
1496         return STATUS_UNSUCCESSFUL;
1497 
1498     /* Get its parent and authentication IDs */
1499     ProcessParentId = ProcessToken->ParentTokenId;
1500     ProcessAuthId = ProcessToken->AuthenticationId;
1501 
1502     /* Dereference the token */
1503     ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken);
1504 
1505     /* Get our parent and authentication IDs */
1506     CallerParentId = Token->ParentTokenId;
1507     CallerAuthId = Token->AuthenticationId;
1508 
1509     /* Compare the token IDs */
1510     if (RtlEqualLuid(&CallerParentId, &ProcessParentId) &&
1511         RtlEqualLuid(&CallerAuthId, &ProcessAuthId))
1512     {
1513         *IsSibling = TRUE;
1514     }
1515 
1516     /* Return success */
1517     return STATUS_SUCCESS;
1518 }
1519 
1520 /**
1521  * @brief
1522  * Copies an existing access token (technically duplicating a new one).
1523  *
1524  * @param[in] Token
1525  * Token to copy.
1526  *
1527  * @param[in] Level
1528  * Impersonation security level to assign to the newly copied token.
1529  *
1530  * @param[in] PreviousMode
1531  * Processor request level mode.
1532  *
1533  * @param[out] NewToken
1534  * The newly copied token.
1535  *
1536  * @return
1537  * Returns STATUS_SUCCESS when token copying has finished successfully. A failure
1538  * NTSTATUS code is returned otherwise.
1539  */
1540 NTSTATUS
1541 NTAPI
SeCopyClientToken(_In_ PACCESS_TOKEN Token,_In_ SECURITY_IMPERSONATION_LEVEL Level,_In_ KPROCESSOR_MODE PreviousMode,_Out_ PACCESS_TOKEN * NewToken)1542 SeCopyClientToken(
1543     _In_ PACCESS_TOKEN Token,
1544     _In_ SECURITY_IMPERSONATION_LEVEL Level,
1545     _In_ KPROCESSOR_MODE PreviousMode,
1546     _Out_ PACCESS_TOKEN* NewToken)
1547 {
1548     NTSTATUS Status;
1549     OBJECT_ATTRIBUTES ObjectAttributes;
1550 
1551     PAGED_CODE();
1552 
1553     InitializeObjectAttributes(&ObjectAttributes,
1554                                NULL,
1555                                0,
1556                                NULL,
1557                                NULL);
1558 
1559     Status = SepDuplicateToken(Token,
1560                                &ObjectAttributes,
1561                                FALSE,
1562                                TokenImpersonation,
1563                                Level,
1564                                PreviousMode,
1565                                (PTOKEN*)NewToken);
1566 
1567     return Status;
1568 }
1569 
1570 /**
1571  * @brief
1572  * Determines if a token is a sandbox inert token or not,
1573  * based upon the token flags.
1574  *
1575  * @param[in] Token
1576  * A valid access token to determine if such token is inert.
1577  *
1578  * @return
1579  * Returns TRUE if the token is inert, FALSE otherwise.
1580  */
1581 BOOLEAN
1582 NTAPI
SeTokenIsInert(_In_ PTOKEN Token)1583 SeTokenIsInert(
1584     _In_ PTOKEN Token)
1585 {
1586     PAGED_CODE();
1587 
1588     return (((PTOKEN)Token)->TokenFlags & TOKEN_SANDBOX_INERT) != 0;
1589 }
1590 
1591 /**
1592  * @brief
1593  * Internal function that deals with access token object destruction and deletion.
1594  * The function is used solely by the object manager mechanism that handles the life
1595  * management of a token object.
1596  *
1597  * @param[in] ObjectBody
1598  * The object body that represents an access token object.
1599  *
1600  * @return
1601  * Nothing.
1602  */
1603 VOID
1604 NTAPI
SepDeleteToken(_In_ PVOID ObjectBody)1605 SepDeleteToken(
1606     _In_ PVOID ObjectBody)
1607 {
1608     NTSTATUS Status;
1609     PTOKEN AccessToken = (PTOKEN)ObjectBody;
1610 
1611     DPRINT("SepDeleteToken()\n");
1612 
1613     /* Remove the referenced logon session from token */
1614     if (AccessToken->LogonSession)
1615     {
1616         Status = SepRmRemoveLogonSessionFromToken(AccessToken);
1617         if (!NT_SUCCESS(Status))
1618         {
1619             /* Something seriously went wrong */
1620             DPRINT1("SepDeleteToken(): Failed to remove the logon session from token (Status: 0x%lx)\n", Status);
1621             return;
1622         }
1623     }
1624 
1625     /* Dereference the logon session */
1626     if ((AccessToken->TokenFlags & TOKEN_SESSION_NOT_REFERENCED) == 0)
1627         SepRmDereferenceLogonSession(&AccessToken->AuthenticationId);
1628 
1629     /* Delete the token lock */
1630     if (AccessToken->TokenLock)
1631         SepDeleteTokenLock(AccessToken);
1632 
1633     /* Delete the dynamic information area */
1634     if (AccessToken->DynamicPart)
1635         ExFreePoolWithTag(AccessToken->DynamicPart, TAG_TOKEN_DYNAMIC);
1636 }
1637 
1638 /**
1639  * @brief
1640  * Internal function that initializes critical kernel data for access
1641  * token implementation in SRM.
1642  *
1643  * @return
1644  * Nothing.
1645  */
1646 CODE_SEG("INIT")
1647 VOID
1648 NTAPI
SepInitializeTokenImplementation(VOID)1649 SepInitializeTokenImplementation(VOID)
1650 {
1651     UNICODE_STRING Name;
1652     OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
1653 
1654     DPRINT("Creating Token Object Type\n");
1655 
1656     /* Initialize the Token type */
1657     RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
1658     RtlInitUnicodeString(&Name, L"Token");
1659     ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
1660     ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
1661     ObjectTypeInitializer.SecurityRequired = TRUE;
1662     ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(TOKEN);
1663     ObjectTypeInitializer.GenericMapping = SepTokenMapping;
1664     ObjectTypeInitializer.PoolType = PagedPool;
1665     ObjectTypeInitializer.ValidAccessMask = TOKEN_ALL_ACCESS;
1666     ObjectTypeInitializer.UseDefaultObject = TRUE;
1667     ObjectTypeInitializer.DeleteProcedure = SepDeleteToken;
1668     ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &SeTokenObjectType);
1669 }
1670 
1671 /**
1672  * @brief
1673  * Assigns a primary access token to a given process.
1674  *
1675  * @param[in] Process
1676  * Process where the token is about to be assigned.
1677  *
1678  * @param[in] Token
1679  * The token to be assigned.
1680  *
1681  * @return
1682  * Nothing.
1683  */
1684 VOID
1685 NTAPI
SeAssignPrimaryToken(_In_ PEPROCESS Process,_In_ PTOKEN Token)1686 SeAssignPrimaryToken(
1687     _In_ PEPROCESS Process,
1688     _In_ PTOKEN Token)
1689 {
1690     PAGED_CODE();
1691 
1692     /* Sanity checks */
1693     ASSERT(Token->TokenType == TokenPrimary);
1694     ASSERT(!Token->TokenInUse);
1695 
1696     /* Clean any previous token */
1697     if (Process->Token.Object) SeDeassignPrimaryToken(Process);
1698 
1699     /* Set the new token */
1700     ObReferenceObject(Token);
1701     Token->TokenInUse = TRUE;
1702     ObInitializeFastReference(&Process->Token, Token);
1703 }
1704 
1705 /**
1706  * @brief
1707  * Retrieves token control information.
1708  *
1709  * @param[in] _Token
1710  * A valid token object.
1711  *
1712  * @param[out] SecurityDescriptor
1713  * The returned token control information.
1714  *
1715  * @return
1716  * Nothing.
1717  */
1718 VOID
1719 NTAPI
SeGetTokenControlInformation(_In_ PACCESS_TOKEN _Token,_Out_ PTOKEN_CONTROL TokenControl)1720 SeGetTokenControlInformation(
1721     _In_ PACCESS_TOKEN _Token,
1722     _Out_ PTOKEN_CONTROL TokenControl)
1723 {
1724     PTOKEN Token = _Token;
1725     PAGED_CODE();
1726 
1727     /* Capture the main fields */
1728     TokenControl->AuthenticationId = Token->AuthenticationId;
1729     TokenControl->TokenId = Token->TokenId;
1730     TokenControl->TokenSource = Token->TokenSource;
1731 
1732     /* Lock the token */
1733     SepAcquireTokenLockShared(Token);
1734 
1735     /* Capture the modified ID */
1736     TokenControl->ModifiedId = Token->ModifiedId;
1737 
1738     /* Unlock it */
1739     SepReleaseTokenLock(Token);
1740 }
1741 
1742 /**
1743  * @brief
1744  * Creates the system process token.
1745  *
1746  * @return
1747  * Returns the system process token if the operations have
1748  * completed successfully.
1749  */
1750 CODE_SEG("INIT")
1751 PTOKEN
1752 NTAPI
SepCreateSystemProcessToken(VOID)1753 SepCreateSystemProcessToken(VOID)
1754 {
1755     ULONG GroupAttributes, OwnerAttributes;
1756     LARGE_INTEGER Expiration;
1757     SID_AND_ATTRIBUTES UserSid;
1758     ULONG GroupsLength;
1759     PSID PrimaryGroup;
1760     OBJECT_ATTRIBUTES ObjectAttributes;
1761     PSID Owner;
1762     PTOKEN Token;
1763     NTSTATUS Status;
1764 
1765     /* Don't ever expire */
1766     Expiration.QuadPart = -1;
1767 
1768     /* All groups mandatory and enabled */
1769     GroupAttributes = SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT;
1770     OwnerAttributes = SE_GROUP_ENABLED | SE_GROUP_OWNER | SE_GROUP_ENABLED_BY_DEFAULT;
1771 
1772     /* User is Local System */
1773     UserSid.Sid = SeLocalSystemSid;
1774     UserSid.Attributes = 0;
1775 
1776     /* Primary group is Local System */
1777     PrimaryGroup = SeLocalSystemSid;
1778 
1779     /* Owner is Administrators */
1780     Owner = SeAliasAdminsSid;
1781 
1782     /* Groups are Administrators, World, and Authenticated Users */
1783     SID_AND_ATTRIBUTES Groups[] =
1784     {
1785         {SeAliasAdminsSid, OwnerAttributes},
1786         {SeWorldSid, GroupAttributes},
1787         {SeAuthenticatedUsersSid, GroupAttributes},
1788         {SeLocalSid, SE_GROUP_ENABLED} // HACK: Temporarily add the local group. See CORE-18250.
1789     };
1790     GroupsLength = sizeof(SID_AND_ATTRIBUTES) +
1791                    SeLengthSid(Groups[0].Sid) +
1792                    SeLengthSid(Groups[1].Sid) +
1793                    SeLengthSid(Groups[2].Sid) +
1794                    SeLengthSid(Groups[3].Sid); // HACK
1795     ASSERT(GroupsLength <= (sizeof(Groups) * sizeof(ULONG)));
1796 
1797     /* Setup the privileges */
1798     LUID_AND_ATTRIBUTES Privileges[] =
1799     {
1800         {SeTcbPrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED},
1801         {SeCreateTokenPrivilege, 0},
1802         {SeTakeOwnershipPrivilege, 0},
1803         {SeCreatePagefilePrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED},
1804         {SeLockMemoryPrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED},
1805         {SeAssignPrimaryTokenPrivilege, 0},
1806         {SeIncreaseQuotaPrivilege, 0},
1807         {SeIncreaseBasePriorityPrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED},
1808         {SeCreatePermanentPrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED},
1809         {SeDebugPrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED},
1810         {SeAuditPrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED},
1811         {SeSecurityPrivilege, 0},
1812         {SeSystemEnvironmentPrivilege, 0},
1813         {SeChangeNotifyPrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED},
1814         {SeBackupPrivilege, 0},
1815         {SeRestorePrivilege, 0},
1816         {SeShutdownPrivilege, 0},
1817         {SeLoadDriverPrivilege, 0},
1818         {SeProfileSingleProcessPrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED},
1819         {SeSystemtimePrivilege, 0},
1820         {SeUndockPrivilege, 0},
1821         {SeManageVolumePrivilege, 0},
1822         {SeImpersonatePrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED},
1823         {SeCreateGlobalPrivilege, SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED},
1824     };
1825 
1826     /* Setup the object attributes */
1827     InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
1828     ASSERT(SeSystemDefaultDacl != NULL);
1829 
1830     /* Create the token */
1831     Status = SepCreateToken((PHANDLE)&Token,
1832                             KernelMode,
1833                             0,
1834                             &ObjectAttributes,
1835                             TokenPrimary,
1836                             SecurityAnonymous,
1837                             &SeSystemAuthenticationId,
1838                             &Expiration,
1839                             &UserSid,
1840                             RTL_NUMBER_OF(Groups),
1841                             Groups,
1842                             GroupsLength,
1843                             RTL_NUMBER_OF(Privileges),
1844                             Privileges,
1845                             Owner,
1846                             PrimaryGroup,
1847                             SeSystemDefaultDacl,
1848                             &SeSystemTokenSource,
1849                             TRUE);
1850     ASSERT(Status == STATUS_SUCCESS);
1851 
1852     /* Return the token */
1853     return Token;
1854 }
1855 
1856 /**
1857  * @brief
1858  * Creates the anonymous logon token for the system. The difference between this
1859  * token and the other one is the inclusion of everyone SID group (being SeWorldSid).
1860  * The other token lacks such group.
1861  *
1862  * @return
1863  * Returns the system's anonymous logon token if the operations have
1864  * completed successfully.
1865  */
1866 CODE_SEG("INIT")
1867 PTOKEN
SepCreateSystemAnonymousLogonToken(VOID)1868 SepCreateSystemAnonymousLogonToken(VOID)
1869 {
1870     SID_AND_ATTRIBUTES UserSid;
1871     PSID PrimaryGroup;
1872     PTOKEN Token;
1873     ULONG GroupsLength;
1874     LARGE_INTEGER Expiration;
1875     OBJECT_ATTRIBUTES ObjectAttributes;
1876     NTSTATUS Status;
1877 
1878     /* The token never expires */
1879     Expiration.QuadPart = -1;
1880 
1881     /* The user is the anonymous logon */
1882     UserSid.Sid = SeAnonymousLogonSid;
1883     UserSid.Attributes = 0;
1884 
1885     /* The primary group is also the anonymous logon */
1886     PrimaryGroup = SeAnonymousLogonSid;
1887 
1888     /* The only group for the token is the World */
1889     SID_AND_ATTRIBUTES Groups[] =
1890     {
1891         {SeWorldSid, SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT}
1892     };
1893     GroupsLength = sizeof(SID_AND_ATTRIBUTES) +
1894                    SeLengthSid(Groups[0].Sid);
1895     ASSERT(GroupsLength <= (sizeof(Groups) * sizeof(ULONG)));
1896 
1897     /* Initialise the object attributes for the token */
1898     InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
1899     ASSERT(SeSystemAnonymousLogonDacl != NULL);
1900 
1901     /* Create token */
1902     Status = SepCreateToken((PHANDLE)&Token,
1903                             KernelMode,
1904                             0,
1905                             &ObjectAttributes,
1906                             TokenPrimary,
1907                             SecurityAnonymous,
1908                             &SeAnonymousAuthenticationId,
1909                             &Expiration,
1910                             &UserSid,
1911                             RTL_NUMBER_OF(Groups),
1912                             Groups,
1913                             GroupsLength,
1914                             0,
1915                             NULL,
1916                             NULL,
1917                             PrimaryGroup,
1918                             SeSystemAnonymousLogonDacl,
1919                             &SeSystemTokenSource,
1920                             TRUE);
1921     ASSERT(Status == STATUS_SUCCESS);
1922 
1923     /* Return the anonymous logon token */
1924     return Token;
1925 }
1926 
1927 /**
1928  * @brief
1929  * Creates the anonymous logon token for the system. This kind of token
1930  * doesn't include the everyone SID group (being SeWorldSid).
1931  *
1932  * @return
1933  * Returns the system's anonymous logon token if the operations have
1934  * completed successfully.
1935  */
1936 CODE_SEG("INIT")
1937 PTOKEN
SepCreateSystemAnonymousLogonTokenNoEveryone(VOID)1938 SepCreateSystemAnonymousLogonTokenNoEveryone(VOID)
1939 {
1940     SID_AND_ATTRIBUTES UserSid;
1941     PSID PrimaryGroup;
1942     PTOKEN Token;
1943     LARGE_INTEGER Expiration;
1944     OBJECT_ATTRIBUTES ObjectAttributes;
1945     NTSTATUS Status;
1946 
1947     /* The token never expires */
1948     Expiration.QuadPart = -1;
1949 
1950     /* The user is the anonymous logon */
1951     UserSid.Sid = SeAnonymousLogonSid;
1952     UserSid.Attributes = 0;
1953 
1954     /* The primary group is also the anonymous logon */
1955     PrimaryGroup = SeAnonymousLogonSid;
1956 
1957     /* Initialise the object attributes for the token */
1958     InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
1959     ASSERT(SeSystemAnonymousLogonDacl != NULL);
1960 
1961     /* Create token */
1962     Status = SepCreateToken((PHANDLE)&Token,
1963                             KernelMode,
1964                             0,
1965                             &ObjectAttributes,
1966                             TokenPrimary,
1967                             SecurityAnonymous,
1968                             &SeAnonymousAuthenticationId,
1969                             &Expiration,
1970                             &UserSid,
1971                             0,
1972                             NULL,
1973                             0,
1974                             0,
1975                             NULL,
1976                             NULL,
1977                             PrimaryGroup,
1978                             SeSystemAnonymousLogonDacl,
1979                             &SeSystemTokenSource,
1980                             TRUE);
1981     ASSERT(Status == STATUS_SUCCESS);
1982 
1983     /* Return the anonymous (not including everyone) logon token */
1984     return Token;
1985 }
1986 
1987 /* PUBLIC FUNCTIONS ***********************************************************/
1988 
1989 /**
1990  * @brief
1991  * Queries the session ID of an access token.
1992  *
1993  * @param[in] Token
1994  * A valid access token where the session ID has to be gathered.
1995  *
1996  * @param[out] pSessionId
1997  * The returned pointer to a session ID to the caller.
1998  *
1999  * @return
2000  * Returns STATUS_SUCCESS.
2001  */
2002 NTSTATUS
2003 NTAPI
SeQuerySessionIdToken(_In_ PACCESS_TOKEN Token,_Out_ PULONG pSessionId)2004 SeQuerySessionIdToken(
2005     _In_ PACCESS_TOKEN Token,
2006     _Out_ PULONG pSessionId)
2007 {
2008     PAGED_CODE();
2009 
2010     /* Lock the token */
2011     SepAcquireTokenLockShared(Token);
2012 
2013     *pSessionId = ((PTOKEN)Token)->SessionId;
2014 
2015     /* Unlock the token */
2016     SepReleaseTokenLock(Token);
2017 
2018     return STATUS_SUCCESS;
2019 }
2020 
2021 /**
2022  * @brief
2023  * Queries the authentication ID of an access token.
2024  *
2025  * @param[in] Token
2026  * A valid access token where the authentication ID has to be gathered.
2027  *
2028  * @param[out] pSessionId
2029  * The returned pointer to an authentication ID to the caller.
2030  *
2031  * @return
2032  * Returns STATUS_SUCCESS.
2033  */
2034 NTSTATUS
2035 NTAPI
SeQueryAuthenticationIdToken(_In_ PACCESS_TOKEN Token,_Out_ PLUID LogonId)2036 SeQueryAuthenticationIdToken(
2037     _In_ PACCESS_TOKEN Token,
2038     _Out_ PLUID LogonId)
2039 {
2040     PAGED_CODE();
2041 
2042     *LogonId = ((PTOKEN)Token)->AuthenticationId;
2043 
2044     return STATUS_SUCCESS;
2045 }
2046 
2047 /**
2048  * @brief
2049  * Gathers the security impersonation level of an access token.
2050  *
2051  * @param[in] Token
2052  * A valid access token where the impersonation level has to be gathered.
2053  *
2054  * @return
2055  * Returns the security impersonation level from a valid token.
2056  */
2057 SECURITY_IMPERSONATION_LEVEL
2058 NTAPI
SeTokenImpersonationLevel(_In_ PACCESS_TOKEN Token)2059 SeTokenImpersonationLevel(
2060     _In_ PACCESS_TOKEN Token)
2061 {
2062     PAGED_CODE();
2063 
2064     return ((PTOKEN)Token)->ImpersonationLevel;
2065 }
2066 
2067 /**
2068  * @brief
2069  * Gathers the token type of an access token. A token ca be either
2070  * a primary token or impersonation token.
2071  *
2072  * @param[in] Token
2073  * A valid access token where the token type has to be gathered.
2074  *
2075  * @return
2076  * Returns the token type from a valid token.
2077  */
2078 TOKEN_TYPE
2079 NTAPI
SeTokenType(_In_ PACCESS_TOKEN Token)2080 SeTokenType(
2081     _In_ PACCESS_TOKEN Token)
2082 {
2083     PAGED_CODE();
2084 
2085     return ((PTOKEN)Token)->TokenType;
2086 }
2087 
2088 /**
2089  * @brief
2090  * Determines if a token is either an admin token or not. Such
2091  * condition is checked based upon TOKEN_HAS_ADMIN_GROUP flag,
2092  * which means if the respective access token belongs to an
2093  * administrator group or not.
2094  *
2095  * @param[in] Token
2096  * A valid access token to determine if such token is admin or not.
2097  *
2098  * @return
2099  * Returns TRUE if the token is an admin one, FALSE otherwise.
2100  */
2101 BOOLEAN
2102 NTAPI
SeTokenIsAdmin(_In_ PACCESS_TOKEN Token)2103 SeTokenIsAdmin(
2104     _In_ PACCESS_TOKEN Token)
2105 {
2106     PAGED_CODE();
2107 
2108     // NOTE: Win7+ instead really checks the list of groups in the token
2109     // (since TOKEN_HAS_ADMIN_GROUP == TOKEN_WRITE_RESTRICTED ...)
2110     return (((PTOKEN)Token)->TokenFlags & TOKEN_HAS_ADMIN_GROUP) != 0;
2111 }
2112 
2113 /**
2114  * @brief
2115  * Determines if a token is restricted or not, based upon the token
2116  * flags.
2117  *
2118  * @param[in] Token
2119  * A valid access token to determine if such token is restricted.
2120  *
2121  * @return
2122  * Returns TRUE if the token is restricted, FALSE otherwise.
2123  */
2124 BOOLEAN
2125 NTAPI
SeTokenIsRestricted(_In_ PACCESS_TOKEN Token)2126 SeTokenIsRestricted(
2127     _In_ PACCESS_TOKEN Token)
2128 {
2129     PAGED_CODE();
2130 
2131     return (((PTOKEN)Token)->TokenFlags & TOKEN_IS_RESTRICTED) != 0;
2132 }
2133 
2134 /**
2135  * @brief
2136  * Determines if a token is write restricted, that is, nobody can write anything
2137  * to it.
2138  *
2139  * @param[in] Token
2140  * A valid access token to determine if such token is write restricted.
2141  *
2142  * @return
2143  * Returns TRUE if the token is write restricted, FALSE otherwise.
2144  *
2145  * @remarks
2146  * First introduced in NT 5.1 SP2 x86 (5.1.2600.2622), absent in NT 5.2,
2147  * then finally re-introduced in Vista+.
2148  */
2149 BOOLEAN
2150 NTAPI
SeTokenIsWriteRestricted(_In_ PACCESS_TOKEN Token)2151 SeTokenIsWriteRestricted(
2152     _In_ PACCESS_TOKEN Token)
2153 {
2154     PAGED_CODE();
2155 
2156     // NOTE: NT 5.1 SP2 x86 checks the SE_BACKUP_PRIVILEGES_CHECKED flag
2157     // while Vista+ checks the TOKEN_WRITE_RESTRICTED flag as one expects.
2158     return (((PTOKEN)Token)->TokenFlags & SE_BACKUP_PRIVILEGES_CHECKED) != 0;
2159 }
2160 
2161 /**
2162  * @brief
2163  * Determines whether the server is allowed to impersonate on behalf
2164  * of a client or not. For further details, see Remarks.
2165  *
2166  * @param[in] ProcessToken
2167  * A pointer to the primary access token of the server process
2168  * that requests impersonation of the client target.
2169  *
2170  * @param[in] TokenToImpersonate
2171  * A pointer to an access token that represents a client that is to
2172  * be impersonated.
2173  *
2174  * @param[in] ImpersonationLevel
2175  * The requested impersonation level.
2176  *
2177  * @return
2178  * Returns TRUE if the conditions checked are met for token impersonation,
2179  * FALSE otherwise.
2180  *
2181  * @remarks
2182  * The server has to meet the following criteria in order to impersonate
2183  * a client, that is:
2184  *
2185  * - The server must not impersonate a client beyond the level that
2186  *   the client imposed on itself.
2187  *
2188  * - The server must be authenticated on the same logon session of
2189  *   the target client.
2190  *
2191  * - IF NOT then the server's user ID has to match to that of the
2192  *   target client.
2193  *
2194  * - The server must not be restricted in order to impersonate a
2195  *   client that is not restricted.
2196  *
2197  * If the associated access token that represents the security properties
2198  * of the server is granted the SeImpersonatePrivilege privilege the server
2199  * is given immediate impersonation, regardless of the conditions above.
2200  * If the client in question is associated with an anonymous token then
2201  * the server is given immediate impersonation. Or if the server simply
2202  * doesn't ask for impersonation but instead it wants to get the security
2203  * identification of a client, the server is given immediate impersonation.
2204  */
2205 BOOLEAN
2206 NTAPI
SeTokenCanImpersonate(_In_ PTOKEN ProcessToken,_In_ PTOKEN TokenToImpersonate,_In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel)2207 SeTokenCanImpersonate(
2208     _In_ PTOKEN ProcessToken,
2209     _In_ PTOKEN TokenToImpersonate,
2210     _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel)
2211 {
2212     BOOLEAN CanImpersonate;
2213     PAGED_CODE();
2214 
2215     /*
2216      * The server may want to obtain identification details of a client
2217      * instead of impersonating so just give the server a pass.
2218      */
2219     if (ImpersonationLevel < SecurityIdentification)
2220     {
2221         DPRINT("The server doesn't ask for impersonation\n");
2222         return TRUE;
2223     }
2224 
2225     /* Time to lock our tokens */
2226     SepAcquireTokenLockShared(ProcessToken);
2227     SepAcquireTokenLockShared(TokenToImpersonate);
2228 
2229     /*
2230      * As the name implies, an anonymous token has invisible security
2231      * identification details. By the general rule these tokens do not
2232      * pose a danger in terms of power escalation so give the server a pass.
2233      */
2234     if (RtlEqualLuid(&TokenToImpersonate->AuthenticationId,
2235                      &SeAnonymousAuthenticationId))
2236     {
2237         DPRINT("The token to impersonate has an anonymous authentication ID, allow impersonation either way\n");
2238         CanImpersonate = TRUE;
2239         goto Quit;
2240     }
2241 
2242     /* Allow impersonation for the process server if it's granted the impersonation privilege */
2243     if ((ProcessToken->TokenFlags & TOKEN_HAS_IMPERSONATE_PRIVILEGE) != 0)
2244     {
2245         DPRINT("The process is granted the impersonation privilege, allow impersonation\n");
2246         CanImpersonate = TRUE;
2247         goto Quit;
2248     }
2249 
2250     /*
2251      * Deny impersonation for the server if it wants to impersonate a client
2252      * beyond what the impersonation level originally permits.
2253      */
2254     if (ImpersonationLevel > TokenToImpersonate->ImpersonationLevel)
2255     {
2256         DPRINT1("Cannot impersonate a client above the permitted impersonation level!\n");
2257         CanImpersonate = FALSE;
2258         goto Quit;
2259     }
2260 
2261     /* Is the server authenticated on the same client originating session? */
2262     if (!RtlEqualLuid(&ProcessToken->AuthenticationId,
2263                       &TokenToImpersonate->OriginatingLogonSession))
2264     {
2265         /* It's not, check that at least both the server and client are the same user */
2266         if (!RtlEqualSid(ProcessToken->UserAndGroups[0].Sid,
2267                          TokenToImpersonate->UserAndGroups[0].Sid))
2268         {
2269             DPRINT1("Server and client aren't the same user!\n");
2270             CanImpersonate = FALSE;
2271             goto Quit;
2272         }
2273 
2274         /*
2275          * Make sure the tokens haven't diverged in terms of restrictions
2276          * that is one token is restricted but the other one isn't. If that
2277          * would have been the case then the server would have impersonated
2278          * a less restricted client thus potentially triggering an elevation,
2279          * which is not what we want.
2280          */
2281         if (SeTokenIsRestricted(ProcessToken) !=
2282             SeTokenIsRestricted(TokenToImpersonate))
2283         {
2284             DPRINT1("Attempting to impersonate a less restricted client token, bail out!\n");
2285             CanImpersonate = FALSE;
2286             goto Quit;
2287         }
2288     }
2289 
2290     /* If we've reached that far then we can impersonate! */
2291     DPRINT("We can impersonate\n");
2292     CanImpersonate = TRUE;
2293 
2294 Quit:
2295     SepReleaseTokenLock(TokenToImpersonate);
2296     SepReleaseTokenLock(ProcessToken);
2297     return CanImpersonate;
2298 }
2299 
2300 /* SYSTEM CALLS ***************************************************************/
2301 
2302 /**
2303  * @brief
2304  * Opens a token that is tied to a thread handle.
2305  *
2306  * @param[out] ThreadHandle
2307  * Thread handle where the token is about to be opened.
2308  *
2309  * @param[in] DesiredAccess
2310  * The request access right for the token.
2311  *
2312  * @param[in] OpenAsSelf
2313  * If set to TRUE, the access check will be made with the security context
2314  * of the process of the calling thread (opening as self). Otherwise the access
2315  * check will be made with the security context of the calling thread instead.
2316  *
2317  * @param[in] HandleAttributes
2318  * Handle attributes for the opened thread token handle.
2319  *
2320  * @param[out] TokenHandle
2321  * The opened token handle returned to the caller for use.
2322  *
2323  * @return
2324  * Returns STATUS_SUCCESS if the function has successfully opened the thread
2325  * token. STATUS_CANT_OPEN_ANONYMOUS is returned if a token has SecurityAnonymous
2326  * as impersonation level and we cannot open it. A failure NTSTATUS code is returned
2327  * otherwise.
2328  */
2329 NTSTATUS
2330 NTAPI
NtOpenThreadTokenEx(_In_ HANDLE ThreadHandle,_In_ ACCESS_MASK DesiredAccess,_In_ BOOLEAN OpenAsSelf,_In_ ULONG HandleAttributes,_Out_ PHANDLE TokenHandle)2331 NtOpenThreadTokenEx(
2332     _In_ HANDLE ThreadHandle,
2333     _In_ ACCESS_MASK DesiredAccess,
2334     _In_ BOOLEAN OpenAsSelf,
2335     _In_ ULONG HandleAttributes,
2336     _Out_ PHANDLE TokenHandle)
2337 {
2338     PETHREAD Thread;
2339     HANDLE hToken;
2340     PTOKEN Token;
2341     BOOLEAN CopyOnOpen, EffectiveOnly;
2342     SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
2343     SE_IMPERSONATION_STATE ImpersonationState;
2344     KPROCESSOR_MODE PreviousMode;
2345     NTSTATUS Status;
2346     BOOLEAN RestoreImpersonation = FALSE;
2347 
2348     PAGED_CODE();
2349 
2350     PreviousMode = ExGetPreviousMode();
2351 
2352     /* Ensure that we can give the handle to the caller */
2353     if (PreviousMode != KernelMode)
2354     {
2355         _SEH2_TRY
2356         {
2357             ProbeForWriteHandle(TokenHandle);
2358         }
2359         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2360         {
2361             /* Return the exception code */
2362             _SEH2_YIELD(return _SEH2_GetExceptionCode());
2363         }
2364         _SEH2_END;
2365     }
2366 
2367     /* Validate object attributes */
2368     HandleAttributes = ObpValidateAttributes(HandleAttributes, PreviousMode);
2369 
2370     /*
2371      * At first open the thread token for information access and verify
2372      * that the token associated with the thread is valid.
2373      */
2374     Status = ObReferenceObjectByHandle(ThreadHandle, THREAD_QUERY_INFORMATION,
2375                                        PsThreadType, PreviousMode, (PVOID*)&Thread,
2376                                        NULL);
2377     if (!NT_SUCCESS(Status))
2378     {
2379         DPRINT1("Failed to reference the object thread (Status 0x%lx)\n", Status);
2380         return Status;
2381     }
2382 
2383     /* Reference the token from the thread */
2384     Token = PsReferenceImpersonationToken(Thread, &CopyOnOpen, &EffectiveOnly,
2385                                           &ImpersonationLevel);
2386     if (Token == NULL)
2387     {
2388         DPRINT("Failed to reference the thread's impersonation token, thread has no token\n");
2389         ObDereferenceObject(Thread);
2390         return STATUS_NO_TOKEN;
2391     }
2392 
2393     /* Ensure the token has no anonymous security */
2394     if (ImpersonationLevel == SecurityAnonymous)
2395     {
2396         DPRINT1("The thread token has anonymous security, can't open it\n");
2397         PsDereferenceImpersonationToken(Token);
2398         ObDereferenceObject(Thread);
2399         return STATUS_CANT_OPEN_ANONYMOUS;
2400     }
2401 
2402     /* Revert to self if OpenAsSelf is specified */
2403     if (OpenAsSelf)
2404     {
2405         RestoreImpersonation = PsDisableImpersonation(PsGetCurrentThread(),
2406                                                       &ImpersonationState);
2407     }
2408 
2409     /* Call the private function to do the job */
2410     Status = SepOpenThreadToken(Thread,
2411                                 ThreadHandle,
2412                                 Token,
2413                                 DesiredAccess,
2414                                 HandleAttributes,
2415                                 EffectiveOnly,
2416                                 CopyOnOpen,
2417                                 ImpersonationLevel,
2418                                 PreviousMode,
2419                                 &hToken);
2420 
2421     /* Restore the impersonation back if needed */
2422     if (RestoreImpersonation)
2423     {
2424         PsRestoreImpersonation(PsGetCurrentThread(), &ImpersonationState);
2425     }
2426 
2427     /* Dereference the access token and the associated thread */
2428     ObDereferenceObject(Token);
2429     ObDereferenceObject(Thread);
2430 
2431     if (!NT_SUCCESS(Status))
2432     {
2433         DPRINT1("Failed to open the thread's token (Status 0x%lx)\n", Status);
2434         return Status;
2435     }
2436 
2437     /* Give the opened token handle to the caller */
2438     _SEH2_TRY
2439     {
2440         *TokenHandle = hToken;
2441     }
2442     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2443     {
2444         Status = _SEH2_GetExceptionCode();
2445     }
2446     _SEH2_END;
2447 
2448     return Status;
2449 }
2450 
2451 /**
2452  * @brief
2453  * Opens a token that is tied to a thread handle.
2454  *
2455  * @param[out] ThreadHandle
2456  * Thread handle where the token is about to be opened.
2457  *
2458  * @param[in] DesiredAccess
2459  * The request access right for the token.
2460  *
2461  * @param[in] OpenAsSelf
2462  * If set to TRUE, the access check will be made with the security context
2463  * of the process of the calling thread (opening as self). Otherwise the access
2464  * check will be made with the security context of the calling thread instead.
2465  *
2466  * @param[out] TokenHandle
2467  * The opened token handle returned to the caller for use.
2468  *
2469  * @return
2470  * See NtOpenThreadTokenEx.
2471  */
2472 NTSTATUS
2473 NTAPI
NtOpenThreadToken(_In_ HANDLE ThreadHandle,_In_ ACCESS_MASK DesiredAccess,_In_ BOOLEAN OpenAsSelf,_Out_ PHANDLE TokenHandle)2474 NtOpenThreadToken(
2475     _In_ HANDLE ThreadHandle,
2476     _In_ ACCESS_MASK DesiredAccess,
2477     _In_ BOOLEAN OpenAsSelf,
2478     _Out_ PHANDLE TokenHandle)
2479 {
2480     return NtOpenThreadTokenEx(ThreadHandle, DesiredAccess, OpenAsSelf, 0,
2481                                TokenHandle);
2482 }
2483 
2484 /**
2485  * @brief
2486  * Compares tokens if they're equal or not.
2487  *
2488  * @param[in] FirstToken
2489  * The first token.
2490  *
2491  * @param[in] SecondToken
2492  * The second token.
2493  *
2494  * @param[out] Equal
2495  * The retrieved value which determines if the tokens are
2496  * equal or not.
2497  *
2498  * @return
2499  * Returns STATUS_SUCCESS, otherwise it returns a failure NTSTATUS code.
2500  */
2501 NTSTATUS
2502 NTAPI
NtCompareTokens(_In_ HANDLE FirstTokenHandle,_In_ HANDLE SecondTokenHandle,_Out_ PBOOLEAN Equal)2503 NtCompareTokens(
2504     _In_ HANDLE FirstTokenHandle,
2505     _In_ HANDLE SecondTokenHandle,
2506     _Out_ PBOOLEAN Equal)
2507 {
2508     KPROCESSOR_MODE PreviousMode;
2509     PTOKEN FirstToken, SecondToken;
2510     BOOLEAN IsEqual;
2511     NTSTATUS Status;
2512 
2513     PAGED_CODE();
2514 
2515     PreviousMode = ExGetPreviousMode();
2516 
2517     if (PreviousMode != KernelMode)
2518     {
2519         _SEH2_TRY
2520         {
2521             ProbeForWriteBoolean(Equal);
2522         }
2523         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2524         {
2525             /* Return the exception code */
2526             _SEH2_YIELD(return _SEH2_GetExceptionCode());
2527         }
2528         _SEH2_END;
2529     }
2530 
2531     Status = ObReferenceObjectByHandle(FirstTokenHandle,
2532                                        TOKEN_QUERY,
2533                                        SeTokenObjectType,
2534                                        PreviousMode,
2535                                        (PVOID*)&FirstToken,
2536                                        NULL);
2537     if (!NT_SUCCESS(Status))
2538     {
2539         DPRINT1("ObReferenceObjectByHandle() failed (Status 0x%lx)\n", Status);
2540         return Status;
2541     }
2542 
2543     Status = ObReferenceObjectByHandle(SecondTokenHandle,
2544                                        TOKEN_QUERY,
2545                                        SeTokenObjectType,
2546                                        PreviousMode,
2547                                        (PVOID*)&SecondToken,
2548                                        NULL);
2549     if (!NT_SUCCESS(Status))
2550     {
2551         DPRINT1("ObReferenceObjectByHandle() failed (Status 0x%lx)\n", Status);
2552         ObDereferenceObject(FirstToken);
2553         return Status;
2554     }
2555 
2556     if (FirstToken != SecondToken)
2557     {
2558         Status = SepCompareTokens(FirstToken,
2559                                   SecondToken,
2560                                   &IsEqual);
2561     }
2562     else
2563     {
2564         IsEqual = TRUE;
2565     }
2566 
2567     ObDereferenceObject(SecondToken);
2568     ObDereferenceObject(FirstToken);
2569 
2570     if (NT_SUCCESS(Status))
2571     {
2572         _SEH2_TRY
2573         {
2574             *Equal = IsEqual;
2575         }
2576         _SEH2_EXCEPT(ExSystemExceptionFilter())
2577         {
2578             Status = _SEH2_GetExceptionCode();
2579         }
2580         _SEH2_END;
2581     }
2582 
2583     return Status;
2584 }
2585 
2586 /**
2587  * @brief
2588  * Allows the calling thread to impersonate the system's anonymous
2589  * logon token.
2590  *
2591  * @param[in] ThreadHandle
2592  * A handle to the thread to start the procedure of logon token
2593  * impersonation. The thread must have the THREAD_IMPERSONATE
2594  * access right.
2595  *
2596  * @return
2597  * Returns STATUS_SUCCESS if the thread has successfully impersonated the
2598  * anonymous logon token, otherwise a failure NTSTATUS code is returned.
2599  *
2600  * @remarks
2601  * By default the system gives the opportunity to the caller to impersonate
2602  * the anonymous logon token without including the Everyone Group SID.
2603  * In cases where the caller wants to impersonate the token including such
2604  * group, the EveryoneIncludesAnonymous registry value setting has to be set
2605  * to 1, from HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa registry
2606  * path. The calling thread must invoke PsRevertToSelf when impersonation
2607  * is no longer needed or RevertToSelf if the calling execution is done
2608  * in user mode.
2609  */
2610 NTSTATUS
2611 NTAPI
NtImpersonateAnonymousToken(_In_ HANDLE ThreadHandle)2612 NtImpersonateAnonymousToken(
2613     _In_ HANDLE ThreadHandle)
2614 {
2615     PETHREAD Thread;
2616     KPROCESSOR_MODE PreviousMode;
2617     NTSTATUS Status;
2618     PAGED_CODE();
2619 
2620     PreviousMode = ExGetPreviousMode();
2621 
2622     /* Obtain the thread object from the handle */
2623     Status = ObReferenceObjectByHandle(ThreadHandle,
2624                                        THREAD_IMPERSONATE,
2625                                        PsThreadType,
2626                                        PreviousMode,
2627                                        (PVOID*)&Thread,
2628                                        NULL);
2629     if (!NT_SUCCESS(Status))
2630     {
2631         DPRINT1("NtImpersonateAnonymousToken(): Failed to reference the object (Status 0x%lx)\n", Status);
2632         return Status;
2633     }
2634 
2635     /* Call the private routine to impersonate the token */
2636     Status = SepImpersonateAnonymousToken(Thread, PreviousMode);
2637     if (!NT_SUCCESS(Status))
2638     {
2639         DPRINT1("NtImpersonateAnonymousToken(): Failed to impersonate the token (Status 0x%lx)\n", Status);
2640     }
2641 
2642     ObDereferenceObject(Thread);
2643     return Status;
2644 }
2645 
2646 /* EOF */
2647