924 lines
25 KiB
C
924 lines
25 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
tokenopn.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements the open thread and process token services.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Jim Kelly (JimK) 2-Aug-1990
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode only.
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
//#ifndef TOKEN_DEBUG
|
|||
|
//#define TOKEN_DEBUG
|
|||
|
//#endif
|
|||
|
|
|||
|
#include "pch.h"
|
|||
|
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SepCreateImpersonationTokenDacl(
|
|||
|
IN PTOKEN Token,
|
|||
|
IN PACCESS_TOKEN PrimaryToken,
|
|||
|
OUT PACL *Acl
|
|||
|
);
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
NTSTATUS
|
|||
|
SepOpenTokenOfThread(
|
|||
|
IN HANDLE ThreadHandle,
|
|||
|
IN BOOLEAN OpenAsSelf,
|
|||
|
OUT PACCESS_TOKEN *Token,
|
|||
|
OUT PETHREAD *Thread,
|
|||
|
OUT PBOOLEAN CopyOnOpen,
|
|||
|
OUT PBOOLEAN EffectiveOnly,
|
|||
|
OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel
|
|||
|
);
|
|||
|
#pragma alloc_text(PAGE,SepCreateImpersonationTokenDacl)
|
|||
|
#pragma alloc_text(PAGE,NtOpenProcessToken)
|
|||
|
#pragma alloc_text(PAGE,NtOpenProcessTokenEx)
|
|||
|
#pragma alloc_text(PAGE,SepOpenTokenOfThread)
|
|||
|
#pragma alloc_text(PAGE,NtOpenThreadToken)
|
|||
|
#pragma alloc_text(PAGE,NtOpenThreadTokenEx)
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SepCreateImpersonationTokenDacl(
|
|||
|
IN PTOKEN Token,
|
|||
|
IN PACCESS_TOKEN PrimaryToken,
|
|||
|
OUT PACL *Acl
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine modifies the DACL protecting the passed token to allow
|
|||
|
the current user (described by the PrimaryToken parameter) full access.
|
|||
|
This permits callers of NtOpenThreadToken to call with OpenAsSelf==TRUE
|
|||
|
and succeed.
|
|||
|
|
|||
|
The new DACL placed on the token is as follows:
|
|||
|
|
|||
|
ACE 0 - Server gets TOKEN_ALL_ACCESS
|
|||
|
|
|||
|
ACE 1 - Client gets TOKEN_ALL_ACCESS
|
|||
|
|
|||
|
ACE 2 - Admins gets TOKEN_ALL_ACCESS
|
|||
|
|
|||
|
ACE 3 - System gets TOKEN_ALL_ACCESS
|
|||
|
|
|||
|
ACE 4 - Restricted gets TOKEN_ALL_ACCESS
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Token - The token whose protection is to be modified.
|
|||
|
|
|||
|
PrimaryToken - Token representing the subject to be granted access.
|
|||
|
|
|||
|
Acl - Returns the modified ACL, allocated out of PagedPool.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSID ServerUserSid;
|
|||
|
PSID ClientUserSid;
|
|||
|
NTSTATUS Status = STATUS_SUCCESS;
|
|||
|
ULONG AclLength;
|
|||
|
PACL NewDacl;
|
|||
|
PSECURITY_DESCRIPTOR OldDescriptor;
|
|||
|
BOOLEAN MemoryAllocated;
|
|||
|
PACL OldDacl;
|
|||
|
BOOLEAN DaclPresent;
|
|||
|
BOOLEAN DaclDefaulted;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ServerUserSid = ((PTOKEN)PrimaryToken)->UserAndGroups[0].Sid;
|
|||
|
|
|||
|
ClientUserSid = Token->UserAndGroups[0].Sid;
|
|||
|
|
|||
|
//
|
|||
|
// Compute how much space we'll need for the new DACL.
|
|||
|
//
|
|||
|
|
|||
|
AclLength = 5 * sizeof( ACCESS_ALLOWED_ACE ) - 5 * sizeof( ULONG ) +
|
|||
|
SeLengthSid( ServerUserSid ) + SeLengthSid( SeLocalSystemSid ) +
|
|||
|
SeLengthSid( ClientUserSid ) + SeLengthSid( SeAliasAdminsSid ) +
|
|||
|
SeLengthSid( SeRestrictedSid ) + sizeof( ACL );
|
|||
|
|
|||
|
NewDacl = ExAllocatePool( PagedPool, AclLength );
|
|||
|
|
|||
|
if (NewDacl == NULL) {
|
|||
|
|
|||
|
*Acl = NULL;
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
Status = RtlCreateAcl( NewDacl, AclLength, ACL_REVISION2 );
|
|||
|
ASSERT(NT_SUCCESS( Status ));
|
|||
|
|
|||
|
Status = RtlAddAccessAllowedAce (
|
|||
|
NewDacl,
|
|||
|
ACL_REVISION2,
|
|||
|
TOKEN_ALL_ACCESS,
|
|||
|
ServerUserSid
|
|||
|
);
|
|||
|
ASSERT( NT_SUCCESS( Status ));
|
|||
|
|
|||
|
Status = RtlAddAccessAllowedAce (
|
|||
|
NewDacl,
|
|||
|
ACL_REVISION2,
|
|||
|
TOKEN_ALL_ACCESS,
|
|||
|
ClientUserSid
|
|||
|
);
|
|||
|
ASSERT( NT_SUCCESS( Status ));
|
|||
|
|
|||
|
Status = RtlAddAccessAllowedAce (
|
|||
|
NewDacl,
|
|||
|
ACL_REVISION2,
|
|||
|
TOKEN_ALL_ACCESS,
|
|||
|
SeAliasAdminsSid
|
|||
|
);
|
|||
|
ASSERT( NT_SUCCESS( Status ));
|
|||
|
|
|||
|
Status = RtlAddAccessAllowedAce (
|
|||
|
NewDacl,
|
|||
|
ACL_REVISION2,
|
|||
|
TOKEN_ALL_ACCESS,
|
|||
|
SeLocalSystemSid
|
|||
|
);
|
|||
|
ASSERT( NT_SUCCESS( Status ));
|
|||
|
|
|||
|
if(ARGUMENT_PRESENT(((PTOKEN)PrimaryToken)->RestrictedSids) ||
|
|||
|
ARGUMENT_PRESENT(Token->RestrictedSids)) {
|
|||
|
Status = RtlAddAccessAllowedAce (
|
|||
|
NewDacl,
|
|||
|
ACL_REVISION2,
|
|||
|
TOKEN_ALL_ACCESS,
|
|||
|
SeRestrictedSid
|
|||
|
);
|
|||
|
ASSERT( NT_SUCCESS( Status ));
|
|||
|
}
|
|||
|
|
|||
|
*Acl = NewDacl;
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NtOpenProcessToken(
|
|||
|
IN HANDLE ProcessHandle,
|
|||
|
IN ACCESS_MASK DesiredAccess,
|
|||
|
OUT PHANDLE TokenHandle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Open a token object associated with a process and return a handle
|
|||
|
that may be used to access that token.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ProcessHandle - Specifies the process whose token is to be
|
|||
|
opened.
|
|||
|
|
|||
|
DesiredAccess - Is an access mask indicating which access types
|
|||
|
are desired to the token. These access types are reconciled
|
|||
|
with the Discretionary Access Control list of the token to
|
|||
|
determine whether the accesses will be granted or denied.
|
|||
|
|
|||
|
TokenHandle - Receives the handle of the newly opened token.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - Indicates the operation was successful.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
return NtOpenProcessTokenEx (ProcessHandle,
|
|||
|
DesiredAccess,
|
|||
|
0,
|
|||
|
TokenHandle);
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NtOpenProcessTokenEx(
|
|||
|
IN HANDLE ProcessHandle,
|
|||
|
IN ACCESS_MASK DesiredAccess,
|
|||
|
IN ULONG HandleAttributes,
|
|||
|
OUT PHANDLE TokenHandle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Open a token object associated with a process and return a handle
|
|||
|
that may be used to access that token.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ProcessHandle - Specifies the process whose token is to be
|
|||
|
opened.
|
|||
|
|
|||
|
DesiredAccess - Is an access mask indicating which access types
|
|||
|
are desired to the token. These access types are reconciled
|
|||
|
with the Discretionary Access Control list of the token to
|
|||
|
determine whether the accesses will be granted or denied.
|
|||
|
|
|||
|
HandleAttributes - Attributes for the created handle. Only OBJ_KERNEL_HANDLE at present.
|
|||
|
|
|||
|
TokenHandle - Receives the handle of the newly opened token.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - Indicates the operation was successful.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
PVOID Token;
|
|||
|
KPROCESSOR_MODE PreviousMode;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
HANDLE LocalHandle;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
PreviousMode = KeGetPreviousMode();
|
|||
|
|
|||
|
//
|
|||
|
// Reject bad flags
|
|||
|
//
|
|||
|
if (HandleAttributes&~OBJ_KERNEL_HANDLE) {
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Probe parameters
|
|||
|
//
|
|||
|
|
|||
|
if (PreviousMode != KernelMode) {
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
ProbeForWriteHandle(TokenHandle);
|
|||
|
|
|||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
return GetExceptionCode();
|
|||
|
} // end_try
|
|||
|
|
|||
|
HandleAttributes &= ~OBJ_KERNEL_HANDLE;
|
|||
|
} //end_if
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Valdiate access to the process and obtain a pointer to the
|
|||
|
// process's token. If successful, this will cause the token's
|
|||
|
// reference count to be incremented.
|
|||
|
//
|
|||
|
|
|||
|
Status = PsOpenTokenOfProcess( ProcessHandle, ((PACCESS_TOKEN *)&Token));
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now try to open the token for the specified desired access
|
|||
|
//
|
|||
|
|
|||
|
Status = ObOpenObjectByPointer(
|
|||
|
(PVOID)Token, // Object
|
|||
|
HandleAttributes, // HandleAttributes
|
|||
|
NULL, // AccessState
|
|||
|
DesiredAccess, // DesiredAccess
|
|||
|
SeTokenObjectType, // ObjectType
|
|||
|
PreviousMode, // AccessMode
|
|||
|
&LocalHandle // Handle
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// And decrement the reference count of the token to counter
|
|||
|
// the action performed by PsOpenTokenOfProcess(). If the open
|
|||
|
// was successful, the handle will have caused the token's
|
|||
|
// reference count to have been incremented.
|
|||
|
//
|
|||
|
|
|||
|
ObDereferenceObject( Token );
|
|||
|
|
|||
|
//
|
|||
|
// Return the new handle
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
*TokenHandle = LocalHandle;
|
|||
|
|
|||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
|
|||
|
return GetExceptionCode();
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SepOpenTokenOfThread(
|
|||
|
IN HANDLE ThreadHandle,
|
|||
|
IN BOOLEAN OpenAsSelf,
|
|||
|
OUT PACCESS_TOKEN *Token,
|
|||
|
OUT PETHREAD *Thread,
|
|||
|
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;
|
|||
|
|
|||
|
KPROCESSOR_MODE
|
|||
|
PreviousMode;
|
|||
|
|
|||
|
SE_IMPERSONATION_STATE
|
|||
|
DisabledImpersonationState;
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
RestoreImpersonationState = FALSE;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
PreviousMode = KeGetPreviousMode();
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// 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;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Reference the impersonation token, if there is one
|
|||
|
//
|
|||
|
|
|||
|
(*Token) = PsReferenceImpersonationToken( *Thread,
|
|||
|
CopyOnOpen,
|
|||
|
EffectiveOnly,
|
|||
|
ImpersonationLevel
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Make sure there is a token
|
|||
|
//
|
|||
|
|
|||
|
if ((*Token) == NULL) {
|
|||
|
ObDereferenceObject( *Thread );
|
|||
|
(*Thread) = NULL;
|
|||
|
return STATUS_NO_TOKEN;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the ImpersonationLevel is high enough to allow
|
|||
|
// the token to be openned.
|
|||
|
//
|
|||
|
|
|||
|
if ((*ImpersonationLevel) <= SecurityAnonymous) {
|
|||
|
PsDereferenceImpersonationToken( (*Token) );
|
|||
|
ObDereferenceObject( *Thread );
|
|||
|
(*Thread) = NULL;
|
|||
|
(*Token) = NULL;
|
|||
|
return STATUS_CANT_OPEN_ANONYMOUS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NtOpenThreadToken(
|
|||
|
IN HANDLE ThreadHandle,
|
|||
|
IN ACCESS_MASK DesiredAccess,
|
|||
|
IN BOOLEAN OpenAsSelf,
|
|||
|
OUT PHANDLE TokenHandle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Open a token object associated with a thread and return a handle that
|
|||
|
may be used to access that token.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ThreadHandle - Specifies the thread whose token is to be opened.
|
|||
|
|
|||
|
DesiredAccess - Is an access mask indicating which access types
|
|||
|
are desired to the token. These access types are reconciled
|
|||
|
with the Discretionary Access Control list of the token to
|
|||
|
determine whether the accesses will be granted or denied.
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
This parameter is necessary to allow a server process to open
|
|||
|
a client's token when the client specified IDENTIFICATION level
|
|||
|
impersonation. In this case, the caller would not be able to
|
|||
|
open the client's token using the client's context (because you
|
|||
|
can't create executive level objects using IDENTIFICATION level
|
|||
|
impersonation).
|
|||
|
|
|||
|
TokenHandle - Receives the handle of the newly opened token.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - Indicates the operation was successful.
|
|||
|
|
|||
|
STATUS_NO_TOKEN - Indicates an attempt has been made to open a
|
|||
|
token associated with a thread that is not currently
|
|||
|
impersonating a client.
|
|||
|
|
|||
|
STATUS_CANT_OPEN_ANONYMOUS - Indicates the client requested anonymous
|
|||
|
impersonation level. An anonymous token can not be openned.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
return NtOpenThreadTokenEx (ThreadHandle,
|
|||
|
DesiredAccess,
|
|||
|
OpenAsSelf,
|
|||
|
0,
|
|||
|
TokenHandle);
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NtOpenThreadTokenEx(
|
|||
|
IN HANDLE ThreadHandle,
|
|||
|
IN ACCESS_MASK DesiredAccess,
|
|||
|
IN BOOLEAN OpenAsSelf,
|
|||
|
IN ULONG HandleAttributes,
|
|||
|
OUT PHANDLE TokenHandle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Open a token object associated with a thread and return a handle that
|
|||
|
may be used to access that token.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ThreadHandle - Specifies the thread whose token is to be opened.
|
|||
|
|
|||
|
DesiredAccess - Is an access mask indicating which access types
|
|||
|
are desired to the token. These access types are reconciled
|
|||
|
with the Discretionary Access Control list of the token to
|
|||
|
determine whether the accesses will be granted or denied.
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
This parameter is necessary to allow a server process to open
|
|||
|
a client's token when the client specified IDENTIFICATION level
|
|||
|
impersonation. In this case, the caller would not be able to
|
|||
|
open the client's token using the client's context (because you
|
|||
|
can't create executive level objects using IDENTIFICATION level
|
|||
|
impersonation).
|
|||
|
|
|||
|
HandleAttributes - Attributes applied to the handle OBJ_KERNEL_HANDLE
|
|||
|
|
|||
|
TokenHandle - Receives the handle of the newly opened token.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - Indicates the operation was successful.
|
|||
|
|
|||
|
STATUS_NO_TOKEN - Indicates an attempt has been made to open a
|
|||
|
token associated with a thread that is not currently
|
|||
|
impersonating a client.
|
|||
|
|
|||
|
STATUS_CANT_OPEN_ANONYMOUS - Indicates the client requested anonymous
|
|||
|
impersonation level. An anonymous token can not be openned.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
KPROCESSOR_MODE PreviousMode;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
PVOID Token;
|
|||
|
PTOKEN NewToken = NULL;
|
|||
|
BOOLEAN CopyOnOpen;
|
|||
|
BOOLEAN EffectiveOnly;
|
|||
|
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
|
|||
|
SE_IMPERSONATION_STATE DisabledImpersonationState;
|
|||
|
BOOLEAN RestoreImpersonationState = FALSE;
|
|||
|
|
|||
|
HANDLE LocalHandle = NULL;
|
|||
|
SECURITY_DESCRIPTOR SecurityDescriptor;
|
|||
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|||
|
PACL NewAcl = NULL;
|
|||
|
PETHREAD Thread = NULL;
|
|||
|
PETHREAD OriginalThread = NULL;
|
|||
|
PACCESS_TOKEN PrimaryToken;
|
|||
|
SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
PreviousMode = KeGetPreviousMode();
|
|||
|
|
|||
|
//
|
|||
|
// Probe parameters
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Reject bad flags
|
|||
|
//
|
|||
|
if (HandleAttributes&~OBJ_KERNEL_HANDLE) {
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
if (PreviousMode != KernelMode) {
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
ProbeForWriteHandle(TokenHandle);
|
|||
|
|
|||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
return GetExceptionCode();
|
|||
|
} // end_try
|
|||
|
|
|||
|
HandleAttributes &= ~OBJ_KERNEL_HANDLE;
|
|||
|
|
|||
|
} //end_if
|
|||
|
|
|||
|
//
|
|||
|
// Valdiate access to the thread and obtain a pointer to the
|
|||
|
// thread's token (if there is one). If successful, this will
|
|||
|
// cause the token's reference count to be incremented.
|
|||
|
//
|
|||
|
// This routine disabled impersonation as necessary to properly
|
|||
|
// honor the OpenAsSelf flag.
|
|||
|
//
|
|||
|
|
|||
|
Status = SepOpenTokenOfThread( ThreadHandle,
|
|||
|
OpenAsSelf,
|
|||
|
((PACCESS_TOKEN *)&Token),
|
|||
|
&OriginalThread,
|
|||
|
&CopyOnOpen,
|
|||
|
&EffectiveOnly,
|
|||
|
&ImpersonationLevel
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// The token was successfully referenced.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// We need to create and/or open a token object, so disable impersonation
|
|||
|
// if necessary.
|
|||
|
//
|
|||
|
|
|||
|
if (OpenAsSelf) {
|
|||
|
RestoreImpersonationState = PsDisableImpersonation(
|
|||
|
PsGetCurrentThread(),
|
|||
|
&DisabledImpersonationState
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the CopyOnOpen flag is not set, then the token can be
|
|||
|
// opened directly. Otherwise, the token must be duplicated,
|
|||
|
// and a handle to the duplicate returned.
|
|||
|
//
|
|||
|
|
|||
|
if (CopyOnOpen) {
|
|||
|
|
|||
|
//
|
|||
|
// Create the new security descriptor for the token.
|
|||
|
//
|
|||
|
// We must obtain the correct SID to put into the Dacl. Do this
|
|||
|
// by finding the process associated with the passed thread
|
|||
|
// and grabbing the User SID out of that process's token.
|
|||
|
// If we just use the current SubjectContext, we'll get the
|
|||
|
// SID of whoever is calling us, which isn't what we want.
|
|||
|
//
|
|||
|
|
|||
|
Status = ObReferenceObjectByHandle(
|
|||
|
ThreadHandle,
|
|||
|
THREAD_ALL_ACCESS,
|
|||
|
PsThreadType,
|
|||
|
KernelMode,
|
|||
|
(PVOID)&Thread,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Verify that the handle is still pointer to the same thread
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(Status) && (Thread != OriginalThread)) {
|
|||
|
Status = STATUS_OBJECT_TYPE_MISMATCH;
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
PEPROCESS Process = THREAD_TO_PROCESS(Thread);
|
|||
|
|
|||
|
PrimaryToken = PsReferencePrimaryToken(Process);
|
|||
|
|
|||
|
Status = SepCreateImpersonationTokenDacl(
|
|||
|
(PTOKEN)Token,
|
|||
|
PrimaryToken,
|
|||
|
&NewAcl
|
|||
|
);
|
|||
|
|
|||
|
PsDereferencePrimaryTokenEx( Process, PrimaryToken );
|
|||
|
|
|||
|
if (NT_SUCCESS( Status )) {
|
|||
|
|
|||
|
if (NewAcl != NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// There exist tokens that either do not have security descriptors at all,
|
|||
|
// or have security descriptors, but do not have DACLs. In either case, do
|
|||
|
// nothing.
|
|||
|
//
|
|||
|
|
|||
|
Status = RtlCreateSecurityDescriptor ( &SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION );
|
|||
|
ASSERT( NT_SUCCESS( Status ));
|
|||
|
|
|||
|
Status = RtlSetDaclSecurityDescriptor (
|
|||
|
&SecurityDescriptor,
|
|||
|
TRUE,
|
|||
|
NewAcl,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
|
|||
|
ASSERT( NT_SUCCESS( Status ));
|
|||
|
}
|
|||
|
|
|||
|
InitializeObjectAttributes(
|
|||
|
&ObjectAttributes,
|
|||
|
NULL,
|
|||
|
HandleAttributes,
|
|||
|
NULL,
|
|||
|
NewAcl == NULL ? NULL : &SecurityDescriptor
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Open a copy of the token
|
|||
|
//
|
|||
|
|
|||
|
Status = SepDuplicateToken(
|
|||
|
(PTOKEN)Token, // ExistingToken
|
|||
|
&ObjectAttributes, // ObjectAttributes
|
|||
|
EffectiveOnly, // EffectiveOnly
|
|||
|
TokenImpersonation, // TokenType
|
|||
|
ImpersonationLevel, // ImpersonationLevel
|
|||
|
KernelMode, // RequestorMode must be kernel mode
|
|||
|
&NewToken
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS( Status )) {
|
|||
|
|
|||
|
//
|
|||
|
// Reference the token so it doesn't go away
|
|||
|
//
|
|||
|
|
|||
|
ObReferenceObject(NewToken);
|
|||
|
|
|||
|
//
|
|||
|
// Insert the new token
|
|||
|
//
|
|||
|
|
|||
|
Status = ObInsertObject( NewToken,
|
|||
|
NULL,
|
|||
|
DesiredAccess,
|
|||
|
0,
|
|||
|
(PVOID *)NULL,
|
|||
|
&LocalHandle
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// We do not have to modify the security on the token in the static case,
|
|||
|
// because in all the places in the system where impersonation takes place
|
|||
|
// over a secure transport (e.g., LPC), CopyOnOpen is set. The only reason
|
|||
|
// we'be be here is if the impersonation is taking place because someone did
|
|||
|
// an NtSetInformationThread and passed in a token.
|
|||
|
//
|
|||
|
// In that case, we absolutely do not want to give the caller guaranteed
|
|||
|
// access, because that would allow anyone who has access to a thread to
|
|||
|
// impersonate any of that thread's clients for any access.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Open the existing token
|
|||
|
//
|
|||
|
|
|||
|
Status = ObOpenObjectByPointer(
|
|||
|
(PVOID)Token, // Object
|
|||
|
HandleAttributes, // HandleAttributes
|
|||
|
NULL, // AccessState
|
|||
|
DesiredAccess, // DesiredAccess
|
|||
|
SeTokenObjectType, // ObjectType
|
|||
|
PreviousMode, // AccessMode
|
|||
|
&LocalHandle // Handle
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
if (NewAcl != NULL) {
|
|||
|
ExFreePool( NewAcl );
|
|||
|
}
|
|||
|
|
|||
|
if (RestoreImpersonationState) {
|
|||
|
PsRestoreImpersonation(
|
|||
|
PsGetCurrentThread(),
|
|||
|
&DisabledImpersonationState
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// And decrement the reference count of the existing token to counter
|
|||
|
// the action performed by PsOpenTokenOfThread. If the open
|
|||
|
// was successful, the handle will have caused the token's
|
|||
|
// reference count to have been incremented.
|
|||
|
//
|
|||
|
|
|||
|
ObDereferenceObject( Token );
|
|||
|
|
|||
|
if (NT_SUCCESS( Status ) && CopyOnOpen) {
|
|||
|
|
|||
|
//
|
|||
|
// Assign the newly duplicated token to the thread.
|
|||
|
//
|
|||
|
|
|||
|
PsImpersonateClient( Thread,
|
|||
|
NewToken,
|
|||
|
FALSE, // turn off CopyOnOpen flag
|
|||
|
EffectiveOnly,
|
|||
|
ImpersonationLevel
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We've impersonated the token so let go of oure reference
|
|||
|
//
|
|||
|
|
|||
|
if (NewToken != NULL) {
|
|||
|
ObDereferenceObject( NewToken );
|
|||
|
}
|
|||
|
|
|||
|
if (CopyOnOpen && (Thread != NULL)) {
|
|||
|
|
|||
|
ObDereferenceObject( Thread );
|
|||
|
}
|
|||
|
|
|||
|
if (OriginalThread != NULL) {
|
|||
|
ObDereferenceObject(OriginalThread);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Return the new handle
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
try {
|
|||
|
*TokenHandle = LocalHandle;
|
|||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
return GetExceptionCode();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|