[NTOS:SE] Properly handle dynamic counters in token

On current master, ReactOS faces these problems:

- ObCreateObject charges both paged and non paged pool a size of TOKEN structure, not the actual dynamic contents of WHAT IS inside a token. For paged pool charge the size is that of the dynamic area (primary group + default DACL if any). This is basically what DynamicCharged is for.
For the non paged pool charge, the actual charge is that of TOKEN structure upon creation. On duplication and filtering however, the paged pool charge size is that of the inherited dynamic charged space from an existing token whereas the non paged pool size is that of the calculated token body
length for the new duplicated/filtered token. On current master, we're literally cheating the kernel by charging the wrong amount of quota not taking into account the dynamic contents which they come from UM.

- Both DynamicCharged and DynamicAvailable are not fully handled (DynamicAvailable is pretty much poorly handled with some cases still to be taking into account). DynamicCharged is barely handled, like at all.

- As a result of these two points above, NtSetInformationToken doesn't check when the caller wants to set up a new default token DACL or primary group if the newly DACL or the said group exceeds the dynamic charged boundary. So what happens is that I'm going to act like a smug bastard fat politician and whack
the primary group and DACL of an token however I want to, because why in the hell not? In reality no, the kernel has to punish whoever attempts to do that, although we currently don't.

- The dynamic area (aka DynamicPart) only picks up the default DACL but not the primary group as well. Generally the dynamic part is composed of primary group and default DACL, if provided.

In addition to that, we aren't returning the dynamic charged and available area in token statistics. SepComputeAvailableDynamicSpace helper is here to accommodate that. Apparently Windows is calculating the dynamic available area rather than just querying the DynamicAvailable field directly from the token.
My theory regarding this is like the following: on Windows both TokenDefaultDacl and TokenPrimaryGroup classes are barely used by the system components during startup (LSASS provides both a DACL and primary group when calling NtCreateToken anyway). In fact DynamicAvailable is 0 during token creation, duplication and filtering when inspecting a token with WinDBG. So
if an application wants to query token statistics that application will face a dynamic available space of 0.
This commit is contained in:
George Bișoc 2022-06-21 22:02:22 +02:00
parent 846c9aa1fd
commit 4471ee4dfa
No known key found for this signature in database
GPG key ID: 688C4FBE25D7DEF6
4 changed files with 422 additions and 120 deletions

View file

@ -390,6 +390,17 @@ SepRemoveUserGroupToken(
_Inout_ PTOKEN Token,
_In_ ULONG Index);
ULONG
SepComputeAvailableDynamicSpace(
_In_ ULONG DynamicCharged,
_In_ PSID PrimaryGroup,
_In_opt_ PACL DefaultDacl);
NTSTATUS
SepRebuildDynamicPartOfToken(
_In_ PTOKEN Token,
_In_ ULONG NewDynamicPartSize);
BOOLEAN
NTAPI
SeTokenCanImpersonate(

View file

@ -636,6 +636,147 @@ SepRemoveUserGroupToken(
Token->UserAndGroupCount--;
}
/**
* @brief
* Computes the exact available dynamic area of an access
* token whilst querying token statistics.
*
* @param[in] DynamicCharged
* The current charged dynamic area of an access token.
* This must not be 0!
*
* @param[in] PrimaryGroup
* A pointer to a primary group SID.
*
* @param[in] DefaultDacl
* If provided, this pointer points to a default DACL of an
* access token.
*
* @return
* Returns the calculated available dynamic area.
*/
ULONG
SepComputeAvailableDynamicSpace(
_In_ ULONG DynamicCharged,
_In_ PSID PrimaryGroup,
_In_opt_ PACL DefaultDacl)
{
ULONG DynamicAvailable;
PAGED_CODE();
/* A token's dynamic area is always charged */
ASSERT(DynamicCharged != 0);
/*
* Take into account the default DACL if
* the token has one. Otherwise the occupied
* space is just the present primary group.
*/
DynamicAvailable = DynamicCharged - RtlLengthSid(PrimaryGroup);
if (DefaultDacl)
{
DynamicAvailable -= DefaultDacl->AclSize;
}
return DynamicAvailable;
}
/**
* @brief
* Re-builds the dynamic part area of an access token
* during an a default DACL or primary group replacement
* within the said token if the said dynamic area can't
* hold the new security content.
*
* @param[in] AccessToken
* A pointer to an access token where its dynamic part
* is to be re-built and expanded based upon the new
* dynamic part size provided by the caller. Dynamic
* part expansion is not always guaranteed. See Remarks
* for further information.
*
* @param[in] NewDynamicPartSize
* The new dynamic part size.
*
* @return
* Returns STATUS_SUCCESS if the function has completed its
* operations successfully. STATUS_INSUFFICIENT_RESOURCES
* is returned if the new dynamic part could not be allocated.
*
* @remarks
* STATUS_SUCCESS does not indicate if the function has re-built
* the dynamic part of a token. If the current dynamic area size
* suffices the new dynamic area length provided by the caller
* then the dynamic area can hold the new security content buffer
* so dynamic part expansion is not necessary.
*/
NTSTATUS
SepRebuildDynamicPartOfToken(
_Inout_ PTOKEN AccessToken,
_In_ ULONG NewDynamicPartSize)
{
PVOID NewDynamicPart;
PVOID PreviousDynamicPart;
ULONG CurrentDynamicLength;
PAGED_CODE();
/* Sanity checks */
ASSERT(AccessToken);
ASSERT(NewDynamicPartSize != 0);
/*
* Compute the exact length of the available
* dynamic part of the access token.
*/
CurrentDynamicLength = AccessToken->DynamicAvailable + RtlLengthSid(AccessToken->PrimaryGroup);
if (AccessToken->DefaultDacl)
{
CurrentDynamicLength += AccessToken->DefaultDacl->AclSize;
}
/*
* Figure out if the current dynamic part is too small
* to fit new contents inside the said dynamic part.
* Rebuild the dynamic area and expand it if necessary.
*/
if (CurrentDynamicLength < NewDynamicPartSize)
{
NewDynamicPart = ExAllocatePoolWithTag(PagedPool,
NewDynamicPartSize,
TAG_TOKEN_DYNAMIC);
if (NewDynamicPart == NULL)
{
DPRINT1("SepRebuildDynamicPartOfToken(): Insufficient resources to allocate new dynamic part!\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Copy the existing dynamic part */
PreviousDynamicPart = AccessToken->DynamicPart;
RtlCopyMemory(NewDynamicPart, PreviousDynamicPart, CurrentDynamicLength);
/* Update the available dynamic area and assign new dynamic */
AccessToken->DynamicAvailable += NewDynamicPartSize - CurrentDynamicLength;
AccessToken->DynamicPart = NewDynamicPart;
/* Move the contents (primary group and default DACL) addresses as well */
AccessToken->PrimaryGroup = (PSID)((ULONG_PTR)AccessToken->DynamicPart +
((ULONG_PTR)AccessToken->PrimaryGroup - (ULONG_PTR)PreviousDynamicPart));
if (AccessToken->DefaultDacl != NULL)
{
AccessToken->DefaultDacl = (PACL)((ULONG_PTR)AccessToken->DynamicPart +
((ULONG_PTR)AccessToken->DefaultDacl - (ULONG_PTR)PreviousDynamicPart));
}
/* And discard the previous dynamic part */
DPRINT("SepRebuildDynamicPartOfToken(): The dynamic part has been re-built with success!\n");
ExFreePoolWithTag(PreviousDynamicPart, TAG_TOKEN_DYNAMIC);
}
return STATUS_SUCCESS;
}
/**
* @unimplemented
* @brief

View file

@ -398,7 +398,7 @@ SeQueryInformationToken(
ts->TokenType = Token->TokenType;
ts->ImpersonationLevel = Token->ImpersonationLevel;
ts->DynamicCharged = Token->DynamicCharged;
ts->DynamicAvailable = Token->DynamicAvailable;
ts->DynamicAvailable = SepComputeAvailableDynamicSpace(Token->DynamicCharged, Token->PrimaryGroup, Token->DefaultDacl);
ts->GroupCount = Token->UserAndGroupCount - 1;
ts->PrivilegeCount = Token->PrivilegeCount;
ts->ModifiedId = Token->ModifiedId;
@ -854,7 +854,7 @@ NtQueryInformationToken(
ts->TokenType = Token->TokenType;
ts->ImpersonationLevel = Token->ImpersonationLevel;
ts->DynamicCharged = Token->DynamicCharged;
ts->DynamicAvailable = Token->DynamicAvailable;
ts->DynamicAvailable = SepComputeAvailableDynamicSpace(Token->DynamicCharged, Token->PrimaryGroup, Token->DefaultDacl);
ts->GroupCount = Token->UserAndGroupCount - 1;
ts->PrivilegeCount = Token->PrivilegeCount;
ts->ModifiedId = Token->ModifiedId;
@ -1116,7 +1116,7 @@ NtQueryInformationToken(
* the operation in question. A failure NTSTATUS code is returned otherwise.
*
* @remarks
* The function is partly implemented, mainly TokenOrigin and TokenDefaultDacl.
* The function is partly implemented, mainly TokenOrigin.
*/
_Must_inspect_result_
__kernel_entry
@ -1227,8 +1227,9 @@ NtSetInformationToken(
if (TokenInformationLength >= sizeof(TOKEN_PRIMARY_GROUP))
{
PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation;
ULONG_PTR PrimaryGroup;
PSID InputSid = NULL, CapturedSid;
ULONG PrimaryGroupIndex;
ULONG PrimaryGroupIndex, NewDynamicLength;
_SEH2_TRY
{
@ -1251,17 +1252,73 @@ NtSetInformationToken(
/* Lock the token */
SepAcquireTokenLockExclusive(Token);
/* Find the primary group amongst the existing token user and groups */
Status = SepFindPrimaryGroupAndDefaultOwner(Token,
CapturedSid,
NULL,
&PrimaryGroupIndex,
NULL);
/*
* We can whack the token's primary group only if
* the charged dynamic space boundary allows us
* to do so. Exceeding this boundary and we're
* busted out.
*/
NewDynamicLength = RtlLengthSid(CapturedSid) +
Token->DefaultDacl ? Token->DefaultDacl->AclSize : 0;
if (NewDynamicLength > Token->DynamicCharged)
{
SepReleaseTokenLock(Token);
SepReleaseSid(CapturedSid, PreviousMode, FALSE);
Status = STATUS_ALLOTTED_SPACE_EXCEEDED;
DPRINT1("NtSetInformationToken(): Couldn't assign new primary group, space exceeded (current length %u, new length %lu)\n",
Token->DynamicCharged, NewDynamicLength);
goto Cleanup;
}
/*
* The dynamic part of the token may require a rebuild
* if the current dynamic area is too small. If not then
* we're pretty much good as is.
*/
Status = SepRebuildDynamicPartOfToken(Token, NewDynamicLength);
if (NT_SUCCESS(Status))
{
/* Found it */
Token->PrimaryGroup = Token->UserAndGroups[PrimaryGroupIndex].Sid;
ExAllocateLocallyUniqueId(&Token->ModifiedId);
/* Find the primary group amongst the existing token user and groups */
Status = SepFindPrimaryGroupAndDefaultOwner(Token,
CapturedSid,
NULL,
&PrimaryGroupIndex,
NULL);
if (NT_SUCCESS(Status))
{
/*
* We have found it. Add the length of
* the previous primary group SID to the
* available dynamic area.
*/
Token->DynamicAvailable += RtlLengthSid(Token->PrimaryGroup);
/*
* Move the default DACL if it's not at the
* head of the dynamic part.
*/
if ((Token->DefaultDacl) &&
((PULONG)(Token->DefaultDacl) != Token->DynamicPart))
{
RtlMoveMemory(Token->DynamicPart,
Token->DefaultDacl,
RtlLengthSid(Token->PrimaryGroup));
Token->DefaultDacl = (PACL)(Token->DynamicPart);
}
/* Take away available space from the dynamic area */
Token->DynamicAvailable -= RtlLengthSid(Token->UserAndGroups[PrimaryGroupIndex].Sid);
/* And assign the primary group */
PrimaryGroup = (ULONG_PTR)(Token->DynamicPart) + Token->DefaultDacl ?
Token->DefaultDacl->AclSize : 0;
RtlCopySid(RtlLengthSid(Token->UserAndGroups[PrimaryGroupIndex].Sid),
(PVOID)PrimaryGroup,
Token->UserAndGroups[PrimaryGroupIndex].Sid);
Token->PrimaryGroup = (PSID)PrimaryGroup;
ExAllocateLocallyUniqueId(&Token->ModifiedId);
}
}
/* Unlock the token */
@ -1309,68 +1366,76 @@ NtSetInformationToken(
&CapturedAcl);
if (NT_SUCCESS(Status))
{
ULONG DynamicLength;
ULONG NewDynamicLength;
ULONG_PTR Acl;
/* 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))
/*
* We can whack the token's default DACL only if
* the charged dynamic space boundary allows us
* to do so. Exceeding this boundary and we're
* busted out.
*/
NewDynamicLength = CapturedAcl->AclSize + RtlLengthSid(Token->PrimaryGroup);
if (NewDynamicLength > Token->DynamicCharged)
{
PVOID NewDynamicPart;
NewDynamicPart = ExAllocatePoolWithTag(PagedPool,
CapturedAcl->AclSize,
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
{
Token->DynamicAvailable = DynamicLength - CapturedAcl->AclSize;
SepReleaseTokenLock(Token);
SepReleaseAcl(CapturedAcl, PreviousMode, TRUE);
Status = STATUS_ALLOTTED_SPACE_EXCEEDED;
DPRINT1("NtSetInformationToken(): Couldn't assign new default DACL, space exceeded (current length %u, new length %lu)\n",
Token->DynamicCharged, NewDynamicLength);
goto Cleanup;
}
/*
* The dynamic part of the token may require a rebuild
* if the current dynamic area is too small. If not then
* we're pretty much good as is.
*/
Status = SepRebuildDynamicPartOfToken(Token, NewDynamicLength);
if (NT_SUCCESS(Status))
{
/*
* Before setting up a new DACL for the
* token object we add up the size of
* the old DACL to the available dynamic
* area
*/
if (Token->DefaultDacl)
{
Token->DynamicAvailable += Token->DefaultDacl->AclSize;
}
/*
* Move the primary group if it's not at the
* head of the dynamic part.
*/
if ((PULONG)(Token->PrimaryGroup) != Token->DynamicPart)
{
RtlMoveMemory(Token->DynamicPart,
Token->PrimaryGroup,
RtlLengthSid(Token->PrimaryGroup));
Token->PrimaryGroup = (PSID)(Token->DynamicPart);
}
/* Take away available space from the dynamic area */
Token->DynamicAvailable -= CapturedAcl->AclSize;
/* Set the new dacl */
Token->DefaultDacl = (PVOID)Token->DynamicPart;
RtlCopyMemory(Token->DefaultDacl,
Acl = (ULONG_PTR)(Token->DynamicPart) + RtlLengthSid(Token->PrimaryGroup);
RtlCopyMemory((PVOID)Acl,
CapturedAcl,
CapturedAcl->AclSize);
Token->DefaultDacl = (PACL)Acl;
ExAllocateLocallyUniqueId(&Token->ModifiedId);
}
/* Unlock the token */
/* Unlock the token and release the ACL */
SepReleaseTokenLock(Token);
ExFreePoolWithTag(CapturedAcl, TAG_ACL);
SepReleaseAcl(CapturedAcl, PreviousMode, TRUE);
}
}
else

View file

@ -12,6 +12,10 @@
#define NDEBUG
#include <debug.h>
/* DEFINES ********************************************************************/
#define SE_TOKEN_DYNAMIC_SLIM 500
/* PRIVATE FUNCTIONS *********************************************************/
/**
@ -121,7 +125,8 @@ SepCreateToken(
ULONG PrivilegesLength;
ULONG UserGroupsLength;
ULONG VariableLength;
ULONG TotalSize;
ULONG DynamicPartSize, TotalSize;
ULONG TokenPagedCharges;
ULONG i;
PAGED_CODE();
@ -169,13 +174,34 @@ SepCreateToken(
VariableLength = PrivilegesLength + UserGroupsLength;
TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
/*
* A token is considered slim if it has the default dynamic
* contents, or in other words, the primary group and ACL.
* We judge if such contents are default by checking their
* total size if it's over the range. On Windows this range
* is 0x1F4 (aka 500). If the size of the whole dynamic contents
* is over that range then the token is considered fat and
* the token will be charged the whole of its token body length
* plus the dynamic size.
*/
DynamicPartSize = DefaultDacl ? DefaultDacl->AclSize : 0;
DynamicPartSize += RtlLengthSid(PrimaryGroup);
if (DynamicPartSize > SE_TOKEN_DYNAMIC_SLIM)
{
TokenPagedCharges = DynamicPartSize + TotalSize;
}
else
{
TokenPagedCharges = SE_TOKEN_DYNAMIC_SLIM + TotalSize;
}
Status = ObCreateObject(PreviousMode,
SeTokenObjectType,
ObjectAttributes,
PreviousMode,
NULL,
TotalSize,
0,
TokenPagedCharges,
0,
(PVOID*)&AccessToken);
if (!NT_SUCCESS(Status))
@ -203,6 +229,7 @@ SepCreateToken(
AccessToken->ExpirationTime = *ExpirationTime;
AccessToken->ModifiedId = ModifiedId;
AccessToken->DynamicCharged = TokenPagedCharges - TotalSize;
AccessToken->TokenFlags = TokenFlags & ~TOKEN_SESSION_NOT_REFERENCED;
@ -332,27 +359,48 @@ SepCreateToken(
goto Quit;
}
AccessToken->PrimaryGroup = AccessToken->UserAndGroups[PrimaryGroupIndex].Sid;
AccessToken->DefaultOwnerIndex = DefaultOwnerIndex;
/*
* Now allocate the token's dynamic information area
* and set the data. The dynamic part consists of two
* contents, the primary group SID and the default DACL
* of the token, in this strict order.
*/
AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
DynamicPartSize,
TAG_TOKEN_DYNAMIC);
if (AccessToken->DynamicPart == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Quit;
}
/* Now allocate the token's dynamic information area and set the data */
AccessToken->DynamicAvailable = 0; // Unused memory in the dynamic area.
AccessToken->DynamicPart = NULL;
/* Unused memory in the dynamic area */
AccessToken->DynamicAvailable = 0;
/*
* Assign the primary group to the token
* and put it in the dynamic part as well.
*/
EndMem = (PVOID)AccessToken->DynamicPart;
AccessToken->PrimaryGroup = EndMem;
RtlCopySid(RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid),
EndMem,
AccessToken->UserAndGroups[PrimaryGroupIndex].Sid);
AccessToken->DefaultOwnerIndex = DefaultOwnerIndex;
EndMem = (PVOID)((ULONG_PTR)EndMem + RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid));
/*
* We have assigned a primary group and put it in the
* dynamic part, now it's time to copy the provided
* default DACL (if it's provided to begin with) into
* the DACL field of the token and put it at the end
* tail of the dynamic part too.
*/
if (DefaultDacl != NULL)
{
AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
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,
RtlCopyMemory(EndMem,
DefaultDacl,
DefaultDacl->AclSize);
}
@ -434,7 +482,7 @@ SepDuplicateToken(
PVOID EndMem;
ULONG PrimaryGroupIndex;
ULONG VariableLength;
ULONG TotalSize;
ULONG DynamicPartSize, TotalSize;
ULONG PrivilegesIndex, GroupsIndex;
PAGED_CODE();
@ -443,14 +491,22 @@ SepDuplicateToken(
VariableLength = Token->VariableLength;
TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
/*
* Compute how much size we need to allocate
* the dynamic part of the newly duplicated
* token.
*/
DynamicPartSize = Token->DefaultDacl ? Token->DefaultDacl->AclSize : 0;
DynamicPartSize += RtlLengthSid(Token->PrimaryGroup);
Status = ObCreateObject(PreviousMode,
SeTokenObjectType,
ObjectAttributes,
PreviousMode,
NULL,
TotalSize,
0,
0,
Token->DynamicCharged,
TotalSize,
(PVOID*)&AccessToken);
if (!NT_SUCCESS(Status))
{
@ -484,6 +540,7 @@ SepDuplicateToken(
AccessToken->ParentTokenId = Token->ParentTokenId;
AccessToken->ExpirationTime = Token->ExpirationTime;
AccessToken->OriginatingLogonSession = Token->OriginatingLogonSession;
AccessToken->DynamicCharged = Token->DynamicCharged;
/* Lock the source token and copy the mutable fields */
SepAcquireTokenLockShared(Token);
@ -584,9 +641,6 @@ SepDuplicateToken(
goto Quit;
}
AccessToken->PrimaryGroup = AccessToken->UserAndGroups[PrimaryGroupIndex].Sid;
AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex;
/* Copy the restricted SIDs */
AccessToken->RestrictedSidCount = 0;
AccessToken->RestrictedSids = NULL;
@ -684,31 +738,40 @@ SepDuplicateToken(
}
}
//
// 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;
AccessToken->DynamicPart = ExAllocatePoolWithTag(PagedPool,
DynamicPartSize,
TAG_TOKEN_DYNAMIC);
if (AccessToken->DynamicPart == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Quit;
}
/* Unused memory in the dynamic area */
AccessToken->DynamicAvailable = 0;
/*
* Assign the primary group to the token
* and put it in the dynamic part as well.
*/
EndMem = (PVOID)AccessToken->DynamicPart;
AccessToken->PrimaryGroup = EndMem;
RtlCopySid(RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid),
EndMem,
AccessToken->UserAndGroups[PrimaryGroupIndex].Sid);
AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex;
EndMem = (PVOID)((ULONG_PTR)EndMem + RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid));
/*
* The existing token has a default DACL only
* if it has an allocated dynamic part.
*/
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,
RtlCopyMemory(EndMem,
Token->DefaultDacl,
Token->DefaultDacl->AclSize);
}
@ -804,6 +867,7 @@ SepPerformTokenFiltering(
NTSTATUS Status;
PTOKEN AccessToken;
PVOID EndMem;
ULONG DynamicPartSize;
ULONG RestrictedSidsLength;
ULONG PrivilegesLength;
ULONG PrimaryGroupIndex;
@ -863,6 +927,14 @@ SepPerformTokenFiltering(
TotalSize = FIELD_OFFSET(TOKEN, VariablePart) + VariableLength;
}
/*
* Compute how much size we need to allocate
* the dynamic part of the newly duplicated
* token.
*/
DynamicPartSize = Token->DefaultDacl ? Token->DefaultDacl->AclSize : 0;
DynamicPartSize += RtlLengthSid(Token->PrimaryGroup);
/* Set up a filtered token object */
Status = ObCreateObject(PreviousMode,
SeTokenObjectType,
@ -870,8 +942,8 @@ SepPerformTokenFiltering(
PreviousMode,
NULL,
TotalSize,
0,
0,
Token->DynamicCharged,
TotalSize,
(PVOID*)&AccessToken);
if (!NT_SUCCESS(Status))
{
@ -907,6 +979,7 @@ SepPerformTokenFiltering(
AccessToken->AuthenticationId = Token->AuthenticationId;
AccessToken->ParentTokenId = Token->TokenId;
AccessToken->OriginatingLogonSession = Token->OriginatingLogonSession;
AccessToken->DynamicCharged = Token->DynamicCharged;
AccessToken->ExpirationTime = Token->ExpirationTime;
@ -1092,28 +1165,40 @@ SepPerformTokenFiltering(
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->DynamicPart = ExAllocatePoolWithTag(PagedPool,
DynamicPartSize,
TAG_TOKEN_DYNAMIC);
if (AccessToken->DynamicPart == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Quit;
}
/* Unused memory in the dynamic area */
AccessToken->DynamicAvailable = 0;
AccessToken->DynamicPart = NULL;
/*
* Assign the primary group to the token
* and put it in the dynamic part as well.
*/
EndMem = (PVOID)AccessToken->DynamicPart;
AccessToken->PrimaryGroup = EndMem;
RtlCopySid(RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid),
EndMem,
AccessToken->UserAndGroups[PrimaryGroupIndex].Sid);
AccessToken->DefaultOwnerIndex = Token->DefaultOwnerIndex;
EndMem = (PVOID)((ULONG_PTR)EndMem + RtlLengthSid(AccessToken->UserAndGroups[PrimaryGroupIndex].Sid));
/*
* The existing token has a default DACL only
* if it has an allocated dynamic part.
*/
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,
RtlCopyMemory(EndMem,
Token->DefaultDacl,
Token->DefaultDacl->AclSize);
}