2022-05-28 19:14:04 +00:00
|
|
|
/*
|
2023-03-07 17:39:46 +00:00
|
|
|
* PROJECT: ReactOS Kernel
|
|
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
|
|
* PURPOSE: Access token Query/Set information classes implementation
|
|
|
|
* COPYRIGHT: Copyright David Welch <welch@cwcom.net>
|
|
|
|
* Copyright 2021-2022 George Bișoc <george.bisoc@reactos.org>
|
2022-05-28 19:14:04 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
|
|
|
|
#include <ntoskrnl.h>
|
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
|
|
|
|
|
|
|
#include <ntlsa.h>
|
|
|
|
|
|
|
|
/* INFORMATION CLASSES ********************************************************/
|
|
|
|
|
|
|
|
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),
|
|
|
|
};
|
|
|
|
|
|
|
|
/* PUBLIC FUNCTIONS *****************************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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);
|
|
|
|
|
|
|
|
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;
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
ts->DynamicAvailable = SepComputeAvailableDynamicSpace(Token->DynamicCharged, Token->PrimaryGroup, Token->DefaultDacl);
|
2022-05-28 19:14:04 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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
|
2022-06-13 17:28:12 +00:00
|
|
|
* A pointer to a variable provided by the caller that receives the actual length
|
|
|
|
* of the buffer pointed by TokenInformation, in bytes. If TokenInformation is NULL
|
|
|
|
* and TokenInformationLength is 0, this parameter receives the required length
|
|
|
|
* needed to store the buffer information in memory. This parameter must not
|
|
|
|
* be NULL!
|
2022-05-28 19:14:04 +00:00
|
|
|
*
|
|
|
|
* @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
|
2022-06-13 17:28:12 +00:00
|
|
|
* one (that is, the class doesn't belong to TOKEN_INFORMATION_CLASS).
|
|
|
|
* STATUS_ACCESS_VIOLATION is returned if ReturnLength is NULL. A failure NTSTATUS
|
|
|
|
* code is returned otherwise.
|
2022-05-28 19:14:04 +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;
|
2022-06-13 18:12:32 +00:00
|
|
|
ULONG RequiredLength = 0;
|
2022-05-28 19:14:04 +00:00
|
|
|
union
|
|
|
|
{
|
|
|
|
PSID PSid;
|
|
|
|
ULONG Ulong;
|
|
|
|
} Unused;
|
|
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
|
|
PreviousMode = ExGetPreviousMode();
|
|
|
|
|
|
|
|
/* Check buffers and class validity */
|
|
|
|
Status = DefaultQueryInfoBufferCheck(TokenInformationClass,
|
|
|
|
SeTokenInformationClass,
|
|
|
|
RTL_NUMBER_OF(SeTokenInformationClass),
|
2022-06-11 11:19:51 +00:00
|
|
|
ICIF_PROBE_READ_WRITE | ICIF_FORCE_RETURN_LENGTH_PROBE,
|
2022-05-28 19:14:04 +00:00
|
|
|
TokenInformation,
|
|
|
|
TokenInformationLength,
|
|
|
|
ReturnLength,
|
|
|
|
NULL,
|
2022-06-11 11:19:51 +00:00
|
|
|
PreviousMode);
|
2022-05-28 19:14:04 +00:00
|
|
|
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))
|
|
|
|
{
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2022-06-13 17:28:12 +00:00
|
|
|
*ReturnLength = RequiredLength;
|
2022-05-28 19:14:04 +00:00
|
|
|
}
|
|
|
|
_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;
|
|
|
|
}
|
|
|
|
|
2022-06-13 17:28:12 +00:00
|
|
|
*ReturnLength = RequiredLength;
|
2022-05-28 19:14:04 +00:00
|
|
|
}
|
|
|
|
_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;
|
|
|
|
}
|
|
|
|
|
2022-06-13 17:28:12 +00:00
|
|
|
*ReturnLength = RequiredLength;
|
2022-05-28 19:14:04 +00:00
|
|
|
}
|
|
|
|
_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;
|
|
|
|
}
|
|
|
|
|
2022-06-13 17:28:12 +00:00
|
|
|
*ReturnLength = RequiredLength;
|
2022-05-28 19:14:04 +00:00
|
|
|
}
|
|
|
|
_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;
|
|
|
|
}
|
|
|
|
|
2022-06-13 17:28:12 +00:00
|
|
|
*ReturnLength = RequiredLength;
|
2022-05-28 19:14:04 +00:00
|
|
|
}
|
|
|
|
_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;
|
|
|
|
}
|
|
|
|
|
2022-06-13 17:28:12 +00:00
|
|
|
*ReturnLength = RequiredLength;
|
2022-05-28 19:14:04 +00:00
|
|
|
}
|
|
|
|
_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;
|
|
|
|
}
|
|
|
|
|
2022-06-13 17:28:12 +00:00
|
|
|
*ReturnLength = RequiredLength;
|
2022-05-28 19:14:04 +00:00
|
|
|
}
|
|
|
|
_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;
|
|
|
|
}
|
|
|
|
|
2022-06-13 17:28:12 +00:00
|
|
|
*ReturnLength = RequiredLength;
|
2022-05-28 19:14:04 +00:00
|
|
|
}
|
|
|
|
_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;
|
|
|
|
}
|
|
|
|
|
2022-06-13 17:28:12 +00:00
|
|
|
*ReturnLength = RequiredLength;
|
2022-05-28 19:14:04 +00:00
|
|
|
}
|
|
|
|
_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;
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
ts->DynamicAvailable = SepComputeAvailableDynamicSpace(Token->DynamicCharged, Token->PrimaryGroup, Token->DefaultDacl);
|
2022-05-28 19:14:04 +00:00
|
|
|
ts->GroupCount = Token->UserAndGroupCount - 1;
|
|
|
|
ts->PrivilegeCount = Token->PrivilegeCount;
|
|
|
|
ts->ModifiedId = Token->ModifiedId;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
|
|
}
|
|
|
|
|
2022-06-13 17:28:12 +00:00
|
|
|
*ReturnLength = RequiredLength;
|
2022-05-28 19:14:04 +00:00
|
|
|
}
|
|
|
|
_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)
|
|
|
|
{
|
|
|
|
to->OriginatingLogonSession = Token->AuthenticationId;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
|
|
}
|
|
|
|
|
2022-06-13 17:28:12 +00:00
|
|
|
*ReturnLength = RequiredLength;
|
2022-05-28 19:14:04 +00:00
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
Status = _SEH2_GetExceptionCode();
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case TokenGroupsAndPrivileges:
|
2022-06-19 10:39:23 +00:00
|
|
|
{
|
|
|
|
PSID Sid, RestrictedSid;
|
|
|
|
ULONG SidLen, RestrictedSidLen;
|
|
|
|
ULONG UserGroupLength, RestrictedSidLength, PrivilegeLength;
|
|
|
|
PTOKEN_GROUPS_AND_PRIVILEGES GroupsAndPrivs = (PTOKEN_GROUPS_AND_PRIVILEGES)TokenInformation;
|
|
|
|
|
|
|
|
DPRINT("NtQueryInformationToken(TokenGroupsAndPrivileges)\n");
|
|
|
|
UserGroupLength = RtlLengthSidAndAttributes(Token->UserAndGroupCount, Token->UserAndGroups);
|
|
|
|
RestrictedSidLength = RtlLengthSidAndAttributes(Token->RestrictedSidCount, Token->RestrictedSids);
|
|
|
|
PrivilegeLength = Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
|
|
|
|
|
|
|
|
RequiredLength = sizeof(TOKEN_GROUPS_AND_PRIVILEGES) +
|
|
|
|
UserGroupLength + RestrictedSidLength + PrivilegeLength;
|
|
|
|
|
|
|
|
_SEH2_TRY
|
|
|
|
{
|
|
|
|
if (TokenInformationLength >= RequiredLength)
|
|
|
|
{
|
|
|
|
GroupsAndPrivs->SidCount = Token->UserAndGroupCount;
|
|
|
|
GroupsAndPrivs->SidLength = UserGroupLength;
|
|
|
|
GroupsAndPrivs->Sids = (PSID_AND_ATTRIBUTES)(GroupsAndPrivs + 1);
|
|
|
|
|
|
|
|
Sid = (PSID)((ULONG_PTR)GroupsAndPrivs->Sids + (Token->UserAndGroupCount * sizeof(SID_AND_ATTRIBUTES)));
|
|
|
|
SidLen = UserGroupLength - (Token->UserAndGroupCount * sizeof(SID_AND_ATTRIBUTES));
|
|
|
|
Status = RtlCopySidAndAttributesArray(Token->UserAndGroupCount,
|
|
|
|
Token->UserAndGroups,
|
|
|
|
SidLen,
|
|
|
|
GroupsAndPrivs->Sids,
|
|
|
|
Sid,
|
|
|
|
&Unused.PSid,
|
|
|
|
&Unused.Ulong);
|
|
|
|
NT_ASSERT(NT_SUCCESS(Status));
|
|
|
|
|
|
|
|
GroupsAndPrivs->RestrictedSidCount = Token->RestrictedSidCount;
|
|
|
|
GroupsAndPrivs->RestrictedSidLength = RestrictedSidLength;
|
|
|
|
GroupsAndPrivs->RestrictedSids = NULL;
|
|
|
|
if (SeTokenIsRestricted(Token))
|
|
|
|
{
|
|
|
|
GroupsAndPrivs->RestrictedSids = (PSID_AND_ATTRIBUTES)((ULONG_PTR)GroupsAndPrivs->Sids + UserGroupLength);
|
|
|
|
|
|
|
|
RestrictedSid = (PSID)((ULONG_PTR)GroupsAndPrivs->RestrictedSids + (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES)));
|
|
|
|
RestrictedSidLen = RestrictedSidLength - (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES));
|
|
|
|
Status = RtlCopySidAndAttributesArray(Token->RestrictedSidCount,
|
|
|
|
Token->RestrictedSids,
|
|
|
|
RestrictedSidLen,
|
|
|
|
GroupsAndPrivs->RestrictedSids,
|
|
|
|
RestrictedSid,
|
|
|
|
&Unused.PSid,
|
|
|
|
&Unused.Ulong);
|
|
|
|
NT_ASSERT(NT_SUCCESS(Status));
|
|
|
|
}
|
|
|
|
|
|
|
|
GroupsAndPrivs->PrivilegeCount = Token->PrivilegeCount;
|
|
|
|
GroupsAndPrivs->PrivilegeLength = PrivilegeLength;
|
|
|
|
GroupsAndPrivs->Privileges = (PLUID_AND_ATTRIBUTES)((ULONG_PTR)(GroupsAndPrivs + 1) +
|
|
|
|
UserGroupLength + RestrictedSidLength);
|
|
|
|
RtlCopyLuidAndAttributesArray(Token->PrivilegeCount,
|
|
|
|
Token->Privileges,
|
|
|
|
GroupsAndPrivs->Privileges);
|
|
|
|
|
|
|
|
GroupsAndPrivs->AuthenticationId = Token->AuthenticationId;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ReturnLength = RequiredLength;
|
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
Status = _SEH2_GetExceptionCode();
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
|
2022-05-28 19:14:04 +00:00
|
|
|
break;
|
2022-06-19 10:39:23 +00:00
|
|
|
}
|
2022-05-28 19:14:04 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-06-13 17:28:12 +00:00
|
|
|
*ReturnLength = RequiredLength;
|
2022-05-28 19:14:04 +00:00
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
Status = _SEH2_GetExceptionCode();
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case TokenSandBoxInert:
|
2022-06-12 12:30:44 +00:00
|
|
|
{
|
|
|
|
ULONG IsTokenSandBoxInert;
|
|
|
|
|
|
|
|
DPRINT("NtQueryInformationToken(TokenSandBoxInert)\n");
|
|
|
|
|
|
|
|
IsTokenSandBoxInert = SeTokenIsInert(Token);
|
|
|
|
_SEH2_TRY
|
|
|
|
{
|
|
|
|
/* Buffer size was already verified, no need to check here again */
|
|
|
|
*(PULONG)TokenInformation = IsTokenSandBoxInert;
|
|
|
|
*ReturnLength = sizeof(ULONG);
|
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
Status = _SEH2_GetExceptionCode();
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
|
2022-05-28 19:14:04 +00:00
|
|
|
break;
|
2022-06-12 12:30:44 +00:00
|
|
|
}
|
2022-05-28 19:14:04 +00:00
|
|
|
|
|
|
|
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;
|
2022-06-13 17:28:12 +00:00
|
|
|
*ReturnLength = RequiredLength;
|
2022-05-28 19:14:04 +00:00
|
|
|
}
|
|
|
|
_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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
* The function is partly implemented, mainly TokenOrigin.
|
2022-05-28 19:14:04 +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)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
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))
|
|
|
|
{
|
|
|
|
/* 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;
|
2022-08-15 20:03:51 +00:00
|
|
|
ULONG AclSize;
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
ULONG_PTR PrimaryGroup;
|
2022-05-28 19:14:04 +00:00
|
|
|
PSID InputSid = NULL, CapturedSid;
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
ULONG PrimaryGroupIndex, NewDynamicLength;
|
2022-05-28 19:14:04 +00:00
|
|
|
|
|
|
|
_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))
|
|
|
|
{
|
|
|
|
/* Lock the token */
|
|
|
|
SepAcquireTokenLockExclusive(Token);
|
|
|
|
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2022-08-16 18:27:27 +00:00
|
|
|
AclSize = Token->DefaultDacl ? Token->DefaultDacl->AclSize : 0;
|
|
|
|
NewDynamicLength = RtlLengthSid(CapturedSid) + AclSize;
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
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);
|
2022-05-28 19:14:04 +00:00
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
/* 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);
|
|
|
|
|
2022-08-15 20:03:51 +00:00
|
|
|
/*
|
|
|
|
* And assign the new primary group. For that
|
|
|
|
* we have to make sure where the primary group
|
|
|
|
* is going to stay in memory, so if this token
|
|
|
|
* has a default DACL then add up its size with
|
|
|
|
* the address of the dynamic part.
|
|
|
|
*/
|
|
|
|
PrimaryGroup = (ULONG_PTR)(Token->DynamicPart) + AclSize;
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
RtlCopySid(RtlLengthSid(Token->UserAndGroups[PrimaryGroupIndex].Sid),
|
|
|
|
(PVOID)PrimaryGroup,
|
|
|
|
Token->UserAndGroups[PrimaryGroupIndex].Sid);
|
|
|
|
Token->PrimaryGroup = (PSID)PrimaryGroup;
|
|
|
|
|
|
|
|
ExAllocateLocallyUniqueId(&Token->ModifiedId);
|
|
|
|
}
|
2022-05-28 19:14:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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, validate, and copy the DACL */
|
|
|
|
Status = SepCaptureAcl(InputAcl,
|
|
|
|
PreviousMode,
|
|
|
|
PagedPool,
|
|
|
|
TRUE,
|
|
|
|
&CapturedAcl);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
|
|
{
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
ULONG NewDynamicLength;
|
|
|
|
ULONG_PTR Acl;
|
2022-05-28 19:14:04 +00:00
|
|
|
|
|
|
|
/* Lock the token */
|
|
|
|
SepAcquireTokenLockExclusive(Token);
|
|
|
|
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
/*
|
|
|
|
* 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)
|
2022-05-28 19:14:04 +00:00
|
|
|
{
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
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;
|
|
|
|
}
|
2022-05-28 19:14:04 +00:00
|
|
|
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
/*
|
|
|
|
* 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)
|
2022-05-28 19:14:04 +00:00
|
|
|
{
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
Token->DynamicAvailable += Token->DefaultDacl->AclSize;
|
2022-05-28 19:14:04 +00:00
|
|
|
}
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Move the primary group if it's not at the
|
|
|
|
* head of the dynamic part.
|
|
|
|
*/
|
|
|
|
if ((PULONG)(Token->PrimaryGroup) != Token->DynamicPart)
|
2022-05-28 19:14:04 +00:00
|
|
|
{
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
RtlMoveMemory(Token->DynamicPart,
|
|
|
|
Token->PrimaryGroup,
|
|
|
|
RtlLengthSid(Token->PrimaryGroup));
|
|
|
|
Token->PrimaryGroup = (PSID)(Token->DynamicPart);
|
2022-05-28 19:14:04 +00:00
|
|
|
}
|
|
|
|
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
/* Take away available space from the dynamic area */
|
|
|
|
Token->DynamicAvailable -= CapturedAcl->AclSize;
|
|
|
|
|
2022-05-28 19:14:04 +00:00
|
|
|
/* Set the new dacl */
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
Acl = (ULONG_PTR)(Token->DynamicPart) + RtlLengthSid(Token->PrimaryGroup);
|
|
|
|
RtlCopyMemory((PVOID)Acl,
|
2022-05-28 19:14:04 +00:00
|
|
|
CapturedAcl,
|
|
|
|
CapturedAcl->AclSize);
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
Token->DefaultDacl = (PACL)Acl;
|
2022-05-28 19:14:04 +00:00
|
|
|
|
|
|
|
ExAllocateLocallyUniqueId(&Token->ModifiedId);
|
|
|
|
}
|
|
|
|
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
/* Unlock the token and release the ACL */
|
2022-05-28 19:14:04 +00:00
|
|
|
SepReleaseTokenLock(Token);
|
[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.
2022-06-21 20:02:22 +00:00
|
|
|
SepReleaseAcl(CapturedAcl, PreviousMode, TRUE);
|
2022-05-28 19:14:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Lock the token */
|
|
|
|
SepAcquireTokenLockExclusive(Token);
|
|
|
|
|
|
|
|
/* Clear the default dacl if present */
|
|
|
|
if (Token->DefaultDacl != NULL)
|
|
|
|
{
|
|
|
|
Token->DynamicAvailable += Token->DefaultDacl->AclSize;
|
|
|
|
RtlZeroMemory(Token->DefaultDacl, Token->DefaultDacl->AclSize);
|
|
|
|
Token->DefaultDacl = NULL;
|
|
|
|
|
|
|
|
ExAllocateLocallyUniqueId(&Token->ModifiedId);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
/* Check for TCB privilege */
|
|
|
|
if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
|
|
|
|
{
|
|
|
|
Status = STATUS_PRIVILEGE_NOT_HELD;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Lock the token */
|
|
|
|
SepAcquireTokenLockExclusive(Token);
|
|
|
|
|
|
|
|
Token->SessionId = SessionId;
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* Check for TCB privilege */
|
|
|
|
if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
|
|
|
|
{
|
|
|
|
Status = STATUS_PRIVILEGE_NOT_HELD;
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if it is 0 */
|
|
|
|
if (SessionReference == 0)
|
|
|
|
{
|
|
|
|
ULONG OldTokenFlags;
|
|
|
|
|
|
|
|
/* Lock the token */
|
|
|
|
SepAcquireTokenLockExclusive(Token);
|
|
|
|
|
|
|
|
/* Atomically set the flag in the token */
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
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 */
|
|
|
|
if (RtlIsZeroLuid(&Token->OriginatingLogonSession))
|
|
|
|
{
|
|
|
|
/* Set the token origin */
|
|
|
|
Token->OriginatingLogonSession =
|
|
|
|
TokenOrigin.OriginatingLogonSession;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* EOF */
|