Files
Ladislav Zezula ad3fd132d2 * Tested and completed Diversenok's pull request
* Workspaces updated to VS 2022
* Minor changes
2023-01-31 08:56:18 +01:00

303 lines
11 KiB
C++

/*****************************************************************************/
/* ReparseDataHsm.cpp Copyright (c) Ladislav Zezula 2018 */
/*---------------------------------------------------------------------------*/
/* Description: Implementation of HSM reparse data functions */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 18.03.14 1.00 Lad The first version of ReparseDataHsm.cpp */
/*****************************************************************************/
#include "FileTest.h"
#include "ReparseDataHsm.h"
#include "resource.h"
//-----------------------------------------------------------------------------
// Local functions
static bool HsmpCheckElement(PHSM_DATA HsmData, ULONG ElementIndex)
{
PHSM_ELEMENT_INFO pElementInfo = &HsmData->ElementInfos[ElementIndex];
ULONG ElementCount = HsmData->NumberOfElements;
if(pElementInfo->Type >= HSM_ELEMENT_TYPE_MAX)
return false;
if(pElementInfo->Offset != 0 && pElementInfo->Offset < HSM_MIN_DATA_SIZE(ElementCount))
return false;
if(pElementInfo->Offset > HsmData->Length)
return false;
if(pElementInfo->Length > HsmData->Length)
return false;
if((pElementInfo->Offset + pElementInfo->Length) > HsmData->Length)
return false;
return true;
}
static NTSTATUS HsmValidateCommonData(PHSM_DATA HsmData, ULONG Magic, ULONG ElementCount, ULONG RemainingLength)
{
ULONG FirstElementOffset;
ULONG NumberOfElements;
ULONG CheckPhase = 0;
// The remaining data must be at least HSM_FILE_DATA_MINSIZE bytes
if(RemainingLength < HSM_MIN_DATA_SIZE(1))
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
CheckPhase = 1;
if(HsmData->Magic != Magic)
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
CheckPhase = 2;
// Check CRC, if present
if((HsmData->Flags & HSM_DATA_HAVE_CRC) && RtlComputeCrc32(0, &HsmData->Length, RemainingLength - 8) != HsmData->Crc32)
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
CheckPhase = 3;
// Check the remaining size
if(HsmData->Length != RemainingLength)
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
CheckPhase = 4;
// Check the zero field
if((NumberOfElements = HsmData->NumberOfElements) == 0)
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
CheckPhase = 5;
// Check the offset of the first element
if((FirstElementOffset = HSM_MIN_DATA_SIZE(NumberOfElements)) > RemainingLength)
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
CheckPhase = 0x10000;
// Check the elements
for (ULONG i = 0; i < min(NumberOfElements, ElementCount); i++)
{
if(!HsmpCheckElement(HsmData, i))
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
CheckPhase++;
}
CheckPhase = 0x20000;
// Check element[0] (version?)
if(NumberOfElements == 0 || RemainingLength < HSM_MIN_DATA_SIZE(1))
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
if(!HsmpCheckElement(HsmData, 0))
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
if(HsmData->ElementInfos[0].Type != HSM_ELEMENT_TYPE_BYTE || HsmData->ElementInfos[0].Length != sizeof(BYTE))
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
if(*HsmGetElementData(HsmData, 0) != 1)
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
CheckPhase = 0x20001;
return STATUS_SUCCESS;
}
//-----------------------------------------------------------------------------
// Uncompresses the HSM reparse data buffer. Note that the buffer may be already
// uncompressed (if the flag 0x8000 is not set).
LPBYTE HsmGetElementData(PHSM_DATA HsmData, ULONG ElementIndex)
{
return ((LPBYTE)HsmData) + HsmData->ElementInfos[ElementIndex].Offset;
}
NTSTATUS HsmUncompressData(PREPARSE_DATA_BUFFER RawReparseData, ULONG RawReparseDataLength, PREPARSE_DATA_BUFFER * OutReparseData)
{
PREPARSE_DATA_BUFFER HsmReparseData = NULL;
NTSTATUS Status = STATUS_SUCCESS;
ULONG HsmReparseDataLength;
ULONG UncompressedSize = 0;
// Is the cloud buffer compressed?
if(RawReparseData->HsmReparseBufferRaw.Flags & 0x8000)
{
HsmReparseDataLength = sizeof(ULONG) + sizeof(USHORT) + sizeof(USHORT) + RawReparseData->HsmReparseBufferRaw.Length;
HsmReparseData = (PREPARSE_DATA_BUFFER)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, HsmReparseDataLength);
if(HsmReparseData != NULL)
{
// Copy the data that don't belong in the compressed area
memcpy(HsmReparseData, RawReparseData, FIELD_OFFSET(REPARSE_DATA_BUFFER, HsmReparseBufferRaw.RawData));
Status = RtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1,
HsmReparseData->HsmReparseBufferRaw.RawData,
HsmReparseDataLength - FIELD_OFFSET(REPARSE_DATA_BUFFER, HsmReparseBufferRaw.RawData),
RawReparseData->HsmReparseBufferRaw.RawData,
RawReparseDataLength - FIELD_OFFSET(REPARSE_DATA_BUFFER, HsmReparseBufferRaw.RawData),
&UncompressedSize);
if(NT_SUCCESS(Status))
{
HsmReparseData->ReparseDataLength = RawReparseData->HsmReparseBufferRaw.Length;
OutReparseData[0] = HsmReparseData;
return STATUS_SUCCESS;
}
// Free the allocated buffer
HeapFree(g_hHeap, 0, HsmReparseData);
HsmReparseData = NULL;
}
return STATUS_INSUFFICIENT_RESOURCES;
}
else
{
OutReparseData[0] = RawReparseData;
return STATUS_SUCCESS;
}
}
//-----------------------------------------------------------------------------
// Validates the HSM reparse point. Basically the same implementation like
// cldflt.sys!HsmpRpValidateBuffer:
// NTSTATUS HsmpRpValidateBuffer(PHSM_REPARSE_DATA pHsmReparseData, ULONG ReparseDataSize)
NTSTATUS HsmpBitmapIsReparseBufferSupported(PHSM_DATA HsmData, ULONG RemainingLength)
{
NTSTATUS Status;
ULONG NumberOfElements;
BYTE Element1;
BYTE Element2;
// Perform common data validation
Status = HsmValidateCommonData(HsmData, HSM_BITMAP_MAGIC, HSM_BITMAP_ELEMENTS, RemainingLength);
if(!NT_SUCCESS(Status))
return Status;
NumberOfElements = HsmData->NumberOfElements;
// Check element[2]
if(NumberOfElements < 2 || RemainingLength < HSM_MIN_DATA_SIZE(2))
return STATUS_NOT_FOUND;
if(!HsmpCheckElement(HsmData, 2))
return STATUS_NOT_FOUND;
if(HsmData->ElementInfos[2].Type != HSM_ELEMENT_TYPE_BYTE || HsmData->ElementInfos[2].Length != sizeof(BYTE))
return STATUS_NOT_FOUND;
Element2 = *HsmGetElementData(HsmData, 2);
if(Element2 != 0)
{
if(NumberOfElements < 4 || HsmData->ElementInfos[4].Offset == 0)
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
if(HsmData->ElementInfos[4].Length > 0x1000)
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
}
if(Element2 > 1)
STATUS_CLOUD_FILE_METADATA_CORRUPT;
if(NumberOfElements < 1 || RemainingLength < HSM_MIN_DATA_SIZE(1))
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
if(!HsmpCheckElement(HsmData, 1))
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
if(HsmData->ElementInfos[1].Type != HSM_ELEMENT_TYPE_BYTE || HsmData->ElementInfos[1].Length != sizeof(BYTE))
return STATUS_NOT_FOUND;
Element1 = *HsmGetElementData(HsmData, 1);
if(Element1 <= 0 || Element1 > 0x14)
STATUS_CLOUD_FILE_METADATA_CORRUPT;
return STATUS_SUCCESS;
}
NTSTATUS HsmpCheckBitmapElement(PHSM_DATA HsmData, ULONG ElementIndex)
{
PHSM_DATA pBitmap = NULL;
NTSTATUS Status = STATUS_SUCCESS;
ULONG RemainingLength = HsmData->Length;
ULONG BitmapLength = 0;
__try
{
if(HsmData->NumberOfElements < ElementIndex || RemainingLength < HSM_MIN_DATA_SIZE(ElementIndex))
{
Status = STATUS_NOT_FOUND;
__leave;
}
if(!HsmpCheckElement(HsmData, ElementIndex))
{
Status = STATUS_NOT_FOUND;
__leave;
}
if(HsmData->ElementInfos[ElementIndex].Type != HSM_ELEMENT_TYPE_BITMAP)
{
Status = STATUS_NOT_FOUND;
__leave;
}
if(HsmData->ElementInfos[ElementIndex].Offset && HsmData->ElementInfos[ElementIndex].Length)
{
pBitmap = (PHSM_DATA)HsmGetElementData(HsmData, ElementIndex);
BitmapLength = HsmData->ElementInfos[ElementIndex].Length;
}
Status = HsmpBitmapIsReparseBufferSupported(pBitmap, BitmapLength);
}
__finally
{
Status = (Status == STATUS_NOT_FOUND) ? STATUS_SUCCESS : Status;
}
return Status;
}
NTSTATUS HsmValidateReparseData(PREPARSE_DATA_BUFFER ReparseData)
{
PHSM_REPARSE_DATA HsmReparseData = (PHSM_REPARSE_DATA)(&ReparseData->HsmReparseBufferRaw);
PHSM_DATA HsmData;
NTSTATUS Status;
ULONG NumberOfElements;
ULONG RemainingLength;
ULONG ElementFlags;
// Check the length
if(HsmReparseData->Length != ReparseData->ReparseDataLength)
return STATUS_INVALID_BLOCK_LENGTH;
// Check the revision
if((HsmReparseData->Flags & 0x0F) > 1)
return STATUS_UNKNOWN_REVISION;
if((HsmReparseData->Flags & 0x0F) < 1)
return STATUS_REVISION_MISMATCH;
// Get the HSM data and the remaining length
HsmData = (PHSM_DATA)&HsmReparseData->FileData;
RemainingLength = HsmReparseData->Length - FIELD_OFFSET(HSM_REPARSE_DATA, FileData);
// Check the common part of the data
Status = HsmValidateCommonData(HsmData, HSM_FILE_MAGIC, HSM_FILE_ELEMENTS, RemainingLength);
if(!NT_SUCCESS(Status))
return Status;
NumberOfElements = HsmData->NumberOfElements;
// Check element[1] (flags?)
if(NumberOfElements < 1 || RemainingLength < HSM_MIN_DATA_SIZE(2))
return STATUS_NOT_FOUND;
if(!HsmpCheckElement(HsmData, 1))
return STATUS_NOT_FOUND;
if(HsmData->ElementInfos[1].Type != HSM_ELEMENT_TYPE_UINT32 || HsmData->ElementInfos[1].Length != sizeof(DWORD))
return STATUS_NOT_FOUND;
ElementFlags = *(PULONG)HsmGetElementData(HsmData, 1);
if(ElementFlags & 0x10)
return STATUS_SUCCESS;
// Check element[2] (stream size)
if(NumberOfElements < 2 || RemainingLength < HSM_MIN_DATA_SIZE(3))
return STATUS_NOT_FOUND;
if(!HsmpCheckElement(HsmData, 2))
return STATUS_NOT_FOUND;
if(HsmData->ElementInfos[2].Type != HSM_ELEMENT_TYPE_UINT64 || HsmData->ElementInfos[2].Length != sizeof(ULONGLONG))
return STATUS_NOT_FOUND;
// Check element[4] (HSM_BITMAP)
Status = HsmpCheckBitmapElement(HsmData, 4);
if(!NT_SUCCESS(Status))
return Status;
// Check element[5] (HSM_BITMAP)
Status = HsmpCheckBitmapElement(HsmData, 5);
if(!NT_SUCCESS(Status))
return Status;
// Check element[6] (HSM_BITMAP)
Status = HsmpCheckBitmapElement(HsmData, 6);
return Status;
}