mirror of
https://github.com/reactos/reactos.git
synced 2024-12-30 19:14:31 +00:00
b284e82f47
Access check is an expensive operation, that is, whenever an access to an object is performed an access check has to be done to ensure the access can be allowed to the calling thread who attempts to access such object. Currently SepAnalyzeAcesFromDacl allocates a block of pool memory for access check rights, nagging the Memory Manager like a desperate naughty creep. So instead initialize the access rights as a simple variable in SepAccessCheck and pass it out as an address to SepAnalyzeAcesFromDacl so that the function will fill it up with access rights. This helps with performance, avoiding wasting a few bits of memory just to hold these access rights. In addition to that, add a few asserts and fix the copyright header on both se.h and accesschk.c, to reflect the Coding Style rules.
347 lines
9.7 KiB
C
347 lines
9.7 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 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->Header.AceType, 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
|
|
}
|
|
|
|
/* EOF */
|