diff options
Diffstat (limited to 'private/ntos/se/token.c')
-rw-r--r-- | private/ntos/se/token.c | 2385 |
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 + ); + } +} |