xref: /reactos/ntoskrnl/ps/security.c (revision 8e2fe925)
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
PspDeleteProcessSecurity(IN PEPROCESS Process)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
PspDeleteThreadSecurity(IN PETHREAD Thread)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
PspInitializeProcessSecurity(IN PEPROCESS Process,IN PEPROCESS Parent OPTIONAL)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
PspWriteTebImpersonationInfo(IN PETHREAD Thread,IN PETHREAD CurrentThread)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
PspAssignPrimaryToken(IN PEPROCESS Process,IN HANDLE Token,IN PACCESS_TOKEN AccessToken OPTIONAL)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
PspSetPrimaryToken(IN PEPROCESS Process,IN HANDLE TokenHandle OPTIONAL,IN PACCESS_TOKEN Token OPTIONAL)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          * In case LUID device maps are enable, we may not be using
331          * system device map for this process, but a logon LUID based
332          * device map. Because we change primary token, this usage is
333          * no longer valid, so dereference the process device map
334          */
335         if (ObIsLUIDDeviceMapsEnabled()) ObDereferenceDeviceMap(Process);
336     }
337 
338     /* Dereference the token */
339     if (!Token) ObDereferenceObject(NewToken);
340     return Status;
341 }
342 
343 /* FUNCTIONS *****************************************************************/
344 
345 /*
346  * @implemented
347  */
348 NTSTATUS
349 NTAPI
NtOpenProcessToken(IN HANDLE ProcessHandle,IN ACCESS_MASK DesiredAccess,OUT PHANDLE TokenHandle)350 NtOpenProcessToken(IN HANDLE ProcessHandle,
351                    IN ACCESS_MASK DesiredAccess,
352                    OUT PHANDLE TokenHandle)
353 {
354     /* Call the newer API */
355     return NtOpenProcessTokenEx(ProcessHandle,
356                                 DesiredAccess,
357                                 0,
358                                 TokenHandle);
359 }
360 
361 /*
362  * @implemented
363  */
364 NTSTATUS
365 NTAPI
NtOpenProcessTokenEx(IN HANDLE ProcessHandle,IN ACCESS_MASK DesiredAccess,IN ULONG HandleAttributes,OUT PHANDLE TokenHandle)366 NtOpenProcessTokenEx(IN HANDLE ProcessHandle,
367                      IN ACCESS_MASK DesiredAccess,
368                      IN ULONG HandleAttributes,
369                      OUT PHANDLE TokenHandle)
370 {
371     PACCESS_TOKEN Token;
372     HANDLE hToken;
373     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
374     NTSTATUS Status;
375     PAGED_CODE();
376     PSTRACE(PS_SECURITY_DEBUG,
377             "Process: %p DesiredAccess: %lx\n", ProcessHandle, DesiredAccess);
378 
379     /* Check if caller was user-mode */
380     if (PreviousMode != KernelMode)
381     {
382         /* Enter SEH for probing */
383         _SEH2_TRY
384         {
385             /* Probe the token handle */
386             ProbeForWriteHandle(TokenHandle);
387         }
388         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
389         {
390             /* Return the exception code */
391             _SEH2_YIELD(return _SEH2_GetExceptionCode());
392         }
393         _SEH2_END;
394     }
395 
396     /* Validate object attributes */
397     HandleAttributes = ObpValidateAttributes(HandleAttributes, PreviousMode);
398 
399     /* Open the process token */
400     Status = PsOpenTokenOfProcess(ProcessHandle, &Token);
401     if (NT_SUCCESS(Status))
402     {
403         /* Reference it by handle and dereference the pointer */
404         Status = ObOpenObjectByPointer(Token,
405                                        HandleAttributes,
406                                        NULL,
407                                        DesiredAccess,
408                                        SeTokenObjectType,
409                                        PreviousMode,
410                                        &hToken);
411         ObDereferenceObject(Token);
412 
413         /* Make sure we got a handle */
414         if (NT_SUCCESS(Status))
415         {
416             /* Enter SEH for write */
417             _SEH2_TRY
418             {
419                 /* Return the handle */
420                 *TokenHandle = hToken;
421             }
422             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
423             {
424                 /* Get exception code */
425                 Status = _SEH2_GetExceptionCode();
426             }
427             _SEH2_END;
428         }
429     }
430 
431     /* Return status */
432     return Status;
433 }
434 
435 /*
436  * @implemented
437  */
438 PACCESS_TOKEN
439 NTAPI
PsReferencePrimaryToken(PEPROCESS Process)440 PsReferencePrimaryToken(PEPROCESS Process)
441 {
442     PACCESS_TOKEN Token;
443     PAGED_CODE();
444     PSTRACE(PS_SECURITY_DEBUG, "Process: %p\n", Process);
445 
446     /* Fast Reference the Token */
447     Token = ObFastReferenceObject(&Process->Token);
448 
449     /* Check if we got the Token or if we got locked */
450     if (!Token)
451     {
452         /* Lock the Process */
453         PspLockProcessSecurityShared(Process);
454 
455         /* Do a Locked Fast Reference */
456         Token = ObFastReferenceObjectLocked(&Process->Token);
457 
458         /* Unlock the Process */
459         PspUnlockProcessSecurityShared(Process);
460     }
461 
462     /* Return the Token */
463     return Token;
464 }
465 
466 /*
467  * @implemented
468  */
469 NTSTATUS
470 NTAPI
PsOpenTokenOfProcess(IN HANDLE ProcessHandle,OUT PACCESS_TOKEN * Token)471 PsOpenTokenOfProcess(IN HANDLE ProcessHandle,
472                      OUT PACCESS_TOKEN* Token)
473 {
474     PEPROCESS Process;
475     NTSTATUS Status;
476     PAGED_CODE();
477     PSTRACE(PS_SECURITY_DEBUG, "Process: %p\n", ProcessHandle);
478 
479     /* Get the Token */
480     Status = ObReferenceObjectByHandle(ProcessHandle,
481                                        PROCESS_QUERY_INFORMATION,
482                                        PsProcessType,
483                                        ExGetPreviousMode(),
484                                        (PVOID*)&Process,
485                                        NULL);
486     if (NT_SUCCESS(Status))
487     {
488         /* Reference the token and dereference the process */
489         *Token = PsReferencePrimaryToken(Process);
490         ObDereferenceObject(Process);
491     }
492 
493     /* Return */
494     return Status;
495 }
496 
497 /*
498  * @implemented
499  */
500 NTSTATUS
501 NTAPI
PsAssignImpersonationToken(IN PETHREAD Thread,IN HANDLE TokenHandle)502 PsAssignImpersonationToken(IN PETHREAD Thread,
503                            IN HANDLE TokenHandle)
504 {
505     PACCESS_TOKEN Token;
506     SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
507     NTSTATUS Status;
508     PAGED_CODE();
509     PSTRACE(PS_SECURITY_DEBUG, "Thread: %p Token: %p\n", Thread, TokenHandle);
510 
511     /* Check if we were given a handle */
512     if (!TokenHandle)
513     {
514         /* Undo impersonation */
515         PsRevertThreadToSelf(Thread);
516         return STATUS_SUCCESS;
517     }
518 
519     /* Get the token object */
520     Status = ObReferenceObjectByHandle(TokenHandle,
521                                        TOKEN_IMPERSONATE,
522                                        SeTokenObjectType,
523                                        KeGetPreviousMode(),
524                                        (PVOID*)&Token,
525                                        NULL);
526     if (!NT_SUCCESS(Status)) return(Status);
527 
528     /* Make sure it's an impersonation token */
529     if (SeTokenType(Token) != TokenImpersonation)
530     {
531         /* Fail */
532         ObDereferenceObject(Token);
533         return STATUS_BAD_TOKEN_TYPE;
534     }
535 
536     /* Get the impersonation level */
537     ImpersonationLevel = SeTokenImpersonationLevel(Token);
538 
539     /* Call the impersonation API */
540     Status = PsImpersonateClient(Thread,
541                                  Token,
542                                  FALSE,
543                                  FALSE,
544                                  ImpersonationLevel);
545 
546     /* Dereference the token and return status */
547     ObDereferenceObject(Token);
548     return Status;
549 }
550 
551 /*
552  * @implemented
553  */
554 VOID
555 NTAPI
PsRevertToSelf(VOID)556 PsRevertToSelf(VOID)
557 {
558     /* Call the per-thread API */
559     PAGED_CODE();
560     PsRevertThreadToSelf(PsGetCurrentThread());
561 }
562 
563 /*
564  * @implemented
565  */
566 VOID
567 NTAPI
PsRevertThreadToSelf(IN PETHREAD Thread)568 PsRevertThreadToSelf(IN PETHREAD Thread)
569 {
570     PTOKEN Token = NULL;
571     PAGED_CODE();
572     PSTRACE(PS_SECURITY_DEBUG, "Thread: %p\n", Thread);
573 
574     /* Make sure we had impersonation information */
575     if (Thread->ActiveImpersonationInfo)
576     {
577         /* Lock the thread security */
578         PspLockThreadSecurityExclusive(Thread);
579 
580         /* Make sure it's still active */
581         if (Thread->ActiveImpersonationInfo)
582         {
583             /* Disable impersonation */
584             PspClearCrossThreadFlag(Thread, CT_ACTIVE_IMPERSONATION_INFO_BIT);
585 
586             /* Get the token */
587             Token = Thread->ImpersonationInfo->Token;
588         }
589 
590         /* Release thread security */
591         PspUnlockThreadSecurityExclusive(Thread);
592 
593         /* Check if we had a token */
594         if (Token)
595         {
596             /* Dereference the impersonation token */
597             ObDereferenceObject(Token);
598 
599             /* Write impersonation info to the TEB */
600             PspWriteTebImpersonationInfo(Thread, PsGetCurrentThread());
601         }
602     }
603 }
604 
605 /*
606  * @implemented
607  */
608 NTSTATUS
609 NTAPI
PsImpersonateClient(IN PETHREAD Thread,IN PACCESS_TOKEN Token,IN BOOLEAN CopyOnOpen,IN BOOLEAN EffectiveOnly,IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel)610 PsImpersonateClient(IN PETHREAD Thread,
611                     IN PACCESS_TOKEN Token,
612                     IN BOOLEAN CopyOnOpen,
613                     IN BOOLEAN EffectiveOnly,
614                     IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel)
615 {
616     PPS_IMPERSONATION_INFORMATION Impersonation, OldData;
617     PTOKEN OldToken = NULL, ProcessToken = NULL;
618     BOOLEAN CopiedToken = FALSE;
619     PACCESS_TOKEN NewToken, ImpersonationToken;
620     PEJOB Job;
621     NTSTATUS Status;
622 
623     PAGED_CODE();
624     PSTRACE(PS_SECURITY_DEBUG, "Thread: %p, Token: %p\n", Thread, Token);
625 
626     /* Check if we don't have a token */
627     if (!Token)
628     {
629         /* Make sure we're impersonating */
630         if (Thread->ActiveImpersonationInfo)
631         {
632             /* We seem to be, lock the thread */
633             PspLockThreadSecurityExclusive(Thread);
634 
635             /* Make sure we're still impersonating */
636             if (Thread->ActiveImpersonationInfo)
637             {
638                 /* Disable impersonation */
639                 PspClearCrossThreadFlag(Thread,
640                                         CT_ACTIVE_IMPERSONATION_INFO_BIT);
641 
642                 /* Get the token */
643                 OldToken = Thread->ImpersonationInfo->Token;
644             }
645 
646             /* Unlock the process and write TEB information */
647             PspUnlockThreadSecurityExclusive(Thread);
648             PspWriteTebImpersonationInfo(Thread, PsGetCurrentThread());
649         }
650     }
651     else
652     {
653         /* Check if we have impersonation info */
654         Impersonation = Thread->ImpersonationInfo;
655         if (!Impersonation)
656         {
657             /* We need to allocate a new one */
658             Impersonation = ExAllocatePoolWithTag(PagedPool,
659                                                   sizeof(*Impersonation),
660                                                   TAG_PS_IMPERSONATION);
661             if (!Impersonation) return STATUS_INSUFFICIENT_RESOURCES;
662 
663             /* Update the pointer */
664             OldData = InterlockedCompareExchangePointer((PVOID*)&Thread->
665                                                         ImpersonationInfo,
666                                                         Impersonation,
667                                                         NULL);
668             if (OldData)
669             {
670                 /* Someone beat us to it, free our copy */
671                 ExFreePoolWithTag(Impersonation, TAG_PS_IMPERSONATION);
672                 Impersonation = OldData;
673             }
674         }
675 
676         /*
677          * Assign the token we get from the caller first. The reason
678          * we have to do that is because we're unsure if we can impersonate
679          * in the first place. In the scenario where we cannot then the
680          * last resort is to make a copy of the token and assign that newly
681          * token to the impersonation information.
682          */
683         ImpersonationToken = Token;
684 
685         /* Obtain a token from the process */
686         ProcessToken = PsReferencePrimaryToken(Thread->ThreadsProcess);
687         if (!ProcessToken)
688         {
689             /* We can't continue this way without having the process' token... */
690             return STATUS_UNSUCCESSFUL;
691         }
692 
693         /* Make sure we can impersonate */
694         if (!SeTokenCanImpersonate(ProcessToken,
695                                    Token,
696                                    ImpersonationLevel))
697         {
698             /* We can't, make a copy of the token instead */
699             Status = SeCopyClientToken(Token,
700                                        SecurityIdentification,
701                                        KernelMode,
702                                        &NewToken);
703             if (!NT_SUCCESS(Status))
704             {
705                 /* We can't even make a copy of the token? Then bail out... */
706                 ObFastDereferenceObject(&Thread->ThreadsProcess->Token, ProcessToken);
707                 return Status;
708             }
709 
710             /*
711              * Since we cannot impersonate, assign the newly copied token.
712              * SeCopyClientToken already holds a reference to the copied token,
713              * let the code path below know that it must not reference it twice.
714              */
715             CopiedToken = TRUE;
716             ImpersonationLevel = SecurityIdentification;
717             ImpersonationToken = NewToken;
718         }
719 
720         /* We no longer need the process' token */
721         ObFastDereferenceObject(&Thread->ThreadsProcess->Token, ProcessToken);
722 
723         /* Check if this is a job */
724         Job = Thread->ThreadsProcess->Job;
725         if (Job != NULL)
726         {
727             /* No admin allowed in this job */
728             if ((Job->SecurityLimitFlags & JOB_OBJECT_SECURITY_NO_ADMIN) &&
729                 SeTokenIsAdmin(ImpersonationToken))
730             {
731                 if (CopiedToken)
732                 {
733                     ObDereferenceObject(ImpersonationToken);
734                 }
735 
736                 return STATUS_ACCESS_DENIED;
737             }
738 
739             /* No restricted tokens allowed in this job */
740             if ((Job->SecurityLimitFlags & JOB_OBJECT_SECURITY_RESTRICTED_TOKEN) &&
741                 SeTokenIsRestricted(ImpersonationToken))
742             {
743                 if (CopiedToken)
744                 {
745                     ObDereferenceObject(ImpersonationToken);
746                 }
747 
748                 return STATUS_ACCESS_DENIED;
749             }
750 
751             /* We don't support job filters yet */
752             if (Job->Filter != NULL)
753             {
754                 ASSERT(Job->Filter == NULL);
755             }
756         }
757 
758         /* Lock thread security */
759         PspLockThreadSecurityExclusive(Thread);
760 
761         /* Check if we're impersonating */
762         if (Thread->ActiveImpersonationInfo)
763         {
764             /* Get the token */
765             OldToken = Impersonation->Token;
766         }
767         else
768         {
769             /* Otherwise, enable impersonation */
770             PspSetCrossThreadFlag(Thread, CT_ACTIVE_IMPERSONATION_INFO_BIT);
771         }
772 
773         /* Now fill it out */
774         Impersonation->ImpersonationLevel = ImpersonationLevel;
775         Impersonation->CopyOnOpen = CopyOnOpen;
776         Impersonation->EffectiveOnly = EffectiveOnly;
777         Impersonation->Token = ImpersonationToken;
778 
779         /* Do not reference the token again if we copied it */
780         if (!CopiedToken)
781         {
782             ObReferenceObject(ImpersonationToken);
783         }
784 
785         /* Unlock the thread */
786         PspUnlockThreadSecurityExclusive(Thread);
787 
788         /* Write impersonation info to the TEB */
789         PspWriteTebImpersonationInfo(Thread, PsGetCurrentThread());
790     }
791 
792     /* Dereference the token and return success */
793     if (OldToken) PsDereferenceImpersonationToken(OldToken);
794     return STATUS_SUCCESS;
795 }
796 
797 /*
798  * @implemented
799  */
800 PACCESS_TOKEN
801 NTAPI
PsReferenceEffectiveToken(IN PETHREAD Thread,OUT IN PTOKEN_TYPE TokenType,OUT PBOOLEAN EffectiveOnly,OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel)802 PsReferenceEffectiveToken(IN PETHREAD Thread,
803                           OUT IN PTOKEN_TYPE TokenType,
804                           OUT PBOOLEAN EffectiveOnly,
805                           OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel)
806 {
807     PEPROCESS Process;
808     PACCESS_TOKEN Token = NULL;
809 
810     PAGED_CODE();
811 
812     PSTRACE(PS_SECURITY_DEBUG,
813             "Thread: %p, TokenType: %p\n", Thread, TokenType);
814 
815     /* Check if we don't have impersonation info */
816     Process = Thread->ThreadsProcess;
817     if (Thread->ActiveImpersonationInfo)
818     {
819         /* Lock the Process */
820         PspLockProcessSecurityShared(Process);
821 
822         /* Make sure impersonation is still active */
823         if (Thread->ActiveImpersonationInfo)
824         {
825             /* Get the token */
826             Token = Thread->ImpersonationInfo->Token;
827             ObReferenceObject(Token);
828 
829             /* Return data to caller */
830             *TokenType = TokenImpersonation;
831             *EffectiveOnly = Thread->ImpersonationInfo->EffectiveOnly;
832             *ImpersonationLevel = Thread->ImpersonationInfo->ImpersonationLevel;
833 
834             /* Unlock the Process */
835             PspUnlockProcessSecurityShared(Process);
836             return Token;
837         }
838 
839         /* Unlock the Process */
840         PspUnlockProcessSecurityShared(Process);
841     }
842 
843     /* Fast Reference the Token */
844     Token = ObFastReferenceObject(&Process->Token);
845 
846     /* Check if we got the Token or if we got locked */
847     if (!Token)
848     {
849         /* Lock the Process */
850         PspLockProcessSecurityShared(Process);
851 
852         /* Do a Locked Fast Reference */
853         Token = ObFastReferenceObjectLocked(&Process->Token);
854 
855         /* Unlock the Process */
856         PspUnlockProcessSecurityShared(Process);
857     }
858 
859     /* Return the token */
860     *TokenType = TokenPrimary;
861     *EffectiveOnly = FALSE;
862     // NOTE: ImpersonationLevel is left untouched on purpose!
863     return Token;
864 }
865 
866 /*
867  * @implemented
868  */
869 PACCESS_TOKEN
870 NTAPI
PsReferenceImpersonationToken(IN PETHREAD Thread,OUT PBOOLEAN CopyOnOpen,OUT PBOOLEAN EffectiveOnly,OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel)871 PsReferenceImpersonationToken(IN PETHREAD Thread,
872                               OUT PBOOLEAN CopyOnOpen,
873                               OUT PBOOLEAN EffectiveOnly,
874                               OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel)
875 {
876     PTOKEN Token = NULL;
877     PAGED_CODE();
878     PSTRACE(PS_SECURITY_DEBUG, "Thread: %p\n", Thread);
879 
880     /* If we don't have impersonation info, just quit */
881     if (!Thread->ActiveImpersonationInfo) return NULL;
882 
883     /* Lock the thread */
884     PspLockThreadSecurityShared(Thread);
885 
886     /* Make sure we still have active impersonation */
887     if (Thread->ActiveImpersonationInfo)
888     {
889         /* Return data from caller */
890         ObReferenceObject(Thread->ImpersonationInfo->Token);
891         *ImpersonationLevel = Thread->ImpersonationInfo->ImpersonationLevel;
892         *CopyOnOpen = Thread->ImpersonationInfo->CopyOnOpen;
893         *EffectiveOnly = Thread->ImpersonationInfo->EffectiveOnly;
894 
895         /* Set the token */
896         Token = Thread->ImpersonationInfo->Token;
897     }
898 
899     /* Unlock thread and return impersonation token */
900     PspUnlockThreadSecurityShared(Thread);
901     return Token;
902 }
903 
904 #undef PsDereferenceImpersonationToken
905 /*
906  * @implemented
907  */
908 VOID
909 NTAPI
PsDereferenceImpersonationToken(IN PACCESS_TOKEN ImpersonationToken)910 PsDereferenceImpersonationToken(IN PACCESS_TOKEN ImpersonationToken)
911 {
912     PAGED_CODE();
913 
914     /* If we got a token, dereference it */
915     if (ImpersonationToken) ObDereferenceObject(ImpersonationToken);
916 }
917 
918 #undef PsDereferencePrimaryToken
919 /*
920  * @implemented
921  */
922 VOID
923 NTAPI
PsDereferencePrimaryToken(IN PACCESS_TOKEN PrimaryToken)924 PsDereferencePrimaryToken(IN PACCESS_TOKEN PrimaryToken)
925 {
926     PAGED_CODE();
927 
928     /* Dereference the token*/
929     ObDereferenceObject(PrimaryToken);
930 }
931 
932 /*
933  * @implemented
934  */
935 BOOLEAN
936 NTAPI
PsDisableImpersonation(IN PETHREAD Thread,OUT PSE_IMPERSONATION_STATE ImpersonationState)937 PsDisableImpersonation(IN PETHREAD Thread,
938                        OUT PSE_IMPERSONATION_STATE ImpersonationState)
939 {
940     PPS_IMPERSONATION_INFORMATION Impersonation = NULL;
941     LONG OldFlags;
942     PAGED_CODE();
943     PSTRACE(PS_SECURITY_DEBUG,
944             "Thread: %p State: %p\n", Thread, ImpersonationState);
945 
946     /* Check if we don't have impersonation */
947     if (Thread->ActiveImpersonationInfo)
948     {
949         /* Lock thread security */
950         PspLockThreadSecurityExclusive(Thread);
951 
952         /* Disable impersonation */
953         OldFlags = PspClearCrossThreadFlag(Thread,
954                                            CT_ACTIVE_IMPERSONATION_INFO_BIT);
955 
956         /* Make sure nobody disabled it behind our back */
957         if (OldFlags & CT_ACTIVE_IMPERSONATION_INFO_BIT)
958         {
959             /* Copy the old state */
960             Impersonation = Thread->ImpersonationInfo;
961             ImpersonationState->Token = Impersonation->Token;
962             ImpersonationState->CopyOnOpen = Impersonation->CopyOnOpen;
963             ImpersonationState->EffectiveOnly = Impersonation->EffectiveOnly;
964             ImpersonationState->Level = Impersonation->ImpersonationLevel;
965         }
966 
967         /* Unlock thread security */
968         PspUnlockThreadSecurityExclusive(Thread);
969 
970         /* If we had impersonation info, return true */
971         if (Impersonation) return TRUE;
972     }
973 
974     /* Clear everything */
975     ImpersonationState->Token = NULL;
976     ImpersonationState->CopyOnOpen = FALSE;
977     ImpersonationState->EffectiveOnly = FALSE;
978     ImpersonationState->Level = SecurityAnonymous;
979     return FALSE;
980 }
981 
982 /*
983  * @implemented
984  */
985 VOID
986 NTAPI
PsRestoreImpersonation(IN PETHREAD Thread,IN PSE_IMPERSONATION_STATE ImpersonationState)987 PsRestoreImpersonation(IN PETHREAD Thread,
988                        IN PSE_IMPERSONATION_STATE ImpersonationState)
989 {
990     PTOKEN Token = NULL;
991     PPS_IMPERSONATION_INFORMATION Impersonation;
992     PAGED_CODE();
993     PSTRACE(PS_SECURITY_DEBUG,
994             "Thread: %p State: %p\n", Thread, ImpersonationState);
995 
996     /* Lock thread security */
997     PspLockThreadSecurityExclusive(Thread);
998 
999     /* Get the impersonation info */
1000     Impersonation = Thread->ImpersonationInfo;
1001 
1002     /* Check if we're impersonating */
1003     if (Thread->ActiveImpersonationInfo)
1004     {
1005         /* Get the token */
1006         Token = Impersonation->Token;
1007     }
1008 
1009     /* Check if we have an impersonation state */
1010     if (ImpersonationState)
1011     {
1012         /* Fill out the impersonation info */
1013         Impersonation->ImpersonationLevel = ImpersonationState->Level;
1014         Impersonation->CopyOnOpen = ImpersonationState->CopyOnOpen;
1015         Impersonation->EffectiveOnly = ImpersonationState->EffectiveOnly;
1016         Impersonation->Token = ImpersonationState->Token;
1017 
1018         /* Enable impersonation */
1019         PspSetCrossThreadFlag(Thread, CT_ACTIVE_IMPERSONATION_INFO_BIT);
1020     }
1021     else
1022     {
1023         /* Disable impersonation */
1024         PspClearCrossThreadFlag(Thread, CT_ACTIVE_IMPERSONATION_INFO_BIT);
1025     }
1026 
1027     /* Unlock the thread */
1028     PspUnlockThreadSecurityExclusive(Thread);
1029 
1030     /* Dereference the token */
1031     if (Token) ObDereferenceObject(Token);
1032 }
1033 
1034 NTSTATUS
1035 NTAPI
NtImpersonateThread(IN HANDLE ThreadHandle,IN HANDLE ThreadToImpersonateHandle,IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService)1036 NtImpersonateThread(IN HANDLE ThreadHandle,
1037                     IN HANDLE ThreadToImpersonateHandle,
1038                     IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService)
1039 {
1040     SECURITY_QUALITY_OF_SERVICE SafeServiceQoS;
1041     SECURITY_CLIENT_CONTEXT ClientContext;
1042     PETHREAD Thread;
1043     PETHREAD ThreadToImpersonate;
1044     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1045     NTSTATUS Status;
1046     PAGED_CODE();
1047     PSTRACE(PS_SECURITY_DEBUG,
1048             "Threads: %p %p\n", ThreadHandle, ThreadToImpersonateHandle);
1049 
1050     /* Check if call came from user mode */
1051     if (PreviousMode != KernelMode)
1052     {
1053         /* Enter SEH for probing */
1054         _SEH2_TRY
1055         {
1056             /* Probe QoS */
1057             ProbeForRead(SecurityQualityOfService,
1058                          sizeof(SECURITY_QUALITY_OF_SERVICE),
1059                          sizeof(ULONG));
1060 
1061             /* Capture it */
1062             SafeServiceQoS = *SecurityQualityOfService;
1063             SecurityQualityOfService = &SafeServiceQoS;
1064         }
1065         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1066         {
1067             /* Return the exception code */
1068             _SEH2_YIELD(return _SEH2_GetExceptionCode());
1069         }
1070         _SEH2_END;
1071     }
1072 
1073     /* Reference the thread */
1074     Status = ObReferenceObjectByHandle(ThreadHandle,
1075                                        THREAD_DIRECT_IMPERSONATION,
1076                                        PsThreadType,
1077                                        PreviousMode,
1078                                        (PVOID*)&Thread,
1079                                        NULL);
1080     if (NT_SUCCESS(Status))
1081     {
1082         /* Reference the impersonating thead */
1083         Status = ObReferenceObjectByHandle(ThreadToImpersonateHandle,
1084                                            THREAD_IMPERSONATE,
1085                                            PsThreadType,
1086                                            PreviousMode,
1087                                            (PVOID*)&ThreadToImpersonate,
1088                                            NULL);
1089         if (NT_SUCCESS(Status))
1090         {
1091             /* Create a client security context */
1092             Status = SeCreateClientSecurity(ThreadToImpersonate,
1093                                             SecurityQualityOfService,
1094                                             0,
1095                                             &ClientContext);
1096             if (NT_SUCCESS(Status))
1097             {
1098                 /* Do the impersonation */
1099                 SeImpersonateClient(&ClientContext, Thread);
1100                 if (ClientContext.ClientToken)
1101                 {
1102                     /* Dereference the client token if we had one */
1103                     ObDereferenceObject(ClientContext.ClientToken);
1104                 }
1105             }
1106 
1107             /* Dereference the thread to impersonate */
1108             ObDereferenceObject(ThreadToImpersonate);
1109         }
1110 
1111         /* Dereference the main thread */
1112         ObDereferenceObject(Thread);
1113     }
1114 
1115     /* Return status */
1116     return Status;
1117 }
1118 /* EOF */
1119