/* * PROJECT: ReactOS Kernel * LICENSE: GPL - See COPYING in the top level directory * FILE: ntoskrnl/ps/security.c * PURPOSE: Process Manager: Process/Thread Security * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) * Eric Kohl * Thomas Weidenmueller (w3seek@reactos.org) */ /* INCLUDES ******************************************************************/ #include #define NDEBUG #include PTOKEN PspBootAccessToken; VOID NTAPI SeAssignPrimaryToken( IN PEPROCESS Process, IN PTOKEN Token ); /* PRIVATE FUNCTIONS *********************************************************/ VOID NTAPI PspDeleteProcessSecurity(IN PEPROCESS Process) { PAGED_CODE(); PSTRACE(PS_SECURITY_DEBUG, "Process: %p\n", Process); /* Check if we have a token */ if (Process->Token.Object) { /* Deassign it */ SeDeassignPrimaryToken(Process); Process->Token.Object = NULL; } } VOID NTAPI PspDeleteThreadSecurity(IN PETHREAD Thread) { PPS_IMPERSONATION_INFORMATION ImpersonationInfo = Thread->ImpersonationInfo; PAGED_CODE(); PSTRACE(PS_SECURITY_DEBUG, "Thread: %p\n", Thread); /* Check if we have active impersonation info */ if (Thread->ActiveImpersonationInfo) { /* Dereference its token */ ObDereferenceObject(ImpersonationInfo->Token); } /* Check if we have impersonation info */ if (ImpersonationInfo) { /* Free it */ ExFreePool(ImpersonationInfo); PspClearCrossThreadFlag(Thread, CT_ACTIVE_IMPERSONATION_INFO_BIT); Thread->ImpersonationInfo = NULL; } } NTSTATUS NTAPI PspInitializeProcessSecurity(IN PEPROCESS Process, IN PEPROCESS Parent OPTIONAL) { NTSTATUS Status = STATUS_SUCCESS; PTOKEN NewToken, ParentToken; PAGED_CODE(); PSTRACE(PS_SECURITY_DEBUG, "Process: %p\n", Process); /* If we have a parent, then duplicate the Token */ if (Parent) { /* Get the Parent Token */ ParentToken = PsReferencePrimaryToken(Parent); /* Duplicate it */ Status = SeSubProcessToken(ParentToken, &NewToken, TRUE, MmGetSessionId(Process)); /* Dereference the Parent */ ObFastDereferenceObject(&Parent->Token, ParentToken); /* Set the new Token */ if (NT_SUCCESS(Status)) { /* Initailize the fast reference */ ObInitializeFastReference(&Process->Token, NewToken); } } else { /* No parent, assign the Boot Token */ ObInitializeFastReference(&Process->Token, NULL); SeAssignPrimaryToken(Process, PspBootAccessToken); } /* Return to caller */ return Status; } NTSTATUS NTAPI PspWriteTebImpersonationInfo(IN PETHREAD Thread, IN PETHREAD CurrentThread) { PEPROCESS Process; PTEB Teb; BOOLEAN Attached = FALSE; BOOLEAN IsImpersonating; KAPC_STATE ApcState; PAGED_CODE(); PSTRACE(PS_SECURITY_DEBUG, "Thread: %p\n", Thread); /* Sanity check */ ASSERT(CurrentThread == PsGetCurrentThread()); /* Get process and TEB */ Process = Thread->ThreadsProcess; Teb = Thread->Tcb.Teb; if (Teb) { /* Check if we're not in the right process */ if (Thread->Tcb.ApcState.Process != &Process->Pcb) { /* Attach to the process */ KeStackAttachProcess(&Process->Pcb, &ApcState); Attached = TRUE; } /* Check if we're in a different thread or acquire rundown */ if ((Thread == CurrentThread) || (ExAcquireRundownProtection(&Thread->RundownProtect))) { /* Check if the thread is impersonating */ IsImpersonating = (BOOLEAN)Thread->ActiveImpersonationInfo; if (IsImpersonating) { /* Set TEB data */ Teb->ImpersonationLocale = -1; Teb->IsImpersonating = 1; } else { /* Set TEB data */ Teb->ImpersonationLocale = 0; Teb->IsImpersonating = 0; } } /* Check if we're in a different thread */ if (Thread != CurrentThread) { /* Release protection */ ExReleaseRundownProtection(&Thread->RundownProtect); } /* Detach */ if (Attached) KeUnstackDetachProcess(&ApcState); } /* Return to caller */ return STATUS_SUCCESS; } NTSTATUS NTAPI PspAssignPrimaryToken(IN PEPROCESS Process, IN HANDLE Token, IN PACCESS_TOKEN AccessToken OPTIONAL) { PACCESS_TOKEN NewToken = AccessToken, OldToken; NTSTATUS Status; PAGED_CODE(); PSTRACE(PS_SECURITY_DEBUG, "Process: %p Token: %p\n", Process, Token); /* Check if we don't have a pointer */ if (!AccessToken) { /* Reference it from the handle */ Status = ObReferenceObjectByHandle(Token, TOKEN_ASSIGN_PRIMARY, SeTokenObjectType, ExGetPreviousMode(), &NewToken, NULL); if (!NT_SUCCESS(Status)) return Status; } /* Exchange tokens */ Status = SeExchangePrimaryToken(Process, NewToken, &OldToken); /* Acquire and release the lock */ PspLockProcessSecurityExclusive(Process); PspUnlockProcessSecurityExclusive(Process); /* Dereference Tokens and Return */ if (NT_SUCCESS(Status)) ObDereferenceObject(OldToken); if (!AccessToken) ObDereferenceObject(NewToken); return Status; } NTSTATUS NTAPI PspSetPrimaryToken(IN PEPROCESS Process, IN HANDLE TokenHandle OPTIONAL, IN PACCESS_TOKEN Token OPTIONAL) { KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); BOOLEAN IsChildOrSibling; PACCESS_TOKEN NewToken = Token; NTSTATUS Status, AccessStatus; BOOLEAN Result, SdAllocated; PSECURITY_DESCRIPTOR SecurityDescriptor = NULL; SECURITY_SUBJECT_CONTEXT SubjectContext; PSTRACE(PS_SECURITY_DEBUG, "Process: %p Token: %p\n", Process, Token); /* Reference the token by handle if we don't already have a token object */ if (!Token) { Status = ObReferenceObjectByHandle(TokenHandle, TOKEN_ASSIGN_PRIMARY, SeTokenObjectType, PreviousMode, (PVOID*)&NewToken, NULL); if (!NT_SUCCESS(Status)) return Status; } /* * Check whether this token is a child or sibling of the current process token. * NOTE: On Windows Vista+ both of these checks (together with extra steps) * are now performed by a new SeIsTokenAssignableToProcess() helper. */ Status = SeIsTokenChild(NewToken, &IsChildOrSibling); if (!NT_SUCCESS(Status)) { /* Failed, dereference */ if (!Token) ObDereferenceObject(NewToken); return Status; } if (!IsChildOrSibling) { Status = SeIsTokenSibling(NewToken, &IsChildOrSibling); if (!NT_SUCCESS(Status)) { /* Failed, dereference */ if (!Token) ObDereferenceObject(NewToken); return Status; } } /* Check if this was an independent token */ if (!IsChildOrSibling) { /* Make sure we have the privilege to assign a new one */ if (!SeSinglePrivilegeCheck(SeAssignPrimaryTokenPrivilege, PreviousMode)) { /* Failed, dereference */ if (!Token) ObDereferenceObject(NewToken); return STATUS_PRIVILEGE_NOT_HELD; } } /* Assign the token */ Status = PspAssignPrimaryToken(Process, NULL, NewToken); if (NT_SUCCESS(Status)) { /* * We need to completely reverify if the process still has access to * itself under this new token. */ Status = ObGetObjectSecurity(Process, &SecurityDescriptor, &SdAllocated); if (NT_SUCCESS(Status)) { /* Setup the security context */ SubjectContext.ProcessAuditId = Process; SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process); SubjectContext.ClientToken = NULL; /* Do the access check */ Result = SeAccessCheck(SecurityDescriptor, &SubjectContext, FALSE, MAXIMUM_ALLOWED, 0, NULL, &PsProcessType->TypeInfo.GenericMapping, PreviousMode, &Process->GrantedAccess, &AccessStatus); /* Dereference the token and let go the SD */ ObFastDereferenceObject(&Process->Token, SubjectContext.PrimaryToken); ObReleaseObjectSecurity(SecurityDescriptor, SdAllocated); /* Remove access if it failed */ if (!Result) Process->GrantedAccess = 0; /* Setup granted access */ Process->GrantedAccess |= (PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE | PROCESS_CREATE_THREAD | PROCESS_DUP_HANDLE | PROCESS_CREATE_PROCESS | PROCESS_SET_INFORMATION | STANDARD_RIGHTS_ALL | PROCESS_SET_QUOTA); } /* * In case LUID device maps are enable, we may not be using * system device map for this process, but a logon LUID based * device map. Because we change primary token, this usage is * no longer valid, so dereference the process device map */ if (ObIsLUIDDeviceMapsEnabled()) ObDereferenceDeviceMap(Process); } /* Dereference the token */ if (!Token) ObDereferenceObject(NewToken); return Status; } /* FUNCTIONS *****************************************************************/ /* * @implemented */ NTSTATUS NTAPI NtOpenProcessToken(IN HANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, OUT PHANDLE TokenHandle) { /* Call the newer API */ return NtOpenProcessTokenEx(ProcessHandle, DesiredAccess, 0, TokenHandle); } /* * @implemented */ NTSTATUS NTAPI NtOpenProcessTokenEx(IN HANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN ULONG HandleAttributes, OUT PHANDLE TokenHandle) { PACCESS_TOKEN Token; HANDLE hToken; KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); NTSTATUS Status; PAGED_CODE(); PSTRACE(PS_SECURITY_DEBUG, "Process: %p DesiredAccess: %lx\n", ProcessHandle, DesiredAccess); /* Check if caller was user-mode */ if (PreviousMode != KernelMode) { /* Enter SEH for probing */ _SEH2_TRY { /* Probe the token handle */ ProbeForWriteHandle(TokenHandle); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } /* Validate object attributes */ HandleAttributes = ObpValidateAttributes(HandleAttributes, PreviousMode); /* Open the process token */ Status = PsOpenTokenOfProcess(ProcessHandle, &Token); if (NT_SUCCESS(Status)) { /* Reference it by handle and dereference the pointer */ Status = ObOpenObjectByPointer(Token, HandleAttributes, NULL, DesiredAccess, SeTokenObjectType, PreviousMode, &hToken); ObDereferenceObject(Token); /* Make sure we got a handle */ if (NT_SUCCESS(Status)) { /* Enter SEH for write */ _SEH2_TRY { /* Return the handle */ *TokenHandle = hToken; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Get exception code */ Status = _SEH2_GetExceptionCode(); } _SEH2_END; } } /* Return status */ return Status; } /* * @implemented */ PACCESS_TOKEN NTAPI PsReferencePrimaryToken(PEPROCESS Process) { PACCESS_TOKEN Token; PAGED_CODE(); PSTRACE(PS_SECURITY_DEBUG, "Process: %p\n", Process); /* Fast Reference the Token */ Token = ObFastReferenceObject(&Process->Token); /* Check if we got the Token or if we got locked */ if (!Token) { /* Lock the Process */ PspLockProcessSecurityShared(Process); /* Do a Locked Fast Reference */ Token = ObFastReferenceObjectLocked(&Process->Token); /* Unlock the Process */ PspUnlockProcessSecurityShared(Process); } /* Return the Token */ return Token; } /* * @implemented */ NTSTATUS NTAPI PsOpenTokenOfProcess(IN HANDLE ProcessHandle, OUT PACCESS_TOKEN* Token) { PEPROCESS Process; NTSTATUS Status; PAGED_CODE(); PSTRACE(PS_SECURITY_DEBUG, "Process: %p\n", ProcessHandle); /* Get the Token */ Status = ObReferenceObjectByHandle(ProcessHandle, PROCESS_QUERY_INFORMATION, PsProcessType, ExGetPreviousMode(), (PVOID*)&Process, NULL); if (NT_SUCCESS(Status)) { /* Reference the token and dereference the process */ *Token = PsReferencePrimaryToken(Process); ObDereferenceObject(Process); } /* Return */ return Status; } /* * @implemented */ NTSTATUS NTAPI PsAssignImpersonationToken(IN PETHREAD Thread, IN HANDLE TokenHandle) { PACCESS_TOKEN Token; SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; NTSTATUS Status; PAGED_CODE(); PSTRACE(PS_SECURITY_DEBUG, "Thread: %p Token: %p\n", Thread, TokenHandle); /* Check if we were given a handle */ if (!TokenHandle) { /* Undo impersonation */ PsRevertThreadToSelf(Thread); return STATUS_SUCCESS; } /* Get the token object */ Status = ObReferenceObjectByHandle(TokenHandle, TOKEN_IMPERSONATE, SeTokenObjectType, KeGetPreviousMode(), (PVOID*)&Token, NULL); if (!NT_SUCCESS(Status)) return(Status); /* Make sure it's an impersonation token */ if (SeTokenType(Token) != TokenImpersonation) { /* Fail */ ObDereferenceObject(Token); return STATUS_BAD_TOKEN_TYPE; } /* Get the impersonation level */ ImpersonationLevel = SeTokenImpersonationLevel(Token); /* Call the impersonation API */ Status = PsImpersonateClient(Thread, Token, FALSE, FALSE, ImpersonationLevel); /* Dereference the token and return status */ ObDereferenceObject(Token); return Status; } /* * @implemented */ VOID NTAPI PsRevertToSelf(VOID) { /* Call the per-thread API */ PAGED_CODE(); PsRevertThreadToSelf(PsGetCurrentThread()); } /* * @implemented */ VOID NTAPI PsRevertThreadToSelf(IN PETHREAD Thread) { PTOKEN Token = NULL; PAGED_CODE(); PSTRACE(PS_SECURITY_DEBUG, "Thread: %p\n", Thread); /* Make sure we had impersonation information */ if (Thread->ActiveImpersonationInfo) { /* Lock the thread security */ PspLockThreadSecurityExclusive(Thread); /* Make sure it's still active */ if (Thread->ActiveImpersonationInfo) { /* Disable impersonation */ PspClearCrossThreadFlag(Thread, CT_ACTIVE_IMPERSONATION_INFO_BIT); /* Get the token */ Token = Thread->ImpersonationInfo->Token; } /* Release thread security */ PspUnlockThreadSecurityExclusive(Thread); /* Check if we had a token */ if (Token) { /* Dereference the impersonation token */ ObDereferenceObject(Token); /* Write impersonation info to the TEB */ PspWriteTebImpersonationInfo(Thread, PsGetCurrentThread()); } } } /* * @implemented */ NTSTATUS NTAPI PsImpersonateClient(IN PETHREAD Thread, IN PACCESS_TOKEN Token, IN BOOLEAN CopyOnOpen, IN BOOLEAN EffectiveOnly, IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel) { PPS_IMPERSONATION_INFORMATION Impersonation, OldData; PTOKEN OldToken = NULL, ProcessToken = NULL; BOOLEAN CopiedToken = FALSE; PACCESS_TOKEN NewToken, ImpersonationToken; PEJOB Job; NTSTATUS Status; PAGED_CODE(); PSTRACE(PS_SECURITY_DEBUG, "Thread: %p, Token: %p\n", Thread, Token); /* Check if we don't have a token */ if (!Token) { /* Make sure we're impersonating */ if (Thread->ActiveImpersonationInfo) { /* We seem to be, lock the thread */ PspLockThreadSecurityExclusive(Thread); /* Make sure we're still impersonating */ if (Thread->ActiveImpersonationInfo) { /* Disable impersonation */ PspClearCrossThreadFlag(Thread, CT_ACTIVE_IMPERSONATION_INFO_BIT); /* Get the token */ OldToken = Thread->ImpersonationInfo->Token; } /* Unlock the process and write TEB information */ PspUnlockThreadSecurityExclusive(Thread); PspWriteTebImpersonationInfo(Thread, PsGetCurrentThread()); } } else { /* Check if we have impersonation info */ Impersonation = Thread->ImpersonationInfo; if (!Impersonation) { /* We need to allocate a new one */ Impersonation = ExAllocatePoolWithTag(PagedPool, sizeof(*Impersonation), TAG_PS_IMPERSONATION); if (!Impersonation) return STATUS_INSUFFICIENT_RESOURCES; /* Update the pointer */ OldData = InterlockedCompareExchangePointer((PVOID*)&Thread-> ImpersonationInfo, Impersonation, NULL); if (OldData) { /* Someone beat us to it, free our copy */ ExFreePoolWithTag(Impersonation, TAG_PS_IMPERSONATION); Impersonation = OldData; } } /* * Assign the token we get from the caller first. The reason * we have to do that is because we're unsure if we can impersonate * in the first place. In the scenario where we cannot then the * last resort is to make a copy of the token and assign that newly * token to the impersonation information. */ ImpersonationToken = Token; /* Obtain a token from the process */ ProcessToken = PsReferencePrimaryToken(Thread->ThreadsProcess); if (!ProcessToken) { /* We can't continue this way without having the process' token... */ return STATUS_UNSUCCESSFUL; } /* Make sure we can impersonate */ if (!SeTokenCanImpersonate(ProcessToken, Token, ImpersonationLevel)) { /* We can't, make a copy of the token instead */ Status = SeCopyClientToken(Token, SecurityIdentification, KernelMode, &NewToken); if (!NT_SUCCESS(Status)) { /* We can't even make a copy of the token? Then bail out... */ ObFastDereferenceObject(&Thread->ThreadsProcess->Token, ProcessToken); return Status; } /* * Since we cannot impersonate, assign the newly copied token. * SeCopyClientToken already holds a reference to the copied token, * let the code path below know that it must not reference it twice. */ CopiedToken = TRUE; ImpersonationLevel = SecurityIdentification; ImpersonationToken = NewToken; } /* We no longer need the process' token */ ObFastDereferenceObject(&Thread->ThreadsProcess->Token, ProcessToken); /* Check if this is a job */ Job = Thread->ThreadsProcess->Job; if (Job != NULL) { /* No admin allowed in this job */ if ((Job->SecurityLimitFlags & JOB_OBJECT_SECURITY_NO_ADMIN) && SeTokenIsAdmin(ImpersonationToken)) { if (CopiedToken) { ObDereferenceObject(ImpersonationToken); } return STATUS_ACCESS_DENIED; } /* No restricted tokens allowed in this job */ if ((Job->SecurityLimitFlags & JOB_OBJECT_SECURITY_RESTRICTED_TOKEN) && SeTokenIsRestricted(ImpersonationToken)) { if (CopiedToken) { ObDereferenceObject(ImpersonationToken); } return STATUS_ACCESS_DENIED; } /* We don't support job filters yet */ if (Job->Filter != NULL) { ASSERT(Job->Filter == NULL); } } /* Lock thread security */ PspLockThreadSecurityExclusive(Thread); /* Check if we're impersonating */ if (Thread->ActiveImpersonationInfo) { /* Get the token */ OldToken = Impersonation->Token; } else { /* Otherwise, enable impersonation */ PspSetCrossThreadFlag(Thread, CT_ACTIVE_IMPERSONATION_INFO_BIT); } /* Now fill it out */ Impersonation->ImpersonationLevel = ImpersonationLevel; Impersonation->CopyOnOpen = CopyOnOpen; Impersonation->EffectiveOnly = EffectiveOnly; Impersonation->Token = ImpersonationToken; /* Do not reference the token again if we copied it */ if (!CopiedToken) { ObReferenceObject(ImpersonationToken); } /* Unlock the thread */ PspUnlockThreadSecurityExclusive(Thread); /* Write impersonation info to the TEB */ PspWriteTebImpersonationInfo(Thread, PsGetCurrentThread()); } /* Dereference the token and return success */ if (OldToken) PsDereferenceImpersonationToken(OldToken); return STATUS_SUCCESS; } /* * @implemented */ PACCESS_TOKEN NTAPI PsReferenceEffectiveToken(IN PETHREAD Thread, OUT IN PTOKEN_TYPE TokenType, OUT PBOOLEAN EffectiveOnly, OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel) { PEPROCESS Process; PACCESS_TOKEN Token = NULL; PAGED_CODE(); PSTRACE(PS_SECURITY_DEBUG, "Thread: %p, TokenType: %p\n", Thread, TokenType); /* Check if we don't have impersonation info */ Process = Thread->ThreadsProcess; if (Thread->ActiveImpersonationInfo) { /* Lock the Process */ PspLockProcessSecurityShared(Process); /* Make sure impersonation is still active */ if (Thread->ActiveImpersonationInfo) { /* Get the token */ Token = Thread->ImpersonationInfo->Token; ObReferenceObject(Token); /* Return data to caller */ *TokenType = TokenImpersonation; *EffectiveOnly = Thread->ImpersonationInfo->EffectiveOnly; *ImpersonationLevel = Thread->ImpersonationInfo->ImpersonationLevel; /* Unlock the Process */ PspUnlockProcessSecurityShared(Process); return Token; } /* Unlock the Process */ PspUnlockProcessSecurityShared(Process); } /* Fast Reference the Token */ Token = ObFastReferenceObject(&Process->Token); /* Check if we got the Token or if we got locked */ if (!Token) { /* Lock the Process */ PspLockProcessSecurityShared(Process); /* Do a Locked Fast Reference */ Token = ObFastReferenceObjectLocked(&Process->Token); /* Unlock the Process */ PspUnlockProcessSecurityShared(Process); } /* Return the token */ *TokenType = TokenPrimary; *EffectiveOnly = FALSE; // NOTE: ImpersonationLevel is left untouched on purpose! return Token; } /* * @implemented */ PACCESS_TOKEN NTAPI PsReferenceImpersonationToken(IN PETHREAD Thread, OUT PBOOLEAN CopyOnOpen, OUT PBOOLEAN EffectiveOnly, OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel) { PTOKEN Token = NULL; PAGED_CODE(); PSTRACE(PS_SECURITY_DEBUG, "Thread: %p\n", Thread); /* If we don't have impersonation info, just quit */ if (!Thread->ActiveImpersonationInfo) return NULL; /* Lock the thread */ PspLockThreadSecurityShared(Thread); /* Make sure we still have active impersonation */ if (Thread->ActiveImpersonationInfo) { /* Return data from caller */ ObReferenceObject(Thread->ImpersonationInfo->Token); *ImpersonationLevel = Thread->ImpersonationInfo->ImpersonationLevel; *CopyOnOpen = Thread->ImpersonationInfo->CopyOnOpen; *EffectiveOnly = Thread->ImpersonationInfo->EffectiveOnly; /* Set the token */ Token = Thread->ImpersonationInfo->Token; } /* Unlock thread and return impersonation token */ PspUnlockThreadSecurityShared(Thread); return Token; } #undef PsDereferenceImpersonationToken /* * @implemented */ VOID NTAPI PsDereferenceImpersonationToken(IN PACCESS_TOKEN ImpersonationToken) { PAGED_CODE(); /* If we got a token, dereference it */ if (ImpersonationToken) ObDereferenceObject(ImpersonationToken); } #undef PsDereferencePrimaryToken /* * @implemented */ VOID NTAPI PsDereferencePrimaryToken(IN PACCESS_TOKEN PrimaryToken) { PAGED_CODE(); /* Dereference the token*/ ObDereferenceObject(PrimaryToken); } /* * @implemented */ BOOLEAN NTAPI PsDisableImpersonation(IN PETHREAD Thread, OUT PSE_IMPERSONATION_STATE ImpersonationState) { PPS_IMPERSONATION_INFORMATION Impersonation = NULL; LONG OldFlags; PAGED_CODE(); PSTRACE(PS_SECURITY_DEBUG, "Thread: %p State: %p\n", Thread, ImpersonationState); /* Check if we don't have impersonation */ if (Thread->ActiveImpersonationInfo) { /* Lock thread security */ PspLockThreadSecurityExclusive(Thread); /* Disable impersonation */ OldFlags = PspClearCrossThreadFlag(Thread, CT_ACTIVE_IMPERSONATION_INFO_BIT); /* Make sure nobody disabled it behind our back */ if (OldFlags & CT_ACTIVE_IMPERSONATION_INFO_BIT) { /* Copy the old state */ Impersonation = Thread->ImpersonationInfo; ImpersonationState->Token = Impersonation->Token; ImpersonationState->CopyOnOpen = Impersonation->CopyOnOpen; ImpersonationState->EffectiveOnly = Impersonation->EffectiveOnly; ImpersonationState->Level = Impersonation->ImpersonationLevel; } /* Unlock thread security */ PspUnlockThreadSecurityExclusive(Thread); /* If we had impersonation info, return true */ if (Impersonation) return TRUE; } /* Clear everything */ ImpersonationState->Token = NULL; ImpersonationState->CopyOnOpen = FALSE; ImpersonationState->EffectiveOnly = FALSE; ImpersonationState->Level = SecurityAnonymous; return FALSE; } /* * @implemented */ VOID NTAPI PsRestoreImpersonation(IN PETHREAD Thread, IN PSE_IMPERSONATION_STATE ImpersonationState) { PTOKEN Token = NULL; PPS_IMPERSONATION_INFORMATION Impersonation; PAGED_CODE(); PSTRACE(PS_SECURITY_DEBUG, "Thread: %p State: %p\n", Thread, ImpersonationState); /* Lock thread security */ PspLockThreadSecurityExclusive(Thread); /* Get the impersonation info */ Impersonation = Thread->ImpersonationInfo; /* Check if we're impersonating */ if (Thread->ActiveImpersonationInfo) { /* Get the token */ Token = Impersonation->Token; } /* Check if we have an impersonation state */ if (ImpersonationState) { /* Fill out the impersonation info */ Impersonation->ImpersonationLevel = ImpersonationState->Level; Impersonation->CopyOnOpen = ImpersonationState->CopyOnOpen; Impersonation->EffectiveOnly = ImpersonationState->EffectiveOnly; Impersonation->Token = ImpersonationState->Token; /* Enable impersonation */ PspSetCrossThreadFlag(Thread, CT_ACTIVE_IMPERSONATION_INFO_BIT); } else { /* Disable impersonation */ PspClearCrossThreadFlag(Thread, CT_ACTIVE_IMPERSONATION_INFO_BIT); } /* Unlock the thread */ PspUnlockThreadSecurityExclusive(Thread); /* Dereference the token */ if (Token) ObDereferenceObject(Token); } NTSTATUS NTAPI NtImpersonateThread(IN HANDLE ThreadHandle, IN HANDLE ThreadToImpersonateHandle, IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService) { SECURITY_QUALITY_OF_SERVICE SafeServiceQoS; SECURITY_CLIENT_CONTEXT ClientContext; PETHREAD Thread; PETHREAD ThreadToImpersonate; KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); NTSTATUS Status; PAGED_CODE(); PSTRACE(PS_SECURITY_DEBUG, "Threads: %p %p\n", ThreadHandle, ThreadToImpersonateHandle); /* Check if call came from user mode */ if (PreviousMode != KernelMode) { /* Enter SEH for probing */ _SEH2_TRY { /* Probe QoS */ ProbeForRead(SecurityQualityOfService, sizeof(SECURITY_QUALITY_OF_SERVICE), sizeof(ULONG)); /* Capture it */ SafeServiceQoS = *SecurityQualityOfService; SecurityQualityOfService = &SafeServiceQoS; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } /* Reference the thread */ Status = ObReferenceObjectByHandle(ThreadHandle, THREAD_DIRECT_IMPERSONATION, PsThreadType, PreviousMode, (PVOID*)&Thread, NULL); if (NT_SUCCESS(Status)) { /* Reference the impersonating thead */ Status = ObReferenceObjectByHandle(ThreadToImpersonateHandle, THREAD_IMPERSONATE, PsThreadType, PreviousMode, (PVOID*)&ThreadToImpersonate, NULL); if (NT_SUCCESS(Status)) { /* Create a client security context */ Status = SeCreateClientSecurity(ThreadToImpersonate, SecurityQualityOfService, 0, &ClientContext); if (NT_SUCCESS(Status)) { /* Do the impersonation */ SeImpersonateClient(&ClientContext, Thread); if (ClientContext.ClientToken) { /* Dereference the client token if we had one */ ObDereferenceObject(ClientContext.ClientToken); } } /* Dereference the thread to impersonate */ ObDereferenceObject(ThreadToImpersonate); } /* Dereference the main thread */ ObDereferenceObject(Thread); } /* Return status */ return Status; } /* EOF */