mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 12:26:32 +00:00
310563aece
As the commit title says. Instead of having the caller figuring out what the ACE type should be of the ACE.
384 lines
11 KiB
C
384 lines
11 KiB
C
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
* PURPOSE: Security subsystem debug routines support
|
|
* COPYRIGHT: Copyright 2022-2023 George Bișoc <george.bisoc@reactos.org>
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
#ifndef NDEBUG
|
|
/**
|
|
* @brief
|
|
* Converts an Access Control Entry (ACE) type to a string.
|
|
*
|
|
* @return
|
|
* Returns a converted ACE type strings. If no
|
|
* known ACE type is found, it will return
|
|
* UNKNOWN TYPE.
|
|
*/
|
|
static
|
|
PCSTR
|
|
SepGetAceTypeString(
|
|
_In_ UCHAR AceType)
|
|
{
|
|
#define TOSTR(x) #x
|
|
static const PCSTR AceTypes[] =
|
|
{
|
|
TOSTR(ACCESS_ALLOWED_ACE_TYPE),
|
|
TOSTR(ACCESS_DENIED_ACE_TYPE),
|
|
TOSTR(SYSTEM_AUDIT_ACE_TYPE),
|
|
TOSTR(SYSTEM_ALARM_ACE_TYPE),
|
|
TOSTR(ACCESS_ALLOWED_COMPOUND_ACE_TYPE),
|
|
TOSTR(ACCESS_ALLOWED_OBJECT_ACE_TYPE),
|
|
TOSTR(ACCESS_DENIED_OBJECT_ACE_TYPE),
|
|
TOSTR(SYSTEM_AUDIT_OBJECT_ACE_TYPE),
|
|
TOSTR(SYSTEM_ALARM_OBJECT_ACE_TYPE),
|
|
TOSTR(ACCESS_ALLOWED_CALLBACK_ACE_TYPE),
|
|
TOSTR(ACCESS_DENIED_CALLBACK_ACE_TYPE),
|
|
TOSTR(ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE),
|
|
TOSTR(ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE),
|
|
TOSTR(SYSTEM_AUDIT_CALLBACK_ACE_TYPE),
|
|
TOSTR(SYSTEM_ALARM_CALLBACK_ACE_TYPE),
|
|
TOSTR(SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE),
|
|
TOSTR(SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE),
|
|
TOSTR(SYSTEM_MANDATORY_LABEL_ACE_TYPE),
|
|
};
|
|
#undef TOSTR
|
|
|
|
if (AceType < RTL_NUMBER_OF(AceTypes))
|
|
return AceTypes[AceType];
|
|
else
|
|
return "UNKNOWN TYPE";
|
|
}
|
|
|
|
/**
|
|
* @brief
|
|
* Dumps the ACE flags to the debugger output.
|
|
*/
|
|
static
|
|
VOID
|
|
SepDumpAceFlags(
|
|
_In_ UCHAR AceFlags)
|
|
{
|
|
#define ACE_FLAG_PRINT(x) \
|
|
if (AceFlags & x) \
|
|
{ \
|
|
DbgPrint(#x "\n"); \
|
|
}
|
|
|
|
ACE_FLAG_PRINT(OBJECT_INHERIT_ACE);
|
|
ACE_FLAG_PRINT(CONTAINER_INHERIT_ACE);
|
|
ACE_FLAG_PRINT(NO_PROPAGATE_INHERIT_ACE);
|
|
ACE_FLAG_PRINT(INHERIT_ONLY_ACE);
|
|
ACE_FLAG_PRINT(INHERITED_ACE);
|
|
#undef ACE_FLAG_PRINT
|
|
}
|
|
|
|
/**
|
|
* @brief
|
|
* Iterates and dumps each ACE debug info in an ACL.
|
|
*/
|
|
static
|
|
VOID
|
|
SepDumpAces(
|
|
_In_ PACL Acl)
|
|
{
|
|
NTSTATUS Status;
|
|
PACE Ace;
|
|
ULONG AceIndex;
|
|
PSID Sid;
|
|
UNICODE_STRING SidString;
|
|
|
|
/* Loop all ACEs and dump their info */
|
|
for (AceIndex = 0; AceIndex < Acl->AceCount; AceIndex++)
|
|
{
|
|
/* Get the ACE at this index */
|
|
Status = RtlGetAce(Acl, AceIndex, (PVOID*)&Ace);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/*
|
|
* Normally this should never happen.
|
|
* Just fail gracefully and stop further
|
|
* debugging of ACEs.
|
|
*/
|
|
DbgPrint("SepDumpAces(): Failed to find the next ACE, stop dumping info...\n");
|
|
return;
|
|
}
|
|
|
|
DbgPrint("================== %lu# ACE DUMP INFO ==================\n", AceIndex);
|
|
DbgPrint("Ace -> 0x%p\n", Ace);
|
|
DbgPrint("Ace->Header -> 0x%p\n", Ace->Header);
|
|
DbgPrint("Ace->Header.AceType -> %s\n", SepGetAceTypeString(Ace->Header.AceType));
|
|
DbgPrint("Ace->AccessMask -> 0x%08lx\n", Ace->AccessMask);
|
|
|
|
Sid = SepGetSidFromAce(Ace);
|
|
ASSERT(Sid);
|
|
RtlConvertSidToUnicodeString(&SidString, Sid, TRUE);
|
|
DbgPrint("Ace SID -> %wZ\n", &SidString);
|
|
RtlFreeUnicodeString(&SidString);
|
|
|
|
DbgPrint("Ace->Header.AceSize -> %u\n", Ace->Header.AceSize);
|
|
DbgPrint("Ace->Header.AceFlags:\n");
|
|
SepDumpAceFlags(Ace->Header.AceFlags);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief
|
|
* Dumps debug info of an Access Control List (ACL).
|
|
*/
|
|
static
|
|
VOID
|
|
SepDumpAclInfo(
|
|
_In_ PACL Acl,
|
|
_In_ BOOLEAN IsSacl)
|
|
{
|
|
/* Dump relevant info */
|
|
DbgPrint("================== %s DUMP INFO ==================\n", IsSacl ? "SACL" : "DACL");
|
|
DbgPrint("Acl->AclRevision -> %u\n", Acl->AclRevision);
|
|
DbgPrint("Acl->AclSize -> %u\n", Acl->AclSize);
|
|
DbgPrint("Acl->AceCount -> %u\n", Acl->AceCount);
|
|
|
|
/* Dump all the ACEs present on this ACL */
|
|
SepDumpAces(Acl);
|
|
}
|
|
|
|
/**
|
|
* @brief
|
|
* Dumps control flags of a security descriptor to the debugger.
|
|
*/
|
|
static
|
|
VOID
|
|
SepDumpSdControlInfo(
|
|
_In_ SECURITY_DESCRIPTOR_CONTROL SdControl)
|
|
{
|
|
#define SD_CONTROL_PRINT(x) \
|
|
if (SdControl & x) \
|
|
{ \
|
|
DbgPrint(#x "\n"); \
|
|
}
|
|
|
|
SD_CONTROL_PRINT(SE_OWNER_DEFAULTED);
|
|
SD_CONTROL_PRINT(SE_GROUP_DEFAULTED);
|
|
SD_CONTROL_PRINT(SE_DACL_PRESENT);
|
|
SD_CONTROL_PRINT(SE_DACL_DEFAULTED);
|
|
SD_CONTROL_PRINT(SE_SACL_PRESENT);
|
|
SD_CONTROL_PRINT(SE_SACL_DEFAULTED);
|
|
SD_CONTROL_PRINT(SE_DACL_UNTRUSTED);
|
|
SD_CONTROL_PRINT(SE_SERVER_SECURITY);
|
|
SD_CONTROL_PRINT(SE_DACL_AUTO_INHERIT_REQ);
|
|
SD_CONTROL_PRINT(SE_SACL_AUTO_INHERIT_REQ);
|
|
SD_CONTROL_PRINT(SE_DACL_AUTO_INHERITED);
|
|
SD_CONTROL_PRINT(SE_SACL_AUTO_INHERITED);
|
|
SD_CONTROL_PRINT(SE_DACL_PROTECTED);
|
|
SD_CONTROL_PRINT(SE_SACL_PROTECTED);
|
|
SD_CONTROL_PRINT(SE_RM_CONTROL_VALID);
|
|
SD_CONTROL_PRINT(SE_SELF_RELATIVE);
|
|
#undef SD_CONTROL_PRINT
|
|
}
|
|
|
|
/**
|
|
* @brief
|
|
* Dumps each security identifier (SID) of an access token to debugger.
|
|
*/
|
|
static
|
|
VOID
|
|
SepDumpSidsOfToken(
|
|
_In_ PSID_AND_ATTRIBUTES Sids,
|
|
_In_ ULONG SidCount)
|
|
{
|
|
ULONG SidIndex;
|
|
UNICODE_STRING SidString;
|
|
|
|
/* Loop all SIDs and dump them */
|
|
for (SidIndex = 0; SidIndex < SidCount; SidIndex++)
|
|
{
|
|
RtlConvertSidToUnicodeString(&SidString, Sids[SidIndex].Sid, TRUE);
|
|
DbgPrint("%lu# %wZ\n", SidIndex, &SidString);
|
|
RtlFreeUnicodeString(&SidString);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
/**
|
|
* @brief
|
|
* Dumps debug information of a security descriptor to the debugger.
|
|
*/
|
|
VOID
|
|
SepDumpSdDebugInfo(
|
|
_In_opt_ PISECURITY_DESCRIPTOR SecurityDescriptor)
|
|
{
|
|
#ifndef NDEBUG
|
|
UNICODE_STRING SidString;
|
|
PSID OwnerSid, GroupSid;
|
|
PACL Dacl, Sacl;
|
|
#endif
|
|
|
|
/* Don't dump anything if no SD was provided */
|
|
if (!SecurityDescriptor)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
/* Cache the necessary security buffers to dump info from */
|
|
OwnerSid = SepGetOwnerFromDescriptor(SecurityDescriptor);
|
|
GroupSid = SepGetGroupFromDescriptor(SecurityDescriptor);
|
|
Sacl = SepGetSaclFromDescriptor(SecurityDescriptor);
|
|
Dacl = SepGetDaclFromDescriptor(SecurityDescriptor);
|
|
|
|
DbgPrint("================== SECURITY DESCRIPTOR DUMP INFO ==================\n");
|
|
DbgPrint("SecurityDescriptor -> 0x%p\n", SecurityDescriptor);
|
|
DbgPrint("SecurityDescriptor->Revision -> %u\n", SecurityDescriptor->Revision);
|
|
DbgPrint("SecurityDescriptor->Control:\n");
|
|
SepDumpSdControlInfo(SecurityDescriptor->Control);
|
|
|
|
/* Dump the Owner SID if the SD belongs to an owner */
|
|
if (OwnerSid)
|
|
{
|
|
RtlConvertSidToUnicodeString(&SidString, OwnerSid, TRUE);
|
|
DbgPrint("SD Owner SID -> %wZ\n", &SidString);
|
|
RtlFreeUnicodeString(&SidString);
|
|
}
|
|
|
|
/* Dump the Group SID if the SD belongs to a group */
|
|
if (GroupSid)
|
|
{
|
|
RtlConvertSidToUnicodeString(&SidString, GroupSid, TRUE);
|
|
DbgPrint("SD Group SID -> %wZ\n", &SidString);
|
|
RtlFreeUnicodeString(&SidString);
|
|
}
|
|
|
|
/* Dump the ACL contents of SACL if this SD has one */
|
|
if (Sacl)
|
|
{
|
|
SepDumpAclInfo(Sacl, TRUE);
|
|
}
|
|
|
|
/* Dump the ACL contents of DACL if this SD has one */
|
|
if (Dacl)
|
|
{
|
|
SepDumpAclInfo(Dacl, FALSE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief
|
|
* Dumps debug information of an access token to the debugger.
|
|
*/
|
|
VOID
|
|
SepDumpTokenDebugInfo(
|
|
_In_opt_ PTOKEN Token)
|
|
{
|
|
#ifndef NDEBUG
|
|
UNICODE_STRING SidString;
|
|
#endif
|
|
|
|
/* Don't dump anything if no token was provided */
|
|
if (!Token)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
/* Dump relevant token info */
|
|
DbgPrint("================== ACCESS TOKEN DUMP INFO ==================\n");
|
|
DbgPrint("Token -> 0x%p\n", Token);
|
|
DbgPrint("Token->ImageFileName -> %s\n", Token->ImageFileName);
|
|
DbgPrint("Token->TokenSource.SourceName -> \"%-.*s\"\n",
|
|
RTL_NUMBER_OF(Token->TokenSource.SourceName),
|
|
Token->TokenSource.SourceName);
|
|
DbgPrint("Token->TokenSource.SourceIdentifier -> %lu.%lu\n",
|
|
Token->TokenSource.SourceIdentifier.HighPart,
|
|
Token->TokenSource.SourceIdentifier.LowPart);
|
|
|
|
RtlConvertSidToUnicodeString(&SidString, Token->PrimaryGroup, TRUE);
|
|
DbgPrint("Token primary group SID -> %wZ\n", &SidString);
|
|
RtlFreeUnicodeString(&SidString);
|
|
|
|
DbgPrint("Token user and groups SIDs:\n");
|
|
SepDumpSidsOfToken(Token->UserAndGroups, Token->UserAndGroupCount);
|
|
|
|
if (SeTokenIsRestricted(Token))
|
|
{
|
|
DbgPrint("Token restricted SIDs:\n");
|
|
SepDumpSidsOfToken(Token->RestrictedSids, Token->RestrictedSidCount);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief
|
|
* Dumps security access rights to the debugger.
|
|
*/
|
|
VOID
|
|
SepDumpAccessRightsStats(
|
|
_In_ PACCESS_CHECK_RIGHTS AccessRights)
|
|
{
|
|
/*
|
|
* Dump the access rights only if we have remaining rights
|
|
* to dump in the first place. RemainingAccessRights can be 0
|
|
* if access check procedure has failed prematurely and this
|
|
* member hasn't been filled yet.
|
|
*/
|
|
if (!AccessRights->RemainingAccessRights)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
DbgPrint("================== ACCESS CHECK RIGHTS STATISTICS ==================\n");
|
|
DbgPrint("Remaining access rights -> 0x%08lx\n", AccessRights->RemainingAccessRights);
|
|
DbgPrint("Granted access rights -> 0x%08lx\n", AccessRights->GrantedAccessRights);
|
|
DbgPrint("Denied access rights -> 0x%08lx\n", AccessRights->DeniedAccessRights);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief
|
|
* Dumps access and status values of each object type
|
|
* in the result list.
|
|
*/
|
|
VOID
|
|
SepDumpAccessAndStatusList(
|
|
_In_ PACCESS_MASK GrantedAccessList,
|
|
_In_ PNTSTATUS AccessStatusList,
|
|
_In_ BOOLEAN IsResultList,
|
|
_In_ POBJECT_TYPE_LIST_INTERNAL ObjectTypeList,
|
|
_In_ ULONG ObjectTypeListLength)
|
|
{
|
|
#ifndef NDEBUG
|
|
ULONG ResultListIndex;
|
|
ULONG ObjectTypeIndex;
|
|
ULONG ResultListLength;
|
|
|
|
DbgPrint("================== ACCESS & STATUS OBJECT TYPE LIST STATISTICS ==================\n");
|
|
ResultListLength = IsResultList ? ObjectTypeListLength : 1;
|
|
for (ResultListIndex = 0; ResultListIndex < ResultListLength; ResultListIndex++)
|
|
{
|
|
DbgPrint("Result Index #%lu, Granted access rights -> 0x%08lx, Access status -> 0x%08lx\n",
|
|
ResultListIndex, GrantedAccessList[ResultListIndex], AccessStatusList[ResultListIndex]);
|
|
}
|
|
|
|
for (ObjectTypeIndex = 0; ObjectTypeIndex < ObjectTypeListLength; ObjectTypeIndex++)
|
|
{
|
|
DbgPrint("================== #%lu OBJECT ACCESS RIGHTS ==================\n", ObjectTypeIndex);
|
|
DbgPrint("Remaining access rights -> 0x%08lx\n", ObjectTypeList[ObjectTypeIndex].ObjectAccessRights.RemainingAccessRights);
|
|
DbgPrint("Granted access rights -> 0x%08lx\n", ObjectTypeList[ObjectTypeIndex].ObjectAccessRights.GrantedAccessRights);
|
|
DbgPrint("Denied access rights -> 0x%08lx\n", ObjectTypeList[ObjectTypeIndex].ObjectAccessRights.DeniedAccessRights);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* EOF */
|