reactos/ntoskrnl/se/access.c
Hermès Bélusca-Maïto 9393fc320e
[FORMATTING] Remove trailing whitespace. Addendum to 34593d93.
Excluded: 3rd-party code (incl. wine) and most of the win32ss.
2021-09-13 03:52:22 +02:00

911 lines
24 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Security access state functions support
* COPYRIGHT: Copyright Alex Ionescu <alex@relsoft.net>
*/
/* INCLUDES *******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS ********************************************************************/
ERESOURCE SepSubjectContextLock;
/* PRIVATE FUNCTIONS **********************************************************/
/**
* @brief
* Checks if a SID is present in a token.
*
* @param[in] _Token
* A valid token object.
*
* @param[in] PrincipalSelfSid
* A principal self SID.
*
* @param[in] _Sid
* A regular SID.
*
* @param[in] Deny
* If set to TRUE, the caller expected that a SID in a token
* must be a deny-only SID, that is, access checks are performed
* only for deny-only ACEs of the said SID.
*
* @param[in] Restricted
* If set to TRUE, the caller expects that a SID in a token is
* restricted.
*
* @return
* Returns TRUE if the specified SID in the call is present in the token,
* FALSE otherwise.
*/
BOOLEAN
NTAPI
SepSidInTokenEx(
_In_ PACCESS_TOKEN _Token,
_In_ PSID PrincipalSelfSid,
_In_ PSID _Sid,
_In_ BOOLEAN Deny,
_In_ BOOLEAN Restricted)
{
ULONG i;
PTOKEN Token = (PTOKEN)_Token;
PISID TokenSid, Sid = (PISID)_Sid;
PSID_AND_ATTRIBUTES SidAndAttributes;
ULONG SidCount, SidLength;
USHORT SidMetadata;
PAGED_CODE();
/* Not yet supported */
ASSERT(PrincipalSelfSid == NULL);
ASSERT(Restricted == FALSE);
/* Check if a principal SID was given, and this is our current SID already */
if ((PrincipalSelfSid) && (RtlEqualSid(SePrincipalSelfSid, Sid)))
{
/* Just use the principal SID in this case */
Sid = PrincipalSelfSid;
}
/* Check if this is a restricted token or not */
if (Restricted)
{
/* Use the restricted SIDs and count */
SidAndAttributes = Token->RestrictedSids;
SidCount = Token->RestrictedSidCount;
}
else
{
/* Use the normal SIDs and count */
SidAndAttributes = Token->UserAndGroups;
SidCount = Token->UserAndGroupCount;
}
/* Do checks here by hand instead of the usual 4 function calls */
SidLength = FIELD_OFFSET(SID,
SubAuthority[Sid->SubAuthorityCount]);
SidMetadata = *(PUSHORT)&Sid->Revision;
/* Loop every SID */
for (i = 0; i < SidCount; i++)
{
TokenSid = (PISID)SidAndAttributes->Sid;
#if SE_SID_DEBUG
UNICODE_STRING sidString;
RtlConvertSidToUnicodeString(&sidString, TokenSid, TRUE);
DPRINT1("SID in Token: %wZ\n", &sidString);
RtlFreeUnicodeString(&sidString);
#endif
/* Check if the SID metadata matches */
if (*(PUSHORT)&TokenSid->Revision == SidMetadata)
{
/* Check if the SID data matches */
if (RtlEqualMemory(Sid, TokenSid, SidLength))
{
/* Check if the group is enabled, or used for deny only */
if ((!(i) && !(SidAndAttributes->Attributes & SE_GROUP_USE_FOR_DENY_ONLY)) ||
(SidAndAttributes->Attributes & SE_GROUP_ENABLED) ||
((Deny) && (SidAndAttributes->Attributes & SE_GROUP_USE_FOR_DENY_ONLY)))
{
/* SID is present */
return TRUE;
}
else
{
/* SID is not present */
return FALSE;
}
}
}
/* Move to the next SID */
SidAndAttributes++;
}
/* SID is not present */
return FALSE;
}
/**
* @brief
* Checks if a SID is present in a token.
*
* @param[in] _Token
* A valid token object.
*
* @param[in] _Sid
* A regular SID.
*
* @return
* Returns TRUE if the specified SID in the call is present in the token,
* FALSE otherwise.
*/
BOOLEAN
NTAPI
SepSidInToken(
_In_ PACCESS_TOKEN _Token,
_In_ PSID Sid)
{
/* Call extended API */
return SepSidInTokenEx(_Token, NULL, Sid, FALSE, FALSE);
}
/**
* @brief
* Checks if a token belongs to the main user, being the owner.
*
* @param[in] _Token
* A valid token object.
*
* @param[in] SecurityDescriptor
* A security descriptor where the owner is to be found.
*
* @param[in] TokenLocked
* If set to TRUE, the token has been already locked and there's
* no need to lock it again. Otherwise the function will acquire
* the lock.
*
* @return
* Returns TRUE if the token belongs to a owner, FALSE otherwise.
*/
BOOLEAN
NTAPI
SepTokenIsOwner(
_In_ PACCESS_TOKEN _Token,
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
_In_ BOOLEAN TokenLocked)
{
PSID Sid;
BOOLEAN Result;
PTOKEN Token = _Token;
/* Get the owner SID */
Sid = SepGetOwnerFromDescriptor(SecurityDescriptor);
ASSERT(Sid != NULL);
/* Lock the token if needed */
if (!TokenLocked) SepAcquireTokenLockShared(Token);
/* Check if the owner SID is found, handling restricted case as well */
Result = SepSidInToken(Token, Sid);
if ((Result) && (Token->TokenFlags & TOKEN_IS_RESTRICTED))
{
Result = SepSidInTokenEx(Token, NULL, Sid, FALSE, TRUE);
}
/* Release the lock if we had acquired it */
if (!TokenLocked) SepReleaseTokenLock(Token);
/* Return the result */
return Result;
}
/**
* @brief
* Retrieves token control information.
*
* @param[in] _Token
* A valid token object.
*
* @param[out] SecurityDescriptor
* The returned token control information.
*
* @return
* Nothing.
*/
VOID
NTAPI
SeGetTokenControlInformation(
_In_ PACCESS_TOKEN _Token,
_Out_ PTOKEN_CONTROL TokenControl)
{
PTOKEN Token = _Token;
PAGED_CODE();
/* Capture the main fields */
TokenControl->AuthenticationId = Token->AuthenticationId;
TokenControl->TokenId = Token->TokenId;
TokenControl->TokenSource = Token->TokenSource;
/* Lock the token */
SepAcquireTokenLockShared(Token);
/* Capture the modified ID */
TokenControl->ModifiedId = Token->ModifiedId;
/* Unlock it */
SepReleaseTokenLock(Token);
}
/**
* @brief
* Creates a client security context based upon an access token.
*
* @param[in] Token
* A valid token object.
*
* @param[in] ClientSecurityQos
* The Quality of Service (QoS) of a client security context.
*
* @param[in] ServerIsRemote
* If the client is a remote server (TRUE), the function will retrieve the
* control information of an access token, that is, we're doing delegation
* and that the server isn't local.
*
* @param[in] TokenType
* Type of token.
*
* @param[in] ThreadEffectiveOnly
* If set to TRUE, the client wants that the current thread wants to modify
* (enable or disable) privileges and groups.
*
* @param[in] ImpersonationLevel
* Security impersonation level filled in the QoS context.
*
* @param[out] ClientContext
* The returned security client context.
*
* @return
* Returns STATUS_SUCCESS if client security creation has completed successfully.
* STATUS_INVALID_PARAMETER is returned if one or more of the parameters are bogus.
* STATUS_BAD_IMPERSONATION_LEVEL is returned if the current impersonation level
* within QoS context doesn't meet with the conditions required. A failure
* NTSTATUS code is returned otherwise.
*/
NTSTATUS
NTAPI
SepCreateClientSecurity(
_In_ PACCESS_TOKEN Token,
_In_ PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos,
_In_ BOOLEAN ServerIsRemote,
_In_ TOKEN_TYPE TokenType,
_In_ BOOLEAN ThreadEffectiveOnly,
_In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
_Out_ PSECURITY_CLIENT_CONTEXT ClientContext)
{
NTSTATUS Status;
PACCESS_TOKEN NewToken;
PAGED_CODE();
/* Check for bogus impersonation level */
if (!VALID_IMPERSONATION_LEVEL(ClientSecurityQos->ImpersonationLevel))
{
/* Fail the call */
return STATUS_INVALID_PARAMETER;
}
/* Check what kind of token this is */
if (TokenType != TokenImpersonation)
{
/* On a primary token, if we do direct access, copy the flag from the QOS */
ClientContext->DirectAccessEffectiveOnly = ClientSecurityQos->EffectiveOnly;
}
else
{
/* This is an impersonation token, is the level ok? */
if (ClientSecurityQos->ImpersonationLevel > ImpersonationLevel)
{
/* Nope, fail */
return STATUS_BAD_IMPERSONATION_LEVEL;
}
/* Is the level too low, or are we doing something other than delegation remotely */
if ((ImpersonationLevel == SecurityAnonymous) ||
(ImpersonationLevel == SecurityIdentification) ||
((ServerIsRemote) && (ImpersonationLevel != SecurityDelegation)))
{
/* Fail the call */
return STATUS_BAD_IMPERSONATION_LEVEL;
}
/* Pick either the thread setting or the QOS setting */
ClientContext->DirectAccessEffectiveOnly =
((ThreadEffectiveOnly) || (ClientSecurityQos->EffectiveOnly)) ? TRUE : FALSE;
}
/* Is this static tracking */
if (ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING)
{
/* Do not use direct access and make a copy */
ClientContext->DirectlyAccessClientToken = FALSE;
Status = SeCopyClientToken(Token,
ClientSecurityQos->ImpersonationLevel,
KernelMode,
&NewToken);
if (!NT_SUCCESS(Status))
return Status;
}
else
{
/* Use direct access and check if this is local */
ClientContext->DirectlyAccessClientToken = TRUE;
if (ServerIsRemote)
{
/* We are doing delegation, so make a copy of the control data */
SeGetTokenControlInformation(Token,
&ClientContext->ClientTokenControl);
}
/* Keep the same token */
NewToken = Token;
}
/* Fill out the context and return success */
ClientContext->SecurityQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
ClientContext->SecurityQos.ImpersonationLevel = ClientSecurityQos->ImpersonationLevel;
ClientContext->SecurityQos.ContextTrackingMode = ClientSecurityQos->ContextTrackingMode;
ClientContext->SecurityQos.EffectiveOnly = ClientSecurityQos->EffectiveOnly;
ClientContext->ServerIsRemote = ServerIsRemote;
ClientContext->ClientToken = NewToken;
return STATUS_SUCCESS;
}
/* PUBLIC FUNCTIONS ***********************************************************/
/**
* @brief
* An extended function that captures the security subject context based upon
* the specified thread and process.
*
* @param[in] Thread
* A thread where the calling thread's token is to be referenced for
* the security context.
*
* @param[in] Process
* A process where the main process' token is to be referenced for
* the security context.
*
* @param[out] SubjectContext
* The returned security subject context.
*
* @return
* Nothing.
*/
VOID
NTAPI
SeCaptureSubjectContextEx(
_In_ PETHREAD Thread,
_In_ PEPROCESS Process,
_Out_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
{
BOOLEAN CopyOnOpen, EffectiveOnly;
PAGED_CODE();
/* Save the unique ID */
SubjectContext->ProcessAuditId = Process->UniqueProcessId;
/* Check if we have a thread */
if (!Thread)
{
/* We don't, so no token */
SubjectContext->ClientToken = NULL;
}
else
{
/* Get the impersonation token */
SubjectContext->ClientToken = PsReferenceImpersonationToken(Thread,
&CopyOnOpen,
&EffectiveOnly,
&SubjectContext->ImpersonationLevel);
}
/* Get the primary token */
SubjectContext->PrimaryToken = PsReferencePrimaryToken(Process);
}
/**
* @brief
* Captures the security subject context of the calling thread and calling
* process.
*
* @param[out] SubjectContext
* The returned security subject context.
*
* @return
* Nothing.
*/
VOID
NTAPI
SeCaptureSubjectContext(
_Out_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
{
/* Call the extended API */
SeCaptureSubjectContextEx(PsGetCurrentThread(),
PsGetCurrentProcess(),
SubjectContext);
}
/**
* @brief
* Locks both the referenced primary and client access tokens of a
* security subject context.
*
* @param[in] SubjectContext
* A valid security context with both referenced tokens.
*
* @return
* Nothing.
*/
VOID
NTAPI
SeLockSubjectContext(
_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
{
PTOKEN PrimaryToken, ClientToken;
PAGED_CODE();
/* Read both tokens */
PrimaryToken = SubjectContext->PrimaryToken;
ClientToken = SubjectContext->ClientToken;
/* Always lock the primary */
SepAcquireTokenLockShared(PrimaryToken);
/* Lock the impersonation one if it's there */
if (!ClientToken) return;
SepAcquireTokenLockShared(ClientToken);
}
/**
* @brief
* Unlocks both the referenced primary and client access tokens of a
* security subject context.
*
* @param[in] SubjectContext
* A valid security context with both referenced tokens.
*
* @return
* Nothing.
*/
VOID
NTAPI
SeUnlockSubjectContext(
_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
{
PTOKEN PrimaryToken, ClientToken;
PAGED_CODE();
/* Read both tokens */
PrimaryToken = SubjectContext->PrimaryToken;
ClientToken = SubjectContext->ClientToken;
/* Unlock the impersonation one if it's there */
if (ClientToken)
{
SepReleaseTokenLock(ClientToken);
}
/* Always unlock the primary one */
SepReleaseTokenLock(PrimaryToken);
}
/**
* @brief
* Releases both the primary and client tokens of a security
* subject context.
*
* @param[in] SubjectContext
* The captured security context.
*
* @return
* Nothing.
*/
VOID
NTAPI
SeReleaseSubjectContext(
_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext)
{
PAGED_CODE();
/* Drop reference on the primary */
ObFastDereferenceObject(&PsGetCurrentProcess()->Token, SubjectContext->PrimaryToken);
SubjectContext->PrimaryToken = NULL;
/* Drop reference on the impersonation, if there was one */
PsDereferenceImpersonationToken(SubjectContext->ClientToken);
SubjectContext->ClientToken = NULL;
}
/**
* @brief
* An extended function that creates an access state.
*
* @param[in] Thread
* Valid thread object where subject context is to be captured.
*
* @param[in] Process
* Valid process object where subject context is to be captured.
*
* @param[in,out] AccessState
* An initialized returned parameter to an access state.
*
* @param[in] AuxData
* Auxiliary security data for access state.
*
* @param[in] Access
* Type of access mask to assign.
*
* @param[in] GenericMapping
* Generic mapping for the access state to assign.
*
* @return
* Returns STATUS_SUCCESS.
*/
NTSTATUS
NTAPI
SeCreateAccessStateEx(
_In_ PETHREAD Thread,
_In_ PEPROCESS Process,
_Inout_ PACCESS_STATE AccessState,
_In_ PAUX_ACCESS_DATA AuxData,
_In_ ACCESS_MASK Access,
_In_ PGENERIC_MAPPING GenericMapping)
{
ACCESS_MASK AccessMask = Access;
PTOKEN Token;
PAGED_CODE();
/* Map the Generic Acess to Specific Access if we have a Mapping */
if ((Access & GENERIC_ACCESS) && (GenericMapping))
{
RtlMapGenericMask(&AccessMask, GenericMapping);
}
/* Initialize the Access State */
RtlZeroMemory(AccessState, sizeof(ACCESS_STATE));
ASSERT(AccessState->SecurityDescriptor == NULL);
ASSERT(AccessState->PrivilegesAllocated == FALSE);
/* Initialize and save aux data */
RtlZeroMemory(AuxData, sizeof(AUX_ACCESS_DATA));
AccessState->AuxData = AuxData;
/* Capture the Subject Context */
SeCaptureSubjectContextEx(Thread,
Process,
&AccessState->SubjectSecurityContext);
/* Set Access State Data */
AccessState->RemainingDesiredAccess = AccessMask;
AccessState->OriginalDesiredAccess = AccessMask;
ExAllocateLocallyUniqueId(&AccessState->OperationID);
/* Get the Token to use */
Token = SeQuerySubjectContextToken(&AccessState->SubjectSecurityContext);
/* Check for Travers Privilege */
if (Token->TokenFlags & TOKEN_HAS_TRAVERSE_PRIVILEGE)
{
/* Preserve the Traverse Privilege */
AccessState->Flags = TOKEN_HAS_TRAVERSE_PRIVILEGE;
}
/* Set the Auxiliary Data */
AuxData->PrivilegeSet = (PPRIVILEGE_SET)((ULONG_PTR)AccessState +
FIELD_OFFSET(ACCESS_STATE,
Privileges));
if (GenericMapping) AuxData->GenericMapping = *GenericMapping;
/* Return Sucess */
return STATUS_SUCCESS;
}
/**
* @brief
* Creates an access state.
*
* @param[in,out] AccessState
* An initialized returned parameter to an access state.
*
* @param[in] AuxData
* Auxiliary security data for access state.
*
* @param[in] Access
* Type of access mask to assign.
*
* @param[in] GenericMapping
* Generic mapping for the access state to assign.
*
* @return
* See SeCreateAccessStateEx.
*/
NTSTATUS
NTAPI
SeCreateAccessState(
_Inout_ PACCESS_STATE AccessState,
_In_ PAUX_ACCESS_DATA AuxData,
_In_ ACCESS_MASK Access,
_In_ PGENERIC_MAPPING GenericMapping)
{
PAGED_CODE();
/* Call the extended API */
return SeCreateAccessStateEx(PsGetCurrentThread(),
PsGetCurrentProcess(),
AccessState,
AuxData,
Access,
GenericMapping);
}
/**
* @brief
* Deletes an allocated access state from the memory.
*
* @param[in] AccessState
* A valid access state.
*
* @return
* Nothing.
*/
VOID
NTAPI
SeDeleteAccessState(
_In_ PACCESS_STATE AccessState)
{
PAUX_ACCESS_DATA AuxData;
PAGED_CODE();
/* Get the Auxiliary Data */
AuxData = AccessState->AuxData;
/* Deallocate Privileges */
if (AccessState->PrivilegesAllocated)
ExFreePoolWithTag(AuxData->PrivilegeSet, TAG_PRIVILEGE_SET);
/* Deallocate Name and Type Name */
if (AccessState->ObjectName.Buffer)
{
ExFreePool(AccessState->ObjectName.Buffer);
}
if (AccessState->ObjectTypeName.Buffer)
{
ExFreePool(AccessState->ObjectTypeName.Buffer);
}
/* Release the Subject Context */
SeReleaseSubjectContext(&AccessState->SubjectSecurityContext);
}
/**
* @brief
* Sets a new generic mapping for an allocated access state.
*
* @param[in] AccessState
* A valid access state.
*
* @param[in] GenericMapping
* New generic mapping to assign.
*
* @return
* Nothing.
*/
VOID
NTAPI
SeSetAccessStateGenericMapping(
_In_ PACCESS_STATE AccessState,
_In_ PGENERIC_MAPPING GenericMapping)
{
PAGED_CODE();
/* Set the Generic Mapping */
((PAUX_ACCESS_DATA)AccessState->AuxData)->GenericMapping = *GenericMapping;
}
/**
* @brief
* Creates a client security context.
*
* @param[in] Thread
* Thread object of the client where impersonation has to begin.
*
* @param[in] Qos
* Quality of service to specify what kind of impersonation to be done.
*
* @param[in] RemoteClient
* If set to TRUE, the client that we're going to impersonate is remote.
*
* @param[out] ClientContext
* The returned security client context.
*
* @return
* See SepCreateClientSecurity.
*/
NTSTATUS
NTAPI
SeCreateClientSecurity(
_In_ PETHREAD Thread,
_In_ PSECURITY_QUALITY_OF_SERVICE Qos,
_In_ BOOLEAN RemoteClient,
_Out_ PSECURITY_CLIENT_CONTEXT ClientContext)
{
TOKEN_TYPE TokenType;
BOOLEAN ThreadEffectiveOnly;
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
PACCESS_TOKEN Token;
NTSTATUS Status;
PAGED_CODE();
/* Reference the correct token */
Token = PsReferenceEffectiveToken(Thread,
&TokenType,
&ThreadEffectiveOnly,
&ImpersonationLevel);
/* Create client security from it */
Status = SepCreateClientSecurity(Token,
Qos,
RemoteClient,
TokenType,
ThreadEffectiveOnly,
ImpersonationLevel,
ClientContext);
/* Check if we failed or static tracking was used */
if (!(NT_SUCCESS(Status)) || (Qos->ContextTrackingMode == SECURITY_STATIC_TRACKING))
{
/* Dereference our copy since it's not being used */
ObDereferenceObject(Token);
}
/* Return status */
return Status;
}
/**
* @brief
* Creates a client security context based upon the captured security
* subject context.
*
* @param[in] SubjectContext
* The captured subject context where client security is to be created
* from.
*
* @param[in] ClientSecurityQos
* Quality of service to specify what kind of impersonation to be done.
*
* @param[in] ServerIsRemote
* If set to TRUE, the client that we're going to impersonate is remote.
*
* @param[out] ClientContext
* The returned security client context.
*
* @return
* See SepCreateClientSecurity.
*/
NTSTATUS
NTAPI
SeCreateClientSecurityFromSubjectContext(
_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,
_In_ PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos,
_In_ BOOLEAN ServerIsRemote,
_Out_ PSECURITY_CLIENT_CONTEXT ClientContext)
{
PACCESS_TOKEN Token;
NTSTATUS Status;
PAGED_CODE();
/* Get the right token and reference it */
Token = SeQuerySubjectContextToken(SubjectContext);
ObReferenceObject(Token);
/* Create the context */
Status = SepCreateClientSecurity(Token,
ClientSecurityQos,
ServerIsRemote,
SubjectContext->ClientToken ?
TokenImpersonation : TokenPrimary,
FALSE,
SubjectContext->ImpersonationLevel,
ClientContext);
/* Check if we failed or static tracking was used */
if (!(NT_SUCCESS(Status)) ||
(ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING))
{
/* Dereference our copy since it's not being used */
ObDereferenceObject(Token);
}
/* Return status */
return Status;
}
/**
* @brief
* Extended function that impersonates a client.
*
* @param[in] ClientContext
* A valid client context.
*
* @param[in] ServerThread
* The thread where impersonation is to be done.
*
* @return
* STATUS_SUCCESS is returned if the calling thread successfully impersonates
* the client. A failure NTSTATUS code is returned otherwise.
*/
NTSTATUS
NTAPI
SeImpersonateClientEx(
_In_ PSECURITY_CLIENT_CONTEXT ClientContext,
_In_opt_ PETHREAD ServerThread)
{
BOOLEAN EffectiveOnly;
PAGED_CODE();
/* Check if direct access is requested */
if (!ClientContext->DirectlyAccessClientToken)
{
/* No, so get the flag from QOS */
EffectiveOnly = ClientContext->SecurityQos.EffectiveOnly;
}
else
{
/* Yes, so see if direct access should be effective only */
EffectiveOnly = ClientContext->DirectAccessEffectiveOnly;
}
/* Use the current thread if one was not passed */
if (!ServerThread) ServerThread = PsGetCurrentThread();
/* Call the lower layer routine */
return PsImpersonateClient(ServerThread,
ClientContext->ClientToken,
TRUE,
EffectiveOnly,
ClientContext->SecurityQos.ImpersonationLevel);
}
/**
* @brief
* Impersonates a client user.
*
* @param[in] ClientContext
* A valid client context.
*
* @param[in] ServerThread
* The thread where impersonation is to be done.
* *
* @return
* Nothing.
*/
VOID
NTAPI
SeImpersonateClient(
_In_ PSECURITY_CLIENT_CONTEXT ClientContext,
_In_opt_ PETHREAD ServerThread)
{
PAGED_CODE();
/* Call the new API */
SeImpersonateClientEx(ClientContext, ServerThread);
}
/* EOF */