reactos/ntoskrnl/se/objtype.c
George Bișoc e38f4c2b36
[NTOS:SE] Implement object type list utilities
This implements various private kernel routines for object type list management
needed for access check code infrastructure. In addition, update the code documentation
and add missing comments.
2023-08-22 17:54:17 +02:00

391 lines
12 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Security object type list support routines
* COPYRIGHT: Copyright Timo Kreuzer <timo.kreuzer@reactos.org>
* Copyright 2023 George Bișoc <george.bisoc@reactos.org>
*/
/* INCLUDES *******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* PRIVATE FUNCTIONS **********************************************************/
/**
* @brief
* Validates a list of object types passed from user mode,
* ensuring the following conditions are met for a valid
* list:
*
* - The list must not be too big and it can be read
* - Each object must have a valid level
* - The level hierarchy between objects has to be consistent
* (e.g. a root cannot have a level 2 subordinate object)
* - The list must have only one root and it must be in the
* first position
* - Each object type GUID can be read and captured
*
* @param[in] ObjectTypeList
* A pointer to an object type list of which the elements
* are being validated.
*
* @param[in] ObjectTypeListLength
* The length of the list, representing the number of object
* elements in that list.
*
* @return
* Returns STATUS_SUCCESS if the list has been validated and it
* contains valid objects. STATUS_INVALID_PARAMETER is returned
* if the list is not valid. Otherwise a NTSTATUS code is returned.
*/
static
NTSTATUS
SepValidateObjectTypeList(
_In_reads_(ObjectTypeListLength) POBJECT_TYPE_LIST ObjectTypeList,
_In_ ULONG ObjectTypeListLength)
{
PGUID ObjectTypeGuid;
ULONG ObjectTypeIndex;
USHORT Level, PrevLevel;
SIZE_T Size;
/* Ensure we do not hit an integer overflow */
Size = ObjectTypeListLength * sizeof(OBJECT_TYPE_LIST);
if (Size == 0)
{
DPRINT1("The object type list is too big, integer overflow alert!\n");
return STATUS_INVALID_PARAMETER;
}
_SEH2_TRY
{
/* Ensure we can actually read from that list */
ProbeForRead(ObjectTypeList, Size, sizeof(ULONG));
/* Begin looping for each object from the list */
for (ObjectTypeIndex = 0;
ObjectTypeIndex < ObjectTypeListLength;
ObjectTypeIndex++)
{
/* Get the level of this object and check for validity */
Level = ObjectTypeList[ObjectTypeIndex].Level;
if (Level > ACCESS_MAX_LEVEL)
{
DPRINT1("Invalid object level found (level %u, at index %lu)\n", Level, ObjectTypeIndex);
_SEH2_YIELD(return STATUS_INVALID_PARAMETER);
}
/* Are we past the first position in the list? */
if (ObjectTypeIndex != 0)
{
/* Ensure that we do not have two object roots */
if (Level == ACCESS_OBJECT_GUID)
{
DPRINT1("This list has two roots (at index %lu)\n", ObjectTypeIndex);
_SEH2_YIELD(return STATUS_INVALID_PARAMETER);
}
/*
* Ensure the current level is consistent with the prior level.
* That means, if the previous object is the root (denoted by the
* level as ACCESS_OBJECT_GUID aka 0) the current object must
* be a child of the parent which is the root (also called a
* property set). Whereas a property is a sibling of the
* child, the property set.
*/
if (Level > PrevLevel + 1)
{
DPRINT1("The object levels are not consistent (current level %u, previous level %u, at index %lu)\n",
Level, PrevLevel, ObjectTypeIndex);
_SEH2_YIELD(return STATUS_INVALID_PARAMETER);
}
}
else
{
/* This is the first position so the object must be the root */
if (Level != ACCESS_OBJECT_GUID)
{
DPRINT1("The object is not the root at first index!\n");
_SEH2_YIELD(return STATUS_INVALID_PARAMETER);
}
}
/* Get the object type and check that we can read from it */
ObjectTypeGuid = ObjectTypeList[ObjectTypeIndex].ObjectType;
ProbeForRead(ObjectTypeGuid, sizeof(GUID), sizeof(ULONG));
/*
* Cache the level, we need it to ensure the levels between
* the previous and the next object are consistent with each other.
*/
PrevLevel = Level;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
return STATUS_SUCCESS;
}
/**
* @brief
* Compares two object type GUIDs for equality.
*
* @param[in] Guid1
* A pointer to the first object type GUID.
*
* @param[in] Guid2
* A pointer to the second object type GUID.
*
* @return
* Returns TRUE if both GUIDs are equal, FALSE otherwise.
*/
static
BOOLEAN
SepIsEqualObjectTypeGuid(
_In_ CONST GUID *Guid1,
_In_ CONST GUID *Guid2)
{
return RtlCompareMemory(Guid1, Guid2, sizeof(GUID)) == sizeof(GUID);
}
/* PUBLIC FUNCTIONS ************************************************************/
/**
* @brief
* Captures an object type GUID from an object
* access control entry (ACE).
*
* @param[in] Ace
* A pointer to an access control entry, of which
* the object type GUID is to be captured from.
*
* @param[in] IsAceDenied
* If set to TRUE, the function will capture the
* GUID from a denied object ACE, otherwise from
* the allowed object ACE.
*
* @return
* Returns a pointer to an object type GUID, otherwise
* NULL is returned if the target ACE does not have
* an object type GUID.
*/
PGUID
SepGetObjectTypeGuidFromAce(
_In_ PACE Ace,
_In_ BOOLEAN IsAceDenied)
{
PGUID ObjectTypeGuid = NULL;
PAGED_CODE();
/* This Ace must not be NULL */
ASSERT(Ace);
/* Grab the GUID based on the object type ACE header */
ObjectTypeGuid = IsAceDenied ? (PGUID)&((PACCESS_DENIED_OBJECT_ACE)Ace)->ObjectType :
(PGUID)&((PACCESS_ALLOWED_OBJECT_ACE)Ace)->ObjectType;
return ObjectTypeGuid;
}
/**
* @brief
* Searches for an object type GUID if it exists
* on an object type list.
*
* @param[in] ObjectTypeList
* A pointer to an object type list.
*
* @param[in] ObjectTypeListLength
* The length of the list, representing the number
* of object elements in that list.
*
* @param[in] ObjectTypeGuid
* A pointer to an object type GUID to search in the
* list of interest.
*
* @param[out] ObjectIndex
* If the function found the target GUID, the function
* returns a pointer to the object index location to
* this parameter.
*
* @return
* Returns TRUE if the object type GUID of interest exists
* in the target list, FALSE otherwise.
*/
BOOLEAN
SepObjectTypeGuidInList(
_In_reads_(ObjectTypeListLength) POBJECT_TYPE_LIST_INTERNAL ObjectTypeList,
_In_ ULONG ObjectTypeListLength,
_In_ PGUID ObjectTypeGuid,
_Out_ PULONG ObjectIndex)
{
ULONG ObjectTypeIndex;
GUID ObjectTypeGuidToSearch;
PAGED_CODE();
/* Loop over the object elements */
for (ObjectTypeIndex = 0;
ObjectTypeIndex < ObjectTypeListLength;
ObjectTypeIndex++)
{
/* Is this the object we are searching for? */
ObjectTypeGuidToSearch = ObjectTypeList[ObjectTypeIndex].ObjectTypeGuid;
if (SepIsEqualObjectTypeGuid(ObjectTypeGuid, &ObjectTypeGuidToSearch))
{
/* Return the indext of that object to caller */
*ObjectIndex = ObjectTypeIndex;
return TRUE;
}
}
return FALSE;
}
/**
* @brief
* Captures a list of object types and converts it to
* an internal form for use by the kernel. The list
* is validated before its data is copied.
*
* @param[in] ObjectTypeList
* A pointer to a list of object types passed from
* UM to be captured.
*
* @param[in] ObjectTypeListLength
* The length size of the list. This length represents
* the number of object elements in that list.
*
* @param[in] PreviousMode
* Processor access level mode. This has to be set to
* UserMode as object type access check is not supported
* in the kernel.
*
* @param[out] CapturedObjectTypeList
* A pointer to a returned captured list of object types.
*
* @return
* Returns STATUS_SUCCESS if the list of object types has been captured
* successfully. STATUS_INVALID_PARAMETER is returned if the caller hasn't
* supplied a buffer list of object types or the list is invalid.
* STATUS_INSUFFICIENT_RESOURCES is returned if pool memory allocation
* for the captured list has failed.
*/
NTSTATUS
SeCaptureObjectTypeList(
_In_reads_opt_(ObjectTypeListLength) POBJECT_TYPE_LIST ObjectTypeList,
_In_ ULONG ObjectTypeListLength,
_In_ KPROCESSOR_MODE PreviousMode,
_Out_ POBJECT_TYPE_LIST_INTERNAL *CapturedObjectTypeList)
{
NTSTATUS Status;
ULONG ObjectTypeIndex;
SIZE_T Size;
PGUID ObjectTypeGuid;
POBJECT_TYPE_LIST_INTERNAL InternalTypeList;
PAGED_CODE();
/* We do not support that in the kernel */
if (PreviousMode == KernelMode)
{
return STATUS_NOT_IMPLEMENTED;
}
/* No count elements of objects means no captured list for you */
if (ObjectTypeListLength == 0)
{
*CapturedObjectTypeList = NULL;
return STATUS_SUCCESS;
}
/* Check if the caller supplied a list since we have the count of elements */
if (ObjectTypeList == NULL)
{
DPRINT1("The caller did not provide a list of object types!\n");
return STATUS_INVALID_PARAMETER;
}
/* Validate that list before we copy contents from it */
Status = SepValidateObjectTypeList(ObjectTypeList, ObjectTypeListLength);
if (!NT_SUCCESS(Status))
{
DPRINT1("SepValidateObjectTypeList failed (Status 0x%08lx)\n", Status);
return Status;
}
/* Allocate a new list */
Size = ObjectTypeListLength * sizeof(OBJECT_TYPE_LIST_INTERNAL);
InternalTypeList = ExAllocatePoolWithTag(PagedPool, Size, TAG_SEPA);
if (InternalTypeList == NULL)
{
DPRINT1("Failed to allocate pool memory for the object type list!\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
_SEH2_TRY
{
/* Loop for every object element, data was already probed */
for (ObjectTypeIndex = 0;
ObjectTypeIndex < ObjectTypeListLength;
ObjectTypeIndex++)
{
/* Copy the object type GUID */
ObjectTypeGuid = ObjectTypeList[ObjectTypeIndex].ObjectType;
InternalTypeList[ObjectTypeIndex].ObjectTypeGuid = *ObjectTypeGuid;
/* Copy the object hierarchy level */
InternalTypeList[ObjectTypeIndex].Level = ObjectTypeList[ObjectTypeIndex].Level;
/* Initialize the access check rights */
InternalTypeList[ObjectTypeIndex].ObjectAccessRights.RemainingAccessRights = 0;
InternalTypeList[ObjectTypeIndex].ObjectAccessRights.GrantedAccessRights = 0;
InternalTypeList[ObjectTypeIndex].ObjectAccessRights.DeniedAccessRights = 0;
}
/* Give the captured list to caller */
*CapturedObjectTypeList = InternalTypeList;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
ExFreePoolWithTag(InternalTypeList, TAG_SEPA);
InternalTypeList = NULL;
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
return STATUS_SUCCESS;
}
/**
* @brief
* Releases a buffer list of object types.
*
* @param[in] CapturedObjectTypeList
* A list of object types to free.
*
* @param[in] PreviousMode
* Processor access level mode.
*/
VOID
SeReleaseObjectTypeList(
_In_ _Post_invalid_ POBJECT_TYPE_LIST_INTERNAL CapturedObjectTypeList,
_In_ KPROCESSOR_MODE PreviousMode)
{
PAGED_CODE();
if ((PreviousMode != KernelMode) && (CapturedObjectTypeList != NULL))
{
ExFreePoolWithTag(CapturedObjectTypeList, TAG_SEPA);
}
}
/* EOF */