2386 lines
65 KiB
C
2386 lines
65 KiB
C
/*++
|
||
|
||
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
|
||
);
|
||
}
|
||
}
|