mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2385 lines
65 KiB
2385 lines
65 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
token.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the initialization, open, duplicate and other
|
|
services of the executive token object.
|
|
|
|
Author:
|
|
|
|
Jim Kelly (JimK) 5-April-1990
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Revision History:
|
|
|
|
v15: robertre
|
|
updated ACL_REVISION
|
|
|
|
--*/
|
|
|
|
|
|
#include "sep.h"
|
|
#include <zwapi.h>
|
|
#include "tokenp.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT,SepTokenInitialization)
|
|
#pragma alloc_text(INIT,SeMakeSystemToken)
|
|
#pragma alloc_text(PAGE,SeTokenType)
|
|
#pragma alloc_text(PAGE,SepCreateToken)
|
|
#pragma alloc_text(PAGE,SeTokenImpersonationLevel)
|
|
#pragma alloc_text(PAGE,SeAssignPrimaryToken)
|
|
#pragma alloc_text(PAGE,SeDeassignPrimaryToken)
|
|
#pragma alloc_text(PAGE,SeExchangePrimaryToken)
|
|
#pragma alloc_text(PAGE,SeGetTokenControlInformation)
|
|
#pragma alloc_text(PAGE,SeSubProcessToken)
|
|
#pragma alloc_text(PAGE,NtCreateToken)
|
|
#pragma alloc_text(PAGE,SepTokenDeleteMethod)
|
|
#pragma alloc_text(PAGE,SepIdAssignableAsOwner)
|
|
#endif
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Global Variables //
|
|
// //
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Generic mapping of access types
|
|
//
|
|
|
|
GENERIC_MAPPING SepTokenMapping = { TOKEN_READ,
|
|
TOKEN_WRITE,
|
|
TOKEN_EXECUTE,
|
|
TOKEN_ALL_ACCESS
|
|
};
|
|
|
|
//
|
|
// Address of token object type descriptor.
|
|
//
|
|
|
|
POBJECT_TYPE SepTokenObjectType;
|
|
|
|
|
|
//
|
|
// Used to track whether or not a system token has been created or not.
|
|
//
|
|
|
|
#if DBG
|
|
BOOLEAN SystemTokenCreated = FALSE;
|
|
#endif //DBG
|
|
|
|
|
|
//
|
|
// Token lock
|
|
//
|
|
|
|
ERESOURCE SepTokenLock;
|
|
|
|
|
|
|
|
|
|
//
|
|
// Used to control the active token diagnostic support provided
|
|
//
|
|
|
|
#ifdef TOKEN_DIAGNOSTICS_ENABLED
|
|
ULONG TokenGlobalFlag = 0;
|
|
#endif // TOKEN_DIAGNOSTICS_ENABLED
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Token Object Routines & Methods //
|
|
// //
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
TOKEN_TYPE
|
|
SeTokenType(
|
|
IN PACCESS_TOKEN Token
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the type of an instance of a token (TokenPrimary,
|
|
or TokenImpersonation).
|
|
|
|
|
|
Arguments:
|
|
|
|
Token - Points to the token whose type is to be returned.
|
|
|
|
Return Value:
|
|
|
|
The token's type.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
return (((PTOKEN)Token)->TokenType);
|
|
}
|
|
|
|
|
|
|
|
SECURITY_IMPERSONATION_LEVEL
|
|
SeTokenImpersonationLevel(
|
|
IN PACCESS_TOKEN Token
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the impersonation level of a token. The token
|
|
is assumed to be a TokenImpersonation type token.
|
|
|
|
|
|
Arguments:
|
|
|
|
Token - Points to the token whose impersonation level is to be returned.
|
|
|
|
Return Value:
|
|
|
|
The token's impersonation level.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
return ((PTOKEN)Token)->ImpersonationLevel;
|
|
}
|
|
|
|
|
|
VOID
|
|
SeAssignPrimaryToken(
|
|
IN PEPROCESS Process,
|
|
IN PACCESS_TOKEN Token
|
|
)
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function establishes a primary token for a process.
|
|
|
|
Arguments:
|
|
|
|
Token - Points to the new primary token.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS
|
|
Status;
|
|
|
|
PTOKEN
|
|
NewToken = (PTOKEN)Token;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(NewToken->TokenType == TokenPrimary);
|
|
ASSERT( !NewToken->TokenInUse );
|
|
|
|
|
|
//
|
|
// Dereference the old token if there is one.
|
|
//
|
|
// Processes typically already have a token that must be
|
|
// dereferenced. There are two cases where this may not
|
|
// be the situation. First, during phase 0 system initialization,
|
|
// the initial system process starts out without a token. Second,
|
|
// if an error occurs during process creation, we may be cleaning
|
|
// up a process that hasn't yet had a primary token assigned.
|
|
//
|
|
|
|
if (Process->Token != NULL) {
|
|
SeDeassignPrimaryToken( Process );
|
|
}
|
|
|
|
|
|
Process->Token=Token;
|
|
NewToken->TokenInUse = TRUE;
|
|
ObReferenceObject(NewToken);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
SeDeassignPrimaryToken(
|
|
IN PEPROCESS Process
|
|
)
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function causes a process reference to a token to be
|
|
dropped.
|
|
|
|
Arguments:
|
|
|
|
Process - Points to the process whose primary token is no longer needed.
|
|
This is probably only the case at process deletion or when
|
|
a primary token is being replaced.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PTOKEN
|
|
OldToken = (PTOKEN)(Process->Token);
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(OldToken->TokenType == TokenPrimary);
|
|
ASSERT(OldToken->TokenInUse);
|
|
|
|
OldToken->TokenInUse = FALSE;
|
|
ObDereferenceObject( OldToken );
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
SeExchangePrimaryToken(
|
|
IN PEPROCESS Process,
|
|
IN PACCESS_TOKEN NewAccessToken,
|
|
OUT PACCESS_TOKEN *OldAccessToken
|
|
)
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to perform the portions of changing a primary
|
|
token that reference the internals of token structures.
|
|
|
|
The new token is checked to make sure it is not already in use.
|
|
|
|
The
|
|
|
|
|
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
!!!!!!!! WARNING WARNING WARNING !!!!!!!!
|
|
!!!!!!!! !!!!!!!!
|
|
!!!!!!!! THIS ROUTINE MUST BE CALLED WITH THE GOBAL !!!!!!!!
|
|
!!!!!!!! PROCESS SECURITY FIELDS LOCK HELD !!!!!!!!
|
|
!!!!!!!! !!!!!!!!
|
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
|
|
Arguments:
|
|
|
|
Process - Points to the process whose primary token is being exchanged.
|
|
|
|
NewAccessToken - Points to the process's new primary token.
|
|
|
|
OldAccessToken - Receives a pointer to the process's current token.
|
|
The caller is responsible for dereferencing this token when
|
|
it is no longer needed. This can't be done while the process
|
|
security locks are held.
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - Everything has been updated.
|
|
|
|
STATUS_TOKEN_ALREADY_IN_USE - A primary token can only be used by a
|
|
single process. That is, each process must have its own primary
|
|
token. The token passed to be assigned as the primary token is
|
|
already in use as a primary token.
|
|
|
|
STATUS_BAD_TOKEN_TYPE - The new token is not a primary token.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS
|
|
Status;
|
|
|
|
PTOKEN
|
|
OldToken = (PTOKEN)(Process->Token);
|
|
|
|
PTOKEN
|
|
NewToken = (PTOKEN)NewAccessToken;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
//
|
|
// We need to access the security fields of both tokens.
|
|
// Access to these fields is guarded by the global Process Security
|
|
// fields lock.
|
|
//
|
|
|
|
ASSERT(OldToken->TokenType == TokenPrimary);
|
|
ASSERT(OldToken->TokenInUse);
|
|
|
|
|
|
//
|
|
// Make sure the new token is a primary token...
|
|
//
|
|
|
|
if ( NewToken->TokenType != TokenPrimary ) {
|
|
return(STATUS_BAD_TOKEN_TYPE);
|
|
}
|
|
|
|
//
|
|
// and that it is not already in use...
|
|
//
|
|
|
|
if (NewToken->TokenInUse) {
|
|
return(STATUS_TOKEN_ALREADY_IN_USE);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// Switch the tokens
|
|
//
|
|
|
|
Process->Token=NewAccessToken;
|
|
NewToken->TokenInUse = TRUE;
|
|
ObReferenceObject(NewToken);
|
|
|
|
//
|
|
// Mark the token as "NOT USED"
|
|
//
|
|
|
|
OldToken->TokenInUse = FALSE;
|
|
|
|
//
|
|
// Return the pointer to the old token. The caller
|
|
// is responsible for dereferencing it if they don't need it.
|
|
//
|
|
|
|
(*OldAccessToken) = OldToken;
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
SeGetTokenControlInformation (
|
|
IN PACCESS_TOKEN Token,
|
|
OUT PTOKEN_CONTROL TokenControl
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is provided for communication session layers, or
|
|
any other executive component that needs to keep track of
|
|
whether a caller's security context has changed between calls.
|
|
Communication session layers will need to check this, for some
|
|
security quality of service modes, to determine whether or not
|
|
a server's security context needs to be updated to reflect
|
|
changes in the client's security context.
|
|
|
|
This routine will also be useful to communications subsystems
|
|
that need to retrieve client' authentication information from
|
|
the local security authority in order to perform a remote
|
|
authentication.
|
|
|
|
|
|
Parameters:
|
|
|
|
Token - Points to the token whose information is to be retrieved.
|
|
|
|
TokenControl - Points to the buffer to receive the token control
|
|
information.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// acquire exclusive access to the token
|
|
//
|
|
|
|
SepAcquireTokenReadLock( (PTOKEN)Token );
|
|
|
|
//
|
|
// Grab the data and run
|
|
//
|
|
|
|
TokenControl->TokenId = ((TOKEN *)Token)->TokenId;
|
|
TokenControl->AuthenticationId = ((TOKEN *)Token)->AuthenticationId;
|
|
TokenControl->ModifiedId = ((TOKEN *)Token)->ModifiedId;
|
|
TokenControl->TokenSource = ((TOKEN *)Token)->TokenSource;
|
|
|
|
SepReleaseTokenReadLock( (PTOKEN)Token );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
PACCESS_TOKEN
|
|
SeMakeSystemToken ()
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is provided for use by executive components
|
|
DURING SYSTEM INITIALIZATION ONLY. It creates a token for
|
|
use by system components.
|
|
|
|
A system token has the following characteristics:
|
|
|
|
- It has LOCAL_SYSTEM as its user ID
|
|
|
|
- It has the following groups with the corresponding
|
|
attributes:
|
|
|
|
ADMINS_ALIAS EnabledByDefault |
|
|
Enabled |
|
|
Owner
|
|
|
|
WORLD EnabledByDefault |
|
|
Enabled |
|
|
Mandatory
|
|
|
|
ADMINISTRATORS (alias) Owner (disabled)
|
|
|
|
|
|
- It has LOCAL_SYSTEM as its primary group.
|
|
|
|
- It has the privileges shown in comments below.
|
|
|
|
|
|
- It has protection that provides TOKEN_ALL_ACCESS to
|
|
the LOCAL_SYSTEM ID.
|
|
|
|
|
|
- It has a default ACL that grants GENERIC_ALL access
|
|
to LOCAL_SYSTEM and GENERIC_EXECUTE to WORLD.
|
|
|
|
|
|
Parameters:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Pointer to a system token.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PVOID Token;
|
|
|
|
SID_AND_ATTRIBUTES UserId;
|
|
TOKEN_PRIMARY_GROUP PrimaryGroup;
|
|
PSID_AND_ATTRIBUTES GroupIds;
|
|
LUID_AND_ATTRIBUTES Privileges[30];
|
|
PACL TokenAcl;
|
|
PSID Owner;
|
|
ULONG NormalGroupAttributes;
|
|
ULONG OwnerGroupAttributes;
|
|
ULONG Length;
|
|
OBJECT_ATTRIBUTES TokenObjectAttributes;
|
|
PSECURITY_DESCRIPTOR TokenSecurityDescriptor;
|
|
ULONG BufferLength;
|
|
PVOID Buffer;
|
|
|
|
ULONG GroupIdsBuffer[128];
|
|
|
|
TIME_FIELDS TimeFields;
|
|
LARGE_INTEGER NoExpiration;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
//
|
|
// Make sure only one system token gets created.
|
|
//
|
|
|
|
#if DBG
|
|
ASSERT( !SystemTokenCreated );
|
|
SystemTokenCreated = TRUE;
|
|
#endif //DBG
|
|
|
|
|
|
//
|
|
// Set up expiration times
|
|
//
|
|
|
|
TimeFields.Year = 3000;
|
|
TimeFields.Month = 1;
|
|
TimeFields.Day = 1;
|
|
TimeFields.Hour = 1;
|
|
TimeFields.Minute = 1;
|
|
TimeFields.Second = 1;
|
|
TimeFields.Milliseconds = 1;
|
|
TimeFields.Weekday = 1;
|
|
|
|
RtlTimeFieldsToTime( &TimeFields, &NoExpiration );
|
|
|
|
|
|
// //
|
|
// // The amount of memory used in the following is gross overkill, but
|
|
// // it is freed up immediately after creating the token.
|
|
// //
|
|
//
|
|
// GroupIds = (PSID_AND_ATTRIBUTES)ExAllocatePool( NonPagedPool, 512 );
|
|
|
|
GroupIds = (PSID_AND_ATTRIBUTES)GroupIdsBuffer;
|
|
|
|
|
|
//
|
|
// Set up the attributes to be assigned to groups
|
|
//
|
|
|
|
NormalGroupAttributes = (SE_GROUP_MANDATORY |
|
|
SE_GROUP_ENABLED_BY_DEFAULT |
|
|
SE_GROUP_ENABLED
|
|
);
|
|
|
|
OwnerGroupAttributes = (SE_GROUP_ENABLED_BY_DEFAULT |
|
|
SE_GROUP_ENABLED |
|
|
SE_GROUP_OWNER
|
|
);
|
|
|
|
//
|
|
// Set up the user ID
|
|
//
|
|
|
|
UserId.Sid = SeLocalSystemSid;
|
|
UserId.Attributes = 0;
|
|
|
|
//
|
|
// Set up the groups
|
|
//
|
|
|
|
|
|
GroupIds->Sid = SeAliasAdminsSid;
|
|
(GroupIds+1)->Sid = SeWorldSid;
|
|
|
|
GroupIds->Attributes = OwnerGroupAttributes;
|
|
(GroupIds+1)->Attributes = NormalGroupAttributes;
|
|
|
|
ASSERT( (RtlLengthSid(GroupIds->Sid) +
|
|
RtlLengthSid((GroupIds+1)->Sid) +
|
|
2*sizeof(ULONG)) <= 512
|
|
);
|
|
|
|
|
|
//
|
|
// Privileges
|
|
//
|
|
|
|
//
|
|
// The privileges in the system token are as follows:
|
|
//
|
|
// Privilege Name Attributes
|
|
// -------------- ----------
|
|
//
|
|
// SeTcbPrivilege enabled/enabled by default
|
|
// SeCreateTokenPrivilege DISabled/NOT enabled by default
|
|
// SeTakeOwnershipPrivilege DISabled/NOT enabled by default
|
|
// SeCreatePagefilePrivilege enabled/enabled by default
|
|
// SeLockMemoryPrivilege enabled/enabled by default
|
|
// SeAssignPrimaryTokenPrivilege DISabled/NOT enabled by default
|
|
// SeIncreaseQuotaPrivilege DISabled/NOT enabled by default
|
|
// SeIncreaseBasePriorityPrivilege enabled/enabled by default
|
|
// SeCreatePermanentPrivilege enabled/enabled by default
|
|
// SeDebugPrivilege enabled/enabled by default
|
|
// SeAuditPrivilege enabled/enabled by default
|
|
// SeSecurityPrivilege DISabled/NOT enabled by default
|
|
// SeSystemEnvironmentPrivilege DISabled/NOT enabled by default
|
|
// SeChangeNotifyPrivilege enabled/enabled by default
|
|
// SeBackupPrivilege DISabled/NOT enabled by default
|
|
// SeRestorePrivilege DISabled/NOT enabled by default
|
|
// SeShutdownPrivilege DISabled/NOT enabled by default
|
|
// SeLoadDriverPrivilege DISabled/NOT enabled by default
|
|
// SeProfileSingleProcessPrivilege enabled/enabled by default
|
|
// SeSystemtimePrivilege DISabled/NOT enabled by default
|
|
//
|
|
|
|
Privileges[0].Luid = SeTcbPrivilege;
|
|
Privileges[0].Attributes =
|
|
(SE_PRIVILEGE_ENABLED_BY_DEFAULT | // Enabled by default
|
|
SE_PRIVILEGE_ENABLED); // Enabled
|
|
|
|
Privileges[1].Luid = SeCreateTokenPrivilege;
|
|
Privileges[1].Attributes = 0; // Only the LSA should enable this.
|
|
|
|
Privileges[2].Luid = SeTakeOwnershipPrivilege;
|
|
Privileges[2].Attributes = 0;
|
|
|
|
Privileges[3].Luid = SeCreatePagefilePrivilege;
|
|
Privileges[3].Attributes =
|
|
(SE_PRIVILEGE_ENABLED_BY_DEFAULT | // Enabled by default
|
|
SE_PRIVILEGE_ENABLED); // Enabled
|
|
|
|
Privileges[4].Luid = SeLockMemoryPrivilege;
|
|
Privileges[4].Attributes =
|
|
(SE_PRIVILEGE_ENABLED_BY_DEFAULT | // Enabled by default
|
|
SE_PRIVILEGE_ENABLED); // Enabled
|
|
|
|
Privileges[5].Luid = SeAssignPrimaryTokenPrivilege;
|
|
Privileges[5].Attributes = 0; // disabled, not enabled by default
|
|
|
|
Privileges[6].Luid = SeIncreaseQuotaPrivilege;
|
|
Privileges[6].Attributes = 0; // disabled, not enabled by default
|
|
|
|
Privileges[7].Luid = SeIncreaseBasePriorityPrivilege;
|
|
Privileges[7].Attributes =
|
|
(SE_PRIVILEGE_ENABLED_BY_DEFAULT | // Enabled by default
|
|
SE_PRIVILEGE_ENABLED); // Enabled
|
|
|
|
Privileges[8].Luid = SeCreatePermanentPrivilege;
|
|
Privileges[8].Attributes =
|
|
(SE_PRIVILEGE_ENABLED_BY_DEFAULT | // Enabled by default
|
|
SE_PRIVILEGE_ENABLED); // Enabled
|
|
|
|
Privileges[9].Luid = SeDebugPrivilege;
|
|
Privileges[9].Attributes =
|
|
(SE_PRIVILEGE_ENABLED_BY_DEFAULT | // Enabled by default
|
|
SE_PRIVILEGE_ENABLED); // Enabled
|
|
|
|
Privileges[10].Luid = SeAuditPrivilege;
|
|
Privileges[10].Attributes =
|
|
(SE_PRIVILEGE_ENABLED_BY_DEFAULT | // Enabled by default
|
|
SE_PRIVILEGE_ENABLED); // Enabled
|
|
|
|
Privileges[11].Luid = SeSecurityPrivilege;
|
|
Privileges[11].Attributes = 0; // disabled, not enabled by default
|
|
|
|
Privileges[12].Luid = SeSystemEnvironmentPrivilege;
|
|
Privileges[12].Attributes = 0; // disabled, not enabled by default
|
|
|
|
Privileges[13].Luid = SeChangeNotifyPrivilege;
|
|
Privileges[13].Attributes =
|
|
(SE_PRIVILEGE_ENABLED_BY_DEFAULT | // Enabled by default
|
|
SE_PRIVILEGE_ENABLED); // Enabled
|
|
|
|
|
|
Privileges[14].Luid = SeBackupPrivilege;
|
|
Privileges[14].Attributes = 0; // disabled, not enabled by default
|
|
|
|
Privileges[15].Luid = SeRestorePrivilege;
|
|
Privileges[15].Attributes = 0; // disabled, not enabled by default
|
|
|
|
Privileges[16].Luid = SeShutdownPrivilege;
|
|
Privileges[16].Attributes = 0; // disabled, not enabled by default
|
|
|
|
Privileges[17].Luid = SeLoadDriverPrivilege;
|
|
Privileges[17].Attributes = 0; // disabled, not enabled by default
|
|
|
|
Privileges[18].Luid = SeProfileSingleProcessPrivilege;
|
|
Privileges[18].Attributes =
|
|
(SE_PRIVILEGE_ENABLED_BY_DEFAULT | // Enabled by default
|
|
SE_PRIVILEGE_ENABLED); // Enabled
|
|
|
|
Privileges[19].Luid = SeSystemtimePrivilege;
|
|
Privileges[19].Attributes = 0; // disabled, not enabled by default
|
|
|
|
//BEFORE ADDING ANOTHER PRIVILEGE ^^ HERE ^^ CHECK THE ARRAY BOUND
|
|
//ALSO INCREMENT THE PRIVILEGE COUNT IN THE SepCreateToken() call
|
|
|
|
|
|
//
|
|
// Establish the primary group and default owner
|
|
//
|
|
|
|
PrimaryGroup.PrimaryGroup = SeLocalSystemSid; // Primary group
|
|
Owner = SeAliasAdminsSid; // Default owner
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// Set up an ACL to protect token as well ...
|
|
// give system full reign of terror. This includes user-mode components
|
|
// running as part of the system.
|
|
//
|
|
|
|
Length = (ULONG)sizeof(ACL) +
|
|
(ULONG)sizeof(ACCESS_ALLOWED_ACE) +
|
|
SeLengthSid( SeLocalSystemSid ) +
|
|
8; // The 8 is just for good measure
|
|
ASSERT( Length < 200 );
|
|
|
|
TokenAcl = (PACL)ExAllocatePoolWithTag(PagedPool, 200, 'cAeS');
|
|
|
|
Status = RtlCreateAcl( TokenAcl, Length, ACL_REVISION2);
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
|
|
Status = RtlAddAccessAllowedAce (
|
|
TokenAcl,
|
|
ACL_REVISION2,
|
|
TOKEN_ALL_ACCESS,
|
|
SeLocalSystemSid
|
|
);
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
|
|
TokenSecurityDescriptor =
|
|
(PSECURITY_DESCRIPTOR)ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
SECURITY_DESCRIPTOR_MIN_LENGTH,
|
|
'dSeS'
|
|
);
|
|
|
|
|
|
Status = RtlCreateSecurityDescriptor(
|
|
TokenSecurityDescriptor,
|
|
SECURITY_DESCRIPTOR_REVISION
|
|
);
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
|
|
Status = RtlSetDaclSecurityDescriptor (
|
|
TokenSecurityDescriptor,
|
|
TRUE,
|
|
TokenAcl,
|
|
FALSE
|
|
);
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
|
|
|
|
Status = RtlSetOwnerSecurityDescriptor (
|
|
TokenSecurityDescriptor,
|
|
SeAliasAdminsSid,
|
|
FALSE // Owner defaulted
|
|
);
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
|
|
Status = RtlSetGroupSecurityDescriptor (
|
|
TokenSecurityDescriptor,
|
|
SeAliasAdminsSid,
|
|
FALSE // Group defaulted
|
|
);
|
|
ASSERT( NT_SUCCESS(Status) );
|
|
|
|
|
|
//
|
|
// Create the system token
|
|
//
|
|
|
|
#ifdef TOKEN_DEBUG
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Debug
|
|
DbgPrint("\n Creating system token...\n");
|
|
// Debug
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
#endif //TOKEN_DEBUG
|
|
|
|
InitializeObjectAttributes(
|
|
&TokenObjectAttributes,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
TokenSecurityDescriptor
|
|
);
|
|
|
|
|
|
|
|
ASSERT(SeSystemDefaultDacl != NULL);
|
|
Status = SepCreateToken(
|
|
(PHANDLE)&Token,
|
|
KernelMode,
|
|
0, // No handle created for system token
|
|
&TokenObjectAttributes,
|
|
TokenPrimary,
|
|
(SECURITY_IMPERSONATION_LEVEL)NULL,
|
|
&SeSystemAuthenticationId,
|
|
&NoExpiration,
|
|
&UserId,
|
|
2, // GroupCount
|
|
GroupIds,
|
|
512, // see call to ExAllocatePool above
|
|
20, // privileges
|
|
Privileges,
|
|
sizeof(Privileges),
|
|
Owner,
|
|
PrimaryGroup.PrimaryGroup,
|
|
SeSystemDefaultDacl,
|
|
&SeSystemTokenSource,
|
|
TRUE, // System token
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
//
|
|
// Assign the security descriptor here, since we don't do it
|
|
// in SepCreateToken for the System Token.
|
|
//
|
|
|
|
BufferLength = Length +
|
|
sizeof(SECURITY_DESCRIPTOR) +
|
|
2 * SeLengthSid(SeAliasAdminsSid);
|
|
|
|
Buffer = (PSECURITY_DESCRIPTOR)ExAllocatePoolWithTag( PagedPool,
|
|
BufferLength,
|
|
'dSeS'
|
|
);
|
|
|
|
Status = RtlAbsoluteToSelfRelativeSD( TokenSecurityDescriptor,
|
|
Buffer,
|
|
&BufferLength
|
|
);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
Status = ObAssignObjectSecurityDescriptor( Token,
|
|
Buffer,
|
|
PagedPool
|
|
);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
//
|
|
// We can free the old one now.
|
|
//
|
|
|
|
ExFreePool( TokenAcl );
|
|
ExFreePool( TokenSecurityDescriptor );
|
|
|
|
return (PACCESS_TOKEN)Token;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
SeSubProcessToken (
|
|
IN PEPROCESS ParentProcess,
|
|
OUT PACCESS_TOKEN *ChildToken
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine makes a token for a sub-process that is a duplicate
|
|
of the parent process's token.
|
|
|
|
|
|
|
|
Parameters:
|
|
|
|
ParentProcess - Pointer to the parent process object. This is used
|
|
to locate the parent process's primary token, and for logging
|
|
purposes.
|
|
|
|
ChildToken - Receives a pointer to the child process's token.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - Indicates the sub-process's token has been created
|
|
successfully.
|
|
|
|
Other status values may be returned from memory allocation or object
|
|
creation services used and typically indicate insufficient resources
|
|
or quota on the requestor's part.
|
|
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// NOTE: THIS ROUTINE CAN BE MADE MUCH MORE EFFICIENT.
|
|
// IT IS DONE IN A BRUTE FORCE FASHION FOR THE LARGE_INTEGER
|
|
// BEING TO GET THINGS UP AND RUNNING.
|
|
//
|
|
// THE PERFORMANCE OF THIS ROUTINE DIRECTLY IMPACTS
|
|
// THE PERFORMANCE OF SUB-PROCESS CREATION.
|
|
//
|
|
|
|
|
|
KPROCESSOR_MODE PreviousMode;
|
|
PTOKEN ParentToken;
|
|
PTOKEN NewToken;
|
|
HANDLE NewTokenHandle;
|
|
OBJECT_ATTRIBUTES PrimaryTokenAttributes;
|
|
|
|
NTSTATUS Status;
|
|
NTSTATUS IgnoreStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
PreviousMode = KeGetPreviousMode();
|
|
|
|
|
|
|
|
InitializeObjectAttributes(
|
|
&PrimaryTokenAttributes,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
|
|
#ifdef TOKEN_DEBUG
|
|
DbgPrint("\nCreating sub-process token...\n");
|
|
DbgPrint("Parent token address = 0x%lx\n", ParentProcess->Token);
|
|
#endif //TOKEN_DEBUG
|
|
|
|
|
|
ParentToken = (PTOKEN)PsReferencePrimaryToken( ParentProcess );
|
|
Status = SepDuplicateToken(
|
|
ParentToken, // ExistingToken
|
|
&PrimaryTokenAttributes, // ObjectAttributes
|
|
FALSE, // EffectiveOnly
|
|
TokenPrimary, // TokenType
|
|
(SECURITY_IMPERSONATION_LEVEL)NULL, // ImpersonationLevel
|
|
KernelMode, // RequestorMode
|
|
&NewToken // NewToken
|
|
);
|
|
PsDereferencePrimaryToken( (PACCESS_TOKEN)ParentToken );
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Insert the new token object, up its ref count, and then
|
|
// delete the new handle.
|
|
//
|
|
|
|
Status = ObInsertObject(
|
|
NewToken,
|
|
NULL,
|
|
0,
|
|
0,
|
|
(PVOID *)NULL,
|
|
&NewTokenHandle
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//Status = ObReferenceObject(
|
|
// NewToken // Object
|
|
// );
|
|
Status = ObReferenceObjectByHandle(
|
|
NewTokenHandle, // Handle
|
|
DELETE, // DesiredAccess
|
|
SepTokenObjectType, // ObjectType
|
|
KernelMode, // AccessMode
|
|
(PVOID *)ChildToken, // Object
|
|
NULL // GrantedAccess
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
((PTOKEN)NewToken)->TokenInUse = TRUE;
|
|
IgnoreStatus = ZwClose( NewTokenHandle );
|
|
}
|
|
|
|
//
|
|
// At this point, either the token has a reference
|
|
// outstanding (and no handles), or the reference
|
|
// failed. If the reference failed, the status will
|
|
// be returned indicating why.
|
|
//
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
SepTokenInitialization ( VOID )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates the token object type descriptor at system
|
|
initialization and stores the address of the object type descriptor
|
|
in global storage. It also created token related global variables.
|
|
|
|
Furthermore, some number of pseudo tokens are created during system
|
|
initialization. These tokens are tracked down and replaced with
|
|
real tokens.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the object type descriptor is
|
|
successfully initialized. Otherwise a value of FALSE is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
|
|
NTSTATUS Status;
|
|
UNICODE_STRING TypeName;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Initialize string descriptor.
|
|
//
|
|
|
|
RtlInitUnicodeString(&TypeName, L"Token");
|
|
|
|
|
|
//
|
|
// Create the global token lock
|
|
//
|
|
|
|
ExInitializeResource(&SepTokenLock);
|
|
|
|
|
|
#if 0
|
|
BUG, BUG Need to get system default ACL to protect token object
|
|
#endif
|
|
|
|
//
|
|
// Create object type descriptor.
|
|
//
|
|
|
|
RtlZeroMemory(&ObjectTypeInitializer,sizeof(ObjectTypeInitializer));
|
|
ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
|
|
ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
|
|
ObjectTypeInitializer.GenericMapping = SepTokenMapping;
|
|
ObjectTypeInitializer.SecurityRequired = TRUE;
|
|
ObjectTypeInitializer.UseDefaultObject = TRUE;
|
|
ObjectTypeInitializer.PoolType = PagedPool;
|
|
ObjectTypeInitializer.ValidAccessMask = TOKEN_ALL_ACCESS;
|
|
ObjectTypeInitializer.DeleteProcedure = SepTokenDeleteMethod;
|
|
|
|
Status = ObCreateObjectType(&TypeName,
|
|
&ObjectTypeInitializer,
|
|
(PSECURITY_DESCRIPTOR)NULL, // BUG, BUG assign real protection
|
|
&SepTokenObjectType
|
|
);
|
|
|
|
|
|
#if 0
|
|
BUG, BUG Now track down all pseudo tokens used during system initialization
|
|
BUG, BUG and replace them with real ones.
|
|
#endif
|
|
|
|
//
|
|
// If the object type descriptor was successfully created, then
|
|
// return a value of TRUE. Otherwise return a value of FALSE.
|
|
//
|
|
|
|
return (BOOLEAN)NT_SUCCESS(Status);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Temporary, for Debug only //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////////
|
|
#ifdef TOKEN_DEBUG
|
|
VOID
|
|
SepDumpToken(
|
|
IN PTOKEN T
|
|
)
|
|
|
|
{
|
|
ULONG Index;
|
|
|
|
//
|
|
// Dump a token
|
|
//
|
|
|
|
DbgPrint("\n");
|
|
|
|
DbgPrint(" address: 0x%lx \n", ((ULONG)T) );
|
|
|
|
DbgPrint(" TokenId: (0x%lx, 0x%lx) \n",
|
|
T->TokenId.HighPart, T->TokenId.LowPart );
|
|
|
|
if ( (T->AuthenticationId.Data[0] == SeSystemAuthenticationId.Data[0]) &&
|
|
(T->AuthenticationId.Data[1] == SeSystemAuthenticationId.Data[1]) &&
|
|
(T->AuthenticationId.Data[2] == SeSystemAuthenticationId.Data[2]) &&
|
|
(T->AuthenticationId.Data[3] == SeSystemAuthenticationId.Data[3]) ) {
|
|
|
|
DbgPrint(" AuthenticationId: SeSystemAuthenticationId \n");
|
|
|
|
} else {
|
|
|
|
DbgPrint(" AuthenticationId: (0x%lx, 0x%lx, 0x%lx, 0x%lx) \n",
|
|
T->AuthenticationId.Data[0],
|
|
T->AuthenticationId.Data[1],
|
|
T->AuthenticationId.Data[2],
|
|
T->AuthenticationId.Data[3] );
|
|
}
|
|
|
|
DbgPrint(" ExpirationTime: 0x%lx, 0x%lx \n",
|
|
T->ExpirationTime.HighPart,
|
|
T->ExpirationTime.LowPart );
|
|
|
|
if (T->TokenType == TokenPrimary) {
|
|
DbgPrint(" TokenType: Primary \n");
|
|
} else {
|
|
if (T->TokenType == TokenImpersonation) {
|
|
DbgPrint(" TokenType: Impersonation \n");
|
|
} else {
|
|
DbgPrint(" TokenType: (Unknown type, value = 0x%lx) \n",
|
|
((ULONG)T-TokenType) );
|
|
}
|
|
}
|
|
|
|
DbgPrint(" ImpersonationLevel: 0x%lx \n",
|
|
((ULONG)T->ImpersonationLevel) );
|
|
|
|
DbgPrint(" TokenSource: (not yet provided) \n");
|
|
DbgPrint(" DynamicCharged: 0x%lx \n", T->DynamicCharged);
|
|
DbgPrint(" UserAndGroupCount: 0x%lx \n", T->UserAndGroupCount);
|
|
DbgPrint(" PrivilegeCount: 0x%lx \n", T->PrivilegeCount);
|
|
DbgPrint(" VariableLength: 0x%lx \n", T->VariableLength);
|
|
|
|
|
|
DbgPrint(" ModifiedId: (0x%lx, 0x%lx) \n",
|
|
T->ModifiedId.HighPart,
|
|
T->ModifiedId.LowPart );
|
|
DbgPrint(" DynamicAvailable: 0x%lx \n", T->DynamicAvailable);
|
|
DbgPrint(" DefaultOwnerIndex: 0x%lx \n", T->DefaultOwnerIndex);
|
|
|
|
|
|
DbgPrint(" Address of DynamicPart: 0x%lx \n",
|
|
(* (PULONG)((PVOID)(&(T->DynamicPart)))) );
|
|
DbgPrint(" Address of Default DACL: 0x%lx \n",
|
|
(* (PULONG)((PVOID)(&(T->DefaultDacl)))) );
|
|
|
|
DbgPrint(" Address Of Variable Part: 0x%lx \n",
|
|
&(T->VariablePart) );
|
|
|
|
DbgPrint("\n");
|
|
DbgPrint(" PrimaryGroup:\n");
|
|
DbgPrint(" Address: 0x%lx \n",
|
|
(* (PULONG)((PVOID)(&(T->PrimaryGroup)))) );
|
|
DbgPrint(" Length: 0x%lx \n",
|
|
SeLengthSid((T->PrimaryGroup)) );
|
|
DbgPrint("\n");
|
|
DbgPrint(" UserAndGroups: 0x%lx \n",
|
|
(* (PULONG)((PVOID)(&(T->UserAndGroups)))) );
|
|
DbgPrint(" User ID - \n");
|
|
DbgPrint(" Address: 0x%lx \n",
|
|
(* (PULONG)((PVOID)(&(T->UserAndGroups[0].Sid)))) );
|
|
DbgPrint(" Attributes: 0x%lx \n",
|
|
(T->UserAndGroups[0].Attributes) );
|
|
DbgPrint(" Length: 0x%lx \n",
|
|
SeLengthSid((T->UserAndGroups[0].Sid)) );
|
|
Index = 1;
|
|
while (Index < T->UserAndGroupCount) {
|
|
DbgPrint(" Group 0x%lx - \n", Index );
|
|
DbgPrint(" Address: 0x%lx \n",
|
|
(* (PULONG)((PVOID)(&(T->UserAndGroups[Index].Sid)))) );
|
|
DbgPrint(" Attributes: 0x%lx \n",
|
|
(T->UserAndGroups[Index].Attributes) );
|
|
DbgPrint(" Length: 0x%lx \n",
|
|
SeLengthSid((T->UserAndGroups[Index].Sid)) );
|
|
Index += 1;
|
|
}
|
|
|
|
|
|
DbgPrint("\n");
|
|
DbgPrint(" Privileges: 0x%lx\n",
|
|
(* (PULONG)((PVOID)(&(T->Privileges)))) );
|
|
Index = 0;
|
|
while (Index < T->PrivilegeCount) {
|
|
DbgPrint(" Privilege 0x%lx - \n", Index );
|
|
DbgPrint(" Address: 0x%lx \n",
|
|
(&(T->Privileges[Index])) );
|
|
DbgPrint(" LUID: (0x%lx, 0x%lx) \n",
|
|
T->Privileges[Index].Luid.HighPart,
|
|
T->Privileges[Index].Luid.LowPart );
|
|
DbgPrint(" Attributes: 0x%lx \n",
|
|
T->Privileges[Index].Attributes );
|
|
|
|
Index += 1;
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
#endif //TOKEN_DEBUG
|
|
|
|
NTSTATUS
|
|
NtCreateToken(
|
|
OUT PHANDLE TokenHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
|
|
IN TOKEN_TYPE TokenType,
|
|
IN PLUID AuthenticationId,
|
|
IN PLARGE_INTEGER ExpirationTime,
|
|
IN PTOKEN_USER User,
|
|
IN PTOKEN_GROUPS Groups,
|
|
IN PTOKEN_PRIVILEGES Privileges,
|
|
IN PTOKEN_OWNER Owner OPTIONAL,
|
|
IN PTOKEN_PRIMARY_GROUP PrimaryGroup,
|
|
IN PTOKEN_DEFAULT_DACL DefaultDacl OPTIONAL,
|
|
IN PTOKEN_SOURCE TokenSource
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a token object and return a handle opened for access to
|
|
that token. This API requires SeCreateTokenPrivilege privilege.
|
|
|
|
Arguments:
|
|
|
|
TokenHandle - Receives the handle of the newly created token.
|
|
|
|
DesiredAccess - Is an access mask indicating which access types
|
|
the handle is to provide to the new object.
|
|
|
|
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 token type is TokenImpersonation, then this parameter
|
|
must specify the impersonation level of the token.
|
|
|
|
TokenType - Type of token to be created. Privilege is required
|
|
to create any type of token.
|
|
|
|
AuthenticationId - Points to a LUID (or LUID) providing a unique
|
|
identifier associated with the authentication. This is used
|
|
within security only, for audit purposes.
|
|
|
|
ExpirationTime - Time at which the token becomes invalid. If this
|
|
value is specified as zero, then the token has no expiration
|
|
time.
|
|
|
|
User - Is the user SID to place in the token.
|
|
|
|
Groups - Are the group SIDs to place in the token.
|
|
|
|
Privileges - Are the privileges to place in the token.
|
|
|
|
Owner - (Optionally) identifies an identifier that is to be used
|
|
as the default owner for the token. If not provided, the
|
|
user ID is made the default owner.
|
|
|
|
PrimaryGroup - Identifies which of the group IDs is to be the
|
|
primary group of the token.
|
|
|
|
DefaultDacl - (optionally) establishes an ACL to be used as the
|
|
default discretionary access protection for the token.
|
|
|
|
TokenSource - Identifies the token source name string and
|
|
identifier to be assigned to the token.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - Indicates the operation was successful.
|
|
|
|
STATUS_INVALID_OWNER - Indicates the ID provided to be assigned
|
|
as the default owner of the token does not have an attribute
|
|
indicating it may be assigned as an owner.
|
|
|
|
STATUS_INVALID_PRIMARY_GROUP - Indicates the group ID provided
|
|
via the PrimaryGroup parameter was not among those assigned
|
|
to the token in the Groups parameter.
|
|
|
|
STATUS_BAD_IMPERSONATION_LEVEL - Indicates no impersonation level
|
|
was provided when attempting to create a token of type
|
|
TokenImpersonation.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
KPROCESSOR_MODE PreviousMode;
|
|
NTSTATUS Status;
|
|
ULONG Ignore;
|
|
|
|
|
|
HANDLE LocalHandle;
|
|
|
|
BOOLEAN SecurityQosPresent = FALSE;
|
|
SECURITY_ADVANCED_QUALITY_OF_SERVICE CapturedSecurityQos;
|
|
|
|
LUID CapturedAuthenticationId;
|
|
LARGE_INTEGER CapturedExpirationTime;
|
|
|
|
PSID_AND_ATTRIBUTES CapturedUser = NULL;
|
|
ULONG CapturedUserLength;
|
|
|
|
ULONG CapturedGroupCount;
|
|
PSID_AND_ATTRIBUTES CapturedGroups = NULL;
|
|
ULONG CapturedGroupsLength;
|
|
|
|
ULONG CapturedPrivilegeCount;
|
|
PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
|
|
ULONG CapturedPrivilegesLength;
|
|
|
|
PSID CapturedOwner = NULL;
|
|
|
|
PSID CapturedPrimaryGroup = NULL;
|
|
|
|
PACL CapturedDefaultDacl = NULL;
|
|
|
|
TOKEN_SOURCE CapturedTokenSource;
|
|
|
|
ULONG CapturedAddress;
|
|
|
|
PAGED_CODE();
|
|
|
|
PreviousMode = KeGetPreviousMode();
|
|
|
|
if (PreviousMode != KernelMode) {
|
|
|
|
//
|
|
// Probe everything necessary for input to the capture subroutines.
|
|
//
|
|
|
|
try {
|
|
|
|
ProbeForWriteHandle(TokenHandle);
|
|
|
|
|
|
ProbeForRead( ExpirationTime, sizeof(LARGE_INTEGER), sizeof(ULONG) );
|
|
ProbeForRead( Groups, sizeof(TOKEN_GROUPS), sizeof(ULONG) );
|
|
ProbeForRead( Privileges, sizeof(TOKEN_PRIVILEGES), sizeof(ULONG) );
|
|
ProbeForRead( TokenSource, sizeof(TOKEN_SOURCE), sizeof(ULONG) );
|
|
|
|
|
|
if ( ARGUMENT_PRESENT(Owner) ) {
|
|
ProbeForRead( Owner, sizeof(TOKEN_OWNER), sizeof(ULONG) );
|
|
}
|
|
|
|
|
|
ProbeForRead(
|
|
PrimaryGroup,
|
|
sizeof(TOKEN_PRIMARY_GROUP),
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
|
|
if ( ARGUMENT_PRESENT(DefaultDacl) ) {
|
|
ProbeForRead(
|
|
DefaultDacl,
|
|
sizeof(TOKEN_DEFAULT_DACL),
|
|
sizeof(ULONG)
|
|
);
|
|
}
|
|
|
|
ProbeForRead(
|
|
AuthenticationId,
|
|
sizeof(LUID),
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode();
|
|
} // end_try
|
|
|
|
} //end_if
|
|
|
|
//
|
|
// Capture the security quality of service.
|
|
// This capture routine necessarily does some probing of its own.
|
|
//
|
|
|
|
Status = SeCaptureSecurityQos(
|
|
ObjectAttributes,
|
|
PreviousMode,
|
|
&SecurityQosPresent,
|
|
&CapturedSecurityQos
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (TokenType == TokenImpersonation) {
|
|
|
|
if (!SecurityQosPresent) {
|
|
return STATUS_BAD_IMPERSONATION_LEVEL;
|
|
} // endif
|
|
|
|
} // endif
|
|
|
|
|
|
//
|
|
// Capture the rest of the arguments.
|
|
// These arguments have already been probed.
|
|
//
|
|
|
|
try {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Capture and validate AuthenticationID
|
|
//
|
|
|
|
RtlCopyLuid( &CapturedAuthenticationId, AuthenticationId );
|
|
|
|
//
|
|
// Capture ExpirationTime
|
|
//
|
|
|
|
CapturedExpirationTime = (*ExpirationTime);
|
|
|
|
//
|
|
// Capture User
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = SeCaptureSidAndAttributesArray(
|
|
&(User->User),
|
|
1,
|
|
PreviousMode,
|
|
NULL, 0,
|
|
PagedPool,
|
|
TRUE,
|
|
&CapturedUser,
|
|
&CapturedUserLength
|
|
);
|
|
}
|
|
|
|
|
|
//
|
|
// Capture Groups
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
CapturedGroupCount = Groups->GroupCount;
|
|
Status = SeCaptureSidAndAttributesArray(
|
|
(Groups->Groups),
|
|
CapturedGroupCount,
|
|
PreviousMode,
|
|
NULL, 0,
|
|
PagedPool,
|
|
TRUE,
|
|
&CapturedGroups,
|
|
&CapturedGroupsLength
|
|
);
|
|
}
|
|
|
|
|
|
//
|
|
// Capture Privileges
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
CapturedPrivilegeCount = Privileges->PrivilegeCount;
|
|
Status = SeCaptureLuidAndAttributesArray(
|
|
(Privileges->Privileges),
|
|
CapturedPrivilegeCount,
|
|
PreviousMode,
|
|
NULL, 0,
|
|
PagedPool,
|
|
TRUE,
|
|
&CapturedPrivileges,
|
|
&CapturedPrivilegesLength
|
|
);
|
|
}
|
|
|
|
|
|
//
|
|
// Capture Owner
|
|
//
|
|
|
|
if ( ARGUMENT_PRESENT(Owner) && NT_SUCCESS(Status)) {
|
|
CapturedAddress = (ULONG)(Owner->Owner);
|
|
Status = SeCaptureSid(
|
|
(PSID)CapturedAddress,
|
|
PreviousMode,
|
|
NULL, 0,
|
|
PagedPool,
|
|
TRUE,
|
|
&CapturedOwner
|
|
);
|
|
}
|
|
|
|
|
|
//
|
|
// Capture PrimaryGroup
|
|
//
|
|
if (NT_SUCCESS(Status)) {
|
|
CapturedAddress = (ULONG)(PrimaryGroup->PrimaryGroup);
|
|
Status = SeCaptureSid(
|
|
(PSID)CapturedAddress,
|
|
PreviousMode,
|
|
NULL, 0,
|
|
PagedPool,
|
|
TRUE,
|
|
&CapturedPrimaryGroup
|
|
);
|
|
}
|
|
|
|
|
|
//
|
|
// Capture DefaultDacl
|
|
//
|
|
|
|
if ( ARGUMENT_PRESENT(DefaultDacl) && NT_SUCCESS(Status) ) {
|
|
CapturedAddress = (ULONG)(DefaultDacl->DefaultDacl);
|
|
if ((PVOID)CapturedAddress != NULL) {
|
|
Status = SeCaptureAcl(
|
|
(PACL)CapturedAddress,
|
|
PreviousMode,
|
|
NULL, 0,
|
|
NonPagedPool,
|
|
TRUE,
|
|
&CapturedDefaultDacl,
|
|
&Ignore
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Capture TokenSource
|
|
//
|
|
|
|
CapturedTokenSource = (*TokenSource);
|
|
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
if (CapturedUser != NULL) {
|
|
SeReleaseSidAndAttributesArray(
|
|
CapturedUser,
|
|
PreviousMode,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
if (CapturedGroups != NULL) {
|
|
SeReleaseSidAndAttributesArray(
|
|
CapturedGroups,
|
|
PreviousMode,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
if (CapturedPrivileges != NULL) {
|
|
SeReleaseLuidAndAttributesArray(
|
|
CapturedPrivileges,
|
|
PreviousMode,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
if (CapturedOwner != NULL) {
|
|
SeReleaseSid( CapturedOwner, PreviousMode, TRUE);
|
|
}
|
|
|
|
if (CapturedPrimaryGroup != NULL) {
|
|
SeReleaseSid( CapturedPrimaryGroup, PreviousMode, TRUE);
|
|
}
|
|
|
|
if (CapturedDefaultDacl != NULL) {
|
|
SeReleaseAcl( CapturedDefaultDacl, PreviousMode, TRUE);
|
|
}
|
|
|
|
if (SecurityQosPresent == TRUE) {
|
|
SeFreeCapturedSecurityQos( &CapturedSecurityQos );
|
|
}
|
|
|
|
return GetExceptionCode();
|
|
|
|
} // end_try{}
|
|
|
|
//
|
|
// Create the token
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = SepCreateToken(
|
|
&LocalHandle,
|
|
PreviousMode,
|
|
DesiredAccess,
|
|
ObjectAttributes,
|
|
TokenType,
|
|
CapturedSecurityQos.ImpersonationLevel,
|
|
&CapturedAuthenticationId,
|
|
&CapturedExpirationTime,
|
|
CapturedUser,
|
|
CapturedGroupCount,
|
|
CapturedGroups,
|
|
CapturedGroupsLength,
|
|
CapturedPrivilegeCount,
|
|
CapturedPrivileges,
|
|
CapturedPrivilegesLength,
|
|
CapturedOwner,
|
|
CapturedPrimaryGroup,
|
|
CapturedDefaultDacl,
|
|
&CapturedTokenSource,
|
|
FALSE, // Not a system token
|
|
SecurityQosPresent ? CapturedSecurityQos.ProxyData : NULL,
|
|
SecurityQosPresent ? CapturedSecurityQos.AuditData : NULL
|
|
);
|
|
}
|
|
|
|
//
|
|
// Clean up the temporary capture buffers
|
|
//
|
|
|
|
if (CapturedUser != NULL) {
|
|
SeReleaseSidAndAttributesArray( CapturedUser, PreviousMode, TRUE);
|
|
}
|
|
if (CapturedGroups != NULL) {
|
|
SeReleaseSidAndAttributesArray( CapturedGroups, PreviousMode, TRUE);
|
|
}
|
|
|
|
if (CapturedPrivileges != NULL) {
|
|
SeReleaseLuidAndAttributesArray( CapturedPrivileges, PreviousMode, TRUE);
|
|
}
|
|
|
|
if (CapturedOwner != NULL) {
|
|
SeReleaseSid( CapturedOwner, PreviousMode, TRUE);
|
|
}
|
|
|
|
if (CapturedPrimaryGroup != NULL) {
|
|
SeReleaseSid( CapturedPrimaryGroup, PreviousMode, TRUE);
|
|
}
|
|
|
|
if (CapturedDefaultDacl != NULL) {
|
|
SeReleaseAcl( CapturedDefaultDacl, PreviousMode, TRUE);
|
|
}
|
|
|
|
if (SecurityQosPresent == TRUE) {
|
|
SeFreeCapturedSecurityQos( &CapturedSecurityQos );
|
|
}
|
|
|
|
//
|
|
// Return the handle to this new token
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
try { *TokenHandle = LocalHandle; }
|
|
except(EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); }
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// Token Private Routines //
|
|
// //
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
VOID
|
|
SepTokenDeleteMethod (
|
|
IN PVOID Token
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is the token object type-specific delete method.
|
|
It is needed to ensure that all memory allocated for the token
|
|
gets deallocated.
|
|
|
|
Arguments:
|
|
|
|
Token - Points to the token object being deleted.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// De-reference the logon session referenced by this token object
|
|
//
|
|
|
|
SepDeReferenceLogonSession( &(((TOKEN *)Token)->AuthenticationId) );
|
|
|
|
|
|
//
|
|
// If the token has an associated Dynamic part, deallocate it.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT( ((TOKEN *)Token)->DynamicPart)) {
|
|
ExFreePool( ((TOKEN *)Token)->DynamicPart );
|
|
}
|
|
|
|
//
|
|
// Free the Proxy and Global audit structures if present.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(((TOKEN *) Token)->ProxyData)) {
|
|
SepFreeProxyData( ((TOKEN *)Token)->ProxyData );
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(((TOKEN *)Token)->AuditData )) {
|
|
ExFreePool( (((TOKEN *)Token)->AuditData) );
|
|
}
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
SepCreateToken(
|
|
OUT PHANDLE TokenHandle,
|
|
IN KPROCESSOR_MODE RequestorMode,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
|
|
IN TOKEN_TYPE TokenType,
|
|
IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel OPTIONAL,
|
|
IN PLUID AuthenticationId,
|
|
IN PLARGE_INTEGER ExpirationTime,
|
|
IN PSID_AND_ATTRIBUTES User,
|
|
IN ULONG GroupCount,
|
|
IN PSID_AND_ATTRIBUTES Groups,
|
|
IN ULONG GroupsLength,
|
|
IN ULONG PrivilegeCount,
|
|
IN PLUID_AND_ATTRIBUTES Privileges,
|
|
IN ULONG PrivilegesLength,
|
|
IN PSID Owner OPTIONAL,
|
|
IN PSID PrimaryGroup,
|
|
IN PACL DefaultDacl OPTIONAL,
|
|
IN PTOKEN_SOURCE TokenSource,
|
|
IN BOOLEAN SystemToken,
|
|
IN PSECURITY_TOKEN_PROXY_DATA ProxyData OPTIONAL,
|
|
IN PSECURITY_TOKEN_AUDIT_DATA AuditData OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a token object and return a handle opened for access to
|
|
that token. This API implements the bulk of the work needed
|
|
for NtCreateToken.
|
|
|
|
All parameters except DesiredAccess and ObjectAttributes are assumed
|
|
to have been probed and captured.
|
|
|
|
The output parameter (TokenHandle) is expected to be returned to a
|
|
safe address, rather than to a user mode address that may cause an
|
|
exception.
|
|
|
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
NOTE: This routine is also used to create the initial system token.
|
|
In that case, the SystemToken parameter is TRUE and no handle
|
|
is established to the token. Instead, a pointer to the token
|
|
is returned via the TokenHandle parameter.
|
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
|
|
|
|
Arguments:
|
|
|
|
TokenHandle - Receives the handle of the newly created token. If the
|
|
SystemToken parameter is specified is true, then this parameter
|
|
receives a pointer to the token instead of a handle to the token.
|
|
|
|
RequestorMode - The mode of the caller on whose behalf the token
|
|
is being created.
|
|
|
|
DesiredAccess - Is an access mask indicating which access types
|
|
the handle is to provide to the new object.
|
|
|
|
ObjectAttributes - Points to the standard object attributes data
|
|
structure. Refer to the NT Object Management
|
|
Specification for a description of this data structure.
|
|
|
|
TokenType - Type of token to be created. Privilege is required
|
|
to create any type of token.
|
|
|
|
ImpersonationLevel - If the token type is TokenImpersonation, then
|
|
this parameter is used to specify the impersonation level of
|
|
the token.
|
|
|
|
AuthenticationId - Points to a LUID (or LUID) providing a unique
|
|
identifier associated with the authentication. This is used
|
|
within security only, for audit purposes.
|
|
|
|
ExpirationTime - Time at which the token becomes invalid. If this
|
|
value is specified as zero, then the token has no expiration
|
|
time.
|
|
|
|
User - Is the user SID to place in the token.
|
|
|
|
GroupCount - Indicates the number of groups in the 'Groups' parameter.
|
|
This value may be zero, in which case the 'Groups' parameter is
|
|
ignored.
|
|
|
|
Groups - Are the group SIDs, and their corresponding attributes,
|
|
to place in the token.
|
|
|
|
GroupsLength - Indicates the length, in bytes, of the array of groups
|
|
to place in the token.
|
|
|
|
PrivilegeCount - Indicates the number of privileges in the 'Privileges'
|
|
parameter. This value may be zero, in which case the 'Privileges'
|
|
parameter is ignored.
|
|
|
|
Privileges - Are the privilege LUIDs, and their corresponding attributes,
|
|
to place in the token.
|
|
|
|
PrivilegesLength - Indicates the length, in bytes, of the array of
|
|
privileges to place in the token.
|
|
|
|
Owner - (Optionally) identifies an identifier that is to be used
|
|
as the default owner for the token. If not provided, the
|
|
user ID is made the default owner.
|
|
|
|
PrimaryGroup - Identifies which of the group IDs is to be the
|
|
primary group of the token.
|
|
|
|
DefaultDacl - (optionally) establishes an ACL to be used as the
|
|
default discretionary access protection for the token.
|
|
|
|
TokenSource - Identifies the token source name string and
|
|
identifier to be assigned to the token.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - Indicates the operation was successful.
|
|
|
|
STATUS_INVALID_OWNER - Indicates the ID provided to be assigned
|
|
as the default owner of the token does not have an attribute
|
|
indicating it may be assigned as an owner.
|
|
|
|
STATUS_INVALID_PRIMARY_GROUP - Indicates the group ID provided
|
|
via the PrimaryGroup parameter was not among those assigned
|
|
to the token in the Groups parameter.
|
|
|
|
STATUS_INVALID_PARAMETER - Indicates that a required parameter,
|
|
such as User or PrimaryGroup, was not provided with a legitimate
|
|
value.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PTOKEN Token;
|
|
NTSTATUS Status;
|
|
|
|
ULONG PagedPoolSize;
|
|
|
|
ULONG PrimaryGroupLength;
|
|
|
|
ULONG TokenBodyLength;
|
|
ULONG VariableLength;
|
|
|
|
ULONG DefaultOwnerIndex;
|
|
|
|
ULONG NextFree;
|
|
PSID NextSidFree;
|
|
|
|
ULONG DynamicLength = TOKEN_DEFAULT_DYNAMIC_CHARGE;
|
|
ULONG DynamicLengthUsed;
|
|
|
|
ULONG SubAuthorityCount;
|
|
ULONG GroupIndex;
|
|
ULONG PrivilegeIndex;
|
|
BOOLEAN OwnerFound;
|
|
|
|
UCHAR TokenFlags = 0;
|
|
|
|
ACCESS_STATE AccessState;
|
|
AUX_ACCESS_DATA AuxData;
|
|
LUID NewModifiedId;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( sizeof(SECURITY_IMPERSONATION_LEVEL) <= sizeof(ULONG) );
|
|
|
|
//
|
|
// Make sure the Enabled and Enabled-by-default bits are set on every
|
|
// mandatory group.
|
|
//
|
|
|
|
for (GroupIndex=0; GroupIndex < GroupCount; GroupIndex++) {
|
|
if (Groups[GroupIndex].Attributes & SE_GROUP_MANDATORY) {
|
|
Groups[GroupIndex].Attributes |= (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check to see if the token being created is going to be granted
|
|
// SeChangeNotifyPrivilege. If so, set a flag in the TokenFlags field
|
|
// so we can find this out quickly.
|
|
//
|
|
|
|
for (PrivilegeIndex = 0; PrivilegeIndex < PrivilegeCount; PrivilegeIndex++) {
|
|
|
|
if (((RtlEqualLuid(&Privileges[PrivilegeIndex].Luid,&SeChangeNotifyPrivilege))
|
|
&&
|
|
(Privileges[PrivilegeIndex].Attributes & SE_PRIVILEGE_ENABLED))) {
|
|
|
|
TokenFlags = TOKEN_HAS_TRAVERSE_PRIVILEGE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Get a ModifiedId to use
|
|
//
|
|
|
|
ExAllocateLocallyUniqueId( &NewModifiedId );
|
|
|
|
//
|
|
// Validate the owner ID, if provided and establish the default
|
|
// owner index.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT(Owner)) {
|
|
|
|
DefaultOwnerIndex = 0;
|
|
|
|
} else {
|
|
|
|
|
|
if ( RtlEqualSid( Owner, User->Sid ) ) {
|
|
|
|
DefaultOwnerIndex = 0;
|
|
|
|
} else {
|
|
|
|
GroupIndex = 0;
|
|
OwnerFound = FALSE;
|
|
|
|
while ((GroupIndex < GroupCount) && (!OwnerFound)) {
|
|
|
|
if ( RtlEqualSid( Owner, (Groups[GroupIndex].Sid) ) ) {
|
|
|
|
//
|
|
// Found a match - make sure it is assignable as owner.
|
|
//
|
|
|
|
if ( SepArrayGroupAttributes( Groups, GroupIndex ) &
|
|
SE_GROUP_OWNER ) {
|
|
|
|
DefaultOwnerIndex = GroupIndex + 1;
|
|
OwnerFound = TRUE;
|
|
|
|
} else {
|
|
|
|
return STATUS_INVALID_OWNER;
|
|
|
|
} // endif Owner attribute set
|
|
|
|
} // endif owner = group
|
|
|
|
GroupIndex += 1;
|
|
|
|
} // endwhile
|
|
|
|
if (!OwnerFound) {
|
|
|
|
return STATUS_INVALID_OWNER;
|
|
|
|
} // endif !OwnerFound
|
|
} // endif owner = user
|
|
} // endif owner specified
|
|
|
|
|
|
|
|
//
|
|
// Increment the reference count for this logon session
|
|
// (fail if there is no corresponding logon session.)
|
|
//
|
|
|
|
Status = SepReferenceLogonSession( AuthenticationId );
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// Calculate the length needed for the variable portion of the token
|
|
// This includes the User ID, Group IDs, and Privileges
|
|
//
|
|
|
|
VariableLength = GroupsLength + PrivilegesLength;
|
|
|
|
SubAuthorityCount = ((SID *)(User->Sid))->SubAuthorityCount;
|
|
VariableLength += sizeof(SID_AND_ATTRIBUTES) +
|
|
(ULONG)LongAlign(RtlLengthRequiredSid( SubAuthorityCount ));
|
|
|
|
|
|
|
|
//
|
|
// Calculate the length needed for the dynamic portion of the token
|
|
// This includes the default Dacl and the primary group.
|
|
//
|
|
|
|
SubAuthorityCount = ((SID *)PrimaryGroup)->SubAuthorityCount;
|
|
DynamicLengthUsed = (ULONG)LongAlign(RtlLengthRequiredSid( SubAuthorityCount ));
|
|
|
|
if (ARGUMENT_PRESENT(DefaultDacl)) {
|
|
DynamicLengthUsed += (ULONG)LongAlign(DefaultDacl->AclSize);
|
|
}
|
|
|
|
if (DynamicLengthUsed > DynamicLength) {
|
|
DynamicLength = DynamicLengthUsed;
|
|
}
|
|
|
|
//
|
|
// Now create the token body
|
|
//
|
|
|
|
TokenBodyLength = sizeof(TOKEN) + VariableLength;
|
|
PagedPoolSize = TokenBodyLength + DynamicLength;
|
|
|
|
|
|
Status = ObCreateObject(
|
|
RequestorMode, // ProbeMode
|
|
SepTokenObjectType, // ObjectType
|
|
ObjectAttributes, // ObjectAttributes
|
|
RequestorMode, // OwnershipMode
|
|
NULL, // ParseContext
|
|
TokenBodyLength, // ObjectBodySize
|
|
PagedPoolSize, // PagedPoolCharge
|
|
0, // NonPagedPoolCharge
|
|
(PVOID *)&Token // Return pointer to object
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
SepDeReferenceLogonSession( AuthenticationId );
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// After this point, we rely on token deletion to clean up the referenced
|
|
// logon session if the creation fails.
|
|
//
|
|
|
|
|
|
//
|
|
// Main Body initialization
|
|
//
|
|
|
|
|
|
ExAllocateLocallyUniqueId( &(Token->TokenId) );
|
|
Token->AuthenticationId = (*AuthenticationId);
|
|
Token->TokenInUse = FALSE;
|
|
Token->ModifiedId = NewModifiedId;
|
|
Token->ExpirationTime = (*ExpirationTime);
|
|
Token->TokenType = TokenType;
|
|
Token->ImpersonationLevel = ImpersonationLevel;
|
|
Token->TokenSource = (*TokenSource);
|
|
|
|
Token->TokenFlags = TokenFlags;
|
|
|
|
Token->DynamicCharged = DynamicLength;
|
|
Token->DynamicAvailable = DynamicLength - DynamicLengthUsed;
|
|
|
|
Token->DefaultOwnerIndex = DefaultOwnerIndex;
|
|
Token->DefaultDacl = NULL;
|
|
|
|
Token->VariableLength = VariableLength;
|
|
|
|
// Ensure SepTokenDeleteMethod knows the buffers aren't allocated yet.
|
|
Token->ProxyData = NULL;
|
|
Token->AuditData = NULL;
|
|
Token->DynamicPart = NULL;
|
|
|
|
if (ARGUMENT_PRESENT( ProxyData )) {
|
|
|
|
Status = SepCopyProxyData(
|
|
&Token->ProxyData,
|
|
ProxyData
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ObDereferenceObject( Token );
|
|
return( STATUS_NO_MEMORY );
|
|
}
|
|
|
|
} else {
|
|
|
|
Token->ProxyData = NULL;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT( AuditData )) {
|
|
|
|
Token->AuditData = ExAllocatePool( PagedPool, sizeof( SECURITY_TOKEN_AUDIT_DATA ));
|
|
|
|
if (Token->AuditData == NULL) {
|
|
ObDereferenceObject( Token );
|
|
return( STATUS_NO_MEMORY );
|
|
}
|
|
|
|
*(Token->AuditData) = *AuditData;
|
|
|
|
} else {
|
|
|
|
Token->AuditData = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Variable part initialization
|
|
// Data is in the following order:
|
|
//
|
|
// Privileges array
|
|
// User (SID_AND_ATTRIBUTES)
|
|
// Groups (SID_AND_ATTRIBUTES)
|
|
// SIDs
|
|
//
|
|
|
|
NextFree = (ULONG)(&Token->VariablePart);
|
|
Token->Privileges = (PLUID_AND_ATTRIBUTES)NextFree;
|
|
Token->PrivilegeCount = PrivilegeCount;
|
|
RtlCopyLuidAndAttributesArray( PrivilegeCount,
|
|
Privileges,
|
|
(PLUID_AND_ATTRIBUTES)NextFree
|
|
);
|
|
|
|
NextFree += (PrivilegeCount * (ULONG)sizeof(LUID_AND_ATTRIBUTES));
|
|
VariableLength -= ( (PrivilegeCount * (ULONG)sizeof(LUID_AND_ATTRIBUTES))
|
|
+ (ULONG)sizeof(SID_AND_ATTRIBUTES) );
|
|
|
|
NextSidFree = (PSID)(NextFree +
|
|
((1 + GroupCount) * (ULONG)sizeof(SID_AND_ATTRIBUTES))
|
|
);
|
|
Token->UserAndGroups = (PSID_AND_ATTRIBUTES)NextFree;
|
|
Token->UserAndGroupCount = 1 + GroupCount;
|
|
Status = RtlCopySidAndAttributesArray(
|
|
1,
|
|
User,
|
|
VariableLength,
|
|
(PSID_AND_ATTRIBUTES)NextFree,
|
|
NextSidFree,
|
|
&NextSidFree,
|
|
&VariableLength
|
|
);
|
|
|
|
ASSERT(NT_SUCCESS(Status));
|
|
NextFree += (ULONG)sizeof(SID_AND_ATTRIBUTES);
|
|
VariableLength -= (GroupCount * (ULONG)sizeof(SID_AND_ATTRIBUTES));
|
|
|
|
Status = RtlCopySidAndAttributesArray(
|
|
GroupCount,
|
|
Groups,
|
|
VariableLength,
|
|
(PSID_AND_ATTRIBUTES)NextFree,
|
|
NextSidFree,
|
|
&NextSidFree,
|
|
&VariableLength
|
|
);
|
|
|
|
|
|
ASSERT(NT_SUCCESS(Status));
|
|
NextFree += (GroupCount * (ULONG)sizeof(SID_AND_ATTRIBUTES));
|
|
|
|
//
|
|
// Dynamic part initialization
|
|
// Data is in the following order:
|
|
//
|
|
// PrimaryGroup (SID)
|
|
// Default Discreationary Acl (ACL)
|
|
//
|
|
|
|
Token->DynamicPart = (PULONG)ExAllocatePoolWithTag( PagedPool, DynamicLength, 'dTeS' );
|
|
|
|
//
|
|
// The attempt to allocate the DynamicPart of the token may have
|
|
// failed. Dereference the created object and exit with an error.
|
|
//
|
|
|
|
if (Token->DynamicPart == NULL) {
|
|
ObDereferenceObject( Token );
|
|
return( STATUS_NO_MEMORY );
|
|
}
|
|
|
|
|
|
NextFree = (ULONG)(Token->DynamicPart);
|
|
|
|
Token->PrimaryGroup = (PSID)NextFree;
|
|
PrimaryGroupLength = RtlLengthRequiredSid( ((SID *)PrimaryGroup)->SubAuthorityCount );
|
|
RtlCopySid( PrimaryGroupLength, (PSID)NextFree, PrimaryGroup );
|
|
NextFree += ((ULONG)LongAlign(PrimaryGroupLength));
|
|
|
|
if (ARGUMENT_PRESENT(DefaultDacl)) {
|
|
Token->DefaultDacl = (PACL)NextFree;
|
|
|
|
RtlMoveMemory( (PVOID)NextFree,
|
|
(PVOID)DefaultDacl,
|
|
DefaultDacl->AclSize
|
|
);
|
|
}
|
|
|
|
#ifdef TOKEN_DEBUG
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Debug
|
|
SepDumpToken( Token );
|
|
// Debug
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
#endif //TOKEN_DEBUG
|
|
|
|
|
|
//
|
|
// Insert the token unless it is a system token.
|
|
//
|
|
|
|
if (!SystemToken) {
|
|
|
|
Status = SeCreateAccessState(
|
|
&AccessState,
|
|
&AuxData,
|
|
DesiredAccess,
|
|
&SepTokenObjectType->TypeInfo.GenericMapping
|
|
);
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
BOOLEAN PrivilegeHeld;
|
|
|
|
PrivilegeHeld = SeSinglePrivilegeCheck(
|
|
SeCreateTokenPrivilege,
|
|
KeGetPreviousMode()
|
|
);
|
|
|
|
if (PrivilegeHeld) {
|
|
|
|
Status = ObInsertObject( Token,
|
|
&AccessState,
|
|
0,
|
|
0,
|
|
(PVOID *)NULL,
|
|
TokenHandle
|
|
);
|
|
|
|
} else {
|
|
|
|
Status = STATUS_PRIVILEGE_NOT_HELD;
|
|
ObDereferenceObject( Token );
|
|
}
|
|
|
|
SeDeleteAccessState( &AccessState );
|
|
|
|
} else {
|
|
|
|
ObDereferenceObject( Token );
|
|
}
|
|
} else {
|
|
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
ObDeleteCapturedInsertInfo(Token);
|
|
//
|
|
// Return pointer instead of handle.
|
|
//
|
|
|
|
(*TokenHandle) = (HANDLE)Token;
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
SepIdAssignableAsOwner(
|
|
IN PTOKEN Token,
|
|
IN ULONG Index
|
|
)
|
|
|
|
/*++
|
|
|
|
|
|
Routine Description:
|
|
|
|
This routine returns a boolean value indicating whether the user
|
|
or group ID in the specified token with the specified index is
|
|
assignable as the owner of an object.
|
|
|
|
If the index is 0, which is always the USER ID, then the ID is
|
|
assignable as owner. Otherwise, the ID is that of a group, and
|
|
it must have the "Owner" attribute set to be assignable.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
Token - Pointer to a locked Token to use.
|
|
|
|
Index - Index into the Token's UserAndGroupsArray. This value
|
|
is assumed to be valid.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Indicates the index corresponds to an ID that may be assigned
|
|
as the owner of objects.
|
|
|
|
FALSE - Indicates the index does not correspond to an ID that may be
|
|
assigned as the owner of objects.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (Index == 0) {
|
|
|
|
return TRUE;
|
|
|
|
} else {
|
|
|
|
return (BOOLEAN)
|
|
( (SepTokenGroupAttributes(Token,Index) & SE_GROUP_OWNER)
|
|
!= 0
|
|
);
|
|
}
|
|
}
|