946 lines
25 KiB
C
946 lines
25 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
tokendup.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements the token duplication service.
|
|||
|
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Jim Kelly (JimK) 5-April-1990
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode only.
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
//#ifndef TOKEN_DEBUG
|
|||
|
//#define TOKEN_DEBUG
|
|||
|
//#endif
|
|||
|
|
|||
|
#include "sep.h"
|
|||
|
#include "seopaque.h"
|
|||
|
#include "tokenp.h"
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE,NtDuplicateToken)
|
|||
|
#pragma alloc_text(PAGE,SepDuplicateToken)
|
|||
|
#pragma alloc_text(PAGE,SepMakeTokenEffectiveOnly)
|
|||
|
#pragma alloc_text(PAGE,SeCopyClientToken)
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NtDuplicateToken(
|
|||
|
IN HANDLE ExistingTokenHandle,
|
|||
|
IN ACCESS_MASK DesiredAccess,
|
|||
|
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
|||
|
IN BOOLEAN EffectiveOnly,
|
|||
|
IN TOKEN_TYPE TokenType,
|
|||
|
OUT PHANDLE NewTokenHandle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Create a new token that is a duplicate of an existing token.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ExistingTokenHandle - Is a handle to a token already open for
|
|||
|
TOKEN_DUPLICATE access.
|
|||
|
|
|||
|
DesiredAccess - Is an access mask indicating which access types
|
|||
|
are desired to the newly created token. If specified as zero,
|
|||
|
the granted access mask of the existing token handle
|
|||
|
is used as the desired access mask for the new token.
|
|||
|
|
|||
|
ObjectAttributes - Points to the standard object attributes data
|
|||
|
structure. Refer to the NT Object Management
|
|||
|
Specification for a description of this data structure.
|
|||
|
|
|||
|
If the new token type is TokenImpersonation, then this
|
|||
|
parameter may be used to specify the impersonation level
|
|||
|
of the new token. If no value is provided, and the source
|
|||
|
token is an impersonation token, then the impersonation level
|
|||
|
of the source will become that of the target as well. If the
|
|||
|
source token is a primary token, then an impersonation level
|
|||
|
must be explicitly provided.
|
|||
|
|
|||
|
If the token being duplicated is an impersonation token, and
|
|||
|
an impersonation level is explicitly provided for the target,
|
|||
|
then the value provided must not be greater than that of the
|
|||
|
source token. For example, an Identification level token can
|
|||
|
not be duplicated to produce a Delegation level token.
|
|||
|
|
|||
|
EffectiveOnly - Is a boolean flag indicating whether the entire
|
|||
|
source token should be duplicated into the target token or
|
|||
|
just the effective (currently enabled) part of the token.
|
|||
|
This provides a means for a caller of a protected subsystem
|
|||
|
to limit which privileges and optional groups are made
|
|||
|
available to the protected subsystem. A value of TRUE
|
|||
|
indicates only the currently enabled parts of the source
|
|||
|
token are to be duplicated. Otherwise, the entire source
|
|||
|
token is duplicated.
|
|||
|
|
|||
|
TokenType - Indicates which type of object the new object is to
|
|||
|
be created as (primary or impersonation). If you are duplicating
|
|||
|
an Impersonation token to produce a Primary token, then
|
|||
|
the Impersonation token must have an impersonation level of
|
|||
|
either DELEGATE or IMPERSONATE.
|
|||
|
|
|||
|
|
|||
|
NewTokenHandle - Receives the handle of the newly created token.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - Indicates the operation was successful.
|
|||
|
|
|||
|
STATUS_INVALID_PARAMETER - Indicates one or more of the parameter values
|
|||
|
was invalid. This value is returned if the target token is not
|
|||
|
an impersonation token.
|
|||
|
|
|||
|
STATUS_BAD_IMPERSONATION_LEVEL - Indicates the impersonation level
|
|||
|
requested for the duplicate token is not compatible with the
|
|||
|
level of the source token. The duplicate token may not be assigned
|
|||
|
a level greater than that of the source token.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
PTOKEN Token;
|
|||
|
PTOKEN NewToken;
|
|||
|
KPROCESSOR_MODE PreviousMode;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
SECURITY_ADVANCED_QUALITY_OF_SERVICE SecurityQos;
|
|||
|
BOOLEAN SecurityQosPresent = FALSE;
|
|||
|
HANDLE LocalHandle;
|
|||
|
|
|||
|
OBJECT_HANDLE_INFORMATION HandleInformation;
|
|||
|
ACCESS_MASK EffectiveDesiredAccess;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
PreviousMode = KeGetPreviousMode();
|
|||
|
|
|||
|
//
|
|||
|
// Probe parameters
|
|||
|
//
|
|||
|
|
|||
|
if (PreviousMode != KernelMode) {
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the TokenType is valid
|
|||
|
//
|
|||
|
|
|||
|
if ( (TokenType < TokenPrimary) && (TokenType > TokenImpersonation) ) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make sure we can write the handle
|
|||
|
//
|
|||
|
|
|||
|
ProbeForWriteHandle(NewTokenHandle);
|
|||
|
|
|||
|
|
|||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
return GetExceptionCode();
|
|||
|
} // end_try
|
|||
|
|
|||
|
} //end_if
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Status = SeCaptureSecurityQos(
|
|||
|
ObjectAttributes,
|
|||
|
PreviousMode,
|
|||
|
&SecurityQosPresent,
|
|||
|
&SecurityQos
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Check the handle's access to the existing token and get
|
|||
|
// a pointer to that token. Pick up the default desired
|
|||
|
// access mask from the handle while we're at it.
|
|||
|
//
|
|||
|
|
|||
|
Status = ObReferenceObjectByHandle(
|
|||
|
ExistingTokenHandle, // Handle
|
|||
|
TOKEN_DUPLICATE, // DesiredAccess
|
|||
|
SepTokenObjectType, // ObjectType
|
|||
|
PreviousMode, // AccessMode
|
|||
|
(PVOID *)&Token, // Object
|
|||
|
&HandleInformation // GrantedAccess
|
|||
|
);
|
|||
|
|
|||
|
if ( !NT_SUCCESS(Status) ) {
|
|||
|
|
|||
|
if (SecurityQosPresent) {
|
|||
|
SeFreeCapturedSecurityQos( &SecurityQos );
|
|||
|
}
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
#ifdef TOKEN_DEBUG
|
|||
|
////////////////////////////////////////////////////////////////////////////
|
|||
|
//
|
|||
|
// Debug
|
|||
|
SepAcquireTokenReadLock( Token );
|
|||
|
DbgPrint("\n");
|
|||
|
DbgPrint("\n");
|
|||
|
DbgPrint("Token being duplicated: \n");
|
|||
|
SepDumpToken( Token );
|
|||
|
SepReleaseTokenReadLock( Token );
|
|||
|
// Debug
|
|||
|
//
|
|||
|
////////////////////////////////////////////////////////////////////////////
|
|||
|
#endif //TOKEN_DEBUG
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Check to see if an alternate desired access mask was provided.
|
|||
|
//
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(DesiredAccess)) {
|
|||
|
|
|||
|
EffectiveDesiredAccess = DesiredAccess;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
EffectiveDesiredAccess = HandleInformation.GrantedAccess;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If no impersonation level was specified, pick one up from
|
|||
|
// the source token.
|
|||
|
//
|
|||
|
|
|||
|
if ( !SecurityQosPresent ) {
|
|||
|
|
|||
|
SecurityQos.ImpersonationLevel = Token->ImpersonationLevel;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
if (Token->TokenType == TokenImpersonation) {
|
|||
|
|
|||
|
//
|
|||
|
// Make sure a legitimate transformation is being requested:
|
|||
|
//
|
|||
|
// (1) The impersonation level of a target duplicate must not
|
|||
|
// exceed that of the source token.
|
|||
|
//
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( SecurityDelegation > SecurityImpersonation );
|
|||
|
ASSERT( SecurityImpersonation > SecurityIdentification );
|
|||
|
ASSERT( SecurityIdentification > SecurityAnonymous );
|
|||
|
|
|||
|
if ( (SecurityQos.ImpersonationLevel > Token->ImpersonationLevel) ) {
|
|||
|
|
|||
|
ObDereferenceObject( (PVOID)Token );
|
|||
|
if (SecurityQosPresent) {
|
|||
|
SeFreeCapturedSecurityQos( &SecurityQos );
|
|||
|
}
|
|||
|
return STATUS_BAD_IMPERSONATION_LEVEL;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we are producing a Primary token from an impersonation
|
|||
|
// token, then specify an impersonation level of at least
|
|||
|
// Impersonate.
|
|||
|
//
|
|||
|
|
|||
|
if ( (Token->TokenType == TokenImpersonation) &&
|
|||
|
(TokenType == TokenPrimary) &&
|
|||
|
(Token->ImpersonationLevel < SecurityImpersonation)
|
|||
|
) {
|
|||
|
ObDereferenceObject( (PVOID)Token );
|
|||
|
if (SecurityQosPresent) {
|
|||
|
SeFreeCapturedSecurityQos( &SecurityQos );
|
|||
|
}
|
|||
|
return STATUS_BAD_IMPERSONATION_LEVEL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Duplicate the existing token
|
|||
|
//
|
|||
|
|
|||
|
NewToken = NULL;
|
|||
|
Status = SepDuplicateToken(
|
|||
|
Token,
|
|||
|
ObjectAttributes,
|
|||
|
EffectiveOnly,
|
|||
|
TokenType,
|
|||
|
SecurityQos.ImpersonationLevel,
|
|||
|
PreviousMode,
|
|||
|
&NewToken
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
//
|
|||
|
// Insert the new token
|
|||
|
//
|
|||
|
|
|||
|
Status = ObInsertObject( NewToken,
|
|||
|
NULL,
|
|||
|
EffectiveDesiredAccess,
|
|||
|
0,
|
|||
|
(PVOID *)NULL,
|
|||
|
&LocalHandle
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
DbgPrint( "SE: ObInsertObject failed (%x) for token at %x\n", Status, NewToken );
|
|||
|
}
|
|||
|
|
|||
|
} else
|
|||
|
if (NewToken != NULL) {
|
|||
|
DbgPrint( "SE: SepDuplicateToken failed (%x) but allocated token at %x\n", Status, NewToken );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We no longer need our reference to the source token
|
|||
|
//
|
|||
|
|
|||
|
ObDereferenceObject( (PVOID)Token );
|
|||
|
|
|||
|
if (SecurityQosPresent) {
|
|||
|
SeFreeCapturedSecurityQos( &SecurityQos );
|
|||
|
}
|
|||
|
|
|||
|
// BUGWARNING Probably need to audit here
|
|||
|
|
|||
|
//
|
|||
|
// Return the new handle
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
try { *NewTokenHandle = LocalHandle; }
|
|||
|
except(EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); }
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SepDuplicateToken(
|
|||
|
IN PTOKEN ExistingToken,
|
|||
|
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
|||
|
IN BOOLEAN EffectiveOnly,
|
|||
|
IN TOKEN_TYPE TokenType,
|
|||
|
IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel OPTIONAL,
|
|||
|
IN KPROCESSOR_MODE RequestorMode,
|
|||
|
OUT PTOKEN *DuplicateToken
|
|||
|
)
|
|||
|
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine does the bulk of the work to actually duplicate
|
|||
|
a token. This routine assumes all access validation and argument
|
|||
|
probing (except the ObjectAttributes) has been performed.
|
|||
|
|
|||
|
THE CALLER IS RESPONSIBLE FOR CHECKING SUBJECT RIGHTS TO CREATE THE
|
|||
|
TYPE OF TOKEN BEING CREATED.
|
|||
|
|
|||
|
This routine acquires a read lock on the token being duplicated.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ExistingToken - Points to the token to be duplicated.
|
|||
|
|
|||
|
ObjectAttributes - Points to the standard object attributes data
|
|||
|
structure. Refer to the NT Object Management
|
|||
|
Specification for a description of this data structure.
|
|||
|
|
|||
|
The security Quality Of Service of the object attributes are ignored.
|
|||
|
This information must be specified using parameters to this
|
|||
|
routine.
|
|||
|
|
|||
|
EffectiveOnly - Is a boolean flag indicating whether the entire
|
|||
|
source token should be duplicated into the target token or
|
|||
|
just the effective (currently enabled) part of the token.
|
|||
|
This provides a means for a caller of a protected subsystem
|
|||
|
to limit which privileges and optional groups are made
|
|||
|
available to the protected subsystem. A value of TRUE
|
|||
|
indicates only the currently enabled parts of the source
|
|||
|
token are to be duplicated. Otherwise, the entire source
|
|||
|
token is duplicated.
|
|||
|
|
|||
|
TokenType - Indicates the type of token to make the duplicate token.
|
|||
|
|
|||
|
ImpersonationLevel - This value specifies the impersonation level
|
|||
|
to assign to the duplicate token. If the TokenType of the
|
|||
|
duplicate is not TokenImpersonation then this parameter is
|
|||
|
ignored. Otherwise, it is must be provided.
|
|||
|
|
|||
|
RequestorMode - Mode of client requesting the token be duplicated.
|
|||
|
|
|||
|
DuplicateToken - Receives a pointer to the duplicate token.
|
|||
|
The token has not yet been inserted into any object table.
|
|||
|
No exceptions are expected when tring to set this OUT value.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - The service successfully completed the requested
|
|||
|
operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
PTOKEN NewToken;
|
|||
|
PULONG DynamicPart;
|
|||
|
ULONG PagedPoolSize;
|
|||
|
ULONG NonPagedPoolSize;
|
|||
|
ULONG TokenBodyLength;
|
|||
|
ULONG FieldOffset;
|
|||
|
|
|||
|
ULONG Index;
|
|||
|
|
|||
|
PSECURITY_TOKEN_PROXY_DATA NewProxyData;
|
|||
|
PSECURITY_TOKEN_AUDIT_DATA NewAuditData;
|
|||
|
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ASSERT( sizeof(SECURITY_IMPERSONATION_LEVEL) <= sizeof(ULONG) );
|
|||
|
|
|||
|
|
|||
|
if ( TokenType == TokenImpersonation ) {
|
|||
|
|
|||
|
ASSERT( SecurityDelegation > SecurityImpersonation );
|
|||
|
ASSERT( SecurityImpersonation > SecurityIdentification );
|
|||
|
ASSERT( SecurityIdentification > SecurityAnonymous );
|
|||
|
|
|||
|
if ( (ImpersonationLevel > SecurityDelegation) ||
|
|||
|
(ImpersonationLevel < SecurityAnonymous) ) {
|
|||
|
|
|||
|
return STATUS_BAD_IMPERSONATION_LEVEL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Increment the reference count for this logon session
|
|||
|
// This can not fail, since there is already a token in this logon
|
|||
|
// session.
|
|||
|
//
|
|||
|
|
|||
|
Status = SepReferenceLogonSession( &(ExistingToken->AuthenticationId) );
|
|||
|
ASSERT( NT_SUCCESS(Status) );
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Note that the size of the dynamic portion of a token can not change
|
|||
|
// once established.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Allocate the dynamic portion
|
|||
|
//
|
|||
|
|
|||
|
DynamicPart = (PULONG)ExAllocatePoolWithTag(
|
|||
|
PagedPool,
|
|||
|
ExistingToken->DynamicCharged,
|
|||
|
'dTeS'
|
|||
|
);
|
|||
|
|
|||
|
if (DynamicPart == NULL) {
|
|||
|
SepDeReferenceLogonSession( &(ExistingToken->AuthenticationId) );
|
|||
|
return( STATUS_INSUFFICIENT_RESOURCES );
|
|||
|
}
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(ExistingToken->ProxyData)) {
|
|||
|
|
|||
|
Status = SepCopyProxyData(
|
|||
|
&NewProxyData,
|
|||
|
ExistingToken->ProxyData
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
SepDeReferenceLogonSession( &(ExistingToken->AuthenticationId) );
|
|||
|
ExFreePool( DynamicPart );
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
NewProxyData = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT( ExistingToken->AuditData )) {
|
|||
|
|
|||
|
NewAuditData = ExAllocatePool( PagedPool, sizeof( SECURITY_TOKEN_AUDIT_DATA ));
|
|||
|
|
|||
|
if (NewAuditData == NULL) {
|
|||
|
|
|||
|
SepFreeProxyData( NewProxyData );
|
|||
|
SepDeReferenceLogonSession( &(ExistingToken->AuthenticationId) );
|
|||
|
ExFreePool( DynamicPart );
|
|||
|
|
|||
|
return( STATUS_INSUFFICIENT_RESOURCES );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
*NewAuditData = *(ExistingToken->AuditData);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
NewAuditData = NULL;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Create a new object
|
|||
|
//
|
|||
|
|
|||
|
TokenBodyLength = (ULONG)sizeof(TOKEN) +
|
|||
|
ExistingToken->VariableLength;
|
|||
|
|
|||
|
NonPagedPoolSize = TokenBodyLength;
|
|||
|
PagedPoolSize = ExistingToken->DynamicCharged;
|
|||
|
|
|||
|
Status = ObCreateObject(
|
|||
|
RequestorMode, // ProbeMode
|
|||
|
SepTokenObjectType, // ObjectType
|
|||
|
ObjectAttributes, // ObjectAttributes
|
|||
|
RequestorMode, // OwnershipMode
|
|||
|
NULL, // ParseContext
|
|||
|
TokenBodyLength, // ObjectBodySize
|
|||
|
PagedPoolSize, // PagedPoolCharge
|
|||
|
NonPagedPoolSize, // NonPagedPoolCharge
|
|||
|
(PVOID *)&NewToken // Return pointer to object
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
SepDeReferenceLogonSession( &(ExistingToken->AuthenticationId) );
|
|||
|
ExFreePool( DynamicPart );
|
|||
|
SepFreeProxyData( NewProxyData );
|
|||
|
|
|||
|
if (NewAuditData != NULL) {
|
|||
|
ExFreePool( NewAuditData );
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// acquire exclusive access to the source token
|
|||
|
//
|
|||
|
|
|||
|
SepAcquireTokenReadLock( ExistingToken );
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Main Body initialization
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// The following fields are unchanged from the source token.
|
|||
|
// Although some may change if EffectiveOnly has been specified.
|
|||
|
//
|
|||
|
|
|||
|
NewToken->AuthenticationId = ExistingToken->AuthenticationId;
|
|||
|
NewToken->ModifiedId = ExistingToken->ModifiedId;
|
|||
|
NewToken->ExpirationTime = ExistingToken->ExpirationTime;
|
|||
|
NewToken->TokenSource = ExistingToken->TokenSource;
|
|||
|
NewToken->DynamicCharged = ExistingToken->DynamicCharged;
|
|||
|
NewToken->DynamicAvailable = ExistingToken->DynamicAvailable;
|
|||
|
NewToken->DefaultOwnerIndex = ExistingToken->DefaultOwnerIndex;
|
|||
|
NewToken->UserAndGroupCount = ExistingToken->UserAndGroupCount;
|
|||
|
NewToken->PrivilegeCount = ExistingToken->PrivilegeCount;
|
|||
|
NewToken->VariableLength = ExistingToken->VariableLength;
|
|||
|
NewToken->TokenFlags = ExistingToken->TokenFlags;
|
|||
|
NewToken->ProxyData = NewProxyData;
|
|||
|
NewToken->AuditData = NewAuditData;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// The following fields differ in the new token.
|
|||
|
//
|
|||
|
|
|||
|
ExAllocateLocallyUniqueId( &(NewToken->TokenId) );
|
|||
|
NewToken->TokenInUse = FALSE;
|
|||
|
NewToken->TokenType = TokenType;
|
|||
|
NewToken->ImpersonationLevel = ImpersonationLevel;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Copy and initialize the variable part.
|
|||
|
// The variable part is assumed to be position independent.
|
|||
|
//
|
|||
|
|
|||
|
RtlMoveMemory( (PVOID)&(NewToken->VariablePart),
|
|||
|
(PVOID)&(ExistingToken->VariablePart),
|
|||
|
ExistingToken->VariableLength
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Set the address of the UserAndGroups array.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( ARGUMENT_PRESENT(ExistingToken->UserAndGroups ) );
|
|||
|
ASSERT( (ULONG)(ExistingToken->UserAndGroups) >=
|
|||
|
(ULONG)(&(ExistingToken->VariablePart)) );
|
|||
|
|
|||
|
FieldOffset = (ULONG)(ExistingToken->UserAndGroups) -
|
|||
|
(ULONG)(&(ExistingToken->VariablePart));
|
|||
|
|
|||
|
NewToken->UserAndGroups =
|
|||
|
(PSID_AND_ATTRIBUTES)(FieldOffset + (ULONG)(&(NewToken->VariablePart)) );
|
|||
|
|
|||
|
//
|
|||
|
// Now go through and change the address of each SID pointer
|
|||
|
// for the user and groups
|
|||
|
//
|
|||
|
|
|||
|
Index = 0;
|
|||
|
|
|||
|
while (Index < ExistingToken->UserAndGroupCount) {
|
|||
|
|
|||
|
FieldOffset = (ULONG)(ExistingToken->UserAndGroups[Index].Sid) -
|
|||
|
(ULONG)(&(ExistingToken->VariablePart));
|
|||
|
|
|||
|
NewToken->UserAndGroups[Index].Sid =
|
|||
|
(PSID)( FieldOffset + (ULONG)(&(NewToken->VariablePart)) );
|
|||
|
|
|||
|
Index += 1;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If present, set the address of the privileges
|
|||
|
//
|
|||
|
|
|||
|
if (ExistingToken->PrivilegeCount > 0) {
|
|||
|
ASSERT( ARGUMENT_PRESENT(ExistingToken->Privileges ) );
|
|||
|
ASSERT( (ULONG)(ExistingToken->Privileges) >=
|
|||
|
(ULONG)(&(ExistingToken->VariablePart)) );
|
|||
|
|
|||
|
FieldOffset = (ULONG)(ExistingToken->Privileges) -
|
|||
|
(ULONG)(&(ExistingToken->VariablePart));
|
|||
|
NewToken->Privileges = (PLUID_AND_ATTRIBUTES)(
|
|||
|
FieldOffset +
|
|||
|
(ULONG)(&(NewToken->VariablePart))
|
|||
|
);
|
|||
|
} else {
|
|||
|
|
|||
|
NewToken->Privileges = NULL;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Copy and initialize the dynamic part.
|
|||
|
// The dynamic part is assumed to be position independent.
|
|||
|
//
|
|||
|
|
|||
|
RtlMoveMemory( (PVOID)DynamicPart,
|
|||
|
(PVOID)(ExistingToken->DynamicPart),
|
|||
|
ExistingToken->DynamicCharged
|
|||
|
);
|
|||
|
|
|||
|
NewToken->DynamicPart = DynamicPart;
|
|||
|
|
|||
|
//
|
|||
|
// If present, set the address of the default Dacl
|
|||
|
//
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(ExistingToken->DefaultDacl)) {
|
|||
|
|
|||
|
ASSERT( (ULONG)(ExistingToken->DefaultDacl) >=
|
|||
|
(ULONG)(ExistingToken->DynamicPart) );
|
|||
|
|
|||
|
FieldOffset = (ULONG)(ExistingToken->DefaultDacl) -
|
|||
|
(ULONG)(ExistingToken->DynamicPart);
|
|||
|
|
|||
|
NewToken->DefaultDacl = (PACL)(FieldOffset + (ULONG)DynamicPart);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
NewToken->DefaultDacl = NULL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Set the address of the primary group
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(ARGUMENT_PRESENT(ExistingToken->PrimaryGroup));
|
|||
|
|
|||
|
ASSERT( (ULONG)(ExistingToken->PrimaryGroup) >=
|
|||
|
(ULONG)(ExistingToken->DynamicPart) );
|
|||
|
|
|||
|
FieldOffset = (ULONG)(ExistingToken->PrimaryGroup) -
|
|||
|
(ULONG)(ExistingToken->DynamicPart);
|
|||
|
|
|||
|
NewToken->PrimaryGroup = (PACL)(FieldOffset + (ULONG)(DynamicPart));
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// For the time being, take the easy way to generating an "EffectiveOnly"
|
|||
|
// duplicate. That is, use the same space required of the original, just
|
|||
|
// eliminate any IDs or privileges not active.
|
|||
|
//
|
|||
|
// Ultimately, if duplication becomes a common operation, then it will be
|
|||
|
// worthwhile to recalculate the actual space needed and copy only the
|
|||
|
// effective IDs/privileges into the new token.
|
|||
|
//
|
|||
|
|
|||
|
if (EffectiveOnly) {
|
|||
|
SepMakeTokenEffectiveOnly( NewToken );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
#ifdef TOKEN_DEBUG
|
|||
|
////////////////////////////////////////////////////////////////////////////
|
|||
|
//
|
|||
|
// Debug
|
|||
|
DbgPrint("\n");
|
|||
|
DbgPrint("\n");
|
|||
|
DbgPrint("\n");
|
|||
|
DbgPrint("Duplicate token:\n");
|
|||
|
SepDumpToken( NewToken );
|
|||
|
// Debug
|
|||
|
//
|
|||
|
////////////////////////////////////////////////////////////////////////////
|
|||
|
#endif //TOKEN_DEBUG
|
|||
|
|
|||
|
//
|
|||
|
// Release the source token.
|
|||
|
//
|
|||
|
|
|||
|
SepReleaseTokenReadLock( ExistingToken );
|
|||
|
|
|||
|
|
|||
|
(*DuplicateToken) = NewToken;
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
SepMakeTokenEffectiveOnly(
|
|||
|
IN PTOKEN Token
|
|||
|
)
|
|||
|
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine eliminates all but the effective groups and privileges from
|
|||
|
a token. It does this by moving elements of the SID and privileges arrays
|
|||
|
to overwrite lapsed IDs/privileges, and then reducing the array element
|
|||
|
counts. This results in wasted memory within the token object.
|
|||
|
|
|||
|
One side effect of this routine is that a token that initially had a
|
|||
|
default owner ID corresponding to a lapsed group will be changed so
|
|||
|
that the default owner ID is the user ID.
|
|||
|
|
|||
|
THIS ROUTINE MUST BE CALLED ONLY AS PART OF TOKEN CREATION (FOR TOKENS
|
|||
|
WHICH HAVE NOT YET BEEN INSERTED INTO AN OBJECT TABLE.) THIS ROUTINE
|
|||
|
MODIFIES READ ONLY TOKEN FIELDS.
|
|||
|
|
|||
|
Note that since we are operating on a token that is not yet visible
|
|||
|
to the user, we do not bother acquiring a read lock on the token
|
|||
|
being modified.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Token - Points to the token to be made effective only.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
ULONG Index;
|
|||
|
ULONG ElementCount;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Walk the privilege array, discarding any lapsed privileges
|
|||
|
//
|
|||
|
|
|||
|
ElementCount = Token->PrivilegeCount;
|
|||
|
Index = 0;
|
|||
|
|
|||
|
while (Index < ElementCount) {
|
|||
|
|
|||
|
//
|
|||
|
// If this privilege is not enabled, replace it with the one at
|
|||
|
// the end of the array and reduce the size of the array by one.
|
|||
|
// Otherwise, move on to the next entry in the array.
|
|||
|
//
|
|||
|
|
|||
|
if ( !(SepTokenPrivilegeAttributes(Token,Index) & SE_PRIVILEGE_ENABLED)
|
|||
|
) {
|
|||
|
|
|||
|
(Token->Privileges)[Index] =
|
|||
|
(Token->Privileges)[ElementCount - 1];
|
|||
|
ElementCount -= 1;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
Index += 1;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} // endwhile
|
|||
|
|
|||
|
Token->PrivilegeCount = ElementCount;
|
|||
|
|
|||
|
//
|
|||
|
// Walk the UserAndGroups array (except for the first entry, which is
|
|||
|
// the user - and can't be disabled) discarding any lapsed groups.
|
|||
|
//
|
|||
|
|
|||
|
ElementCount = Token->UserAndGroupCount;
|
|||
|
ASSERT( ElementCount >= 1 ); // Must be at least a user ID
|
|||
|
Index = 1; // Start at the first group, not the user ID.
|
|||
|
|
|||
|
while (Index < ElementCount) {
|
|||
|
|
|||
|
//
|
|||
|
// If this group is not enabled, replace it with the one at
|
|||
|
// the end of the array and reduce the size of the array by one.
|
|||
|
//
|
|||
|
if ( !(SepTokenGroupAttributes(Token, Index) & SE_GROUP_ENABLED) ){
|
|||
|
|
|||
|
(Token->UserAndGroups)[Index] =
|
|||
|
(Token->UserAndGroups)[ElementCount - 1];
|
|||
|
ElementCount -= 1;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
Index += 1;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} // endwhile
|
|||
|
|
|||
|
Token->UserAndGroupCount = ElementCount;
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SeCopyClientToken(
|
|||
|
IN PACCESS_TOKEN ClientToken,
|
|||
|
IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
|
|||
|
IN KPROCESSOR_MODE RequestorMode,
|
|||
|
OUT PACCESS_TOKEN *DuplicateToken
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine copies a client's token as part of establishing a client
|
|||
|
context for impersonation.
|
|||
|
|
|||
|
The result will be an impersonation token.
|
|||
|
|
|||
|
No handles to the new token are established.
|
|||
|
|
|||
|
The token will be an exact duplicate of the source token. It is the
|
|||
|
caller's responsibility to ensure an effective only copy of the token
|
|||
|
is produced when the token is opened, if necessary.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ClientToken - Points to the token to be duplicated. This may be either
|
|||
|
a primary or impersonation token.
|
|||
|
|
|||
|
ImpersonationLevel - The impersonation level to be assigned to the new
|
|||
|
token.
|
|||
|
|
|||
|
RequestorMode - Mode to be assigned as the owner mode of the new token.
|
|||
|
|
|||
|
DuplicateToken - Receives a pointer to the duplicate token.
|
|||
|
The token has not yet been inserted into any object table.
|
|||
|
No exceptions are expected when tring to set this OUT value.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - The service successfully completed the requested
|
|||
|
operation.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|||
|
PTOKEN NewToken;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
InitializeObjectAttributes(
|
|||
|
&ObjectAttributes,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
Status = SepDuplicateToken(
|
|||
|
(PTOKEN)ClientToken, // ExistingToken
|
|||
|
&ObjectAttributes, // ObjectAttributes
|
|||
|
FALSE, // EffectiveOnly
|
|||
|
TokenImpersonation, // TokenType (target)
|
|||
|
ImpersonationLevel, // ImpersonationLevel
|
|||
|
RequestorMode, // RequestorMode
|
|||
|
&NewToken // DuplicateToken
|
|||
|
);
|
|||
|
|
|||
|
(*DuplicateToken) = (PACCESS_TOKEN)NewToken;
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|