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