Files
Ladislav Zezula f21a975675 Progress
2024-01-08 09:57:12 +01:00

612 lines
17 KiB
C++

/*****************************************************************************/
/* AceResource.cpp Copyright (c) Ladislav Zezula 2023 */
/*---------------------------------------------------------------------------*/
/* Description: */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 23.11.23 1.00 Lad Created */
/*****************************************************************************/
#include "FileTest.h"
//-----------------------------------------------------------------------------
// Local structures
typedef struct _LOCAL_OCTET_STRING
{
ULONG Length;
BYTE Data[256];
} LOCAL_OCTET_STRING, *PLOCAL_OCTET_STRING;
static void MakeOctetString(LOCAL_OCTET_STRING & OctetString, LPCVOID lpData, ULONG cbData)
{
assert(cbData <= 256);
memset(&OctetString, 0, sizeof(LOCAL_OCTET_STRING));
memcpy(OctetString.Data, lpData, cbData);
OctetString.Length = cbData;
}
static void MakeOctetString(LOCAL_OCTET_STRING & OctetString, ULONGLONG IntValue)
{
// Prepare an 8-byte octet string (little endian)
MakeOctetString(OctetString, &IntValue, sizeof(IntValue));
}
static void MakeOctetStringSid(LOCAL_OCTET_STRING & OctetString, PSID pSid)
{
MakeOctetString(OctetString, pSid, RtlLengthSid(pSid));
}
//-----------------------------------------------------------------------------
// Copies data to an output buffer
NTSTATUS CopyDataAway(LPBYTE pbPtr, LPBYTE pbEnd, LPCVOID lpData, ULONG cbData, PULONG pcbMoveBy)
{
if(cbData > (ULONG)(pbEnd - pbPtr))
return STATUS_BUFFER_OVERFLOW;
// Copy the data to the target buffer
memcpy(pbPtr, lpData, cbData);
// Give the length of the data
if(pcbMoveBy != NULL)
pcbMoveBy[0] = cbData;
return STATUS_SUCCESS;
}
static LPBYTE ExportDataAway(LPBYTE pbPtr, LPBYTE pbEnd, LPCVOID lpData, ULONG cbData)
{
if(CopyDataAway(pbPtr, pbEnd, lpData, cbData, NULL) != STATUS_SUCCESS)
return NULL;
return pbPtr;
}
static LPBYTE ExportDataAway(LPBYTE pbPtr, LPBYTE pbEnd, LOCAL_OCTET_STRING & OctetString)
{
return ExportDataAway(pbPtr, pbEnd, &OctetString, sizeof(ULONG) + OctetString.Length);
}
//-----------------------------------------------------------------------------
// ACE_CSA_OBJECT implementation
ACE_CSA_OBJECT::ACE_CSA_OBJECT()
{
lpData = NULL;
cbData = 0;
}
ACE_CSA_OBJECT::~ACE_CSA_OBJECT()
{
Clear();
}
LPBYTE ACE_CSA_OBJECT::ImportData(LPBYTE pbStructure, LPBYTE pbEnd, size_t Offset, size_t Length)
{
LPVOID lpNewData;
// Check for length overflow
if((Length & 0xFFFFFFFF) != Length)
{
SetLastError((DWORD)STATUS_INVALID_PARAMETER);
return NULL;
}
// Is there enough data in the input?
if((pbStructure + Offset + Length) > pbEnd)
{
SetLastError((DWORD)STATUS_INVALID_PARAMETER);
return NULL;
}
// Allocate the buffer for the data.
// Make sure that it's always aligned to 8
if((lpNewData = LocalAlloc(LPTR, ALIGN_TO_SIZE(Length, 8))) == NULL)
{
SetLastError((DWORD)STATUS_NO_MEMORY);
return NULL;
}
// Free any existing data
Clear();
// Copy the data
memcpy(lpNewData, pbStructure + Offset, Length);
lpData = lpNewData;
cbData = (ULONG)(Length);
// Return the position of the data after the input
return pbStructure + Offset + ALIGN_TO_SIZE(Length, sizeof(DWORD));
}
LPBYTE ACE_CSA_OBJECT::ImportOctet(PACE_OCTET_STRING pOctetString)
{
LPBYTE pbOctet = (LPBYTE)(pOctetString);
size_t cbOctet = OctetStringSize(pOctetString);
return ImportData(pbOctet, pbOctet + cbOctet, 0, cbOctet);
}
void ACE_CSA_OBJECT::Clear()
{
if(lpData != NULL)
LocalFree(lpData);
lpData = NULL;
cbData = 0;
}
size_t ACE_CSA_OBJECT::ExportSize(size_t cbAlignSize) const
{
return ALIGN_TO_SIZE(cbData, cbAlignSize);
}
LPBYTE ACE_CSA_OBJECT::Export(LPBYTE pbPtr, LPBYTE pbEnd) const
{
size_t cbLength = ExportSize();
// Check whether the object fits into the buffer
if((pbPtr + cbLength) > pbEnd)
{
SetLastError(ERROR_BUFFER_OVERFLOW);
return NULL;
}
// Copy the data to the buffer
memcpy(pbPtr, lpData, cbLength);
return pbPtr + ALIGN_TO_SIZE(cbLength, sizeof(DWORD));
}
LPBYTE ACE_CSA_OBJECT::Default(LPBYTE /* pbPtr */, LPBYTE /* pbEnd */, DWORD /* dwIndexHint */) const
{
// Should never be called
assert(false);
return NULL;
}
LPBYTE ACE_CSA_OBJECT::Import(LPCVOID /* lpObject */)
{
// Import of an unknown object is not implemented
SetLastError((DWORD)STATUS_NOT_SUPPORTED);
return NULL;
}
//-----------------------------------------------------------------------------
// ACE_CSA_DWORD64 implementation
LPBYTE ACE_CSA_DWORD64::Default(LPBYTE pbPtr, LPBYTE pbEnd, DWORD i) const
{
DWORD64 DefaultValues[] = {0xDEADBABEULL, 0x12342ULL};
DWORD64 DefaultValue = (i < _countof(DefaultValues)) ? DefaultValues[i] : 0x1234567812345678ULL + i;
return ExportDataAway(pbPtr, pbEnd, &DefaultValue, sizeof(DefaultValue));
}
LPBYTE ACE_CSA_DWORD64::Import(LPCVOID lpObject)
{
LPBYTE pbObject = (LPBYTE)(lpObject);
return ImportData(pbObject, pbObject + sizeof(DWORD64), 0, sizeof(DWORD64));
}
//-----------------------------------------------------------------------------
// ACE_CSA_LPWSTR implementation
LPBYTE ACE_CSA_LPWSTR::Default(LPBYTE pbPtr, LPBYTE pbEnd, DWORD i) const
{
LPCWSTR DefaultValues[] = {L"Daenerys", L"Targaryen", L"TheStormBorn"};
LPCWSTR DefaultValue = DefaultValues[i];
WCHAR szBuffer[128];
if(i >= _countof(DefaultValues))
{
StringCchPrintfW(szBuffer, _countof(szBuffer), L"DefaultString%u", i);
DefaultValue = szBuffer;
}
return ExportDataAway(pbPtr, pbEnd, DefaultValue, (ULONG)StringLength(DefaultValue));
}
LPBYTE ACE_CSA_LPWSTR::Import(LPCVOID lpObject)
{
LPBYTE pbString = (LPBYTE)(lpObject);
size_t cbString;
// Ignore the length, calculate it on our own
if(lpObject == NULL)
return pbString;
cbString = StringLength(lpObject);
// Proceed with the import
return ImportData(pbString, pbString + cbString, 0, cbString);
}
size_t ACE_CSA_LPWSTR::StringLength(LPCVOID lpObject) const
{
size_t cbString = 0;
if(lpObject != NULL)
cbString = (wcslen((LPWSTR)(lpObject)) + 1) * sizeof(WCHAR);
return cbString;
}
//-----------------------------------------------------------------------------
// ACE_CSA_SID implementation
// Windows kernel requires the SID to be prepended with 32-bit length: [Length] [SID]
LPBYTE ACE_CSA_SID::Default(LPBYTE pbPtr, LPBYTE pbEnd, DWORD i) const
{
LOCAL_OCTET_STRING OctetString;
switch(i)
{
case 0:
MakeOctetStringSid(OctetString, (PSID)(SidLocAdmins));
break;
case 1:
MakeOctetStringSid(OctetString, (PSID)(SidLocUsers));
break;
default:
MakeOctetStringSid(OctetString, (PSID)(SidEveryone));
break;
}
return ExportDataAway(pbPtr, pbEnd, OctetString);
}
LPBYTE ACE_CSA_SID::Import(LPCVOID lpObject)
{
return ImportOctet((PACE_OCTET_STRING)(lpObject));
}
//-----------------------------------------------------------------------------
// ACE_CSA_BOOLEAN implementation
// Windows kernel requires each BOOLEAN value to have 8 bytes
size_t ACE_CSA_BOOLEAN::ExportSize(size_t cbAlignSize) const
{
return ALIGN_TO_SIZE(sizeof(ULONG64), cbAlignSize);
}
LPBYTE ACE_CSA_BOOLEAN::Default(LPBYTE pbPtr, LPBYTE pbEnd, DWORD i) const
{
ULONG64 DefaultValue = i & 1;
return ExportDataAway(pbPtr, pbEnd, &DefaultValue, sizeof(DefaultValue));
}
LPBYTE ACE_CSA_BOOLEAN::Import(LPCVOID lpObject)
{
LPBYTE pbObject = (LPBYTE)(lpObject);
return ImportData(pbObject, pbObject + sizeof(BOOLEAN), 0, sizeof(BOOLEAN));
}
//-----------------------------------------------------------------------------
// ACE_CSA_OCTET_STRING implementation
LPBYTE ACE_CSA_OCTET_STRING::Default(LPBYTE pbPtr, LPBYTE pbEnd, DWORD i) const
{
LOCAL_OCTET_STRING OctetString;
BYTE OctetString0[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
BYTE OctetString1[] = {0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6, 0x07, 0x18, 0x29, 0x3A, 0x4B, 0x3C, 0x4D, 0x01, 0x02, 0x03, 0x04};
switch(i)
{
case 0:
MakeOctetString(OctetString, OctetString0, sizeof(OctetString0));
break;
case 1:
MakeOctetString(OctetString, OctetString1, sizeof(OctetString1));
break;
default:
MakeOctetString(OctetString, i);
break;
}
return ExportDataAway(pbPtr, pbEnd, OctetString);
}
LPBYTE ACE_CSA_OCTET_STRING::Import(LPCVOID lpObject)
{
return ImportOctet((PACE_OCTET_STRING)(lpObject));
}
//-----------------------------------------------------------------------------
// ACE_CSA_HELPER implementation
ACE_CSA_HELPER::ACE_CSA_HELPER(LPCWSTR szName, WORD wValueType, DWORD dwValueCount)
{
// Setup the object so that it does not contain anything
InitialReset();
// Create the object name
if(Name.Import(szName) == NULL)
return;
// Set the value type and count
SetValueType(wValueType, dwValueCount);
}
ACE_CSA_HELPER::ACE_CSA_HELPER()
{
InitialReset();
}
ACE_CSA_HELPER::~ACE_CSA_HELPER()
{
Clear();
}
void ACE_CSA_HELPER::InitialReset()
{
ValueType = Reserved = 0;
ValueCount = Flags = 0;
ppObjects = NULL;
}
void ACE_CSA_HELPER::Clear()
{
// Reset the attribute name
Name.Clear();
// Free the values
if(ppObjects != NULL)
delete[] ppObjects;
InitialReset();
}
NTSTATUS ACE_CSA_HELPER::SetValueName(LPCWSTR szName)
{
return (Name.Import(szName) != NULL) ? ERROR_SUCCESS : GetLastError();
}
NTSTATUS ACE_CSA_HELPER::SetValueType(WORD wValueType, DWORD dwValueCount)
{
ACE_CSA_OBJECT * ppSaveObjects = NULL;
NTSTATUS Status;
DWORD dwSaveValueCount = 0;
BYTE CopyBuffer[MAX_ACL_LENGTH];
// If we are changing the value type, we need to free the current values
if(wValueType != ValueType)
{
// Free the current objects
if(ppObjects != NULL)
delete[] ppObjects;
ppSaveObjects = ppObjects = NULL;
}
else if(dwValueCount != ValueCount)
{
dwSaveValueCount = ValueCount;
ppSaveObjects = ppObjects;
ppObjects = NULL;
}
else
{
return STATUS_SUCCESS;
}
// Setup the value type and value count
ValueCount = dwValueCount;
ValueType = wValueType;
// Allocate elements and supply default values
if((Status = AllocateElements()) == STATUS_SUCCESS)
{
LPBYTE pbPtr = CopyBuffer;
LPBYTE pbEnd = pbPtr + sizeof(CopyBuffer);
LPBYTE pbResult;
for(DWORD i = 0; i < ValueCount; i++)
{
// Make sure that the copy buffer is zeroed
memset(CopyBuffer, 0, sizeof(CopyBuffer));
// Export the existing object or create default value
if(ppSaveObjects && i < dwSaveValueCount)
{
pbResult = ppSaveObjects[i].Export(pbPtr, pbEnd);
}
else
{
pbResult = ppObjects[i].Default(pbPtr, pbEnd, i);
}
// Do we have some value?
if(pbResult != NULL)
{
ppObjects[i].Import(pbPtr);
}
}
}
else
{
ValueCount = dwSaveValueCount;
ppObjects = ppSaveObjects;
}
return Status;
}
NTSTATUS ACE_CSA_HELPER::SetValueData(LPCVOID lpObject, ULONG Index)
{
// Objects must be already allocated
assert(ppObjects != NULL);
assert(ValueCount != 0);
// Check for overflow
if(Index >= ValueCount)
return STATUS_BUFFER_OVERFLOW;
// Import the object
return (ppObjects[Index].Import(lpObject) != NULL) ? STATUS_SUCCESS : GetLastError();
}
NTSTATUS ACE_CSA_HELPER::AllocateElements()
{
// Sanity checks
assert(ppObjects == NULL);
assert(ValueCount != 0);
assert(ValueType != 0);
// Allocate the elements based on element type
switch(ValueType)
{
case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
ppObjects = new ACE_CSA_DWORD64[ValueCount];
break;
case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
ppObjects = new ACE_CSA_LPWSTR[ValueCount];
break;
case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
ppObjects = new ACE_CSA_SID[ValueCount];
break;
case CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN:
ppObjects = new ACE_CSA_BOOLEAN[ValueCount];
break;
case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
ppObjects = new ACE_CSA_OCTET_STRING[ValueCount];
break;
default:
return STATUS_INVALID_PARAMETER;
}
// If the allocation failed, return error
return (ppObjects != NULL) ? STATUS_SUCCESS : STATUS_NO_MEMORY;
}
NTSTATUS ACE_CSA_HELPER::Import(LPBYTE pbAttrRel, LPBYTE pbAttrEnd, PULONG pcbMoveBy)
{
NTSTATUS Status = STATUS_BAD_DATA;
ULONG cbBase = FIELD_OFFSET(CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1, Values);
ULONG cbMoveBy = 0;
// Free the current values
Clear();
// Enough data to cover CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1?
if((pbAttrRel + cbBase) <= pbAttrEnd)
{
PCLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 pAttrRel = (PCLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1)(pbAttrRel);
LPBYTE pbEndObject;
// Import the base values
ValueType = pAttrRel->ValueType;
Reserved = pAttrRel->Reserved;
Flags = pAttrRel->Flags;
ValueCount = pAttrRel->ValueCount;
cbMoveBy = cbBase;
// Import the name
if((pbEndObject = Name.Import(pbAttrRel + pAttrRel->Name)) == NULL)
return GetLastError();
cbMoveBy = max(cbMoveBy, (ULONG)(pbEndObject - pbAttrRel));
// Enough to cover the value offsets too?
if((pbAttrRel + (ValueCount * sizeof(DWORD))) <= pbAttrEnd)
{
// Make sure that we have the elements
if((Status = AllocateElements()) == STATUS_SUCCESS)
{
for(ULONG i = 0; i < ValueCount; i++)
{
// Import the n-th value. TODO: Verify size of the data!!!
pbEndObject = ppObjects[i].Import(pbAttrRel + pAttrRel->Values.pUint64[i]);
if(pbEndObject == NULL)
return GetLastError();
// Update the biggest offset
cbMoveBy = max(cbMoveBy, (ULONG)(pbEndObject - pbAttrRel));
}
}
}
}
// Give the result to the caller. Always try to eat up to 8-byte boundary
if(pcbMoveBy != NULL)
pcbMoveBy[0] = GetSizeAlignedToMax(pbAttrRel, pbAttrEnd, cbMoveBy);
return Status;
}
// In case NtSetSecurityObject returns STATUS_INVALID_ACL, look here:
// nt!RtlpValidRelativeAttribute(PCLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 pAttrRel, size_t cbAttrRel)
PCLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 ACE_CSA_HELPER::Export(PULONG pcbLength) const
{
PCLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 pAttrRel = NULL;
ULONG cbLength = 0;
// Allocate buffer. Don't bother with calculating the length.
// The maximum size of an ACE is 0xFFF8 bytes, which we can afford to allocate
pAttrRel = (PCLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1)LocalAlloc(LPTR, MAX_ACL_LENGTH);
if(pAttrRel != NULL)
{
LPBYTE pbStructure = (LPBYTE)(pAttrRel);
LPBYTE pbPtr = pbStructure + FIELD_OFFSET(CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1, Values) + ValueCount * sizeof(ULONG);
LPBYTE pbEnd = pbStructure + MAX_ACL_LENGTH;
// Copy the base structure
pAttrRel->ValueType = ValueType;
pAttrRel->Reserved = Reserved;
pAttrRel->Flags = Flags;
pAttrRel->ValueCount = ValueCount;
// Export the attribute name
if(Name.Export(pbPtr, pbEnd) > pbPtr)
{
pAttrRel->Name = (ULONG)(pbPtr - pbStructure);
pbPtr = pbPtr + Name.ExportSize(sizeof(DWORD));
}
// Copy values
for(ULONG i = 0; i < ValueCount; i++)
{
// Write the value offset
pAttrRel->Values.pUint64[i] = (ULONG)(pbPtr - pbStructure);
// Write the value itself
pbPtr = ppObjects[i].Export(pbPtr, pbEnd);
assert(pbPtr != NULL);
}
// Update the length
cbLength = (ULONG)(pbPtr - pbStructure);
}
// Give the result to the caller
if(pcbLength != NULL)
pcbLength[0] = cbLength;
return pAttrRel;
}
ULONG ACE_CSA_HELPER::GetSizeAlignedToMax(LPBYTE pbPtr, LPBYTE pbEnd, ULONG cbLength)
{
ULONG cbLengthAligned;
// Can we eat up size aligned to 8 bytes?
cbLengthAligned = ALIGN_TO_SIZE(cbLength, 8);
if((pbPtr + cbLengthAligned) <= pbEnd)
return cbLengthAligned;
// Can we eat up size aligned to 4 bytes?
cbLengthAligned = ALIGN_TO_SIZE(cbLength, 4);
if((pbPtr + cbLengthAligned) <= pbEnd)
return cbLengthAligned;
// Can we eat up size aligned to 2 bytes?
cbLengthAligned = ALIGN_TO_SIZE(cbLength, 2);
if((pbPtr + cbLengthAligned) <= pbEnd)
return cbLengthAligned;
// Return the length as-is
assert(false);
return cbLength;
}