xref: /reactos/ntoskrnl/ps/security.c (revision 4567e13e)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/ps/security.c
5  * PURPOSE:         Process Manager: Process/Thread Security
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  *                  Eric Kohl
8  *                  Thomas Weidenmueller (w3seek@reactos.org)
9  */
10 
11 /* INCLUDES ******************************************************************/
12 
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16 
17 PTOKEN PspBootAccessToken;
18 
19 VOID
20 NTAPI
21 SeAssignPrimaryToken(
22     IN PEPROCESS Process,
23     IN PTOKEN Token
24 );
25 
26 /* PRIVATE FUNCTIONS *********************************************************/
27 
28 VOID
29 NTAPI
30 PspDeleteProcessSecurity(IN PEPROCESS Process)
31 {
32     PAGED_CODE();
33     PSTRACE(PS_SECURITY_DEBUG, "Process: %p\n", Process);
34 
35     /* Check if we have a token */
36     if (Process->Token.Object)
37     {
38         /* Deassign it */
39         SeDeassignPrimaryToken(Process);
40         Process->Token.Object = NULL;
41     }
42 }
43 
44 VOID
45 NTAPI
46 PspDeleteThreadSecurity(IN PETHREAD Thread)
47 {
48     PPS_IMPERSONATION_INFORMATION ImpersonationInfo = Thread->ImpersonationInfo;
49     PAGED_CODE();
50     PSTRACE(PS_SECURITY_DEBUG, "Thread: %p\n", Thread);
51 
52     /* Check if we have active impersonation info */
53     if (Thread->ActiveImpersonationInfo)
54     {
55         /* Dereference its token */
56         ObDereferenceObject(ImpersonationInfo->Token);
57     }
58 
59     /* Check if we have impersonation info */
60     if (ImpersonationInfo)
61     {
62         /* Free it */
63         ExFreePool(ImpersonationInfo);
64         PspClearCrossThreadFlag(Thread, CT_ACTIVE_IMPERSONATION_INFO_BIT);
65         Thread->ImpersonationInfo = NULL;
66     }
67 }
68 
69 NTSTATUS
70 NTAPI
71 PspInitializeProcessSecurity(IN PEPROCESS Process,
72                              IN PEPROCESS Parent OPTIONAL)
73 {
74     NTSTATUS Status = STATUS_SUCCESS;
75     PTOKEN NewToken, ParentToken;
76     PAGED_CODE();
77     PSTRACE(PS_SECURITY_DEBUG, "Process: %p\n", Process);
78 
79     /* If we have a parent, then duplicate the Token */
80     if (Parent)
81     {
82         /* Get the Parent Token */
83         ParentToken = PsReferencePrimaryToken(Parent);
84 
85         /* Duplicate it */
86         Status = SeSubProcessToken(ParentToken,
87                                    &NewToken,
88                                    TRUE,
89                                    MmGetSessionId(Process));
90 
91         /* Dereference the Parent */
92         ObFastDereferenceObject(&Parent->Token, ParentToken);
93 
94         /* Set the new Token */
95         if (NT_SUCCESS(Status))
96         {
97             /* Initailize the fast reference */
98             ObInitializeFastReference(&Process->Token, NewToken);
99         }
100     }
101     else
102     {
103         /* No parent, assign the Boot Token */
104         ObInitializeFastReference(&Process->Token, NULL);
105         SeAssignPrimaryToken(Process, PspBootAccessToken);
106     }
107 
108     /* Return to caller */
109     return Status;
110 }
111 
112 NTSTATUS
113 NTAPI
114 PspWriteTebImpersonationInfo(IN PETHREAD Thread,
115                              IN PETHREAD CurrentThread)
116 {
117     PEPROCESS Process;
118     PTEB Teb;
119     BOOLEAN Attached = FALSE;
120     BOOLEAN IsImpersonating;
121     KAPC_STATE ApcState;
122     PAGED_CODE();
123     PSTRACE(PS_SECURITY_DEBUG, "Thread: %p\n", Thread);
124 
125     /* Sanity check */
126     ASSERT(CurrentThread == PsGetCurrentThread());
127 
128     /* Get process and TEB */
129     Process = Thread->ThreadsProcess;
130     Teb = Thread->Tcb.Teb;
131     if (Teb)
132     {
133         /* Check if we're not in the right process */
134         if (Thread->Tcb.ApcState.Process != &Process->Pcb)
135         {
136             /* Attach to the process */
137             KeStackAttachProcess(&Process->Pcb, &ApcState);
138             Attached = TRUE;
139         }
140 
141         /* Check if we're in a different thread or acquire rundown */
142         if ((Thread == CurrentThread) ||
143             (ExAcquireRundownProtection(&Thread->RundownProtect)))
144         {
145             /* Check if the thread is impersonating */
146             IsImpersonating = (BOOLEAN)Thread->ActiveImpersonationInfo;
147             if (IsImpersonating)
148             {
149                 /* Set TEB data */
150                 Teb->ImpersonationLocale = -1;
151                 Teb->IsImpersonating = 1;
152             }
153             else
154             {
155                 /* Set TEB data */
156                 Teb->ImpersonationLocale = 0;
157                 Teb->IsImpersonating = 0;
158             }
159         }
160 
161         /* Check if we're in a different thread */
162         if (Thread != CurrentThread)
163         {
164             /* Release protection */
165             ExReleaseRundownProtection(&Thread->RundownProtect);
166         }
167 
168         /* Detach */
169         if (Attached) KeUnstackDetachProcess(&ApcState);
170     }
171 
172     /* Return to caller */
173     return STATUS_SUCCESS;
174 }
175 
176 NTSTATUS
177 NTAPI
178 PspAssignPrimaryToken(IN PEPROCESS Process,
179                       IN HANDLE Token,
180                       IN PACCESS_TOKEN AccessToken OPTIONAL)
181 {
182     PACCESS_TOKEN NewToken = AccessToken, OldToken;
183     NTSTATUS Status;
184     PAGED_CODE();
185     PSTRACE(PS_SECURITY_DEBUG, "Process: %p Token: %p\n", Process, Token);
186 
187     /* Check if we don't have a pointer */
188     if (!AccessToken)
189     {
190         /* Reference it from the handle */
191         Status = ObReferenceObjectByHandle(Token,
192                                            TOKEN_ASSIGN_PRIMARY,
193                                            SeTokenObjectType,
194                                            ExGetPreviousMode(),
195                                            &NewToken,
196                                            NULL);
197         if (!NT_SUCCESS(Status)) return Status;
198     }
199 
200     /* Exchange tokens */
201     Status = SeExchangePrimaryToken(Process, NewToken, &OldToken);
202 
203     /* Acquire and release the lock */
204     PspLockProcessSecurityExclusive(Process);
205     PspUnlockProcessSecurityExclusive(Process);
206 
207     /* Dereference Tokens and Return */
208     if (NT_SUCCESS(Status)) ObDereferenceObject(OldToken);
209     if (!AccessToken) ObDereferenceObject(NewToken);
210     return Status;
211 }
212 
213 NTSTATUS
214 NTAPI
215 PspSetPrimaryToken(IN PEPROCESS Process,
216                    IN HANDLE TokenHandle OPTIONAL,
217                    IN PACCESS_TOKEN Token OPTIONAL)
218 {
219     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
220     BOOLEAN IsChildOrSibling;
221     PACCESS_TOKEN NewToken = Token;
222     NTSTATUS Status, AccessStatus;
223     BOOLEAN Result, SdAllocated;
224     PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
225     SECURITY_SUBJECT_CONTEXT SubjectContext;
226 
227     PSTRACE(PS_SECURITY_DEBUG, "Process: %p Token: %p\n", Process, Token);
228 
229     /* Reference the token by handle if we don't already have a token object */
230     if (!Token)
231     {
232         Status = ObReferenceObjectByHandle(TokenHandle,
233                                            TOKEN_ASSIGN_PRIMARY,
234                                            SeTokenObjectType,
235                                            PreviousMode,
236                                            (PVOID*)&NewToken,
237                                            NULL);
238         if (!NT_SUCCESS(Status)) return Status;
239     }
240 
241     /*
242      * Check whether this token is a child or sibling of the current process token.
243      * NOTE: On Windows Vista+ both of these checks (together with extra steps)
244      * are now performed by a new SeIsTokenAssignableToProcess() helper.
245      */
246     Status = SeIsTokenChild(NewToken, &IsChildOrSibling);
247     if (!NT_SUCCESS(Status))
248     {
249         /* Failed, dereference */
250         if (!Token) ObDereferenceObject(NewToken);
251         return Status;
252     }
253     if (!IsChildOrSibling)
254     {
255         Status = SeIsTokenSibling(NewToken, &IsChildOrSibling);
256         if (!NT_SUCCESS(Status))
257         {
258             /* Failed, dereference */
259             if (!Token) ObDereferenceObject(NewToken);
260             return Status;
261         }
262     }
263 
264     /* Check if this was an independent token */
265     if (!IsChildOrSibling)
266     {
267         /* Make sure we have the privilege to assign a new one */
268         if (!SeSinglePrivilegeCheck(SeAssignPrimaryTokenPrivilege,
269                                     PreviousMode))
270         {
271             /* Failed, dereference */
272             if (!Token) ObDereferenceObject(NewToken);
273             return STATUS_PRIVILEGE_NOT_HELD;
274         }
275     }
276 
277     /* Assign the token */
278     Status = PspAssignPrimaryToken(Process, NULL, NewToken);
279     if (NT_SUCCESS(Status))
280     {
281         /*
282          * We need to completely reverify if the process still has access to
283          * itself under this new token.
284          */
285         Status = ObGetObjectSecurity(Process,
286                                      &SecurityDescriptor,
287                                      &SdAllocated);
288         if (NT_SUCCESS(Status))
289         {
290             /* Setup the security context */
291             SubjectContext.ProcessAuditId = Process;
292             SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process);
293             SubjectContext.ClientToken = NULL;
294 
295             /* Do the access check */
296             Result = SeAccessCheck(SecurityDescriptor,
297                                    &SubjectContext,
298                                    FALSE,
299                                    MAXIMUM_ALLOWED,
300                                    0,
301                                    NULL,
302                                    &PsProcessType->TypeInfo.GenericMapping,
303                                    PreviousMode,
304                                    &Process->GrantedAccess,
305                                    &AccessStatus);
306 
307             /* Dereference the token and let go the SD */
308             ObFastDereferenceObject(&Process->Token,
309                                     SubjectContext.PrimaryToken);
310             ObReleaseObjectSecurity(SecurityDescriptor, SdAllocated);
311 
312             /* Remove access if it failed */
313             if (!Result) Process->GrantedAccess = 0;
314 
315             /* Setup granted access */
316             Process->GrantedAccess |= (PROCESS_VM_OPERATION |
317                                        PROCESS_VM_READ |
318                                        PROCESS_VM_WRITE |
319                                        PROCESS_QUERY_INFORMATION |
320                                        PROCESS_TERMINATE |
321                                        PROCESS_CREATE_THREAD |
322                                        PROCESS_DUP_HANDLE |
323                                        PROCESS_CREATE_PROCESS |
324                                        PROCESS_SET_INFORMATION |
325                                        STANDARD_RIGHTS_ALL |
326                                        PROCESS_SET_QUOTA);
327         }
328     }
329 
330     /* Dereference the token */
331     if (!Token) ObDereferenceObject(NewToken);
332     return Status;
333 }
334 
335 /* FUNCTIONS *****************************************************************/
336 
337 /*
338  * @implemented
339  */
340 NTSTATUS
341 NTAPI
342 NtOpenProcessToken(IN HANDLE ProcessHandle,
343                    IN ACCESS_MASK DesiredAccess,
344                    OUT PHANDLE TokenHandle)
345 {
346     /* Call the newer API */
347     return NtOpenProcessTokenEx(ProcessHandle,
348                                 DesiredAccess,
349                                 0,
350                                 TokenHandle);
351 }
352 
353 /*
354  * @implemented
355  */
356 NTSTATUS
357 NTAPI
358 NtOpenProcessTokenEx(IN HANDLE ProcessHandle,
359                      IN ACCESS_MASK DesiredAccess,
360                      IN ULONG HandleAttributes,
361                      OUT PHANDLE TokenHandle)
362 {
363     PACCESS_TOKEN Token;
364     HANDLE hToken;
365     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
366     NTSTATUS Status;
367     PAGED_CODE();
368     PSTRACE(PS_SECURITY_DEBUG,
369             "Process: %p DesiredAccess: %lx\n", ProcessHandle, DesiredAccess);
370 
371     /* Check if caller was user-mode */
372     if (PreviousMode != KernelMode)
373     {
374         /* Enter SEH for probing */
375         _SEH2_TRY
376         {
377             /* Probe the token handle */
378             ProbeForWriteHandle(TokenHandle);
379         }
380         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
381         {
382             /* Return the exception code */
383             _SEH2_YIELD(return _SEH2_GetExceptionCode());
384         }
385         _SEH2_END;
386     }
387 
388     /* Validate object attributes */
389     HandleAttributes = ObpValidateAttributes(HandleAttributes, PreviousMode);
390 
391     /* Open the process token */
392     Status = PsOpenTokenOfProcess(ProcessHandle, &Token);
393     if (NT_SUCCESS(Status))
394     {
395         /* Reference it by handle and dereference the pointer */
396         Status = ObOpenObjectByPointer(Token,
397                                        HandleAttributes,
398                                        NULL,
399                                        DesiredAccess,
400                                        SeTokenObjectType,
401                                        PreviousMode,
402                                        &hToken);
403         ObDereferenceObject(Token);
404 
405         /* Make sure we got a handle */
406         if (NT_SUCCESS(Status))
407         {
408             /* Enter SEH for write */
409             _SEH2_TRY
410             {
411                 /* Return the handle */
412                 *TokenHandle = hToken;
413             }
414             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
415             {
416                 /* Get exception code */
417                 Status = _SEH2_GetExceptionCode();
418             }
419             _SEH2_END;
420         }
421     }
422 
423     /* Return status */
424     return Status;
425 }
426 
427 /*
428  * @implemented
429  */
430 PACCESS_TOKEN
431 NTAPI
432 PsReferencePrimaryToken(PEPROCESS Process)
433 {
434     PACCESS_TOKEN Token;
435     PAGED_CODE();
436     PSTRACE(PS_SECURITY_DEBUG, "Process: %p\n", Process);
437 
438     /* Fast Reference the Token */
439     Token = ObFastReferenceObject(&Process->Token);
440 
441     /* Check if we got the Token or if we got locked */
442     if (!Token)
443     {
444         /* Lock the Process */
445         PspLockProcessSecurityShared(Process);
446 
447         /* Do a Locked Fast Reference */
448         Token = ObFastReferenceObjectLocked(&Process->Token);
449 
450         /* Unlock the Process */
451         PspUnlockProcessSecurityShared(Process);
452     }
453 
454     /* Return the Token */
455     return Token;
456 }
457 
458 /*
459  * @implemented
460  */
461 NTSTATUS
462 NTAPI
463 PsOpenTokenOfProcess(IN HANDLE ProcessHandle,
464                      OUT PACCESS_TOKEN* Token)
465 {
466     PEPROCESS Process;
467     NTSTATUS Status;
468     PAGED_CODE();
469     PSTRACE(PS_SECURITY_DEBUG, "Process: %p\n", ProcessHandle);
470 
471     /* Get the Token */
472     Status = ObReferenceObjectByHandle(ProcessHandle,
473                                        PROCESS_QUERY_INFORMATION,
474                                        PsProcessType,
475                                        ExGetPreviousMode(),
476                                        (PVOID*)&Process,
477                                        NULL);
478     if (NT_SUCCESS(Status))
479     {
480         /* Reference the token and dereference the process */
481         *Token = PsReferencePrimaryToken(Process);
482         ObDereferenceObject(Process);
483     }
484 
485     /* Return */
486     return Status;
487 }
488 
489 /*
490  * @implemented
491  */
492 NTSTATUS
493 NTAPI
494 PsAssignImpersonationToken(IN PETHREAD Thread,
495                            IN HANDLE TokenHandle)
496 {
497     PACCESS_TOKEN Token;
498     SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
499     NTSTATUS Status;
500     PAGED_CODE();
501     PSTRACE(PS_SECURITY_DEBUG, "Thread: %p Token: %p\n", Thread, TokenHandle);
502 
503     /* Check if we were given a handle */
504     if (!TokenHandle)
505     {
506         /* Undo impersonation */
507         PsRevertThreadToSelf(Thread);
508         return STATUS_SUCCESS;
509     }
510 
511     /* Get the token object */
512     Status = ObReferenceObjectByHandle(TokenHandle,
513                                        TOKEN_IMPERSONATE,
514                                        SeTokenObjectType,
515                                        KeGetPreviousMode(),
516                                        (PVOID*)&Token,
517                                        NULL);
518     if (!NT_SUCCESS(Status)) return(Status);
519 
520     /* Make sure it's an impersonation token */
521     if (SeTokenType(Token) != TokenImpersonation)
522     {
523         /* Fail */
524         ObDereferenceObject(Token);
525         return STATUS_BAD_TOKEN_TYPE;
526     }
527 
528     /* Get the impersonation level */
529     ImpersonationLevel = SeTokenImpersonationLevel(Token);
530 
531     /* Call the impersonation API */
532     Status = PsImpersonateClient(Thread,
533                                  Token,
534                                  FALSE,
535                                  FALSE,
536                                  ImpersonationLevel);
537 
538     /* Dereference the token and return status */
539     ObDereferenceObject(Token);
540     return Status;
541 }
542 
543 /*
544  * @implemented
545  */
546 VOID
547 NTAPI
548 PsRevertToSelf(VOID)
549 {
550     /* Call the per-thread API */
551     PAGED_CODE();
552     PsRevertThreadToSelf(PsGetCurrentThread());
553 }
554 
555 /*
556  * @implemented
557  */
558 VOID
559 NTAPI
560 PsRevertThreadToSelf(IN PETHREAD Thread)
561 {
562     PTOKEN Token = NULL;
563     PAGED_CODE();
564     PSTRACE(PS_SECURITY_DEBUG, "Thread: %p\n", Thread);
565 
566     /* Make sure we had impersonation information */
567     if (Thread->ActiveImpersonationInfo)
568     {
569         /* Lock the thread security */
570         PspLockThreadSecurityExclusive(Thread);
571 
572         /* Make sure it's still active */
573         if (Thread->ActiveImpersonationInfo)
574         {
575             /* Disable impersonation */
576             PspClearCrossThreadFlag(Thread, CT_ACTIVE_IMPERSONATION_INFO_BIT);
577 
578             /* Get the token */
579             Token = Thread->ImpersonationInfo->Token;
580         }
581 
582         /* Release thread security */
583         PspUnlockThreadSecurityExclusive(Thread);
584 
585         /* Check if we had a token */
586         if (Token)
587         {
588             /* Dereference the impersonation token */
589             ObDereferenceObject(Token);
590 
591             /* Write impersonation info to the TEB */
592             PspWriteTebImpersonationInfo(Thread, PsGetCurrentThread());
593         }
594     }
595 }
596 
597 /*
598  * @implemented
599  */
600 NTSTATUS
601 NTAPI
602 PsImpersonateClient(IN PETHREAD Thread,
603                     IN PACCESS_TOKEN Token,
604                     IN BOOLEAN CopyOnOpen,
605                     IN BOOLEAN EffectiveOnly,
606                     IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel)
607 {
608     PPS_IMPERSONATION_INFORMATION Impersonation, OldData;
609     PTOKEN OldToken = NULL;
610     PAGED_CODE();
611     PSTRACE(PS_SECURITY_DEBUG, "Thread: %p, Token: %p\n", Thread, Token);
612 
613     /* Check if we don't have a token */
614     if (!Token)
615     {
616         /* Make sure we're impersonating */
617         if (Thread->ActiveImpersonationInfo)
618         {
619             /* We seem to be, lock the thread */
620             PspLockThreadSecurityExclusive(Thread);
621 
622             /* Make sure we're still impersonating */
623             if (Thread->ActiveImpersonationInfo)
624             {
625                 /* Disable impersonation */
626                 PspClearCrossThreadFlag(Thread,
627                                         CT_ACTIVE_IMPERSONATION_INFO_BIT);
628 
629                 /* Get the token */
630                 OldToken = Thread->ImpersonationInfo->Token;
631             }
632 
633             /* Unlock the process and write TEB information */
634             PspUnlockThreadSecurityExclusive(Thread);
635             PspWriteTebImpersonationInfo(Thread, PsGetCurrentThread());
636         }
637     }
638     else
639     {
640         /* Check if we have impersonation info */
641         Impersonation = Thread->ImpersonationInfo;
642         if (!Impersonation)
643         {
644             /* We need to allocate a new one */
645             Impersonation = ExAllocatePoolWithTag(PagedPool,
646                                                   sizeof(*Impersonation),
647                                                   TAG_PS_IMPERSONATION);
648             if (!Impersonation) return STATUS_INSUFFICIENT_RESOURCES;
649 
650             /* Update the pointer */
651             OldData = InterlockedCompareExchangePointer((PVOID*)&Thread->
652                                                         ImpersonationInfo,
653                                                         Impersonation,
654                                                         NULL);
655             if (OldData)
656             {
657                 /* Someone beat us to it, free our copy */
658                 ExFreePoolWithTag(Impersonation, TAG_PS_IMPERSONATION);
659                 Impersonation = OldData;
660             }
661         }
662 
663         /* Check if this is a job, which we don't support yet */
664         if (Thread->ThreadsProcess->Job) ASSERT(FALSE);
665 
666         /* Lock thread security */
667         PspLockThreadSecurityExclusive(Thread);
668 
669         /* Check if we're impersonating */
670         if (Thread->ActiveImpersonationInfo)
671         {
672             /* Get the token */
673             OldToken = Impersonation->Token;
674         }
675         else
676         {
677             /* Otherwise, enable impersonation */
678             PspSetCrossThreadFlag(Thread, CT_ACTIVE_IMPERSONATION_INFO_BIT);
679         }
680 
681         /* Now fill it out */
682         Impersonation->ImpersonationLevel = ImpersonationLevel;
683         Impersonation->CopyOnOpen = CopyOnOpen;
684         Impersonation->EffectiveOnly = EffectiveOnly;
685         Impersonation->Token = Token;
686         ObReferenceObject(Token);
687 
688         /* Unlock the thread */
689         PspUnlockThreadSecurityExclusive(Thread);
690 
691         /* Write impersonation info to the TEB */
692         PspWriteTebImpersonationInfo(Thread, PsGetCurrentThread());
693     }
694 
695     /* Dereference the token and return success */
696     if (OldToken) PsDereferenceImpersonationToken(OldToken);
697     return STATUS_SUCCESS;
698 }
699 
700 /*
701  * @implemented
702  */
703 PACCESS_TOKEN
704 NTAPI
705 PsReferenceEffectiveToken(IN PETHREAD Thread,
706                           OUT IN PTOKEN_TYPE TokenType,
707                           OUT PBOOLEAN EffectiveOnly,
708                           OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel)
709 {
710     PEPROCESS Process;
711     PACCESS_TOKEN Token = NULL;
712 
713     PAGED_CODE();
714 
715     PSTRACE(PS_SECURITY_DEBUG,
716             "Thread: %p, TokenType: %p\n", Thread, TokenType);
717 
718     /* Check if we don't have impersonation info */
719     Process = Thread->ThreadsProcess;
720     if (Thread->ActiveImpersonationInfo)
721     {
722         /* Lock the Process */
723         PspLockProcessSecurityShared(Process);
724 
725         /* Make sure impersonation is still active */
726         if (Thread->ActiveImpersonationInfo)
727         {
728             /* Get the token */
729             Token = Thread->ImpersonationInfo->Token;
730             ObReferenceObject(Token);
731 
732             /* Return data to caller */
733             *TokenType = TokenImpersonation;
734             *EffectiveOnly = Thread->ImpersonationInfo->EffectiveOnly;
735             *ImpersonationLevel = Thread->ImpersonationInfo->ImpersonationLevel;
736 
737             /* Unlock the Process */
738             PspUnlockProcessSecurityShared(Process);
739             return Token;
740         }
741 
742         /* Unlock the Process */
743         PspUnlockProcessSecurityShared(Process);
744     }
745 
746     /* Fast Reference the Token */
747     Token = ObFastReferenceObject(&Process->Token);
748 
749     /* Check if we got the Token or if we got locked */
750     if (!Token)
751     {
752         /* Lock the Process */
753         PspLockProcessSecurityShared(Process);
754 
755         /* Do a Locked Fast Reference */
756         Token = ObFastReferenceObjectLocked(&Process->Token);
757 
758         /* Unlock the Process */
759         PspUnlockProcessSecurityShared(Process);
760     }
761 
762     /* Return the token */
763     *TokenType = TokenPrimary;
764     *EffectiveOnly = FALSE;
765     // NOTE: ImpersonationLevel is left untouched on purpose!
766     return Token;
767 }
768 
769 /*
770  * @implemented
771  */
772 PACCESS_TOKEN
773 NTAPI
774 PsReferenceImpersonationToken(IN PETHREAD Thread,
775                               OUT PBOOLEAN CopyOnOpen,
776                               OUT PBOOLEAN EffectiveOnly,
777                               OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel)
778 {
779     PTOKEN Token = NULL;
780     PAGED_CODE();
781     PSTRACE(PS_SECURITY_DEBUG, "Thread: %p\n", Thread);
782 
783     /* If we don't have impersonation info, just quit */
784     if (!Thread->ActiveImpersonationInfo) return NULL;
785 
786     /* Lock the thread */
787     PspLockThreadSecurityShared(Thread);
788 
789     /* Make sure we still have active impersonation */
790     if (Thread->ActiveImpersonationInfo)
791     {
792         /* Return data from caller */
793         ObReferenceObject(Thread->ImpersonationInfo->Token);
794         *ImpersonationLevel = Thread->ImpersonationInfo->ImpersonationLevel;
795         *CopyOnOpen = Thread->ImpersonationInfo->CopyOnOpen;
796         *EffectiveOnly = Thread->ImpersonationInfo->EffectiveOnly;
797 
798         /* Set the token */
799         Token = Thread->ImpersonationInfo->Token;
800     }
801 
802     /* Unlock thread and return impersonation token */
803     PspUnlockThreadSecurityShared(Thread);
804     return Token;
805 }
806 
807 #undef PsDereferenceImpersonationToken
808 /*
809  * @implemented
810  */
811 VOID
812 NTAPI
813 PsDereferenceImpersonationToken(IN PACCESS_TOKEN ImpersonationToken)
814 {
815     PAGED_CODE();
816 
817     /* If we got a token, dereference it */
818     if (ImpersonationToken) ObDereferenceObject(ImpersonationToken);
819 }
820 
821 #undef PsDereferencePrimaryToken
822 /*
823  * @implemented
824  */
825 VOID
826 NTAPI
827 PsDereferencePrimaryToken(IN PACCESS_TOKEN PrimaryToken)
828 {
829     PAGED_CODE();
830 
831     /* Dereference the token*/
832     ObDereferenceObject(PrimaryToken);
833 }
834 
835 /*
836  * @implemented
837  */
838 BOOLEAN
839 NTAPI
840 PsDisableImpersonation(IN PETHREAD Thread,
841                        OUT PSE_IMPERSONATION_STATE ImpersonationState)
842 {
843     PPS_IMPERSONATION_INFORMATION Impersonation = NULL;
844     LONG OldFlags;
845     PAGED_CODE();
846     PSTRACE(PS_SECURITY_DEBUG,
847             "Thread: %p State: %p\n", Thread, ImpersonationState);
848 
849     /* Check if we don't have impersonation */
850     if (Thread->ActiveImpersonationInfo)
851     {
852         /* Lock thread security */
853         PspLockThreadSecurityExclusive(Thread);
854 
855         /* Disable impersonation */
856         OldFlags = PspClearCrossThreadFlag(Thread,
857                                            CT_ACTIVE_IMPERSONATION_INFO_BIT);
858 
859         /* Make sure nobody disabled it behind our back */
860         if (OldFlags & CT_ACTIVE_IMPERSONATION_INFO_BIT)
861         {
862             /* Copy the old state */
863             Impersonation = Thread->ImpersonationInfo;
864             ImpersonationState->Token = Impersonation->Token;
865             ImpersonationState->CopyOnOpen = Impersonation->CopyOnOpen;
866             ImpersonationState->EffectiveOnly = Impersonation->EffectiveOnly;
867             ImpersonationState->Level = Impersonation->ImpersonationLevel;
868         }
869 
870         /* Unlock thread security */
871         PspUnlockThreadSecurityExclusive(Thread);
872 
873         /* If we had impersonation info, return true */
874         if (Impersonation) return TRUE;
875     }
876 
877     /* Clear everything */
878     ImpersonationState->Token = NULL;
879     ImpersonationState->CopyOnOpen = FALSE;
880     ImpersonationState->EffectiveOnly = FALSE;
881     ImpersonationState->Level = SecurityAnonymous;
882     return FALSE;
883 }
884 
885 /*
886  * @implemented
887  */
888 VOID
889 NTAPI
890 PsRestoreImpersonation(IN PETHREAD Thread,
891                        IN PSE_IMPERSONATION_STATE ImpersonationState)
892 {
893     PTOKEN Token = NULL;
894     PPS_IMPERSONATION_INFORMATION Impersonation;
895     PAGED_CODE();
896     PSTRACE(PS_SECURITY_DEBUG,
897             "Thread: %p State: %p\n", Thread, ImpersonationState);
898 
899     /* Lock thread security */
900     PspLockThreadSecurityExclusive(Thread);
901 
902     /* Get the impersonation info */
903     Impersonation = Thread->ImpersonationInfo;
904 
905     /* Check if we're impersonating */
906     if (Thread->ActiveImpersonationInfo)
907     {
908         /* Get the token */
909         Token = Impersonation->Token;
910     }
911 
912     /* Check if we have an impersonation state */
913     if (ImpersonationState)
914     {
915         /* Fill out the impersonation info */
916         Impersonation->ImpersonationLevel = ImpersonationState->Level;
917         Impersonation->CopyOnOpen = ImpersonationState->CopyOnOpen;
918         Impersonation->EffectiveOnly = ImpersonationState->EffectiveOnly;
919         Impersonation->Token = ImpersonationState->Token;
920 
921         /* Enable impersonation */
922         PspSetCrossThreadFlag(Thread, CT_ACTIVE_IMPERSONATION_INFO_BIT);
923     }
924     else
925     {
926         /* Disable impersonation */
927         PspClearCrossThreadFlag(Thread, CT_ACTIVE_IMPERSONATION_INFO_BIT);
928     }
929 
930     /* Unlock the thread */
931     PspUnlockThreadSecurityExclusive(Thread);
932 
933     /* Dereference the token */
934     if (Token) ObDereferenceObject(Token);
935 }
936 
937 NTSTATUS
938 NTAPI
939 NtImpersonateThread(IN HANDLE ThreadHandle,
940                     IN HANDLE ThreadToImpersonateHandle,
941                     IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService)
942 {
943     SECURITY_QUALITY_OF_SERVICE SafeServiceQoS;
944     SECURITY_CLIENT_CONTEXT ClientContext;
945     PETHREAD Thread;
946     PETHREAD ThreadToImpersonate;
947     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
948     NTSTATUS Status;
949     PAGED_CODE();
950     PSTRACE(PS_SECURITY_DEBUG,
951             "Threads: %p %p\n", ThreadHandle, ThreadToImpersonateHandle);
952 
953     /* Check if call came from user mode */
954     if (PreviousMode != KernelMode)
955     {
956         /* Enter SEH for probing */
957         _SEH2_TRY
958         {
959             /* Probe QoS */
960             ProbeForRead(SecurityQualityOfService,
961                          sizeof(SECURITY_QUALITY_OF_SERVICE),
962                          sizeof(ULONG));
963 
964             /* Capture it */
965             SafeServiceQoS = *SecurityQualityOfService;
966             SecurityQualityOfService = &SafeServiceQoS;
967         }
968         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
969         {
970             /* Return the exception code */
971             _SEH2_YIELD(return _SEH2_GetExceptionCode());
972         }
973         _SEH2_END;
974     }
975 
976     /* Reference the thread */
977     Status = ObReferenceObjectByHandle(ThreadHandle,
978                                        THREAD_DIRECT_IMPERSONATION,
979                                        PsThreadType,
980                                        PreviousMode,
981                                        (PVOID*)&Thread,
982                                        NULL);
983     if (NT_SUCCESS(Status))
984     {
985         /* Reference the impersonating thead */
986         Status = ObReferenceObjectByHandle(ThreadToImpersonateHandle,
987                                            THREAD_IMPERSONATE,
988                                            PsThreadType,
989                                            PreviousMode,
990                                            (PVOID*)&ThreadToImpersonate,
991                                            NULL);
992         if (NT_SUCCESS(Status))
993         {
994             /* Create a client security context */
995             Status = SeCreateClientSecurity(ThreadToImpersonate,
996                                             SecurityQualityOfService,
997                                             0,
998                                             &ClientContext);
999             if (NT_SUCCESS(Status))
1000             {
1001                 /* Do the impersonation */
1002                 SeImpersonateClient(&ClientContext, Thread);
1003                 if (ClientContext.ClientToken)
1004                 {
1005                     /* Dereference the client token if we had one */
1006                     ObDereferenceObject(ClientContext.ClientToken);
1007                 }
1008             }
1009 
1010             /* Dereference the thread to impersonate */
1011             ObDereferenceObject(ThreadToImpersonate);
1012         }
1013 
1014         /* Dereference the main thread */
1015         ObDereferenceObject(Thread);
1016     }
1017 
1018     /* Return status */
1019     return Status;
1020 }
1021 /* EOF */
1022