reactos/ntoskrnl/se/token.c

6908 lines
227 KiB
C
Raw Normal View History

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Security token implementation support
* COPYRIGHT: Copyright David Welch <welch@cwcom.net>
* Copyright 2021 George Bișoc <george.bisoc@reactos.org>
*/
/* INCLUDES *******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
#include <ntlsa.h>
/* GLOBALS ********************************************************************/
POBJECT_TYPE SeTokenObjectType = NULL;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
TOKEN_SOURCE SeSystemTokenSource = {"*SYSTEM*", {0}};
LUID SeSystemAuthenticationId = SYSTEM_LUID;
LUID SeAnonymousAuthenticationId = ANONYMOUS_LOGON_LUID;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
static GENERIC_MAPPING SepTokenMapping = {
TOKEN_READ,
TOKEN_WRITE,
TOKEN_EXECUTE,
TOKEN_ALL_ACCESS
};
static const INFORMATION_CLASS_INFO SeTokenInformationClass[] = {
/* Class 0 not used, blame MS! */
IQS_NONE,
/* TokenUser */
IQS_SAME(TOKEN_USER, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
/* TokenGroups */
IQS_SAME(TOKEN_GROUPS, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
/* TokenPrivileges */
IQS_SAME(TOKEN_PRIVILEGES, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
/* TokenOwner */
IQS_SAME(TOKEN_OWNER, ULONG, ICIF_QUERY | ICIF_SET | ICIF_SIZE_VARIABLE),
/* TokenPrimaryGroup */
IQS_SAME(TOKEN_PRIMARY_GROUP, ULONG, ICIF_QUERY | ICIF_SET | ICIF_SIZE_VARIABLE),
/* TokenDefaultDacl */
IQS_SAME(TOKEN_DEFAULT_DACL, ULONG, ICIF_QUERY | ICIF_SET | ICIF_SIZE_VARIABLE),
/* TokenSource */
IQS_SAME(TOKEN_SOURCE, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
/* TokenType */
IQS_SAME(TOKEN_TYPE, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
/* TokenImpersonationLevel */
IQS_SAME(SECURITY_IMPERSONATION_LEVEL, ULONG, ICIF_QUERY),
/* TokenStatistics */
IQS_SAME(TOKEN_STATISTICS, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
/* TokenRestrictedSids */
IQS_SAME(TOKEN_GROUPS, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
/* TokenSessionId */
IQS_SAME(ULONG, ULONG, ICIF_QUERY | ICIF_SET),
/* TokenGroupsAndPrivileges */
IQS_SAME(TOKEN_GROUPS_AND_PRIVILEGES, ULONG, ICIF_QUERY | ICIF_QUERY_SIZE_VARIABLE),
/* TokenSessionReference */
IQS_SAME(ULONG, ULONG, ICIF_SET),
/* TokenSandBoxInert */
IQS_SAME(ULONG, ULONG, ICIF_QUERY),
/* TokenAuditPolicy */
IQS_SAME(TOKEN_AUDIT_POLICY_INFORMATION, ULONG, ICIF_SET | ICIF_SET_SIZE_VARIABLE),
/* TokenOrigin */
IQS_SAME(TOKEN_ORIGIN, ULONG, ICIF_QUERY | ICIF_SET),
};
/* FUNCTIONS *****************************************************************/
/**
* @brief
* Creates a lock for the token.
*
* @param[in,out] Token
* A token which lock has to be created.
*
* @return
* STATUS_SUCCESS if the pool allocation and resource initialisation have
* completed successfully, otherwise STATUS_INSUFFICIENT_RESOURCES on a
* pool allocation failure.
*/
static
NTSTATUS
SepCreateTokenLock(
_Inout_ PTOKEN Token)
{
PAGED_CODE();
Token->TokenLock = ExAllocatePoolWithTag(NonPagedPool,
sizeof(ERESOURCE),
TAG_SE_TOKEN_LOCK);
if (Token->TokenLock == NULL)
{
DPRINT1("SepCreateTokenLock(): Failed to allocate memory!\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
ExInitializeResourceLite(Token->TokenLock);
return STATUS_SUCCESS;
}
/**
* @brief
* Deletes a lock of a token.
*
* @param[in,out] Token
* A token which contains the lock.
*
* @return
* Nothing.
*/
static
VOID
SepDeleteTokenLock(
_Inout_ PTOKEN Token)
{
PAGED_CODE();
ExDeleteResourceLite(Token->TokenLock);
ExFreePoolWithTag(Token->TokenLock, TAG_SE_TOKEN_LOCK);
}
/**
* @brief
* Compares the elements of SID arrays provided by tokens.
* The elements that are being compared for equality are
* the SIDs and their attributes.
*
* @param[in] SidArrayToken1
* SID array from the first token.
*
* @param[in] CountSidArray1
* SID count array from the first token.
*
* @param[in] SidArrayToken2
* SID array from the second token.
*
* @param[in] CountSidArray2
* SID count array from the second token.
*
* @return
* Returns TRUE if the elements match from either arrays,
* FALSE otherwise.
*/
static
BOOLEAN
SepCompareSidAndAttributesFromTokens(
_In_ PSID_AND_ATTRIBUTES SidArrayToken1,
_In_ ULONG CountSidArray1,
_In_ PSID_AND_ATTRIBUTES SidArrayToken2,
_In_ ULONG CountSidArray2)
{
ULONG FirstCount, SecondCount;
PSID_AND_ATTRIBUTES FirstSidArray, SecondSidArray;
PAGED_CODE();
/* Bail out if index counters provided are not equal */
if (CountSidArray1 != CountSidArray2)
{
DPRINT("SepCompareSidAndAttributesFromTokens(): Index counters are not the same!\n");
return FALSE;
}
/* Loop over the SID arrays and compare them */
for (FirstCount = 0; FirstCount < CountSidArray1; FirstCount++)
{
for (SecondCount = 0; SecondCount < CountSidArray2; SecondCount++)
{
FirstSidArray = &SidArrayToken1[FirstCount];
SecondSidArray = &SidArrayToken2[SecondCount];
if (RtlEqualSid(FirstSidArray->Sid, SecondSidArray->Sid) &&
FirstSidArray->Attributes == SecondSidArray->Attributes)
{
break;
}
}
/* We've exhausted the array of the second token without finding this one */
if (SecondCount == CountSidArray2)
{
DPRINT("SepCompareSidAndAttributesFromTokens(): No matching elements could be found in either token!\n");
return FALSE;
}
}
return TRUE;
}
/**
* @brief
* Compares the elements of privilege arrays provided by tokens.
* The elements that are being compared for equality are
* the privileges and their attributes.
*
* @param[in] PrivArrayToken1
* Privilege array from the first token.
*
* @param[in] CountPrivArray1
* Privilege count array from the first token.
*
* @param[in] PrivArrayToken2
* Privilege array from the second token.
*
* @param[in] CountPrivArray2
* Privilege count array from the second token.
*
* @return
* Returns TRUE if the elements match from either arrays,
* FALSE otherwise.
*/
static
BOOLEAN
SepComparePrivilegeAndAttributesFromTokens(
_In_ PLUID_AND_ATTRIBUTES PrivArrayToken1,
_In_ ULONG CountPrivArray1,
_In_ PLUID_AND_ATTRIBUTES PrivArrayToken2,
_In_ ULONG CountPrivArray2)
{
ULONG FirstCount, SecondCount;
PLUID_AND_ATTRIBUTES FirstPrivArray, SecondPrivArray;
PAGED_CODE();
/* Bail out if index counters provided are not equal */
if (CountPrivArray1 != CountPrivArray2)
{
DPRINT("SepComparePrivilegeAndAttributesFromTokens(): Index counters are not the same!\n");
return FALSE;
}
/* Loop over the privilege arrays and compare them */
for (FirstCount = 0; FirstCount < CountPrivArray1; FirstCount++)
{
for (SecondCount = 0; SecondCount < CountPrivArray2; SecondCount++)
{
FirstPrivArray = &PrivArrayToken1[FirstCount];
SecondPrivArray = &PrivArrayToken2[SecondCount];
if (RtlEqualLuid(&FirstPrivArray->Luid, &SecondPrivArray->Luid) &&
FirstPrivArray->Attributes == SecondPrivArray->Attributes)
{
break;
}
}
/* We've exhausted the array of the second token without finding this one */
if (SecondCount == CountPrivArray2)
{
DPRINT("SepComparePrivilegeAndAttributesFromTokens(): No matching elements could be found in either token!\n");
return FALSE;
}
}
return TRUE;
}
/**
* @brief
* Compares tokens if they're equal based on all the following properties. If all
* of the said conditions are met then the tokens are deemed as equal.
*
* - Every SID that is present in either token is also present in the other one.
* - Both or none of the tokens are restricted.
* - If both tokens are restricted, every SID that is restricted in either token is
* also restricted in the other one.
* - Every privilege present in either token is also present in the other one.
*
* @param[in] FirstToken
* The first token.
*
* @param[in] SecondToken
* The second token.
*
* @param[out] Equal
* The retrieved value which determines if the tokens are
* equal or not.
*
* @return
* Returns STATUS_SUCCESS.
*/
static
NTSTATUS
SepCompareTokens(
_In_ PTOKEN FirstToken,
_In_ PTOKEN SecondToken,
_Out_ PBOOLEAN Equal)
{
BOOLEAN Restricted, IsEqual = FALSE;
PAGED_CODE();
ASSERT(FirstToken != SecondToken);
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Lock the tokens */
SepAcquireTokenLockShared(FirstToken);
SepAcquireTokenLockShared(SecondToken);
/* Check if every SID that is present in either token is also present in the other one */
if (!SepCompareSidAndAttributesFromTokens(FirstToken->UserAndGroups,
FirstToken->UserAndGroupCount,
SecondToken->UserAndGroups,
SecondToken->UserAndGroupCount))
{
goto Quit;
}
/* Is one token restricted but the other isn't? */
Restricted = SeTokenIsRestricted(FirstToken);
if (Restricted != SeTokenIsRestricted(SecondToken))
{
/* If that's the case then bail out */
goto Quit;
}
/*
* If both tokens are restricted check if every SID
* that is restricted in either token is also restricted
* in the other one.
*/
if (Restricted)
{
if (!SepCompareSidAndAttributesFromTokens(FirstToken->RestrictedSids,
FirstToken->RestrictedSidCount,
SecondToken->RestrictedSids,
SecondToken->RestrictedSidCount))
{
goto Quit;
}
}
/* Check if every privilege present in either token is also present in the other one */
if (!SepComparePrivilegeAndAttributesFromTokens(FirstToken->Privileges,
FirstToken->PrivilegeCount,
SecondToken->Privileges,
SecondToken->PrivilegeCount))
{
goto Quit;
}
/* If we're here then the tokens are equal */
IsEqual = TRUE;
DPRINT("SepCompareTokens(): Tokens are equal!\n");
Quit:
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Unlock the tokens */
SepReleaseTokenLock(SecondToken);
SepReleaseTokenLock(FirstToken);
*Equal = IsEqual;
return STATUS_SUCCESS;
}
/**
* @brief
* Private function that impersonates the system's anonymous logon token.
* The major bulk of the impersonation procedure is done here.
*
* @param[in] Thread
* The executive thread object that is to impersonate the client.
*
* @param[in] PreviousMode
* The access processor mode, indicating if the call is executed
* in kernel or user mode.
*
* @return
* Returns STATUS_SUCCESS if the impersonation has succeeded.
* STATUS_UNSUCCESSFUL is returned if the primary token couldn't be
* obtained from the current process to perform additional tasks.
* STATUS_ACCESS_DENIED is returned if the process' primary token is
* restricted, which for this matter we cannot impersonate onto a
* restricted process. Otherwise a failure NTSTATUS code is returned.
*/
static
NTSTATUS
SepImpersonateAnonymousToken(
_In_ PETHREAD Thread,
_In_ KPROCESSOR_MODE PreviousMode)
{
NTSTATUS Status;
PTOKEN TokenToImpersonate, ProcessToken;
ULONG IncludeEveryoneValueData;
PAGED_CODE();
/*
* We must check first which kind of token
* shall we assign for the thread to impersonate,
* the one with Everyone Group SID or the other
* without. Invoke the registry helper to
* return the data value for us.
*/
Status = SepRegQueryHelper(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Lsa",
L"EveryoneIncludesAnonymous",
REG_DWORD,
sizeof(IncludeEveryoneValueData),
&IncludeEveryoneValueData);
if (!NT_SUCCESS(Status))
{
DPRINT1("SepRegQueryHelper(): Failed to query the registry value (Status 0x%lx)\n", Status);
return Status;
}
if (IncludeEveryoneValueData == 0)
{
DPRINT("SepImpersonateAnonymousToken(): Assigning the token not including the Everyone Group SID...\n");
TokenToImpersonate = SeAnonymousLogonTokenNoEveryone;
}
else
{
DPRINT("SepImpersonateAnonymousToken(): Assigning the token including the Everyone Group SID...\n");
TokenToImpersonate = SeAnonymousLogonToken;
}
/*
* Tell the object manager that we're going to use this token
* object now by incrementing the reference count.
*/
Status = ObReferenceObjectByPointer(TokenToImpersonate,
TOKEN_IMPERSONATE,
SeTokenObjectType,
PreviousMode);
if (!NT_SUCCESS(Status))
{
DPRINT1("SepImpersonateAnonymousToken(): Couldn't be able to use the token, bail out...\n");
return Status;
}
/*
* Reference the primary token of the current process that the anonymous
* logon token impersonation procedure is being performed. We'll be going
* to use the process' token to figure out if the process is actually
* restricted or not.
*/
ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess());
if (!ProcessToken)
{
DPRINT1("SepImpersonateAnonymousToken(): Couldn't be able to get the process' primary token, bail out...\n");
ObDereferenceObject(TokenToImpersonate);
return STATUS_UNSUCCESSFUL;
}
/* Now, is the token from the current process restricted? */
if (SeTokenIsRestricted(ProcessToken))
{
DPRINT1("SepImpersonateAnonymousToken(): The process is restricted, can't do anything. Bail out...\n");
PsDereferencePrimaryToken(ProcessToken);
ObDereferenceObject(TokenToImpersonate);
return STATUS_ACCESS_DENIED;
}
/*
* Finally it's time to impersonate! But first, fast dereference the
* process' primary token as we no longer need it.
*/
ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken);
Status = PsImpersonateClient(Thread, TokenToImpersonate, TRUE, FALSE, SecurityImpersonation);
if (!NT_SUCCESS(Status))
{
DPRINT1("SepImpersonateAnonymousToken(): Failed to impersonate, bail out...\n");
ObDereferenceObject(TokenToImpersonate);
return Status;
}
return Status;
}
/**
* @brief
* Updates the token's flags based upon the privilege that the token
* has been granted. The flag can either be taken out or given to the token
* if the attributes of the specified privilege is enabled or not.
*
* @param[in,out] Token
* The token where the flags are to be changed.
*
* @param[in] Index
* The index count which represents the total sum of privileges. The count in question
* MUST NOT exceed the expected privileges count of the token.
*
* @return
* Nothing.
*/
static
VOID
SepUpdateSinglePrivilegeFlagToken(
_Inout_ PTOKEN Token,
_In_ ULONG Index)
{
ULONG TokenFlag;
ASSERT(Index < Token->PrivilegeCount);
/* The high part of all values we are interested in is 0 */
if (Token->Privileges[Index].Luid.HighPart != 0)
{
return;
}
/* Check for certain privileges to update flags */
if (Token->Privileges[Index].Luid.LowPart == SE_CHANGE_NOTIFY_PRIVILEGE)
{
TokenFlag = TOKEN_HAS_TRAVERSE_PRIVILEGE;
}
else if (Token->Privileges[Index].Luid.LowPart == SE_BACKUP_PRIVILEGE)
{
TokenFlag = TOKEN_HAS_BACKUP_PRIVILEGE;
}
else if (Token->Privileges[Index].Luid.LowPart == SE_RESTORE_PRIVILEGE)
{
TokenFlag = TOKEN_HAS_RESTORE_PRIVILEGE;
}
else if (Token->Privileges[Index].Luid.LowPart == SE_IMPERSONATE_PRIVILEGE)
{
TokenFlag = TOKEN_HAS_IMPERSONATE_PRIVILEGE;
}
else
{
/* Nothing to do */
return;
}
/* Check if the specified privilege is enabled */
if (Token->Privileges[Index].Attributes & SE_PRIVILEGE_ENABLED)
{
/* It is enabled, so set the flag */
Token->TokenFlags |= TokenFlag;
}
else
{
/* Is is disabled, so remove the flag */
Token->TokenFlags &= ~TokenFlag;
}
}
/**
* @brief
* Updates the token's flags based upon the privilege that the token
* has been granted. The function uses the private helper, SepUpdateSinglePrivilegeFlagToken,
* in order to update the flags of a token.
*
* @param[in,out] Token
* The token where the flags are to be changed.
*
* @return
* Nothing.
*/
static
VOID
SepUpdatePrivilegeFlagsToken(
_Inout_ PTOKEN Token)
{
ULONG i;
/* Loop all privileges */
for (i = 0; i < Token->PrivilegeCount; i++)
{
/* Updates the flags for this privilege */
SepUpdateSinglePrivilegeFlagToken(Token, i);
}
}
/**
* @brief
* Removes a privilege from the token.
*
* @param[in,out] Token
* The token where the privilege is to be removed.
*
* @param[in] Index
* The index count which represents the number position of the privilege
* we want to remove.
*
* @return
* Nothing.
*/
static
VOID
SepRemovePrivilegeToken(
_Inout_ PTOKEN Token,
_In_ ULONG Index)
{
ULONG MoveCount;
ASSERT(Index < Token->PrivilegeCount);
/* Calculate the number of trailing privileges */
MoveCount = Token->PrivilegeCount - Index - 1;
if (MoveCount != 0)
{
/* Move them one location ahead */
RtlMoveMemory(&Token->Privileges[Index],
&Token->Privileges[Index + 1],
MoveCount * sizeof(LUID_AND_ATTRIBUTES));
}
/* Update privilege count */
Token->PrivilegeCount--;
}
/**
* @brief
* Removes a group from the token.
*
* @param[in,out] Token
* The token where the group is to be removed.
*
* @param[in] Index
* The index count which represents the number position of the group
* we want to remove.
*
* @return
* Nothing.
*/
static
VOID
SepRemoveUserGroupToken(
_Inout_ PTOKEN Token,
_In_ ULONG Index)
{
ULONG MoveCount;
ASSERT(Index < Token->UserAndGroupCount);
/* Calculate the number of trailing groups */
MoveCount = Token->UserAndGroupCount - Index - 1;
if (MoveCount != 0)
{
/* Time to remove the group by moving one location ahead */
RtlMoveMemory(&Token->UserAndGroups[Index],
&Token->UserAndGroups[Index + 1],
MoveCount * sizeof(SID_AND_ATTRIBUTES));
}
/* Remove one group count */
Token->UserAndGroupCount--;
}
/**
* @unimplemented
* @brief
* Frees (de-allocates) the proxy data memory block of a token.
*
* @param[in,out] ProxyData
* The proxy data to be freed.
*
* @return
* Nothing.
*/
VOID
NTAPI
SepFreeProxyData(
_Inout_ PVOID ProxyData)
{
UNIMPLEMENTED;
}
/**
* @unimplemented
* @brief
* Copies the proxy data from the source into the destination of a token.
*
* @param[out] Dest
* The destination path where the proxy data is to be copied to.
*
* @param[in] Src
* The source path where the proxy data is be copied from.
*
* @return
* To be added...
*/
NTSTATUS
NTAPI
SepCopyProxyData(
_Out_ PVOID* Dest,
_In_ PVOID Src)
{
UNIMPLEMENTED;
return STATUS_NOT_IMPLEMENTED;
}
/**
* @brief
* Replaces the old access token of a process (pointed by the EPROCESS kernel structure) with a
* new access token. The new access token must be a primary token for use.
*
* @param[in] Process
* The process instance where its access token is about to be replaced.
*
* @param[in] NewAccessToken
* The new token that it's going to replace the old one.
*
* @param[out] OldAccessToken
* The returned old token that's been replaced, which the caller can do anything.
*
* @return
* Returns STATUS_SUCCESS if the exchange operation between tokens has completed successfully.
* STATUS_BAD_TOKEN_TYPE is returned if the new token is not a primary one so that we cannot
* exchange it with the old one from the process. STATUS_TOKEN_ALREADY_IN_USE is returned if
* both tokens aren't equal which means one of them has different properties (groups, privileges, etc.)
* and as such one of them is currently in use. A failure NTSTATUS code is returned otherwise.
*/
NTSTATUS
NTAPI
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
SeExchangePrimaryToken(
_In_ PEPROCESS Process,
_In_ PACCESS_TOKEN NewAccessToken,
_Out_ PACCESS_TOKEN* OldAccessToken)
{
PTOKEN OldToken;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
PTOKEN NewToken = (PTOKEN)NewAccessToken;
PAGED_CODE();
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
if (NewToken->TokenType != TokenPrimary)
return STATUS_BAD_TOKEN_TYPE;
if (NewToken->TokenInUse)
{
BOOLEAN IsEqual;
NTSTATUS Status;
/* Maybe we're trying to set the same token */
OldToken = PsReferencePrimaryToken(Process);
if (OldToken == NewToken)
{
/* So it's a nop. */
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
*OldAccessToken = OldToken;
return STATUS_SUCCESS;
}
Status = SepCompareTokens(OldToken, NewToken, &IsEqual);
if (!NT_SUCCESS(Status))
{
PsDereferencePrimaryToken(OldToken);
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
*OldAccessToken = NULL;
return Status;
}
if (!IsEqual)
{
PsDereferencePrimaryToken(OldToken);
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
*OldAccessToken = NULL;
return STATUS_TOKEN_ALREADY_IN_USE;
}
/* Silently return STATUS_SUCCESS but do not set the new token,
* as it's already in use elsewhere. */
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
*OldAccessToken = OldToken;
return STATUS_SUCCESS;
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Lock the new token */
SepAcquireTokenLockExclusive(NewToken);
/* Mark new token in use */
NewToken->TokenInUse = TRUE;
/* Set the session ID for the new token */
NewToken->SessionId = MmGetSessionId(Process);
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Unlock the new token */
SepReleaseTokenLock(NewToken);
/* Reference the new token */
ObReferenceObject(NewToken);
/* Replace the old with the new */
OldToken = ObFastReplaceObject(&Process->Token, NewToken);
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Lock the old token */
SepAcquireTokenLockExclusive(OldToken);
/* Mark the old token as free */
OldToken->TokenInUse = FALSE;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Unlock the old token */
SepReleaseTokenLock(OldToken);
*OldAccessToken = (PACCESS_TOKEN)OldToken;
return STATUS_SUCCESS;
}
/**
* @brief
* Removes the primary token of a process.
*
* @param[in,out] Process
* The process instance with the access token to be removed.
*
* @return
* Nothing.
*/
VOID
NTAPI
SeDeassignPrimaryToken(
_Inout_ PEPROCESS Process)
{
PTOKEN OldToken;
/* Remove the Token */
OldToken = ObFastReplaceObject(&Process->Token, NULL);
/* Mark the Old Token as free */
OldToken->TokenInUse = FALSE;
/* Dereference the Token */
ObDereferenceObject(OldToken);
}
/**
* @brief
* Computes the length size of a SID.
*
* @param[in] Count
* Total count of entries that have SIDs in them (that being PSID_AND_ATTRIBUTES in this context).
*
* @param[in] Src
* Source that points to the attributes and SID entry structure.
*
* @return
* Returns the total length of a SID size.
*/
static ULONG
RtlLengthSidAndAttributes(
_In_ ULONG Count,
_In_ PSID_AND_ATTRIBUTES Src)
{
ULONG i;
ULONG uLength;
PAGED_CODE();
uLength = Count * sizeof(SID_AND_ATTRIBUTES);
for (i = 0; i < Count; i++)
uLength += RtlLengthSid(Src[i].Sid);
return uLength;
}
/**
* @brief
* Finds the primary group and default owner entity based on the submitted primary group instance
* and an access token.
*
* @param[in] Token
* Access token to begin the search query of primary group and default owner.
*
* @param[in] PrimaryGroup
* A primary group SID to be used for search query, determining if user & groups of a token
* and the submitted primary group do match.
*
* @param[in] DefaultOwner
* The default owner. If specified, it's used to determine if the token belongs to the actual user,
* that is, being the owner himself.
*
* @param[out] PrimaryGroupIndex
* Returns the primary group index.
*
* @param[out] DefaultOwnerIndex
* Returns the default owner index.
*
* @return
* Returns STATUS_SUCCESS if the find query operation has completed successfully and that at least one
* search result is requested by the caller. STATUS_INVALID_PARAMETER is returned if the caller hasn't requested
* any search result. STATUS_INVALID_OWNER is returned if the specified default user owner does not match with the other
* user from the token. STATUS_INVALID_PRIMARY_GROUP is returned if the specified default primary group does not match with the
* other group from the token.
*/
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
static NTSTATUS
SepFindPrimaryGroupAndDefaultOwner(
_In_ PTOKEN Token,
_In_ PSID PrimaryGroup,
_In_opt_ PSID DefaultOwner,
_Out_opt_ PULONG PrimaryGroupIndex,
_Out_opt_ PULONG DefaultOwnerIndex)
{
ULONG i;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* We should return at least a search result */
if (!PrimaryGroupIndex && !DefaultOwnerIndex)
return STATUS_INVALID_PARAMETER;
if (PrimaryGroupIndex)
{
/* Initialize with an invalid index */
// Token->PrimaryGroup = NULL;
*PrimaryGroupIndex = Token->UserAndGroupCount;
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
if (DefaultOwnerIndex)
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
if (DefaultOwner)
{
/* An owner is specified: check whether this is actually the user */
if (RtlEqualSid(Token->UserAndGroups[0].Sid, DefaultOwner))
{
/*
* It's the user (first element in array): set it
* as the owner and stop the search for it.
*/
*DefaultOwnerIndex = 0;
DefaultOwnerIndex = NULL;
}
else
{
/* An owner is specified: initialize with an invalid index */
*DefaultOwnerIndex = Token->UserAndGroupCount;
}
}
else
{
/*
* No owner specified: set the user (first element in array)
* as the owner and stop the search for it.
*/
*DefaultOwnerIndex = 0;
DefaultOwnerIndex = NULL;
}
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Validate and set the primary group and default owner indices */
for (i = 0; i < Token->UserAndGroupCount; i++)
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Stop the search if we have found what we searched for */
if (!PrimaryGroupIndex && !DefaultOwnerIndex)
break;
if (DefaultOwnerIndex && DefaultOwner &&
RtlEqualSid(Token->UserAndGroups[i].Sid, DefaultOwner) &&
(Token->UserAndGroups[i].Attributes & SE_GROUP_OWNER))
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Owner is found, stop the search for it */
*DefaultOwnerIndex = i;
DefaultOwnerIndex = NULL;
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
if (PrimaryGroupIndex &&
RtlEqualSid(Token->UserAndGroups[i].Sid, PrimaryGroup))
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Primary group is found, stop the search for it */
// Token->PrimaryGroup = Token->UserAndGroups[i].Sid;
*PrimaryGroupIndex = i;
PrimaryGroupIndex = NULL;
}
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
if (DefaultOwnerIndex)
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
if (*DefaultOwnerIndex == Token->UserAndGroupCount)
return STATUS_INVALID_OWNER;
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
if (PrimaryGroupIndex)
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
if (*PrimaryGroupIndex == Token->UserAndGroupCount)
// if (Token->PrimaryGroup == NULL)
return STATUS_INVALID_PRIMARY_GROUP;
}
return STATUS_SUCCESS;
}
/**
* @brief
* Duplicates an access token, from an existing valid token.
*
* @param[in] Token
* Access token to duplicate.
*
* @param[in] ObjectAttributes
* Object attributes for the new token.
*
* @param[in] EffectiveOnly
* If set to TRUE, the function removes all the disabled privileges and groups of the token
* to duplicate.
*
* @param[in] TokenType
* Type of token.
*
* @param[in] Level
* Security impersonation level of a token.
*
* @param[in] PreviousMode
* The processor request level mode.
*
* @param[out] NewAccessToken
* The duplicated token.
*
* @return
* Returns STATUS_SUCCESS if the token has been duplicated. STATUS_INSUFFICIENT_RESOURCES is returned
* if memory pool allocation of the dynamic part of the token for duplication has failed due to the lack
* of memory resources. A failure NTSTATUS code is returned otherwise.
*/
NTSTATUS
NTAPI
SepDuplicateToken(
_In_ PTOKEN Token,
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ BOOLEAN EffectiveOnly,
_In_ TOKEN_TYPE TokenType,
_In_ SECURITY_IMPERSONATION_LEVEL Level,
_In_ KPROCESSOR_MODE PreviousMode,
_Out_ PTOKEN* NewAccessToken)
{
NTSTATUS Status;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
PTOKEN AccessToken;
PVOID EndMem;
ULONG PrimaryGroupIndex;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
ULONG VariableLength;
ULONG TotalSize;
ULONG PrivilegesIndex, GroupsIndex;
PAGED_CODE();
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Compute how much size we need to allocate for the token */
VariableLength = Token->VariableLength;
TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
Status = ObCreateObject(PreviousMode,
SeTokenObjectType,
ObjectAttributes,
PreviousMode,
NULL,
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
TotalSize,
0,
0,
(PVOID*)&AccessToken);
if (!NT_SUCCESS(Status))
{
DPRINT1("ObCreateObject() failed (Status 0x%lx)\n", Status);
return Status;
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Zero out the buffer and initialize the token */
RtlZeroMemory(AccessToken, TotalSize);
ExAllocateLocallyUniqueId(&AccessToken->TokenId);
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
AccessToken->TokenType = TokenType;
AccessToken->ImpersonationLevel = Level;
/* Initialise the lock for the access token */
Status = SepCreateTokenLock(AccessToken);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(AccessToken);
return Status;
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Copy the immutable fields */
RtlCopyLuid(&AccessToken->TokenSource.SourceIdentifier,
&Token->TokenSource.SourceIdentifier);
RtlCopyMemory(AccessToken->TokenSource.SourceName,
Token->TokenSource.SourceName,
sizeof(Token->TokenSource.SourceName));
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
AccessToken->AuthenticationId = Token->AuthenticationId;
AccessToken->ParentTokenId = Token->ParentTokenId;
AccessToken->ExpirationTime = Token->ExpirationTime;
AccessToken->OriginatingLogonSession = Token->OriginatingLogonSession;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Lock the source token and copy the mutable fields */
SepAcquireTokenLockExclusive(Token);
AccessToken->SessionId = Token->SessionId;
RtlCopyLuid(&AccessToken->ModifiedId, &Token->ModifiedId);
AccessToken->TokenFlags = Token->TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
/* Reference the logon session */
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
if (!NT_SUCCESS(Status))
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* No logon session could be found, bail out */
DPRINT1("SepRmReferenceLogonSession() failed (Status 0x%lx)\n", Status);
/* Set the flag for proper cleanup by the delete procedure */
AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED;
goto Quit;
}
/* Insert the referenced logon session into the token */
Status = SepRmInsertLogonSessionIntoToken(AccessToken);
if (!NT_SUCCESS(Status))
{
/* Failed to insert the logon session into the token, bail out */
DPRINT1("SepRmInsertLogonSessionIntoToken() failed (Status 0x%lx)\n", Status);
goto Quit;
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Assign the data that reside in the TOKEN's variable information area */
AccessToken->VariableLength = VariableLength;
EndMem = (PVOID)&AccessToken->VariablePart;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Copy the privileges */
AccessToken->PrivilegeCount = 0;
AccessToken->Privileges = NULL;
if (Token->Privileges && (Token->PrivilegeCount > 0))
{
ULONG PrivilegesLength = Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID));
ASSERT(VariableLength >= PrivilegesLength);
AccessToken->PrivilegeCount = Token->PrivilegeCount;
AccessToken->Privileges = EndMem;
EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength);
VariableLength -= PrivilegesLength;
RtlCopyMemory(AccessToken->Privileges,
Token->Privileges,
AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
}
/* Copy the user and groups */
AccessToken->UserAndGroupCount = 0;
AccessToken->UserAndGroups = NULL;
if (Token->UserAndGroups && (Token->UserAndGroupCount > 0))
{
AccessToken->UserAndGroupCount = Token->UserAndGroupCount;
AccessToken->UserAndGroups = EndMem;
EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups);
Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount,
Token->UserAndGroups,
VariableLength,
AccessToken->UserAndGroups,
EndMem,
&EndMem,
&VariableLength);
if (!NT_SUCCESS(Status))
{
DPRINT1("RtlCopySidAndAttributesArray(UserAndGroups) failed (Status 0x%lx)\n", Status);
goto Quit;
}
}
/* Find the token primary group */
Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken,
Token->PrimaryGroup,
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
NULL,
&PrimaryGroupIndex,
NULL);
if (!NT_SUCCESS(Status))
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
DPRINT1("SepFindPrimaryGroupAndDefaultOwner failed (Status 0x%lx)\n", Status);
goto Quit;
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
AccessToken->PrimaryGroup = AccessToken->UserAndGroups[PrimaryGroupIndex].Sid;
AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Copy the restricted SIDs */
AccessToken->RestrictedSidCount = 0;
AccessToken->RestrictedSids = NULL;
if (Token->RestrictedSids && (Token->RestrictedSidCount > 0))
{
AccessToken->RestrictedSidCount = Token->RestrictedSidCount;
AccessToken->RestrictedSids = EndMem;
EndMem = &AccessToken->RestrictedSids[AccessToken->RestrictedSidCount];
VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->RestrictedSids);
Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount,
Token->RestrictedSids,
VariableLength,
AccessToken->RestrictedSids,
EndMem,
&EndMem,
&VariableLength);
if (!NT_SUCCESS(Status))
{
DPRINT1("RtlCopySidAndAttributesArray(RestrictedSids) failed (Status 0x%lx)\n", Status);
goto Quit;
}
}
/*
* Filter the token by removing the disabled privileges
* and groups if the caller wants to duplicate an access
* token as effective only.
*/
if (EffectiveOnly)
{
/* Begin querying the groups and search for disabled ones */
for (GroupsIndex = 0; GroupsIndex < AccessToken->UserAndGroupCount; GroupsIndex++)
{
/*
* A group or user is considered disabled if its attributes is either
* 0 or SE_GROUP_ENABLED is not included in the attributes flags list.
* That is because a certain user and/or group can have several attributes
* that bear no influence on whether a user/group is enabled or not
* (SE_GROUP_ENABLED_BY_DEFAULT for example which is a mere indicator
* that the group has just been enabled by default). A mandatory
* group (that is, the group has SE_GROUP_MANDATORY attribute)
* by standards it's always enabled and no one can disable it.
*/
if (AccessToken->UserAndGroups[GroupsIndex].Attributes == 0 ||
(AccessToken->UserAndGroups[GroupsIndex].Attributes & SE_GROUP_ENABLED) == 0)
{
/*
* If this group is an administrators group
* and the token belongs to such group,
* we've to take away TOKEN_HAS_ADMIN_GROUP
* for the fact that's not enabled and as
* such the token no longer belongs to
* this group.
*/
if (RtlEqualSid(SeAliasAdminsSid,
&AccessToken->UserAndGroups[GroupsIndex].Sid))
{
AccessToken->TokenFlags &= ~TOKEN_HAS_ADMIN_GROUP;
}
/*
* A group is not enabled, it's time to remove
* from the token and update the groups index
* accordingly and continue with the next group.
*/
SepRemoveUserGroupToken(AccessToken, GroupsIndex);
GroupsIndex--;
}
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Begin querying the privileges and search for disabled ones */
for (PrivilegesIndex = 0; PrivilegesIndex < AccessToken->PrivilegeCount; PrivilegesIndex++)
{
/*
* A privilege is considered disabled if its attributes is either
* 0 or SE_PRIVILEGE_ENABLED is not included in the attributes flags list.
* That is because a certain privilege can have several attributes
* that bear no influence on whether a privilege is enabled or not
* (SE_PRIVILEGE_ENABLED_BY_DEFAULT for example which is a mere indicator
* that the privilege has just been enabled by default).
*/
if (AccessToken->Privileges[PrivilegesIndex].Attributes == 0 ||
(AccessToken->Privileges[PrivilegesIndex].Attributes & SE_PRIVILEGE_ENABLED) == 0)
{
/*
* A privilege is not enabled, therefor it's time
* to strip it from the token and continue with the next
* privilege. Of course we must also want to update the
* privileges index accordingly.
*/
SepRemovePrivilegeToken(AccessToken, PrivilegesIndex);
PrivilegesIndex--;
}
}
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
//
// NOTE: So far our dynamic area only contains
// the default dacl, so this makes the following
// code pretty simple. The day where it stores
// other data, the code will require adaptations.
//
/* Now allocate the TOKEN's dynamic information area and set the data */
AccessToken->DynamicAvailable = 0; // Unused memory in the dynamic area.
AccessToken->DynamicPart = NULL;
if (Token->DynamicPart && Token->DefaultDacl)
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
Token->DefaultDacl->AclSize,
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
TAG_TOKEN_DYNAMIC);
if (AccessToken->DynamicPart == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
goto Quit;
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
EndMem = (PVOID)AccessToken->DynamicPart;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
AccessToken->DefaultDacl = EndMem;
RtlCopyMemory(AccessToken->DefaultDacl,
Token->DefaultDacl,
Token->DefaultDacl->AclSize);
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Unlock the source token */
SepReleaseTokenLock(Token);
/* Return the token */
*NewAccessToken = AccessToken;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
Status = STATUS_SUCCESS;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
Quit:
if (!NT_SUCCESS(Status))
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Unlock the source token */
SepReleaseTokenLock(Token);
/* Dereference the token, the delete procedure will clean it up */
ObDereferenceObject(AccessToken);
}
return Status;
}
/**
* @brief
* Subtracts a token in exchange of duplicating a new one.
*
* @param[in] ParentToken
* The parent access token for duplication.
*
* @param[out] Token
* The new duplicated token.
*
* @param[in] InUse
* Set this to TRUE if the token is about to be used immediately after the call execution
* of this function, FALSE otherwise.
*
* @param[in] SessionId
* Session ID for the token to be assigned.
*
* @return
* Returns STATUS_SUCCESS if token subtracting and duplication have completed successfully.
* A failure NTSTATUS code is returned otherwise.
*/
NTSTATUS
NTAPI
SeSubProcessToken(
_In_ PTOKEN ParentToken,
_Out_ PTOKEN *Token,
_In_ BOOLEAN InUse,
_In_ ULONG SessionId)
{
PTOKEN NewToken;
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS Status;
/* Initialize the attributes and duplicate it */
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
Status = SepDuplicateToken(ParentToken,
&ObjectAttributes,
FALSE,
TokenPrimary,
ParentToken->ImpersonationLevel,
KernelMode,
&NewToken);
if (NT_SUCCESS(Status))
{
/* Insert it */
Status = ObInsertObject(NewToken,
NULL,
0,
0,
NULL,
NULL);
if (NT_SUCCESS(Status))
{
/* Set the session ID */
NewToken->SessionId = SessionId;
NewToken->TokenInUse = InUse;
/* Return the token */
*Token = NewToken;
}
}
/* Return status */
return Status;
}
/**
* @brief
* Checks if the token is a child of the other token
* of the current process that the calling thread is invoking this function.
*
* @param[in] Token
* An access token to determine if it's a child or not.
*
* @param[out] IsChild
* The returned boolean result.
*
* @return
* Returns STATUS_SUCCESS when the function finishes its operation. STATUS_UNSUCCESSFUL is
* returned if primary token of the current calling process couldn't be referenced otherwise.
*/
NTSTATUS
NTAPI
SeIsTokenChild(
_In_ PTOKEN Token,
_Out_ PBOOLEAN IsChild)
{
PTOKEN ProcessToken;
LUID ProcessTokenId, CallerParentId;
/* Assume failure */
*IsChild = FALSE;
/* Reference the process token */
ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess());
if (!ProcessToken)
return STATUS_UNSUCCESSFUL;
/* Get its token ID */
ProcessTokenId = ProcessToken->TokenId;
/* Dereference the token */
ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken);
/* Get our parent token ID */
CallerParentId = Token->ParentTokenId;
/* Compare the token IDs */
if (RtlEqualLuid(&CallerParentId, &ProcessTokenId))
*IsChild = TRUE;
/* Return success */
return STATUS_SUCCESS;
}
/**
* @brief
* Checks if the token is a sibling of the other token of
* the current process that the calling thread is invoking this function.
*
* @param[in] Token
* An access token to determine if it's a sibling or not.
*
* @param[out] IsSibling
* The returned boolean result.
*
* @return
* Returns STATUS_SUCCESS when the function finishes its operation. STATUS_UNSUCCESSFUL is
* returned if primary token of the current calling process couldn't be referenced otherwise.
*/
NTSTATUS
NTAPI
SeIsTokenSibling(
_In_ PTOKEN Token,
_Out_ PBOOLEAN IsSibling)
{
PTOKEN ProcessToken;
LUID ProcessParentId, ProcessAuthId;
LUID CallerParentId, CallerAuthId;
/* Assume failure */
*IsSibling = FALSE;
/* Reference the process token */
ProcessToken = PsReferencePrimaryToken(PsGetCurrentProcess());
if (!ProcessToken)
return STATUS_UNSUCCESSFUL;
/* Get its parent and authentication IDs */
ProcessParentId = ProcessToken->ParentTokenId;
ProcessAuthId = ProcessToken->AuthenticationId;
/* Dereference the token */
ObFastDereferenceObject(&PsGetCurrentProcess()->Token, ProcessToken);
/* Get our parent and authentication IDs */
CallerParentId = Token->ParentTokenId;
CallerAuthId = Token->AuthenticationId;
/* Compare the token IDs */
if (RtlEqualLuid(&CallerParentId, &ProcessParentId) &&
RtlEqualLuid(&CallerAuthId, &ProcessAuthId))
{
*IsSibling = TRUE;
}
/* Return success */
return STATUS_SUCCESS;
}
/**
* @brief
* Copies an existing access token (technically duplicating a new one).
*
* @param[in] Token
* Token to copy.
*
* @param[in] Level
* Impersonation security level to assign to the newly copied token.
*
* @param[in] PreviousMode
* Processor request level mode.
*
* @param[out] NewToken
* The newly copied token.
*
* @return
* Returns STATUS_SUCCESS when token copying has finished successfully. A failure
* NTSTATUS code is returned otherwise.
*/
NTSTATUS
NTAPI
SeCopyClientToken(
_In_ PACCESS_TOKEN Token,
_In_ SECURITY_IMPERSONATION_LEVEL Level,
_In_ KPROCESSOR_MODE PreviousMode,
_Out_ PACCESS_TOKEN* NewToken)
{
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
PAGED_CODE();
InitializeObjectAttributes(&ObjectAttributes,
NULL,
0,
NULL,
NULL);
Status = SepDuplicateToken(Token,
&ObjectAttributes,
FALSE,
TokenImpersonation,
Level,
PreviousMode,
(PTOKEN*)NewToken);
return Status;
}
/**
* @brief
* Internal function that deals with access token object destruction and deletion.
* The function is used solely by the object manager mechanism that handles the life
* management of a token object.
*
* @param[in] ObjectBody
* The object body that represents an access token object.
*
* @return
* Nothing.
*/
VOID
NTAPI
SepDeleteToken(
_In_ PVOID ObjectBody)
{
NTSTATUS Status;
PTOKEN AccessToken = (PTOKEN)ObjectBody;
DPRINT("SepDeleteToken()\n");
/* Remove the referenced logon session from token */
if (AccessToken->LogonSession)
{
Status = SepRmRemoveLogonSessionFromToken(AccessToken);
if (!NT_SUCCESS(Status))
{
/* Something seriously went wrong */
DPRINT1("SepDeleteToken(): Failed to remove the logon session from token (Status: 0x%lx)\n", Status);
return;
}
}
/* Dereference the logon session */
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
if ((AccessToken->TokenFlags & TOKEN_SESSION_NOT_REFERENCED) == 0)
SepRmDereferenceLogonSession(&AccessToken->AuthenticationId);
/* Delete the token lock */
if (AccessToken->TokenLock)
SepDeleteTokenLock(AccessToken);
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Delete the dynamic information area */
if (AccessToken->DynamicPart)
ExFreePoolWithTag(AccessToken->DynamicPart, TAG_TOKEN_DYNAMIC);
}
/**
* @brief
* Internal function that initializes critical kernel data for access
* token implementation in SRM.
*
* @return
* Nothing.
*/
CODE_SEG("INIT")
VOID
NTAPI
SepInitializeTokenImplementation(VOID)
{
UNICODE_STRING Name;
OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
DPRINT("Creating Token Object Type\n");
/* Initialize the Token type */
RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
RtlInitUnicodeString(&Name, L"Token");
ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
ObjectTypeInitializer.SecurityRequired = TRUE;
ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(TOKEN);
ObjectTypeInitializer.GenericMapping = SepTokenMapping;
ObjectTypeInitializer.PoolType = PagedPool;
ObjectTypeInitializer.ValidAccessMask = TOKEN_ALL_ACCESS;
ObjectTypeInitializer.UseDefaultObject = TRUE;
ObjectTypeInitializer.DeleteProcedure = SepDeleteToken;
ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &SeTokenObjectType);
}
/**
* @brief
* Assigns a primary access token to a given process.
*
* @param[in] Process
* Process where the token is about to be assigned.
*
* @param[in] Token
* The token to be assigned.
*
* @return
* Nothing.
*/
VOID
NTAPI
SeAssignPrimaryToken(
_In_ PEPROCESS Process,
_In_ PTOKEN Token)
{
PAGED_CODE();
/* Sanity checks */
ASSERT(Token->TokenType == TokenPrimary);
ASSERT(!Token->TokenInUse);
/* Clean any previous token */
if (Process->Token.Object) SeDeassignPrimaryToken(Process);
/* Set the new token */
ObReferenceObject(Token);
Token->TokenInUse = TRUE;
ObInitializeFastReference(&Process->Token, Token);
}
/**
* @brief
* Internal function responsible for access token object creation in the kernel.
* A fully created token objected is inserted into the token handle, thus the handle
* becoming a valid handle to an access token object and ready for use.
*
* @param[out] TokenHandle
* Valid token handle that's ready for use after token creation and object insertion.
*
* @param[in] PreviousMode
* Processor request level mode.
*
* @param[in] DesiredAccess
* Desired access right for the token object to be granted. This kind of access right
* impacts how the token can be used and who.
*
* @param[in] ObjectAttributes
* Object attributes for the token to be created.
*
* @param[in] TokenType
* Type of token to assign upon creation.
*
* @param[in] ImpersonationLevel
* Security impersonation level of token to assign upon creation.
*
* @param[in] AuthenticationId
* Authentication ID that represents the authentication information of the token.
*
* @param[in] ExpirationTime
* Expiration time of the token to assign. A value of -1 means that the token never
* expires and its life depends upon the amount of references this token object has.
*
* @param[in] User
* User entry to assign to the token.
*
* @param[in] GroupCount
* The total number of groups count for the token.
*
* @param[in] Groups
* The group entries for the token.
*
* @param[in] GroupsLength
* The length size of the groups array, pointed by the Groups parameter.
*
* @param[in] PrivilegeCount
* The total number of priivleges that the newly created token has.
*
* @param[in] Privileges
* The privileges for the token.
*
* @param[in] Owner
* The main user (or also owner) that represents the token that we create.
*
* @param[in] PrimaryGroup
* The main group that represents the token that we create.
*
* @param[in] DefaultDacl
* A discretionary access control list for the token.
*
* @param[in] TokenSource
* Source (or the origin) of the access token that creates it.
*
* @param[in] SystemToken
* If set to TRUE, the newly created token is a system token and only in charge
* by the internal system. The function directly returns a pointer to the
* created token object for system kernel use. Otherwise if set to FALSE, the
* function inserts the object to a handle making it a regular access token.
*
* @return
* Returns STATUS_SUCCESS if token creation has completed successfully.
* STATUS_INSUFFICIENT_RESOURCES is returned if the dynamic area of memory of the
* token hasn't been allocated because of lack of memory resources. A failure
* NTSTATUS code is returned otherwise.
*/
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
NTSTATUS
NTAPI
SepCreateToken(
_Out_ PHANDLE TokenHandle,
_In_ KPROCESSOR_MODE PreviousMode,
_In_ ACCESS_MASK DesiredAccess,
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ TOKEN_TYPE TokenType,
_In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
_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_opt_ PSID Owner,
_In_ PSID PrimaryGroup,
_In_opt_ PACL DefaultDacl,
_In_ PTOKEN_SOURCE TokenSource,
_In_ BOOLEAN SystemToken)
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
NTSTATUS Status;
PTOKEN AccessToken;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
ULONG TokenFlags = 0;
ULONG PrimaryGroupIndex, DefaultOwnerIndex;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
LUID TokenId;
LUID ModifiedId;
PVOID EndMem;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
ULONG PrivilegesLength;
ULONG UserGroupsLength;
ULONG VariableLength;
ULONG TotalSize;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
ULONG i;
PAGED_CODE();
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
/* Loop all groups */
for (i = 0; i < GroupCount; i++)
{
/* Check for mandatory groups */
if (Groups[i].Attributes & SE_GROUP_MANDATORY)
{
/* Force them to be enabled */
Groups[i].Attributes |= (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT);
}
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
/* Check of the group is an admin group */
if (RtlEqualSid(SeAliasAdminsSid, Groups[i].Sid))
{
/* Remember this so we can optimize queries later */
TokenFlags |= TOKEN_HAS_ADMIN_GROUP;
}
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Allocate unique IDs for the token */
ExAllocateLocallyUniqueId(&TokenId);
ExAllocateLocallyUniqueId(&ModifiedId);
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Compute how much size we need to allocate for the token */
/* Privileges size */
PrivilegesLength = PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID));
/* User and groups size */
UserGroupsLength = (1 + GroupCount) * sizeof(SID_AND_ATTRIBUTES);
UserGroupsLength += RtlLengthSid(User->Sid);
for (i = 0; i < GroupCount; i++)
{
UserGroupsLength += RtlLengthSid(Groups[i].Sid);
}
UserGroupsLength = ALIGN_UP_BY(UserGroupsLength, sizeof(PVOID));
/* Add the additional groups array length */
UserGroupsLength += ALIGN_UP_BY(GroupsLength, sizeof(PVOID));
VariableLength = PrivilegesLength + UserGroupsLength;
TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Status = ObCreateObject(PreviousMode,
SeTokenObjectType,
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
ObjectAttributes,
PreviousMode,
NULL,
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
TotalSize,
0,
0,
(PVOID*)&AccessToken);
if (!NT_SUCCESS(Status))
{
DPRINT1("ObCreateObject() failed (Status 0x%lx)\n", Status);
return Status;
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Zero out the buffer and initialize the token */
RtlZeroMemory(AccessToken, TotalSize);
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
RtlCopyLuid(&AccessToken->TokenId, &TokenId);
AccessToken->TokenType = TokenType;
AccessToken->ImpersonationLevel = ImpersonationLevel;
/* Initialise the lock for the access token */
Status = SepCreateTokenLock(AccessToken);
if (!NT_SUCCESS(Status))
goto Quit;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
RtlCopyLuid(&AccessToken->TokenSource.SourceIdentifier,
&TokenSource->SourceIdentifier);
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
RtlCopyMemory(AccessToken->TokenSource.SourceName,
TokenSource->SourceName,
sizeof(TokenSource->SourceName));
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
AccessToken->ExpirationTime = *ExpirationTime;
RtlCopyLuid(&AccessToken->ModifiedId, &ModifiedId);
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
AccessToken->TokenFlags = TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Copy and reference the logon session */
RtlCopyLuid(&AccessToken->AuthenticationId, AuthenticationId);
Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
if (!NT_SUCCESS(Status))
{
/* No logon session could be found, bail out */
DPRINT1("SepRmReferenceLogonSession() failed (Status 0x%lx)\n", Status);
/* Set the flag for proper cleanup by the delete procedure */
AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED;
goto Quit;
}
/* Insert the referenced logon session into the token */
Status = SepRmInsertLogonSessionIntoToken(AccessToken);
if (!NT_SUCCESS(Status))
{
/* Failed to insert the logon session into the token, bail out */
DPRINT1("SepRmInsertLogonSessionIntoToken() failed (Status 0x%lx)\n", Status);
goto Quit;
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Assign the data that reside in the TOKEN's variable information area */
AccessToken->VariableLength = VariableLength;
EndMem = (PVOID)&AccessToken->VariablePart;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Copy the privileges */
AccessToken->PrivilegeCount = PrivilegeCount;
AccessToken->Privileges = NULL;
if (PrivilegeCount > 0)
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
AccessToken->Privileges = EndMem;
EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength);
VariableLength -= PrivilegesLength;
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
RtlCopyMemory(AccessToken->Privileges,
Privileges,
PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
else
{
RtlCopyMemory(AccessToken->Privileges,
Privileges,
PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
}
if (!NT_SUCCESS(Status))
goto Quit;
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Update the privilege flags */
SepUpdatePrivilegeFlagsToken(AccessToken);
/* Copy the user and groups */
AccessToken->UserAndGroupCount = 1 + GroupCount;
AccessToken->UserAndGroups = EndMem;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups);
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Status = RtlCopySidAndAttributesArray(1,
User,
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
VariableLength,
&AccessToken->UserAndGroups[0],
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
EndMem,
&EndMem,
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
&VariableLength);
if (!NT_SUCCESS(Status))
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
goto Quit;
Status = RtlCopySidAndAttributesArray(GroupCount,
Groups,
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
VariableLength,
&AccessToken->UserAndGroups[1],
EndMem,
&EndMem,
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
&VariableLength);
if (!NT_SUCCESS(Status))
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
goto Quit;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Find the token primary group and default owner */
Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken,
PrimaryGroup,
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
Owner,
&PrimaryGroupIndex,
&DefaultOwnerIndex);
if (!NT_SUCCESS(Status))
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
DPRINT1("SepFindPrimaryGroupAndDefaultOwner failed (Status 0x%lx)\n", Status);
goto Quit;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
AccessToken->PrimaryGroup = AccessToken->UserAndGroups[PrimaryGroupIndex].Sid;
AccessToken->DefaultOwnerIndex = DefaultOwnerIndex;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Now allocate the TOKEN's dynamic information area and set the data */
AccessToken->DynamicAvailable = 0; // Unused memory in the dynamic area.
AccessToken->DynamicPart = NULL;
if (DefaultDacl != NULL)
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
DefaultDacl->AclSize,
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
TAG_TOKEN_DYNAMIC);
if (AccessToken->DynamicPart == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
goto Quit;
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
EndMem = (PVOID)AccessToken->DynamicPart;
AccessToken->DefaultDacl = EndMem;
RtlCopyMemory(AccessToken->DefaultDacl,
DefaultDacl,
DefaultDacl->AclSize);
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Insert the token only if it's not the system token, otherwise return it directly */
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
if (!SystemToken)
{
Status = ObInsertObject(AccessToken,
NULL,
DesiredAccess,
0,
NULL,
TokenHandle);
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
if (!NT_SUCCESS(Status))
{
DPRINT1("ObInsertObject() failed (Status 0x%lx)\n", Status);
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
}
}
else
{
/* Return pointer instead of handle */
*TokenHandle = (HANDLE)AccessToken;
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
Quit:
if (!NT_SUCCESS(Status))
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Dereference the token, the delete procedure will clean it up */
ObDereferenceObject(AccessToken);
}
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
return Status;
}
/**
* @brief
* Private helper function responsible for creating a restricted access
* token, that is, a filtered token from privileges and groups and with
* restricted SIDs added into the token on demand by the caller.
*
* @param[in] Token
* An existing and valid access token.
*
* @param[in] PrivilegesToBeDeleted
* A list of privileges to be deleted within the token that's going
* to be filtered. This parameter is ignored if the caller wants to disable
* all the privileges by specifying DISABLE_MAX_PRIVILEGE in the flags
* parameter.
*
* @param[in] SidsToBeDisabled
* A list of group SIDs to be disabled within the token. This parameter
* can be NULL.
*
* @param[in] RestrictedSidsIntoToken
* A list of restricted SIDs to be added into the token. This parameter
* can be NULL.
*
* @param[in] PrivilegesCount
* The privilege count of the privileges list.
*
* @param[in] RegularGroupsSidCount
* The SIDs count of the group SIDs list.
*
* @param[in] RestrictedSidsCount
* The restricted SIDs count of restricted SIDs list.
*
* @param[in] PrivilegeFlags
* Influences how the privileges should be filtered in an access
* token. See NtFilterToken syscall for more information.
*
* @param[in] PreviousMode
* Processor level access mode.
*
* @param[out] FilteredToken
* The filtered token, returned to the caller.
*
* @return
* Returns STATUS_SUCCESS if token token filtering has completed successfully.
* STATUS_INVALID_PARAMETER is returned if one or more of the parameters
* do not meet the conditions imposed by the function. A failure NTSTATUS
* code is returned otherwise.
*
* @remarks
* The final outcome of privileges and/or SIDs filtering is not always
* deterministic. That is, any privileges or SIDs that aren't present
* in the access token are ignored and the function continues with the
* next privilege or SID to find for filtering. For a fully deterministic
* outcome the caller is responsible for querying the information details
* of privileges and SIDs present in the token and then afterwards use
* such obtained information to do any kind of filtering to the token.
*/
static
NTSTATUS
SepPerformTokenFiltering(
_In_ PTOKEN Token,
_In_opt_ PLUID_AND_ATTRIBUTES PrivilegesToBeDeleted,
_In_opt_ PSID_AND_ATTRIBUTES SidsToBeDisabled,
_In_opt_ PSID_AND_ATTRIBUTES RestrictedSidsIntoToken,
_When_(PrivilegesToBeDeleted != NULL, _In_) ULONG PrivilegesCount,
_When_(SidsToBeDisabled != NULL, _In_) ULONG RegularGroupsSidCount,
_When_(RestrictedSidsIntoToken != NULL, _In_) ULONG RestrictedSidsCount,
_In_ ULONG PrivilegeFlags,
_In_ KPROCESSOR_MODE PreviousMode,
_Out_ PTOKEN *FilteredToken)
{
PTOKEN AccessToken;
NTSTATUS Status;
PVOID EndMem;
ULONG RestrictedSidsLength;
ULONG PrivilegesLength;
ULONG PrimaryGroupIndex;
ULONG RestrictedSidsInList;
ULONG RestrictedSidsInToken;
ULONG VariableLength, TotalSize;
ULONG PrivsInToken, PrivsInList;
ULONG GroupsInToken, GroupsInList;
BOOLEAN WantPrivilegesDisabled;
BOOLEAN FoundPrivilege;
BOOLEAN FoundGroup;
PAGED_CODE();
/* Ensure that the token we get is not garbage */
ASSERT(Token);
/* Assume the caller doesn't want privileges disabled */
WantPrivilegesDisabled = FALSE;
/* Assume we haven't found anything */
FoundPrivilege = FALSE;
FoundGroup = FALSE;
/*
* Take the size that we need for filtered token
* allocation based upon the existing access token
* we've been given.
*/
VariableLength = Token->VariableLength;
if (RestrictedSidsIntoToken != NULL)
{
/*
* If the caller provided a list of restricted SIDs
* to be added onto the filtered access token then
* we must compute the size which is the total space
* of the current token and the length of the restricted
* SIDs for the filtered token.
*/
RestrictedSidsLength = RestrictedSidsCount * sizeof(SID_AND_ATTRIBUTES);
RestrictedSidsLength += RtlLengthSidAndAttributes(RestrictedSidsCount, RestrictedSidsIntoToken);
RestrictedSidsLength = ALIGN_UP_BY(RestrictedSidsLength, sizeof(PVOID));
/*
* The variable length of the token is not just
* the actual space length of the existing token
* but also the sum of the restricted SIDs length.
*/
VariableLength += RestrictedSidsLength;
TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength + RestrictedSidsLength;
}
else
{
/* Otherwise the size is of the actual current token */
TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
}
/* Set up a filtered token object */
Status = ObCreateObject(PreviousMode,
SeTokenObjectType,
NULL,
PreviousMode,
NULL,
TotalSize,
0,
0,
(PVOID*)&AccessToken);
if (!NT_SUCCESS(Status))
{
DPRINT1("SepPerformTokenFiltering(): Failed to create the filtered token object (Status 0x%lx)\n", Status);
return Status;
}
/* Initialize the token and begin filling stuff to it */
RtlZeroMemory(AccessToken, TotalSize);
/* Set up a lock for the new token */
Status = SepCreateTokenLock(AccessToken);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(AccessToken);
return Status;
}
/* Allocate new IDs for the token */
ExAllocateLocallyUniqueId(&AccessToken->TokenId);
ExAllocateLocallyUniqueId(&AccessToken->ModifiedId);
/* Copy the type and impersonation level from the token */
AccessToken->TokenType = Token->TokenType;
AccessToken->ImpersonationLevel = Token->ImpersonationLevel;
/* Copy the immutable fields */
RtlCopyLuid(&AccessToken->TokenSource.SourceIdentifier,
&Token->TokenSource.SourceIdentifier);
RtlCopyMemory(AccessToken->TokenSource.SourceName,
Token->TokenSource.SourceName,
sizeof(Token->TokenSource.SourceName));
RtlCopyLuid(&AccessToken->AuthenticationId, &Token->AuthenticationId);
RtlCopyLuid(&AccessToken->ParentTokenId, &Token->TokenId);
RtlCopyLuid(&AccessToken->OriginatingLogonSession,
&Token->OriginatingLogonSession);
AccessToken->ExpirationTime = Token->ExpirationTime;
/* Copy the mutable fields */
AccessToken->SessionId = Token->SessionId;
AccessToken->TokenFlags = Token->TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
/* Reference the logon session */
Status = SepRmReferenceLogonSession(&AccessToken->AuthenticationId);
if (!NT_SUCCESS(Status))
{
/* We failed, bail out*/
DPRINT1("SepPerformTokenFiltering(): Failed to reference the logon session (Status 0x%lx)\n", Status);
AccessToken->TokenFlags |= TOKEN_SESSION_NOT_REFERENCED;
goto Quit;
}
/* Insert the referenced logon session into the token */
Status = SepRmInsertLogonSessionIntoToken(AccessToken);
if (!NT_SUCCESS(Status))
{
/* Failed to insert the logon session into the token, bail out */
DPRINT1("SepPerformTokenFiltering(): Failed to insert the logon session into token (Status 0x%lx)\n", Status);
goto Quit;
}
/* Assign the data that reside in the token's variable information area */
AccessToken->VariableLength = VariableLength;
EndMem = (PVOID)&AccessToken->VariablePart;
/* Copy the privileges from the existing token */
AccessToken->PrivilegeCount = 0;
AccessToken->Privileges = NULL;
if (Token->Privileges && (Token->PrivilegeCount > 0))
{
PrivilegesLength = Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
PrivilegesLength = ALIGN_UP_BY(PrivilegesLength, sizeof(PVOID));
/*
* Ensure that the token can actually hold all
* the privileges from the existing token.
* Otherwise something's seriously wrong and
* we've to guard ourselves.
*/
ASSERT(VariableLength >= PrivilegesLength);
AccessToken->PrivilegeCount = Token->PrivilegeCount;
AccessToken->Privileges = EndMem;
EndMem = (PVOID)((ULONG_PTR)EndMem + PrivilegesLength);
VariableLength -= PrivilegesLength;
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
RtlCopyMemory(AccessToken->Privileges,
Token->Privileges,
AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(goto Quit);
}
_SEH2_END;
}
else
{
RtlCopyMemory(AccessToken->Privileges,
Token->Privileges,
AccessToken->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
}
}
/* Copy the user and groups */
AccessToken->UserAndGroupCount = 0;
AccessToken->UserAndGroups = NULL;
if (Token->UserAndGroups && (Token->UserAndGroupCount > 0))
{
AccessToken->UserAndGroupCount = Token->UserAndGroupCount;
AccessToken->UserAndGroups = EndMem;
EndMem = &AccessToken->UserAndGroups[AccessToken->UserAndGroupCount];
VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->UserAndGroups);
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount,
Token->UserAndGroups,
VariableLength,
AccessToken->UserAndGroups,
EndMem,
&EndMem,
&VariableLength);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(goto Quit);
}
_SEH2_END;
}
else
{
Status = RtlCopySidAndAttributesArray(AccessToken->UserAndGroupCount,
Token->UserAndGroups,
VariableLength,
AccessToken->UserAndGroups,
EndMem,
&EndMem,
&VariableLength);
if (!NT_SUCCESS(Status))
{
DPRINT1("SepPerformTokenFiltering(): Failed to copy the groups into token (Status 0x%lx)\n", Status);
goto Quit;
}
}
}
/* Copy the restricted SIDs */
AccessToken->RestrictedSidCount = 0;
AccessToken->RestrictedSids = NULL;
if (Token->RestrictedSids && (Token->RestrictedSidCount > 0))
{
AccessToken->RestrictedSidCount = Token->RestrictedSidCount;
AccessToken->RestrictedSids = EndMem;
EndMem = &AccessToken->RestrictedSids[AccessToken->RestrictedSidCount];
VariableLength -= ((ULONG_PTR)EndMem - (ULONG_PTR)AccessToken->RestrictedSids);
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount,
Token->RestrictedSids,
VariableLength,
AccessToken->RestrictedSids,
EndMem,
&EndMem,
&VariableLength);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(goto Quit);
}
_SEH2_END;
}
else
{
Status = RtlCopySidAndAttributesArray(AccessToken->RestrictedSidCount,
Token->RestrictedSids,
VariableLength,
AccessToken->RestrictedSids,
EndMem,
&EndMem,
&VariableLength);
if (!NT_SUCCESS(Status))
{
DPRINT1("SepPerformTokenFiltering(): Failed to copy the restricted SIDs into token (Status 0x%lx)\n", Status);
goto Quit;
}
}
}
/* Search for the primary group */
Status = SepFindPrimaryGroupAndDefaultOwner(AccessToken,
Token->PrimaryGroup,
NULL,
&PrimaryGroupIndex,
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("SepPerformTokenFiltering(): Failed searching for the primary group (Status 0x%lx)\n", Status);
goto Quit;
}
/* Assign the primary group and default owner index now */
AccessToken->PrimaryGroup = AccessToken->UserAndGroups[PrimaryGroupIndex].Sid;
AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex;
/* Now allocate the token's dynamic information area and set the data */
AccessToken->DynamicAvailable = 0;
AccessToken->DynamicPart = NULL;
if (Token->DynamicPart && Token->DefaultDacl)
{
AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
Token->DefaultDacl->AclSize,
TAG_TOKEN_DYNAMIC);
if (AccessToken->DynamicPart == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Quit;
}
EndMem = (PVOID)AccessToken->DynamicPart;
AccessToken->DefaultDacl = EndMem;
RtlCopyMemory(AccessToken->DefaultDacl,
Token->DefaultDacl,
Token->DefaultDacl->AclSize);
}
/*
* Now figure out what does the caller
* want with the privileges.
*/
if (PrivilegeFlags & DISABLE_MAX_PRIVILEGE)
{
/*
* The caller wants them disabled, cache this request
* for later operations.
*/
WantPrivilegesDisabled = TRUE;
}
if (PrivilegeFlags & SANDBOX_INERT)
{
/* The caller wants an inert token, store the TOKEN_SANDBOX_INERT flag now */
AccessToken->TokenFlags |= TOKEN_SANDBOX_INERT;
}
/*
* Now it's time to filter the token's privileges.
* Loop all the privileges in the token.
*/
for (PrivsInToken = 0; PrivsInToken < AccessToken->PrivilegeCount; PrivsInToken++)
{
if (WantPrivilegesDisabled)
{
/*
* We got the acknowledgement that the caller wants
* to disable all the privileges so let's just do it.
* However, as per the general documentation is stated
* that only SE_CHANGE_NOTIFY_PRIVILEGE must be kept
* therefore in that case we must skip this privilege.
*/
if (AccessToken->Privileges[PrivsInToken].Luid.LowPart == SE_CHANGE_NOTIFY_PRIVILEGE)
{
continue;
}
else
{
/*
* The act of disabling privileges actually means
* "deleting" them from the access token entirely.
* First we must disable them so that we can update
* token flags accordingly.
*/
AccessToken->Privileges[PrivsInToken].Attributes &= ~SE_PRIVILEGE_ENABLED;
SepUpdateSinglePrivilegeFlagToken(AccessToken, PrivsInToken);
/* Remove the privileges now */
SepRemovePrivilegeToken(AccessToken, PrivsInToken);
PrivsInToken--;
}
}
else
{
if (PrivilegesToBeDeleted != NULL)
{
/* Loop the privileges we've got to delete */
for (PrivsInList = 0; PrivsInList < PrivilegesCount; PrivsInList++)
{
/* Does this privilege exist in the token? */
if (RtlEqualLuid(&AccessToken->Privileges[PrivsInToken].Luid,
&PrivilegesToBeDeleted[PrivsInList].Luid))
{
/* Mark that we found it */
FoundPrivilege = TRUE;
break;
}
}
/* Did we find the privilege? */
if (PrivsInList == PrivilegesCount)
{
/* We didn't, continue with next one */
continue;
}
}
}
/*
* If we have found the target privilege in the token
* based on the privileges list given by the caller
* then begin deleting it.
*/
if (FoundPrivilege)
{
/* Disable the privilege and update the flags */
AccessToken->Privileges[PrivsInToken].Attributes &= ~SE_PRIVILEGE_ENABLED;
SepUpdateSinglePrivilegeFlagToken(AccessToken, PrivsInToken);
/* Delete the privilege */
SepRemovePrivilegeToken(AccessToken, PrivsInToken);
/*
* Adjust the index and reset the FoundPrivilege indicator
* so that we can continue with the next privilege to delete.
*/
PrivsInToken--;
FoundPrivilege = FALSE;
continue;
}
}
/*
* Loop the group SIDs that we want to disable as
* per on the request by the caller.
*/
if (SidsToBeDisabled != NULL)
{
for (GroupsInToken = 0; GroupsInToken < AccessToken->UserAndGroupCount; GroupsInToken++)
{
for (GroupsInList = 0; GroupsInList < RegularGroupsSidCount; GroupsInList++)
{
/* Does this group SID exist in the token? */
if (RtlEqualSid(&AccessToken->UserAndGroups[GroupsInToken].Sid,
&SidsToBeDisabled[GroupsInList].Sid))
{
/* Mark that we found it */
FoundGroup = TRUE;
break;
}
}
/* Did we find the group? */
if (GroupsInList == RegularGroupsSidCount)
{
/* We didn't, continue with next one */
continue;
}
/* If we have found the group, disable it */
if (FoundGroup)
{
/*
* If the acess token belongs to the administrators
* group and this is the target group, we must take
* away TOKEN_HAS_ADMIN_GROUP flag from the token.
*/
if (RtlEqualSid(SeAliasAdminsSid,
&AccessToken->UserAndGroups[GroupsInToken].Sid))
{
AccessToken->TokenFlags &= ~TOKEN_HAS_ADMIN_GROUP;
}
/*
* If the target group that we have found it is the
* owner then from now on it no longer is but the user.
* Therefore assign the default owner index as the user.
*/
if (AccessToken->DefaultOwnerIndex == GroupsInToken)
{
AccessToken->DefaultOwnerIndex = 0;
}
/*
* The principle of disabling a group SID is by
* taking away SE_GROUP_ENABLED_BY_DEFAULT and
* SE_GROUP_ENABLED attributes and assign
* SE_GROUP_USE_FOR_DENY_ONLY. This renders
* SID a "Deny only" SID.
*/
AccessToken->UserAndGroups[GroupsInToken].Attributes &= ~(SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT);
AccessToken->UserAndGroups[GroupsInToken].Attributes |= SE_GROUP_USE_FOR_DENY_ONLY;
/* Adjust the index and continue with the next group */
GroupsInToken--;
FoundGroup = FALSE;
continue;
}
}
}
/*
* Insert the restricted SIDs into the token on
* the request by the caller.
*/
if (RestrictedSidsIntoToken != NULL)
{
for (RestrictedSidsInList = 0; RestrictedSidsInList < RestrictedSidsCount; RestrictedSidsInList++)
{
/* Did the caller assign attributes to the restricted SIDs? */
if (RestrictedSidsIntoToken[RestrictedSidsInList].Attributes != 0)
{
/* There mustn't be any attributes, bail out */
DPRINT1("SepPerformTokenFiltering(): There mustn't be any attributes to restricted SIDs!\n");
Status = STATUS_INVALID_PARAMETER;
goto Quit;
}
}
/*
* Ensure that the token can hold the restricted SIDs
* (the variable length is calculated at the beginning
* of the routine call).
*/
ASSERT(VariableLength >= RestrictedSidsLength);
/*
* Now let's begin inserting the restricted SIDs into the filtered
* access token from the list the caller gave us.
*/
AccessToken->RestrictedSidCount = RestrictedSidsCount;
AccessToken->RestrictedSids = EndMem;
EndMem = (PVOID)((ULONG_PTR)EndMem + RestrictedSidsLength);
VariableLength -= RestrictedSidsLength;
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
RtlCopyMemory(AccessToken->RestrictedSids,
RestrictedSidsIntoToken,
AccessToken->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(goto Quit);
}
_SEH2_END;
}
else
{
RtlCopyMemory(AccessToken->RestrictedSids,
RestrictedSidsIntoToken,
AccessToken->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES));
}
/*
* As we've copied the restricted SIDs into
* the token, we must assign them the following
* combination of attributes SE_GROUP_ENABLED,
* SE_GROUP_ENABLED_BY_DEFAULT and SE_GROUP_MANDATORY.
* With such attributes we estabilish that restricting
* SIDs into the token are enabled for access checks.
*/
for (RestrictedSidsInToken = 0; RestrictedSidsInToken < AccessToken->RestrictedSidCount; RestrictedSidsInToken++)
{
AccessToken->RestrictedSids[RestrictedSidsInToken].Attributes |= (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY);
}
/*
* As we added restricted SIDs into the token, mark
* it as restricted.
*/
AccessToken->TokenFlags |= TOKEN_IS_RESTRICTED;
}
/* We've finally filtered the token, give it to the caller */
*FilteredToken = AccessToken;
Status = STATUS_SUCCESS;
DPRINT("SepPerformTokenFiltering(): The token has been filtered!\n");
Quit:
if (!NT_SUCCESS(Status))
{
/* Dereference the token */
ObDereferenceObject(AccessToken);
}
return Status;
}
/**
* @brief
* Creates the system process token.
*
* @return
* Returns the system process token if the operations have
* completed successfully.
*/
CODE_SEG("INIT")
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
PTOKEN
NTAPI
SepCreateSystemProcessToken(VOID)
{
LUID_AND_ATTRIBUTES Privileges[25];
ULONG GroupAttributes, OwnerAttributes;
SID_AND_ATTRIBUTES Groups[32];
LARGE_INTEGER Expiration;
SID_AND_ATTRIBUTES UserSid;
ULONG GroupsLength;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
PSID PrimaryGroup;
OBJECT_ATTRIBUTES ObjectAttributes;
PSID Owner;
ULONG i;
PTOKEN Token;
NTSTATUS Status;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
/* Don't ever expire */
Expiration.QuadPart = -1;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
/* All groups mandatory and enabled */
GroupAttributes = SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT;
OwnerAttributes = SE_GROUP_ENABLED | SE_GROUP_OWNER | SE_GROUP_ENABLED_BY_DEFAULT;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* User is Local System */
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
UserSid.Sid = SeLocalSystemSid;
UserSid.Attributes = 0;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Primary group is Local System */
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
PrimaryGroup = SeLocalSystemSid;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Owner is Administrators */
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Owner = SeAliasAdminsSid;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Groups are Administrators, World, and Authenticated Users */
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Groups[0].Sid = SeAliasAdminsSid;
Groups[0].Attributes = OwnerAttributes;
Groups[1].Sid = SeWorldSid;
Groups[1].Attributes = GroupAttributes;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
Groups[2].Sid = SeAuthenticatedUsersSid;
Groups[2].Attributes = GroupAttributes;
GroupsLength = sizeof(SID_AND_ATTRIBUTES) +
SeLengthSid(Groups[0].Sid) +
SeLengthSid(Groups[1].Sid) +
SeLengthSid(Groups[2].Sid);
ASSERT(GroupsLength <= sizeof(Groups));
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
/* Setup the privileges */
i = 0;
Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
Privileges[i++].Luid = SeTcbPrivilege;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Privileges[i].Attributes = 0;
Privileges[i++].Luid = SeCreateTokenPrivilege;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Privileges[i].Attributes = 0;
Privileges[i++].Luid = SeTakeOwnershipPrivilege;
Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Privileges[i++].Luid = SeCreatePagefilePrivilege;
Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Privileges[i++].Luid = SeLockMemoryPrivilege;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Privileges[i].Attributes = 0;
Privileges[i++].Luid = SeAssignPrimaryTokenPrivilege;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Privileges[i].Attributes = 0;
Privileges[i++].Luid = SeIncreaseQuotaPrivilege;
Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Privileges[i++].Luid = SeIncreaseBasePriorityPrivilege;
Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Privileges[i++].Luid = SeCreatePermanentPrivilege;
Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Privileges[i++].Luid = SeDebugPrivilege;
Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Privileges[i++].Luid = SeAuditPrivilege;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Privileges[i].Attributes = 0;
Privileges[i++].Luid = SeSecurityPrivilege;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Privileges[i].Attributes = 0;
Privileges[i++].Luid = SeSystemEnvironmentPrivilege;
Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Privileges[i++].Luid = SeChangeNotifyPrivilege;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Privileges[i].Attributes = 0;
Privileges[i++].Luid = SeBackupPrivilege;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Privileges[i].Attributes = 0;
Privileges[i++].Luid = SeRestorePrivilege;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Privileges[i].Attributes = 0;
Privileges[i++].Luid = SeShutdownPrivilege;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Privileges[i].Attributes = 0;
Privileges[i++].Luid = SeLoadDriverPrivilege;
Privileges[i].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_ENABLED;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Privileges[i++].Luid = SeProfileSingleProcessPrivilege;
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Privileges[i].Attributes = 0;
Privileges[i++].Luid = SeSystemtimePrivilege;
ASSERT(i == 20);
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
/* Setup the object attributes */
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
ASSERT(SeSystemDefaultDacl != NULL);
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
/* Create the token */
Status = SepCreateToken((PHANDLE)&Token,
KernelMode,
0,
&ObjectAttributes,
TokenPrimary,
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
SecurityAnonymous,
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
&SeSystemAuthenticationId,
&Expiration,
&UserSid,
3,
Groups,
GroupsLength,
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
20,
Privileges,
Owner,
PrimaryGroup,
SeSystemDefaultDacl,
&SeSystemTokenSource,
TRUE);
ASSERT(Status == STATUS_SUCCESS);
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
/* Return the token */
return Token;
}
/**
* @brief
* Creates the anonymous logon token for the system. The difference between this
* token and the other one is the inclusion of everyone SID group (being SeWorldSid).
* The other token lacks such group.
*
* @return
* Returns the system's anonymous logon token if the operations have
* completed successfully.
*/
CODE_SEG("INIT")
PTOKEN
SepCreateSystemAnonymousLogonToken(VOID)
{
SID_AND_ATTRIBUTES Groups[32], UserSid;
PSID PrimaryGroup;
PTOKEN Token;
ULONG GroupsLength;
LARGE_INTEGER Expiration;
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS Status;
/* The token never expires */
Expiration.QuadPart = -1;
/* The user is the anonymous logon */
UserSid.Sid = SeAnonymousLogonSid;
UserSid.Attributes = 0;
/* The primary group is also the anonymous logon */
PrimaryGroup = SeAnonymousLogonSid;
/* The only group for the token is the World */
Groups[0].Sid = SeWorldSid;
Groups[0].Attributes = SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT;
GroupsLength = sizeof(SID_AND_ATTRIBUTES) +
SeLengthSid(Groups[0].Sid);
ASSERT(GroupsLength <= sizeof(Groups));
/* Initialise the object attributes for the token */
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
ASSERT(SeSystemAnonymousLogonDacl != NULL);
/* Create token */
Status = SepCreateToken((PHANDLE)&Token,
KernelMode,
0,
&ObjectAttributes,
TokenPrimary,
SecurityAnonymous,
&SeAnonymousAuthenticationId,
&Expiration,
&UserSid,
1,
Groups,
GroupsLength,
0,
NULL,
NULL,
PrimaryGroup,
SeSystemAnonymousLogonDacl,
&SeSystemTokenSource,
TRUE);
ASSERT(Status == STATUS_SUCCESS);
/* Return the anonymous logon token */
return Token;
}
/**
* @brief
* Creates the anonymous logon token for the system. This kind of token
* doesn't include the everyone SID group (being SeWorldSid).
*
* @return
* Returns the system's anonymous logon token if the operations have
* completed successfully.
*/
CODE_SEG("INIT")
PTOKEN
SepCreateSystemAnonymousLogonTokenNoEveryone(VOID)
{
SID_AND_ATTRIBUTES UserSid;
PSID PrimaryGroup;
PTOKEN Token;
LARGE_INTEGER Expiration;
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS Status;
/* The token never expires */
Expiration.QuadPart = -1;
/* The user is the anonymous logon */
UserSid.Sid = SeAnonymousLogonSid;
UserSid.Attributes = 0;
/* The primary group is also the anonymous logon */
PrimaryGroup = SeAnonymousLogonSid;
/* Initialise the object attributes for the token */
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
ASSERT(SeSystemAnonymousLogonDacl != NULL);
/* Create token */
Status = SepCreateToken((PHANDLE)&Token,
KernelMode,
0,
&ObjectAttributes,
TokenPrimary,
SecurityAnonymous,
&SeAnonymousAuthenticationId,
&Expiration,
&UserSid,
0,
NULL,
0,
0,
NULL,
NULL,
PrimaryGroup,
SeSystemAnonymousLogonDacl,
&SeSystemTokenSource,
TRUE);
ASSERT(Status == STATUS_SUCCESS);
/* Return the anonymous (not including everyone) logon token */
return Token;
}
/* PUBLIC FUNCTIONS ***********************************************************/
/**
* @brief
* Filters an access token from an existing token, making it more restricted
* than the previous one.
*
* @param[in] ExistingToken
* An existing token for filtering.
*
* @param[in] Flags
* Privilege flag options. This parameter argument influences how the token
* is filtered. Such parameter can be 0. See NtFilterToken syscall for
* more information.
*
* @param[in] SidsToDisable
* Array of SIDs to disable. Such parameter can be NULL.
*
* @param[in] PrivilegesToDelete
* Array of privileges to delete. If DISABLE_MAX_PRIVILEGE flag is specified
* in the Flags parameter, PrivilegesToDelete is ignored.
*
* @param[in] RestrictedSids
* An array of restricted SIDs for the new filtered token. Such parameter
* can be NULL.
*
* @param[out] FilteredToken
* The newly filtered token, returned to the caller.
*
* @return
* Returns STATUS_SUCCESS if the function has successfully completed its
* operations and that the access token has been filtered. STATUS_INVALID_PARAMETER
* is returned if one or more of the parameter are not valid. A failure NTSTATUS code
* is returned otherwise.
*
* @remarks
* WARNING -- The caller IS RESPONSIBLE for locking the existing access token
* before attempting to do any kind of filtering operation into
* the token. The lock MUST BE RELEASED after this kernel routine
* has finished doing its work.
*/
NTSTATUS
NTAPI
SeFilterToken(
_In_ PACCESS_TOKEN ExistingToken,
_In_ ULONG Flags,
_In_opt_ PTOKEN_GROUPS SidsToDisable,
_In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete,
_In_opt_ PTOKEN_GROUPS RestrictedSids,
_Out_ PACCESS_TOKEN *FilteredToken)
{
NTSTATUS Status;
PTOKEN AccessToken;
ULONG PrivilegesCount = 0;
ULONG SidsCount = 0;
ULONG RestrictedSidsCount = 0;
PAGED_CODE();
/* Begin copying the counters */
if (SidsToDisable != NULL)
{
SidsCount = SidsToDisable->GroupCount;
}
if (PrivilegesToDelete != NULL)
{
PrivilegesCount = PrivilegesToDelete->PrivilegeCount;
}
if (RestrictedSids != NULL)
{
RestrictedSidsCount = RestrictedSids->GroupCount;
}
/* Call the internal API */
Status = SepPerformTokenFiltering(ExistingToken,
PrivilegesToDelete->Privileges,
SidsToDisable->Groups,
RestrictedSids->Groups,
PrivilegesCount,
SidsCount,
RestrictedSidsCount,
Flags,
KernelMode,
&AccessToken);
if (!NT_SUCCESS(Status))
{
DPRINT1("SeFilterToken(): Failed to filter the token (Status 0x%lx)\n", Status);
return Status;
}
/* Insert the filtered token */
Status = ObInsertObject(AccessToken,
NULL,
0,
0,
NULL,
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("SeFilterToken(): Failed to insert the token (Status 0x%lx)\n", Status);
return Status;
}
/* Give it to the caller */
*FilteredToken = AccessToken;
return Status;
}
/**
* @brief
* Queries information details about the given token to the call. The difference
* between NtQueryInformationToken and this routine is that the system call has
* user mode buffer data probing and additional protection checks whereas this
* routine doesn't have any of these. The routine is used exclusively in kernel
* mode.
*
* @param[in] AccessToken
* An access token to be given.
*
* @param[in] TokenInformationClass
* Token information class.
*
* @param[out] TokenInformation
* Buffer with retrieved information. Such information is arbitrary, depending
* on the requested information class.
*
* @return
* Returns STATUS_SUCCESS if the operation to query the desired information
* has completed successfully. STATUS_INSUFFICIENT_RESOURCES is returned if
* pool memory allocation has failed to satisfy an operation. Otherwise
* STATUS_INVALID_INFO_CLASS is returned indicating that the information
* class provided is not supported by the routine.
*
* @remarks
* Only certain information classes are not implemented in this function and
* these are TokenOrigin, TokenGroupsAndPrivileges, TokenRestrictedSids and
* TokenSandBoxInert. The following classes are implemented in NtQueryInformationToken
* only.
*/
NTSTATUS
NTAPI
SeQueryInformationToken(
_In_ PACCESS_TOKEN AccessToken,
_In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
_Outptr_result_buffer_(_Inexpressible_(token-dependent)) PVOID *TokenInformation)
{
NTSTATUS Status;
PTOKEN Token = (PTOKEN)AccessToken;
ULONG RequiredLength;
union
{
PSID PSid;
ULONG Ulong;
} Unused;
PAGED_CODE();
/* Lock the token */
SepAcquireTokenLockShared(Token);
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
switch (TokenInformationClass)
{
case TokenUser:
{
PTOKEN_USER tu;
DPRINT("SeQueryInformationToken(TokenUser)\n");
RequiredLength = sizeof(TOKEN_USER) +
RtlLengthSid(Token->UserAndGroups[0].Sid);
/* Allocate the output buffer */
tu = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (tu == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
Status = RtlCopySidAndAttributesArray(1,
&Token->UserAndGroups[0],
RequiredLength - sizeof(TOKEN_USER),
&tu->User,
(PSID)(tu + 1),
&Unused.PSid,
&Unused.Ulong);
/* Return the structure */
*TokenInformation = tu;
Status = STATUS_SUCCESS;
break;
}
case TokenGroups:
{
PTOKEN_GROUPS tg;
ULONG SidLen;
PSID Sid;
DPRINT("SeQueryInformationToken(TokenGroups)\n");
RequiredLength = sizeof(tg->GroupCount) +
RtlLengthSidAndAttributes(Token->UserAndGroupCount - 1, &Token->UserAndGroups[1]);
SidLen = RequiredLength - sizeof(tg->GroupCount) -
((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES));
/* Allocate the output buffer */
tg = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (tg == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
Sid = (PSID)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES)));
tg->GroupCount = Token->UserAndGroupCount - 1;
Status = RtlCopySidAndAttributesArray(Token->UserAndGroupCount - 1,
&Token->UserAndGroups[1],
SidLen,
&tg->Groups[0],
Sid,
&Unused.PSid,
&Unused.Ulong);
/* Return the structure */
*TokenInformation = tg;
Status = STATUS_SUCCESS;
break;
}
case TokenPrivileges:
{
PTOKEN_PRIVILEGES tp;
DPRINT("SeQueryInformationToken(TokenPrivileges)\n");
RequiredLength = sizeof(tp->PrivilegeCount) +
(Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
/* Allocate the output buffer */
tp = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (tp == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
tp->PrivilegeCount = Token->PrivilegeCount;
RtlCopyLuidAndAttributesArray(Token->PrivilegeCount,
Token->Privileges,
&tp->Privileges[0]);
/* Return the structure */
*TokenInformation = tp;
Status = STATUS_SUCCESS;
break;
}
case TokenOwner:
{
PTOKEN_OWNER to;
ULONG SidLen;
DPRINT("SeQueryInformationToken(TokenOwner)\n");
SidLen = RtlLengthSid(Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
RequiredLength = sizeof(TOKEN_OWNER) + SidLen;
/* Allocate the output buffer */
to = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (to == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
to->Owner = (PSID)(to + 1);
Status = RtlCopySid(SidLen,
to->Owner,
Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
/* Return the structure */
*TokenInformation = to;
Status = STATUS_SUCCESS;
break;
}
case TokenPrimaryGroup:
{
PTOKEN_PRIMARY_GROUP tpg;
ULONG SidLen;
DPRINT("SeQueryInformationToken(TokenPrimaryGroup)\n");
SidLen = RtlLengthSid(Token->PrimaryGroup);
RequiredLength = sizeof(TOKEN_PRIMARY_GROUP) + SidLen;
/* Allocate the output buffer */
tpg = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (tpg == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
tpg->PrimaryGroup = (PSID)(tpg + 1);
Status = RtlCopySid(SidLen,
tpg->PrimaryGroup,
Token->PrimaryGroup);
/* Return the structure */
*TokenInformation = tpg;
Status = STATUS_SUCCESS;
break;
}
case TokenDefaultDacl:
{
PTOKEN_DEFAULT_DACL tdd;
DPRINT("SeQueryInformationToken(TokenDefaultDacl)\n");
RequiredLength = sizeof(TOKEN_DEFAULT_DACL);
if (Token->DefaultDacl != NULL)
RequiredLength += Token->DefaultDacl->AclSize;
/* Allocate the output buffer */
tdd = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (tdd == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
if (Token->DefaultDacl != NULL)
{
tdd->DefaultDacl = (PACL)(tdd + 1);
RtlCopyMemory(tdd->DefaultDacl,
Token->DefaultDacl,
Token->DefaultDacl->AclSize);
}
else
{
tdd->DefaultDacl = NULL;
}
/* Return the structure */
*TokenInformation = tdd;
Status = STATUS_SUCCESS;
break;
}
case TokenSource:
{
PTOKEN_SOURCE ts;
DPRINT("SeQueryInformationToken(TokenSource)\n");
RequiredLength = sizeof(TOKEN_SOURCE);
/* Allocate the output buffer */
ts = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (ts == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
*ts = Token->TokenSource;
/* Return the structure */
*TokenInformation = ts;
Status = STATUS_SUCCESS;
break;
}
case TokenType:
{
PTOKEN_TYPE tt;
DPRINT("SeQueryInformationToken(TokenType)\n");
RequiredLength = sizeof(TOKEN_TYPE);
/* Allocate the output buffer */
tt = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (tt == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
*tt = Token->TokenType;
/* Return the structure */
*TokenInformation = tt;
Status = STATUS_SUCCESS;
break;
}
case TokenImpersonationLevel:
{
PSECURITY_IMPERSONATION_LEVEL sil;
DPRINT("SeQueryInformationToken(TokenImpersonationLevel)\n");
RequiredLength = sizeof(SECURITY_IMPERSONATION_LEVEL);
/* Fail if the token is not an impersonation token */
if (Token->TokenType != TokenImpersonation)
{
Status = STATUS_INVALID_INFO_CLASS;
break;
}
/* Allocate the output buffer */
sil = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (sil == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
*sil = Token->ImpersonationLevel;
/* Return the structure */
*TokenInformation = sil;
Status = STATUS_SUCCESS;
break;
}
case TokenStatistics:
{
PTOKEN_STATISTICS ts;
DPRINT("SeQueryInformationToken(TokenStatistics)\n");
RequiredLength = sizeof(TOKEN_STATISTICS);
/* Allocate the output buffer */
ts = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (ts == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
ts->TokenId = Token->TokenId;
ts->AuthenticationId = Token->AuthenticationId;
ts->ExpirationTime = Token->ExpirationTime;
ts->TokenType = Token->TokenType;
ts->ImpersonationLevel = Token->ImpersonationLevel;
ts->DynamicCharged = Token->DynamicCharged;
ts->DynamicAvailable = Token->DynamicAvailable;
ts->GroupCount = Token->UserAndGroupCount - 1;
ts->PrivilegeCount = Token->PrivilegeCount;
ts->ModifiedId = Token->ModifiedId;
/* Return the structure */
*TokenInformation = ts;
Status = STATUS_SUCCESS;
break;
}
case TokenSessionId:
{
DPRINT("SeQueryInformationToken(TokenSessionId)\n");
Status = SeQuerySessionIdToken(Token, (PULONG)TokenInformation);
break;
}
default:
DPRINT1("SeQueryInformationToken(%d) invalid information class\n", TokenInformationClass);
Status = STATUS_INVALID_INFO_CLASS;
break;
}
/* Release the lock of the token */
SepReleaseTokenLock(Token);
return Status;
}
/**
* @brief
* Queries the session ID of an access token.
*
* @param[in] Token
* A valid access token where the session ID has to be gathered.
*
* @param[out] pSessionId
* The returned pointer to a session ID to the caller.
*
* @return
* Returns STATUS_SUCCESS.
*/
NTSTATUS
NTAPI
SeQuerySessionIdToken(
_In_ PACCESS_TOKEN Token,
_Out_ PULONG pSessionId)
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
PAGED_CODE();
/* Lock the token */
SepAcquireTokenLockShared(Token);
*pSessionId = ((PTOKEN)Token)->SessionId;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Unlock the token */
SepReleaseTokenLock(Token);
return STATUS_SUCCESS;
}
/**
* @brief
* Queries the authentication ID of an access token.
*
* @param[in] Token
* A valid access token where the authentication ID has to be gathered.
*
* @param[out] pSessionId
* The returned pointer to an authentication ID to the caller.
*
* @return
* Returns STATUS_SUCCESS.
*/
NTSTATUS
NTAPI
SeQueryAuthenticationIdToken(
_In_ PACCESS_TOKEN Token,
_Out_ PLUID LogonId)
{
PAGED_CODE();
*LogonId = ((PTOKEN)Token)->AuthenticationId;
return STATUS_SUCCESS;
}
/**
* @brief
* Gathers the security impersonation level of an access token.
*
* @param[in] Token
* A valid access token where the impersonation level has to be gathered.
*
* @return
* Returns the security impersonation level from a valid token.
*/
SECURITY_IMPERSONATION_LEVEL
NTAPI
SeTokenImpersonationLevel(
_In_ PACCESS_TOKEN Token)
{
PAGED_CODE();
return ((PTOKEN)Token)->ImpersonationLevel;
}
/**
* @brief
* Gathers the token type of an access token. A token ca be either
* a primary token or impersonation token.
*
* @param[in] Token
* A valid access token where the token type has to be gathered.
*
* @return
* Returns the token type from a valid token.
*/
TOKEN_TYPE
NTAPI
SeTokenType(
_In_ PACCESS_TOKEN Token)
{
PAGED_CODE();
return ((PTOKEN)Token)->TokenType;
}
/**
* @brief
* Determines if a token is either an admin token or not. Such
* condition is checked based upon TOKEN_HAS_ADMIN_GROUP flag,
* which means if the respective access token belongs to an
* administrator group or not.
*
* @param[in] Token
* A valid access token to determine if such token is admin or not.
*
* @return
* Returns TRUE if the token is an admin one, FALSE otherwise.
*/
BOOLEAN
NTAPI
SeTokenIsAdmin(
_In_ PACCESS_TOKEN Token)
{
PAGED_CODE();
// NOTE: Win7+ instead really checks the list of groups in the token
// (since TOKEN_HAS_ADMIN_GROUP == TOKEN_WRITE_RESTRICTED ...)
return (((PTOKEN)Token)->TokenFlags & TOKEN_HAS_ADMIN_GROUP) != 0;
}
/**
* @brief
* Determines if a token is restricted or not, based upon the token
* flags.
*
* @param[in] Token
* A valid access token to determine if such token is restricted.
*
* @return
* Returns TRUE if the token is restricted, FALSE otherwise.
*/
BOOLEAN
NTAPI
SeTokenIsRestricted(
_In_ PACCESS_TOKEN Token)
{
PAGED_CODE();
return (((PTOKEN)Token)->TokenFlags & TOKEN_IS_RESTRICTED) != 0;
}
/**
* @brief
* Determines if a token is write restricted, that is, nobody can write anything
* to it.
*
* @param[in] Token
* A valid access token to determine if such token is write restricted.
*
* @return
* Returns TRUE if the token is write restricted, FALSE otherwise.
*
* @remarks
* First introduced in NT 5.1 SP2 x86 (5.1.2600.2622), absent in NT 5.2,
* then finally re-introduced in Vista+.
*/
BOOLEAN
NTAPI
SeTokenIsWriteRestricted(
_In_ PACCESS_TOKEN Token)
{
PAGED_CODE();
// NOTE: NT 5.1 SP2 x86 checks the SE_BACKUP_PRIVILEGES_CHECKED flag
// while Vista+ checks the TOKEN_WRITE_RESTRICTED flag as one expects.
return (((PTOKEN)Token)->TokenFlags & SE_BACKUP_PRIVILEGES_CHECKED) != 0;
}
/**
* @brief
* Ensures that client impersonation can occur by checking if the token
* we're going to assign as the impersonation token can be actually impersonated
* in the first place. The routine is used primarily by PsImpersonateClient.
*
* @param[in] ProcessToken
* Token from a process.
*
* @param[in] TokenToImpersonate
* Token that we are going to impersonate.
*
* @param[in] ImpersonationLevel
* Security impersonation level grade.
*
* @return
* Returns TRUE if the conditions checked are met for token impersonation,
* FALSE otherwise.
*/
BOOLEAN
NTAPI
SeTokenCanImpersonate(
_In_ PTOKEN ProcessToken,
_In_ PTOKEN TokenToImpersonate,
_In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel)
{
BOOLEAN CanImpersonate;
PAGED_CODE();
/*
* SecurityAnonymous and SecurityIdentification levels do not
* allow impersonation.
*/
if (ImpersonationLevel == SecurityAnonymous ||
ImpersonationLevel == SecurityIdentification)
{
return FALSE;
}
/* Time to lock our tokens */
SepAcquireTokenLockShared(ProcessToken);
SepAcquireTokenLockShared(TokenToImpersonate);
/* What kind of authentication ID does the token have? */
if (RtlEqualLuid(&TokenToImpersonate->AuthenticationId,
&SeAnonymousAuthenticationId))
{
/*
* OK, it looks like the token has an anonymous
* authentication. Is that token created by the system?
*/
if (TokenToImpersonate->TokenSource.SourceName != SeSystemTokenSource.SourceName &&
!RtlEqualLuid(&TokenToImpersonate->TokenSource.SourceIdentifier, &SeSystemTokenSource.SourceIdentifier))
{
/* It isn't, we can't impersonate regular tokens */
DPRINT("SeTokenCanImpersonate(): Token has an anonymous authentication ID, can't impersonate!\n");
CanImpersonate = FALSE;
goto Quit;
}
}
/* Are the SID values from both tokens equal? */
if (!RtlEqualSid(ProcessToken->UserAndGroups->Sid,
TokenToImpersonate->UserAndGroups->Sid))
{
/* They aren't, bail out */
DPRINT("SeTokenCanImpersonate(): Tokens SIDs are not equal!\n");
CanImpersonate = FALSE;
goto Quit;
}
/*
* Make sure the tokens aren't diverged in terms of
* restrictions, that is, one token is restricted
* but the other one isn't.
*/
if (SeTokenIsRestricted(ProcessToken) !=
SeTokenIsRestricted(TokenToImpersonate))
{
/*
* One token is restricted so we cannot
* continue further at this point, bail out.
*/
DPRINT("SeTokenCanImpersonate(): One token is restricted, can't continue!\n");
CanImpersonate = FALSE;
goto Quit;
}
/* If we've reached that far then we can impersonate! */
DPRINT("SeTokenCanImpersonate(): We can impersonate.\n");
CanImpersonate = TRUE;
Quit:
/* We're done, unlock the tokens now */
SepReleaseTokenLock(ProcessToken);
SepReleaseTokenLock(TokenToImpersonate);
return CanImpersonate;
}
/* SYSTEM CALLS ***************************************************************/
/**
* @brief
* Queries a specific type of information in regard of an access token based upon
* the information class. The calling thread must have specific access rights in order
* to obtain specific information about the token.
*
* @param[in] TokenHandle
* A handle of a token where information is to be gathered.
*
* @param[in] TokenInformationClass
* Token information class.
*
* @param[out] TokenInformation
* A returned output buffer with token information, which information is arbitrarily upon
* the information class chosen.
*
* @param[in] TokenInformationLength
* Length of the token information buffer, in bytes.
*
* @param[out] ReturnLength
* If specified in the call, the function returns the total length size of the token
* information buffer..
*
* @return
* Returns STATUS_SUCCESS if information querying has completed successfully.
* STATUS_BUFFER_TOO_SMALL is returned if the information length that represents
* the token information buffer is not greater than the required length.
* STATUS_INVALID_HANDLE is returned if the token handle is not a valid one.
* STATUS_INVALID_INFO_CLASS is returned if the information class is not a valid
* one (that is, the class doesn't belong to TOKEN_INFORMATION_CLASS). A failure
* NTSTATUS code is returned otherwise.
*/
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
_Must_inspect_result_
__kernel_entry
NTSTATUS
NTAPI
NtQueryInformationToken(
_In_ HANDLE TokenHandle,
_In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
_Out_writes_bytes_to_opt_(TokenInformationLength, *ReturnLength)
PVOID TokenInformation,
_In_ ULONG TokenInformationLength,
_Out_ PULONG ReturnLength)
{
NTSTATUS Status;
KPROCESSOR_MODE PreviousMode;
PTOKEN Token;
ULONG RequiredLength;
union
{
PSID PSid;
ULONG Ulong;
} Unused;
PAGED_CODE();
PreviousMode = ExGetPreviousMode();
/* Check buffers and class validity */
Status = DefaultQueryInfoBufferCheck(TokenInformationClass,
SeTokenInformationClass,
RTL_NUMBER_OF(SeTokenInformationClass),
TokenInformation,
TokenInformationLength,
ReturnLength,
NULL,
2021-05-03 13:46:35 +00:00
PreviousMode,
TRUE);
if (!NT_SUCCESS(Status))
{
DPRINT("NtQueryInformationToken() failed, Status: 0x%x\n", Status);
return Status;
}
Status = ObReferenceObjectByHandle(TokenHandle,
(TokenInformationClass == TokenSource) ? TOKEN_QUERY_SOURCE : TOKEN_QUERY,
SeTokenObjectType,
PreviousMode,
(PVOID*)&Token,
NULL);
if (NT_SUCCESS(Status))
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Lock the token */
SepAcquireTokenLockShared(Token);
switch (TokenInformationClass)
{
case TokenUser:
{
PTOKEN_USER tu = (PTOKEN_USER)TokenInformation;
DPRINT("NtQueryInformationToken(TokenUser)\n");
RequiredLength = sizeof(TOKEN_USER) +
RtlLengthSid(Token->UserAndGroups[0].Sid);
_SEH2_TRY
{
if (TokenInformationLength >= RequiredLength)
{
Status = RtlCopySidAndAttributesArray(1,
&Token->UserAndGroups[0],
RequiredLength - sizeof(TOKEN_USER),
&tu->User,
(PSID)(tu + 1),
&Unused.PSid,
&Unused.Ulong);
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
if (ReturnLength != NULL)
{
*ReturnLength = RequiredLength;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
break;
}
case TokenGroups:
{
PTOKEN_GROUPS tg = (PTOKEN_GROUPS)TokenInformation;
DPRINT("NtQueryInformationToken(TokenGroups)\n");
RequiredLength = sizeof(tg->GroupCount) +
RtlLengthSidAndAttributes(Token->UserAndGroupCount - 1, &Token->UserAndGroups[1]);
_SEH2_TRY
{
if (TokenInformationLength >= RequiredLength)
{
ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) -
((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES));
PSID Sid = (PSID_AND_ATTRIBUTES)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES)));
tg->GroupCount = Token->UserAndGroupCount - 1;
Status = RtlCopySidAndAttributesArray(Token->UserAndGroupCount - 1,
&Token->UserAndGroups[1],
SidLen,
&tg->Groups[0],
Sid,
&Unused.PSid,
&Unused.Ulong);
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
if (ReturnLength != NULL)
{
*ReturnLength = RequiredLength;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
break;
}
case TokenPrivileges:
{
PTOKEN_PRIVILEGES tp = (PTOKEN_PRIVILEGES)TokenInformation;
DPRINT("NtQueryInformationToken(TokenPrivileges)\n");
RequiredLength = sizeof(tp->PrivilegeCount) +
(Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
_SEH2_TRY
{
if (TokenInformationLength >= RequiredLength)
{
tp->PrivilegeCount = Token->PrivilegeCount;
RtlCopyLuidAndAttributesArray(Token->PrivilegeCount,
Token->Privileges,
&tp->Privileges[0]);
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
if (ReturnLength != NULL)
{
*ReturnLength = RequiredLength;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
break;
}
case TokenOwner:
{
PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation;
ULONG SidLen;
DPRINT("NtQueryInformationToken(TokenOwner)\n");
SidLen = RtlLengthSid(Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
RequiredLength = sizeof(TOKEN_OWNER) + SidLen;
_SEH2_TRY
{
if (TokenInformationLength >= RequiredLength)
{
to->Owner = (PSID)(to + 1);
Status = RtlCopySid(SidLen,
to->Owner,
Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
if (ReturnLength != NULL)
{
*ReturnLength = RequiredLength;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
break;
}
case TokenPrimaryGroup:
{
PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation;
ULONG SidLen;
DPRINT("NtQueryInformationToken(TokenPrimaryGroup)\n");
SidLen = RtlLengthSid(Token->PrimaryGroup);
RequiredLength = sizeof(TOKEN_PRIMARY_GROUP) + SidLen;
_SEH2_TRY
{
if (TokenInformationLength >= RequiredLength)
{
tpg->PrimaryGroup = (PSID)(tpg + 1);
Status = RtlCopySid(SidLen,
tpg->PrimaryGroup,
Token->PrimaryGroup);
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
if (ReturnLength != NULL)
{
*ReturnLength = RequiredLength;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
break;
}
case TokenDefaultDacl:
{
PTOKEN_DEFAULT_DACL tdd = (PTOKEN_DEFAULT_DACL)TokenInformation;
DPRINT("NtQueryInformationToken(TokenDefaultDacl)\n");
RequiredLength = sizeof(TOKEN_DEFAULT_DACL);
if (Token->DefaultDacl != NULL)
RequiredLength += Token->DefaultDacl->AclSize;
_SEH2_TRY
{
if (TokenInformationLength >= RequiredLength)
{
if (Token->DefaultDacl != NULL)
{
tdd->DefaultDacl = (PACL)(tdd + 1);
RtlCopyMemory(tdd->DefaultDacl,
Token->DefaultDacl,
Token->DefaultDacl->AclSize);
}
else
{
tdd->DefaultDacl = NULL;
}
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
if (ReturnLength != NULL)
{
*ReturnLength = RequiredLength;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
break;
}
case TokenSource:
{
PTOKEN_SOURCE ts = (PTOKEN_SOURCE)TokenInformation;
DPRINT("NtQueryInformationToken(TokenSource)\n");
RequiredLength = sizeof(TOKEN_SOURCE);
_SEH2_TRY
{
if (TokenInformationLength >= RequiredLength)
{
*ts = Token->TokenSource;
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
if (ReturnLength != NULL)
{
*ReturnLength = RequiredLength;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
break;
}
case TokenType:
{
PTOKEN_TYPE tt = (PTOKEN_TYPE)TokenInformation;
DPRINT("NtQueryInformationToken(TokenType)\n");
RequiredLength = sizeof(TOKEN_TYPE);
_SEH2_TRY
{
if (TokenInformationLength >= RequiredLength)
{
*tt = Token->TokenType;
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
if (ReturnLength != NULL)
{
*ReturnLength = RequiredLength;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
break;
}
case TokenImpersonationLevel:
{
PSECURITY_IMPERSONATION_LEVEL sil = (PSECURITY_IMPERSONATION_LEVEL)TokenInformation;
DPRINT("NtQueryInformationToken(TokenImpersonationLevel)\n");
/* Fail if the token is not an impersonation token */
if (Token->TokenType != TokenImpersonation)
{
Status = STATUS_INVALID_INFO_CLASS;
break;
}
RequiredLength = sizeof(SECURITY_IMPERSONATION_LEVEL);
_SEH2_TRY
{
if (TokenInformationLength >= RequiredLength)
{
*sil = Token->ImpersonationLevel;
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
if (ReturnLength != NULL)
{
*ReturnLength = RequiredLength;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
break;
}
case TokenStatistics:
{
PTOKEN_STATISTICS ts = (PTOKEN_STATISTICS)TokenInformation;
DPRINT("NtQueryInformationToken(TokenStatistics)\n");
RequiredLength = sizeof(TOKEN_STATISTICS);
_SEH2_TRY
{
if (TokenInformationLength >= RequiredLength)
{
ts->TokenId = Token->TokenId;
ts->AuthenticationId = Token->AuthenticationId;
ts->ExpirationTime = Token->ExpirationTime;
ts->TokenType = Token->TokenType;
ts->ImpersonationLevel = Token->ImpersonationLevel;
ts->DynamicCharged = Token->DynamicCharged;
ts->DynamicAvailable = Token->DynamicAvailable;
ts->GroupCount = Token->UserAndGroupCount - 1;
ts->PrivilegeCount = Token->PrivilegeCount;
ts->ModifiedId = Token->ModifiedId;
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
if (ReturnLength != NULL)
{
*ReturnLength = RequiredLength;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
break;
}
case TokenOrigin:
{
PTOKEN_ORIGIN to = (PTOKEN_ORIGIN)TokenInformation;
DPRINT("NtQueryInformationToken(TokenOrigin)\n");
RequiredLength = sizeof(TOKEN_ORIGIN);
_SEH2_TRY
{
if (TokenInformationLength >= RequiredLength)
{
RtlCopyLuid(&to->OriginatingLogonSession,
&Token->AuthenticationId);
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
if (ReturnLength != NULL)
{
*ReturnLength = RequiredLength;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
break;
}
case TokenGroupsAndPrivileges:
DPRINT1("NtQueryInformationToken(TokenGroupsAndPrivileges) not implemented\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case TokenRestrictedSids:
{
PTOKEN_GROUPS tg = (PTOKEN_GROUPS)TokenInformation;
DPRINT("NtQueryInformationToken(TokenRestrictedSids)\n");
RequiredLength = sizeof(tg->GroupCount) +
RtlLengthSidAndAttributes(Token->RestrictedSidCount, Token->RestrictedSids);
_SEH2_TRY
{
if (TokenInformationLength >= RequiredLength)
{
ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) -
(Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES));
PSID Sid = (PSID)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
(Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES)));
tg->GroupCount = Token->RestrictedSidCount;
Status = RtlCopySidAndAttributesArray(Token->RestrictedSidCount,
Token->RestrictedSids,
SidLen,
&tg->Groups[0],
Sid,
&Unused.PSid,
&Unused.Ulong);
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
if (ReturnLength != NULL)
{
*ReturnLength = RequiredLength;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
break;
}
case TokenSandBoxInert:
DPRINT1("NtQueryInformationToken(TokenSandboxInert) not implemented\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case TokenSessionId:
{
ULONG SessionId = 0;
DPRINT("NtQueryInformationToken(TokenSessionId)\n");
Status = SeQuerySessionIdToken(Token, &SessionId);
if (NT_SUCCESS(Status))
{
_SEH2_TRY
{
/* Buffer size was already verified, no need to check here again */
*(PULONG)TokenInformation = SessionId;
if (ReturnLength != NULL)
{
*ReturnLength = sizeof(ULONG);
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
break;
}
default:
DPRINT1("NtQueryInformationToken(%d) invalid information class\n", TokenInformationClass);
Status = STATUS_INVALID_INFO_CLASS;
break;
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Unlock and dereference the token */
SepReleaseTokenLock(Token);
ObDereferenceObject(Token);
}
return Status;
}
/**
* @unimplemented
* @brief
* Sets (modifies) some specific information in regard of an access token. The
* calling thread must have specific access rights in order to modify token's
* information data.
*
* @param[in] TokenHandle
* A handle of a token where information is to be modified.
*
* @param[in] TokenInformationClass
* Token information class.
*
* @param[in] TokenInformation
* An arbitrary pointer to a buffer with token information to set. Such
* arbitrary buffer depends on the information class chosen that the caller
* wants to modify such information data of a token.
*
* @param[in] TokenInformationLength
* Length of the token information buffer, in bytes.
*
* @return
* Returns STATUS_SUCCESS if information setting has completed successfully.
* STATUS_INFO_LENGTH_MISMATCH is returned if the information length of the
* buffer is less than the required length. STATUS_INSUFFICIENT_RESOURCES is
* returned if memory pool allocation has failed. STATUS_PRIVILEGE_NOT_HELD
* is returned if the calling thread hasn't the required privileges to perform
* the operation in question. A failure NTSTATUS code is returned otherwise.
*
* @remarks
* The function is partly implemented, mainly TokenOrigin and TokenDefaultDacl.
*/
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
_Must_inspect_result_
__kernel_entry
NTSTATUS
NTAPI
NtSetInformationToken(
_In_ HANDLE TokenHandle,
_In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
_In_reads_bytes_(TokenInformationLength) PVOID TokenInformation,
_In_ ULONG TokenInformationLength)
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
NTSTATUS Status;
PTOKEN Token;
KPROCESSOR_MODE PreviousMode;
ULONG NeededAccess = TOKEN_ADJUST_DEFAULT;
PAGED_CODE();
PreviousMode = ExGetPreviousMode();
Status = DefaultSetInfoBufferCheck(TokenInformationClass,
SeTokenInformationClass,
RTL_NUMBER_OF(SeTokenInformationClass),
TokenInformation,
TokenInformationLength,
PreviousMode);
if (!NT_SUCCESS(Status))
{
/* Invalid buffers */
DPRINT("NtSetInformationToken() failed, Status: 0x%x\n", Status);
return Status;
}
if (TokenInformationClass == TokenSessionId)
{
NeededAccess |= TOKEN_ADJUST_SESSIONID;
}
Status = ObReferenceObjectByHandle(TokenHandle,
NeededAccess,
SeTokenObjectType,
PreviousMode,
(PVOID*)&Token,
NULL);
if (NT_SUCCESS(Status))
{
switch (TokenInformationClass)
{
case TokenOwner:
{
if (TokenInformationLength >= sizeof(TOKEN_OWNER))
{
PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation;
PSID InputSid = NULL, CapturedSid;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
ULONG DefaultOwnerIndex;
_SEH2_TRY
{
InputSid = to->Owner;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(goto Cleanup);
}
_SEH2_END;
Status = SepCaptureSid(InputSid,
PreviousMode,
PagedPool,
FALSE,
&CapturedSid);
if (NT_SUCCESS(Status))
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Lock the token */
SepAcquireTokenLockExclusive(Token);
/* Find the owner amongst the existing token user and groups */
Status = SepFindPrimaryGroupAndDefaultOwner(Token,
NULL,
CapturedSid,
NULL,
&DefaultOwnerIndex);
if (NT_SUCCESS(Status))
{
/* Found it */
Token->DefaultOwnerIndex = DefaultOwnerIndex;
ExAllocateLocallyUniqueId(&Token->ModifiedId);
}
/* Unlock the token */
SepReleaseTokenLock(Token);
SepReleaseSid(CapturedSid,
PreviousMode,
FALSE);
}
}
else
{
Status = STATUS_INFO_LENGTH_MISMATCH;
}
break;
}
case TokenPrimaryGroup:
{
if (TokenInformationLength >= sizeof(TOKEN_PRIMARY_GROUP))
{
PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation;
PSID InputSid = NULL, CapturedSid;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
ULONG PrimaryGroupIndex;
_SEH2_TRY
{
InputSid = tpg->PrimaryGroup;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(goto Cleanup);
}
_SEH2_END;
Status = SepCaptureSid(InputSid,
PreviousMode,
PagedPool,
FALSE,
&CapturedSid);
if (NT_SUCCESS(Status))
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Lock the token */
SepAcquireTokenLockExclusive(Token);
/* Find the primary group amongst the existing token user and groups */
Status = SepFindPrimaryGroupAndDefaultOwner(Token,
CapturedSid,
NULL,
&PrimaryGroupIndex,
NULL);
if (NT_SUCCESS(Status))
{
/* Found it */
Token->PrimaryGroup = Token->UserAndGroups[PrimaryGroupIndex].Sid;
ExAllocateLocallyUniqueId(&Token->ModifiedId);
}
/* Unlock the token */
SepReleaseTokenLock(Token);
SepReleaseSid(CapturedSid,
PreviousMode,
FALSE);
}
}
else
{
Status = STATUS_INFO_LENGTH_MISMATCH;
}
break;
}
case TokenDefaultDacl:
{
if (TokenInformationLength >= sizeof(TOKEN_DEFAULT_DACL))
{
PTOKEN_DEFAULT_DACL tdd = (PTOKEN_DEFAULT_DACL)TokenInformation;
PACL InputAcl = NULL;
_SEH2_TRY
{
InputAcl = tdd->DefaultDacl;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(goto Cleanup);
}
_SEH2_END;
if (InputAcl != NULL)
{
PACL CapturedAcl;
/* Capture and copy the dacl */
Status = SepCaptureAcl(InputAcl,
PreviousMode,
PagedPool,
TRUE,
&CapturedAcl);
if (NT_SUCCESS(Status))
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
ULONG DynamicLength;
/* Lock the token */
SepAcquireTokenLockExclusive(Token);
//
// NOTE: So far our dynamic area only contains
// the default dacl, so this makes the following
// code pretty simple. The day where it stores
// other data, the code will require adaptations.
//
DynamicLength = Token->DynamicAvailable;
// Add here any other data length present in the dynamic area...
if (Token->DefaultDacl)
DynamicLength += Token->DefaultDacl->AclSize;
/* Reallocate the dynamic area if it is too small */
Status = STATUS_SUCCESS;
if ((DynamicLength < CapturedAcl->AclSize) ||
(Token->DynamicPart == NULL))
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
PVOID NewDynamicPart;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
NewDynamicPart = ExAllocatePoolWithTag(PagedPool,
CapturedAcl->AclSize,
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
TAG_TOKEN_DYNAMIC);
if (NewDynamicPart == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
}
else
{
if (Token->DynamicPart != NULL)
{
// RtlCopyMemory(NewDynamicPart, Token->DynamicPart, DynamicLength);
ExFreePoolWithTag(Token->DynamicPart, TAG_TOKEN_DYNAMIC);
}
Token->DynamicPart = NewDynamicPart;
Token->DynamicAvailable = 0;
}
}
else
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
{
Token->DynamicAvailable = DynamicLength - CapturedAcl->AclSize;
}
if (NT_SUCCESS(Status))
{
/* Set the new dacl */
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
Token->DefaultDacl = (PVOID)Token->DynamicPart;
RtlCopyMemory(Token->DefaultDacl,
CapturedAcl,
CapturedAcl->AclSize);
ExAllocateLocallyUniqueId(&Token->ModifiedId);
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Unlock the token */
SepReleaseTokenLock(Token);
ExFreePoolWithTag(CapturedAcl, TAG_ACL);
}
}
else
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Lock the token */
SepAcquireTokenLockExclusive(Token);
/* Clear the default dacl if present */
if (Token->DefaultDacl != NULL)
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
Token->DynamicAvailable += Token->DefaultDacl->AclSize;
RtlZeroMemory(Token->DefaultDacl, Token->DefaultDacl->AclSize);
Token->DefaultDacl = NULL;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
ExAllocateLocallyUniqueId(&Token->ModifiedId);
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Unlock the token */
SepReleaseTokenLock(Token);
}
}
else
{
Status = STATUS_INFO_LENGTH_MISMATCH;
}
break;
}
case TokenSessionId:
{
ULONG SessionId = 0;
_SEH2_TRY
{
/* Buffer size was already verified, no need to check here again */
SessionId = *(PULONG)TokenInformation;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(goto Cleanup);
}
_SEH2_END;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Check for TCB privilege */
if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
{
Status = STATUS_PRIVILEGE_NOT_HELD;
break;
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Lock the token */
SepAcquireTokenLockExclusive(Token);
Token->SessionId = SessionId;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
ExAllocateLocallyUniqueId(&Token->ModifiedId);
/* Unlock the token */
SepReleaseTokenLock(Token);
break;
}
case TokenSessionReference:
{
ULONG SessionReference;
_SEH2_TRY
{
/* Buffer size was already verified, no need to check here again */
SessionReference = *(PULONG)TokenInformation;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(goto Cleanup);
}
_SEH2_END;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Check for TCB privilege */
if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
{
Status = STATUS_PRIVILEGE_NOT_HELD;
goto Cleanup;
}
/* Check if it is 0 */
if (SessionReference == 0)
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
ULONG OldTokenFlags;
/* Lock the token */
SepAcquireTokenLockExclusive(Token);
/* Atomically set the flag in the token */
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
OldTokenFlags = RtlInterlockedSetBits(&Token->TokenFlags,
TOKEN_SESSION_NOT_REFERENCED);
/*
* If the flag was already set, do not dereference again
* the logon session. Use SessionReference as an indicator
* to know whether to really dereference the session.
*/
if (OldTokenFlags == Token->TokenFlags)
SessionReference = ULONG_MAX;
/*
* Otherwise if the flag was never set but just for this first time then
* remove the referenced logon session data from the token and dereference
* the logon session when needed.
*/
if (SessionReference == 0)
{
SepRmRemoveLogonSessionFromToken(Token);
SepRmDereferenceLogonSession(&Token->AuthenticationId);
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Unlock the token */
SepReleaseTokenLock(Token);
}
break;
}
case TokenAuditPolicy:
{
PTOKEN_AUDIT_POLICY_INFORMATION PolicyInformation =
(PTOKEN_AUDIT_POLICY_INFORMATION)TokenInformation;
SEP_AUDIT_POLICY AuditPolicy;
ULONG i;
_SEH2_TRY
{
ProbeForRead(PolicyInformation,
FIELD_OFFSET(TOKEN_AUDIT_POLICY_INFORMATION,
Policies[PolicyInformation->PolicyCount]),
sizeof(ULONG));
/* Loop all policies in the structure */
for (i = 0; i < PolicyInformation->PolicyCount; i++)
{
/* Set the corresponding bits in the packed structure */
switch (PolicyInformation->Policies[i].Category)
{
case AuditCategorySystem:
AuditPolicy.PolicyElements.System = PolicyInformation->Policies[i].Value;
break;
case AuditCategoryLogon:
AuditPolicy.PolicyElements.Logon = PolicyInformation->Policies[i].Value;
break;
case AuditCategoryObjectAccess:
AuditPolicy.PolicyElements.ObjectAccess = PolicyInformation->Policies[i].Value;
break;
case AuditCategoryPrivilegeUse:
AuditPolicy.PolicyElements.PrivilegeUse = PolicyInformation->Policies[i].Value;
break;
case AuditCategoryDetailedTracking:
AuditPolicy.PolicyElements.DetailedTracking = PolicyInformation->Policies[i].Value;
break;
case AuditCategoryPolicyChange:
AuditPolicy.PolicyElements.PolicyChange = PolicyInformation->Policies[i].Value;
break;
case AuditCategoryAccountManagement:
AuditPolicy.PolicyElements.AccountManagement = PolicyInformation->Policies[i].Value;
break;
case AuditCategoryDirectoryServiceAccess:
AuditPolicy.PolicyElements.DirectoryServiceAccess = PolicyInformation->Policies[i].Value;
break;
case AuditCategoryAccountLogon:
AuditPolicy.PolicyElements.AccountLogon = PolicyInformation->Policies[i].Value;
break;
}
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(goto Cleanup);
}
_SEH2_END;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Check for TCB privilege */
if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
{
Status = STATUS_PRIVILEGE_NOT_HELD;
break;
}
/* Lock the token */
SepAcquireTokenLockExclusive(Token);
/* Set the new audit policy */
Token->AuditPolicy = AuditPolicy;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
ExAllocateLocallyUniqueId(&Token->ModifiedId);
/* Unlock the token */
SepReleaseTokenLock(Token);
break;
}
case TokenOrigin:
{
TOKEN_ORIGIN TokenOrigin;
_SEH2_TRY
{
/* Copy the token origin */
TokenOrigin = *(PTOKEN_ORIGIN)TokenInformation;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(goto Cleanup);
}
_SEH2_END;
/* Check for TCB privilege */
if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
{
Status = STATUS_PRIVILEGE_NOT_HELD;
break;
}
/* Lock the token */
SepAcquireTokenLockExclusive(Token);
/* Check if there is no token origin set yet */
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
if (RtlIsZeroLuid(&Token->OriginatingLogonSession))
{
/* Set the token origin */
Token->OriginatingLogonSession =
TokenOrigin.OriginatingLogonSession;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
ExAllocateLocallyUniqueId(&Token->ModifiedId);
}
/* Unlock the token */
SepReleaseTokenLock(Token);
break;
}
default:
{
DPRINT1("Invalid TokenInformationClass: 0x%lx\n",
TokenInformationClass);
Status = STATUS_INVALID_INFO_CLASS;
break;
}
}
Cleanup:
ObDereferenceObject(Token);
}
if (!NT_SUCCESS(Status))
{
DPRINT1("NtSetInformationToken failed with Status 0x%lx\n", Status);
}
return Status;
}
/**
* @brief
* Duplicates a token.
*
* @param[in] ExistingTokenHandle
* An existing token to duplicate.
*
* @param[in] DesiredAccess
* The desired access rights for the new duplicated token.
*
* @param[in] ObjectAttributes
* Object attributes for the new duplicated token.
*
* @param[in] EffectiveOnly
* If set to TRUE, the function removes all the disabled privileges and groups
* of the token to duplicate.
*
* @param[in] TokenType
* Type of token to assign to the duplicated token.
*
* @param[out] NewTokenHandle
* The returned duplicated token handle.
*
* @return
* STATUS_SUCCESS is returned if token duplication has completed successfully.
* STATUS_BAD_IMPERSONATION_LEVEL is returned if the caller erroneously wants
* to raise the impersonation level even though the conditions do not permit
* it. A failure NTSTATUS code is returned otherwise.
*
* @remarks
* Some sources claim 4th param is ImpersonationLevel, but on W2K
* this is certainly NOT true, although I can't say for sure that EffectiveOnly
* is correct either. -Gunnar
* This is true. EffectiveOnly overrides SQOS.EffectiveOnly. - IAI
* NOTE for readers: http://hex.pp.ua/nt/NtDuplicateToken.php is therefore
* wrong in that regard, while MSDN documentation is correct.
*/
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
_Must_inspect_result_
__kernel_entry
NTSTATUS
NTAPI
NtDuplicateToken(
_In_ HANDLE ExistingTokenHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ BOOLEAN EffectiveOnly,
_In_ TOKEN_TYPE TokenType,
_Out_ PHANDLE NewTokenHandle)
{
KPROCESSOR_MODE PreviousMode;
HANDLE hToken;
PTOKEN Token;
PTOKEN NewToken;
PSECURITY_QUALITY_OF_SERVICE CapturedSecurityQualityOfService;
BOOLEAN QoSPresent;
OBJECT_HANDLE_INFORMATION HandleInformation;
NTSTATUS Status;
PAGED_CODE();
if (TokenType != TokenImpersonation &&
TokenType != TokenPrimary)
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
{
return STATUS_INVALID_PARAMETER;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
}
PreviousMode = KeGetPreviousMode();
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
ProbeForWriteHandle(NewTokenHandle);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
Status = SepCaptureSecurityQualityOfService(ObjectAttributes,
PreviousMode,
PagedPool,
FALSE,
&CapturedSecurityQualityOfService,
&QoSPresent);
if (!NT_SUCCESS(Status))
{
DPRINT1("NtDuplicateToken() failed to capture QoS! Status: 0x%x\n", Status);
return Status;
}
Status = ObReferenceObjectByHandle(ExistingTokenHandle,
TOKEN_DUPLICATE,
SeTokenObjectType,
PreviousMode,
(PVOID*)&Token,
&HandleInformation);
if (!NT_SUCCESS(Status))
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
DPRINT1("Failed to reference token (Status 0x%lx)\n", Status);
SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
PreviousMode,
FALSE);
return Status;
}
/*
* Fail, if the original token is an impersonation token and the caller
* tries to raise the impersonation level of the new token above the
* impersonation level of the original token.
*/
if (Token->TokenType == TokenImpersonation)
{
if (QoSPresent &&
CapturedSecurityQualityOfService->ImpersonationLevel >Token->ImpersonationLevel)
{
ObDereferenceObject(Token);
SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
PreviousMode,
FALSE);
return STATUS_BAD_IMPERSONATION_LEVEL;
}
}
/*
* Fail, if a primary token is to be created from an impersonation token
* and and the impersonation level of the impersonation token is below SecurityImpersonation.
*/
if (Token->TokenType == TokenImpersonation &&
TokenType == TokenPrimary &&
Token->ImpersonationLevel < SecurityImpersonation)
{
ObDereferenceObject(Token);
SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
PreviousMode,
FALSE);
return STATUS_BAD_IMPERSONATION_LEVEL;
}
Status = SepDuplicateToken(Token,
ObjectAttributes,
EffectiveOnly,
TokenType,
(QoSPresent ? CapturedSecurityQualityOfService->ImpersonationLevel : SecurityAnonymous),
PreviousMode,
&NewToken);
ObDereferenceObject(Token);
if (NT_SUCCESS(Status))
{
Status = ObInsertObject(NewToken,
NULL,
(DesiredAccess ? DesiredAccess : HandleInformation.GrantedAccess),
0,
NULL,
&hToken);
if (NT_SUCCESS(Status))
{
_SEH2_TRY
{
*NewTokenHandle = hToken;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
}
/* Free the captured structure */
SepReleaseSecurityQualityOfService(CapturedSecurityQualityOfService,
PreviousMode,
FALSE);
return Status;
}
/**
* @brief
* Private routine that iterates over the groups of an
* access token to be adjusted as per on request by the
* caller, where a group can be enabled or disabled.
*
* @param[in] Token
* Access token where its groups are to be enabled or disabled.
*
* @param[in] NewState
* A list of groups with new state attributes to be assigned to
* the token.
*
* @param[in] NewStateCount
* The captured count number of groups in the list.
*
* @param[in] ApplyChanges
* If set to FALSE, the function will only iterate over the token's
* groups without performing any kind of modification. If set to TRUE,
* the changes will be applied immediately when the function has done
* looping the groups.
*
* @param[in] ResetToDefaultStates
* The function will reset the groups in an access token to default
* states if set to TRUE. In such scenario the function ignores
* NewState outright. Otherwise if set to FALSE, the function will
* use NewState to assign the newly attributes to adjust the token's
* groups. SE_GROUP_ENABLED_BY_DEFAULT is a flag indicator that is used
* for such purpose.
*
* @param[out] ChangesMade
* Returns TRUE if changes to token's groups have been made, otherwise
* FALSE is returned. Bear in mind such changes aren't always deterministic.
* See remarks for further details.
*
* @param[out] PreviousGroupsState
* If requested by the caller, the function will return the previous state
* of groups in an access token prior taking action on adjusting the token.
* This is a UM (user mode) pointer and it's prone to raise exceptions
* if such pointer address is not valid.
*
* @param[out] ChangedGroups
* Returns the total number of changed groups in an access token. This
* argument could also indicate the number of groups to be changed if
* the calling thread hasn't chosen to apply the changes yet. A number
* of 0 indicates no groups have been or to be changed because the groups'
* attributes in a token are the same as the ones from NewState given by
* the caller.
*
* @return
* STATUS_SUCCESS is returned if the function has successfully completed
* the operation of adjusting groups in a token. STATUS_CANT_DISABLE_MANDATORY
* is returned if there was an attempt to disable a mandatory group which is
* not possible. STATUS_CANT_ENABLE_DENY_ONLY is returned if there was an attempt
* to enable a "use for Deny only" group which is not allowed, that is, a restricted
* group. STATUS_NOT_ALL_ASSIGNED is returned if not all the groups are actually
* assigned to the token.
*
* @remarks
* Token groups adjusting can be judged to be deterministic or not based on the
* NT status code value. That is, STATUS_SUCCESS indicates the function not only
* has iterated over the whole groups in a token, it also has applied the changes
* thoroughly without impediment and the results perfectly match with the request
* desired by the caller. In this situation the condition is deemed deterministic.
* In a different situation however, if the status code was STATUS_NOT_ALL_ASSIGNED,
* the function would still continue looping the groups in a token and apply the
* changes whenever possible where the respective groups actually exist in the
* token. This kind of situation is deemed as indeterministic.
* For STATUS_CANT_DISABLE_MANDATORY and STATUS_CANT_ENABLE_DENY_ONLY the scenario
* is even more indeterministic as the iteration of groups comes to a halt thus
* leaving all other possible groups to be adjusted.
*/
static
NTSTATUS
SepAdjustGroups(
_In_ PTOKEN Token,
_In_opt_ PSID_AND_ATTRIBUTES NewState,
_In_ ULONG NewStateCount,
_In_ BOOLEAN ApplyChanges,
_In_ BOOLEAN ResetToDefaultStates,
_Out_ PBOOLEAN ChangesMade,
_Out_opt_ PTOKEN_GROUPS PreviousGroupsState,
_Out_ PULONG ChangedGroups)
{
ULONG GroupsInToken, GroupsInList;
ULONG ChangeCount, GroupsCount, NewAttributes;
PAGED_CODE();
/* Ensure that the token we get is not plain garbage */
ASSERT(Token);
/* Initialize the counters and begin the work */
*ChangesMade = FALSE;
GroupsCount = 0;
ChangeCount = 0;
/* Begin looping all the groups in the token */
for (GroupsInToken = 0; GroupsInToken < Token->UserAndGroupCount; GroupsInToken++)
{
/* Does the caller want to reset groups to default states? */
if (ResetToDefaultStates)
{
/*
* SE_GROUP_ENABLED_BY_DEFAULT is a special indicator that informs us
* if a certain group has been enabled by default or not. In case
* a group is enabled by default but it is not currently enabled then
* at that point we must enable it back by default. For now just
* assign the respective SE_GROUP_ENABLED attribute as we'll do the
* eventual work later.
*/
if ((Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_ENABLED_BY_DEFAULT) &&
(Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_ENABLED) == 0)
{
NewAttributes = Token->UserAndGroups[GroupsInToken].Attributes |= SE_GROUP_ENABLED;
}
/*
* Unlike the case above, a group that hasn't been enabled by
* default but it's currently enabled then we must disable
* it back.
*/
if ((Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_ENABLED_BY_DEFAULT) == 0 &&
(Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_ENABLED))
{
NewAttributes = Token->UserAndGroups[GroupsInToken].Attributes & ~SE_GROUP_ENABLED;
}
}
else
{
/* Loop the provided groups in the list then */
for (GroupsInList = 0; GroupsInList < NewStateCount; GroupsInList++)
{
/* Does this group exist in the token? */
if (RtlEqualSid(&Token->UserAndGroups[GroupsInToken].Sid,
&NewState[GroupsInList].Sid))
{
/*
* This is the group that we're looking for.
* However, it could be that the group is a
* mandatory group which we are not allowed
* and cannot disable it.
*/
if ((Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_MANDATORY) &&
(NewState[GroupsInList].Attributes & SE_GROUP_ENABLED) == 0)
{
/* It is mandatory, forget about this group */
DPRINT1("SepAdjustGroups(): The SID group is mandatory!\n");
return STATUS_CANT_DISABLE_MANDATORY;
}
/*
* We've to ensure that apart the group mustn't be
* mandatory, it mustn't be a restricted group as
* well. That is, the group is marked with
* SE_GROUP_USE_FOR_DENY_ONLY flag and no one
* can enable it because it's for "deny" use only.
*/
if ((Token->UserAndGroups[GroupsInToken].Attributes & SE_GROUP_USE_FOR_DENY_ONLY) &&
(NewState[GroupsInList].Attributes & SE_GROUP_ENABLED))
{
/* This group is restricted, forget about it */
DPRINT1("SepAdjustGroups(): The SID group is for use deny only!\n");
return STATUS_CANT_ENABLE_DENY_ONLY;
}
/* Copy the attributes and stop searching */
NewAttributes = NewState[GroupsInList].Attributes;
NewAttributes &= SE_GROUP_ENABLED;
NewAttributes = Token->UserAndGroups[GroupsInToken].Attributes & ~SE_GROUP_ENABLED;
break;
}
/* Did we find the specific group we wanted? */
if (GroupsInList == NewStateCount)
{
/* We didn't, continue with the next token's group */
continue;
}
}
/* Count the group that we found it */
GroupsCount++;
/* Does the token have the same attributes as the caller requested them? */
if (Token->UserAndGroups[GroupsInToken].Attributes != NewAttributes)
{
/*
* No, then it's time to make some adjustment to the
* token's groups. Does the caller want the previous states
* of groups?
*/
if (PreviousGroupsState != NULL)
{
PreviousGroupsState->Groups[ChangeCount] = Token->UserAndGroups[GroupsInToken];
}
/* Time to apply the changes now? */
if (ApplyChanges)
{
/* The caller gave us consent, apply and report that we made changes! */
Token->UserAndGroups[GroupsInToken].Attributes = NewAttributes;
*ChangesMade = TRUE;
}
/* Increment the count change */
ChangeCount++;
}
}
}
/* Report the number of previous saved groups */
if (PreviousGroupsState != NULL)
{
PreviousGroupsState->GroupCount = ChangeCount;
}
/* Report the number of changed groups */
*ChangedGroups = ChangeCount;
/* Did we miss some groups? */
if (!ResetToDefaultStates && (GroupsCount < NewStateCount))
{
/*
* If we're at this stage then we are in a situation
* where the adjust changes done to token's groups is
* not deterministic as the caller might have wanted
* as per NewState parameter.
*/
DPRINT1("SepAdjustGroups(): The token hasn't all the groups assigned!\n");
return STATUS_NOT_ALL_ASSIGNED;
}
return STATUS_SUCCESS;
}
/**
* @brief
* Changes the list of groups by enabling or disabling them
* in an access token. Unlike NtAdjustPrivilegesToken,
* this API routine does not remove groups.
*
* @param[in] TokenHandle
* Token handle where the list of groups SID are to be adjusted.
* The access token must have TOKEN_ADJUST_GROUPS access right
* in order to change the groups in a token. The token must also
* have TOKEN_QUERY access right if the caller requests the previous
* states of groups list, that is, PreviousState is not NULL.
*
* @param[in] ResetToDefault
* If set to TRUE, the function resets the list of groups to default
* enabled and disabled states. NewState is ignored in this case.
* Otherwise if the parameter is set to FALSE, the function expects
* a new list of groups from NewState to be adjusted within the token.
*
* @param[in] NewState
* A new list of groups SID that the function will use it accordingly to
* modify the current list of groups SID of a token.
*
* @param[in] BufferLength
* The length size of the buffer that is pointed by the NewState parameter
* argument, in bytes.
*
* @param[out] PreviousState
* If specified, the function will return to the caller the old list of groups
* SID. If this parameter is NULL, ReturnLength must also be NULL.
*
* @param[out] ReturnLength
* If specified, the function will return the total size length of the old list
* of groups SIDs, in bytes.
*
* @return
* STATUS_SUCCESS is returned if the function has successfully adjusted the
* token's groups. STATUS_INVALID_PARAMETER is returned if the caller has
* submitted one or more invalid parameters, that is, the caller didn't want
* to reset the groups to default state but no NewState argument list has been
* provided. STATUS_BUFFER_TOO_SMALL is returned if the buffer length given
* by the caller is smaller than the required length size. A failure NTSTATUS
* code is returned otherwise.
*/
NTSTATUS
NTAPI
NtAdjustGroupsToken(
_In_ HANDLE TokenHandle,
_In_ BOOLEAN ResetToDefault,
_In_ PTOKEN_GROUPS NewState,
_In_ ULONG BufferLength,
_Out_writes_bytes_to_opt_(BufferLength, *ReturnLength)
PTOKEN_GROUPS PreviousState,
_When_(PreviousState != NULL, _Out_) PULONG ReturnLength)
{
PTOKEN Token;
NTSTATUS Status;
KPROCESSOR_MODE PreviousMode;
ULONG ChangeCount, RequiredLength;
ULONG CapturedCount = 0;
ULONG CapturedLength = 0;
ULONG NewStateSize = 0;
PSID_AND_ATTRIBUTES CapturedGroups = NULL;
BOOLEAN ChangesMade = FALSE;
BOOLEAN LockAndReferenceAcquired = FALSE;
PAGED_CODE();
/*
* If the caller doesn't want to reset the groups of an
* access token to default states then at least we must
* expect a list of groups to be adjusted based on NewState
* parameter. Otherwise bail out because the caller has
* no idea what they're doing.
*/
if (!ResetToDefault && !NewState)
{
DPRINT1("NtAdjustGroupsToken(): The caller hasn't provided any list of groups to adjust!\n");
return STATUS_INVALID_PARAMETER;
}
PreviousMode = ExGetPreviousMode();
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
/* Probe NewState */
if (!ResetToDefault)
{
/* Probe the header */
ProbeForRead(NewState, sizeof(*NewState), sizeof(ULONG));
CapturedCount = NewState->GroupCount;
NewStateSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedCount]);
ProbeForRead(NewState, NewStateSize, sizeof(ULONG));
}
if (PreviousState != NULL)
{
ProbeForWrite(PreviousState, BufferLength, sizeof(ULONG));
ProbeForWrite(ReturnLength, sizeof(*ReturnLength), sizeof(ULONG));
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
else
{
/*
* We're calling directly from the kernel, just retrieve
* the number count of captured groups outright.
*/
if (!ResetToDefault)
{
CapturedCount = NewState->GroupCount;
}
}
/* Time to capture the NewState list */
if (!ResetToDefault)
{
_SEH2_TRY
{
Status = SeCaptureSidAndAttributesArray(NewState->Groups,
CapturedCount,
PreviousMode,
NULL,
0,
PagedPool,
TRUE,
&CapturedGroups,
&CapturedLength);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
if (!NT_SUCCESS(Status))
{
DPRINT1("NtAdjustGroupsToken(): Failed to capture the NewState list of groups (Status 0x%lx)\n", Status);
return Status;
}
}
/* Time to reference the token */
Status = ObReferenceObjectByHandle(TokenHandle,
TOKEN_ADJUST_GROUPS | (PreviousState != NULL ? TOKEN_QUERY : 0),
SeTokenObjectType,
PreviousMode,
(PVOID*)&Token,
NULL);
if (!NT_SUCCESS(Status))
{
/* We couldn't reference the access token, bail out */
DPRINT1("NtAdjustGroupsToken(): Failed to reference the token (Status 0x%lx)\n", Status);
if (CapturedGroups != NULL)
{
SeReleaseSidAndAttributesArray(CapturedGroups,
PreviousMode,
TRUE);
}
goto Quit;
}
/* Lock the token */
SepAcquireTokenLockExclusive(Token);
LockAndReferenceAcquired = TRUE;
/* Count the number of groups to be changed */
Status = SepAdjustGroups(Token,
CapturedGroups,
CapturedCount,
FALSE,
ResetToDefault,
&ChangesMade,
NULL,
&ChangeCount);
/* Does the caller want the previous state of groups? */
if (PreviousState != NULL)
{
/* Calculate the required length */
RequiredLength = FIELD_OFFSET(TOKEN_GROUPS, Groups[ChangeCount]);
/* Return the required length to the caller */
_SEH2_TRY
{
*ReturnLength = RequiredLength;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Bail out and return the exception code */
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(goto Quit);
}
_SEH2_END;
/* The buffer length provided is smaller than the required length, bail out */
if (BufferLength < RequiredLength)
{
Status = STATUS_BUFFER_TOO_SMALL;
goto Quit;
}
}
/*
* Now it's time to apply changes. Wrap the code
* in SEH as we are returning the old groups state
* list to the caller since PreviousState is a
* UM pointer.
*/
_SEH2_TRY
{
Status = SepAdjustGroups(Token,
CapturedGroups,
CapturedCount,
TRUE,
ResetToDefault,
&ChangesMade,
PreviousState,
&ChangeCount);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Bail out and return the exception code */
Status = _SEH2_GetExceptionCode();
/* Force the write as we touched the token still */
ChangesMade = TRUE;
_SEH2_YIELD(goto Quit);
}
_SEH2_END;
Quit:
/* Allocate a new ID for the token as we made changes */
if (ChangesMade)
{
ExAllocateLocallyUniqueId(&Token->ModifiedId);
}
/* Have we successfully acquired the lock and referenced the token before? */
if (LockAndReferenceAcquired)
{
/* Unlock and dereference the token */
SepReleaseTokenLock(Token);
ObDereferenceObject(Token);
}
/* Release the captured groups */
if (CapturedGroups != NULL)
{
SeReleaseSidAndAttributesArray(CapturedGroups,
PreviousMode,
TRUE);
}
return Status;
}
/**
* @brief
* Removes a certain amount of privileges of a token based upon the request
* by the caller.
*
* @param[in,out] Token
* Token handle where the privileges are about to be modified.
*
* @param[in] DisableAllPrivileges
* If set to TRUE, the function disables all the privileges.
*
* @param[in] NewState
* A new list of privileges that the function will use it accordingly to
* either disable or enable the said privileges and change them.
*
* @param[in] NewStateCount
* The new total number count of privileges.
*
* @param[out] PreviousState
* If specified, the function will return the previous state list of privileges.
*
* @param[in] ApplyChanges
* If set to TRUE, the function will immediatelly apply the changes onto the
* token's privileges.
*
* @param[out] ChangedPrivileges
* The returned count number of changed privileges.
*
* @param[out] ChangesMade
* If TRUE, the function has made changes to the token's privileges. FALSE
* otherwise.
*
* @return
* Returns STATUS_SUCCESS if the function has successfully changed the list
* of privileges. STATUS_NOT_ALL_ASSIGNED is returned if not every privilege
* has been changed.
*/
static
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
NTSTATUS
SepAdjustPrivileges(
_Inout_ PTOKEN Token,
_In_ BOOLEAN DisableAllPrivileges,
_In_opt_ PLUID_AND_ATTRIBUTES NewState,
_In_ ULONG NewStateCount,
_Out_opt_ PTOKEN_PRIVILEGES PreviousState,
_In_ BOOLEAN ApplyChanges,
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
_Out_ PULONG ChangedPrivileges,
_Out_ PBOOLEAN ChangesMade)
{
ULONG i, j, PrivilegeCount, ChangeCount, NewAttributes;
PAGED_CODE();
/* Count the found privileges and those that need to be changed */
PrivilegeCount = 0;
ChangeCount = 0;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
*ChangesMade = FALSE;
/* Loop all privileges in the token */
for (i = 0; i < Token->PrivilegeCount; i++)
{
/* Shall all of them be disabled? */
if (DisableAllPrivileges)
{
/* The new attributes are the old ones, but disabled */
NewAttributes = Token->Privileges[i].Attributes & ~SE_PRIVILEGE_ENABLED;
}
else
{
/* Otherwise loop all provided privileges */
for (j = 0; j < NewStateCount; j++)
{
/* Check if this is the LUID we are looking for */
if (RtlEqualLuid(&Token->Privileges[i].Luid, &NewState[j].Luid))
{
DPRINT("Found privilege\n");
/* Copy SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_REMOVED */
NewAttributes = NewState[j].Attributes;
NewAttributes &= (SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_REMOVED);
NewAttributes |= Token->Privileges[i].Attributes & ~SE_PRIVILEGE_ENABLED;
/* Stop looking */
break;
}
}
/* Check if we didn't find the privilege */
if (j == NewStateCount)
{
/* Continue with the token's next privilege */
continue;
}
}
/* We found a privilege, count it */
PrivilegeCount++;
/* Does the privilege need to be changed? */
if (Token->Privileges[i].Attributes != NewAttributes)
{
/* Does the caller want the old privileges? */
if (PreviousState != NULL)
{
/* Copy the old privilege */
PreviousState->Privileges[ChangeCount] = Token->Privileges[i];
}
/* Does the caller want to apply the changes? */
if (ApplyChanges)
{
/* Shall we remove the privilege? */
if (NewAttributes & SE_PRIVILEGE_REMOVED)
{
/* Set the token as disabled and update flags for it */
Token->Privileges[i].Attributes &= ~SE_PRIVILEGE_ENABLED;
SepUpdateSinglePrivilegeFlagToken(Token, i);
/* Remove the privilege */
SepRemovePrivilegeToken(Token, i);
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
*ChangesMade = TRUE;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Fix the running index and continue with next one */
i--;
continue;
}
/* Set the new attributes and update flags */
Token->Privileges[i].Attributes = NewAttributes;
SepUpdateSinglePrivilegeFlagToken(Token, i);
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
*ChangesMade = TRUE;
}
/* Increment the change count */
ChangeCount++;
}
}
/* Set the number of saved privileges */
if (PreviousState != NULL)
PreviousState->PrivilegeCount = ChangeCount;
/* Return the number of changed privileges */
*ChangedPrivileges = ChangeCount;
/* Check if we missed some */
if (!DisableAllPrivileges && (PrivilegeCount < NewStateCount))
{
return STATUS_NOT_ALL_ASSIGNED;
}
return STATUS_SUCCESS;
}
/**
* @brief
* Removes a certain amount of privileges of a token based upon the request
* by the caller.
*
* @param[in,out] Token
* Token handle where the privileges are about to be modified.
*
* @param[in] DisableAllPrivileges
* If set to TRUE, the function disables all the privileges.
*
* @param[in] NewState
* A new list of privileges that the function will use it accordingly to
* either disable or enable the said privileges and change them.
*
* @param[in] NewStateCount
* The new total number count of privileges.
*
* @param[out] PreviousState
* If specified, the function will return the previous state list of privileges.
*
* @param[in] ApplyChanges
* If set to TRUE, the function will immediatelly apply the changes onto the
* token's privileges.
*
* @param[out] ChangedPrivileges
* The returned count number of changed privileges.
*
* @param[out] ChangesMade
* If TRUE, the function has made changes to the token's privileges. FALSE
* otherwise.
*
* @return
* Returns STATUS_SUCCESS if the function has successfully changed the list
* of privileges. STATUS_NOT_ALL_ASSIGNED is returned if not every privilege
* has been changed.
*/
_Must_inspect_result_
__kernel_entry
NTSTATUS
NTAPI
NtAdjustPrivilegesToken(
_In_ HANDLE TokenHandle,
_In_ BOOLEAN DisableAllPrivileges,
_In_opt_ PTOKEN_PRIVILEGES NewState,
_In_ ULONG BufferLength,
_Out_writes_bytes_to_opt_(BufferLength,*ReturnLength)
PTOKEN_PRIVILEGES PreviousState,
_When_(PreviousState!=NULL, _Out_) PULONG ReturnLength)
{
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
NTSTATUS Status;
KPROCESSOR_MODE PreviousMode;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
PTOKEN Token;
PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
ULONG CapturedCount = 0;
ULONG CapturedLength = 0;
ULONG NewStateSize = 0;
ULONG ChangeCount;
ULONG RequiredLength;
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
BOOLEAN ChangesMade = FALSE;
PAGED_CODE();
DPRINT("NtAdjustPrivilegesToken() called\n");
/* Fail, if we do not disable all privileges but NewState is NULL */
if (DisableAllPrivileges == FALSE && NewState == NULL)
return STATUS_INVALID_PARAMETER;
PreviousMode = KeGetPreviousMode();
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
/* Probe NewState */
if (DisableAllPrivileges == FALSE)
{
/* First probe the header */
ProbeForRead(NewState, sizeof(TOKEN_PRIVILEGES), sizeof(ULONG));
CapturedCount = NewState->PrivilegeCount;
NewStateSize = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[CapturedCount]);
ProbeForRead(NewState, NewStateSize, sizeof(ULONG));
}
/* Probe PreviousState and ReturnLength */
if (PreviousState != NULL)
{
ProbeForWrite(PreviousState, BufferLength, sizeof(ULONG));
ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG));
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
else
{
/* This is kernel mode, we trust the caller */
if (DisableAllPrivileges == FALSE)
CapturedCount = NewState->PrivilegeCount;
}
/* Do we need to capture the new state? */
if (DisableAllPrivileges == FALSE)
{
_SEH2_TRY
{
/* Capture the new state array of privileges */
Status = SeCaptureLuidAndAttributesArray(NewState->Privileges,
CapturedCount,
PreviousMode,
NULL,
0,
PagedPool,
TRUE,
&CapturedPrivileges,
&CapturedLength);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
if (!NT_SUCCESS(Status))
return Status;
}
/* Reference the token */
Status = ObReferenceObjectByHandle(TokenHandle,
TOKEN_ADJUST_PRIVILEGES | (PreviousState != NULL ? TOKEN_QUERY : 0),
SeTokenObjectType,
PreviousMode,
(PVOID*)&Token,
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to reference token (Status 0x%lx)\n", Status);
/* Release the captured privileges */
if (CapturedPrivileges != NULL)
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
{
SeReleaseLuidAndAttributesArray(CapturedPrivileges,
PreviousMode,
TRUE);
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
}
return Status;
}
/* Lock the token */
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
SepAcquireTokenLockExclusive(Token);
/* Count the privileges that need to be changed, do not apply them yet */
Status = SepAdjustPrivileges(Token,
DisableAllPrivileges,
CapturedPrivileges,
CapturedCount,
NULL,
FALSE,
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
&ChangeCount,
&ChangesMade);
/* Check if the caller asked for the previous state */
if (PreviousState != NULL)
{
/* Calculate the required length */
RequiredLength = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[ChangeCount]);
/* Try to return the required buffer length */
_SEH2_TRY
{
*ReturnLength = RequiredLength;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Do cleanup and return the exception code */
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(goto Cleanup);
}
_SEH2_END;
/* Fail, if the buffer length is smaller than the required length */
if (BufferLength < RequiredLength)
{
Status = STATUS_BUFFER_TOO_SMALL;
goto Cleanup;
}
}
/* Now enter SEH, since we might return the old privileges */
_SEH2_TRY
{
/* This time apply the changes */
Status = SepAdjustPrivileges(Token,
DisableAllPrivileges,
CapturedPrivileges,
CapturedCount,
PreviousState,
TRUE,
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
&ChangeCount,
&ChangesMade);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Do cleanup and return the exception code */
Status = _SEH2_GetExceptionCode();
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
ChangesMade = TRUE; // Force write.
_SEH2_YIELD(goto Cleanup);
}
_SEH2_END;
Cleanup:
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Touch the token if we made changes */
if (ChangesMade)
ExAllocateLocallyUniqueId(&Token->ModifiedId);
/* Unlock and dereference the token */
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
SepReleaseTokenLock(Token);
ObDereferenceObject(Token);
/* Release the captured privileges */
if (CapturedPrivileges != NULL)
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
{
SeReleaseLuidAndAttributesArray(CapturedPrivileges,
PreviousMode,
TRUE);
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
}
DPRINT ("NtAdjustPrivilegesToken() done\n");
return Status;
}
/**
* @brief
* Creates an access token.
*
* @param[out] TokenHandle
* The returned created token handle to the caller.
*
* @param[in] DesiredAccess
* The desired access rights for the token that we're creating.
*
* @param[in] ObjectAttributes
* The object attributes for the token object that we're creating.
*
* @param[in] TokenType
* The type of token to assign for the newly created token.
*
* @param[in] AuthenticationId
* Authentication ID that represents the token's identity.
*
* @param[in] ExpirationTime
* Expiration time for the token. If set to -1, the token never expires.
*
* @param[in] TokenUser
* The main user entity for the token to assign.
*
* @param[in] TokenGroups
* Group list of SIDs for the token to assign.
*
* @param[in] TokenPrivileges
* Privileges for the token.
*
* @param[in] TokenOwner
* The main user that owns the newly created token.
*
* @param[in] TokenPrimaryGroup
* The primary group that represents as the main group of the token.
*
* @param[in] TokenDefaultDacl
* Discretionary access control list for the token. This limits on how
* the token can be used, accessed and used by whom.
*
* @param[in] TokenSource
* The source origin of the token who creates it.
*
* @return
* Returns STATUS_SUCCESS if the function has successfully created the token.
* A failure NTSTATUS code is returned otherwise.
*/
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
__kernel_entry
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
NTSTATUS
NTAPI
NtCreateToken(
_Out_ PHANDLE TokenHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ TOKEN_TYPE TokenType,
_In_ PLUID AuthenticationId,
_In_ PLARGE_INTEGER ExpirationTime,
_In_ PTOKEN_USER TokenUser,
_In_ PTOKEN_GROUPS TokenGroups,
_In_ PTOKEN_PRIVILEGES TokenPrivileges,
_In_opt_ PTOKEN_OWNER TokenOwner,
_In_ PTOKEN_PRIMARY_GROUP TokenPrimaryGroup,
_In_opt_ PTOKEN_DEFAULT_DACL TokenDefaultDacl,
_In_ PTOKEN_SOURCE TokenSource)
{
HANDLE hToken;
KPROCESSOR_MODE PreviousMode;
ULONG PrivilegeCount, GroupCount;
PSID OwnerSid, PrimaryGroupSid;
PACL DefaultDacl;
LARGE_INTEGER LocalExpirationTime = {{0, 0}};
LUID LocalAuthenticationId;
TOKEN_SOURCE LocalTokenSource;
SECURITY_QUALITY_OF_SERVICE LocalSecurityQos;
PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
PSID_AND_ATTRIBUTES CapturedUser = NULL;
PSID_AND_ATTRIBUTES CapturedGroups = NULL;
PSID CapturedOwnerSid = NULL;
PSID CapturedPrimaryGroupSid = NULL;
PACL CapturedDefaultDacl = NULL;
ULONG PrivilegesLength, UserLength, GroupsLength;
NTSTATUS Status;
PAGED_CODE();
PreviousMode = ExGetPreviousMode();
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
ProbeForWriteHandle(TokenHandle);
if (ObjectAttributes != NULL)
{
ProbeForRead(ObjectAttributes,
sizeof(OBJECT_ATTRIBUTES),
sizeof(ULONG));
LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService;
}
ProbeForRead(AuthenticationId,
sizeof(LUID),
sizeof(ULONG));
LocalAuthenticationId = *AuthenticationId;
LocalExpirationTime = ProbeForReadLargeInteger(ExpirationTime);
ProbeForRead(TokenUser,
sizeof(TOKEN_USER),
sizeof(ULONG));
ProbeForRead(TokenGroups,
sizeof(TOKEN_GROUPS),
sizeof(ULONG));
GroupCount = TokenGroups->GroupCount;
ProbeForRead(TokenPrivileges,
sizeof(TOKEN_PRIVILEGES),
sizeof(ULONG));
PrivilegeCount = TokenPrivileges->PrivilegeCount;
if (TokenOwner != NULL)
{
ProbeForRead(TokenOwner,
sizeof(TOKEN_OWNER),
sizeof(ULONG));
OwnerSid = TokenOwner->Owner;
}
else
{
OwnerSid = NULL;
}
ProbeForRead(TokenPrimaryGroup,
sizeof(TOKEN_PRIMARY_GROUP),
sizeof(ULONG));
PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup;
if (TokenDefaultDacl != NULL)
{
ProbeForRead(TokenDefaultDacl,
sizeof(TOKEN_DEFAULT_DACL),
sizeof(ULONG));
DefaultDacl = TokenDefaultDacl->DefaultDacl;
}
else
{
DefaultDacl = NULL;
}
ProbeForRead(TokenSource,
sizeof(TOKEN_SOURCE),
sizeof(ULONG));
LocalTokenSource = *TokenSource;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
else
{
if (ObjectAttributes != NULL)
LocalSecurityQos = *(SECURITY_QUALITY_OF_SERVICE*)ObjectAttributes->SecurityQualityOfService;
LocalAuthenticationId = *AuthenticationId;
LocalExpirationTime = *ExpirationTime;
GroupCount = TokenGroups->GroupCount;
PrivilegeCount = TokenPrivileges->PrivilegeCount;
OwnerSid = TokenOwner ? TokenOwner->Owner : NULL;
PrimaryGroupSid = TokenPrimaryGroup->PrimaryGroup;
DefaultDacl = TokenDefaultDacl ? TokenDefaultDacl->DefaultDacl : NULL;
LocalTokenSource = *TokenSource;
}
/* Check token type */
if ((TokenType < TokenPrimary) ||
(TokenType > TokenImpersonation))
{
return STATUS_BAD_TOKEN_TYPE;
}
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
/* Check for token creation privilege */
if (!SeSinglePrivilegeCheck(SeCreateTokenPrivilege, PreviousMode))
{
return STATUS_PRIVILEGE_NOT_HELD;
}
/* Capture the user SID and attributes */
Status = SeCaptureSidAndAttributesArray(&TokenUser->User,
1,
PreviousMode,
NULL,
0,
PagedPool,
FALSE,
&CapturedUser,
&UserLength);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
/* Capture the groups SID and attributes array */
Status = SeCaptureSidAndAttributesArray(&TokenGroups->Groups[0],
GroupCount,
PreviousMode,
NULL,
0,
PagedPool,
FALSE,
&CapturedGroups,
&GroupsLength);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
/* Capture privileges */
Status = SeCaptureLuidAndAttributesArray(&TokenPrivileges->Privileges[0],
PrivilegeCount,
PreviousMode,
NULL,
0,
PagedPool,
FALSE,
&CapturedPrivileges,
&PrivilegesLength);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
/* Capture the token owner SID */
if (TokenOwner != NULL)
{
Status = SepCaptureSid(OwnerSid,
PreviousMode,
PagedPool,
FALSE,
&CapturedOwnerSid);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
}
/* Capture the token primary group SID */
Status = SepCaptureSid(PrimaryGroupSid,
PreviousMode,
PagedPool,
FALSE,
&CapturedPrimaryGroupSid);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
/* Capture DefaultDacl */
if (DefaultDacl != NULL)
{
Status = SepCaptureAcl(DefaultDacl,
PreviousMode,
NonPagedPool,
FALSE,
&CapturedDefaultDacl);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
}
/* Call the internal function */
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
Status = SepCreateToken(&hToken,
PreviousMode,
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
DesiredAccess,
ObjectAttributes,
TokenType,
LocalSecurityQos.ImpersonationLevel,
&LocalAuthenticationId,
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
&LocalExpirationTime,
CapturedUser,
GroupCount,
CapturedGroups,
GroupsLength,
PrivilegeCount,
CapturedPrivileges,
CapturedOwnerSid,
CapturedPrimaryGroupSid,
CapturedDefaultDacl,
&LocalTokenSource,
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
FALSE);
if (NT_SUCCESS(Status))
{
_SEH2_TRY
{
*TokenHandle = hToken;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
Cleanup:
/* Release what we captured */
SeReleaseSidAndAttributesArray(CapturedUser, PreviousMode, FALSE);
SeReleaseSidAndAttributesArray(CapturedGroups, PreviousMode, FALSE);
SeReleaseLuidAndAttributesArray(CapturedPrivileges, PreviousMode, FALSE);
SepReleaseSid(CapturedOwnerSid, PreviousMode, FALSE);
SepReleaseSid(CapturedPrimaryGroupSid, PreviousMode, FALSE);
SepReleaseAcl(CapturedDefaultDacl, PreviousMode, FALSE);
return Status;
}
/**
* @brief
* Opens a token that is tied to a thread handle.
*
* @param[out] ThreadHandle
* Thread handle where the token is about to be opened.
*
* @param[in] DesiredAccess
* The request access right for the token.
*
* @param[in] OpenAsSelf
* If set to TRUE, the access check will be made with the security context
* of the process of the calling thread (opening as self). Otherwise the access
* check will be made with the security context of the calling thread instead.
*
* @param[in] HandleAttributes
* Handle attributes for the opened thread token handle.
*
* @param[out] TokenHandle
* The opened token handle returned to the caller for use.
*
* @return
* Returns STATUS_SUCCESS if the function has successfully opened the thread
* token. STATUS_CANT_OPEN_ANONYMOUS is returned if a token has SecurityAnonymous
* as impersonation level and we cannot open it. A failure NTSTATUS code is returned
* otherwise.
*/
NTSTATUS
NTAPI
NtOpenThreadTokenEx(
_In_ HANDLE ThreadHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ BOOLEAN OpenAsSelf,
_In_ ULONG HandleAttributes,
_Out_ PHANDLE TokenHandle)
{
PETHREAD Thread;
HANDLE hToken;
PTOKEN Token, NewToken = NULL, PrimaryToken;
BOOLEAN CopyOnOpen, EffectiveOnly;
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
SE_IMPERSONATION_STATE ImpersonationState;
OBJECT_ATTRIBUTES ObjectAttributes;
SECURITY_DESCRIPTOR SecurityDescriptor;
PACL Dacl = NULL;
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status;
BOOLEAN RestoreImpersonation = FALSE;
PAGED_CODE();
PreviousMode = ExGetPreviousMode();
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
ProbeForWriteHandle(TokenHandle);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
/* Validate object attributes */
HandleAttributes = ObpValidateAttributes(HandleAttributes, PreviousMode);
/*
* At first open the thread token for information access and verify
* that the token associated with thread is valid.
*/
Status = ObReferenceObjectByHandle(ThreadHandle, THREAD_QUERY_INFORMATION,
PsThreadType, PreviousMode, (PVOID*)&Thread,
NULL);
if (!NT_SUCCESS(Status))
{
return Status;
}
Token = PsReferenceImpersonationToken(Thread, &CopyOnOpen, &EffectiveOnly,
&ImpersonationLevel);
if (Token == NULL)
{
ObDereferenceObject(Thread);
return STATUS_NO_TOKEN;
}
if (ImpersonationLevel == SecurityAnonymous)
{
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
PsDereferenceImpersonationToken(Token);
ObDereferenceObject(Thread);
return STATUS_CANT_OPEN_ANONYMOUS;
}
/*
* Revert to self if OpenAsSelf is specified.
*/
if (OpenAsSelf)
{
RestoreImpersonation = PsDisableImpersonation(PsGetCurrentThread(),
&ImpersonationState);
}
if (CopyOnOpen)
{
PrimaryToken = PsReferencePrimaryToken(Thread->ThreadsProcess);
Status = SepCreateImpersonationTokenDacl(Token, PrimaryToken, &Dacl);
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
ObFastDereferenceObject(&Thread->ThreadsProcess->Token, PrimaryToken);
if (NT_SUCCESS(Status))
{
if (Dacl)
{
Status = RtlCreateSecurityDescriptor(&SecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION);
if (!NT_SUCCESS(Status))
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
{
DPRINT1("NtOpenThreadTokenEx(): Failed to create a security descriptor (Status 0x%lx)\n", Status);
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
}
Status = RtlSetDaclSecurityDescriptor(&SecurityDescriptor, TRUE, Dacl,
FALSE);
if (!NT_SUCCESS(Status))
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
{
DPRINT1("NtOpenThreadTokenEx(): Failed to set a DACL to the security descriptor (Status 0x%lx)\n", Status);
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
}
}
InitializeObjectAttributes(&ObjectAttributes, NULL, HandleAttributes,
NULL, Dacl ? &SecurityDescriptor : NULL);
Status = SepDuplicateToken(Token, &ObjectAttributes, EffectiveOnly,
TokenImpersonation, ImpersonationLevel,
KernelMode, &NewToken);
if (!NT_SUCCESS(Status))
{
DPRINT1("NtOpenThreadTokenEx(): Failed to duplicate the token (Status 0x%lx)\n", Status);
}
ObReferenceObject(NewToken);
Status = ObInsertObject(NewToken, NULL, DesiredAccess, 0, NULL,
&hToken);
if (!NT_SUCCESS(Status))
{
DPRINT1("NtOpenThreadTokenEx(): Failed to insert the token object (Status 0x%lx)\n", Status);
}
}
else
{
DPRINT1("NtOpenThreadTokenEx(): Failed to impersonate token from DACL (Status 0x%lx)\n", Status);
}
}
else
{
Status = ObOpenObjectByPointer(Token, HandleAttributes,
NULL, DesiredAccess, SeTokenObjectType,
PreviousMode, &hToken);
if (!NT_SUCCESS(Status))
{
DPRINT1("NtOpenThreadTokenEx(): Failed to open the object (Status 0x%lx)\n", Status);
}
}
if (Dacl) ExFreePoolWithTag(Dacl, TAG_ACL);
if (RestoreImpersonation)
{
PsRestoreImpersonation(PsGetCurrentThread(), &ImpersonationState);
}
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
ObDereferenceObject(Token);
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
if (NT_SUCCESS(Status) && CopyOnOpen)
{
Status = PsImpersonateClient(Thread, NewToken, FALSE, EffectiveOnly, ImpersonationLevel);
if (!NT_SUCCESS(Status))
{
DPRINT1("NtOpenThreadTokenEx(): Failed to impersonate the client (Status 0x%lx)\n", Status);
}
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
}
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
if (NewToken) ObDereferenceObject(NewToken);
ObDereferenceObject(Thread);
if (NT_SUCCESS(Status))
{
_SEH2_TRY
{
*TokenHandle = hToken;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
return Status;
}
/**
* @brief
* Opens a token that is tied to a thread handle.
*
* @param[out] ThreadHandle
* Thread handle where the token is about to be opened.
*
* @param[in] DesiredAccess
* The request access right for the token.
*
* @param[in] OpenAsSelf
* If set to TRUE, the access check will be made with the security context
* of the process of the calling thread (opening as self). Otherwise the access
* check will be made with the security context of the calling thread instead.
*
* @param[out] TokenHandle
* The opened token handle returned to the caller for use.
*
* @return
* See NtOpenThreadTokenEx.
*/
NTSTATUS
NTAPI
NtOpenThreadToken(
_In_ HANDLE ThreadHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ BOOLEAN OpenAsSelf,
_Out_ PHANDLE TokenHandle)
{
return NtOpenThreadTokenEx(ThreadHandle, DesiredAccess, OpenAsSelf, 0,
TokenHandle);
}
/**
* @brief
* Compares tokens if they're equal or not.
*
* @param[in] FirstToken
* The first token.
*
* @param[in] SecondToken
* The second token.
*
* @param[out] Equal
* The retrieved value which determines if the tokens are
* equal or not.
*
* @return
* Returns STATUS_SUCCESS, otherwise it returns a failure NTSTATUS code.
*/
NTSTATUS
NTAPI
NtCompareTokens(
_In_ HANDLE FirstTokenHandle,
_In_ HANDLE SecondTokenHandle,
_Out_ PBOOLEAN Equal)
{
KPROCESSOR_MODE PreviousMode;
PTOKEN FirstToken, SecondToken;
BOOLEAN IsEqual;
NTSTATUS Status;
PAGED_CODE();
PreviousMode = ExGetPreviousMode();
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
ProbeForWriteBoolean(Equal);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
Status = ObReferenceObjectByHandle(FirstTokenHandle,
TOKEN_QUERY,
SeTokenObjectType,
PreviousMode,
(PVOID*)&FirstToken,
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("ObReferenceObjectByHandle() failed (Status 0x%lx)\n", Status);
return Status;
}
Status = ObReferenceObjectByHandle(SecondTokenHandle,
TOKEN_QUERY,
SeTokenObjectType,
PreviousMode,
(PVOID*)&SecondToken,
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("ObReferenceObjectByHandle() failed (Status 0x%lx)\n", Status);
ObDereferenceObject(FirstToken);
return Status;
}
if (FirstToken != SecondToken)
{
Status = SepCompareTokens(FirstToken,
SecondToken,
&IsEqual);
}
else
{
IsEqual = TRUE;
}
ObDereferenceObject(SecondToken);
[NTOS:SE] Overhaul the token management code. - Overhaul SepCreateToken() and SepDuplicateToken() so that they implement the "variable information area" of the token, where immutable lists of user & groups and privileges reside, and the "dynamic information area" (allocated separately in paged pool), where mutable data such as the token's default DACL is stored. Perform the necessary adaptations in SepDeleteToken() and in NtSetInformationToken(). - Actually dereference the token's logon session, when needed, in the 'TokenSessionReference' case in NtSetInformationToken(). - Overhaul SepFindPrimaryGroupAndDefaultOwner() so that it returns the indices of candidate primary group and default owner within the token's user & groups array. This allows for fixing the 'TokenOwner' and 'TokenPrimaryGroup' cases of NtSetInformationToken(), since the owner or primary group being set *MUST* already exist in the token's user & groups array (as a by-product, memory corruptions that existed before due to the broken way of setting these properties disappear too). - Lock tokens every time operations are performed on them (NOTE: we still use a global token lock!). - Touch the ModifiedId LUID member of tokens everytime a write operation (property change, etc...) is made on them. - Fix some group attributes in the SYSTEM process token, SepCreateSystemProcessToken(). - Make the SeCreateTokenPrivilege mandatory when calling NtCreateToken(). - Update the token pool tags. - Explicitly use the Ex*ResourceLite() versions of the locking functions in the token locking macros.
2018-06-26 20:50:53 +00:00
ObDereferenceObject(FirstToken);
if (NT_SUCCESS(Status))
{
_SEH2_TRY
{
*Equal = IsEqual;
}
_SEH2_EXCEPT(ExSystemExceptionFilter())
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
return Status;
}
/**
* @brief
* Creates an access token in a restricted form
* from the original existing token, that is, such
* action is called filtering.
*
* @param[in] ExistingTokenHandle
* A handle to an access token which is to be filtered.
*
* @param[in] Flags
* Privilege flag options. This parameter argument influences how the
* token's privileges are filtered. For further details see remarks.
*
* @param[in] SidsToDisable
* Array of SIDs to disable. The action of doing so assigns the
* SE_GROUP_USE_FOR_DENY_ONLY attribute to the respective group
* SID and takes away SE_GROUP_ENABLED and SE_GROUP_ENABLED_BY_DEFAULT.
* This parameter can be NULL. This can be a UM pointer.
*
* @param[in] PrivilegesToDelete
* Array of privileges to delete. The function will walk within this
* array to determine if the specified privileges do exist in the
* access token. Any missing privileges gets ignored. This parameter
* can be NULL. This can be a UM pointer.
*
* @param[in] RestrictedSids
* An array list of restricted groups SID to be added in the access
* token. A token that is already restricted the newly added restricted
* SIDs are redundant information in addition to the existing restricted
* SIDs in the token. This parameter can be NULL. This can be a UM pointer.
*
* @param[out] NewTokenHandle
* A new handle to the restricted (filtered) access token. This can be a
* UM pointer.
*
* @return
* Returns STATUS_SUCCESS if the routine has successfully filtered the
* access token. STATUS_INVALID_PARAMETER is returned if one or more
* parameters are not valid (see SepPerformTokenFiltering routine call
* for more information). A failure NTSTATUS code is returned otherwise.
*
* @remarks
* The Flags parameter determines the final outcome of how the privileges
* in an access token are filtered. This parameter can take these supported
* values (these can be combined):
*
* 0 -- Filter the token's privileges in the usual way. The function expects
* that the caller MUST PROVIDE a valid array list of privileges to be
* deleted (that is, PrivilegesToDelete MUSTN'T BE NULL).
*
* DISABLE_MAX_PRIVILEGE -- Disables (deletes) all the privileges except SeChangeNotifyPrivilege
* in the new access token. Bear in mind if this flag is specified
* the routine ignores PrivilegesToDelete.
*
* SANDBOX_INERT -- Stores the TOKEN_SANDBOX_INERT token flag within the access token.
*
* LUA_TOKEN -- The newly filtered access token is a LUA token. This flag is not
* supported in Windows Server 2003.
*
* WRITE_RESTRICTED -- The newly filtered token has the restricted SIDs that are
* considered only when evaluating write access onto the token.
* This value is not supported in Windows Server 2003.
*/
NTSTATUS
NTAPI
NtFilterToken(
_In_ HANDLE ExistingTokenHandle,
_In_ ULONG Flags,
_In_opt_ PTOKEN_GROUPS SidsToDisable,
_In_opt_ PTOKEN_PRIVILEGES PrivilegesToDelete,
_In_opt_ PTOKEN_GROUPS RestrictedSids,
_Out_ PHANDLE NewTokenHandle)
{
PTOKEN Token, FilteredToken;
HANDLE FilteredTokenHandle;
NTSTATUS Status;
KPROCESSOR_MODE PreviousMode;
OBJECT_HANDLE_INFORMATION HandleInfo;
ULONG ResultLength;
ULONG CapturedSidsCount = 0;
ULONG CapturedPrivilegesCount = 0;
ULONG CapturedRestrictedSidsCount = 0;
ULONG ProbeSize = 0;
PSID_AND_ATTRIBUTES CapturedSids = NULL;
PSID_AND_ATTRIBUTES CapturedRestrictedSids = NULL;
PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
PAGED_CODE();
PreviousMode = ExGetPreviousMode();
_SEH2_TRY
{
/* Probe SidsToDisable */
if (SidsToDisable != NULL)
{
/* Probe the header */
ProbeForRead(SidsToDisable, sizeof(*SidsToDisable), sizeof(ULONG));
CapturedSidsCount = SidsToDisable->GroupCount;
ProbeSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedSidsCount]);
ProbeForRead(SidsToDisable, ProbeSize, sizeof(ULONG));
}
/* Probe PrivilegesToDelete */
if (PrivilegesToDelete != NULL)
{
/* Probe the header */
ProbeForRead(PrivilegesToDelete, sizeof(*PrivilegesToDelete), sizeof(ULONG));
CapturedPrivilegesCount = PrivilegesToDelete->PrivilegeCount;
ProbeSize = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[CapturedPrivilegesCount]);
ProbeForRead(PrivilegesToDelete, ProbeSize, sizeof(ULONG));
}
/* Probe RestrictedSids */
if (RestrictedSids != NULL)
{
/* Probe the header */
ProbeForRead(RestrictedSids, sizeof(*RestrictedSids), sizeof(ULONG));
CapturedRestrictedSidsCount = RestrictedSids->GroupCount;
ProbeSize = FIELD_OFFSET(TOKEN_GROUPS, Groups[CapturedRestrictedSidsCount]);
ProbeForRead(RestrictedSids, ProbeSize, sizeof(ULONG));
}
/* Probe the handle */
ProbeForWriteHandle(NewTokenHandle);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
/* Reference the token and do the job */
Status = ObReferenceObjectByHandle(ExistingTokenHandle,
TOKEN_DUPLICATE,
SeTokenObjectType,
PreviousMode,
(PVOID*)&Token,
&HandleInfo);
if (!NT_SUCCESS(Status))
{
DPRINT1("NtFilterToken(): Failed to reference the token (Status 0x%lx)\n", Status);
return Status;
}
/* Lock the token */
SepAcquireTokenLockExclusive(Token);
/* Capture the group SIDs */
if (SidsToDisable != NULL)
{
Status = SeCaptureSidAndAttributesArray(SidsToDisable->Groups,
CapturedSidsCount,
PreviousMode,
NULL,
0,
PagedPool,
TRUE,
&CapturedSids,
&ResultLength);
if (!NT_SUCCESS(Status))
{
DPRINT1("NtFilterToken(): Failed to capture the SIDs (Status 0x%lx)\n", Status);
goto Quit;
}
}
/* Capture the privileges */
if (PrivilegesToDelete != NULL)
{
Status = SeCaptureLuidAndAttributesArray(PrivilegesToDelete->Privileges,
CapturedPrivilegesCount,
PreviousMode,
NULL,
0,
PagedPool,
TRUE,
&CapturedPrivileges,
&ResultLength);
if (!NT_SUCCESS(Status))
{
DPRINT1("NtFilterToken(): Failed to capture the privileges (Status 0x%lx)\n", Status);
goto Quit;
}
}
/* Capture the restricted SIDs */
if (RestrictedSids != NULL)
{
Status = SeCaptureSidAndAttributesArray(RestrictedSids->Groups,
CapturedRestrictedSidsCount,
PreviousMode,
NULL,
0,
PagedPool,
TRUE,
&CapturedRestrictedSids,
&ResultLength);
if (!NT_SUCCESS(Status))
{
DPRINT1("NtFilterToken(): Failed to capture the restricted SIDs (Status 0x%lx)\n", Status);
goto Quit;
}
}
/* Call the internal API so that it can filter the token for us */
Status = SepPerformTokenFiltering(Token,
CapturedPrivileges,
CapturedSids,
CapturedRestrictedSids,
CapturedPrivilegesCount,
CapturedSidsCount,
CapturedRestrictedSidsCount,
Flags,
PreviousMode,
&FilteredToken);
if (!NT_SUCCESS(Status))
{
DPRINT1("NtFilterToken(): Failed to filter the token (Status 0x%lx)\n", Status);
goto Quit;
}
/* We got our filtered token, insert it to the handle */
Status = ObInsertObject(FilteredToken,
NULL,
HandleInfo.GrantedAccess,
0,
NULL,
&FilteredTokenHandle);
if (!NT_SUCCESS(Status))
{
DPRINT1("NtFilterToken(): Failed to insert the filtered token object into the handle (Status 0x%lx)\n", Status);
goto Quit;
}
/* And give it to the caller once we're done */
_SEH2_TRY
{
*NewTokenHandle = FilteredTokenHandle;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
_SEH2_YIELD(goto Quit);
}
_SEH2_END;
Quit:
/* Unlock and dereference the token */
SepReleaseTokenLock(Token);
ObDereferenceObject(Token);
/* Release all the stuff we've captured */
if (CapturedSids != NULL)
{
SeReleaseSidAndAttributesArray(CapturedSids,
PreviousMode,
TRUE);
CapturedSids = NULL;
}
if (CapturedPrivileges != NULL)
{
SeReleaseLuidAndAttributesArray(CapturedPrivileges,
PreviousMode,
TRUE);
CapturedPrivileges = NULL;
}
if (CapturedRestrictedSids != NULL)
{
SeReleaseSidAndAttributesArray(CapturedRestrictedSids,
PreviousMode,
TRUE);
CapturedRestrictedSids = NULL;
}
return Status;
}
/**
* @brief
* Allows the calling thread to impersonate the system's anonymous
* logon token.
*
* @param[in] ThreadHandle
* A handle to the thread to start the procedure of logon token
* impersonation. The thread must have the THREAD_IMPERSONATE
* access right.
*
* @return
* Returns STATUS_SUCCESS if the thread has successfully impersonated the
* anonymous logon token, otherwise a failure NTSTATUS code is returned.
*
* @remarks
* By default the system gives the opportunity to the caller to impersonate
* the anonymous logon token without including the Everyone Group SID.
* In cases where the caller wants to impersonate the token including such
* group, the EveryoneIncludesAnonymous registry value setting has to be set
* to 1, from HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa registry
* path. The calling thread must invoke PsRevertToSelf when impersonation
* is no longer needed or RevertToSelf if the calling execution is done
* in user mode.
*/
NTSTATUS
NTAPI
NtImpersonateAnonymousToken(
_In_ HANDLE ThreadHandle)
{
PETHREAD Thread;
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status;
PAGED_CODE();
PreviousMode = ExGetPreviousMode();
/* Obtain the thread object from the handle */
Status = ObReferenceObjectByHandle(ThreadHandle,
THREAD_IMPERSONATE,
PsThreadType,
PreviousMode,
(PVOID*)&Thread,
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("NtImpersonateAnonymousToken(): Failed to reference the object (Status 0x%lx)\n", Status);
return Status;
}
/* Call the private routine to impersonate the token */
Status = SepImpersonateAnonymousToken(Thread, PreviousMode);
if (!NT_SUCCESS(Status))
{
DPRINT1("NtImpersonateAnonymousToken(): Failed to impersonate the token (Status 0x%lx)\n", Status);
}
ObDereferenceObject(Thread);
return Status;
}
/* EOF */