1469 lines
32 KiB
C
1469 lines
32 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
security.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the security related portions of the process
|
||
structure.
|
||
|
||
|
||
Author:
|
||
|
||
Mark Lucovsky (markl) 25-Apr-1989
|
||
Jim Kelly (JimK) 2-August-1990
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "psp.h"
|
||
|
||
|
||
PACCESS_TOKEN
|
||
PsReferencePrimaryToken(
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns a pointer to the primary token of a process.
|
||
The reference count of that primary token is incremented to protect
|
||
the pointer returned.
|
||
|
||
When the pointer is no longer needed, it should be freed using
|
||
PsDereferencePrimaryToken().
|
||
|
||
|
||
Arguments:
|
||
|
||
Process - Supplies the address of the process whose primary token
|
||
is to be referenced.
|
||
|
||
Return Value:
|
||
|
||
A pointer to the specified process's primary token.
|
||
|
||
--*/
|
||
|
||
{
|
||
PACCESS_TOKEN
|
||
Token;
|
||
|
||
ASSERT( Process->Pcb.Header.Type == ProcessObject );
|
||
|
||
//
|
||
// For performance sake, we may want to change this to use
|
||
// an executive interlocked add routine in the future.
|
||
//
|
||
|
||
|
||
//
|
||
// Lock the process security fields.
|
||
//
|
||
|
||
PspLockProcessSecurityFields();
|
||
|
||
//
|
||
// Grab the current token pointer value
|
||
//
|
||
|
||
Token = Process->Token;
|
||
|
||
//
|
||
// Increment the reference count of the primary token to protect our
|
||
// pointer.
|
||
//
|
||
|
||
ObReferenceObject(Token);
|
||
|
||
//
|
||
// Release the process security fields
|
||
//
|
||
|
||
PspFreeProcessSecurityFields();
|
||
|
||
return Token;
|
||
|
||
}
|
||
|
||
PACCESS_TOKEN
|
||
PsReferenceImpersonationToken(
|
||
IN PETHREAD Thread,
|
||
OUT PBOOLEAN CopyOnOpen,
|
||
OUT PBOOLEAN EffectiveOnly,
|
||
OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns a pointer to the impersonation token of a thread.
|
||
The reference count of that impersonation token is incremented to protect
|
||
the pointer returned.
|
||
|
||
If the thread is not currently impersonating a client, then a null pointer
|
||
is returned.
|
||
|
||
If the thread is impersonating a client, then information about the
|
||
means of impersonation are also returned (ImpersonationLevel).
|
||
|
||
If a non-null value is returned, then PsDereferenceImpersonationToken()
|
||
must be called to decrement the token's reference count when the pointer
|
||
is no longer needed.
|
||
|
||
|
||
Arguments:
|
||
|
||
Thread - Supplies the address of the thread whose impersonation token
|
||
is to be referenced.
|
||
|
||
CopyOnOpen - The current value of the Thread->ImpersonationInfo->CopyOnOpen field.
|
||
|
||
EffectiveOnly - The current value of the Thread->ImpersonationInfo->EffectiveOnly field.
|
||
|
||
ImpersonationLevel - The current value of the Thread->ImpersonationInfo->ImpersonationLevel
|
||
field.
|
||
|
||
Return Value:
|
||
|
||
A pointer to the specified thread's impersonation token.
|
||
|
||
If the thread is not currently impersonating a client, then NULL is
|
||
returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
PACCESS_TOKEN
|
||
Token;
|
||
|
||
ASSERT( Thread->Tcb.Header.Type == ThreadObject );
|
||
//
|
||
// before going through the lock overhead just look to see if it is
|
||
// null. There is no race. Grabbing the lock is not needed until
|
||
// we decide to use the token at which point we re check to see it
|
||
// it is null.
|
||
// This check saves about 300 instructions.
|
||
//
|
||
|
||
if ( !Thread->ActiveImpersonationInfo ) {
|
||
return NULL;
|
||
}
|
||
|
||
|
||
//
|
||
// Lock the process security fields.
|
||
//
|
||
|
||
PspLockProcessSecurityFields();
|
||
|
||
//
|
||
// Grab the current token pointer value
|
||
//
|
||
|
||
|
||
if ( Thread->ActiveImpersonationInfo ) {
|
||
|
||
//
|
||
// Return the thread's impersonation level, etc.
|
||
//
|
||
|
||
Token = Thread->ImpersonationInfo->Token;
|
||
(*ImpersonationLevel) = Thread->ImpersonationInfo->ImpersonationLevel;
|
||
(*CopyOnOpen) = Thread->ImpersonationInfo->CopyOnOpen;
|
||
(*EffectiveOnly) = Thread->ImpersonationInfo->EffectiveOnly;
|
||
|
||
|
||
//
|
||
// Increment the reference count of the token to protect our
|
||
// pointer.
|
||
//
|
||
|
||
ObReferenceObject(Token);
|
||
|
||
} else {
|
||
Token = NULL;
|
||
}
|
||
|
||
|
||
//
|
||
// Release the security fields.
|
||
//
|
||
|
||
PspFreeProcessSecurityFields();
|
||
|
||
return Token;
|
||
|
||
}
|
||
|
||
PACCESS_TOKEN
|
||
PsReferenceEffectiveToken(
|
||
IN PETHREAD Thread,
|
||
OUT PTOKEN_TYPE TokenType,
|
||
OUT PBOOLEAN EffectiveOnly,
|
||
OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns a pointer to the effective token of a thread. The
|
||
effective token of a thread is the thread's impersonation token if it has
|
||
one. Otherwise, it is the primary token of the thread's process.
|
||
|
||
The reference count of the effective token is incremented to protect
|
||
the pointer returned.
|
||
|
||
If the thread is impersonating a client, then the impersonation level
|
||
is also returned.
|
||
|
||
Either PsDereferenceImpersonationToken() (for an impersonation token) or
|
||
PsDereferencePrimaryToken() (for a primary token) must be called to
|
||
decrement the token's reference count when the pointer is no longer
|
||
needed.
|
||
|
||
|
||
Arguments:
|
||
|
||
Thread - Supplies the address of the thread whose effective token
|
||
is to be referenced.
|
||
|
||
TokenType - Receives the type of the effective token. If the thread
|
||
is currently impersonating a client, then this will be
|
||
TokenImpersonation. Othwerwise, it will be TokenPrimary.
|
||
|
||
EffectiveOnly - If the token type is TokenImpersonation, then this
|
||
receives the value of the client thread's Thread->Client->EffectiveOnly field.
|
||
Otherwise, it is set to FALSE.
|
||
|
||
ImpersonationLevel - The current value of the Thread->Client->ImpersonationLevel
|
||
field for an impersonation token and is not set for a primary token.
|
||
|
||
Return Value:
|
||
|
||
A pointer to the specified thread's effective token.
|
||
|
||
--*/
|
||
|
||
{
|
||
PACCESS_TOKEN
|
||
Token;
|
||
|
||
ASSERT( Thread->Tcb.Header.Type == ThreadObject );
|
||
|
||
//
|
||
// Lock the process security fields.
|
||
//
|
||
|
||
PspLockProcessSecurityFields();
|
||
|
||
//
|
||
// Grab the current impersonation token pointer value
|
||
//
|
||
|
||
|
||
if ( Thread->ActiveImpersonationInfo ) {
|
||
|
||
Token = Thread->ImpersonationInfo->Token;
|
||
|
||
//
|
||
// Return the thread's impersonation level, etc.
|
||
//
|
||
|
||
(*TokenType) = TokenImpersonation;
|
||
(*EffectiveOnly) = Thread->ImpersonationInfo->EffectiveOnly;
|
||
(*ImpersonationLevel) = Thread->ImpersonationInfo->ImpersonationLevel;
|
||
|
||
|
||
|
||
} else {
|
||
|
||
//
|
||
// Get the thread's primary token if it wasn't impersonating a client.
|
||
//
|
||
|
||
Token = THREAD_TO_PROCESS(Thread)->Token;
|
||
|
||
|
||
//
|
||
// Only the TokenType and CopyOnOpen OUT parameters are
|
||
// returned for a primary token.
|
||
//
|
||
|
||
(*TokenType) = TokenPrimary;
|
||
(*EffectiveOnly) = FALSE;
|
||
|
||
}
|
||
|
||
//
|
||
// Increment the reference count of the token to protect our
|
||
// pointer.
|
||
//
|
||
|
||
ObReferenceObject(Token);
|
||
|
||
//
|
||
// Release the security fields.
|
||
//
|
||
|
||
PspFreeProcessSecurityFields();
|
||
|
||
|
||
return Token;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
PsOpenTokenOfThread(
|
||
IN HANDLE ThreadHandle,
|
||
IN BOOLEAN OpenAsSelf,
|
||
OUT PACCESS_TOKEN *Token,
|
||
OUT PBOOLEAN CopyOnOpen,
|
||
OUT PBOOLEAN EffectiveOnly,
|
||
OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function does the thread specific processing of
|
||
an NtOpenThreadToken() service.
|
||
|
||
The service validates that the handle has appropriate access
|
||
to reference the thread. If so, it goes on to increment
|
||
the reference count of the token object to prevent it from
|
||
going away while the rest of the NtOpenThreadToken() request
|
||
is processed.
|
||
|
||
NOTE: If this call completes successfully, the caller is responsible
|
||
for decrementing the reference count of the target token.
|
||
This must be done using PsDereferenceImpersonationToken().
|
||
|
||
Arguments:
|
||
|
||
ThreadHandle - Supplies a handle to a thread object.
|
||
|
||
OpenAsSelf - Is a boolean value indicating whether the access should
|
||
be made using the calling thread's current security context, which
|
||
may be that of a client (if impersonating), or using the caller's
|
||
process-level security context. A value of FALSE indicates the
|
||
caller's current context should be used un-modified. A value of
|
||
TRUE indicates the request should be fulfilled using the process
|
||
level security context.
|
||
|
||
Token - If successful, receives a pointer to the thread's token
|
||
object.
|
||
|
||
CopyOnOpen - The current value of the Thread->Client->CopyOnOpen field.
|
||
|
||
EffectiveOnly - The current value of the Thread->Client->EffectiveOnly field.
|
||
|
||
ImpersonationLevel - The current value of the Thread->Client->ImpersonationLevel
|
||
field.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the call completed successfully.
|
||
|
||
STATUS_NO_TOKEN - Indicates the referenced thread is not currently
|
||
impersonating a client.
|
||
|
||
STATUS_CANT_OPEN_ANONYMOUS - Indicates the client requested anonymous
|
||
impersonation level. An anonymous token can not be openned.
|
||
|
||
status may also be any value returned by an attemp the reference
|
||
the thread object for THREAD_QUERY_INFORMATION access.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS
|
||
Status;
|
||
|
||
PETHREAD
|
||
Thread;
|
||
|
||
KPROCESSOR_MODE
|
||
PreviousMode;
|
||
|
||
SE_IMPERSONATION_STATE
|
||
DisabledImpersonationState;
|
||
|
||
BOOLEAN
|
||
RestoreImpersonationState = FALSE;
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
|
||
//
|
||
// Disable impersonation if necessary
|
||
//
|
||
|
||
if (OpenAsSelf) {
|
||
RestoreImpersonationState = PsDisableImpersonation(
|
||
PsGetCurrentThread(),
|
||
&DisabledImpersonationState
|
||
);
|
||
}
|
||
|
||
//
|
||
// Make sure the handle grants the appropriate access to the specified
|
||
// thread.
|
||
//
|
||
|
||
Status = ObReferenceObjectByHandle(
|
||
ThreadHandle,
|
||
THREAD_QUERY_INFORMATION,
|
||
PsThreadType,
|
||
PreviousMode,
|
||
(PVOID *)&Thread,
|
||
NULL
|
||
);
|
||
|
||
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
|
||
if (RestoreImpersonationState) {
|
||
PsRestoreImpersonation(
|
||
PsGetCurrentThread(),
|
||
&DisabledImpersonationState
|
||
);
|
||
}
|
||
|
||
//
|
||
// Reference the impersonation token, if there is one
|
||
//
|
||
|
||
(*Token) = PsReferenceImpersonationToken( Thread,
|
||
CopyOnOpen,
|
||
EffectiveOnly,
|
||
ImpersonationLevel
|
||
);
|
||
|
||
|
||
//
|
||
// dereference the target thread.
|
||
//
|
||
|
||
ObDereferenceObject( Thread );
|
||
|
||
//
|
||
// Make sure there is a token
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT(*Token)) {
|
||
return STATUS_NO_TOKEN;
|
||
}
|
||
|
||
|
||
//
|
||
// Make sure the ImpersonationLevel is high enough to allow
|
||
// the token to be openned.
|
||
//
|
||
|
||
if ((*ImpersonationLevel) <= SecurityAnonymous) {
|
||
PsDereferenceImpersonationToken( (*Token) );
|
||
(*Token) = NULL;
|
||
return STATUS_CANT_OPEN_ANONYMOUS;
|
||
}
|
||
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
PsOpenTokenOfProcess(
|
||
IN HANDLE ProcessHandle,
|
||
OUT PACCESS_TOKEN *Token
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function does the process specific processing of
|
||
an NtOpenProcessToken() service.
|
||
|
||
The service validates that the handle has appropriate access
|
||
to referenced process. If so, it goes on to reference the
|
||
primary token object to prevent it from going away while the
|
||
rest of the NtOpenProcessToken() request is processed.
|
||
|
||
NOTE: If this call completes successfully, the caller is responsible
|
||
for decrementing the reference count of the target token.
|
||
This must be done using the PsDereferencePrimaryToken() API.
|
||
|
||
Arguments:
|
||
|
||
ProcessHandle - Supplies a handle to a process object whose primary
|
||
token is to be opened.
|
||
|
||
Token - If successful, receives a pointer to the process's token
|
||
object.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the call completed successfully.
|
||
|
||
status may also be any value returned by an attemp the reference
|
||
the process object for PROCESS_QUERY_INFORMATION access.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS
|
||
Status;
|
||
|
||
PEPROCESS
|
||
Process;
|
||
|
||
KPROCESSOR_MODE
|
||
PreviousMode;
|
||
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
//
|
||
// Make sure the handle grants the appropriate access to the specified
|
||
// process.
|
||
//
|
||
|
||
Status = ObReferenceObjectByHandle(
|
||
ProcessHandle,
|
||
PROCESS_QUERY_INFORMATION,
|
||
PsProcessType,
|
||
PreviousMode,
|
||
(PVOID *)&Process,
|
||
NULL
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
return Status;
|
||
|
||
}
|
||
|
||
//
|
||
// Reference the primary token
|
||
// (This takes care of gaining exlusive access to the process
|
||
// security fields for us)
|
||
//
|
||
|
||
(*Token) = PsReferencePrimaryToken( Process );
|
||
|
||
|
||
|
||
//
|
||
// Done with the process object
|
||
//
|
||
|
||
ObDereferenceObject( Process );
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
PsImpersonateClient(
|
||
IN PETHREAD Thread,
|
||
IN PACCESS_TOKEN Token,
|
||
IN BOOLEAN CopyOnOpen,
|
||
IN BOOLEAN EffectiveOnly,
|
||
IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets up the specified thread so that it is impersonating
|
||
the specified client. This will result in the reference count of the
|
||
token representing the client being incremented to reflect the new
|
||
reference.
|
||
|
||
If the thread is currently impersonating a client, that token will be
|
||
dereferenced.
|
||
|
||
|
||
|
||
Arguments:
|
||
|
||
Thread - points to the thread which is going to impersonate a client.
|
||
|
||
Token - Points to the token to be assigned as the impersonation token.
|
||
This does NOT have to be a TokenImpersonation type token. This
|
||
allows direct reference of client process's primary tokens.
|
||
|
||
CopyOnOpen - If TRUE, indicates the token is considered to be private
|
||
by the assigner and should be copied if opened. For example, a
|
||
session layer may be using a token to represent a client's context.
|
||
If the session is trying to synchronize the context of the client,
|
||
then user mode code should not be given direct access to the session
|
||
layer's token.
|
||
|
||
Basically, session layers should always specify TRUE for this, while
|
||
tokens assigned by the server itself (handle based) should specify
|
||
FALSE.
|
||
|
||
|
||
EffectiveOnly - Is a boolean value to be assigned as the
|
||
Thread->ImpersonationInfo->EffectiveOnly field value for the
|
||
impersonation. A value of FALSE indicates the server is allowed
|
||
to enable currently disabled groups and privileges.
|
||
|
||
ImpersonationLevel - Is the impersonation level that the server is allowed
|
||
to access the token with.
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the call completed successfully.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PPS_IMPERSONATION_INFORMATION
|
||
NewClient;
|
||
|
||
PACCESS_TOKEN
|
||
OldToken;
|
||
|
||
|
||
ASSERT( Thread->Tcb.Header.Type == ThreadObject );
|
||
|
||
|
||
//
|
||
// Lock the process security fields
|
||
//
|
||
|
||
PspLockProcessSecurityFields();
|
||
|
||
|
||
if (!ARGUMENT_PRESENT(Token)) {
|
||
|
||
//
|
||
// This is a request to revert to self.
|
||
// Clean up any client information.
|
||
//
|
||
|
||
if ( Thread->ActiveImpersonationInfo ) {
|
||
|
||
OldToken = Thread->ImpersonationInfo->Token;
|
||
Thread->ActiveImpersonationInfo = FALSE;
|
||
} else {
|
||
OldToken = NULL;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// If we are already impersonating someone,
|
||
// use the already allocated block. This avoids
|
||
// an alloc and a free.
|
||
//
|
||
|
||
if ( Thread->ActiveImpersonationInfo ) {
|
||
|
||
//
|
||
// capture the old token pointer.
|
||
// We'll dereference it after unlocking the security fields.
|
||
//
|
||
|
||
OldToken = Thread->ImpersonationInfo->Token;
|
||
NewClient = Thread->ImpersonationInfo;
|
||
|
||
} else {
|
||
|
||
OldToken = NULL;
|
||
|
||
if ( Thread->ImpersonationInfo ) {
|
||
NewClient = Thread->ImpersonationInfo;
|
||
} else {
|
||
|
||
//
|
||
// Allocate and set up the Client block
|
||
//
|
||
|
||
NewClient = (PPS_IMPERSONATION_INFORMATION)ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
sizeof(PS_IMPERSONATION_INFORMATION),
|
||
'mIsP');
|
||
if (NewClient == NULL) {
|
||
PspFreeProcessSecurityFields();
|
||
return; // BUG, BUG - ARGH! I need to fix PsImpersonateClient so that it can fail !!
|
||
// CHECK TO MAKE SURE THIS WASN'T EXPORTED TO DRIVERS.
|
||
}
|
||
|
||
Thread->ImpersonationInfo = NewClient;
|
||
}
|
||
}
|
||
|
||
NewClient->ImpersonationLevel = ImpersonationLevel;
|
||
NewClient->EffectiveOnly = EffectiveOnly;
|
||
NewClient->CopyOnOpen = CopyOnOpen;
|
||
NewClient->Token = Token;
|
||
Thread->ActiveImpersonationInfo = TRUE;
|
||
|
||
ObReferenceObject(NewClient->Token);
|
||
}
|
||
|
||
//
|
||
// Release the security fields
|
||
//
|
||
|
||
PspFreeProcessSecurityFields();
|
||
|
||
|
||
//
|
||
// Free the old client token, if necessary.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( OldToken )) {
|
||
|
||
PsDereferenceImpersonationToken( OldToken );
|
||
}
|
||
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
PsDisableImpersonation(
|
||
IN PETHREAD Thread,
|
||
IN PSE_IMPERSONATION_STATE ImpersonationState
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine temporarily disables the impersonation of a thread.
|
||
The impersonation state is saved for quick replacement later. The
|
||
impersonation token is left referenced and a pointer to it is held
|
||
in the IMPERSONATION_STATE data structure.
|
||
|
||
PsRestoreImpersonation() must be used after this routine is called.
|
||
|
||
|
||
|
||
Arguments:
|
||
|
||
Thread - points to the thread whose impersonation (if any) is to
|
||
be temporarily disabled.
|
||
|
||
ImpersonationState - receives the current impersonation information,
|
||
including a pointer to the impersonation token.
|
||
|
||
|
||
Return Value:
|
||
|
||
TRUE - Indicates the impersonation state has been saved and the
|
||
impersonation has been temporarily disabled.
|
||
|
||
FALSE - Indicates the specified thread was not impersonating a client.
|
||
No action has been taken.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
|
||
PPS_IMPERSONATION_INFORMATION
|
||
OldClient;
|
||
|
||
ASSERT( Thread->Tcb.Header.Type == ThreadObject );
|
||
|
||
//
|
||
// Lock the process security fields
|
||
//
|
||
|
||
PspLockProcessSecurityFields();
|
||
|
||
//
|
||
// Capture the impersonation information (if there is any).
|
||
//
|
||
|
||
if ( Thread->ActiveImpersonationInfo ) {
|
||
|
||
OldClient = Thread->ImpersonationInfo;
|
||
ImpersonationState->Level = OldClient->ImpersonationLevel;
|
||
ImpersonationState->EffectiveOnly = OldClient->EffectiveOnly;
|
||
ImpersonationState->CopyOnOpen = OldClient->CopyOnOpen;
|
||
ImpersonationState->Token = OldClient->Token;
|
||
} else {
|
||
|
||
//
|
||
// Not impersonating. Just make up some values.
|
||
// The NULL for the token indicates we aren't impersonating.
|
||
//
|
||
|
||
OldClient = NULL;
|
||
ImpersonationState->Level = SecurityAnonymous;
|
||
ImpersonationState->EffectiveOnly = FALSE;
|
||
ImpersonationState->CopyOnOpen = FALSE;
|
||
ImpersonationState->Token = NULL;
|
||
}
|
||
|
||
//
|
||
// Clear the Client field to indicate the thread is not impersonating.
|
||
//
|
||
|
||
Thread->ActiveImpersonationInfo = FALSE;
|
||
|
||
|
||
//
|
||
// Release the security fields
|
||
//
|
||
|
||
PspFreeProcessSecurityFields();
|
||
|
||
|
||
if ( OldClient ) {
|
||
|
||
return TRUE;
|
||
|
||
} else {
|
||
return FALSE;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
PsRestoreImpersonation(
|
||
IN PETHREAD Thread,
|
||
IN PSE_IMPERSONATION_STATE ImpersonationState
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine restores an impersonation that has been temporarily disabled
|
||
using PsDisableImpersonation().
|
||
|
||
Notice that if this routine finds the thread is already impersonating
|
||
(again), then restoring the temporarily disabled impersonation will cause
|
||
the current impersonation to be abandoned.
|
||
|
||
|
||
|
||
Arguments:
|
||
|
||
Thread - points to the thread whose impersonation is to be restored.
|
||
|
||
ImpersontionState - receives the current impersontion information,
|
||
including a pointer ot the impersonation token.
|
||
|
||
|
||
Return Value:
|
||
|
||
TRUE - Indicates the impersonation state has been saved and the
|
||
impersonation has been temporarily disabled.
|
||
|
||
FALSE - Indicates the specified thread was not impersonating a client.
|
||
No action has been taken.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
|
||
//
|
||
// The processing for restore is identical to that for Impersonation,
|
||
// except that the token's reference count will be incremented (and
|
||
// so we need to do one dereference).
|
||
|
||
PsImpersonateClient(
|
||
Thread,
|
||
ImpersonationState->Token,
|
||
ImpersonationState->CopyOnOpen,
|
||
ImpersonationState->EffectiveOnly,
|
||
ImpersonationState->Level
|
||
);
|
||
|
||
|
||
ObDereferenceObject( ImpersonationState->Token );
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
PsRevertToSelf( )
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine causes the calling thread to discontinue
|
||
impersonating a client. If the thread is not currently
|
||
impersonating a client, no action is taken.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PETHREAD
|
||
Thread = PsGetCurrentThread();
|
||
|
||
PACCESS_TOKEN OldToken;
|
||
|
||
//
|
||
// Lock the process security fields
|
||
//
|
||
|
||
PspLockProcessSecurityFields();
|
||
|
||
//
|
||
// See if the thread is impersonating a client
|
||
// and dereference that token if so.
|
||
//
|
||
|
||
if ( Thread->ActiveImpersonationInfo ) {
|
||
|
||
Thread->ActiveImpersonationInfo = FALSE;
|
||
OldToken = Thread->ImpersonationInfo->Token;
|
||
} else {
|
||
OldToken = NULL;
|
||
}
|
||
|
||
|
||
//
|
||
// Release the security fields
|
||
//
|
||
|
||
PspFreeProcessSecurityFields();
|
||
|
||
|
||
//
|
||
// Free the old client info...
|
||
//
|
||
|
||
if ( OldToken ) {
|
||
ObDereferenceObject( OldToken );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
PspInitializeProcessSecurity(
|
||
IN PEPROCESS Parent OPTIONAL,
|
||
IN PEPROCESS Child
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function initializes a new process's security fields, including
|
||
the assignment of a new primary token.
|
||
|
||
The child process is assumed to not yet have been inserted into
|
||
an object table.
|
||
|
||
NOTE: IT IS EXPECTED THAT THIS SERVICE WILL BE CALLED WITH A NULL
|
||
PARENT PROCESS POINTER EXACTLY ONCE - FOR THE INITIAL SYSTEM
|
||
PROCESS.
|
||
|
||
|
||
Arguments:
|
||
|
||
Parent - An optional pointer to the process being used as the parent
|
||
of the new process. If this value is NULL, then the process is
|
||
assumed to be the initial system process, and the boot token is
|
||
assigned rather than a duplicate of the parent process's primary
|
||
token.
|
||
|
||
Child - Supplies the address of the process being initialized. This
|
||
process does not yet require security field contention protection.
|
||
In particular, the security fields may be accessed without first
|
||
acquiring the process security fields lock.
|
||
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS
|
||
Status;
|
||
|
||
|
||
//
|
||
// Assign the primary token
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(Parent)) {
|
||
|
||
//
|
||
// create the primary token
|
||
// This is a duplicate of the parent's token.
|
||
//
|
||
|
||
Status = SeSubProcessToken(
|
||
Parent,
|
||
&Child->Token
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
Child->Token = NULL; // Needed to ensure proper deletion
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Reference and assign the boot token
|
||
//
|
||
// The use of a single boot access token assumes there is
|
||
// exactly one parentless process in the system - the initial
|
||
// process. If this ever changes, this code will need to change
|
||
// to match the new condition (so that a token doesn't end up
|
||
// being shared by multiple processes.
|
||
//
|
||
|
||
Child->Token = NULL;
|
||
SeAssignPrimaryToken( Child, PspBootAccessToken );
|
||
Status = STATUS_SUCCESS;
|
||
|
||
|
||
}
|
||
|
||
return Status;
|
||
|
||
}
|
||
|
||
VOID
|
||
PspDeleteProcessSecurity(
|
||
IN PEPROCESS Process
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function cleans up a process's security fields as part of process
|
||
deletion. It is assumed no other references to the process can occur
|
||
during or after a call to this routine. This enables us to reference
|
||
the process security fields without acquiring the lock protecting those
|
||
fields.
|
||
|
||
NOTE: It may be desirable to add auditing capability to this routine
|
||
at some point.
|
||
|
||
|
||
Arguments:
|
||
|
||
Process - A pointer to the process being deleted.
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
|
||
|
||
|
||
//
|
||
// If we are deleting a process that didn't successfully complete
|
||
// process initialization, then there may be no token associated
|
||
// with it yet.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(Process->Token)) {
|
||
SeDeassignPrimaryToken( Process );
|
||
Process->Token = NULL;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
PspAssignPrimaryToken(
|
||
IN PEPROCESS Process,
|
||
IN HANDLE Token
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function performs the security portions of primary token assignment.
|
||
It is expected that the proper access to the process and thread objects,
|
||
as well as necessary privilege, has already been established.
|
||
|
||
A primary token can only be replaced if the process has no threads, or
|
||
has one thread. This is because the thread objects point to the primary
|
||
token and must have those pointers updated when the primary token is
|
||
changed. This is only expected to be necessary at logon time, when
|
||
the process is in its infancy and either has zero threads or maybe one
|
||
inactive thread.
|
||
|
||
If the assignment is successful, the old token is dereferenced and the
|
||
new one is referenced.
|
||
|
||
|
||
|
||
Arguments:
|
||
|
||
Process - A pointer to the process whose primary token is being
|
||
replaced.
|
||
|
||
Token - The handle value of the token to be assigned as the primary
|
||
token.
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the primary token has been successfully
|
||
replaced.
|
||
|
||
STATUS_BAD_TOKEN_TYPE - Indicates the token is not of type TokenPrimary.
|
||
|
||
STATUS_TOKEN_IN_USE - Indicates the token is already in use by
|
||
another process.
|
||
|
||
Other status may be returned when attempting to reference the token
|
||
object.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS
|
||
Status;
|
||
|
||
PACCESS_TOKEN
|
||
NewToken,
|
||
OldToken;
|
||
|
||
KPROCESSOR_MODE
|
||
PreviousMode;
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
//
|
||
// Reference the specified token, and make sure it can be assigned
|
||
// as a primary token.
|
||
//
|
||
|
||
Status = ObReferenceObjectByHandle (
|
||
Token,
|
||
TOKEN_ASSIGN_PRIMARY,
|
||
SeTokenObjectType(),
|
||
PreviousMode,
|
||
(PVOID *)&NewToken,
|
||
NULL
|
||
);
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
return Status;
|
||
}
|
||
|
||
|
||
//
|
||
// Lock the process security fields.
|
||
//
|
||
|
||
PspLockProcessSecurityFields();
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// This routine makes sure the NewToken is suitable for assignment
|
||
// as a primary token.
|
||
//
|
||
|
||
Status = SeExchangePrimaryToken( Process, NewToken, &OldToken );
|
||
|
||
|
||
//
|
||
// Release the process security fields
|
||
//
|
||
|
||
PspFreeProcessSecurityFields();
|
||
|
||
//
|
||
// Free the old token (we don't need it).
|
||
// This can't be done while the security fields are locked.
|
||
//
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
ObDereferenceObject( OldToken );
|
||
}
|
||
|
||
//
|
||
// Undo the handle reference
|
||
//
|
||
|
||
ObDereferenceObject( NewToken );
|
||
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
VOID
|
||
PspInitializeThreadSecurity(
|
||
IN PEPROCESS Process,
|
||
IN PETHREAD Thread
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function initializes a new thread's security fields.
|
||
|
||
|
||
Arguments:
|
||
|
||
Process - Points to the process the thread belongs to.
|
||
|
||
Thread - Points to the thread object being initialized.
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
|
||
//
|
||
// Initially not impersonating anyone.
|
||
//
|
||
|
||
Thread->ImpersonationInfo = NULL;
|
||
Thread->ActiveImpersonationInfo = FALSE;
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
PspDeleteThreadSecurity(
|
||
IN PETHREAD Thread
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function cleans up a thread's security fields as part of thread
|
||
deletion. It is assumed no other references to the thread can occur
|
||
during or after a call to this routine, so no locking is necessary
|
||
to access the thread security fields.
|
||
|
||
|
||
Arguments:
|
||
|
||
Thread - A pointer to the thread being deleted.
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// clean-up client information, if there is any.
|
||
//
|
||
|
||
if ( Thread->ActiveImpersonationInfo ) {
|
||
ObDereferenceObject( Thread->ImpersonationInfo->Token );
|
||
}
|
||
|
||
if ( Thread->ImpersonationInfo ) {
|
||
ExFreePool( Thread->ImpersonationInfo );
|
||
Thread->ActiveImpersonationInfo = FALSE;
|
||
Thread->ImpersonationInfo = NULL;
|
||
}
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
PsAssignImpersonationToken(
|
||
IN PETHREAD Thread,
|
||
IN HANDLE Token
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function performs the security portions of establishing an
|
||
impersonation token. This routine is expected to be used only in
|
||
the case where the subject has asked for impersonation explicitly
|
||
providing an impersonation token. Other services are provided for
|
||
use by communication session layers that need to establish an
|
||
impersonation on a server's behalf.
|
||
|
||
It is expected that the proper access to the thread object has already
|
||
been established.
|
||
|
||
The following rules apply:
|
||
|
||
1) The caller must have TOKEN_IMPERSONATE access to the token
|
||
for any action to be taken.
|
||
|
||
2) If the token may NOT be used for impersonation (e.g., not an
|
||
impersonation token) no action is taken.
|
||
|
||
3) Otherwise, any existing impersonation token is dereferenced and
|
||
the new token is established as the impersonation token.
|
||
|
||
|
||
|
||
Arguments:
|
||
|
||
Thread - A pointer to the thread whose impersonation token is being
|
||
set.
|
||
|
||
Token - The handle value of the token to be assigned as the impersonation
|
||
token. If this value is NULL, then current impersonation (if any)
|
||
is terminated and no new impersonation is established.
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the primary token has been successfully
|
||
replaced.
|
||
|
||
STATUS_BAD_TOKEN_TYPE - Indicates the token is not of type
|
||
TokenImpersonation.
|
||
|
||
Other status may be returned when attempting to reference the token
|
||
object.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS
|
||
Status;
|
||
|
||
PACCESS_TOKEN
|
||
NewToken;
|
||
|
||
KPROCESSOR_MODE
|
||
PreviousMode;
|
||
|
||
SECURITY_IMPERSONATION_LEVEL
|
||
ImpersonationLevel;
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
if (!ARGUMENT_PRESENT(Token)) {
|
||
|
||
NewToken = NULL;
|
||
|
||
//
|
||
// Don't care what value ImpersonationLevel has, it won't
|
||
// be used.
|
||
//
|
||
|
||
} else {
|
||
|
||
//
|
||
// Reference the specified token for TOKEN_IMPERSONATE access
|
||
//
|
||
|
||
Status = ObReferenceObjectByHandle (
|
||
Token,
|
||
TOKEN_IMPERSONATE,
|
||
SeTokenObjectType(),
|
||
PreviousMode,
|
||
(PVOID *)&NewToken,
|
||
NULL
|
||
);
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Make sure the token is an impersonation token.
|
||
//
|
||
|
||
if (SeTokenType( NewToken ) != TokenImpersonation ) {
|
||
ObDereferenceObject( NewToken );
|
||
return STATUS_BAD_TOKEN_TYPE;
|
||
}
|
||
|
||
ImpersonationLevel = SeTokenImpersonationLevel( NewToken );
|
||
}
|
||
|
||
//
|
||
// The rest can be done by PsImpersonateClient
|
||
//
|
||
|
||
PsImpersonateClient(
|
||
Thread,
|
||
NewToken,
|
||
FALSE, // CopyOnOpen
|
||
FALSE, //EffectiveOnly
|
||
ImpersonationLevel
|
||
);
|
||
|
||
//
|
||
// dereference the passed token, if there is one.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(NewToken)) {
|
||
ObDereferenceObject( NewToken );
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|