634 lines
17 KiB
C
634 lines
17 KiB
C
|
|
|||
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
seclient.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements routines providing client impersonation to
|
|||
|
communication session layers (such as LPC Ports).
|
|||
|
|
|||
|
WARNING: The following notes apply to the use of these services:
|
|||
|
|
|||
|
(1) No synchronization of operations to a security context block are
|
|||
|
performed by these services. The caller of these services must
|
|||
|
ensure that use of an individual security context block is
|
|||
|
serialized to prevent simultaneous, incompatible updates.
|
|||
|
|
|||
|
(2) Any or all of these services may create, open, or operate on a
|
|||
|
token object. This may result in a mutex being acquired at
|
|||
|
MUTEXT_LEVEL_SE_TOKEN level. The caller must ensure that no
|
|||
|
mutexes are held at levels that conflict with this action.
|
|||
|
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Jim Kelly (JimK) 1-August-1990
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode only.
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
|
|||
|
#include "sep.h"
|
|||
|
#include "seopaque.h"
|
|||
|
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE,SeCreateClientSecurity)
|
|||
|
#pragma alloc_text(PAGE,SeUpdateClientSecurity)
|
|||
|
#pragma alloc_text(PAGE,SeImpersonateClient)
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// Routines //
|
|||
|
// //
|
|||
|
////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SeCreateClientSecurity (
|
|||
|
IN PETHREAD ClientThread,
|
|||
|
IN PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos,
|
|||
|
IN BOOLEAN ServerIsRemote,
|
|||
|
OUT PSECURITY_CLIENT_CONTEXT ClientContext
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service initializes a context block to represent a client's
|
|||
|
security context. This may simply result in a reference to the
|
|||
|
client's token, or may cause the client's token to be duplicated,
|
|||
|
depending upon the security quality of service information specified.
|
|||
|
|
|||
|
NOTE
|
|||
|
|
|||
|
The code in this routine is optimized for DYNAMIC context
|
|||
|
tracking. This is only mode in which direct access to a
|
|||
|
caller's token is allowed, and the mode expected to be used
|
|||
|
most often. STATIC context tracking always requires the
|
|||
|
caller's token to be copied.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ClientThread - Points to the client's thread. This is used to
|
|||
|
locate the client's security context (token).
|
|||
|
|
|||
|
ClientSecurityQos - Points to the security quality of service
|
|||
|
parameters specified by the client for this communication
|
|||
|
session.
|
|||
|
|
|||
|
ServerIsRemote - Provides an indication as to whether the session
|
|||
|
this context block is being used for is an inter-system
|
|||
|
session or intra-system session. This is reconciled with the
|
|||
|
impersonation level of the client thread's token (in case the
|
|||
|
client has a client of his own that didn't specify delegation).
|
|||
|
|
|||
|
ClientContext - Points to the client security context block to be
|
|||
|
initialized.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - The service completed successfully.
|
|||
|
|
|||
|
STATUS_BAD_IMPERSONATION_LEVEL - The client is currently
|
|||
|
impersonating either an Anonymous or Identification level
|
|||
|
token, which can not be passed on for use by another server.
|
|||
|
This status may also be returned if the security context
|
|||
|
block is for an inter-system communication session and the
|
|||
|
client thread is impersonating a client of its own using
|
|||
|
other than delegation impersonation level.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status = STATUS_SUCCESS;
|
|||
|
PACCESS_TOKEN Token;
|
|||
|
TOKEN_TYPE TokenType;
|
|||
|
BOOLEAN ThreadEffectiveOnly;
|
|||
|
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
|
|||
|
PACCESS_TOKEN DuplicateToken;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Gain access to the client thread's effective token
|
|||
|
//
|
|||
|
|
|||
|
Token = PsReferenceEffectiveToken(
|
|||
|
ClientThread,
|
|||
|
&TokenType,
|
|||
|
&ThreadEffectiveOnly,
|
|||
|
&ImpersonationLevel
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the client is not trying to abuse use of a
|
|||
|
// client of its own by attempting an invalid impersonation.
|
|||
|
// Also set the ClientContext->DirectAccessEffectiveOnly flag
|
|||
|
// appropriately if the impersonation is legitimate. The
|
|||
|
// DirectAccessEffectiveOnly flag value will end up being ignored
|
|||
|
// if STATIC mode is requested, but this is the most efficient
|
|||
|
// place to calculate it, and we are optimizing for DYNAMIC mode.
|
|||
|
//
|
|||
|
|
|||
|
if (TokenType == TokenImpersonation) {
|
|||
|
|
|||
|
if ( ClientSecurityQos->ImpersonationLevel > ImpersonationLevel) {
|
|||
|
|
|||
|
PsDereferenceImpersonationToken( Token );
|
|||
|
return STATUS_BAD_IMPERSONATION_LEVEL;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if ( SepBadImpersonationLevel(ImpersonationLevel,ServerIsRemote)) {
|
|||
|
|
|||
|
PsDereferenceImpersonationToken( Token );
|
|||
|
return STATUS_BAD_IMPERSONATION_LEVEL;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// TokenType is TokenImpersonation and the impersonation is legit.
|
|||
|
// Set the DirectAccessEffectiveOnly flag to be the minimum of
|
|||
|
// the current thread value and the caller specified value.
|
|||
|
//
|
|||
|
|
|||
|
ClientContext->DirectAccessEffectiveOnly =
|
|||
|
( (ThreadEffectiveOnly || (ClientSecurityQos->EffectiveOnly)) ?
|
|||
|
TRUE : FALSE );
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// TokenType is TokenPrimary. In this case, the client specified
|
|||
|
// EffectiveOnly value is always used.
|
|||
|
//
|
|||
|
|
|||
|
ClientContext->DirectAccessEffectiveOnly =
|
|||
|
ClientSecurityQos->EffectiveOnly;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Copy the token if necessary (i.e., static tracking requested)
|
|||
|
//
|
|||
|
|
|||
|
if (ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING) {
|
|||
|
|
|||
|
ClientContext->DirectlyAccessClientToken = FALSE;
|
|||
|
|
|||
|
Status = SeCopyClientToken(
|
|||
|
Token,
|
|||
|
ClientSecurityQos->ImpersonationLevel,
|
|||
|
KernelMode,
|
|||
|
&DuplicateToken
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
if ( NT_SUCCESS(Status) ) {
|
|||
|
ObDeleteCapturedInsertInfo(DuplicateToken);
|
|||
|
}
|
|||
|
//
|
|||
|
// No longer need the pointer to the client's token
|
|||
|
//
|
|||
|
|
|||
|
if (TokenType == TokenPrimary) {
|
|||
|
PsDereferencePrimaryToken( Token );
|
|||
|
} else {
|
|||
|
PsDereferenceImpersonationToken( Token );
|
|||
|
}
|
|||
|
|
|||
|
Token = DuplicateToken;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If there was an error, we're done.
|
|||
|
//
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
ClientContext->DirectlyAccessClientToken = TRUE;
|
|||
|
|
|||
|
if (ServerIsRemote) {
|
|||
|
//
|
|||
|
// Get a copy of the client token's control information
|
|||
|
// so that we can tell if it changes in the future.
|
|||
|
//
|
|||
|
|
|||
|
SeGetTokenControlInformation( Token,
|
|||
|
&ClientContext->ClientTokenControl
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
ClientContext->SecurityQos.Length =
|
|||
|
(ULONG)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 = Token;
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
#if SAVE_FOR_PRODUCT_2
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SeUpdateClientSecurity(
|
|||
|
IN PETHREAD ClientThread,
|
|||
|
IN OUT PSECURITY_CLIENT_CONTEXT ClientContext,
|
|||
|
OUT PBOOLEAN ChangesMade,
|
|||
|
OUT PBOOLEAN NewToken
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service is used to update a client security context block
|
|||
|
based upon the client's current security context and the security
|
|||
|
quality of service parameters specified when the security block
|
|||
|
was created. If the SecurityContextTracking specified when the
|
|||
|
context block was created indicated static tracking, then no
|
|||
|
change will be made to the context block. Otherwise, a change may
|
|||
|
be made.
|
|||
|
|
|||
|
|
|||
|
An indication of whether any changes were made is returned to the
|
|||
|
caller. This may be used by communication session layers
|
|||
|
providing remote communications to decide whether or not to send
|
|||
|
an updated security context to the remote server's node. It may
|
|||
|
also be used by a server session layer to decide whether or not to
|
|||
|
inform a server that a previously obtained handle to a token no
|
|||
|
longer represents the current security context.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ClientThread - Points to the client's thread. This is used to
|
|||
|
locate the security context to synchronize with.
|
|||
|
|
|||
|
ClientContext - Points to client security context block to be
|
|||
|
updated.
|
|||
|
|
|||
|
ChangesMade - Receives an indication as to whether any changes to
|
|||
|
the client's security context had been made since the last
|
|||
|
time the security context block was synchronized. This will
|
|||
|
always be FALSE if static security tracking is in effect.
|
|||
|
|
|||
|
NewToken - Receives an indication as to whether the same token
|
|||
|
is used to represent the client's current context, or whether
|
|||
|
the context now points to a new token. If the client's token
|
|||
|
is directly referenced, then this indicates the client changed
|
|||
|
tokens (and the new one is now referenced). If the client's token
|
|||
|
isn't directly referenced, then this indicates it was necessary
|
|||
|
to delete one token and create another one. This will always be
|
|||
|
FALSE if static security tracking is in effect.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - The service completed successfully.
|
|||
|
|
|||
|
STATUS_BAD_IMPERSONATION_LEVEL - The client is currently
|
|||
|
impersonating either an Anonymous or Identification level
|
|||
|
token, which can not be passed on for use by another server.
|
|||
|
This status may also be returned if the security context
|
|||
|
block is for an inter-system communication session and the
|
|||
|
client thread is impersonating a client of its own using
|
|||
|
other than delegation impersonation level.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
PACCESS_TOKEN Token;
|
|||
|
TOKEN_TYPE TokenType;
|
|||
|
BOOLEAN ThreadEffectiveOnly;
|
|||
|
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
|
|||
|
PACCESS_TOKEN DuplicateToken;
|
|||
|
TOKEN_CONTROL TokenControl;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if (ClientContext->SecurityQos.ContextTrackingMode ==
|
|||
|
SECURITY_STATIC_TRACKING) {
|
|||
|
|
|||
|
(*NewToken) = FALSE;
|
|||
|
(*ChangesMade) = FALSE;
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// Optimize for the directly accessed token //
|
|||
|
// //
|
|||
|
//////////////////////////////////////////////
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Gain access to the client thread's effective token
|
|||
|
//
|
|||
|
|
|||
|
Token = PsReferenceEffectiveToken(
|
|||
|
ClientThread,
|
|||
|
&TokenType,
|
|||
|
&ThreadEffectiveOnly,
|
|||
|
&ImpersonationLevel
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// See if the token is the same.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
SeGetTokenControlInformation( Token, &TokenControl );
|
|||
|
|
|||
|
if ( SeSameToken( &TokenControl,
|
|||
|
&ClientContext->ClientTokenControl) ) {
|
|||
|
|
|||
|
(*NewToken = FALSE);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Same token.
|
|||
|
// Is it unmodified?
|
|||
|
//
|
|||
|
|
|||
|
if ( (TokenControl.ModifiedId.HighPart ==
|
|||
|
ClientContext->ClientTokenControl.ModifiedId.HighPart) &&
|
|||
|
(TokenControl.ModifiedId.LowPart ==
|
|||
|
ClientContext->ClientTokenControl.ModifiedId.LowPart) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Yup. No changes necessary.
|
|||
|
//
|
|||
|
|
|||
|
if (TokenType == TokenPrimary) {
|
|||
|
PsDereferencePrimaryToken( Token );
|
|||
|
} else {
|
|||
|
PsDereferenceImpersonationToken( Token );
|
|||
|
}
|
|||
|
|
|||
|
(*ChangesMade) = FALSE;
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Same token, but it has been modified.
|
|||
|
// If we are directly accessing the token, then we can
|
|||
|
// just indicate it has changed and return. Otherwise
|
|||
|
// we have to actually update our copy of the token.
|
|||
|
//
|
|||
|
|
|||
|
(*ChangesMade) = TRUE;
|
|||
|
if (ClientContext->DirectlyAccessClientToken) {
|
|||
|
|
|||
|
if (TokenType == TokenPrimary) {
|
|||
|
PsDereferencePrimaryToken( Token );
|
|||
|
} else {
|
|||
|
PsDereferenceImpersonationToken( Token );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Save the new modified count and whether or not
|
|||
|
// the token is for effective use only
|
|||
|
//
|
|||
|
|
|||
|
ClientContext->ClientTokenControl.ModifiedId =
|
|||
|
TokenControl.ModifiedId;
|
|||
|
ClientContext->DirectAccessEffectiveOnly =
|
|||
|
( (ThreadEffectiveOnly || (ClientContext->SecurityQos.EffectiveOnly)) ?
|
|||
|
TRUE : FALSE );
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// There is a possibility for a fair performance gain here
|
|||
|
// by just updating the existing token to match its origin.
|
|||
|
// However, it isn't clear that this case is ever really
|
|||
|
// used, so the effort and complexity is avoided at this time.
|
|||
|
// If it is found that this case is used, then this code
|
|||
|
// can be added.
|
|||
|
//
|
|||
|
// Instead, we just fall through to the case of completely
|
|||
|
// different tokens below.
|
|||
|
//
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Not the same token, or the same token has changed.
|
|||
|
// In either case, we're going to create a new copy of the token
|
|||
|
// and dump the old copy.
|
|||
|
//
|
|||
|
// Make sure the current impersonation situation is legitimate.
|
|||
|
//
|
|||
|
|
|||
|
(*NewToken) = TRUE;
|
|||
|
(*ChangesMade) = TRUE;
|
|||
|
if (TokenType == TokenImpersonation) {
|
|||
|
if ( SepBadImpersonationLevel(ImpersonationLevel,
|
|||
|
ClientContext->ServerIsRemote)) {
|
|||
|
|
|||
|
PsDereferenceImpersonationToken( Token );
|
|||
|
return STATUS_BAD_IMPERSONATION_LEVEL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Copy the token
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Status = SeCopyClientToken(
|
|||
|
Token,
|
|||
|
ClientContext->SecurityQos.ImpersonationLevel,
|
|||
|
KernelMode,
|
|||
|
&DuplicateToken
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// No longer need the pointer to the client's effective token
|
|||
|
//
|
|||
|
|
|||
|
if (TokenType == TokenPrimary) {
|
|||
|
PsDereferencePrimaryToken( Token );
|
|||
|
} else {
|
|||
|
PsDereferenceImpersonationToken( Token );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If there was an error, we're done.
|
|||
|
//
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Otherwise, replace the current token with the new one.
|
|||
|
//
|
|||
|
|
|||
|
Token = ClientContext->ClientToken;
|
|||
|
ClientContext->ClientToken = DuplicateToken;
|
|||
|
ClientContext->DirectlyAccessClientToken = FALSE;
|
|||
|
|
|||
|
if (SeTokenType( Token ) == TokenPrimary) {
|
|||
|
PsDereferencePrimaryToken( Token );
|
|||
|
} else {
|
|||
|
PsDereferenceImpersonationToken( Token );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Get a copy of the current token's control information
|
|||
|
// so that we can tell if it changes in the future.
|
|||
|
//
|
|||
|
|
|||
|
SeGetTokenControlInformation( DuplicateToken,
|
|||
|
&ClientContext->ClientTokenControl
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
SeImpersonateClient(
|
|||
|
IN PSECURITY_CLIENT_CONTEXT ClientContext,
|
|||
|
IN PETHREAD ServerThread OPTIONAL
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service is used to cause the calling thread to impersonate a
|
|||
|
client. The client security context in ClientContext is assumed to
|
|||
|
be up to date.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ClientContext - Points to client security context block.
|
|||
|
|
|||
|
ServerThread - (Optional) Specifies the thread which is to be made to
|
|||
|
impersonate the client. If not specified, the calling thread is
|
|||
|
used.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
BOOLEAN EffectiveValueToUse;
|
|||
|
PETHREAD Thread;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if (ClientContext->DirectlyAccessClientToken) {
|
|||
|
EffectiveValueToUse = ClientContext->DirectAccessEffectiveOnly;
|
|||
|
} else {
|
|||
|
EffectiveValueToUse = ClientContext->SecurityQos.EffectiveOnly;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// if a ServerThread wasn't specified, then default to the current
|
|||
|
// thread.
|
|||
|
//
|
|||
|
|
|||
|
if (!ARGUMENT_PRESENT(ServerThread)) {
|
|||
|
Thread = PsGetCurrentThread();
|
|||
|
} else {
|
|||
|
Thread = ServerThread;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Assign the context to the calling thread
|
|||
|
//
|
|||
|
|
|||
|
PsImpersonateClient( Thread,
|
|||
|
ClientContext->ClientToken,
|
|||
|
TRUE,
|
|||
|
EffectiveValueToUse,
|
|||
|
ClientContext->SecurityQos.ImpersonationLevel
|
|||
|
);
|
|||
|
|
|||
|
}
|