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