https://git.reactos.org/?p=reactos.git;a=commitdiff;h=44b8e5caac51157cc6c01…
commit 44b8e5caac51157cc6c01d1c07810fc971d3e9ea
Author: George Bișoc <george.bisoc(a)reactos.org>
AuthorDate: Sun Jan 17 21:46:30 2021 +0100
Commit: Victor Perevertkin <victor(a)perevertkin.ru>
CommitDate: Thu Mar 4 16:22:56 2021 +0300
[NTOS:SE] Complete the SepCompareTokens implementation
* Implement SepCompareSidAndAttributesFromTokens and SepComparePrivilegeAndAttributesFromTokens functions for array elements comparison
* Implement the token comparison code in SepCompareTokens function
* Add a missing PAGED_CODE() in SepCompareTokens as most of the token comparison code is paged
* Use SAL annotations for SepCompareTokens and NtCompareTokens
---
ntoskrnl/se/token.c | 244 ++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 228 insertions(+), 16 deletions(-)
diff --git a/ntoskrnl/se/token.c b/ntoskrnl/se/token.c
index 1f12e0109d2..872e5e15d0d 100644
--- a/ntoskrnl/se/token.c
+++ b/ntoskrnl/se/token.c
@@ -135,12 +135,173 @@ SepDeleteTokenLock(
ExFreePoolWithTag(Token->TokenLock, TAG_SE_TOKEN_LOCK);
}
-static NTSTATUS
-SepCompareTokens(IN PTOKEN FirstToken,
- IN PTOKEN SecondToken,
- OUT PBOOLEAN Equal)
+/**
+ * @brief
+ * Compares the elements of SID arrays provided by tokens.
+ * The elements that are being compared for equality are
+ * the SIDs and their attributes.
+ *
+ * @param[in] SidArrayToken1
+ * SID array from the first token.
+ *
+ * @param[in] CountSidArray1
+ * SID count array from the first token.
+ *
+ * @param[in] SidArrayToken2
+ * SID array from the second token.
+ *
+ * @param[in] CountSidArray2
+ * SID count array from the second token.
+ *
+ * @return
+ * Returns TRUE if the elements match from either arrays,
+ * FALSE otherwise.
+ */
+static
+BOOLEAN
+SepCompareSidAndAttributesFromTokens(
+ _In_ PSID_AND_ATTRIBUTES SidArrayToken1,
+ _In_ ULONG CountSidArray1,
+ _In_ PSID_AND_ATTRIBUTES SidArrayToken2,
+ _In_ ULONG CountSidArray2)
+{
+ ULONG FirstCount, SecondCount;
+ PSID_AND_ATTRIBUTES FirstSidArray, SecondSidArray;
+ PAGED_CODE();
+
+ /* Bail out if index counters provided are not equal */
+ if (CountSidArray1 != CountSidArray2)
+ {
+ DPRINT("SepCompareSidAndAttributesFromTokens(): Index counters are not the same!\n");
+ return FALSE;
+ }
+
+ /* Loop over the SID arrays and compare them */
+ for (FirstCount = 0; FirstCount < CountSidArray1; FirstCount++)
+ {
+ for (SecondCount = 0; SecondCount < CountSidArray2; SecondCount++)
+ {
+ FirstSidArray = &SidArrayToken1[FirstCount];
+ SecondSidArray = &SidArrayToken2[SecondCount];
+
+ if (RtlEqualSid(FirstSidArray->Sid, SecondSidArray->Sid) &&
+ FirstSidArray->Attributes == SecondSidArray->Attributes)
+ {
+ break;
+ }
+ }
+
+ /* We've exhausted the array of the second token without finding this one */
+ if (SecondCount == CountSidArray2)
+ {
+ DPRINT("SepCompareSidAndAttributesFromTokens(): No matching elements could be found in either token!\n");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * @brief
+ * Compares the elements of privilege arrays provided by tokens.
+ * The elements that are being compared for equality are
+ * the privileges and their attributes.
+ *
+ * @param[in] PrivArrayToken1
+ * Privilege array from the first token.
+ *
+ * @param[in] CountPrivArray1
+ * Privilege count array from the first token.
+ *
+ * @param[in] PrivArrayToken2
+ * Privilege array from the second token.
+ *
+ * @param[in] CountPrivArray2
+ * Privilege count array from the second token.
+ *
+ * @return
+ * Returns TRUE if the elements match from either arrays,
+ * FALSE otherwise.
+ */
+static
+BOOLEAN
+SepComparePrivilegeAndAttributesFromTokens(
+ _In_ PLUID_AND_ATTRIBUTES PrivArrayToken1,
+ _In_ ULONG CountPrivArray1,
+ _In_ PLUID_AND_ATTRIBUTES PrivArrayToken2,
+ _In_ ULONG CountPrivArray2)
+{
+ ULONG FirstCount, SecondCount;
+ PLUID_AND_ATTRIBUTES FirstPrivArray, SecondPrivArray;
+ PAGED_CODE();
+
+ /* Bail out if index counters provided are not equal */
+ if (CountPrivArray1 != CountPrivArray2)
+ {
+ DPRINT("SepComparePrivilegeAndAttributesFromTokens(): Index counters are not the same!\n");
+ return FALSE;
+ }
+
+ /* Loop over the privilege arrays and compare them */
+ for (FirstCount = 0; FirstCount < CountPrivArray1; FirstCount++)
+ {
+ for (SecondCount = 0; SecondCount < CountPrivArray2; SecondCount++)
+ {
+ FirstPrivArray = &PrivArrayToken1[FirstCount];
+ SecondPrivArray = &PrivArrayToken2[SecondCount];
+
+ if (RtlEqualLuid(&FirstPrivArray->Luid, &SecondPrivArray->Luid) &&
+ FirstPrivArray->Attributes == SecondPrivArray->Attributes)
+ {
+ break;
+ }
+ }
+
+ /* We've exhausted the array of the second token without finding this one */
+ if (SecondCount == CountPrivArray2)
+ {
+ DPRINT("SepComparePrivilegeAndAttributesFromTokens(): No matching elements could be found in either token!\n");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * @brief
+ * Compares tokens if they're equal based on all the following properties. If all
+ * of the said conditions are met then the tokens are deemed as equal.
+ *
+ * - Every SID that is present in either token is also present in the other one.
+ * - Both or none of the tokens are restricted.
+ * - If both tokens are restricted, every SID that is restricted in either token is
+ * also restricted in the other one.
+ * - Every privilege present in either token is also present in the other one.
+ *
+ * @param[in] FirstToken
+ * The first token.
+ *
+ * @param[in] SecondToken
+ * The second token.
+ *
+ * @param[out] Equal
+ * The retrieved value which determines if the tokens are
+ * equal or not.
+ *
+ * @return
+ * Returns STATUS_SUCCESS.
+ */
+static
+NTSTATUS
+SepCompareTokens(
+ _In_ PTOKEN FirstToken,
+ _In_ PTOKEN SecondToken,
+ _Out_ PBOOLEAN Equal)
{
BOOLEAN Restricted, IsEqual = FALSE;
+ PAGED_CODE();
ASSERT(FirstToken != SecondToken);
@@ -148,21 +309,53 @@ SepCompareTokens(IN PTOKEN FirstToken,
SepAcquireTokenLockShared(FirstToken);
SepAcquireTokenLockShared(SecondToken);
- /* FIXME: Check if every SID that is present in either token is also present in the other one */
+ /* Check if every SID that is present in either token is also present in the other one */
+ if (!SepCompareSidAndAttributesFromTokens(FirstToken->UserAndGroups,
+ FirstToken->UserAndGroupCount,
+ SecondToken->UserAndGroups,
+ SecondToken->UserAndGroupCount))
+ {
+ goto Quit;
+ }
+ /* Is one token restricted but the other isn't? */
Restricted = SeTokenIsRestricted(FirstToken);
- if (Restricted == SeTokenIsRestricted(SecondToken))
+ if (Restricted != SeTokenIsRestricted(SecondToken))
+ {
+ /* If that's the case then bail out */
+ goto Quit;
+ }
+
+ /*
+ * If both tokens are restricted check if every SID
+ * that is restricted in either token is also restricted
+ * in the other one.
+ */
+ if (Restricted)
{
- if (Restricted)
+ if (!SepCompareSidAndAttributesFromTokens(FirstToken->RestrictedSids,
+ FirstToken->RestrictedSidCount,
+ SecondToken->RestrictedSids,
+ SecondToken->RestrictedSidCount))
{
- /* FIXME: Check if every SID that is restricted in either token is also restricted in the other one */
+ goto Quit;
}
+ }
- /* FIXME: Check if every privilege that is present in either token is also present in the other one */
- DPRINT1("FIXME: Pretending tokens are equal!\n");
- IsEqual = TRUE;
+ /* Check if every privilege present in either token is also present in the other one */
+ if (!SepComparePrivilegeAndAttributesFromTokens(FirstToken->Privileges,
+ FirstToken->PrivilegeCount,
+ SecondToken->Privileges,
+ SecondToken->PrivilegeCount))
+ {
+ goto Quit;
}
+ /* If we're here then the tokens are equal */
+ IsEqual = TRUE;
+ DPRINT("SepCompareTokens(): Tokens are equal!\n");
+
+Quit:
/* Unlock the tokens */
SepReleaseTokenLock(SecondToken);
SepReleaseTokenLock(FirstToken);
@@ -3921,14 +4114,29 @@ NtOpenThreadToken(IN HANDLE ThreadHandle,
TokenHandle);
}
-/*
- * @unimplemented
+/**
+ * @brief
+ * Compares tokens if they're equal or not.
+ *
+ * @param[in] FirstToken
+ * The first token.
+ *
+ * @param[in] SecondToken
+ * The second token.
+ *
+ * @param[out] Equal
+ * The retrieved value which determines if the tokens are
+ * equal or not.
+ *
+ * @return
+ * Returns STATUS_SUCCESS, otherwise it returns a failure NTSTATUS code.
*/
NTSTATUS
NTAPI
-NtCompareTokens(IN HANDLE FirstTokenHandle,
- IN HANDLE SecondTokenHandle,
- OUT PBOOLEAN Equal)
+NtCompareTokens(
+ _In_ HANDLE FirstTokenHandle,
+ _In_ HANDLE SecondTokenHandle,
+ _Out_ PBOOLEAN Equal)
{
KPROCESSOR_MODE PreviousMode;
PTOKEN FirstToken, SecondToken;
@@ -3960,7 +4168,10 @@ NtCompareTokens(IN HANDLE FirstTokenHandle,
(PVOID*)&FirstToken,
NULL);
if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("ObReferenceObjectByHandle() failed (Status 0x%lx)\n", Status);
return Status;
+ }
Status = ObReferenceObjectByHandle(SecondTokenHandle,
TOKEN_QUERY,
@@ -3970,6 +4181,7 @@ NtCompareTokens(IN HANDLE FirstTokenHandle,
NULL);
if (!NT_SUCCESS(Status))
{
+ DPRINT1("ObReferenceObjectByHandle() failed (Status 0x%lx)\n", Status);
ObDereferenceObject(FirstToken);
return Status;
}
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=a340ec17677fbad860a23…
commit a340ec17677fbad860a238f88a01f11a9130dd78
Author: George Bișoc <george.bisoc(a)reactos.org>
AuthorDate: Thu Mar 4 02:43:30 2021 +0100
Commit: GitHub <noreply(a)github.com>
CommitDate: Thu Mar 4 04:43:30 2021 +0300
[NTOS:PS] Guard the quota in a spin lock (#3419)
Prior to acquiring a quota from the process and do whatever it's needed to do (charge it or return it back), we must guard ourselves with a spinlock so that we may not get into potential race conditions. In Windows Server 2003, PspGivebackQuota and PspExpandQuota do the same thing and they're the equivalent to PspReturnProcessQuotaSpecifiedPool and PspChargeProcessQuotaSpecifiedPool in our codebase.
---
ntoskrnl/ps/quota.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/ntoskrnl/ps/quota.c b/ntoskrnl/ps/quota.c
index 0645654a320..947f78a658e 100644
--- a/ntoskrnl/ps/quota.c
+++ b/ntoskrnl/ps/quota.c
@@ -38,28 +38,32 @@ PspChargeProcessQuotaSpecifiedPool(IN PEPROCESS Process,
IN PS_QUOTA_TYPE QuotaType,
IN SIZE_T Amount)
{
+ KIRQL OldIrql;
ASSERT(Process);
ASSERT(Process != PsInitialSystemProcess);
ASSERT(QuotaType < PsQuotaTypes);
ASSERT(Process->QuotaBlock);
- /* Note: Race warning. TODO: Needs to add/use lock for this */
+ /* Guard our quota in a spin lock */
+ KeAcquireSpinLock(&PspQuotaLock, &OldIrql);
+
if (Process->QuotaUsage[QuotaType] + Amount >
Process->QuotaBlock->QuotaEntry[QuotaType].Limit)
{
DPRINT1("Quota exceeded, but ROS will let it slide...\n");
+ KeReleaseSpinLock(&PspQuotaLock, OldIrql);
return STATUS_SUCCESS;
//return STATUS_QUOTA_EXCEEDED; /* caller raises the exception */
}
InterlockedExchangeAdd((LONG*)&Process->QuotaUsage[QuotaType], Amount);
- /* Note: Race warning. TODO: Needs to add/use lock for this */
if (Process->QuotaPeak[QuotaType] < Process->QuotaUsage[QuotaType])
{
Process->QuotaPeak[QuotaType] = Process->QuotaUsage[QuotaType];
}
+ KeReleaseSpinLock(&PspQuotaLock, OldIrql);
return STATUS_SUCCESS;
}
@@ -73,10 +77,15 @@ PspReturnProcessQuotaSpecifiedPool(IN PEPROCESS Process,
IN PS_QUOTA_TYPE QuotaType,
IN SIZE_T Amount)
{
+ KIRQL OldIrql;
ASSERT(Process);
ASSERT(Process != PsInitialSystemProcess);
ASSERT(QuotaType < PsQuotaTypes);
ASSERT(!(Amount & 0x80000000)); /* we need to be able to negate it */
+
+ /* Guard our quota in a spin lock */
+ KeAcquireSpinLock(&PspQuotaLock, &OldIrql);
+
if (Process->QuotaUsage[QuotaType] < Amount)
{
DPRINT1("WARNING: Process->QuotaUsage sanity check failed.\n");
@@ -86,6 +95,8 @@ PspReturnProcessQuotaSpecifiedPool(IN PEPROCESS Process,
InterlockedExchangeAdd((LONG*)&Process->QuotaUsage[QuotaType],
-(LONG)Amount);
}
+
+ KeReleaseSpinLock(&PspQuotaLock, OldIrql);
}
/* FUNCTIONS ***************************************************************/