summaryrefslogtreecommitdiffstats
path: root/private/ntos/se/tokendup.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/se/tokendup.c')
-rw-r--r--private/ntos/se/tokendup.c945
1 files changed, 945 insertions, 0 deletions
diff --git a/private/ntos/se/tokendup.c b/private/ntos/se/tokendup.c
new file mode 100644
index 000000000..d2ba49ae6
--- /dev/null
+++ b/private/ntos/se/tokendup.c
@@ -0,0 +1,945 @@
+/*++
+
+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;
+
+}