From 4471ee4dfaddb2440601fd61c01542b586b7c2d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20Bi=C8=99oc?= Date: Tue, 21 Jun 2022 22:02:22 +0200 Subject: [PATCH] [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. --- ntoskrnl/include/internal/se.h | 11 ++ ntoskrnl/se/token.c | 141 ++++++++++++++++++++++ ntoskrnl/se/tokencls.c | 181 +++++++++++++++++++--------- ntoskrnl/se/tokenlif.c | 209 +++++++++++++++++++++++---------- 4 files changed, 422 insertions(+), 120 deletions(-) diff --git a/ntoskrnl/include/internal/se.h b/ntoskrnl/include/internal/se.h index de65310b331..b81585c64eb 100644 --- a/ntoskrnl/include/internal/se.h +++ b/ntoskrnl/include/internal/se.h @@ -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( diff --git a/ntoskrnl/se/token.c b/ntoskrnl/se/token.c index ad2dd8e806f..33354f2ea8b 100644 --- a/ntoskrnl/se/token.c +++ b/ntoskrnl/se/token.c @@ -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 diff --git a/ntoskrnl/se/tokencls.c b/ntoskrnl/se/tokencls.c index 8bb5598ce9e..4810996114e 100644 --- a/ntoskrnl/se/tokencls.c +++ b/ntoskrnl/se/tokencls.c @@ -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 diff --git a/ntoskrnl/se/tokenlif.c b/ntoskrnl/se/tokenlif.c index c3900c2dce5..9f6e9aef9c8 100644 --- a/ntoskrnl/se/tokenlif.c +++ b/ntoskrnl/se/tokenlif.c @@ -12,6 +12,10 @@ #define NDEBUG #include +/* 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); }