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