Files
2026-04-29 17:10:47 +02:00

3590 lines
125 KiB
C++

/*****************************************************************************/
/* Page09Security.cpp Copyright (c) Ladislav Zezula 2005 */
/*---------------------------------------------------------------------------*/
/* Description : */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 15.08.05 1.00 Lad The first version of Page09Security.cpp */
/*****************************************************************************/
#include "FileTest.h"
#include "resource.h"
//-----------------------------------------------------------------------------
// Local variables
#define ALL_SACL_SECURITY_INFORMATION 0xFFFFFFF8 // Mask for all system-type security infos
#define ITEM_DATA_MAGIC 0xDEADBABE
#define MAX_ITEM_TEXT 1024
#define MAX_NEST_LEVEL 8
#define ItemDataNULL (PVOID)(INT_PTR)(NULL)
#define ItemDataValid (PVOID)(INT_PTR)(1)
typedef enum _ITEM_TYPE
{
ItemTypeUnknown,
ItemTypeOwner, // The item contains "OWNER_SECURITY_INFORMATION"
ItemTypeGroup, // The item contains "GROUP_SECURITY_INFORMATION"
ItemTypeDacl, // The item contains "DACL_SECURITY_INFORMATION"
ItemTypeSacl, // The item contains "SACL_SECURITY_INFORMATION"
ItemTypeNoAcl, // The item says that an ACL is not present
ItemTypeNullAcl, // NULL ACL
ItemTypeSid, // Security Identifier (SID)
ItemTypeAce, // Access Control Entry (ACE) from DACL
ItemTypeAceType, // ACE_HEADER::AceType
ItemTypeBool, // A boolean value
ItemTypeUint08, // 8-bit integer
ItemTypeUint16, // 16-bit integer
ItemTypeUint32, // 32-bit integer
ItemTypeUint64, // 64-bit integer
ItemTypeLPWSTR, // Pointer to a zero-terminate unicode string (LPWSTR)
ItemTypeBinN, // An octet string with length at the beginning
ItemTypeGuid, // An object GUID
ItemTypeGuid2, // An inherited object GUID
ItemTypeSid11, // mandatory label SID
ItemTypeSid17, // Policy label SID
ItemTypeSid19, // Trust level SID
ItemTypeCSA_V1, // The CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 structure
ItemTypeCSA_VType, // CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1::ValueType
ItemTypeCSA_VCnt, // CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1::ValueCount
ItemTypeSidN, // "Counted" SID (with preceding ULONG, containing length)
ItemTypeCondition, // ACE condition
} ITEM_TYPE, *PITEM_TYPE;
typedef struct _NON_EDITABLE_DATA
{
ULONG Magic; // ITEM_DATA_MAGIC
ULONG Length; // Length of the subsequent data
BYTE Data[1]; // The data itself
} NON_EDITABLE_DATA, *PNON_EDITABLE_DATA;
typedef struct _TREE_ITEM_INFO
{
ITEM_TYPE ItemType;
UINT nIDFormat1; // Format string when no data
UINT nIDFormat2; // Format string when there are data
// Array of flag values and flag names
TFlagInfo * pFlagInfos;
// Converts the binary item to string representation that will be shown in the tree view item
NTSTATUS (*ToString) (_TREE_ITEM_INFO * pItemInfo, LPTSTR szBuffer, size_t ccBuffer, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy);
// Converts the string representation to binary item
NTSTATUS(*StringTo) (_TREE_ITEM_INFO * pItemInfo, LPCTSTR szString, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy);
// Creates a new item of that type
NTSTATUS(*CreateNew)(_TREE_ITEM_INFO * pItemInfo, LPBYTE pbDataBuffer, size_t * pcbDataBuffer);
// Type of the item data
PVOID ItemData;
} TREE_ITEM_INFO, *PTREE_ITEM_INFO;
typedef const TREE_ITEM_INFO *PCTREE_ITEM_INFO;
typedef struct _ACE_FIELD_INFO
{
ULONG AceLayoutFlag;
TREE_ITEM_INFO TreeItem;
} ACE_FIELD_INFO, *PACE_FIELD_INFO;
typedef struct _TV_SELECTION
{
size_t ItemIndex[MAX_NEST_LEVEL]; // Item index in each level
size_t nNestLevel; // Nest level of the item that was selected
} TV_SELECTION, *PTV_SELECTION;
static LPCTSTR szAceTypeSuffix = _T("_TYPE");
static LPCSTR szUnknownAceType = "UNKNOWN_ACE_TYPE: %02x";
static size_t GLOBAL_ItemIndex = INVALID_ITEM_INDEX;
static PACL pAclInWork = NULL; // Current ACL-in-construction
static PACE pAceInWork = NULL; // Current ACE-in-construction
static SID_IDENTIFIER_AUTHORITY SiaNull = SECURITY_NULL_SID_AUTHORITY;
static SID_IDENTIFIER_AUTHORITY SiaWorld = SECURITY_WORLD_SID_AUTHORITY;
static SID_IDENTIFIER_AUTHORITY SiaLabel = SECURITY_MANDATORY_LABEL_AUTHORITY;
static SID_IDENTIFIER_AUTHORITY SiaPolicy = SECURITY_SCOPED_POLICY_ID_AUTHORITY;
static SID_IDENTIFIER_AUTHORITY SiaTrust = SECURITY_PROCESS_TRUST_AUTHORITY;
static const BYTE EmptyAcl[] =
{
ACL_REVISION, 0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const BYTE FullControlAcl[] =
{
0x04, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10,
0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00
};
static const LPCTSTR szNoGuidString = _T("(none)");
static TFlagInfo SecurityInformations[] =
{
FLAGINFO_BITV(OWNER_SECURITY_INFORMATION),
FLAGINFO_BITV(GROUP_SECURITY_INFORMATION),
FLAGINFO_BITV(DACL_SECURITY_INFORMATION),
FLAGINFO_BITV(SACL_SECURITY_INFORMATION),
FLAGINFO_BITV(LABEL_SECURITY_INFORMATION),
FLAGINFO_BITV(ATTRIBUTE_SECURITY_INFORMATION),
FLAGINFO_BITV(SCOPE_SECURITY_INFORMATION),
FLAGINFO_BITV(PROCESS_TRUST_LABEL_SECURITY_INFORMATION),
FLAGINFO_BITV(ACCESS_FILTER_SECURITY_INFORMATION),
FLAGINFO_BITV(BACKUP_SECURITY_INFORMATION),
FLAGINFO_BITV(PROTECTED_DACL_SECURITY_INFORMATION),
FLAGINFO_BITV(PROTECTED_SACL_SECURITY_INFORMATION),
FLAGINFO_BITV(UNPROTECTED_DACL_SECURITY_INFORMATION),
FLAGINFO_BITV(UNPROTECTED_SACL_SECURITY_INFORMATION),
FLAGINFO_END()
};
static TFlagInfo AclRevFlags[] =
{
FLAGINFO_NUMV(ACL_REVISION1),
FLAGINFO_NUMV(ACL_REVISION2),
FLAGINFO_NUMV(ACL_REVISION3),
FLAGINFO_NUMV(ACL_REVISION_DS),
FLAGINFO_END()
};
static TFlagInfo AceHdrTypes[] =
{
FLAGINFO_NUMV(ACCESS_ALLOWED_ACE_TYPE),
FLAGINFO_NUMV(ACCESS_DENIED_ACE_TYPE),
FLAGINFO_NUMV(SYSTEM_AUDIT_ACE_TYPE),
FLAGINFO_NUMV(SYSTEM_ALARM_ACE_TYPE),
FLAGINFO_NUMV(ACCESS_ALLOWED_COMPOUND_ACE_TYPE),
FLAGINFO_NUMV(ACCESS_ALLOWED_OBJECT_ACE_TYPE),
FLAGINFO_NUMV(ACCESS_DENIED_OBJECT_ACE_TYPE),
FLAGINFO_NUMV(SYSTEM_AUDIT_OBJECT_ACE_TYPE),
FLAGINFO_NUMV(SYSTEM_ALARM_OBJECT_ACE_TYPE),
FLAGINFO_NUMV(ACCESS_ALLOWED_CALLBACK_ACE_TYPE),
FLAGINFO_NUMV(ACCESS_DENIED_CALLBACK_ACE_TYPE),
FLAGINFO_NUMV(ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE),
FLAGINFO_NUMV(ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE),
FLAGINFO_NUMV(SYSTEM_AUDIT_CALLBACK_ACE_TYPE),
FLAGINFO_NUMV(SYSTEM_ALARM_CALLBACK_ACE_TYPE),
FLAGINFO_NUMV(SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE),
FLAGINFO_NUMV(SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE),
FLAGINFO_NUMV(SYSTEM_MANDATORY_LABEL_ACE_TYPE),
FLAGINFO_NUMV(SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE),
FLAGINFO_NUMV(SYSTEM_SCOPED_POLICY_ID_ACE_TYPE),
FLAGINFO_NUMV(SYSTEM_PROCESS_TRUST_LABEL_ACE_TYPE),
FLAGINFO_NUMV(SYSTEM_ACCESS_FILTER_ACE_TYPE),
FLAGINFO_END()
};
static TFlagInfo AceHdrFlags[] =
{
FLAGINFO_BITV(OBJECT_INHERIT_ACE),
FLAGINFO_BITV(CONTAINER_INHERIT_ACE),
FLAGINFO_BITV(NO_PROPAGATE_INHERIT_ACE),
FLAGINFO_BITV(INHERIT_ONLY_ACE),
FLAGINFO_BITV(INHERITED_ACE),
FLAGINFO_BITV(SUCCESSFUL_ACCESS_ACE_FLAG),
FLAGINFO_BITV(FAILED_ACCESS_ACE_FLAG),
FLAGINFO_END()
};
static TFlagInfo AceHdrFlags2[] =
{
FLAGINFO_BITV(OBJECT_INHERIT_ACE),
FLAGINFO_BITV(CONTAINER_INHERIT_ACE),
FLAGINFO_BITV(NO_PROPAGATE_INHERIT_ACE),
FLAGINFO_BITV(INHERIT_ONLY_ACE),
FLAGINFO_BITV(INHERITED_ACE),
FLAGINFO_BITV(TRUST_PROTECTED_FILTER_ACE_FLAG),
FLAGINFO_END()
};
static TFlagInfo AceMasks[] =
{
FLAGINFO_BITV(FILE_READ_DATA),
FLAGINFO_BITV(FILE_WRITE_DATA),
FLAGINFO_BITV(FILE_APPEND_DATA),
FLAGINFO_BITV(FILE_READ_EA),
FLAGINFO_BITV(FILE_WRITE_EA),
FLAGINFO_BITV(FILE_EXECUTE),
FLAGINFO_BITV(FILE_DELETE_CHILD),
FLAGINFO_BITV(FILE_READ_ATTRIBUTES),
FLAGINFO_BITV(FILE_WRITE_ATTRIBUTES),
FLAGINFO_BITV(DELETE),
FLAGINFO_BITV(READ_CONTROL),
FLAGINFO_BITV(WRITE_DAC),
FLAGINFO_BITV(WRITE_OWNER),
FLAGINFO_BITV(SYNCHRONIZE),
FLAGINFO_BITV(GENERIC_WRITE),
FLAGINFO_BITV(GENERIC_READ),
FLAGINFO_BITV(GENERIC_WRITE),
FLAGINFO_BITV(GENERIC_EXECUTE),
FLAGINFO_BITV(GENERIC_ALL),
FLAGINFO_END()
};
static TFlagInfo AdsAceMasks[] =
{
FLAGINFO_BITV(ADS_RIGHT_DS_CREATE_CHILD),
FLAGINFO_BITV(ADS_RIGHT_DS_DELETE_CHILD),
FLAGINFO_BITV(ADS_RIGHT_ACTRL_DS_LIST),
FLAGINFO_BITV(ADS_RIGHT_DS_SELF),
FLAGINFO_BITV(ADS_RIGHT_DS_READ_PROP),
FLAGINFO_BITV(ADS_RIGHT_DS_WRITE_PROP),
FLAGINFO_BITV(ADS_RIGHT_DS_DELETE_TREE),
FLAGINFO_BITV(ADS_RIGHT_DS_LIST_OBJECT),
FLAGINFO_BITV(ADS_RIGHT_DS_CONTROL_ACCESS),
FLAGINFO_BITV(ADS_RIGHT_DELETE),
FLAGINFO_BITV(ADS_RIGHT_READ_CONTROL),
FLAGINFO_BITV(ADS_RIGHT_WRITE_DAC),
FLAGINFO_BITV(ADS_RIGHT_WRITE_OWNER),
FLAGINFO_BITV(ADS_RIGHT_SYNCHRONIZE),
FLAGINFO_BITV(ADS_RIGHT_ACCESS_SYSTEM_SECURITY),
FLAGINFO_BITV(ADS_RIGHT_GENERIC_READ),
FLAGINFO_BITV(ADS_RIGHT_GENERIC_WRITE),
FLAGINFO_BITV(ADS_RIGHT_GENERIC_EXECUTE),
FLAGINFO_BITV(ADS_RIGHT_GENERIC_ALL),
FLAGINFO_END()
};
static TFlagInfo LabelMasks[] =
{
FLAGINFO_BITV(SYSTEM_MANDATORY_LABEL_NO_WRITE_UP),
FLAGINFO_BITV(SYSTEM_MANDATORY_LABEL_NO_READ_UP),
FLAGINFO_BITV(SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP),
FLAGINFO_END()
};
static TFlagInfo CAceTypes[] =
{
FLAGINFO_BITV(COMPOUND_ACE_IMPERSONATION),
FLAGINFO_END()
};
static TFlagInfo ObjAceFlags[] =
{
FLAGINFO_BITV(ACE_OBJECT_TYPE_PRESENT),
FLAGINFO_BITV(ACE_INHERITED_OBJECT_TYPE_PRESENT),
FLAGINFO_END()
};
static TFlagInfo CompoundAceTypes[] =
{
FLAGINFO_BITV(COMPOUND_ACE_IMPERSONATION),
FLAGINFO_END()
};
static TFlagInfo IntgrLevels[] =
{
FLAGINFO_NUMV(SECURITY_MANDATORY_UNTRUSTED_RID),
FLAGINFO_NUMV(SECURITY_MANDATORY_LOW_RID),
FLAGINFO_NUMV(SECURITY_MANDATORY_MEDIUM_RID),
FLAGINFO_NUMV(SECURITY_MANDATORY_MEDIUM_PLUS_RID),
FLAGINFO_NUMV(SECURITY_MANDATORY_HIGH_RID),
FLAGINFO_NUMV(SECURITY_MANDATORY_SYSTEM_RID),
FLAGINFO_NUMV(SECURITY_MANDATORY_PROTECTED_PROCESS_RID),
FLAGINFO_END()
};
// Process trust types
// Source: https://github.com/googleprojectzero/sandbox-attacksurface-analysis-tools/NtApiDotNet/NtSemaphore.cs
#define TT_None 0
#define TT_ProtectedLight 512
#define TT_Protected 1024
static TFlagInfo PsTrustTypes[] =
{
FLAGINFO_NUMV(TT_None),
FLAGINFO_NUMV(TT_ProtectedLight),
FLAGINFO_NUMV(TT_Protected),
FLAGINFO_END()
};
// Process trust levels
#define TL_None 0
#define TL_Authenticode 1024
#define TL_AntiMalware 1536
#define TL_App 2048
#define TL_Windows 4096
#define TL_WinTcb 8192
static TFlagInfo PsTrustLevels[] =
{
FLAGINFO_NUMV(TL_None),
FLAGINFO_NUMV(TL_Authenticode),
FLAGINFO_NUMV(TL_AntiMalware),
FLAGINFO_NUMV(TL_App),
FLAGINFO_NUMV(TL_Windows),
FLAGINFO_NUMV(TL_WinTcb),
FLAGINFO_END()
};
static TFlagInfo CSA_ValTypes[] =
{
FLAGINFO_NUMV(CLAIM_SECURITY_ATTRIBUTE_TYPE_INVALID),
FLAGINFO_NUMV(CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64),
FLAGINFO_NUMV(CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64),
FLAGINFO_NUMV(CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING),
FLAGINFO_NUMV(CLAIM_SECURITY_ATTRIBUTE_TYPE_FQBN),
FLAGINFO_NUMV(CLAIM_SECURITY_ATTRIBUTE_TYPE_SID),
FLAGINFO_NUMV(CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN),
FLAGINFO_NUMV(CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING),
FLAGINFO_END()
};
static TFlagInfo CSA_Flags[] =
{
FLAGINFO_BITV(CLAIM_SECURITY_ATTRIBUTE_NON_INHERITABLE),
FLAGINFO_BITV(CLAIM_SECURITY_ATTRIBUTE_VALUE_CASE_SENSITIVE),
FLAGINFO_BITV(CLAIM_SECURITY_ATTRIBUTE_USE_FOR_DENY_ONLY),
FLAGINFO_BITV(CLAIM_SECURITY_ATTRIBUTE_DISABLED_BY_DEFAULT),
FLAGINFO_BITV(CLAIM_SECURITY_ATTRIBUTE_DISABLED),
FLAGINFO_BITV(CLAIM_SECURITY_ATTRIBUTE_MANDATORY),
FLAGINFO_END()
};
//-----------------------------------------------------------------------------
// Debug
//#define _COMPARE_ACLS
#ifdef _COMPARE_ACLS
BYTE SaveAclBuffer[MAX_ACL_LENGTH] = {0};
#endif
//-----------------------------------------------------------------------------
// Local functions
static void ReverseArrayItems(HTREEITEM array[], size_t length)
{
size_t start = 0;
size_t end = length - 1;
while(start < end)
{
// Swap elements at start and end indices
HTREEITEM temp = array[start];
array[start] = array[end];
array[end] = temp;
// Move indices towards the center
start++;
end--;
}
}
static PNON_EDITABLE_DATA IsItemDataPointer(PVOID ptr)
{
if((UINT_PTR)(ptr) > 0x100)
{
if(((PNON_EDITABLE_DATA)(ptr))->Magic == ITEM_DATA_MAGIC)
{
return (PNON_EDITABLE_DATA)(ptr);
}
}
return NULL;
}
static bool IsValidAceAttributeType(LPBYTE pbBuffer)
{
WORD wValueType = *(PWORD)(pbBuffer);
// Check valid values
if(CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64 <= wValueType && wValueType <= CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING)
return true;
if(CLAIM_SECURITY_ATTRIBUTE_TYPE_SID <= wValueType && wValueType <= CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING)
return true;
return false;
}
static bool IsValidAceAttributeCount(LPBYTE pbBuffer)
{
DWORD dwValueCount = *(PDWORD)(pbBuffer);
// Artificially limited value count
return (0 < dwValueCount && dwValueCount <= 100);
}
static bool IsItemTypeEditable(PTREE_ITEM_INFO pItemInfo)
{
switch(pItemInfo->ItemType)
{
case ItemTypeAce:
case ItemTypeCondition:
return false;
default:
return true;
}
}
static bool IsIntegerTypeWithFlags(PTREE_ITEM_INFO pItemInfo)
{
int nItemType;
if(pItemInfo->pFlagInfos != NULL)
{
nItemType = (int)pItemInfo->ItemType;
return ((int)ItemTypeUint08 <= nItemType && nItemType <= (int)ItemTypeUint64);
}
return false;
}
static LPCSTR GetAceTypeString(DWORD AceType)
{
static CHAR szBuffer[64];
BYTE MaxAceType = (BYTE)(_countof(AceHdrTypes) - 1);
// Insert the "root" item with ACE type
if(AceType < MaxAceType)
return AceHdrTypes[AceType].szFlagText;
// Prepare string for an unknown ACE type
StringCchPrintfA(szBuffer, _countof(szBuffer), szUnknownAceType, AceType);
return szBuffer;
}
static bool GetAceTypeString(LPTSTR szBuffer, size_t ccBuffer, PACE_HEADER pAceHeader)
{
LPCSTR szAceType = GetAceTypeString(pAceHeader->AceType);
LPTSTR szSuffix;
// Format the ACE type name
StringCchPrintf(szBuffer, ccBuffer, _T("%hs"), szAceType);
// Cut off the "_TYPE" suffix
if((szSuffix = _tcsrchr(szBuffer, _T('_'))) != NULL)
{
if(!_tcscmp(szSuffix, szAceTypeSuffix))
{
szSuffix[0] = 0;
}
}
return true;
}
static ULONG AceObjectGuidFlag(ITEM_TYPE ItemType)
{
if(ItemType == ItemTypeGuid)
return ACE_OBJECT_TYPE_PRESENT;
if(ItemType == ItemTypeGuid2)
return ACE_INHERITED_OBJECT_TYPE_PRESENT;
return 0;
}
static ULONG AceObjectGuidPresent(PACE pAce, ITEM_TYPE ItemType)
{
if(pAce != NULL)
{
ACE_HELPER AceHelper(pAce->Header.AceType);
if(AceHelper.AceLayout & ACE_FIELD_GUID_FLAGS)
{
return (pAce->Flags & AceObjectGuidFlag(ItemType));
}
}
return 0;
}
void SidToString(PSID pvSid, LPTSTR szString, size_t cchString, bool bAddUserName)
{
PSID_IDENTIFIER_AUTHORITY pSia;
SID * pSid = (SID *)pvSid;
LPTSTR szStringEnd = szString + cchString;
UCHAR SubAuthCount;
// Add the "S-%u-" begin with revision
pSia = GetSidIdentifierAuthority(pSid);
StringCchPrintfEx(szString, (szStringEnd - szString), &szString, NULL, 0, _T("S-%u-%u"),
pSid->Revision,
pSia->Value[5]);
// Add the subauthorities
SubAuthCount = *GetSidSubAuthorityCount(pSid);
for(DWORD i = 0; i < SubAuthCount; i++)
{
DWORD dwSubAuth = *GetSidSubAuthority(pSid, i);
StringCchPrintfEx(szString, (szStringEnd - szString), &szString, NULL, 0, L"-%u", dwSubAuth);
}
// If we are required to add user name, do it.
if(bAddUserName)
{
SID_NAME_USE SidNameUse;
TCHAR szDomainName[128] = _T("");
TCHAR szUserName[128] = _T("");
DWORD cchDomainName = _countof(szDomainName);
DWORD cchUserName = _countof(szUserName);
if(LookupAccountSid(NULL, pSid, szUserName, &cchUserName, szDomainName, &cchDomainName, &SidNameUse))
{
if(szDomainName[0] != 0)
StringCchPrintf(szString, (szStringEnd - szString), _T(" (%s\\%s)"), szDomainName, szUserName);
else
StringCchPrintf(szString, (szStringEnd - szString), _T(" (%s)"), szUserName);
}
else
{
StringCchPrintf(szString, (szStringEnd - szString), _T(" (Unknown SID)"));
}
}
}
//
// The SID in the SYSTEM_MANDATORY_LABEL_ACE has the following format:
//
// - IdentifierAuthority is set to SECURITY_MANDATORY_LABEL_AUTHORITY
// - The last subauthority is set to one of the SECURITY_MANDATORY_XXXX values
//
static LPBYTE GetSystemSidIntegerValue(PSID pSid, PSID_IDENTIFIER_AUTHORITY pSiaExpected, UCHAR SubAuthCountNeeded = 1)
{
UCHAR SubAuthCount;
if(pSid != NULL && RtlLengthSid(pSid) > FIELD_OFFSET(SYSTEM_MANDATORY_LABEL_ACE, SidStart))
{
PSID_IDENTIFIER_AUTHORITY pSia = GetSidIdentifierAuthority(pSid);
// Compare the required identifier authority
if(!memcmp(pSia, pSiaExpected, sizeof(SID_IDENTIFIER_AUTHORITY)))
{
// Get the number of sub-authorities
if((SubAuthCount = *GetSidSubAuthorityCount(pSid)) == SubAuthCountNeeded)
{
// Get the last authority
return (LPBYTE)GetSidSubAuthority(pSid, 0);
}
}
}
// Return NULL
assert(false);
return NULL;
}
static LPBYTE GetSidIntegrityLevel(PSID pSid)
{
return GetSystemSidIntegerValue(pSid, &SiaLabel);
}
static LPBYTE GetSidScopedPolicyId(PSID pSid)
{
return GetSystemSidIntegerValue(pSid, &SiaPolicy);
}
static LPBYTE GetSidProcessTrustLevel(PSID pSid)
{
return GetSystemSidIntegerValue(pSid, &SiaTrust, 2);
}
//-----------------------------------------------------------------------------
// Local functions - tree items
static DWORD GetDefaultAceTypeForAcl(PCTREE_ITEM_INFO pItemInfo)
{
return (pItemInfo->ItemType == ItemTypeSacl) ? SYSTEM_MANDATORY_LABEL_ACE_TYPE : ACCESS_ALLOWED_ACE_TYPE;
}
static int GetDefaultCharLimit(PCTREE_ITEM_INFO pItemInfo)
{
if(pItemInfo->ItemType == ItemTypeUint08 || pItemInfo->ItemType == ItemTypeAceType)
return 8;
if(pItemInfo->ItemType == ItemTypeUint16)
return 16;
if(pItemInfo->ItemType == ItemTypeUint32)
return 32;
if(pItemInfo->ItemType == ItemTypeUint64)
return 64;
return 256;
}
static void UpdateAceVariables(PACE pAce, LPBYTE pbPtr)
{
if(pAce != NULL)
{
LPBYTE pbAce = (LPBYTE)(pAce);
// Update the length of the ACE
pAce->Header.AceSize = (WORD)(pbPtr - pbAce);
// Update the ACL-in-work variables
if(pAclInWork != NULL)
{
// If the ACE is of ACCESS_ALLOWED_COMPOUND_ACE_TYPE, the ACL revision must be 3 or higher
if(pAce->Header.AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE)
pAclInWork->AclRevision = max(pAclInWork->AclRevision, ACL_REVISION3);
// If the ACE is one of the object ACEs or later, raise the ACL revision
if(pAce->Header.AceType >= ACCESS_ALLOWED_OBJECT_ACE_TYPE)
pAclInWork->AclRevision = max(pAclInWork->AclRevision, ACL_REVISION_DS);
// Update the ACE count
pAclInWork->AceCount++;
}
}
}
// The item text is expected to be in format "Name: 0x12345678"
static LPTSTR GetItemTextValue(LPTSTR szItemText, bool bKeepQuotedPart = false)
{
LPTSTR szStringEnd;
LPTSTR szTextPtr;
// Retrieve the first occurrence of ":"
if((szTextPtr = _tcschr(szItemText, _T(':'))) != NULL)
{
// Skip the colon
szItemText = szTextPtr + 1;
// Skip spaces
while(szItemText[0] == ' ')
szItemText++;
// Did we come across a quotation mark?
if(szItemText[0] == _T('\"'))
{
if((szStringEnd = _tcschr(szItemText + 1, _T('\"'))) > szItemText)
{
szStringEnd[0] = 0;
szItemText += 1;
}
}
// If the number is followed by a space, cut it
if(bKeepQuotedPart == false)
{
if((szTextPtr = _tcschr(szItemText, _T(' '))) != NULL)
{
szTextPtr[0] = 0;
}
}
}
// Return the text
return szItemText;
}
static bool TV_TextToEditText(const _TREE_ITEM_INFO * pItemInfo, LPTSTR szBuffer, size_t ccBuffer, LPCTSTR szItemText)
{
LPCTSTR szTextValue;
// Copy the entire text from the source to the target
StringCchCopy(szBuffer, ccBuffer, szItemText);
// Extract the text value from the item
szTextValue = GetItemTextValue(szBuffer, (pItemInfo->ItemType == ItemTypeCondition));
// Move the text value
if(szTextValue > szBuffer)
memmove(szBuffer, szTextValue, (_tcslen(szTextValue) + 1) * sizeof(TCHAR));
return true;
}
static void TV_MakeItemText(
PTREE_ITEM_INFO pItemInfo,
LPTSTR szBuffer,
size_t ccBuffer,
LPBYTE pbPtr,
LPBYTE pbEnd,
PULONG pcbMoveBy = NULL)
{
ULONG cbMoveBy = 0;
TCHAR szDataText[0x400] = {0};
UINT nIDFormat = pItemInfo->nIDFormat1;
bool bApplyIndex = false;
// Do we have data and format for it?
if(pItemInfo->nIDFormat2)
{
if(pItemInfo->ToString != NULL)
{
// Format the numeric or whatever value
if(NT_SUCCESS(pItemInfo->ToString(pItemInfo, szDataText, _countof(szDataText), pbPtr, pbEnd, &cbMoveBy)))
{
nIDFormat = pItemInfo->nIDFormat2;
bApplyIndex = true;
}
}
}
// Finally, format the data to the item
if(pcbMoveBy != NULL)
pcbMoveBy[0] = cbMoveBy;
// Format the final value
if(GLOBAL_ItemIndex != INVALID_ITEM_INDEX && bApplyIndex)
rsprintf(szBuffer, ccBuffer, nIDFormat, GLOBAL_ItemIndex, szDataText);
else
rsprintf(szBuffer, ccBuffer, nIDFormat, szDataText);
}
//-----------------------------------------------------------------------------
// Conversion of String <-> Integer Value (Hex)
template <typename INTEGER>
NTSTATUS ToString_Hex(PTREE_ITEM_INFO pItemInfo, LPTSTR szBuffer, size_t ccBuffer, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
ULONG64 dwIntValue = 0;
// Capture the integer value
if((pbPtr + sizeof(INTEGER)) > pbEnd)
return STATUS_BUFFER_OVERFLOW;
if(ccBuffer < (sizeof(INTEGER) * 2) + 1)
return STATUS_BUFFER_OVERFLOW;
dwIntValue = *(INTEGER *)(pbPtr);
// Write the "0x" prefix
StringCchCopyEx(szBuffer, ccBuffer, _T("0x"), &szBuffer, &ccBuffer, 0);
// Convert to string
Hex2TextXX(dwIntValue, szBuffer, sizeof(INTEGER));
// If the value has flags, we add the flags suffix
if(pItemInfo->pFlagInfos != NULL)
{
// Convert the flags to their text representations
TFlagString fs(pItemInfo->pFlagInfos, dwIntValue);
if(fs.GetConvertedFlagsCount())
{
StringCchCat(szBuffer, ccBuffer, _T(" "));
StringCchCat(szBuffer, ccBuffer, fs);
}
}
// Give the move by
if(pcbMoveBy != NULL)
pcbMoveBy[0] = sizeof(INTEGER);
return STATUS_SUCCESS;
}
template <typename INTEGER>
NTSTATUS StringTo_Hex(PTREE_ITEM_INFO /* pItemInfo */, LPCTSTR szString, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
LONGLONG dwIntValue;
if((pbPtr + sizeof(INTEGER)) <= pbEnd)
{
if(Text2Hex64(szString, &dwIntValue) == ERROR_SUCCESS)
{
// Copy the integer
*(INTEGER *)(pbPtr) = (INTEGER)(dwIntValue);
// Give the pcbMoveBy
if(pcbMoveBy != NULL)
pcbMoveBy[0] = sizeof(INTEGER);
return STATUS_SUCCESS;
}
}
return STATUS_BUFFER_OVERFLOW;
}
static NTSTATUS ToString_Hex1(PTREE_ITEM_INFO pItemInfo, LPTSTR szBuffer, size_t ccBuffer, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
return ToString_Hex<BYTE>(pItemInfo, szBuffer, ccBuffer, pbPtr, pbEnd, pcbMoveBy);
}
static NTSTATUS StringTo_Hex1(PTREE_ITEM_INFO pItemInfo, LPCTSTR szString, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
return StringTo_Hex<BYTE>(pItemInfo, szString, pbPtr, pbEnd, pcbMoveBy);
}
static NTSTATUS ToString_Hex2(PTREE_ITEM_INFO pItemInfo, LPTSTR szBuffer, size_t ccBuffer, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
return ToString_Hex<WORD>(pItemInfo, szBuffer, ccBuffer, pbPtr, pbEnd, pcbMoveBy);
}
static NTSTATUS StringTo_Hex2(PTREE_ITEM_INFO pItemInfo, LPCTSTR szString, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
return StringTo_Hex<WORD>(pItemInfo, szString, pbPtr, pbEnd, pcbMoveBy);
}
static NTSTATUS ToString_Hex4(PTREE_ITEM_INFO pItemInfo, LPTSTR szBuffer, size_t ccBuffer, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
return ToString_Hex<DWORD>(pItemInfo, szBuffer, ccBuffer, pbPtr, pbEnd, pcbMoveBy);
}
static NTSTATUS StringTo_Hex4(PTREE_ITEM_INFO pItemInfo, LPCTSTR szString, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
return StringTo_Hex<DWORD>(pItemInfo, szString, pbPtr, pbEnd, pcbMoveBy);
}
static NTSTATUS ToString_Hex8(PTREE_ITEM_INFO pItemInfo, LPTSTR szBuffer, size_t ccBuffer, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
return ToString_Hex<DWORD64>(pItemInfo, szBuffer, ccBuffer, pbPtr, pbEnd, pcbMoveBy);
}
static NTSTATUS StringTo_Hex8(PTREE_ITEM_INFO pItemInfo, LPCTSTR szString, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
return StringTo_Hex<DWORD64>(pItemInfo, szString, pbPtr, pbEnd, pcbMoveBy);
}
// For fields that are auto-calculated, such as ACE_HEADER::AceSize
static NTSTATUS StringTo_Auto(PTREE_ITEM_INFO pItemInfo, LPCTSTR /* szString */, LPBYTE /* pbPtr */, LPBYTE /* pbEnd */, PULONG pcbMoveBy = NULL)
{
if(pcbMoveBy != NULL)
pcbMoveBy[0] = (pItemInfo->ItemType == ItemTypeUint16) ? sizeof(USHORT) : sizeof(DWORD);
return (pAclInWork || pAceInWork) ? STATUS_SUCCESS : STATUS_AUTO_CALCULATED;
}
//-----------------------------------------------------------------------------
// Conversion of String <-> Trust Level
static bool ToString_TrustLevel(PTREE_ITEM_INFO /* pItemInfo */, LPTSTR szBuffer, size_t ccBuffer, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
LPDWORD PtrTrustLevels = (LPDWORD)(pbPtr);
LPTSTR szBuffEnd = szBuffer + ccBuffer - 1;
ULONG cbMoveBy = 0;
if((pbPtr + sizeof(DWORD64)) <= pbEnd)
{
// Write the 64-bit value
StringCchPrintfEx(szBuffer, (szBuffEnd - szBuffer), &szBuffer, NULL, 0, _T("%x-%x "), PtrTrustLevels[0], PtrTrustLevels[1]);
// Write the trust levels
szBuffer = FlagsToString(PsTrustTypes, szBuffer, (szBuffEnd - szBuffer), *(LPDWORD)(pbPtr + 0));
StringCchCatEx(szBuffer, (szBuffEnd - szBuffer), _T("/"), &szBuffer, NULL, 0);
szBuffer = FlagsToString(PsTrustLevels, szBuffer, (szBuffEnd - szBuffer), *(LPDWORD)(pbPtr + 4));
cbMoveBy = sizeof(DWORD64);
}
// Give the pcbMoveBy
if(pcbMoveBy != NULL)
pcbMoveBy[0] = cbMoveBy;
return (cbMoveBy != 0);
}
static NTSTATUS StringTo_TrustLevel(PTREE_ITEM_INFO /* pItemInfo */, LPCTSTR szString, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
LPTSTR szHiPart;
LPTSTR szLoPart;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
DWORD TrustLevel[2];
TCHAR szBuffer[0x40];
if((pbPtr + sizeof(DWORD64)) <= pbEnd)
{
StringCchCopy(szBuffer, _countof(szBuffer), szString);
szHiPart = szBuffer;
if((szLoPart = _tcschr(szHiPart, _T('-'))) != NULL)
{
*szLoPart++ = 0;
if(Text2Hex32(szHiPart, &TrustLevel[0]) == ERROR_SUCCESS &&
Text2Hex32(szLoPart, &TrustLevel[1]) == ERROR_SUCCESS)
{
if(pcbMoveBy != NULL)
pcbMoveBy[0] = sizeof(DWORD64);
memcpy(pbPtr, TrustLevel, sizeof(TrustLevel));
Status = STATUS_SUCCESS;
}
}
}
return Status;
}
//-----------------------------------------------------------------------------
// Conversion of String <-> Binary Data: BOOL
static NTSTATUS ToString_Bool(PTREE_ITEM_INFO /* pItemInfo */, LPTSTR szBuffer, size_t ccBuffer, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
if((pbPtr + sizeof(BYTE)) > pbEnd)
return STATUS_BUFFER_OVERFLOW;
if(pcbMoveBy != NULL)
pcbMoveBy[0] = sizeof(BYTE);
StringCchCopy(szBuffer, ccBuffer, pbPtr[0] ? _T("TRUE") : _T("FALSE"));
return STATUS_SUCCESS;
}
static NTSTATUS StringTo_Bool(PTREE_ITEM_INFO pItemInfo, LPCTSTR szString, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
NTSTATUS Status;
BYTE FalseValue = 0;
BYTE TrueValue = 1;
BYTE ItemValue[1];
// Check for TRUE / FALSE
if(!_tcsicmp(szString, _T("FALSE")) || !_tcsicmp(szString, _T("OFF")))
return CopyDataAway(pbPtr, pbEnd, &FalseValue, sizeof(FalseValue), pcbMoveBy);
if(!_tcsicmp(szString, _T("TRUE")) || !_tcsicmp(szString, _T("ON")))
return CopyDataAway(pbPtr, pbEnd, &TrueValue, sizeof(TrueValue), pcbMoveBy);
// Try numeric value. If nonzero, it's TRUE, if not, it's FALSE
if((Status = StringTo_Hex1(pItemInfo, szString, &ItemValue[0], &ItemValue[1])) == STATUS_SUCCESS)
{
if(ItemValue[0] == 0)
return CopyDataAway(pbPtr, pbEnd, &FalseValue, sizeof(FalseValue), pcbMoveBy);
else
return CopyDataAway(pbPtr, pbEnd, &TrueValue, sizeof(TrueValue), pcbMoveBy);
}
return Status;
}
//-----------------------------------------------------------------------------
// Conversion of LPWSTR to string
static NTSTATUS ToString_STR(PTREE_ITEM_INFO /* pItemInfo */, LPTSTR szBuffer, size_t ccBuffer, LPBYTE pbPtr, LPBYTE /* pbEnd */, PULONG pcbMoveBy = NULL)
{
LPWSTR szString = (LPWSTR)(pbPtr);
if(pcbMoveBy != NULL)
pcbMoveBy[0] = (ULONG)((wcslen(szString) + 1) * sizeof(WCHAR));
StringCchPrintf(szBuffer, ccBuffer, _T("\"%s\""), szString);
return STATUS_SUCCESS;
}
static NTSTATUS StringTo_STR(PTREE_ITEM_INFO /* pItemInfo */, LPCTSTR szString, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
ULONG cbString = (ULONG)((wcslen(szString) + 1) * sizeof(WCHAR));
return CopyDataAway(pbPtr, pbEnd, szString, cbString, pcbMoveBy);
}
//-----------------------------------------------------------------------------
// Conversion of String <-> Binary Data: SID
static TREE_ITEM_INFO ItemType_IntLevel = {ItemTypeUint32, 0, IDS_FORMAT_INT_LEVEL, IntgrLevels};
static TREE_ITEM_INFO ItemType_PolicyId = {ItemTypeUint32, 0, IDS_FORMAT_POLICY_ID};
static TREE_ITEM_INFO ItemType_TrustLev = {ItemTypeUint64, 0, IDS_FORMAT_TRUST_LEVEL};
static NTSTATUS ToString_Sid(PTREE_ITEM_INFO /* pItemInfo */, LPTSTR szBuffer, size_t ccBuffer, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG cbMoveBy = 0;
// If there is a SID, format it to the buffer
if(pbPtr != NULL)
{
if((pbPtr + 8) <= pbEnd)
{
PSID_IDENTIFIER_AUTHORITY pSia;
LPBYTE pbIntValue;
PSID pSid = (PSID)(pbPtr);
// Determine the SID type by the authority
pSia = GetSidIdentifierAuthority(pSid);
// Is it a mandatory label?
if(!memcmp(pSia, &SiaLabel, sizeof(SID_IDENTIFIER_AUTHORITY)))
{
if((pbIntValue = GetSidIntegrityLevel(pSid)) != NULL)
ToString_Hex4(&ItemType_IntLevel, szBuffer, ccBuffer, pbIntValue, pbIntValue + sizeof(DWORD));
cbMoveBy = RtlLengthSid(pSid);
}
else if(!memcmp(pSia, &SiaPolicy, sizeof(SID_IDENTIFIER_AUTHORITY)))
{
if((pbIntValue = GetSidScopedPolicyId(pSid)) != NULL)
ToString_Hex4(&ItemType_PolicyId, szBuffer, ccBuffer, pbIntValue, pbIntValue + sizeof(DWORD));
cbMoveBy = RtlLengthSid(pSid);
}
else if(!memcmp(pSia, &SiaTrust, sizeof(SID_IDENTIFIER_AUTHORITY)))
{
if((pbIntValue = GetSidProcessTrustLevel(pSid)) != NULL)
ToString_TrustLevel(&ItemType_TrustLev, szBuffer, ccBuffer, pbIntValue, pbIntValue + sizeof(ULONG64));
cbMoveBy = RtlLengthSid(pSid);
}
else
{
SidToString(pSid, szBuffer, ccBuffer, true);
cbMoveBy = RtlLengthSid(pSid);
}
}
else
{
Status = STATUS_INVALID_PARAMETER;
}
}
else
{
rsprintf(szBuffer, ccBuffer, IDS_NOT_PRESENT);
}
// Give the length to move
if(pcbMoveBy != NULL)
pcbMoveBy[0] = cbMoveBy;
return Status;
}
static NTSTATUS StringTo_Sid19(PTREE_ITEM_INFO /* pItemInfo */, LPCTSTR szText, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
NTSTATUS Status;
LPBYTE pbIntegerValue;
// Try to convert a trust level value
if((Status = CopyDataAway(pbPtr, pbEnd, SidSystemAce19, sizeof(SidSystemAce19), pcbMoveBy)) == STATUS_SUCCESS)
{
if((pbIntegerValue = GetSystemSidIntegerValue((PSID)(pbPtr), &SiaTrust, 2)) != NULL)
{
// Convert the trust level to binary value.
Status = StringTo_TrustLevel(&ItemType_TrustLev, szText, pbIntegerValue, pbIntegerValue + sizeof(DWORD64));
}
}
return Status;
}
static NTSTATUS StringTo_Sid(PTREE_ITEM_INFO pItemInfo, LPCTSTR szText, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
SID_NAME_USE SidNameUse;
NTSTATUS Status = STATUS_INVALID_PARAMETER;
LPBYTE pbIntegerValue;
TCHAR szDomainName[256];
DWORD ccDomainName = _countof(szDomainName);
DWORD cbSid = (ULONG)(pbEnd - pbPtr);
PSID pSid = NULL;
WORD AceFlags = 0;
// Mandatory SIDs have just integrity level
if(pItemInfo->ItemType == ItemTypeSid11)
{
// Copy the default SID to the buffer
if((Status = CopyDataAway(pbPtr, pbEnd, SidLabelMedium, sizeof(SidLabelMedium), pcbMoveBy)) == STATUS_SUCCESS)
{
if((pbIntegerValue = GetSystemSidIntegerValue((PSID)(pbPtr), &SiaLabel)) != NULL)
{
Status = StringTo_Hex4(&ItemType_IntLevel, szText, pbIntegerValue, pbIntegerValue + sizeof(ULONG));
}
}
return Status;
}
// Policy SIDs have just policy ID
if(pItemInfo->ItemType == ItemTypeSid17)
{
// Copy the default SID to the buffer
if((Status = CopyDataAway(pbPtr, pbEnd, SidSystemAce17, sizeof(SidSystemAce17), pcbMoveBy)) == STATUS_SUCCESS)
{
if((pbIntegerValue = GetSystemSidIntegerValue((PSID)(pbPtr), &SiaPolicy)) != NULL)
{
Status = StringTo_Hex4(&ItemType_PolicyId, szText, pbIntegerValue, pbIntegerValue + sizeof(ULONG));
}
}
return Status;
}
// Trust level SID: Either two 32-bit values or the "Everyone" SID
if(pItemInfo->ItemType == ItemTypeSid19)
{
// Try to convert a trust level SID
if((Status = StringTo_Sid19(pItemInfo, szText, pbPtr, pbEnd, pcbMoveBy)) != STATUS_SUCCESS)
{
TREE_ITEM_INFO TreeItem_NormalSid = {ItemTypeSid, IDS_NOT_PRESENT, IDS_FORMAT_SID, NULL, ToString_Sid, StringTo_Sid};
if((Status = StringTo_Sid(&TreeItem_NormalSid, szText, pbPtr, pbEnd, pcbMoveBy)) == STATUS_SUCCESS)
{
AceFlags = 0;
}
}
else
{
AceFlags = TRUST_PROTECTED_FILTER_ACE_FLAG;
}
// We also need to set the flags in the ACE header
if(NT_SUCCESS(Status) && (pAceInWork != NULL) && (pAceInWork->Header.AceType == SYSTEM_ACCESS_FILTER_ACE_TYPE))
pAceInWork->Header.AceFlags |= AceFlags;
return Status;
}
// SIDs entered in the raw format: "S-1-"
if(!_tcsnicmp(szText, _T("S-1-"), 4))
{
if(ConvertStringSidToSid(szText, &pSid))
{
if((cbSid = RtlLengthSid(pSid)) != 0)
{
if((ULONG)(pbEnd - pbPtr) >= cbSid)
{
Status = CopyDataAway(pbPtr, pbEnd, pSid, cbSid, pcbMoveBy);
LocalFree(pSid);
return Status;
}
}
}
}
// Convert the account name to SID
if(LookupAccountName(NULL, szText, (PSID)(pbPtr), &cbSid, szDomainName, &ccDomainName, &SidNameUse))
{
if(pcbMoveBy != NULL)
pcbMoveBy[0] = cbSid;
Status = STATUS_SUCCESS;
}
return Status;
}
static NTSTATUS CreateNew_Sid(PTREE_ITEM_INFO /* pItemInfo */, LPBYTE pbDataBuffer, size_t * pcbDataBuffer)
{
PSID pSid;
size_t cbDataBuffer = pcbDataBuffer[0];
NTSTATUS Status = STATUS_UNSUCCESSFUL;
// Create new SID
if((pSid = ACE_HELPER::GetDefaultSid()) != NULL)
{
ULONG cbSid = RtlLengthSid(pSid);
if(pcbDataBuffer != NULL)
pcbDataBuffer[0] = cbSid;
Status = CopyDataAway(pbDataBuffer, pbDataBuffer + cbDataBuffer, pSid, cbSid);
}
return Status;
}
//-----------------------------------------------------------------------------
// Conversion of String <-> Binary Data: GUID
// Get GUID from object-based ACEs, like ACCESS_ALLOWED_OBJECT_ACE
// * ACCESS_ALLOWED_OBJECT_ACE::ObjectType is only present if ACE_OBJECT_TYPE_PRESENT
// * ACCESS_ALLOWED_OBJECT_ACE::InheritedObjectType is only present if ACE_INHERITED_OBJECT_TYPE_PRESENT
static NTSTATUS ToString_Guid(PTREE_ITEM_INFO pItemInfo, LPTSTR szBuffer, size_t ccBuffer, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
NTSTATUS Status = STATUS_BUFFER_OVERFLOW;
ULONG cbMoveBy = 0;
// Only present if the appropriate GUID flag is present in the ACE
if(AceObjectGuidPresent(pAceInWork, pItemInfo->ItemType))
{
if((pbPtr + sizeof(GUID)) <= pbEnd)
{
GuidToString((LPGUID)(pbPtr), szBuffer, ccBuffer);
cbMoveBy = sizeof(GUID);
Status = STATUS_SUCCESS;
}
}
else
{
StringCchCopy(szBuffer, ccBuffer, szNoGuidString);
Status = STATUS_SUCCESS;
}
if(pcbMoveBy != NULL)
pcbMoveBy[0] = cbMoveBy;
return Status;
}
static NTSTATUS StringTo_Guid(PTREE_ITEM_INFO pItemInfo, LPCTSTR szString, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
NTSTATUS Status = STATUS_BUFFER_OVERFLOW;
DWORD dwFlagToModify = 0;
ULONG cbMoveBy = 0;
// Check for free space
if((pbPtr + sizeof(GUID)) <= pbEnd)
{
// Try to convert the GUID. Anything unconverted is considered as no GUID
if(StringToGuid(szString, (LPGUID)(pbPtr)))
cbMoveBy = sizeof(GUID);
Status = STATUS_SUCCESS;
// Modify the flags in the ACE
if(pAceInWork != NULL)
{
// Determine the modify flag
dwFlagToModify = (pItemInfo->ItemType == ItemTypeGuid) ? ACE_OBJECT_TYPE_PRESENT : ACE_INHERITED_OBJECT_TYPE_PRESENT;
pAceInWork->Flags = cbMoveBy ? (pAceInWork->Flags | dwFlagToModify) : (pAceInWork->Flags & ~dwFlagToModify);
}
}
// Give the pcbMoveBy
if(pcbMoveBy != NULL)
pcbMoveBy[0] = cbMoveBy;
return Status;
}
//-----------------------------------------------------------------------------
// Conversion of SID preceded by 32-bit integer, containing length
static NTSTATUS ToString_SidN(PTREE_ITEM_INFO pItemInfo, LPTSTR szBuffer, size_t ccBuffer, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
NTSTATUS Status = STATUS_BUFFER_OVERFLOW;
ULONG cbLength = 0;
// Enough data to cover 32-bit int?
if((pbPtr + sizeof(ULONG)) <= pbEnd)
{
// Copy the length
memcpy(&cbLength, pbPtr, sizeof(ULONG));
pbPtr += sizeof(ULONG);
// Give the result to the caller
if(pcbMoveBy != NULL)
pcbMoveBy[0] = sizeof(ULONG) + cbLength;
Status = ToString_Sid(pItemInfo, szBuffer, ccBuffer, pbPtr, pbPtr + cbLength);
}
return Status;
}
static NTSTATUS StringTo_SidN(PTREE_ITEM_INFO pItemInfo, LPCTSTR szString, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
NTSTATUS Status;
ULONG cbMoveBy = 0;
// Convert the string-SID to binary SID
Status = StringTo_Sid(pItemInfo, szString, pbPtr + sizeof(ULONG), pbEnd, &cbMoveBy);
if(NT_SUCCESS(Status))
{
// Supply the length of the SID
if(pcbMoveBy != NULL)
pcbMoveBy[0] = cbMoveBy + sizeof(ULONG);
*(PULONG)(pbPtr) = RtlLengthSid(pbPtr + sizeof(ULONG));
}
return Status;
}
//-----------------------------------------------------------------------------
// Conversion of OCTET_STRING preceded by 32-bit integer, containing length
static NTSTATUS ToString_BinN(PTREE_ITEM_INFO /* pItemInfo */, LPTSTR szBuffer, size_t ccBuffer, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
PACE_OCTET_STRING pOctetString = (PACE_OCTET_STRING)(pbPtr);
LPTSTR szBufferEnd;
// Can a length be there?
if((pbPtr + sizeof(ULONG)) <= pbEnd)
{
// Can the whole octet string be there?
if((pbPtr + sizeof(ULONG) + pOctetString->cbData) <= pbEnd)
{
// Set the string range
pbPtr = pOctetString->pbData;
pbEnd = pbPtr + pOctetString->cbData;
szBufferEnd = szBuffer + ccBuffer - 1;
// Format the binary data into a spaced string
while(pbPtr < pbEnd && (szBuffer + 3) < szBufferEnd)
{
szBuffer[0] = HexaAlphabetUpper[pbPtr[0] >> 0x04];
szBuffer[1] = HexaAlphabetUpper[pbPtr[0] & 0x0F];
szBuffer += 2;
pbPtr++;
}
szBuffer[0] = 0;
// Give the result to the caller
if(pcbMoveBy != NULL)
pcbMoveBy[0] = OctetStringSize(pOctetString);
return STATUS_SUCCESS;
}
}
return STATUS_BUFFER_OVERFLOW;
}
static NTSTATUS StringTo_BinN(PTREE_ITEM_INFO /* pItemInfo */, LPCTSTR szString, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
LPBYTE pbBin = pbPtr + sizeof(ULONG);
size_t cbBin = 0;
DWORD dwErrCode;
ULONG cbMoveBy;
// Convert the string-SID to binary SID
if((dwErrCode = StringToBinary(szString, pbBin, (pbEnd - pbBin), &cbBin)) != ERROR_SUCCESS)
return STATUS_BAD_DATA;
cbMoveBy = (ULONG)(cbBin);
// Supply the length of the octet string
if(pcbMoveBy != NULL)
pcbMoveBy[0] = cbMoveBy + sizeof(ULONG);
*(PULONG)(pbPtr) = cbMoveBy;
return STATUS_SUCCESS;
}
//-----------------------------------------------------------------------------
// Conversion of ACE type (string like "ACCESS_ALLOWED_ACE_TYPE")
static NTSTATUS ToString_Ace(PTREE_ITEM_INFO /* pItemInfo */, LPTSTR szBuffer, size_t ccBuffer, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
PACE_HEADER pAceHeader = (PACE_HEADER)(pbPtr);
ULONG cbMoveBy = 0;
if((pbPtr + sizeof(ACE_HEADER)) < pbEnd)
{
GetAceTypeString(szBuffer, ccBuffer, (PACE_HEADER)(pbPtr));
cbMoveBy = pAceHeader->AceSize;
}
else
{
LoadString(g_hInst, IDS_NOT_PRESENT, szBuffer, (int)(ccBuffer));
}
if(pcbMoveBy != NULL)
pcbMoveBy[0] = cbMoveBy;
return STATUS_SUCCESS;
}
static NTSTATUS StringTo_Ace(PTREE_ITEM_INFO /* pItemInfo */, LPCTSTR szString, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
LPTSTR szEndPtr = NULL;
size_t nLength = _tcslen(szString);
DWORD dwAceType;
char szAceType[256];
// Prepare ANSI verison of the ACE type
StringCchCopyX(szAceType, _countof(szAceType), szString);
// Parse all ACE types
for(size_t i = 0; AceHdrTypes[i].szFlagText != NULL; i++)
{
if(!_strnicmp(szAceType, AceHdrTypes[i].szFlagText, nLength))
{
if(AceHdrTypes[i].szFlagText[nLength] == 0 || AceHdrTypes[i].szFlagText[nLength] == '_')
{
BYTE AceType = (BYTE)(i);
return CopyDataAway(pbPtr, pbEnd, &AceType, sizeof(AceType), pcbMoveBy);
}
}
}
// An unknown ACE type: "UNKNOWN_ACE_TYPE: %02x"
dwAceType = _tcstoul(szString, &szEndPtr, 16);
if(dwAceType <= 0xFF && szEndPtr[0] == 0)
{
BYTE AceType = (BYTE)(dwAceType);
return CopyDataAway(pbPtr, pbEnd, &AceType, sizeof(AceType), pcbMoveBy);
}
return STATUS_INVALID_PARAMETER;
}
//-----------------------------------------------------------------------------
// Conversion of binary condition to string
static NTSTATUS ToString_Cond(PTREE_ITEM_INFO /* pItemInfo */, LPTSTR szBuffer, size_t ccBuffer, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
NTSTATUS Status = STATUS_BUFFER_OVERFLOW;
ULONG cbMoveBy = 0;
// If there is a SID, format it to the buffer
if(pbPtr && pbEnd > pbPtr)
{
LPTSTR szCondition = NULL;
DWORD dwErrCode;
dwErrCode = LocalGetStringForCondition(pbPtr, (DWORD)(pbEnd - pbPtr), &szCondition, NULL, NULL, NULL, NULL, false);
if(dwErrCode == ERROR_SUCCESS && szCondition != NULL)
{
StringCchCopy(szBuffer, ccBuffer, szCondition);
LocalFree(szCondition);
cbMoveBy = (ULONG)(pbEnd - pbPtr);
Status = STATUS_SUCCESS;
}
}
else
{
LoadString(g_hInst, IDS_EMPTY_STREAM, szBuffer, (int)(ccBuffer));
Status = STATUS_SUCCESS;
}
// Give the length to move
if(pcbMoveBy != NULL)
pcbMoveBy[0] = cbMoveBy;
return Status;
}
static NTSTATUS StringTo_Saved(PTREE_ITEM_INFO pItemInfo, LPCTSTR /* szText */, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
PNON_EDITABLE_DATA pData;
// Did we store the raw condition?
if((pData = IsItemDataPointer(pItemInfo->ItemData)) != NULL)
{
if((pbPtr + pData->Length) <= pbEnd)
{
return CopyDataAway(pbPtr, pbEnd, pData->Data, pData->Length, pcbMoveBy);
}
return STATUS_SUCCESS;
}
return STATUS_BAD_DATA;
}
static NTSTATUS CreateNew_Acl(PTREE_ITEM_INFO pItemInfo, LPBYTE pbDataBuffer, size_t * pcbDataBuffer)
{
LPBYTE pbEnd = pbDataBuffer + pcbDataBuffer[0];
LPBYTE pbPtr = pbDataBuffer;
// Pre-fill the entire buffer with zeros
memset(pbPtr, 0, (pbEnd - pbPtr));
// Create the ACL header
if((pbPtr + sizeof(ACL)) <= pbEnd)
{
PACE_HEADER pAceHeader;
DWORD dwAceType = GetDefaultAceTypeForAcl(pItemInfo);
PACL pAcl = (PACL)(pbPtr);
// Fill-in the ACL header
pAcl->AclRevision = ACL_REVISION_DS;
pAcl->AclSize = (WORD)(pcbDataBuffer[0]);
pbPtr += sizeof(ACL);
// Create an ACE and imprint it into the ACL
if((pAceHeader = ACE_HELPER(dwAceType).AddToAcl(pAcl)) != NULL)
{
pcbDataBuffer[0] = pAcl->AclSize = (WORD)(pbPtr + pAceHeader->AceSize - pbDataBuffer);
pAcl->AceCount++;
}
return STATUS_SUCCESS;
}
return STATUS_BUFFER_OVERFLOW;
}
static TREE_ITEM_INFO TreeItem_Owner = {ItemTypeOwner, IDS_OWNER_SECURITY_INFORMATION};
static TREE_ITEM_INFO TreeItem_Group = {ItemTypeGroup, IDS_GROUP_SECURITY_INFORMATION};
static TREE_ITEM_INFO TreeItem_Dacl = {ItemTypeDacl, IDS_DACL_SECURITY_INFORMATION};
static TREE_ITEM_INFO TreeItem_Sacl = {ItemTypeSacl, IDS_SACL_SECURITY_INFORMATION};
static TREE_ITEM_INFO TreeItem_NullAcl = {ItemTypeNullAcl, IDS_NULL_ACL, IDS_FORMAT_STR, NULL, NULL, NULL, CreateNew_Acl};
static TREE_ITEM_INFO TreeItem_NoAcl = {ItemTypeNoAcl, IDS_NOT_PRESENT, IDS_FORMAT_STR, NULL, NULL, NULL, CreateNew_Acl};
static TREE_ITEM_INFO TreeItem_UserSid = {ItemTypeSid, IDS_NOT_PRESENT, IDS_FORMAT_SID, NULL, ToString_Sid, StringTo_Sid, CreateNew_Sid};
static TREE_ITEM_INFO TreeItem_AclRev = {ItemTypeUint08, 0, IDS_FORMAT_ACL_REVIS, AclRevFlags, ToString_Hex1, StringTo_Hex1};
static TREE_ITEM_INFO TreeItem_AclSbz1 = {ItemTypeUint08, 0, IDS_FORMAT_ACL_SBZ1, NULL, ToString_Hex1, StringTo_Hex1};
static TREE_ITEM_INFO TreeItem_AclSize = {ItemTypeUint16, 0, IDS_FORMAT_ACL_SIZE, NULL, ToString_Hex2, StringTo_Auto};
static TREE_ITEM_INFO TreeItem_AceCnt = {ItemTypeUint16, 0, IDS_FORMAT_ACL_COUNT, NULL, ToString_Hex2, StringTo_Auto};
static TREE_ITEM_INFO TreeItem_AclSbz2 = {ItemTypeUint16, 0, IDS_FORMAT_ACL_SBZ2, NULL, ToString_Hex2, StringTo_Hex2};
static TREE_ITEM_INFO TreeItem_Ace = {ItemTypeAce, IDS_NULL_ACL, IDS_FORMAT_STR, NULL, ToString_Ace, StringTo_Ace};
static TREE_ITEM_INFO TreeItem_CSA_Name = {ItemTypeLPWSTR, 0, IDS_FORMAT_NAME, NULL, ToString_STR, StringTo_STR};
static TREE_ITEM_INFO TreeItem_CSA_VTyp = {ItemTypeCSA_VType, 0, IDS_FORMAT_VALTYPE, CSA_ValTypes,ToString_Hex2, StringTo_Hex2};
static TREE_ITEM_INFO TreeItem_CSA_Res = {ItemTypeUint16, 0, IDS_FORMAT_RESERVED, NULL, ToString_Hex2, StringTo_Hex2};
static TREE_ITEM_INFO TreeItem_CSA_Flgs = {ItemTypeUint32, 0, IDS_FORMAT_FLAGS, CSA_Flags, ToString_Hex4, StringTo_Hex4};
static TREE_ITEM_INFO TreeItem_CSA_VCnt = {ItemTypeCSA_VCnt, 0, IDS_FORMAT_VALCOUNT, NULL, ToString_Hex4, StringTo_Hex4};
static TREE_ITEM_INFO TreeItem_CSA_U64 = {ItemTypeUint64, IDS_FORMAT_VALUE, IDS_FORMAT_VALINDEX, NULL, ToString_Hex8, StringTo_Hex8};
static TREE_ITEM_INFO TreeItem_CSA_STR = {ItemTypeLPWSTR, IDS_FORMAT_VALUE, IDS_FORMAT_VALINDEX, NULL, ToString_STR, StringTo_STR};
static TREE_ITEM_INFO TreeItem_CSA_SidN = {ItemTypeSidN, IDS_FORMAT_VALUE, IDS_FORMAT_VALINDEX, NULL, ToString_SidN, StringTo_SidN};
static TREE_ITEM_INFO TreeItem_CSA_BOOL = {ItemTypeBool, IDS_FORMAT_VALUE, IDS_FORMAT_VALINDEX, NULL, ToString_Bool, StringTo_Bool};
static TREE_ITEM_INFO TreeItem_CSA_BinN = {ItemTypeBinN, IDS_FORMAT_VALUE, IDS_FORMAT_VALINDEX, NULL, ToString_BinN, StringTo_BinN};
static PCTREE_ITEM_INFO AclFieldInfos[] =
{
&TreeItem_AclRev,
&TreeItem_AclSbz1,
&TreeItem_AclSize,
&TreeItem_AceCnt,
&TreeItem_AclSbz2,
};
static ACE_FIELD_INFO AceFieldInfos[] =
{
{ACE_FIELD_HTYPE, {ItemTypeAceType, 0, IDS_FORMAT_ACE_HTYPE, AceHdrTypes, ToString_Hex1, StringTo_Hex1}},
{ACE_FIELD_HFLAGS, {ItemTypeUint08, 0, IDS_FORMAT_ACE_HFLAGS, AceHdrFlags, ToString_Hex1, StringTo_Hex1}},
{ACE_FIELD_HFLAGS2, {ItemTypeUint08, 0, IDS_FORMAT_ACE_HFLAGS, AceHdrFlags2,ToString_Hex1, StringTo_Hex1}},
{ACE_FIELD_HSIZE, {ItemTypeUint16, 0, IDS_FORMAT_ACE_HSIZE, NULL, ToString_Hex2, StringTo_Auto}},
{ACE_FIELD_ACCESS_MASK, {ItemTypeUint32, 0, IDS_FORMAT_ACE_MASK, AceMasks, ToString_Hex4, StringTo_Hex4}},
{ACE_FIELD_ADS_ACCESS_MASK, {ItemTypeUint32, 0, IDS_FORMAT_ACE_MASK, AdsAceMasks, ToString_Hex4, StringTo_Hex4}},
{ACE_FIELD_MANDATORY_MASK, {ItemTypeUint32, 0, IDS_FORMAT_ACE_MASK, LabelMasks, ToString_Hex4, StringTo_Hex4}},
{ACE_FIELD_GUID_FLAGS, {ItemTypeUint32, 0, IDS_FORMAT_ACE_FLAGS, ObjAceFlags, ToString_Hex4, StringTo_Auto}},
{ACE_FIELD_COMPOUND_TYPE, {ItemTypeUint16, 0, IDS_FORMAT_ACE_CTYPE, CAceTypes, ToString_Hex2, StringTo_Hex2}},
{ACE_FIELD_COMPOUND_RSVD, {ItemTypeUint16, 0, IDS_FORMAT_RESERVED, NULL, ToString_Hex2, StringTo_Hex2}},
{ACE_FIELD_OBJECT_TYPE1, {ItemTypeGuid, 0, IDS_FORMAT_OBJ_TYPE, NULL, ToString_Guid, StringTo_Guid}},
{ACE_FIELD_OBJECT_TYPE2, {ItemTypeGuid2, 0, IDS_FORMAT_OBJ_TYPEI, NULL, ToString_Guid, StringTo_Guid}},
{ACE_FIELD_SID, {ItemTypeSid, 0, IDS_FORMAT_SID, NULL, ToString_Sid, StringTo_Sid}},
{ACE_FIELD_CLIENT_SID, {ItemTypeSid, 0, IDS_FORMAT_CSID, NULL, ToString_Sid, StringTo_Sid}},
{ACE_FIELD_MANDATORY_SID, {ItemTypeSid11, 0, IDS_FORMAT_INT_LEVEL, IntgrLevels, ToString_Sid, StringTo_Sid}},
{ACE_FIELD_POLICY_SID, {ItemTypeSid17, 0, IDS_FORMAT_POLICY_ID, NULL, ToString_Sid, StringTo_Sid}},
{ACE_FIELD_TRUST_SID, {ItemTypeSid19, 0, IDS_FORMAT_TRUST_LEVEL,NULL, ToString_Sid, StringTo_Sid}},
{ACE_FIELD_CSA_V1, {ItemTypeCSA_V1, IDS_FORMAT_CSA_V1}},
{ACE_FIELD_CONDITION, {ItemTypeCondition, 0, IDS_FORMAT_CONDITION, NULL, ToString_Cond, StringTo_Saved}}
};
//-----------------------------------------------------------------------------
// Inserting items
static PTREE_ITEM_INFO TV_GetItemParam(HWND hWndTree, HTREEITEM hItem)
{
return (PTREE_ITEM_INFO)TreeView_GetItemParam(hWndTree, hItem);
}
static PTREE_ITEM_INFO TV_GetItemParamAndText(HWND hWndTree, HTREEITEM hItem, LPTSTR szBuffer, int ccBuffer)
{
TVITEM tvi = {TVIF_TEXT | TVIF_PARAM};
// Get the item text and item param
tvi.hItem = hItem;
tvi.pszText = szBuffer;
tvi.cchTextMax = ccBuffer;
TreeView_GetItem(hWndTree, &tvi);
// Return the item param
return (PTREE_ITEM_INFO)(tvi.lParam);
}
static bool TV_IsSecurityAttributes(HWND hWndTree, HTREEITEM hItem)
{
PTREE_ITEM_INFO pItemInfo = TV_GetItemParam(hWndTree, hItem);
return ((pItemInfo = TV_GetItemParam(hWndTree, hItem)) != NULL && pItemInfo->ItemType == ItemTypeCSA_V1);
}
static DWORD TV_GetRemainingItemCount(HWND hWndTree, HTREEITEM hItem)
{
DWORD ValueCount = 0;
while(hItem != NULL)
{
ValueCount++;
hItem = TreeView_GetNextSibling(hWndTree, hItem);
}
return ValueCount;
}
static bool TV_SaveSelection(HWND hWndTree, TV_SELECTION & tvs)
{
HTREEITEM ItemChain[MAX_NEST_LEVEL] = {0};
HTREEITEM hSelected = TreeView_GetSelection(hWndTree);
HTREEITEM hParent = TVI_ROOT;
HTREEITEM hItem = hSelected;
size_t nIndex = 0;
// Reset the selection structure
memset(&tvs, 0, sizeof(TV_SELECTION));
tvs.nNestLevel = 0;
// Get the chain up to the root item
while(hItem != NULL)
{
if(tvs.nNestLevel >= _countof(ItemChain))
return false;
ItemChain[tvs.nNestLevel++] = hItem;
hItem = TreeView_GetParent(hWndTree, hItem);
}
// Reverse the array
ReverseArrayItems(ItemChain, tvs.nNestLevel);
// Search the multi-level tree view
for(size_t nLevel = 0; nLevel < tvs.nNestLevel; nLevel++)
{
// Retriueve the first item of that level
if((hItem = TreeView_GetChild(hWndTree, hParent)) == NULL)
return false;
nIndex = 0;
// Enumerate all next items
while(hItem != ItemChain[nLevel])
{
if((hItem = TreeView_GetNextSibling(hWndTree, hItem)) == NULL)
return false;
nIndex++;
}
// Remember the item count of that level
tvs.ItemIndex[nLevel] = nIndex;
hParent = hItem;
}
return true;
}
static bool TV_RestoreSelection(HWND hWndTree, TV_SELECTION & tvs, HTREEITEM hDefItem)
{
HTREEITEM hParent = TVI_ROOT;
HTREEITEM hItem = NULL;
for(size_t nLevel = 0; nLevel < tvs.nNestLevel; nLevel++)
{
hItem = TreeView_GetChild(hWndTree, hParent);
for(size_t nIndex = 0; nIndex < tvs.ItemIndex[nLevel]; )
{
if((hItem = TreeView_GetNextSibling(hWndTree, hItem)) == NULL)
return false;
nIndex++;
}
hParent = hItem;
}
// Return the found item
TreeView_SelectItem(hWndTree, (hItem != NULL) ? hItem : hDefItem);
return true;
}
static size_t TV_GetAceResourceValueIndex(LPNMTVDISPINFO pTVDispInfo)
{
PTREE_ITEM_INFO pItemInfo;
HTREEITEM hParent;
HTREEITEM hFocus = pTVDispInfo->item.hItem;
HTREEITEM hItem;
size_t nIndex = 0;
HWND hWndTree = pTVDispInfo->hdr.hwndFrom;
// Retrieve the parent item and the first child
if((hParent = TreeView_GetParent(hWndTree, hFocus)) != NULL)
{
if((pItemInfo = TV_GetItemParam(hWndTree, hParent)) != NULL)
{
if(pItemInfo->ItemType == ItemTypeCSA_V1)
{
// Retrieve the first child item and iterate over item
hItem = TreeView_GetChild(hWndTree, hParent);
while(hItem != NULL)
{
// If we found the n-th sub item, return its index
if(hItem == hFocus)
{
return (nIndex >= 5) ? (nIndex - 5) : INVALID_ITEM_INDEX;
}
// Move to the next sub item
hItem = TreeView_GetNextSibling(hWndTree, hItem);
nIndex++;
}
}
}
}
return INVALID_ITEM_INDEX;
}
static void TV_AllocateItemData(HWND hWndTree, HTREEITEM hItem, LPBYTE pbPtr, LPBYTE pbEnd)
{
PNON_EDITABLE_DATA pData;
PTREE_ITEM_INFO pItemInfo;
size_t Length = (pbEnd - pbPtr) + sizeof(NON_EDITABLE_DATA);
if((pItemInfo = TV_GetItemParam(hWndTree, hItem)) != NULL)
{
// There must be no data yet
assert(pItemInfo->ItemData == ItemDataNULL || pItemInfo->ItemData == ItemDataValid);
// Are there data of non-zero length?
if(pbEnd > pbPtr)
{
if((pData = (PNON_EDITABLE_DATA)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, Length)) != NULL)
{
pData->Magic = ITEM_DATA_MAGIC;
pData->Length = (ULONG)(pbEnd - pbPtr);
memcpy(pData->Data, pbPtr, pbEnd - pbPtr);
}
pItemInfo->ItemData = pData;
}
}
}
static HTREEITEM TV_GetAceSibling(HWND hWndTree, HTREEITEM hItem, BOOL bNextItem)
{
PTREE_ITEM_INFO pItemInfo;
HTREEITEM hSibling;
WPARAM wParam = (bNextItem == FALSE) ? TVGN_PREVIOUS : TVGN_NEXT;
// Get the previous sibling item
hSibling = (HTREEITEM)SendMessage(hWndTree, TVM_GETNEXTITEM, wParam, (LPARAM)(hItem));
if(hSibling != NULL)
{
if((pItemInfo = TV_GetItemParam(hWndTree, hSibling)) != NULL)
{
if(pItemInfo->ItemType == ItemTypeAce)
{
return hSibling;
}
}
}
return NULL;
}
static HTREEITEM TV_InsertNewItem(
HWND hWndTree,
HTREEITEM hParent,
HTREEITEM hInsertAfter,
PCTREE_ITEM_INFO pItemInfo,
LPBYTE pbPtr = NULL,
LPBYTE pbEnd = NULL,
PULONG pcbMoveBy = NULL)
{
PTREE_ITEM_INFO pNewInfo;
TVINSERTSTRUCT tvis;
HTREEITEM hItem = NULL;
TCHAR szItemText[MAX_ITEM_TEXT];
ULONG cbMoveBy = 0;
// Create copy of the item info structure
if((pNewInfo = (PTREE_ITEM_INFO)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, sizeof(TREE_ITEM_INFO))) != NULL)
{
// Copy the item data
memcpy(pNewInfo, pItemInfo, sizeof(TREE_ITEM_INFO));
pNewInfo->ItemData = (pbPtr && pbEnd > pbPtr) ? ItemDataValid : ItemDataNULL;
// Prepare the item text
TV_MakeItemText(pNewInfo, szItemText, _countof(szItemText), pbPtr, pbEnd, &cbMoveBy);
pNewInfo->ItemData = (pbPtr && pbEnd > pbPtr && cbMoveBy) ? ItemDataValid : ItemDataNULL;
// Insert the item to the tree
tvis.hParent = (hParent != NULL) ? hParent : TVI_ROOT;
tvis.hInsertAfter = (hInsertAfter != NULL) ? hInsertAfter : TVI_LAST;
tvis.item.mask = TVIF_TEXT | TVIF_PARAM;
tvis.item.pszText = szItemText;
tvis.item.lParam = (LPARAM)(pNewInfo);
hItem = TreeView_InsertItem(hWndTree, &tvis);
// Expand the item
if(hParent != TVI_ROOT && hItem != NULL)
{
TreeView_Expand(hWndTree, hParent, TVE_EXPAND);
TreeView_SelectItem(hWndTree, hItem);
}
}
// Give the number of bytes eaten
if(pcbMoveBy != NULL)
pcbMoveBy[0] = cbMoveBy;
return hItem;
}
static HTREEITEM TV_InsertIndexedItem(
HWND hWndTree,
HTREEITEM hParent,
HTREEITEM hInsertAfter,
PTREE_ITEM_INFO pItemInfo,
LPVOID lpElement,
size_t cbElement,
size_t nIndex = INVALID_ITEM_INDEX)
{
HTREEITEM hItem;
LPBYTE pbPtr = (LPBYTE)(lpElement);
LPBYTE pbEnd = pbPtr + cbElement;
size_t SaveIndex = GLOBAL_ItemIndex;
if(nIndex != INVALID_ITEM_INDEX)
GLOBAL_ItemIndex = nIndex;
hItem = TV_InsertNewItem(hWndTree, hParent, hInsertAfter, pItemInfo, pbPtr, pbEnd);
GLOBAL_ItemIndex = SaveIndex;
return hItem;
}
static HTREEITEM TV_InsertIndexedItem(
HWND hWndTree,
HTREEITEM hParent,
HTREEITEM hInsertAfter,
PTREE_ITEM_INFO pItemInfo,
ACE_CSA_OBJECT * pObject,
size_t nIndex = INVALID_ITEM_INDEX)
{
LPBYTE pbEnd;
LPBYTE pbPtr;
BYTE ObjectBuffer[MAX_ACL_LENGTH];
// Export the item to the plain text
pbEnd = ObjectBuffer + sizeof(ObjectBuffer);
if((pbPtr = pObject->Export(ObjectBuffer, pbEnd)) == NULL)
{
assert(false);
return NULL;
}
// Create the item
return TV_InsertIndexedItem(hWndTree, hParent, hInsertAfter, pItemInfo, ObjectBuffer, (pbPtr - ObjectBuffer), nIndex);
}
static HTREEITEM TV_InsertNewItemSid(
HWND hWndTree,
HTREEITEM hParent,
HTREEITEM hInsertAfter,
PTREE_ITEM_INFO pItemInfo,
PSID pSid)
{
LPBYTE pbSid = (LPBYTE)(pSid);
LPBYTE pbEnd = (LPBYTE)(pSid);
// Remove all children, if any
TreeView_DeleteChildren(hWndTree, hParent);
// Insert the SID item
if(pSid != NULL)
pbEnd += RtlLengthSid(pSid);
return TV_InsertNewItem(hWndTree, hParent, hInsertAfter, pItemInfo, pbSid, pbEnd);
}
static HTREEITEM TV_InsertNewItemCSA_V1(
HWND hWndTree,
HTREEITEM hParent,
LPBYTE pbPtr,
LPBYTE pbEnd,
PULONG pcbMoveBy)
{
HTREEITEM hInsertAfter = TVI_FIRST;
ACE_CSA_HELPER CsaHelper;
size_t cbAttrRel = (pbEnd - pbPtr);
size_t SaveIndex = GLOBAL_ItemIndex;
// Verify the structure
if(cbAttrRel < sizeof(PCLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1))
return NULL;
// Convert the security attribute to absolute security attribute
if(CsaHelper.Import(pbPtr, pbEnd, pcbMoveBy) == ERROR_SUCCESS)
{
// Insert members of CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1
TV_InsertIndexedItem(hWndTree, hParent, TVI_LAST, &TreeItem_CSA_Name, &CsaHelper.Name);
TV_InsertIndexedItem(hWndTree, hParent, TVI_LAST, &TreeItem_CSA_VTyp, &CsaHelper.ValueType, sizeof(CsaHelper.ValueType));
TV_InsertIndexedItem(hWndTree, hParent, TVI_LAST, &TreeItem_CSA_Res, &CsaHelper.Reserved, sizeof(CsaHelper.Reserved));
TV_InsertIndexedItem(hWndTree, hParent, TVI_LAST, &TreeItem_CSA_Flgs, &CsaHelper.Flags, sizeof(CsaHelper.Flags));
TV_InsertIndexedItem(hWndTree, hParent, TVI_LAST, &TreeItem_CSA_VCnt, &CsaHelper.ValueCount, sizeof(CsaHelper.ValueCount));
// Parse all values
if(hInsertAfter != NULL)
{
// Insert all values
for(ULONG i = 0; i < CsaHelper.ValueCount && hInsertAfter != NULL; i++)
{
switch(CsaHelper.ValueType)
{
case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
hInsertAfter = TV_InsertIndexedItem(hWndTree, hParent, TVI_LAST, &TreeItem_CSA_U64, &CsaHelper.ppObjects[i], i);
break;
case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
hInsertAfter = TV_InsertIndexedItem(hWndTree, hParent, TVI_LAST, &TreeItem_CSA_STR, &CsaHelper.ppObjects[i], i);
break;
case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
hInsertAfter = TV_InsertIndexedItem(hWndTree, hParent, TVI_LAST, &TreeItem_CSA_SidN, &CsaHelper.ppObjects[i], i);
break;
case CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN:
hInsertAfter = TV_InsertIndexedItem(hWndTree, hParent, TVI_LAST, &TreeItem_CSA_BOOL, &CsaHelper.ppObjects[i], i);
break;
case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
hInsertAfter = TV_InsertIndexedItem(hWndTree, hParent, TVI_LAST, &TreeItem_CSA_BinN, &CsaHelper.ppObjects[i], i);
break;
default:
hInsertAfter = NULL;
assert(false);
break;
}
}
}
}
GLOBAL_ItemIndex = SaveIndex;
return hInsertAfter;
}
static void TV_InsertNewItemAceFields(
HWND hWndTree,
HTREEITEM hParent,
const ACE_HELPER & AceHelper,
LPBYTE pbPtr,
LPBYTE pbEnd)
{
HTREEITEM hInsertAfter = TVI_FIRST;
// Delete any existing children
TreeView_DeleteChildren(hWndTree, hParent);
// Special: Save the value of ACE_HEADER::Flags
if(pAclInWork != NULL && pAceInWork != NULL)
pAceInWork->Header.AceFlags = AceHelper.AceFlags;
// Insert all ACE members according to the bit mask in the ACE helper
for(size_t i = 0; i < _countof(AceFieldInfos); i++)
{
ULONG cbMoveBy = 0;
// Is that flag present there?
if(AceHelper.AceLayout & AceFieldInfos[i].AceLayoutFlag)
{
// Insert the ACE field
hInsertAfter = TV_InsertNewItem(hWndTree,
hParent,
hInsertAfter,
&AceFieldInfos[i].TreeItem,
pbPtr, pbEnd, &cbMoveBy);
if(hInsertAfter == NULL)
break;
pbPtr += cbMoveBy;
// Insert the ACE field sub structure
switch(AceFieldInfos[i].TreeItem.ItemType)
{
case ItemTypeCSA_V1:
TV_InsertNewItemCSA_V1(hWndTree, hInsertAfter, AceHelper.AttrRel, AceHelper.AttrRel + AceHelper.AttrRelLength, &cbMoveBy);
pbPtr += cbMoveBy;
break;
}
}
// Special: The CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 structure
if((AceHelper.AceLayout & ACE_FIELD_CSA_V1) && (AceFieldInfos[i].AceLayoutFlag == ACE_FIELD_CSA_V1))
{
TV_AllocateItemData(hWndTree, hInsertAfter, pbPtr - cbMoveBy, pbEnd);
}
// Special: Allocate the item data for condition
if((AceHelper.AceLayout & ACE_FIELD_CONDITION) && (AceFieldInfos[i].AceLayoutFlag == ACE_FIELD_CONDITION))
{
TV_AllocateItemData(hWndTree, hInsertAfter, pbPtr - cbMoveBy, pbEnd);
}
}
}
static HTREEITEM TV_InsertNewItemAce(
HWND hWndTree,
HTREEITEM hParent,
HTREEITEM hInsertAfter,
PACE_HEADER pAceHeader)
{
HTREEITEM hAceItem;
ACE_HELPER AceHelper;
LPBYTE pbPtr = (LPBYTE)(pAceHeader);
LPBYTE pbEnd = pbPtr + pAceHeader->AceSize;
// Disable redrawing
EnableRedraw(hWndTree, FALSE);
// Insert the main ACE item
hAceItem = TV_InsertNewItem(hWndTree, hParent, hInsertAfter, &TreeItem_Ace, pbPtr, pbEnd);
if(hAceItem != NULL)
{
// Set the ACE to the ACE helper, we can parse the ACE fields easier
AceHelper.SetAce(pAceHeader);
// For ACE-forming flags like GUID flags, we need to set ACE-in-work
pAceInWork = (PACE)(pAceHeader);
// Insert the ACE fields
TV_InsertNewItemAceFields(hWndTree, hAceItem, AceHelper, pbPtr, pbEnd);
// Invalidate pointer to the GUID flags
pAceInWork = NULL;
}
// Enable redrawing
EnableRedraw(hWndTree);
return hAceItem;
}
static HTREEITEM TV_InsertNewItemAclFields(
HWND hWndTree,
HTREEITEM hParent,
HTREEITEM hInsertAfter,
PACL pAcl)
{
LPBYTE pbAclPtr = (LPBYTE)(pAcl);
LPBYTE pbAclEnd = pbAclPtr + pAcl->AclSize;
ULONG AceCount = pAcl->AceCount;
// Insert fields from the ACE header
for(size_t i = 0; i < _countof(AclFieldInfos); i++)
{
ULONG cbMoveBy = 0;
hInsertAfter = TV_InsertNewItem(hWndTree,
hParent,
hInsertAfter,
AclFieldInfos[i],
pbAclPtr,
pbAclEnd,
&cbMoveBy);
if(hInsertAfter == NULL)
break;
pbAclPtr += cbMoveBy;
}
// Parse the ACEs
while(AceCount > 0 && (pbAclPtr + sizeof(ACE_HEADER)) < pbAclEnd)
{
PACE_HEADER pAceHeader = (PACE_HEADER)(pbAclPtr);
LPBYTE pbAceEnd = (LPBYTE)(pAceHeader) + pAceHeader->AceSize;
// The ACE should not go past the end of ACL
if(pbAceEnd > pbAclEnd)
break;
// Insert the ACE to the list
if((hInsertAfter = TV_InsertNewItemAce(hWndTree, hParent, hInsertAfter, pAceHeader)) == NULL)
break;
// Move the data pointer by the size of the ACE
pbAclPtr += pAceHeader->AceSize;
AceCount--;
}
return hInsertAfter;
}
static void TV_InsertNewItemAcl(
HWND hWndTree,
HTREEITEM hParent,
HTREEITEM hInsertAfter,
PCTREE_ITEM_INFO pItemInfo,
PACL pAcl,
BOOLEAN pAclPresent)
{
HTREEITEM hAclItem;
// Insert the main item
if((hAclItem = TV_InsertNewItem(hWndTree, hParent, hInsertAfter, pItemInfo)) != NULL)
{
// (pAcl == NULL) can mean either NULL ACL or ACL is not present
// https://learn.microsoft.com/en-us/windows/win32/secauthz/null-dacls-and-empty-dacls
if(pAcl == NULL)
{
if(pAclPresent)
TV_InsertNewItem(hWndTree, hAclItem, hInsertAfter, &TreeItem_NullAcl);
else
TV_InsertNewItem(hWndTree, hAclItem, hInsertAfter, &TreeItem_NoAcl);
}
// If the ACL is valid, insert ACEs. Note that there may be no ACEs present,
// forming an empty ACL that denies everything to to everyone
else if (pAcl->AclSize != 0)
{
// Insert the ACL fields
hInsertAfter = TV_InsertNewItemAclFields(hWndTree,
hAclItem,
NULL,
pAcl);
}
}
}
static NTSTATUS TV_ItemToData(HWND hWndTree, HTREEITEM hItem, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
PTREE_ITEM_INFO pItemInfo;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
TCHAR szItemText[MAX_ITEM_TEXT];
// Retrieve the item info
if((pItemInfo = TV_GetItemParamAndText(hWndTree, hItem, szItemText, _countof(szItemText))) != NULL)
{
// If there a "string-to-data" function?
if(pItemInfo->StringTo != NULL)
{
Status = pItemInfo->StringTo(pItemInfo, GetItemTextValue(szItemText), pbPtr, pbEnd, pcbMoveBy);
}
}
return Status;
}
static NTSTATUS TV_ItemToData(HWND hWndTree, HTREEITEM hItem, LPVOID lpBuffer, size_t cbBuffer, PULONG pcbMoveBy, HTREEITEM * phNextItem)
{
LPBYTE pbBuffer = (LPBYTE)(lpBuffer);
// Check the item handle
if(hWndTree == NULL || hItem == NULL)
return STATUS_UNSUCCESSFUL;
// Give the next sibling, if required
if(phNextItem != NULL)
phNextItem[0] = TreeView_GetNextSibling(hWndTree, hItem);
return TV_ItemToData(hWndTree, hItem, pbBuffer, pbBuffer + cbBuffer, pcbMoveBy);
}
static NTSTATUS TV_ItemsToCSA_v1(HWND hWndTree, HTREEITEM hParent, ACE_CSA_HELPER & CsaHelper)
{
HTREEITEM hItem;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
TCHAR szName[256];
DWORD ValueCount = 0;
DWORD Flags = 0;
DWORD Index = 0;
WORD ValueType = 0;
WORD Reserved = 0;
// Retrieve the first child
if((hItem = TreeView_GetChild(hWndTree, hParent)) != NULL)
{
// Get the name of the attribute
if((Status = TV_ItemToData(hWndTree, hItem, szName, sizeof(szName), NULL, &hItem)) != STATUS_SUCCESS)
return Status;
CsaHelper.SetValueName(szName);
// Get the value type
if((Status = TV_ItemToData(hWndTree, hItem, &ValueType, sizeof(ValueType), NULL, &hItem)) != STATUS_SUCCESS)
return Status;
// Get the "Reserved" value
if((Status = TV_ItemToData(hWndTree, hItem, &Reserved, sizeof(Reserved), NULL, &hItem)) != STATUS_SUCCESS)
return Status;
// Get the "Flags" value
if((Status = TV_ItemToData(hWndTree, hItem, &Flags, sizeof(Flags), NULL, &hItem)) != STATUS_SUCCESS)
return Status;
// Get the supposed value count
if((Status = TV_ItemToData(hWndTree, hItem, &ValueCount, sizeof(ValueCount), NULL, &hItem)) != STATUS_SUCCESS)
return Status;
// Now get the *real* value count
if((ValueCount = TV_GetRemainingItemCount(hWndTree, hItem)) == 0)
return STATUS_UNSUCCESSFUL;
// Change the type of the values
if(CsaHelper.SetValueType(ValueType, ValueCount) == ERROR_SUCCESS)
{
// Store the missing members
CsaHelper.Reserved = Reserved;
CsaHelper.Flags = Flags;
// Load the values
while(hItem != NULL)
{
ULONG cbDataLength = 0;
BYTE ValueData[512] = {0};
if((Status = TV_ItemToData(hWndTree, hItem, ValueData, sizeof(ValueData), &cbDataLength, &hItem)) != STATUS_SUCCESS)
return Status;
if((Status = CsaHelper.SetValueData(ValueData, Index++)) != STATUS_SUCCESS)
return Status;
}
}
}
return Status;
}
static NTSTATUS TV_ItemsToCSA_v1(HWND hWndTree, HTREEITEM hParent, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
PCLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 pAttrRel;
ACE_CSA_HELPER CsaHelper;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
ULONG cbAttrRel = 0;
// Create the CSA_HELPER from the tree view items
if(TV_ItemsToCSA_v1(hWndTree, hParent, CsaHelper) == ERROR_SUCCESS)
{
// Export the attribute structures from the tree item
if((pAttrRel = CsaHelper.Export(&cbAttrRel)) != NULL)
{
Status = CopyDataAway(pbPtr, pbEnd, pAttrRel, cbAttrRel, pcbMoveBy);
LocalFree(pAttrRel);
}
}
return Status;
}
static NTSTATUS TV_ItemsToData(HWND hWndTree, HTREEITEM hParent, LPBYTE pbPtr, LPBYTE pbEnd, PULONG pcbMoveBy = NULL)
{
PTREE_ITEM_INFO pParentInfo = TV_GetItemParam(hWndTree, hParent);
HTREEITEM hItem = TreeView_GetChild(hWndTree, hParent);
LPBYTE pbBegin = pbPtr;
// If we're starting an ACE, then remember the ACE header
if(pParentInfo && pParentInfo->ItemType == ItemTypeAce)
pAceInWork = (PACE)(pbPtr);
// Keep going over all siblings
while(hItem != NULL)
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG cbMoveBy = 0;
bool bNeedItemToData = true;
// If there are a child items, go recursively on the children
if(TreeView_GetChild(hWndTree, hItem) != NULL)
{
// Special treatment for CLAIM_SECURITY_ATTRIBUTES v1
if(TV_IsSecurityAttributes(hWndTree, hItem))
Status = TV_ItemsToCSA_v1(hWndTree, hItem, pbPtr, pbEnd, &cbMoveBy);
else
Status = TV_ItemsToData(hWndTree, hItem, pbPtr, pbEnd, &cbMoveBy);
// If the items retrieval failed, we can try its own StringTo method
bNeedItemToData = (Status != STATUS_SUCCESS);
}
// Do we still need the data from the item?
if(bNeedItemToData)
{
Status = TV_ItemToData(hWndTree, hItem, pbPtr, pbEnd, &cbMoveBy);
}
// If the operation failed, bail out
if(!NT_SUCCESS(Status))
return Status;
pbPtr += cbMoveBy;
// Get the next sibling to the tree item
hItem = TreeView_GetNextSibling(hWndTree, hItem);
}
// When an ACE is being finished, fill some variables that depend on ACE layout and size
UpdateAceVariables(pAceInWork, pbPtr);
pAceInWork = NULL;
// Give length of the data to the caller
if(pcbMoveBy != NULL)
pcbMoveBy[0] = (ULONG)(pbPtr - pbBegin);
return STATUS_SUCCESS;
}
static DWORD TV_GetAceType(HWND hWndTree, HTREEITEM hItem)
{
PTREE_ITEM_INFO pItemInfo;
PACE_HEADER pAceHeader;
DWORD dwAceType = ACCESS_ALLOWED_ACE_TYPE;
BYTE AceHeader[sizeof(ACE_HEADER)];
// Retrieve the tree item info
if((pItemInfo = TV_GetItemParam(hWndTree, hItem)) != NULL)
{
if(pItemInfo->ItemType == ItemTypeAce)
{
// The buffer will overflow here because the whole ACE can't fit in it
if(TV_ItemsToData(hWndTree, hItem, AceHeader, AceHeader + sizeof(ACE_HEADER)) == STATUS_BUFFER_OVERFLOW)
{
pAceHeader = (PACE_HEADER)(AceHeader);
dwAceType = pAceHeader->AceType;
}
}
}
return dwAceType;
}
static void TV_ResetAceItem(HWND hWndTree, HTREEITEM hItem, LPBYTE pbAceData, ULONG cbAceData)
{
PACE_HEADER pAceHeader = (PACE_HEADER)(pbAceData);
ACE_HELPER AceHelper;
TCHAR szItemText[MAX_ITEM_TEXT];
// Init the item text
GetAceTypeString(szItemText, _countof(szItemText), pAceHeader);
TreeView_SetItemText(hWndTree, hItem, szItemText);
// Init the subitems
AceHelper.SetAce(pAceHeader);
// For ACE-forming flags like GUID flags, we need to set ACE-in-work
pAceInWork = (PACE)(pbAceData);
TV_InsertNewItemAceFields(hWndTree, hItem, AceHelper, pbAceData, pbAceData + cbAceData);
pAceInWork = NULL;
}
static void TV_SwapItems(HWND hWndTree, HTREEITEM hItem1, HTREEITEM hItem2)
{
PACE pSaveAceInWork = pAceInWork;
ULONG cbAceData1 = 0;
ULONG cbAceData2 = 0;
BYTE AceData1[MAX_ACL_LENGTH];
BYTE AceData2[MAX_ACL_LENGTH];
// Disable redraw
EnableRedraw(hWndTree, FALSE);
// Read the data from the first item
pAceInWork = (PACE)(AceData1);
if(NT_SUCCESS(TV_ItemsToData(hWndTree, hItem1, AceData1, AceData1 + sizeof(AceData1), &cbAceData1)))
{
// Read the data from the second item
pAceInWork = (PACE)(AceData2);
if(NT_SUCCESS(TV_ItemsToData(hWndTree, hItem2, AceData2, AceData2 + sizeof(AceData2), &cbAceData2)))
{
// Initialize the item texts
TV_ResetAceItem(hWndTree, hItem1, AceData2, cbAceData2);
TV_ResetAceItem(hWndTree, hItem2, AceData1, cbAceData1);
}
}
// Enable redrawing and paint
EnableRedraw(hWndTree, TRUE);
pAceInWork = pSaveAceInWork;
}
static NTSTATUS TreeView_ItemToAcl(HWND hWndTree, HTREEITEM hParent, PACL * ppAcl)
{
NTSTATUS Status = STATUS_NO_MEMORY;
LPBYTE pbAcl;
ULONG cbMoveBy = 0;
// Allocate buffer for the entire ACL
if((pbAcl = (LPBYTE)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, MAX_ACL_LENGTH)) != NULL)
{
// Set the ACL in work
pAclInWork = (PACL)(pbAcl);
pAclInWork->AceCount = 0;
// Process the ACL
if((Status = TV_ItemsToData(hWndTree, hParent, pbAcl, pbAcl + MAX_ACL_LENGTH, &cbMoveBy)) == STATUS_SUCCESS)
{
pAclInWork->AclSize = (WORD)(cbMoveBy);
ppAcl[0] = pAclInWork;
}
else
{
// Conversion to ACL failed, free the ACL
HeapFree(g_hHeap, 0, pbAcl);
}
// Reset the ACL-in-work pointer
pAclInWork = NULL;
}
return Status;
}
static NTSTATUS TreeView_ItemToAcl(
HWND hWndTree,
HTREEITEM hParent,
PACL * ppAcl,
BOOLEAN * pbAclPresent)
{
PTREE_ITEM_INFO pItemInfo;
HTREEITEM hItem = TreeView_GetChild(hWndTree, hParent);
NTSTATUS Status;
// Retrieve the item information
if((pItemInfo = TV_GetItemParam(hWndTree, hItem)) != NULL)
{
switch(pItemInfo->ItemType)
{
case ItemTypeNoAcl:
pbAclPresent[0] = FALSE;
ppAcl[0] = NULL;
return STATUS_SUCCESS;
case ItemTypeNullAcl:
pbAclPresent[0] = TRUE;
ppAcl[0] = NULL;
return STATUS_SUCCESS;
default:
if((Status = TreeView_ItemToAcl(hWndTree, hParent, ppAcl)) == STATUS_SUCCESS)
pbAclPresent[0] = TRUE;
return Status;
}
}
return STATUS_NOT_SUPPORTED;
}
static void TreeView_SecurityDescriptorToTreeView(
HWND hWndTree,
PSECURITY_DESCRIPTOR pSD)
{
HTREEITEM hItem;
TCHAR szNotPresent[128];
PSID pOwner = NULL;
PSID pGroup = NULL;
PACL pAcl = NULL;
BOOLEAN bDefaulted;
BOOLEAN bAclPresent;
// Turn off redrawing for faster response
EnableRedraw(hWndTree, FALSE);
// Clear all current tree view items
TreeView_DeleteAllItems(hWndTree);
LoadString(g_hInst, IDS_NOT_PRESENT, szNotPresent, _countof(szNotPresent));
//
// Insert tree item for owner security information
//
hItem = TV_InsertNewItem(hWndTree, NULL, NULL, &TreeItem_Owner);
RtlGetOwnerSecurityDescriptor(pSD, &pOwner, &bDefaulted);
TV_InsertNewItemSid(hWndTree, hItem, NULL, &TreeItem_UserSid, pOwner);
//
// Insert tree item for group security information
//
hItem = TV_InsertNewItem(hWndTree, NULL, NULL, &TreeItem_Group);
RtlGetGroupSecurityDescriptor(pSD, &pGroup, &bDefaulted);
TV_InsertNewItemSid(hWndTree, hItem, NULL, &TreeItem_UserSid, pGroup);
//
// Insert tree item for DACL security information
//
pAcl = NULL;
bAclPresent = FALSE;
RtlGetDaclSecurityDescriptor(pSD, &bAclPresent, &pAcl, &bDefaulted);
TV_InsertNewItemAcl(hWndTree, NULL, NULL, &TreeItem_Dacl, pAcl, bAclPresent);
//
// Insert tree item for SACL security information
//
pAcl = NULL;
bAclPresent = FALSE;
RtlGetSaclSecurityDescriptor(pSD, &bAclPresent, &pAcl, &bDefaulted);
TV_InsertNewItemAcl(hWndTree, NULL, NULL, &TreeItem_Sacl, pAcl, bAclPresent);
#ifdef _COMPARE_ACLS
if(pAcl != NULL)
{
assert(pAcl->AclSize <= sizeof(SaveAclBuffer));
memcpy(SaveAclBuffer, pAcl, pAcl->AclSize);
}
#endif
// Enable redrawing back
EnableRedraw(hWndTree, TRUE);
}
static void TreeView_DeferItemText(HWND hDlg, WPARAM wParam, LPARAM lParam)
{
HWND hWndTree = GetDlgItem(hDlg, IDC_SECURITY);
TVITEM tvi;
// Apply the text to the tree item
tvi.mask = TVIF_TEXT;
tvi.hItem = (HTREEITEM)wParam;
tvi.pszText = (LPTSTR)lParam;
TreeView_SetItem(hWndTree, &tvi);
// Free the text
delete [] tvi.pszText;
}
static void TreeView_DeferChangeIntWithFlags(HWND hDlg, WPARAM wParam)
{
PTREE_ITEM_INFO pItemInfo;
HTREEITEM hItem = (HTREEITEM)(wParam);
ULONGLONG IntValue = 0;
DWORD dwIntValue;
HWND hWndTree = GetDlgItem(hDlg, IDC_SECURITY);
if((pItemInfo = TV_GetItemParam(hWndTree, hItem)) != NULL)
{
LPBYTE pbPtr = (LPBYTE)(&dwIntValue);
LPBYTE pbEnd = pbPtr + sizeof(dwIntValue);
TCHAR szItemText[MAX_ITEM_TEXT];
// Sanity checks
assert(pItemInfo->pFlagInfos != NULL);
dwIntValue = (DWORD)(IntValue);
// Retrieve the integer value out of the item
if(TV_ItemToData(hWndTree, hItem, pbPtr, pbEnd) == STATUS_SUCCESS)
{
DWORD dwSaveValue = dwIntValue;
if(FlagsDialog(hDlg, IDS_SET_NEW_VALUE, pItemInfo->pFlagInfos, dwIntValue) == IDOK && dwIntValue != dwSaveValue)
{
IntValue = dwIntValue;
TV_MakeItemText(pItemInfo, szItemText, _countof(szItemText), pbPtr, pbEnd);
TreeView_SetItemText(hWndTree, hItem, szItemText);
}
}
}
}
static void TreeView_DeferChangeWholeAce(HWND hDlg, WPARAM wParam, LPARAM lParam)
{
TV_SELECTION tvs;
ACE_HELPER * pAceHelper = (ACE_HELPER *)(lParam);
PACE_HEADER pAceHeader;
HTREEITEM hItem = (HTREEITEM)(wParam);
HWND hWndTree = GetDlgItem(hDlg, IDC_SECURITY);
BYTE AceBuffer[MAX_ACL_LENGTH];
// The ACE_HELPER must be valid
if(pAceHelper != NULL)
{
// Export the ACE to the plain buffer
if((pAceHeader = pAceHelper->Export(AceBuffer, sizeof(AceBuffer))) != NULL)
{
// Stop redrawing
EnableRedraw(hWndTree, FALSE);
// Save the selection of the tree view item
TV_SaveSelection(hWndTree, tvs);
// Build the ACE into the item
TV_ResetAceItem(hWndTree, hItem, (LPBYTE)(pAceHeader), pAceHeader->AceSize);
// Select the root item
TV_RestoreSelection(hWndTree, tvs, hItem);
// Enable redrawing back
EnableRedraw(hWndTree);
}
delete pAceHelper;
}
}
static int TreeView_DeferChangeAceGuid(HWND hDlg, WPARAM wParam, LPARAM lParam)
{
PTREE_ITEM_INFO pItemInfo;
HTREEITEM hParent;
HTREEITEM hItem;
HWND hWndTree = GetDlgItem(hDlg, IDC_SECURITY);
if((hItem = TreeView_GetSelection(hWndTree)) != NULL)
{
if((pItemInfo = TV_GetItemParam(hWndTree, hItem)) != NULL)
{
if((hParent = TreeView_GetParent(hWndTree, hItem)) != NULL)
{
ACE_HELPER * pAceHelper;
ULONG cbMoveBy = 0;
BYTE AceBuffer[MAX_ACL_LENGTH] = {0};
if(TV_ItemsToData(hWndTree, hParent, AceBuffer, &AceBuffer[MAX_ACL_LENGTH], &cbMoveBy) == STATUS_SUCCESS)
{
if((pAceHelper = new ACE_HELPER()) != NULL)
{
// Setup the ACE from the data
pAceHelper->SetAce((PACE_HEADER)(AceBuffer));
// Now perform action-specific modification
switch(MAKELONG(wParam, lParam))
{
case MAKELONG(0, TRUE): // Create new GUID1
pAceHelper->Flags |= ACE_OBJECT_TYPE_PRESENT;
break;
case MAKELONG(1, TRUE): // Create new GUID1
pAceHelper->Flags |= ACE_INHERITED_OBJECT_TYPE_PRESENT;
break;
default: // Modify GUID1 or GUID2
pAceHelper->Flags |= ((PACE)(AceBuffer))->Flags;
break;
}
// Update the whole ACE
PostMessage(hDlg, WM_DEFER_CHANGE_WHOLE_ACE, (WPARAM)(hParent), (LPARAM)(pAceHelper));
}
}
}
}
}
return TRUE;
}
static void TreeView_DeferChangeAceResource(HWND hDlg, WPARAM wParam, LPARAM lParam)
{
PTREE_ITEM_INFO pItemInfo;
HTREEITEM hParent;
HTREEITEM hItem = (HTREEITEM)(wParam);
HWND hWndTree = GetDlgItem(hDlg, IDC_SECURITY);
bool bResourceTypeChanged = false;
bool bChangeWholeAce = false;
if((pItemInfo = TV_GetItemParam(hWndTree, hItem)) != NULL)
{
DWORD ValueCount = 3;
WORD ValueType = 0;
// Item-specific
switch(pItemInfo->ItemType)
{
case ItemTypeCSA_VType:
if(TV_ItemToData(hWndTree, hItem, &ValueType, sizeof(ValueType), NULL, NULL) == STATUS_SUCCESS)
{
if(lParam == TRUE)
{
DWORD dwSaveValueType = ValueType;
DWORD dwValueType = ValueType;
// Ask the user for new ACE type
if(FlagsDialog(hDlg, IDS_ACE_TYPE, CSA_ValTypes, dwValueType) != IDOK || dwValueType == dwSaveValueType)
return;
ValueType = (WORD)(dwValueType);
}
bResourceTypeChanged = true;
bChangeWholeAce = true;
}
break;
case ItemTypeCSA_VCnt:
if(TV_ItemToData(hWndTree, hItem, &ValueCount, sizeof(ValueCount), NULL, NULL) == STATUS_SUCCESS)
bChangeWholeAce = true;
break;
}
// OK to setup new ACE?
if(bChangeWholeAce)
{
ACE_CSA_HELPER CsaHelper;
NTSTATUS Status;
// Load the CSA helper from the tree view
hParent = TreeView_GetParent(hWndTree, hItem);
TV_ItemsToCSA_v1(hWndTree, hParent, CsaHelper);
hParent = TreeView_GetParent(hWndTree, hParent);
// Preserve the value type, if it didn't change
if(bResourceTypeChanged == false)
ValueType = CsaHelper.ValueType;
// Initiate changing the resource type and count
Status = CsaHelper.SetValueType(ValueType, ValueCount);
PostMessage(hDlg, WM_DEFER_CHANGE_WHOLE_ACE, (WPARAM)(hParent), (LPARAM)(new ACE_HELPER(CsaHelper)));
// If the change of the resource type/count failed, show error code
if(Status != STATUS_SUCCESS)
SetResultInfo(hDlg, RSI_NTSTATUS, Status);
}
}
}
static SECURITY_INFORMATION GetDialogSecurityInfo(HWND hDlg)
{
TFileTestData * pData = GetDialogData(hDlg);
// Clear all five main flags
pData->SecurityInformation &= ~(OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION);
// Set all flags whose check box is set
if(IsDlgButtonChecked(hDlg, IDC_OWNER_INFORMATION) == BST_CHECKED)
pData->SecurityInformation |= OWNER_SECURITY_INFORMATION;
if(IsDlgButtonChecked(hDlg, IDC_GROUP_INFORMATION) == BST_CHECKED)
pData->SecurityInformation |= GROUP_SECURITY_INFORMATION;
if(IsDlgButtonChecked(hDlg, IDC_DACL_INFORMATION) == BST_CHECKED)
pData->SecurityInformation |= DACL_SECURITY_INFORMATION;
if(IsDlgButtonChecked(hDlg, IDC_SACL_INFORMATION) == BST_CHECKED)
pData->SecurityInformation |= SACL_SECURITY_INFORMATION;
if(IsDlgButtonChecked(hDlg, IDC_LABEL_INFORMATION) == BST_CHECKED)
pData->SecurityInformation |= LABEL_SECURITY_INFORMATION;
return pData->SecurityInformation;
}
static void SetDialogSecurityInfo(HWND hDlg)
{
TFileTestData * pData = GetDialogData(hDlg);
int nChecked;
nChecked = (pData->SecurityInformation & OWNER_SECURITY_INFORMATION) ? BST_CHECKED : BST_UNCHECKED;
CheckDlgButton(hDlg, IDC_OWNER_INFORMATION, nChecked);
nChecked = (pData->SecurityInformation & GROUP_SECURITY_INFORMATION) ? BST_CHECKED : BST_UNCHECKED;
CheckDlgButton(hDlg, IDC_GROUP_INFORMATION, nChecked);
nChecked = (pData->SecurityInformation & DACL_SECURITY_INFORMATION) ? BST_CHECKED : BST_UNCHECKED;
CheckDlgButton(hDlg, IDC_DACL_INFORMATION, nChecked);
nChecked = (pData->SecurityInformation & SACL_SECURITY_INFORMATION) ? BST_CHECKED : BST_UNCHECKED;
CheckDlgButton(hDlg, IDC_SACL_INFORMATION, nChecked);
nChecked = (pData->SecurityInformation & LABEL_SECURITY_INFORMATION) ? BST_CHECKED : BST_UNCHECKED;
CheckDlgButton(hDlg, IDC_LABEL_INFORMATION, nChecked);
}
static void UpdateContextMenu(HWND hWndTree, HTREEITEM hItem, HMENU hMainMenu)
{
HMENU hSubMenu = GetSubMenu(hMainMenu, 0);
UINT uEnabled;
// Move ACE up is only allowed when the ACE is not the first one
uEnabled = (TV_GetAceSibling(hWndTree, hItem, FALSE) == NULL) ? MF_GRAYED : MF_ENABLED;
EnableMenuItem(hSubMenu, IDC_MOVE_ACE_UP, uEnabled);
// Move ACE down is only allowed when the ACE is not the last one
uEnabled = (TV_GetAceSibling(hWndTree, hItem, TRUE) == NULL) ? MF_GRAYED : MF_ENABLED;
EnableMenuItem(hSubMenu, IDC_MOVE_ACE_DOWN, uEnabled);
}
static HTREEITEM TreeView_GetPreviousItem(HWND hWndTree, HTREEITEM hItem)
{
hItem = TreeView_GetPrevSibling(hWndTree, hItem);
if(hItem == NULL)
hItem = TVI_FIRST;
return hItem;
}
static BOOL DeferSetItemTextValue(HWND hDlg, HTREEITEM hItem, LPCTSTR szItemText)
{
LPTSTR szBuffer;
// Create copy of the buffer
if((szBuffer = NewStr(szItemText)) == NULL)
return FALSE;
// Yes, accept changes
PostMessage(hDlg, WM_DEFER_ITEM_TEXT, (WPARAM)(hItem), (LPARAM)(szBuffer));
return TRUE;
}
static NTSTATUS NtQuerySecurityObject(HANDLE hObject, SECURITY_INFORMATION SecInfo, PSECURITY_DESCRIPTOR * ppSD, DWORD * pcbSD)
{
PSECURITY_DESCRIPTOR lpSD = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
DWORD dwTryCount = 0;
ULONG cbSD = 0;
// Try 5 times at the most
while(dwTryCount++ < 5)
{
// Try to query the object security into the current buffer
Status = NtQuerySecurityObject(hObject, SecInfo, lpSD, cbSD, &cbSD);
if(NT_SUCCESS(Status))
break;
// Free the old buffer
if(lpSD != NULL)
HeapFree(g_hHeap, 0, lpSD);
lpSD = NULL;
// Allocate new buffer
if((lpSD = (PSECURITY_DESCRIPTOR)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, cbSD)) == NULL)
{
Status = STATUS_NO_MEMORY;
break;
}
}
// If we are trying too many times, bail out
pcbSD[0] = cbSD;
ppSD[0] = lpSD;
return Status;
}
//-----------------------------------------------------------------------------
// Message handlers
static TAnchors * pAnchors = NULL;
static int OnInitDialog(HWND hDlg, LPARAM lParam)
{
TFileTestData * pData;
PROPSHEETPAGE * pPage = (PROPSHEETPAGE *)lParam;
// Remember the data pointer
SetDialogData(hDlg, pPage->lParam);
pData = (TFileTestData *)pPage->lParam;
// Initialize the "More ..." hyperlink
InitURLButton(hDlg, IDC_MORE_SECURITY_INFORMATION, FALSE);
// Configure dialog resizing
if(pData->bEnableResizing)
{
pAnchors = new TAnchors(hDlg);
pAnchors->AddAnchor(hDlg, IDC_MAIN_FRAME, akLeft | akTop | akRight);
pAnchors->AddAnchor(hDlg, IDC_SECURITY, akAll);
pAnchors->AddAnchor(hDlg, IDC_QUERY_SECURITY, akLeft | akBottom);
pAnchors->AddAnchor(hDlg, IDC_SET_BLANK, akLeftCenter | akBottom);
pAnchors->AddAnchor(hDlg, IDC_SET_SECURITY, akRight | akBottom);
pAnchors->AddAnchor(hDlg, IDC_RESULT_FRAME, akLeft | akRight | akBottom);
pAnchors->AddAnchor(hDlg, IDC_ERROR_CODE_TITLE, akLeft | akBottom);
pAnchors->AddAnchor(hDlg, IDC_ERROR_CODE, akLeft | akRight | akBottom);
pAnchors->AddAnchor(hDlg, IDC_INFORMATION_TITLE, akLeft | akBottom);
pAnchors->AddAnchor(hDlg, IDC_INFORMATION, akLeft | akRight | akBottom);
}
// Set the security information to the dialog
SetDialogSecurityInfo(hDlg);
// Set blank security descriptor
PostMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_SET_BLANK, BN_CLICKED), 0);
return TRUE;
}
static int OnSetAclType(HWND hDlg, UINT nIDCtrl)
{
PTREE_ITEM_INFO pItemInfo;
HTREEITEM hItem;
HWND hWndTree = GetDlgItem(hDlg, IDC_SECURITY);
// Get the selected item
if((hItem = TreeView_GetSelection(hWndTree)) != NULL)
{
// Retrieve the item info
if((pItemInfo = TV_GetItemParam(hWndTree, hItem)) != NULL)
{
// The item must be DACL or SACL
if(pItemInfo->ItemType == ItemTypeDacl || pItemInfo->ItemType == ItemTypeSacl)
{
// Delete all children
TreeView_DeleteChildren(hWndTree, hItem);
// For each item, do the specific action
switch(nIDCtrl)
{
case IDC_SET_NULL_ACL:
{
TV_InsertNewItem(hWndTree, hItem, NULL, &TreeItem_NullAcl);
break;
}
case IDC_SET_EMPTY_ACL:
{
TV_InsertNewItemAclFields(hWndTree, hItem, NULL, (PACL)(EmptyAcl));
break;
}
case IDC_SET_FULL_CONTROL:
{
TV_InsertNewItemAclFields(hWndTree, hItem, NULL, (PACL)(FullControlAcl));
break;
}
}
}
}
}
return TRUE;
}
static int OnInsertSiblingAce(HWND hDlg, BOOL bBeforeSelected)
{
PTREE_ITEM_INFO pItemInfo;
HTREEITEM hParent;
HTREEITEM hItem = NULL;
HWND hWndTree = GetDlgItem(hDlg, IDC_SECURITY);
BYTE AceBuffer[256];
// Retrieve the currently selected item
if((hItem = TreeView_GetSelection(hWndTree)) != NULL)
{
// Get the parent item
hParent = TreeView_GetParent(hWndTree, hItem);
// Get the item info of the *parent* item
if((pItemInfo = TV_GetItemParam(hWndTree, hParent)) != NULL)
{
HTREEITEM hInsertAfter = hItem;
DWORD dwAceType = TV_GetAceType(hWndTree, hItem);
// Get previous or next
if(bBeforeSelected)
{
if((hInsertAfter = TreeView_GetPreviousItem(hWndTree, hItem)) == NULL)
{
hInsertAfter = TVI_FIRST;
}
}
// Ask the user which ACE he wants to insert
if(FlagsDialog(hDlg, IDS_ACE_TYPE, AceHdrTypes, dwAceType) == IDOK)
{
PACE_HEADER pAceHeader;
ACE_HELPER AceHelper(dwAceType);
// Export the ACE and convert to tree items
if((pAceHeader = AceHelper.Export(AceBuffer, sizeof(AceBuffer))) != NULL)
{
if((hItem = TV_InsertNewItemAce(hWndTree, hParent, hInsertAfter, pAceHeader)) != NULL)
{
TreeView_Select(hWndTree, hItem, TVGN_CARET);
}
}
}
}
}
return TRUE;
}
static int OnSwapAceWith(HWND hDlg, BOOL bWithPrevious)
{
HTREEITEM hSwapWith;
HTREEITEM hItem;
HWND hWndTree = GetDlgItem(hDlg, IDC_SECURITY);
// Retrieve the item where to insert it
hItem = TreeView_GetSelection(hWndTree);
// Get the previous or next item to swap with
if(bWithPrevious == FALSE)
{
hSwapWith = TreeView_GetNextSibling(hWndTree, hItem);
TV_SwapItems(hWndTree, hItem, hSwapWith);
}
else
{
hSwapWith = TreeView_GetPrevSibling(hWndTree, hItem);
TV_SwapItems(hWndTree, hSwapWith, hItem);
}
// Select the target item
TreeView_Select(hWndTree, hSwapWith, TVGN_CARET);
return TRUE;
}
static int OnSetAceType(HWND hDlg)
{
PTREE_ITEM_INFO pItemInfo;
HTREEITEM hItem;
HWND hWndTree = GetDlgItem(hDlg, IDC_SECURITY);
if((hItem = TreeView_GetSelection(hWndTree)) != NULL)
{
if((pItemInfo = TV_GetItemParam(hWndTree, hItem)) != NULL)
{
DWORD dwSaveAceType = 0;
DWORD dwAceType;
BYTE AceType = 0;
if(TV_ItemToData(hWndTree, hItem, &AceType, sizeof(AceType), NULL, NULL) == STATUS_SUCCESS)
{
// Get the 32-bit ACE type
dwSaveAceType = dwAceType = AceType;
// Ask the user for new ACE type
if(FlagsDialog(hDlg, IDS_ACE_TYPE, AceHdrTypes, dwAceType) == IDOK && dwAceType != dwSaveAceType)
{
// Find the proper parent with the ACE type
while(pItemInfo->ItemType != ItemTypeAce)
{
if((hItem = TreeView_GetParent(hWndTree, hItem)) == NULL)
return FALSE;
pItemInfo = TV_GetItemParam(hWndTree, hItem);
}
// Rebuild the whole ACE
PostMessage(hDlg, WM_DEFER_CHANGE_WHOLE_ACE, (WPARAM)(hItem), (LPARAM)(new ACE_HELPER(dwAceType)));
}
}
}
}
return TRUE;
}
static int OnDeleteAce(HWND hDlg)
{
HTREEITEM hParent;
HTREEITEM hItem;
HWND hWndTree = GetDlgItem(hDlg, IDC_SECURITY);
hItem = TreeView_GetSelection(hWndTree);
hParent = TreeView_GetParent(hWndTree, hItem);
if(hParent != NULL && hItem != NULL)
{
// Disable redrawing
EnableRedraw(hWndTree, FALSE);
// Delete the tree item
if(TreeView_DeleteItem(hWndTree, hItem))
{
// If there are no children, set the enpty ACL
if(TreeView_GetChildCount(hWndTree, hParent) == 0)
{
// Insert the ACL as empty
TV_InsertNewItemAclFields(hWndTree, hParent, TVI_LAST, (PACL)(EmptyAcl));
TreeView_Select(hWndTree, hParent, TVGN_CARET);
}
}
EnableRedraw(hWndTree);
}
return TRUE;
}
static int OnSetBlankSecurityDescriptor(HWND hDlg)
{
SECURITY_DESCRIPTOR sd;
HWND hWndTree = GetDlgItem(hDlg, IDC_SECURITY);
// Initialize the tree view with blank security descriptor
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
TreeView_SecurityDescriptorToTreeView(hWndTree, &sd);
return TRUE;
}
static int OnMoreSecurityInformation(HWND hDlg)
{
TFileTestData * pData = GetDialogData(hDlg);
// Read the security information from this dialog
GetDialogSecurityInfo(hDlg);
FlagsDialog(hDlg, IDS_SECURITY_INFORMATION, SecurityInformations, pData->SecurityInformation);
SetDialogSecurityInfo(hDlg);
return TRUE;
}
static int OnQuerySecurity(HWND hDlg)
{
PSECURITY_DESCRIPTOR lpSD = NULL;
TFileTestData * pData = GetDialogData(hDlg);
NTSTATUS Status = STATUS_SUCCESS;
ULONG cbSD = 0;
HWND hWndTree = GetDlgItem(hDlg, IDC_SECURITY);
// Get the mask about which security information we want
GetDialogSecurityInfo(hDlg);
// Query the security information
Status = NtQuerySecurityObject(pData->hFile, pData->SecurityInformation, &lpSD, &cbSD);
// If succeeded, load our tree view with security information
if(NT_SUCCESS(Status))
TreeView_SecurityDescriptorToTreeView(hWndTree, lpSD);
if(lpSD != NULL)
HeapFree(g_hHeap, 0, lpSD);
SetResultInfo(hDlg, RSI_NTSTATUS | RSI_INFO_INT32, Status, cbSD);
return TRUE;
}
static int OnSetSecurity(HWND hDlg)
{
TFileTestData * pData = GetDialogData(hDlg);
SECURITY_DESCRIPTOR sd;
SECURITY_INFORMATION AppliedSecInfo = 0;
HTREEITEM hChildItem[4];
NTSTATUS Status = STATUS_SUCCESS;
HWND hWndTree = GetDlgItem(hDlg, IDC_SECURITY);
PACL pDacl = NULL;
PACL pSacl = NULL;
BYTE OwnerSid[MAX_SID_LENGTH];
BYTE GroupSid[MAX_SID_LENGTH];
BOOLEAN bDaclPresent = FALSE;
BOOLEAN bSaclPresent = FALSE;
// Get the mask about which security information we want
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
GetDialogSecurityInfo(hDlg);
// Get handles of all child items
hChildItem[0] = TreeView_GetChild(hWndTree, TVI_ROOT);
hChildItem[1] = TreeView_GetNextSibling(hWndTree, hChildItem[0]);
hChildItem[2] = TreeView_GetNextSibling(hWndTree, hChildItem[1]);
hChildItem[3] = TreeView_GetNextSibling(hWndTree, hChildItem[2]);
//
// Put owner into the security descriptor
//
if(NT_SUCCESS(Status) && (pData->SecurityInformation & OWNER_SECURITY_INFORMATION))
{
Status = TV_ItemsToData(hWndTree, hChildItem[0], OwnerSid, OwnerSid + sizeof(OwnerSid));
if(NT_SUCCESS(Status))
{
Status = RtlSetOwnerSecurityDescriptor(&sd, (PSID)(OwnerSid), FALSE);
AppliedSecInfo |= OWNER_SECURITY_INFORMATION;
}
}
//
// Put group into the security descriptor
//
if(NT_SUCCESS(Status) && (pData->SecurityInformation & GROUP_SECURITY_INFORMATION))
{
Status = TV_ItemsToData(hWndTree, hChildItem[1], GroupSid, GroupSid + sizeof(GroupSid));
if(NT_SUCCESS(Status))
{
Status = RtlSetGroupSecurityDescriptor(&sd, (PSID)(GroupSid), FALSE);
AppliedSecInfo |= GROUP_SECURITY_INFORMATION;
}
}
//
// Put DACL into the security descriptor
//
if(NT_SUCCESS(Status) && (pData->SecurityInformation & DACL_SECURITY_INFORMATION))
{
Status = TreeView_ItemToAcl(hWndTree, hChildItem[2], &pDacl, &bDaclPresent);
if(NT_SUCCESS(Status))
{
Status = RtlSetDaclSecurityDescriptor(&sd, bDaclPresent, pDacl, FALSE);
AppliedSecInfo |= DACL_SECURITY_INFORMATION;
}
}
//
// Put SACL into the security descriptor
//
if(NT_SUCCESS(Status) && (pData->SecurityInformation & ALL_SACL_SECURITY_INFORMATION))
{
Status = TreeView_ItemToAcl(hWndTree, hChildItem[3], &pSacl, &bSaclPresent);
if(NT_SUCCESS(Status))
{
#ifdef _COMPARE_ACLS
assert(pSacl->AclSize <= sizeof(SaveAclBuffer));
assert(memcmp(SaveAclBuffer, pSacl, pSacl->AclSize) == 0);
#endif
Status = RtlSetSaclSecurityDescriptor(&sd, bSaclPresent, pSacl, FALSE);
AppliedSecInfo |= (pData->SecurityInformation & ALL_SACL_SECURITY_INFORMATION);
}
}
// Apply the security descriptor to the file
if(NT_SUCCESS(Status))
Status = NtSetSecurityObject(pData->hFile, AppliedSecInfo, &sd);
SetResultInfo(hDlg, RSI_NTSTATUS | RSI_NOINFO, Status);
// Free all 4 parts of the security information
if(pSacl != NULL)
HeapFree(g_hHeap, 0, pSacl);
if(pDacl != NULL)
HeapFree(g_hHeap, 0, pDacl);
return TRUE;
}
static int OnSetActive(HWND hDlg)
{
TFileTestData * pData = GetDialogData(hDlg);
BOOL bEnable = FALSE;
if(IsHandleValid(pData->hFile))
bEnable = TRUE;
EnableDlgItems(hDlg, bEnable, IDC_QUERY_SECURITY, IDC_SET_SECURITY, 0);
return TRUE;
}
static int OnBeginLabelEdit(HWND hDlg, LPNMTVDISPINFO pTVDispInfo)
{
PTREE_ITEM_INFO pItemInfo = (PTREE_ITEM_INFO)(pTVDispInfo->item.lParam);
HWND hWndTree = pTVDispInfo->hdr.hwndFrom;
HWND hWndEdit;
BOOL bStartEditing = FALSE;
TCHAR szEditText[256];
// Both ToString and StringTo methods must be present
if(pItemInfo && pItemInfo->ToString && pItemInfo->StringTo)
{
// Exception: ACE condition is not editable
if(IsItemTypeEditable(pItemInfo))
{
// Convert the item text to the editable text
if(TV_TextToEditText(pItemInfo, szEditText, _countof(szEditText), pTVDispInfo->item.pszText))
{
// Retrieve the handle to the edit box
if((hWndEdit = TreeView_GetEditControl(hWndTree)) != NULL)
{
// Apply the text limit to the edit text
Edit_LimitText(hWndEdit, GetDefaultCharLimit(pItemInfo));
// Apply the editable text to the edit field
SetWindowText(hWndEdit, szEditText);
bStartEditing = TRUE;
}
}
}
}
// If the editing started successfully,
// make sure that Esc key will not close the entire FileTest
if(bStartEditing)
{
DisableCloseDialog(hDlg, bStartEditing);
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, FALSE);
}
else
{
SetResultInfo(hDlg, RSI_NTSTATUS, STATUS_CANNOT_EDIT_THIS);
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, TRUE);
}
return TRUE;
}
static int OnEndLabelEdit(HWND hDlg, LPNMTVDISPINFO pTVDispInfo)
{
PTREE_ITEM_INFO pItemInfo = (PTREE_ITEM_INFO)(pTVDispInfo->item.lParam);
ACE_HELPER * pAceHelper;
HTREEITEM hParent;
LPBYTE pbBuffer;
size_t SaveIndex = GLOBAL_ItemIndex;
ULONG cbBuffer = 0x1000;
ULONG cbMoveBy = 0;
BOOL bAcceptChanges = FALSE;
TCHAR szItemText[MAX_ITEM_TEXT];
// If pszText contains NULL, it means that the user cancelled the editing
if(pTVDispInfo->item.pszText && pTVDispInfo->item.pszText[0])
{
// Can we convert both values?
if(pItemInfo && pItemInfo->ToString && pItemInfo->StringTo)
{
if((pbBuffer = (LPBYTE)LocalAlloc(LPTR, cbBuffer)) != NULL)
{
NTSTATUS Status;
// Convert the string to data
Status = pItemInfo->StringTo(pItemInfo, pTVDispInfo->item.pszText, pbBuffer, pbBuffer + cbBuffer, &cbMoveBy);
if(NT_SUCCESS(Status))
{
switch(pItemInfo->ItemType)
{
case ItemTypeAceType: // Changing ACE type: We need to set the whole ACE again
pAceHelper = new ACE_HELPER(pbBuffer[0]);
hParent = TreeView_GetParent(pTVDispInfo->hdr.hwndFrom, pTVDispInfo->item.hItem);
PostMessage(hDlg, WM_DEFER_CHANGE_WHOLE_ACE, (WPARAM)(hParent), (LPARAM)(pAceHelper));
bAcceptChanges = TRUE;
break;
case ItemTypeGuid:
PostMessage(hDlg, WM_DEFER_CHANGE_ACE_GUID, 0, FALSE);
bAcceptChanges = TRUE;
break;
case ItemTypeGuid2:
PostMessage(hDlg, WM_DEFER_CHANGE_ACE_GUID, 1, FALSE);
bAcceptChanges = TRUE;
break;
case ItemTypeCSA_VType:
if(IsValidAceAttributeType(pbBuffer))
{
PostMessage(hDlg, WM_DEFER_CHANGE_ACE_CSA, (WPARAM)(pTVDispInfo->item.hItem), FALSE);
bAcceptChanges = TRUE;
}
break;
case ItemTypeCSA_VCnt:
if(IsValidAceAttributeCount(pbBuffer))
{
PostMessage(hDlg, WM_DEFER_CHANGE_ACE_CSA, (WPARAM)(pTVDispInfo->item.hItem), FALSE);
bAcceptChanges = TRUE;
}
break;
default: // Convert the item to text
GLOBAL_ItemIndex = TV_GetAceResourceValueIndex(pTVDispInfo);
TV_MakeItemText(pItemInfo, szItemText, _countof(szItemText), pbBuffer, pbBuffer + cbMoveBy);
bAcceptChanges = DeferSetItemTextValue(hDlg, pTVDispInfo->item.hItem, szItemText);
GLOBAL_ItemIndex = SaveIndex;
break;
}
}
// Set the result
if(bAcceptChanges == FALSE)
SetResultInfo(hDlg, RSI_NTSTATUS | RSI_INFO_INT32, Status, 0);
else
SetResultInfo(hDlg, RSI_NTSTATUS | RSI_NOINFO, STATUS_SUCCESS);
LocalFree(pbBuffer);
}
}
}
// Enable the exit button
DisableCloseDialog(hDlg, FALSE);
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, bAcceptChanges);
return TRUE;
}
static int OnDeleteItem(HWND /* hDlg */, LPNMTREEVIEW pNMTreeView)
{
PTREE_ITEM_INFO pItemInfo;
if((pItemInfo = (PTREE_ITEM_INFO)pNMTreeView->itemOld.lParam) != NULL)
{
if(IsItemDataPointer(pItemInfo))
HeapFree(g_hHeap, 0, pItemInfo->ItemData);
HeapFree(g_hHeap, 0, pItemInfo);
}
return TRUE;
}
static int OnTVContextMenu(HWND hDlg, LPARAM lParam)
{
PTREE_ITEM_INFO pItemInfo;
HTREEITEM hItem;
POINT pt;
HMENU hMainMenu = NULL;
HWND hWndTree = GetDlgItem(hDlg, IDC_SECURITY);
RECT rect;
// Get the selected item
if((hItem = TreeView_GetSelection(hWndTree)) != NULL)
{
// Get the LPARAM of the tree item.
if((pItemInfo = TV_GetItemParam(hWndTree, hItem)) != NULL)
{
switch(pItemInfo->ItemType)
{
case ItemTypeDacl:
case ItemTypeSacl:
hMainMenu = FindContextMenu(IDR_ACL_TYPE_MENU);
break;
case ItemTypeAce:
hMainMenu = FindContextMenu(IDR_ACE_MENU);
break;
}
}
// If we picked a menu, execute it
if(hMainMenu != 0)
{
// Update the menu
UpdateContextMenu(hWndTree, hItem, hMainMenu);
// If we don't have the coords, make them from the tree item
if(lParam == 0xFFFFFFFF)
{
TreeView_GetItemRect(hWndTree, hItem, &rect, TRUE);
pt.x = rect.left;
pt.y = rect.bottom;
ClientToScreen(hWndTree, &pt);
lParam = MAKELPARAM(pt.x, pt.y);
}
// Execute the menu
return ExecuteContextMenu(hDlg, hMainMenu, lParam);
}
}
return FALSE;
}
static int OnTVRightClick(HWND hDlg)
{
TVHITTESTINFO hti;
HTREEITEM hItem;
POINT pt;
HWND hWndTree = GetDlgItem(hDlg, IDC_SECURITY);
// Select the right-clicked tree view item
GetCursorPos(&pt);
ScreenToClient(hWndTree, &pt);
hti.pt = pt;
hti.flags = TVHT_ONITEMLABEL;
hItem = TreeView_HitTest(hWndTree, &hti);
// If there is an item clicked, select it
if(hItem != NULL)
TreeView_Select(hWndTree, hItem, TVGN_CARET);
return FALSE;
}
static int OnTVDoubleClick(HWND hDlg)
{
PTREE_ITEM_INFO pItemInfo;
HTREEITEM hInsertAfter = TVI_FIRST;
HTREEITEM hParent;
HTREEITEM hItem;
HWND hWndTree = GetDlgItem(hDlg, IDC_SECURITY);
BYTE DataBuffer[0x200];
size_t cbDataBuffer = sizeof(DataBuffer);
// Get the handle to the selected item
if((hItem = TreeView_GetSelection(hWndTree)) != NULL)
{
// Retrieve the parent of the item
hParent = TreeView_GetParent(hWndTree, hItem);
// Retrieve the tree item info of the selected item
if((pItemInfo = TV_GetItemParam(hWndTree, hItem)) != NULL)
{
switch(pItemInfo->ItemType)
{
case ItemTypeAce: // Change the whole ACE type
case ItemTypeAceType:
PostMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_SET_ACE_TYPE, 0), 0);
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, TRUE);
return TRUE;
case ItemTypeGuid: // Create new ACE guid here
PostMessage(hDlg, WM_DEFER_CHANGE_ACE_GUID, 0, TRUE);
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, TRUE);
return TRUE;
case ItemTypeGuid2:
PostMessage(hDlg, WM_DEFER_CHANGE_ACE_GUID, 1, TRUE);
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, TRUE);
return TRUE;
case ItemTypeCSA_VType:
PostMessage(hDlg, WM_DEFER_CHANGE_ACE_CSA, (WPARAM)(hItem), TRUE);
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, TRUE);
return TRUE;
}
// Does the item have handler for double click?
if(pItemInfo->CreateNew != NULL && pItemInfo->ItemData != ItemDataValid)
{
PTREE_ITEM_INFO pNewInfo = pItemInfo;
TREE_ITEM_INFO SaveItemInfo;
// Save the item info
memcpy(&SaveItemInfo, pItemInfo, sizeof(TREE_ITEM_INFO));
pItemInfo = &SaveItemInfo;
// Special case: When replacing NO_ACL or NULL_ACL, we want to pass parent item
if(pItemInfo->ItemType == ItemTypeNoAcl || pItemInfo->ItemType == ItemTypeNullAcl)
pNewInfo = TV_GetItemParam(hWndTree, hParent);
// Let the item to create new one
if(NT_SUCCESS(pItemInfo->CreateNew(pNewInfo, DataBuffer, &cbDataBuffer)))
{
// Get the previous item
hInsertAfter = TreeView_GetPreviousItem(hWndTree, hItem);
EnableRedraw(hWndTree, FALSE);
// Delete the subitems of the item
TreeView_DeleteItem(hWndTree, hItem);
// Insert the new item with the created data
switch(SaveItemInfo.ItemType)
{
case ItemTypeSid:
TV_InsertNewItemSid(hWndTree, hParent, hInsertAfter, &SaveItemInfo, (PSID)(DataBuffer));
break;
case ItemTypeNoAcl:
TV_InsertNewItemAclFields(hWndTree, hParent, hInsertAfter, (PACL)(DataBuffer));
break;
case ItemTypeNullAcl:
TV_InsertNewItemAclFields(hWndTree, hParent, NULL, (PACL)(FullControlAcl));
break;
}
EnableRedraw(hWndTree);
}
}
// If the item is an integer type and has flags, we can show the supporting dialog
if(IsIntegerTypeWithFlags(pItemInfo))
{
PostMessage(hDlg, WM_DEFER_CHANGE_INT_FLAGS, (WPARAM)(hItem), TRUE);
return TRUE;
}
}
}
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, TRUE);
return TRUE;
}
static void OnTVKeyDown(HWND hDlg, NMTVKEYDOWN * pNMTVKeyDown)
{
if(pNMTVKeyDown->wVKey == VK_SPACE)
{
OnTVDoubleClick(hDlg);
return;
}
if(pNMTVKeyDown->wVKey == 'C' && GetAsyncKeyState(VK_CONTROL) < 0)
{
TreeView_CopyToClipboard(pNMTVKeyDown->hdr.hwndFrom);
return;
}
}
static int OnCommand(HWND hDlg, UINT nNotify, UINT nIDCtrl)
{
if(nNotify == BN_CLICKED || nNotify == 1)
{
switch(nIDCtrl)
{
case ID_EDIT_LABEL:
return TreeView_EditLabel_ID(hDlg, IDC_SECURITY);
case ID_DOUBLE_CLICK:
return OnTVDoubleClick(hDlg);
case IDC_SET_NULL_ACL:
case IDC_SET_EMPTY_ACL:
case IDC_SET_FULL_CONTROL:
return OnSetAclType(hDlg, nIDCtrl);
case IDC_NEW_ACE_BEFORE:
return OnInsertSiblingAce(hDlg, TRUE);
case IDC_NEW_ACE_AFTER:
return OnInsertSiblingAce(hDlg, FALSE);
case IDC_MOVE_ACE_UP:
return OnSwapAceWith(hDlg, TRUE);
case IDC_MOVE_ACE_DOWN:
return OnSwapAceWith(hDlg, FALSE);
case IDC_SET_ACE_TYPE:
return OnSetAceType(hDlg);
case IDC_DELETE_ACE:
return OnDeleteAce(hDlg);
case IDC_SET_BLANK:
return OnSetBlankSecurityDescriptor(hDlg);
case IDC_MORE_SECURITY_INFORMATION:
return OnMoreSecurityInformation(hDlg);
case IDC_QUERY_SECURITY:
return OnQuerySecurity(hDlg);
case IDC_SET_SECURITY:
return OnSetSecurity(hDlg);
}
}
return FALSE;
}
static int OnNotify(HWND hDlg, NMHDR * pNMHDR)
{
switch(pNMHDR->code)
{
case PSN_SETACTIVE:
return OnSetActive(hDlg);
case NM_RCLICK:
if(pNMHDR->idFrom == IDC_SECURITY)
return OnTVRightClick(hDlg);
break;
case NM_DBLCLK:
if(pNMHDR->idFrom == IDC_SECURITY)
return OnTVDoubleClick(hDlg);
break;
case TVN_KEYDOWN:
OnTVKeyDown(hDlg, (NMTVKEYDOWN *)pNMHDR);
break;
case TVN_BEGINLABELEDIT:
return OnBeginLabelEdit(hDlg, (LPNMTVDISPINFO)pNMHDR);
case TVN_ENDLABELEDIT:
return OnEndLabelEdit(hDlg, (LPNMTVDISPINFO)pNMHDR);
case TVN_DELETEITEM:
return OnDeleteItem(hDlg, (LPNMTREEVIEW)pNMHDR);
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Public functions
INT_PTR CALLBACK PageProc09(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// Handlers specific to our dialog
switch(uMsg)
{
case WM_INITDIALOG:
OnInitDialog(hDlg, lParam);
return TRUE;
case WM_SIZE:
case WM_GETMINMAXINFO:
return pAnchors->OnMessage(uMsg, wParam, lParam);
case WM_DRAWITEM:
if(wParam == IDC_MORE_SECURITY_INFORMATION)
DrawURLButton(hDlg, (LPDRAWITEMSTRUCT)lParam);
return TRUE;
case WM_DEFER_ITEM_TEXT:
TreeView_DeferItemText(hDlg, wParam, lParam);
return TRUE;
case WM_DEFER_CHANGE_INT_FLAGS:
TreeView_DeferChangeIntWithFlags(hDlg, wParam);
return TRUE;
case WM_DEFER_CHANGE_WHOLE_ACE:
TreeView_DeferChangeWholeAce(hDlg, wParam, lParam);
return TRUE;
case WM_DEFER_CHANGE_ACE_GUID:
TreeView_DeferChangeAceGuid(hDlg, wParam, lParam);
return TRUE;
case WM_DEFER_CHANGE_ACE_CSA:
TreeView_DeferChangeAceResource(hDlg, wParam, lParam);
return TRUE;
case WM_CONTEXTMENU:
return OnTVContextMenu(hDlg, lParam);
case WM_COMMAND:
return OnCommand(hDlg, HIWORD(wParam), LOWORD(wParam));
case WM_NOTIFY:
return OnNotify(hDlg, (NMHDR *)lParam);
case WM_DESTROY:
if(pAnchors != NULL)
delete pAnchors;
pAnchors = NULL;
return FALSE;
}
return FALSE;
}