https://git.reactos.org/?p=reactos.git;a=commitdiff;h=a42f642ea117ffc27a378…
commit a42f642ea117ffc27a378c4938b555dfdd461a45
Author: George Bișoc <george.bisoc(a)reactos.org>
AuthorDate: Wed Jun 21 17:55:19 2023 +0200
Commit: unknown <george.bisoc(a)reactos.org>
CommitDate: Tue Aug 22 17:54:17 2023 +0200
[NTOS:SE] Implement access security checks by type
- Implement SepDenyAccessObjectTypeResultList, SepAllowAccessObjectTypeResultList,
SepDenyAccessObjectTypeList and SepAllowAccessObjectTypeList. These routines will
be used to grant or deny access to sub-objects of an object in the list.
- Refactor SepAnalyzeAcesFromDacl and SepAccessCheck to accomodate the newly
implemented access check by type mechanism.
- SepAccessCheck will now be SepAccessCheckWorker, a worker helper function that
further
abstracts the access check mechanism in the kernel. Whereas the SepAccessCheck name
will be
used as a centralized function used by the access check NT system calls.
- Deprecate SepGetSDOwner and SepGetSDGroup in favor of SepGetOwnerFromDescriptor
and
SepGetGroupFromDescriptor. The former functions were buggy as they might potentially
return garbage data if either the owner or group were passed as NULL to a security
descriptor, hence a second chance exception fault. This was caught when writing
tests
for NtAccessCheckByType.
- Shorten the debug prints by removing the name of the functions, the person who
reads
the debugger output has to look at the source code anyway.
---
ntoskrnl/se/accesschk.c | 1928 ++++++++++++++++++++++++++++++++++-------------
1 file changed, 1409 insertions(+), 519 deletions(-)
diff --git a/ntoskrnl/se/accesschk.c b/ntoskrnl/se/accesschk.c
index 690f7c029e7..d63e07429e8 100644
--- a/ntoskrnl/se/accesschk.c
+++ b/ntoskrnl/se/accesschk.c
@@ -15,6 +15,371 @@
/* PRIVATE FUNCTIONS **********************************************************/
+/**
+ * @brief
+ * Denies access of a target object and the children objects
+ * in an object type list.
+ *
+ * @param[in,out] ObjectTypeList
+ * A pointer to an object type list where access is to be
+ * denied for the target object and its children in the
+ * hierarchy list.
+ *
+ * @param[in] ObjectTypeListLength
+ * The length of the object type list. This length represents
+ * the number of object elements in the list.
+ *
+ * @param[in] AccessMask
+ * The access mask right that is to be denied for the object.
+ *
+ * @param[in] ObjectTypeGuid
+ * A pointer to a object type GUID, that identifies the object.
+ * This GUID is used to search for the target object in the list.
+ * If this parameter is set to NULL, the function will deny access
+ * starting from the object itself in the list (aka the root).
+ */
+static
+VOID
+SepDenyAccessObjectTypeResultList(
+ _Inout_ POBJECT_TYPE_LIST_INTERNAL ObjectTypeList,
+ _In_ ULONG ObjectTypeListLength,
+ _In_ ACCESS_MASK AccessMask,
+ _In_opt_ PGUID ObjectTypeGuid)
+{
+ ULONG ObjectTypeIndex;
+ ULONG ReturnedObjectIndex;
+ USHORT Level;
+
+ PAGED_CODE();
+
+ DPRINT("Access rights 0x%08lx\n", AccessMask);
+
+ /*
+ * The object type of interest is the one that was supplied
+ * by the creator who made the ACE. If the object type was
+ * not supplied then we have no clear indication from where
+ * shall we start updating the access rights of objects on
+ * this list, so we have to begin from the root (aka the
+ * object itself).
+ */
+ if (!ObjectTypeGuid)
+ {
+ DPRINT("No object type provided, updating access rights from
root\n");
+ ReturnedObjectIndex = 0;
+ goto LoopAndUpdateRightsObjects;
+ }
+
+ /* Check if that object exists in the list */
+ if (SepObjectTypeGuidInList(ObjectTypeList,
+ ObjectTypeListLength,
+ ObjectTypeGuid,
+ &ReturnedObjectIndex))
+ {
+LoopAndUpdateRightsObjects:
+ /* Update the access rights of the target object */
+ ObjectTypeList[ReturnedObjectIndex].ObjectAccessRights.DeniedAccessRights |=
+ (AccessMask &
~ObjectTypeList[ReturnedObjectIndex].ObjectAccessRights.GrantedAccessRights);
+ DPRINT("Denied rights 0x%08lx of target object at index %lu\n",
+ ObjectTypeList[ReturnedObjectIndex].ObjectAccessRights.DeniedAccessRights,
ReturnedObjectIndex);
+
+ /* And update the children of the target object */
+ for (ObjectTypeIndex = ReturnedObjectIndex + 1;
+ ObjectTypeIndex < ObjectTypeListLength;
+ ObjectTypeIndex++)
+ {
+ /*
+ * Stop looking for children objects if we hit an object that has
+ * the same level as the target object or less.
+ */
+ Level = ObjectTypeList[ObjectTypeIndex].Level;
+ if (Level <= ObjectTypeList[ReturnedObjectIndex].Level)
+ {
+ DPRINT("We looked for all children objects, stop looking\n");
+ break;
+ }
+
+ /* Update the access right of the child */
+ ObjectTypeList[ObjectTypeIndex].ObjectAccessRights.DeniedAccessRights |=
+ (AccessMask &
~ObjectTypeList[ObjectTypeIndex].ObjectAccessRights.GrantedAccessRights);
+ DPRINT("Denied rights 0x%08lx of child object at index %lu\n",
+ ObjectTypeList[ObjectTypeIndex].ObjectAccessRights.DeniedAccessRights,
ObjectTypeIndex);
+ }
+ }
+}
+
+/**
+ * @brief
+ * Allows access of a target object and the children objects
+ * in an object type list.
+ *
+ * @param[in,out] ObjectTypeList
+ * A pointer to an object type list where access is to be
+ * allowed for the target object and its children in the
+ * hierarchy list.
+ *
+ * @param[in] ObjectTypeListLength
+ * The length of the object type list. This length represents
+ * the number of object elements in the list.
+ *
+ * @param[in] AccessMask
+ * The access mask right that is to be allowed for the object.
+ *
+ * @param[in] ObjectTypeGuid
+ * A pointer to a object type GUID, that identifies the object.
+ * This GUID is used to search for the target object in the list.
+ * If this parameter is set to NULL, the function will allow access
+ * starting from the object itself in the list (aka the root).
+ */
+static
+VOID
+SepAllowAccessObjectTypeResultList(
+ _Inout_ POBJECT_TYPE_LIST_INTERNAL ObjectTypeList,
+ _In_ ULONG ObjectTypeListLength,
+ _In_ ACCESS_MASK AccessMask,
+ _In_opt_ PGUID ObjectTypeGuid)
+{
+ ULONG ObjectTypeIndex;
+ ULONG ReturnedObjectIndex;
+ USHORT Level;
+
+ PAGED_CODE();
+
+ DPRINT("Access rights 0x%08lx\n", AccessMask);
+
+ /*
+ * Begin updating the access rights from the root object
+ * (see comment in SepDenyAccessObjectTypeListMaximum).
+ */
+ if (!ObjectTypeGuid)
+ {
+ DPRINT("No object type provided, updating access rights from
root\n");
+ ReturnedObjectIndex = 0;
+ goto LoopAndUpdateRightsObjects;
+ }
+
+ /* Check if that object exists in the list */
+ if (SepObjectTypeGuidInList(ObjectTypeList,
+ ObjectTypeListLength,
+ ObjectTypeGuid,
+ &ReturnedObjectIndex))
+ {
+LoopAndUpdateRightsObjects:
+ /* Update the access rights of the target object */
+ ObjectTypeList[ReturnedObjectIndex].ObjectAccessRights.GrantedAccessRights |=
+ (AccessMask &
~ObjectTypeList[ReturnedObjectIndex].ObjectAccessRights.DeniedAccessRights);
+ DPRINT("Granted rights 0x%08lx of target object at index %lu\n",
+ ObjectTypeList[ReturnedObjectIndex].ObjectAccessRights.GrantedAccessRights,
ReturnedObjectIndex);
+
+ /* And update the children of the target object */
+ for (ObjectTypeIndex = ReturnedObjectIndex + 1;
+ ObjectTypeIndex < ObjectTypeListLength;
+ ObjectTypeIndex++)
+ {
+ /*
+ * Stop looking for children objects if we hit an object that has
+ * the same level as the target object or less.
+ */
+ Level = ObjectTypeList[ObjectTypeIndex].Level;
+ if (Level <= ObjectTypeList[ReturnedObjectIndex].Level)
+ {
+ break;
+ }
+
+ /* Update the access right of the child */
+ ObjectTypeList[ObjectTypeIndex].ObjectAccessRights.GrantedAccessRights |=
+ (AccessMask &
~ObjectTypeList[ObjectTypeIndex].ObjectAccessRights.DeniedAccessRights);
+ DPRINT("Granted rights 0x%08lx of child object at index %lu\n",
+ ObjectTypeList[ObjectTypeIndex].ObjectAccessRights.GrantedAccessRights,
ObjectTypeIndex);
+ }
+ }
+}
+
+/**
+ * @brief
+ * Denies access of a target object in the object type
+ * list. This access is denied for the whole hierarchy
+ * in the list.
+ *
+ * @param[in,out] ObjectTypeList
+ * A pointer to an object type list where access is to be
+ * denied for the target object. This operation applies
+ * for the entire hierarchy of the object type list.
+ *
+ * @param[in] ObjectTypeListLength
+ * The length of the object type list. This length represents
+ * the number of object elements in the list.
+ *
+ * @param[in] AccessMask
+ * The access mask right that is to be denied for the object.
+ *
+ * @param[in] ObjectTypeGuid
+ * A pointer to a object type GUID, that identifies the object.
+ * This GUID is used to search for the target object in the list.
+ * If this parameter is set to NULL, the function will deny access
+ * to the object itself in the list (aka the root).
+ *
+ * @param[out] BreakOnDeny
+ * A pointer returned boolean value to the caller. The function
+ * will return TRUE if the requested remaining right is denied
+ * by the ACE, otherwise it returns FALSE.
+ */
+static
+VOID
+SepDenyAccessObjectTypeList(
+ _Inout_ POBJECT_TYPE_LIST_INTERNAL ObjectTypeList,
+ _In_ ULONG ObjectTypeListLength,
+ _In_ ACCESS_MASK AccessMask,
+ _In_opt_ PGUID ObjectTypeGuid,
+ _Out_opt_ PBOOLEAN BreakOnDeny)
+{
+ ULONG ReturnedObjectIndex;
+ BOOLEAN MustBreak;
+
+ PAGED_CODE();
+
+ DPRINT("Access rights 0x%08lx\n", AccessMask);
+
+ /* Assume we do not want to break at first */
+ MustBreak = FALSE;
+
+ /*
+ * If no object type was supplied then tell the caller it has to break on
+ * searching for other ACEs if the requested remaining access right is
+ * denied by the deny ACE itself. Track down that denied right too.
+ */
+ if (!ObjectTypeGuid)
+ {
+ if (ObjectTypeList[0].ObjectAccessRights.RemainingAccessRights &
AccessMask)
+ {
+ DPRINT("Root object requests remaining access right that is denied
0x%08lx\n", AccessMask);
+ MustBreak = TRUE;
+ }
+
+ ObjectTypeList[0].ObjectAccessRights.DeniedAccessRights |=
+ (AccessMask &
~ObjectTypeList[0].ObjectAccessRights.GrantedAccessRights);
+ DPRINT("Denied rights of root object 0x%08lx\n",
ObjectTypeList[0].ObjectAccessRights.DeniedAccessRights);
+ goto Quit;
+ }
+
+ /*
+ * If the object exists tell the caller it has to break down if the requested
+ * remaining access right is denied by the ACE. Track down the denied right too.
+ */
+ if (SepObjectTypeGuidInList(ObjectTypeList,
+ ObjectTypeListLength,
+ ObjectTypeGuid,
+ &ReturnedObjectIndex))
+ {
+ if (ObjectTypeList[ReturnedObjectIndex].ObjectAccessRights.RemainingAccessRights
& AccessMask)
+ {
+ DPRINT("Object at index %lu requests remaining access right that is
denied 0x%08lx\n", ReturnedObjectIndex, AccessMask);
+ MustBreak = TRUE;
+ }
+
+ ObjectTypeList[ReturnedObjectIndex].ObjectAccessRights.DeniedAccessRights |=
+ (AccessMask &
~ObjectTypeList[ReturnedObjectIndex].ObjectAccessRights.GrantedAccessRights);
+ DPRINT("Denied rights 0x%08lx of object at index %lu\n",
+ ObjectTypeList[ReturnedObjectIndex].ObjectAccessRights.DeniedAccessRights,
ReturnedObjectIndex);
+ }
+
+Quit:
+ /* Signal the caller he has to break if he wants to */
+ if (BreakOnDeny)
+ {
+ *BreakOnDeny = MustBreak;
+ }
+}
+
+/**
+ * @brief
+ * Allows access of a target object in the object type
+ * list. This access is allowed for the whole hierarchy
+ * in the list.
+ *
+ * @param[in,out] ObjectTypeList
+ * A pointer to an object type list where access is to be
+ * allowed for the target object. This operation applies
+ * for the entire hierarchy of the object type list.
+ *
+ * @param[in] ObjectTypeListLength
+ * The length of the object type list. This length represents
+ * the number of object elements in the list.
+ *
+ * @param[in] AccessMask
+ * The access mask right that is to be allowed for the object.
+ *
+ * @param[in] RemoveRemainingRights
+ * If set to TRUE, the function will remove the remaining rights
+ * of a target object. It will also grant access of the said object.
+ * Otherwise if set to FALSE, the function will only grant access.
+ *
+ * @param[in] ObjectTypeGuid
+ * A pointer to a object type GUID, that identifies the object.
+ * This GUID is used to search for the target object in the list.
+ * If this parameter is set to NULL, the function will allow access
+ * to the object itself in the list (aka the root).
+ */
+static
+VOID
+SepAllowAccessObjectTypeList(
+ _Inout_ POBJECT_TYPE_LIST_INTERNAL ObjectTypeList,
+ _In_ ULONG ObjectTypeListLength,
+ _In_ ACCESS_MASK AccessMask,
+ _In_ BOOLEAN RemoveRemainingRights,
+ _In_opt_ PGUID ObjectTypeGuid)
+{
+ ULONG ReturnedObjectIndex;
+
+ PAGED_CODE();
+
+ DPRINT("Access rights 0x%08lx\n", AccessMask);
+
+ /*
+ * If no object type was supplied then remove the remaining rights
+ * of the object itself, the root. Track down that right to the
+ * granted rights as well.
+ */
+ if (!ObjectTypeGuid)
+ {
+ if (RemoveRemainingRights)
+ {
+ ObjectTypeList[0].ObjectAccessRights.RemainingAccessRights &=
~AccessMask;
+ DPRINT("Remaining rights of root object 0x%08lx\n",
ObjectTypeList[0].ObjectAccessRights.RemainingAccessRights);
+ }
+
+ ObjectTypeList[0].ObjectAccessRights.GrantedAccessRights |=
+ (AccessMask &
~ObjectTypeList[0].ObjectAccessRights.DeniedAccessRights);
+ DPRINT("Granted rights of root object 0x%08lx\n",
ObjectTypeList[0].ObjectAccessRights.GrantedAccessRights);
+ return;
+ }
+
+ /*
+ * Grant access to the object if it exists by removing the remaining
+ * rights. Unlike the NtAccessCheckByTypeResultList variant we do not
+ * care about the children of the target object beccause NtAccessCheckByType
+ * will either grant or deny access to the entire hierarchy of the list.
+ */
+ if (SepObjectTypeGuidInList(ObjectTypeList,
+ ObjectTypeListLength,
+ ObjectTypeGuid,
+ &ReturnedObjectIndex))
+ {
+ /* Remove the remaining rights of that object */
+ if (RemoveRemainingRights)
+ {
+ ObjectTypeList[ReturnedObjectIndex].ObjectAccessRights.RemainingAccessRights
&= ~AccessMask;
+ DPRINT("Remaining rights of object 0x%08lx at index %lu\n",
+
ObjectTypeList[ReturnedObjectIndex].ObjectAccessRights.RemainingAccessRights,
ReturnedObjectIndex);
+ }
+
+ /* And track it down to the granted access rights */
+ ObjectTypeList[ReturnedObjectIndex].ObjectAccessRights.GrantedAccessRights |=
+ (AccessMask &
~ObjectTypeList[ReturnedObjectIndex].ObjectAccessRights.DeniedAccessRights);
+ DPRINT("Granted rights of object 0x%08lx at index %lu\n",
+ ObjectTypeList[ReturnedObjectIndex].ObjectAccessRights.GrantedAccessRights,
ReturnedObjectIndex);
+ }
+}
+
/**
* @brief
* Analyzes an access control entry that is present in a discretionary
@@ -78,11 +443,19 @@
* question represents the number of elements in such array. This parameter must be 0
* if no array list is provided.
*
+ * @param[in] UseResultList
+ * This parameter is to used to determine how to perform an object type access check.
+ * If set to TRUE, the function will either grant or deny access to the object and
sub-objects
+ * in the hierarchy list. If set to FALSE, the function will either grant or deny access
to
+ * the target that will affect the entire hierarchy of the list. This parameter is used
+ * if the access action type is AccessCheckMaximum.
+ *
* @param[in,out] AccessCheckRights
* A pointer to a structure that contains the access check rights. This function fills
* up this structure with remaining, granted and denied rights to the caller for
* access check. Henceforth, this parameter must not be NULL!
*/
+static
VOID
SepAnalyzeAcesFromDacl(
_In_ ACCESS_CHECK_RIGHT_TYPE ActionType,
@@ -93,15 +466,19 @@ SepAnalyzeAcesFromDacl(
_In_ BOOLEAN IsTokenRestricted,
_In_opt_ PSID PrincipalSelfSid,
_In_ PGENERIC_MAPPING GenericMapping,
- _In_opt_ POBJECT_TYPE_LIST ObjectTypeList,
+ _In_opt_ POBJECT_TYPE_LIST_INTERNAL ObjectTypeList,
_In_ ULONG ObjectTypeListLength,
+ _In_ BOOLEAN UseResultList,
_Inout_ PACCESS_CHECK_RIGHTS AccessCheckRights)
{
NTSTATUS Status;
PACE CurrentAce;
ULONG AceIndex;
+ ULONG ObjectTypeIndex;
PSID Sid;
+ PGUID ObjectTypeGuid;
ACCESS_MASK Access;
+ BOOLEAN BreakOnDeny;
PAGED_CODE();
@@ -109,10 +486,6 @@ SepAnalyzeAcesFromDacl(
ASSERT(Dacl);
ASSERT(AccessToken);
- /* TODO: To be removed once we support object type handling in Se */
- DBG_UNREFERENCED_PARAMETER(ObjectTypeList);
- DBG_UNREFERENCED_PARAMETER(ObjectTypeListLength);
-
/* TODO: To be removed once we support compound ACEs handling in Se */
DBG_UNREFERENCED_PARAMETER(PrimaryAccessToken);
@@ -160,7 +533,7 @@ SepAnalyzeAcesFromDacl(
/* Deny access rights that have not been granted yet */
AccessCheckRights->DeniedAccessRights |= (Access &
~AccessCheckRights->GrantedAccessRights);
- DPRINT("SepAnalyzeAcesFromDacl(): DeniedAccessRights
0x%08lx\n", AccessCheckRights->DeniedAccessRights);
+ DPRINT("DeniedAccessRights 0x%08lx\n",
AccessCheckRights->DeniedAccessRights);
}
}
else if (CurrentAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
@@ -182,12 +555,108 @@ SepAnalyzeAcesFromDacl(
/* Grant access rights that have not been denied yet */
AccessCheckRights->GrantedAccessRights |= (Access &
~AccessCheckRights->DeniedAccessRights);
- DPRINT("SepAnalyzeAcesFromDacl(): GrantedAccessRights
0x%08lx\n", AccessCheckRights->GrantedAccessRights);
+ DPRINT("GrantedAccessRights 0x%08lx\n",
AccessCheckRights->GrantedAccessRights);
+ }
+ }
+ else if (CurrentAce->Header.AceType ==
ACCESS_DENIED_OBJECT_ACE_TYPE)
+ {
+ /* Get the SID and object type from this ACE */
+ Sid = SepGetSidFromAce(ACCESS_DENIED_OBJECT_ACE_TYPE,
CurrentAce);
+ ObjectTypeGuid = SepGetObjectTypeGuidFromAce(CurrentAce, TRUE);
+ ASSERT(Sid);
+
+ if (SepSidInTokenEx(AccessToken, PrincipalSelfSid, Sid, TRUE,
IsTokenRestricted))
+ {
+ /* Get this access right from the ACE */
+ Access = CurrentAce->AccessMask;
+
+ /* Map this access right if it has a generic mask right */
+ if ((Access & GENERIC_ACCESS) &&
GenericMapping)
+ {
+ RtlMapGenericMask(&Access, GenericMapping);
+ }
+
+ /* If no list was passed treat this is as
ACCESS_DENIED_ACE_TYPE */
+ if (!ObjectTypeList && !ObjectTypeListLength)
+ {
+ AccessCheckRights->DeniedAccessRights |= (Access &
~AccessCheckRights->GrantedAccessRights);
+ DPRINT("DeniedAccessRights 0x%08lx\n",
AccessCheckRights->DeniedAccessRights);
+ }
+ else if (!UseResultList)
+ {
+ /*
+ * We have an object type list but the caller wants to
deny access
+ * to the entire hierarchy list. Evaluate the rights of
the object
+ * for the whole list. Ignore what the function tells us
if we have
+ * to break on deny or not because we only want to keep
track of
+ * denied rights.
+ */
+ SepDenyAccessObjectTypeList(ObjectTypeList,
+ ObjectTypeListLength,
+ Access,
+ ObjectTypeGuid,
+ NULL);
+ }
+ else
+ {
+ /* Otherwise evaluate the access rights for each
sub-object */
+ SepDenyAccessObjectTypeResultList(ObjectTypeList,
+ ObjectTypeListLength,
+ Access,
+ ObjectTypeGuid);
+ }
+ }
+ }
+ else if (CurrentAce->Header.AceType ==
ACCESS_ALLOWED_OBJECT_ACE_TYPE)
+ {
+ /* Get the SID and object type from this ACE */
+ Sid = SepGetSidFromAce(ACCESS_ALLOWED_OBJECT_ACE_TYPE,
CurrentAce);
+ ObjectTypeGuid = SepGetObjectTypeGuidFromAce(CurrentAce,
FALSE);
+ ASSERT(Sid);
+
+ if (SepSidInTokenEx(AccessToken, PrincipalSelfSid, Sid, FALSE,
IsTokenRestricted))
+ {
+ /* Get this access right from the ACE */
+ Access = CurrentAce->AccessMask;
+
+ /* Map this access right if it has a generic mask right */
+ if ((Access & GENERIC_ACCESS) &&
GenericMapping)
+ {
+ RtlMapGenericMask(&Access, GenericMapping);
+ }
+
+ /* If no list was passed treat this is as
ACCESS_ALLOWED_ACE_TYPE */
+ if (!ObjectTypeList && !ObjectTypeListLength)
+ {
+ AccessCheckRights->GrantedAccessRights |= (Access
& ~AccessCheckRights->DeniedAccessRights);
+ DPRINT("GrantedAccessRights 0x%08lx\n",
AccessCheckRights->GrantedAccessRights);
+ }
+ else if (!UseResultList)
+ {
+ /*
+ * We have an object type list but the caller wants to
allow access
+ * to the entire hierarchy list. Evaluate the rights of
the object
+ * for the whole list.
+ */
+ SepAllowAccessObjectTypeList(ObjectTypeList,
+ ObjectTypeListLength,
+ Access,
+ FALSE,
+ ObjectTypeGuid);
+ }
+ else
+ {
+ /* Otherwise evaluate the access rights for each
sub-object */
+ SepAllowAccessObjectTypeResultList(ObjectTypeList,
+
ObjectTypeListLength,
+ Access,
+ ObjectTypeGuid);
+ }
}
}
else
{
- DPRINT1("SepAnalyzeAcesFromDacl(): Unsupported ACE type
0x%lx\n", CurrentAce->Header.AceType);
+ DPRINT1("Unsupported ACE type 0x%lx\n",
CurrentAce->Header.AceType);
}
}
}
@@ -207,6 +676,17 @@ SepAnalyzeAcesFromDacl(
ASSERT(RemainingAccess != 0);
AccessCheckRights->RemainingAccessRights = RemainingAccess;
+ /* Fill the remaining rights of each object in the list if we have one */
+ if (ObjectTypeList && (ObjectTypeListLength != 0))
+ {
+ for (ObjectTypeIndex = 0;
+ ObjectTypeIndex < ObjectTypeListLength;
+ ObjectTypeIndex++)
+ {
+
ObjectTypeList[ObjectTypeIndex].ObjectAccessRights.RemainingAccessRights =
RemainingAccess;
+ }
+ }
+
/* Loop over the DACL to retrieve ACEs */
for (AceIndex = 0; AceIndex < Dacl->AceCount; AceIndex++)
{
@@ -246,7 +726,7 @@ SepAnalyzeAcesFromDacl(
*/
if (AccessCheckRights->RemainingAccessRights &
Access)
{
- DPRINT("SepAnalyzeAcesFromDacl(): Refuted access
0x%08lx\n", Access);
+ DPRINT("Refuted access 0x%08lx\n", Access);
AccessCheckRights->DeniedAccessRights |= Access;
break;
}
@@ -270,17 +750,107 @@ SepAnalyzeAcesFromDacl(
}
/* Remove the remaining rights */
- DPRINT("SepAnalyzeAcesFromDacl(): RemainingAccessRights
0x%08lx Access 0x%08lx\n", AccessCheckRights->RemainingAccessRights, Access);
+ DPRINT("RemainingAccessRights 0x%08lx Access
0x%08lx\n", AccessCheckRights->RemainingAccessRights, Access);
AccessCheckRights->RemainingAccessRights &= ~Access;
- DPRINT("SepAnalyzeAcesFromDacl(): RemainingAccessRights
0x%08lx\n", AccessCheckRights->RemainingAccessRights);
+ DPRINT("RemainingAccessRights 0x%08lx\n",
AccessCheckRights->RemainingAccessRights);
/* Track the granted access right */
AccessCheckRights->GrantedAccessRights |= Access;
}
}
+ else if (CurrentAce->Header.AceType ==
ACCESS_DENIED_OBJECT_ACE_TYPE)
+ {
+ /* Get the SID and object type from this ACE */
+ Sid = SepGetSidFromAce(ACCESS_DENIED_OBJECT_ACE_TYPE,
CurrentAce);
+ ObjectTypeGuid = SepGetObjectTypeGuidFromAce(CurrentAce, TRUE);
+ ASSERT(Sid);
+
+ if (SepSidInTokenEx(AccessToken, PrincipalSelfSid, Sid, TRUE,
IsTokenRestricted))
+ {
+ /* Get this access right from the ACE */
+ Access = CurrentAce->AccessMask;
+
+ /* Map this access right if it has a generic mask right */
+ if ((Access & GENERIC_ACCESS) &&
GenericMapping)
+ {
+ RtlMapGenericMask(&Access, GenericMapping);
+ }
+
+ /* If no list was passed treat this is as
ACCESS_DENIED_ACE_TYPE */
+ if (!ObjectTypeList && !ObjectTypeListLength)
+ {
+ if (AccessCheckRights->RemainingAccessRights &
Access)
+ {
+ DPRINT("Refuted access 0x%08lx\n",
Access);
+ AccessCheckRights->DeniedAccessRights |= Access;
+ break;
+ }
+ }
+ else
+ {
+ /*
+ * Otherwise evaluate the rights of the object for the
entire list.
+ * The function will signal us if the caller requested a
right that is
+ * denied by the ACE of an object in the list.
+ */
+ SepDenyAccessObjectTypeList(ObjectTypeList,
+ ObjectTypeListLength,
+ Access,
+ ObjectTypeGuid,
+ &BreakOnDeny);
+
+ /* We are acknowledged the caller requested a denied
right */
+ if (BreakOnDeny)
+ {
+ DPRINT("Refuted access 0x%08lx\n",
Access);
+ break;
+ }
+ }
+ }
+ }
+ else if (CurrentAce->Header.AceType ==
ACCESS_ALLOWED_OBJECT_ACE_TYPE)
+ {
+ /* Get the SID and object type from this ACE */
+ Sid = SepGetSidFromAce(ACCESS_ALLOWED_OBJECT_ACE_TYPE,
CurrentAce);
+ ObjectTypeGuid = SepGetObjectTypeGuidFromAce(CurrentAce,
FALSE);
+ ASSERT(Sid);
+
+ if (SepSidInTokenEx(AccessToken, PrincipalSelfSid, Sid, FALSE,
IsTokenRestricted))
+ {
+ /* Get this access right from the ACE */
+ Access = CurrentAce->AccessMask;
+
+ /* Map this access right if it has a generic mask right */
+ if ((Access & GENERIC_ACCESS) &&
GenericMapping)
+ {
+ RtlMapGenericMask(&Access, GenericMapping);
+ }
+
+ /* If no list was passed treat this is as
ACCESS_ALLOWED_ACE_TYPE */
+ if (!ObjectTypeList && !ObjectTypeListLength)
+ {
+ /* Remove the remaining rights */
+ DPRINT("RemainingAccessRights 0x%08lx Access
0x%08lx\n", AccessCheckRights->RemainingAccessRights, Access);
+ AccessCheckRights->RemainingAccessRights &=
~Access;
+ DPRINT("RemainingAccessRights 0x%08lx\n",
AccessCheckRights->RemainingAccessRights);
+
+ /* Track the granted access right */
+ AccessCheckRights->GrantedAccessRights |= Access;
+ }
+ else
+ {
+ /* Otherwise evaluate the rights of the object for the
entire list */
+ SepAllowAccessObjectTypeList(ObjectTypeList,
+ ObjectTypeListLength,
+ Access,
+ TRUE,
+ ObjectTypeGuid);
+ }
+ }
+ }
else
{
- DPRINT1("SepAnalyzeAcesFromDacl(): Unsupported ACE type
0x%lx\n", CurrentAce->Header.AceType);
+ DPRINT1("Unsupported ACE type 0x%lx\n",
CurrentAce->Header.AceType);
}
}
}
@@ -296,11 +866,10 @@ SepAnalyzeAcesFromDacl(
/**
* @brief
- * Private function that determines whether security access rights can be given
- * to the calling thread in order to access an object depending on the security
- * descriptor and other security context entities, such as an owner. This
- * function is the heart and brain of the whole access check algorithm in
- * the kernel.
+ * Private worker function that determines whether security access rights can be
+ * givento the calling thread in order to access an object depending on the
+ * security descriptor and other security context entities, such as an owner. This
+ * function is the heart and brain of the whole access check algorithm in the kernel.
*
* @param[in] ClientAccessToken
* A pointer to a client (thread) access token that requests access rights
@@ -368,15 +937,15 @@ SepAnalyzeAcesFromDacl(
* Returns TRUE if access onto the specific object is allowed, FALSE
* otherwise.
*/
+static
BOOLEAN
-NTAPI
-SepAccessCheck(
+SepAccessCheckWorker(
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
_In_opt_ PACCESS_TOKEN ClientAccessToken,
_In_ PACCESS_TOKEN PrimaryAccessToken,
_In_opt_ PSID PrincipalSelfSid,
_In_ ACCESS_MASK DesiredAccess,
- _In_opt_ POBJECT_TYPE_LIST ObjectTypeList,
+ _In_opt_ POBJECT_TYPE_LIST_INTERNAL ObjectTypeList,
_In_ ULONG ObjectTypeListLength,
_In_ ACCESS_MASK PreviouslyGrantedAccess,
_In_ PGENERIC_MAPPING GenericMapping,
@@ -387,12 +956,16 @@ SepAccessCheck(
_Out_ PNTSTATUS AccessStatusList)
{
ACCESS_MASK RemainingAccess;
- ULONG ResultListLength;
+ ACCESS_MASK WantedRights;
+ ACCESS_MASK MaskDesired;
+ ACCESS_MASK GrantedRights = 0;
ULONG ResultListIndex;
+ ULONG ObjectTypeIndex;
PACL Dacl;
BOOLEAN Present;
BOOLEAN Defaulted;
NTSTATUS Status;
+ BOOLEAN AccessIsGranted = FALSE;
PACCESS_TOKEN Token = NULL;
ACCESS_CHECK_RIGHTS AccessCheckRights = {0};
@@ -408,7 +981,7 @@ SepAccessCheck(
if (!PreviouslyGrantedAccess)
{
/* Then there's nothing to give */
- DPRINT1("SepAccessCheck(): The caller has no previously granted access
gained!\n");
+ DPRINT1("The caller has no previously granted access gained!\n");
Status = STATUS_ACCESS_DENIED;
goto ReturnCommonStatus;
}
@@ -427,6 +1000,23 @@ SepAccessCheck(
/* Initialize remaining access rights */
RemainingAccess = DesiredAccess;
+ /*
+ * Initialize the required rights if the caller wants to know access
+ * for the object and each sub-object in the list.
+ */
+ if (UseResultList)
+ {
+ if (DesiredAccess & MAXIMUM_ALLOWED)
+ {
+ WantedRights = (DesiredAccess | PreviouslyGrantedAccess) &
~MAXIMUM_ALLOWED;
+ MaskDesired = ~MAXIMUM_ALLOWED;
+ }
+ else
+ {
+ WantedRights = MaskDesired = DesiredAccess | PreviouslyGrantedAccess;
+ }
+ }
+
/*
* Obtain the token provided by the caller. Client (or also
* called impersonation or thread) token takes precedence over
@@ -501,15 +1091,19 @@ SepAccessCheck(
}
else
{
- DPRINT1("SepAccessCheck(): The DACL has no ACEs and the caller has no
previously granted access!\n");
+ DPRINT1("The DACL has no ACEs and the caller has no previously granted
access!\n");
PreviouslyGrantedAccess = 0;
Status = STATUS_ACCESS_DENIED;
}
goto ReturnCommonStatus;
}
- /* Determine the MAXIMUM_ALLOWED access rights according to the DACL */
- if (DesiredAccess & MAXIMUM_ALLOWED)
+ /*
+ * Determine the MAXIMUM_ALLOWED access rights according to the DACL.
+ * Or if the caller is supplying a list of object types then determine
+ * the rights of each object on that list.
+ */
+ if ((DesiredAccess & MAXIMUM_ALLOWED) || UseResultList)
{
/* Perform access checks against ACEs from this DACL */
SepAnalyzeAcesFromDacl(AccessCheckMaximum,
@@ -522,6 +1116,7 @@ SepAccessCheck(
GenericMapping,
ObjectTypeList,
ObjectTypeListLength,
+ UseResultList,
&AccessCheckRights);
/*
@@ -540,33 +1135,145 @@ SepAccessCheck(
GenericMapping,
ObjectTypeList,
ObjectTypeListLength,
+ UseResultList,
&AccessCheckRights);
}
- /* Fail if some rights have not been granted */
- RemainingAccess &= ~(MAXIMUM_ALLOWED |
AccessCheckRights.GrantedAccessRights);
- if (RemainingAccess != 0)
+ /* The caller did not provide an object type list, check access only for that
object */
+ if (!ObjectTypeList && !ObjectTypeListLength)
{
- DPRINT1("SepAccessCheck(): Failed to grant access rights.
RemainingAccess = 0x%08lx DesiredAccess = 0x%08lx\n", RemainingAccess,
DesiredAccess);
- PreviouslyGrantedAccess = 0;
- Status = STATUS_ACCESS_DENIED;
- goto ReturnCommonStatus;
- }
+ /* Fail if some rights have not been granted */
+ RemainingAccess &= ~(MAXIMUM_ALLOWED |
AccessCheckRights.GrantedAccessRights);
+ if (RemainingAccess != 0)
+ {
+ DPRINT1("Failed to grant access rights. RemainingAccess = 0x%08lx
DesiredAccess = 0x%08lx\n", RemainingAccess, DesiredAccess);
+ PreviouslyGrantedAccess = 0;
+ Status = STATUS_ACCESS_DENIED;
+ goto ReturnCommonStatus;
+ }
- /* Set granted access right and access status */
- PreviouslyGrantedAccess |= AccessCheckRights.GrantedAccessRights;
- if (PreviouslyGrantedAccess != 0)
- {
- Status = STATUS_SUCCESS;
+ /* Set granted access right and access status */
+ PreviouslyGrantedAccess |= AccessCheckRights.GrantedAccessRights;
+ if (PreviouslyGrantedAccess != 0)
+ {
+ Status = STATUS_SUCCESS;
+ }
+ else
+ {
+ DPRINT1("Failed to grant access rights. PreviouslyGrantedAccess == 0
DesiredAccess = %08lx\n", DesiredAccess);
+ Status = STATUS_ACCESS_DENIED;
+ }
+
+ /* We are done here */
+ goto ReturnCommonStatus;
}
- else
+ else if (!UseResultList)
{
- DPRINT1("SepAccessCheck(): Failed to grant access rights.
PreviouslyGrantedAccess == 0 DesiredAccess = %08lx\n", DesiredAccess);
- Status = STATUS_ACCESS_DENIED;
- }
+ /*
+ * We have a list but the caller wants to know if access can be granted
+ * to an object in the list. Access will either be granted or denied
+ * to the whole hierarchy of the list. Look for every object in the list
+ * that has granted access rights and collect them.
+ */
+ for (ObjectTypeIndex = 0;
+ ObjectTypeIndex < ObjectTypeListLength;
+ ObjectTypeIndex++)
+ {
+ if
(ObjectTypeList[ObjectTypeIndex].ObjectAccessRights.GrantedAccessRights != 0)
+ {
+ GrantedRights |=
ObjectTypeList[ObjectTypeIndex].ObjectAccessRights.GrantedAccessRights;
+ }
+ }
- /* We have successfully granted all the rights */
- goto ReturnCommonStatus;
+ /* Now check if acccess can be granted */
+ RemainingAccess &= ~(MAXIMUM_ALLOWED | GrantedRights);
+ if (RemainingAccess != 0)
+ {
+ DPRINT1("Failed to grant access rights to the whole object hierarchy
list. RemainingAccess = 0x%08lx DesiredAccess = 0x%08lx\n",
+ RemainingAccess, DesiredAccess);
+ PreviouslyGrantedAccess = 0;
+ Status = STATUS_ACCESS_DENIED;
+ goto ReturnCommonStatus;
+ }
+
+ /* Set granted access right and access status */
+ PreviouslyGrantedAccess |= GrantedRights;
+ if (PreviouslyGrantedAccess != 0)
+ {
+ Status = STATUS_SUCCESS;
+ }
+ else
+ {
+ DPRINT1("Failed to grant access rights to the whole object hierarchy
list. PreviouslyGrantedAccess == 0 DesiredAccess = %08lx\n",
+ DesiredAccess);
+ Status = STATUS_ACCESS_DENIED;
+ }
+
+ /* We are done here */
+ goto ReturnCommonStatus;
+ }
+ else
+ {
+ /*
+ * We have a list and the caller wants to know access for each
+ * sub-object in the list. Report the access status and granted
+ * rights for the object and each sub-object in the list.
+ */
+ for (ObjectTypeIndex = 0;
+ ObjectTypeIndex < ObjectTypeListLength;
+ ObjectTypeIndex++)
+ {
+ /* Check if we have some rights */
+ GrantedRights =
(ObjectTypeList[ObjectTypeIndex].ObjectAccessRights.GrantedAccessRights |
PreviouslyGrantedAccess) & MaskDesired;
+ if (GrantedRights != 0)
+ {
+ /*
+ * If we still have some remaining rights to grant the ultimate
+ * conclusion is that the caller has no access to the object
itself.
+ */
+ RemainingAccess = (~GrantedRights & WantedRights);
+ if (RemainingAccess != 0)
+ {
+ DPRINT1("Failed to grant access rights at specific object at
index %lu. RemainingAccess = 0x%08lx DesiredAccess = 0x%08lx\n",
+ ObjectTypeIndex, RemainingAccess, DesiredAccess);
+ AccessStatusList[ObjectTypeIndex] = STATUS_ACCESS_DENIED;
+ }
+ else
+ {
+ AccessStatusList[ObjectTypeIndex] = STATUS_SUCCESS;
+ }
+ }
+ else
+ {
+ /* No access is given */
+ DPRINT1("Failed to grant access rights at specific object at
index %lu. No access is given\n", ObjectTypeIndex);
+ AccessStatusList[ObjectTypeIndex] = STATUS_ACCESS_DENIED;
+ }
+
+ /* Return the access rights to the caller */
+ GrantedAccessList[ObjectTypeIndex] = GrantedRights;
+ }
+
+ /*
+ * We have built a list of access statuses for each object but
+ * we still need to figure out the common status for the
+ * function. The same status code will be used to check if
+ * we should report any security debug stuff once we are done.
+ */
+ Status = STATUS_SUCCESS;
+ for (ResultListIndex = 0; ResultListIndex < ObjectTypeListLength;
ResultListIndex++)
+ {
+ /* There is at least one sub-object of which access cannot be granted
*/
+ if (AccessStatusList[ResultListIndex] == STATUS_ACCESS_DENIED)
+ {
+ Status = AccessStatusList[ResultListIndex];
+ break;
+ }
+ }
+
+ /* We are done here */
+ goto ReturnCommonStatus;
+ }
}
/* Grant rights according to the DACL */
@@ -580,15 +1287,45 @@ SepAccessCheck(
GenericMapping,
ObjectTypeList,
ObjectTypeListLength,
+ UseResultList,
&AccessCheckRights);
- /* Fail if some rights have not been granted */
- if (AccessCheckRights.RemainingAccessRights != 0)
+ /* The caller did not provide an object type list, check access only for that object
*/
+ if (!ObjectTypeList && !ObjectTypeListLength)
{
- DPRINT1("SepAccessCheck(): Failed to grant access rights. RemainingAccess =
0x%08lx DesiredAccess = 0x%08lx\n", AccessCheckRights.RemainingAccessRights,
DesiredAccess);
- PreviouslyGrantedAccess = 0;
- Status = STATUS_ACCESS_DENIED;
- goto ReturnCommonStatus;
+ /* Fail if some rights have not been granted */
+ if (AccessCheckRights.RemainingAccessRights != 0)
+ {
+ DPRINT1("Failed to grant access rights. RemainingAccess = 0x%08lx
DesiredAccess = 0x%08lx\n", AccessCheckRights.RemainingAccessRights,
DesiredAccess);
+ PreviouslyGrantedAccess = 0;
+ Status = STATUS_ACCESS_DENIED;
+ goto ReturnCommonStatus;
+ }
+ }
+ else
+ {
+ /*
+ * We have an object type list, look for the object of which
+ * remaining rights are all granted.
+ */
+ for (ObjectTypeIndex = 0;
+ ObjectTypeIndex < ObjectTypeListLength;
+ ObjectTypeIndex++)
+ {
+ if (ObjectTypeList[ObjectTypeIndex].ObjectAccessRights.RemainingAccessRights
== 0)
+ {
+ AccessIsGranted = TRUE;
+ break;
+ }
+ }
+
+ if (!AccessIsGranted)
+ {
+ DPRINT1("Failed to grant access rights to the whole object hierarchy
list. DesiredAccess = 0x%08lx\n", DesiredAccess);
+ PreviouslyGrantedAccess = 0;
+ Status = STATUS_ACCESS_DENIED;
+ goto ReturnCommonStatus;
+ }
}
/*
@@ -607,15 +1344,49 @@ SepAccessCheck(
GenericMapping,
ObjectTypeList,
ObjectTypeListLength,
+ UseResultList,
&AccessCheckRights);
- /* Fail if some rights have not been granted */
- if (AccessCheckRights.RemainingAccessRights != 0)
+ /* The caller did not provide an object type list, check access only for that
object */
+ if (!ObjectTypeList && !ObjectTypeListLength)
{
- DPRINT1("SepAccessCheck(): Failed to grant access rights.
RemainingAccess = 0x%08lx DesiredAccess = 0x%08lx\n",
AccessCheckRights.RemainingAccessRights, DesiredAccess);
- PreviouslyGrantedAccess = 0;
- Status = STATUS_ACCESS_DENIED;
- goto ReturnCommonStatus;
+ /* Fail if some rights have not been granted */
+ if (AccessCheckRights.RemainingAccessRights != 0)
+ {
+ DPRINT1("Failed to grant access rights. RemainingAccess = 0x%08lx
DesiredAccess = 0x%08lx\n", AccessCheckRights.RemainingAccessRights,
DesiredAccess);
+ PreviouslyGrantedAccess = 0;
+ Status = STATUS_ACCESS_DENIED;
+ goto ReturnCommonStatus;
+ }
+ }
+ else
+ {
+ /*
+ * We have an object type list, look for the object of which remaining
+ * rights are all granted. The user may have access to the requested
+ * object but on a restricted token case the user is only granted partial
+ * access. If access is denied to restricted SIDs, the bottom line is that
+ * access is denied to the user.
+ */
+ AccessIsGranted = FALSE;
+ for (ObjectTypeIndex = 0;
+ ObjectTypeIndex < ObjectTypeListLength;
+ ObjectTypeIndex++)
+ {
+ if
(ObjectTypeList[ObjectTypeIndex].ObjectAccessRights.RemainingAccessRights == 0)
+ {
+ AccessIsGranted = TRUE;
+ break;
+ }
+ }
+
+ if (!AccessIsGranted)
+ {
+ DPRINT1("Failed to grant access rights to the whole object hierarchy
list. DesiredAccess = 0x%08lx\n", DesiredAccess);
+ PreviouslyGrantedAccess = 0;
+ Status = STATUS_ACCESS_DENIED;
+ goto ReturnCommonStatus;
+ }
}
}
@@ -625,7 +1396,7 @@ SepAccessCheck(
/* Fail if no rights have been granted */
if (PreviouslyGrantedAccess == 0)
{
- DPRINT1("SepAccessCheck(): Failed to grant access rights.
PreviouslyGrantedAccess == 0 DesiredAccess = %08lx\n", DesiredAccess);
+ DPRINT1("Failed to grant access rights. PreviouslyGrantedAccess == 0
DesiredAccess = %08lx\n", DesiredAccess);
Status = STATUS_ACCESS_DENIED;
goto ReturnCommonStatus;
}
@@ -637,11 +1408,10 @@ SepAccessCheck(
Status = STATUS_SUCCESS;
ReturnCommonStatus:
- ResultListLength = UseResultList ? ObjectTypeListLength : 1;
- for (ResultListIndex = 0; ResultListIndex < ResultListLength; ResultListIndex++)
+ if (!UseResultList)
{
- GrantedAccessList[ResultListIndex] = PreviouslyGrantedAccess;
- AccessStatusList[ResultListIndex] = Status;
+ *GrantedAccessList = PreviouslyGrantedAccess;
+ *AccessStatusList = Status;
}
#if DBG
@@ -650,67 +1420,25 @@ ReturnCommonStatus:
{
SepDumpSdDebugInfo(SecurityDescriptor);
SepDumpTokenDebugInfo(Token);
- SepDumpAccessRightsStats(&AccessCheckRights);
+
+ if (ObjectTypeList && (ObjectTypeListLength != 0))
+ {
+ SepDumpAccessAndStatusList(GrantedAccessList,
+ AccessStatusList,
+ UseResultList,
+ ObjectTypeList,
+ ObjectTypeListLength);
+ }
+ else
+ {
+ SepDumpAccessRightsStats(&AccessCheckRights);
+ }
}
#endif
return NT_SUCCESS(Status);
}
-/**
- * @brief
- * Retrieves the main user from a security descriptor.
- *
- * @param[in] SecurityDescriptor
- * A valid allocated security descriptor structure where the owner
- * is to be retrieved.
- *
- * @return
- * Returns a SID that represents the main user (owner).
- */
-static PSID
-SepGetSDOwner(
- _In_ PSECURITY_DESCRIPTOR _SecurityDescriptor)
-{
- PISECURITY_DESCRIPTOR SecurityDescriptor = _SecurityDescriptor;
- PSID Owner;
-
- if (SecurityDescriptor->Control & SE_SELF_RELATIVE)
- Owner = (PSID)((ULONG_PTR)SecurityDescriptor->Owner +
- (ULONG_PTR)SecurityDescriptor);
- else
- Owner = (PSID)SecurityDescriptor->Owner;
-
- return Owner;
-}
-
-/**
- * @brief
- * Retrieves the group from a security descriptor.
- *
- * @param[in] SecurityDescriptor
- * A valid allocated security descriptor structure where the group
- * is to be retrieved.
- *
- * @return
- * Returns a SID that represents a group.
- */
-static PSID
-SepGetSDGroup(
- _In_ PSECURITY_DESCRIPTOR _SecurityDescriptor)
-{
- PISECURITY_DESCRIPTOR SecurityDescriptor = _SecurityDescriptor;
- PSID Group;
-
- if (SecurityDescriptor->Control & SE_SELF_RELATIVE)
- Group = (PSID)((ULONG_PTR)SecurityDescriptor->Group +
- (ULONG_PTR)SecurityDescriptor);
- else
- Group = (PSID)SecurityDescriptor->Group;
-
- return Group;
-}
-
/**
* @brief
* Retrieves the length size of a set list of privileges structure.
@@ -736,71 +1464,133 @@ SepGetPrivilegeSetLength(
(PrivilegeSet->PrivilegeCount - 1) *
sizeof(LUID_AND_ATTRIBUTES));
}
-/* PUBLIC FUNCTIONS ***********************************************************/
-
/**
* @brief
- * Determines whether security access rights can be given to an object
- * depending on the security descriptor and other security context
- * entities, such as an owner.
+ * Internal function that performs a security check against the
+ * client who requests access on a resource object. This function
+ * is used by access check NT system calls.
*
* @param[in] SecurityDescriptor
- * Security descriptor of the object that is being accessed.
+ * A pointer to a security descriptor that identifies the security
+ * information of an object being accessed. This function walks
+ * through this descriptor for any ACLs and respective access
+ * rights if access can be granted.
*
- * @param[in] SubjectSecurityContext
- * The captured subject security context.
+ * @param[in] ClientToken
+ * A handle to an access token, that identifies the client of which
+ * requests access to the target object.
*
- * @param[in] SubjectContextLocked
- * If set to TRUE, the caller acknowledges that the subject context
- * has already been locked by the caller himself. If set to FALSE,
- * the function locks the subject context.
+ * @param[in] PrincipalSelfSid
+ * A pointer to a principal self SID. This parameter can be NULL if
+ * the associated object being checked for access does not represent
+ * a principal.
*
* @param[in] DesiredAccess
- * Access right bitmask that the calling thread wants to acquire.
- *
- * @param[in] PreviouslyGrantedAccess
- * The access rights previously acquired in the past.
- *
- * @param[out] Privileges
- * The returned set of privileges.
+ * The access right bitmask where the client wants to acquire. This
+ * can be an OR'ed set of multiple access rights or MAXIMUM_ALLOWED
+ * to request all of possible access rights the target object allows.
+ * If only some rights were granted but not all the access is deemed
+ * as denied.
*
* @param[in] GenericMapping
* The generic mapping of access rights of an object type.
*
- * @param[in] AccessMode
- * The processor request level mode.
+ * @param[out] PrivilegeSet
+ * A pointer to a set of privileges that were used to perform the
+ * access check, returned to caller. This function will return no
+ * privileges (privilege count set to 0) if no privileges were used
+ * to accomplish the access check. This parameter must not be NULL!
+ *
+ * @param[in,out] PrivilegeSetLength
+ * The total length size of a set of privileges. This length represents
+ * the count of elements in the privilege set array.
+ *
+ * @param[in] ObjectTypeList
+ * A pointer to a given object type list. If this parameter is not NULL
+ * the function will perform an access check against the main object
+ * and sub-objects of this list. If this parameter is NULL and
+ * ObjectTypeListLength is 0, the function will perform a normal
+ * access check instead.
+ *
+ * @param[in] ObjectTypeListLength
+ * The length of the object type list array, pointed by ObjectTypeList.
+ * This length in question represents the number of elements in such array.
+ * This parameter must be 0 if no array list is provided.
+ *
+ * @param[in] UseResultList
+ * If this parameter is set to TRUE, the function will return the GrantedAccess
+ * and AccessStatus parameter as arrays of granted rights and status value for
+ * each individual object element pointed by ObjectTypeList.
*
* @param[out] GrantedAccess
- * A list of granted access rights.
+ * A pointer to granted access rights, returned to the caller. If ObjectTypeList
+ * is not NULL this paramater is an array of granted access rights for the object
+ * and each individual sub-object of the list.
*
* @param[out] AccessStatus
- * The returned status code specifying why access cannot be made
- * onto an object (if said access is denied in the first place).
+ * A pointer to a status code, returned to the caller. This status code
+ * represents whether access is granted or denied to the client on the
+ * target object. The difference between the status code of the function
+ * is that code indicates whether the function has successfully completed
+ * the access check operation. If ObjectTypeList is not NULL, this
+ * parameter is an array of access status for the object and each individual
+ * sub-object of the list.
*
* @return
- * Returns TRUE if access onto the specific object is allowed, FALSE
- * otherwise.
+ * Returns STATUS_SUCCESS if access check has been done without problems
+ * and that the object can be accessed. STATUS_GENERIC_NOT_MAPPED is returned
+ * if no generic access right is mapped. STATUS_NO_IMPERSONATION_TOKEN is returned
+ * if the token from the handle is not an impersonation token.
+ * STATUS_BAD_IMPERSONATION_LEVEL is returned if the token cannot be impersonated
+ * because the current security impersonation level doesn't permit so.
+ * STATUS_INVALID_SECURITY_DESCR is returned if the security descriptor given
+ * to the call is not a valid one. STATUS_BUFFER_TOO_SMALL is returned if
+ * the buffer to the captured privileges has a length that is less than the required
+ * size of the set of privileges. STATUS_INVALID_PARAMETER is returned if the caller
+ * did not provide an object type list but the caller wanted to invoke an object type
+ * result list access check, or if the list is out of order or the list is invalid.
+ * A failure NTSTATUS code is returned otherwise.
+ *
+ * @remarks
+ * The function performs an access check against the object type list,
+ * if provided, depending on the UseResultList parameter. That is, if that
+ * parameter was set to TRUE the function will either grant or deny access
+ * to each individual sub-object and return an array of access status and
+ * granted rights for the corresponding object type list. Otherwise the function
+ * will grant or deny access to the object type list hierarchy as a whole.
*/
-BOOLEAN
-NTAPI
-SeAccessCheck(
+static
+NTSTATUS
+SepAccessCheck(
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
- _In_ PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
- _In_ BOOLEAN SubjectContextLocked,
+ _In_ HANDLE ClientToken,
+ _In_opt_ PSID PrincipalSelfSid,
_In_ ACCESS_MASK DesiredAccess,
- _In_ ACCESS_MASK PreviouslyGrantedAccess,
- _Out_ PPRIVILEGE_SET* Privileges,
_In_ PGENERIC_MAPPING GenericMapping,
- _In_ KPROCESSOR_MODE AccessMode,
+ _Out_writes_bytes_(*PrivilegeSetLength) PPRIVILEGE_SET PrivilegeSet,
+ _Inout_ PULONG PrivilegeSetLength,
+ _In_reads_opt_(ObjectTypeListLength) POBJECT_TYPE_LIST ObjectTypeList,
+ _In_ ULONG ObjectTypeListLength,
+ _In_ BOOLEAN UseResultList,
_Out_ PACCESS_MASK GrantedAccess,
_Out_ PNTSTATUS AccessStatus)
{
- BOOLEAN ret;
+ PSECURITY_DESCRIPTOR CapturedSecurityDescriptor = NULL;
+ POBJECT_TYPE_LIST_INTERNAL CapturedObjectTypeList = NULL;
+ PSID CapturedPrincipalSelfSid = NULL;
+ SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
+ KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
+ ACCESS_MASK PreviouslyGrantedAccess = 0;
+ PPRIVILEGE_SET Privileges = NULL;
+ ULONG CapturedPrivilegeSetLength, RequiredPrivilegeSetLength;
+ ULONG ResultListIndex;
+ PTOKEN Token;
+ NTSTATUS Status;
PAGED_CODE();
/* Check if this is kernel mode */
- if (AccessMode == KernelMode)
+ if (PreviousMode == KernelMode)
{
/* Check if kernel wants everything */
if (DesiredAccess & MAXIMUM_ALLOWED)
@@ -808,291 +1598,52 @@ SeAccessCheck(
/* Give it */
*GrantedAccess = GenericMapping->GenericAll;
*GrantedAccess |= (DesiredAccess &~ MAXIMUM_ALLOWED);
- *GrantedAccess |= PreviouslyGrantedAccess;
}
else
{
- /* Give the desired and previous access */
- *GrantedAccess = DesiredAccess | PreviouslyGrantedAccess;
+ /* Just give the desired access */
+ *GrantedAccess = DesiredAccess;
}
/* Success */
*AccessStatus = STATUS_SUCCESS;
- return TRUE;
- }
-
- /* Check if we didn't get an SD */
- if (!SecurityDescriptor)
- {
- /* Automatic failure */
- *AccessStatus = STATUS_ACCESS_DENIED;
- return FALSE;
- }
-
- /* Check for invalid impersonation */
- if ((SubjectSecurityContext->ClientToken) &&
- (SubjectSecurityContext->ImpersonationLevel < SecurityImpersonation))
- {
- *AccessStatus = STATUS_BAD_IMPERSONATION_LEVEL;
- return FALSE;
+ return STATUS_SUCCESS;
}
- /* Acquire the lock if needed */
- if (!SubjectContextLocked)
- SeLockSubjectContext(SubjectSecurityContext);
-
- /* Check if the token is the owner and grant WRITE_DAC and READ_CONTROL rights */
- if (DesiredAccess & (WRITE_DAC | READ_CONTROL | MAXIMUM_ALLOWED))
+ /* Protect probe in SEH */
+ _SEH2_TRY
{
- PACCESS_TOKEN Token = SubjectSecurityContext->ClientToken ?
- SubjectSecurityContext->ClientToken :
SubjectSecurityContext->PrimaryToken;
+ /* Probe all pointers */
+ ProbeForRead(GenericMapping, sizeof(GENERIC_MAPPING), sizeof(ULONG));
+ ProbeForRead(PrivilegeSetLength, sizeof(ULONG), sizeof(ULONG));
+ ProbeForWrite(PrivilegeSet, *PrivilegeSetLength, sizeof(ULONG));
- if (SepTokenIsOwner(Token,
- SecurityDescriptor,
- FALSE))
+ /*
+ * Probe the access and status list based on the way
+ * we are going to fill data in.
+ */
+ if (UseResultList)
{
- if (DesiredAccess & MAXIMUM_ALLOWED)
- PreviouslyGrantedAccess |= (WRITE_DAC | READ_CONTROL);
- else
- PreviouslyGrantedAccess |= (DesiredAccess & (WRITE_DAC |
READ_CONTROL));
-
- DesiredAccess &= ~(WRITE_DAC | READ_CONTROL);
- }
- }
+ /* Bail out on an empty list */
+ if (!ObjectTypeListLength)
+ {
+ DPRINT1("The object type list is empty\n");
+ _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
+ }
- if (DesiredAccess == 0)
- {
- *GrantedAccess = PreviouslyGrantedAccess;
- if (PreviouslyGrantedAccess == 0)
- {
- DPRINT1("Request for zero access to an object. Denying.\n");
- *AccessStatus = STATUS_ACCESS_DENIED;
- ret = FALSE;
+ ProbeForWrite(GrantedAccess, sizeof(ACCESS_MASK) * ObjectTypeListLength,
sizeof(ULONG));
+ ProbeForWrite(AccessStatus, sizeof(NTSTATUS) * ObjectTypeListLength,
sizeof(ULONG));
}
else
{
- *AccessStatus = STATUS_SUCCESS;
- ret = TRUE;
+ ProbeForWrite(GrantedAccess, sizeof(ACCESS_MASK), sizeof(ULONG));
+ ProbeForWrite(AccessStatus, sizeof(NTSTATUS), sizeof(ULONG));
}
+
+ /* Capture the privilege set length and the mapping */
+ CapturedPrivilegeSetLength = *PrivilegeSetLength;
}
- else
- {
- /* Call the internal function */
- ret = SepAccessCheck(SecurityDescriptor,
- SubjectSecurityContext->ClientToken,
- SubjectSecurityContext->PrimaryToken,
- NULL,
- DesiredAccess,
- NULL,
- 0,
- PreviouslyGrantedAccess,
- GenericMapping,
- AccessMode,
- FALSE,
- Privileges,
- GrantedAccess,
- AccessStatus);
- }
-
- /* Release the lock if needed */
- if (!SubjectContextLocked)
- SeUnlockSubjectContext(SubjectSecurityContext);
-
- return ret;
-}
-
-/**
- * @brief
- * Determines whether security access rights can be given to an object
- * depending on the security descriptor. Unlike the regular access check
- * procedure in the NT kernel, the fast traverse check is a faster way
- * to quickly check if access can be made into an object.
- *
- * @param[in] SecurityDescriptor
- * Security descriptor of the object that is being accessed.
- *
- * @param[in] AccessState
- * An access state to determine if the access token in the current
- * security context of the object is an restricted token.
- *
- * @param[in] DesiredAccess
- * The access right bitmask where the calling thread wants to acquire.
- *
- * @param[in] AccessMode
- * Process level request mode.
- *
- * @return
- * Returns TRUE if access onto the specific object is allowed, FALSE
- * otherwise.
- */
-BOOLEAN
-NTAPI
-SeFastTraverseCheck(
- _In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
- _In_ PACCESS_STATE AccessState,
- _In_ ACCESS_MASK DesiredAccess,
- _In_ KPROCESSOR_MODE AccessMode)
-{
- PACL Dacl;
- ULONG AceIndex;
- PKNOWN_ACE Ace;
-
- PAGED_CODE();
-
- ASSERT(AccessMode != KernelMode);
-
- if (SecurityDescriptor == NULL)
- return FALSE;
-
- /* Get DACL */
- Dacl = SepGetDaclFromDescriptor(SecurityDescriptor);
- /* If no DACL, grant access */
- if (Dacl == NULL)
- return TRUE;
-
- /* No ACE -> Deny */
- if (!Dacl->AceCount)
- return FALSE;
-
- /* Can't perform the check on restricted token */
- if (AccessState->Flags & TOKEN_IS_RESTRICTED)
- return FALSE;
-
- /* Browse the ACEs */
- for (AceIndex = 0, Ace = (PKNOWN_ACE)((ULONG_PTR)Dacl + sizeof(ACL));
- AceIndex < Dacl->AceCount;
- AceIndex++, Ace = (PKNOWN_ACE)((ULONG_PTR)Ace + Ace->Header.AceSize))
- {
- if (Ace->Header.AceFlags & INHERIT_ONLY_ACE)
- continue;
-
- /* If access-allowed ACE */
- if (Ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
- {
- /* Check if all accesses are granted */
- if (!(Ace->Mask & DesiredAccess))
- continue;
-
- /* Check SID and grant access if matching */
- if (RtlEqualSid(SeWorldSid, &(Ace->SidStart)))
- return TRUE;
- }
- /* If access-denied ACE */
- else if (Ace->Header.AceType == ACCESS_DENIED_ACE_TYPE)
- {
- /* Here, only check if it denies any access wanted and deny if so */
- if (Ace->Mask & DesiredAccess)
- return FALSE;
- }
- }
-
- /* Faulty, deny */
- return FALSE;
-}
-
-/* SYSTEM CALLS ***************************************************************/
-
-/**
- * @brief
- * Determines whether security access rights can be given to an object
- * depending on the security descriptor and a valid handle to an access
- * token.
- *
- * @param[in] SecurityDescriptor
- * Security descriptor of the object that is being accessed.
- *
- * @param[in] TokenHandle
- * A handle to a token.
- *
- * @param[in] DesiredAccess
- * The access right bitmask where the calling thread wants to acquire.
- *
- * @param[in] GenericMapping
- * The generic mapping of access rights of an object type.
- *
- * @param[out] PrivilegeSet
- * The returned set of privileges.
- *
- * @param[in,out] PrivilegeSetLength
- * The total length size of a set of privileges.
- *
- * @param[out] GrantedAccess
- * A list of granted access rights.
- *
- * @param[out] AccessStatus
- * The returned status code specifying why access cannot be made
- * onto an object (if said access is denied in the first place).
- *
- * @return
- * Returns STATUS_SUCCESS if access check has been done without problems
- * and that the object can be accessed. STATUS_GENERIC_NOT_MAPPED is returned
- * if no generic access right is mapped. STATUS_NO_IMPERSONATION_TOKEN is returned
- * if the token from the handle is not an impersonation token.
- * STATUS_BAD_IMPERSONATION_LEVEL is returned if the token cannot be impersonated
- * because the current security impersonation level doesn't permit so.
- * STATUS_INVALID_SECURITY_DESCR is returned if the security descriptor given
- * to the call is not a valid one. STATUS_BUFFER_TOO_SMALL is returned if
- * the buffer to the captured privileges has a length that is less than the required
- * size of the set of privileges. A failure NTSTATUS code is returned otherwise.
- */
-NTSTATUS
-NTAPI
-NtAccessCheck(
- _In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
- _In_ HANDLE TokenHandle,
- _In_ ACCESS_MASK DesiredAccess,
- _In_ PGENERIC_MAPPING GenericMapping,
- _Out_opt_ PPRIVILEGE_SET PrivilegeSet,
- _Inout_ PULONG PrivilegeSetLength,
- _Out_ PACCESS_MASK GrantedAccess,
- _Out_ PNTSTATUS AccessStatus)
-{
- PSECURITY_DESCRIPTOR CapturedSecurityDescriptor = NULL;
- SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
- KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
- ACCESS_MASK PreviouslyGrantedAccess = 0;
- PPRIVILEGE_SET Privileges = NULL;
- ULONG CapturedPrivilegeSetLength, RequiredPrivilegeSetLength;
- PTOKEN Token;
- NTSTATUS Status;
-
- PAGED_CODE();
-
- /* Check if this is kernel mode */
- if (PreviousMode == KernelMode)
- {
- /* Check if kernel wants everything */
- if (DesiredAccess & MAXIMUM_ALLOWED)
- {
- /* Give it */
- *GrantedAccess = GenericMapping->GenericAll;
- *GrantedAccess |= (DesiredAccess &~ MAXIMUM_ALLOWED);
- }
- else
- {
- /* Just give the desired access */
- *GrantedAccess = DesiredAccess;
- }
-
- /* Success */
- *AccessStatus = STATUS_SUCCESS;
- return STATUS_SUCCESS;
- }
-
- /* Protect probe in SEH */
- _SEH2_TRY
- {
- /* Probe all pointers */
- ProbeForRead(GenericMapping, sizeof(GENERIC_MAPPING), sizeof(ULONG));
- ProbeForRead(PrivilegeSetLength, sizeof(ULONG), sizeof(ULONG));
- ProbeForWrite(PrivilegeSet, *PrivilegeSetLength, sizeof(ULONG));
- ProbeForWrite(GrantedAccess, sizeof(ACCESS_MASK), sizeof(ULONG));
- ProbeForWrite(AccessStatus, sizeof(NTSTATUS), sizeof(ULONG));
-
- /* Capture the privilege set length and the mapping */
- CapturedPrivilegeSetLength = *PrivilegeSetLength;
- }
- _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return the exception code */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
@@ -1101,10 +1652,13 @@ NtAccessCheck(
/* Check for unmapped access rights */
if (DesiredAccess & (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE |
GENERIC_ALL))
+ {
+ DPRINT1("Some generic rights are not mapped\n");
return STATUS_GENERIC_NOT_MAPPED;
+ }
/* Reference the token */
- Status = ObReferenceObjectByHandle(TokenHandle,
+ Status = ObReferenceObjectByHandle(ClientToken,
TOKEN_QUERY,
SeTokenObjectType,
PreviousMode,
@@ -1112,7 +1666,7 @@ NtAccessCheck(
NULL);
if (!NT_SUCCESS(Status))
{
- DPRINT("Failed to reference token (Status %lx)\n", Status);
+ DPRINT1("Failed to reference token (Status 0x%08lx)\n", Status);
return Status;
}
@@ -1127,11 +1681,23 @@ NtAccessCheck(
/* Check the impersonation level */
if (Token->ImpersonationLevel < SecurityIdentification)
{
- DPRINT("Impersonation level < SecurityIdentification\n");
+ DPRINT1("Impersonation level < SecurityIdentification\n");
ObDereferenceObject(Token);
return STATUS_BAD_IMPERSONATION_LEVEL;
}
+ /* Capture the object type list, the list is probed by the function itself */
+ Status = SeCaptureObjectTypeList(ObjectTypeList,
+ ObjectTypeListLength,
+ PreviousMode,
+ &CapturedObjectTypeList);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("Failed to capture the object type list (Status 0x%08lx)\n",
Status);
+ ObDereferenceObject(Token);
+ return Status;
+ }
+
/* Check for ACCESS_SYSTEM_SECURITY and WRITE_OWNER access */
Status = SePrivilegePolicyCheck(&DesiredAccess,
&PreviouslyGrantedAccess,
@@ -1141,10 +1707,29 @@ NtAccessCheck(
PreviousMode);
if (!NT_SUCCESS(Status))
{
- DPRINT("SePrivilegePolicyCheck failed (Status 0x%08lx)\n", Status);
+ DPRINT1("SePrivilegePolicyCheck failed (Status 0x%08lx)\n", Status);
+ SeReleaseObjectTypeList(CapturedObjectTypeList, PreviousMode);
ObDereferenceObject(Token);
- *AccessStatus = Status;
- *GrantedAccess = 0;
+
+ /*
+ * The caller does not have the required access to do an access check.
+ * Propagate the access and status for the whole hierarchy of the list
+ * or just to single target object.
+ */
+ if (UseResultList)
+ {
+ for (ResultListIndex = 0; ResultListIndex < ObjectTypeListLength;
ResultListIndex++)
+ {
+ AccessStatus[ResultListIndex] = Status;
+ GrantedAccess[ResultListIndex] = 0;
+ }
+ }
+ else
+ {
+ *AccessStatus = Status;
+ *GrantedAccess = 0;
+ }
+
return STATUS_SUCCESS;
}
@@ -1159,8 +1744,9 @@ NtAccessCheck(
/* Fail if the privilege set buffer is too small */
if (CapturedPrivilegeSetLength < RequiredPrivilegeSetLength)
{
- ObDereferenceObject(Token);
SeFreePrivileges(Privileges);
+ SeReleaseObjectTypeList(CapturedObjectTypeList, PreviousMode);
+ ObDereferenceObject(Token);
*PrivilegeSetLength = RequiredPrivilegeSetLength;
return STATUS_BUFFER_TOO_SMALL;
}
@@ -1180,6 +1766,7 @@ NtAccessCheck(
/* Fail if the privilege set buffer is too small */
if (CapturedPrivilegeSetLength < sizeof(PRIVILEGE_SET))
{
+ SeReleaseObjectTypeList(CapturedObjectTypeList, PreviousMode);
ObDereferenceObject(Token);
*PrivilegeSetLength = sizeof(PRIVILEGE_SET);
return STATUS_BUFFER_TOO_SMALL;
@@ -1198,7 +1785,8 @@ NtAccessCheck(
&CapturedSecurityDescriptor);
if (!NT_SUCCESS(Status))
{
- DPRINT("Failed to capture the Security Descriptor\n");
+ DPRINT1("Failed to capture the Security Descriptor\n");
+ SeReleaseObjectTypeList(CapturedObjectTypeList, PreviousMode);
ObDereferenceObject(Token);
return Status;
}
@@ -1206,23 +1794,45 @@ NtAccessCheck(
/* Check the captured security descriptor */
if (CapturedSecurityDescriptor == NULL)
{
- DPRINT("Security Descriptor is NULL\n");
+ DPRINT1("Security Descriptor is NULL\n");
+ SeReleaseObjectTypeList(CapturedObjectTypeList, PreviousMode);
ObDereferenceObject(Token);
return STATUS_INVALID_SECURITY_DESCR;
}
/* Check security descriptor for valid owner and group */
- if (SepGetSDOwner(CapturedSecurityDescriptor) == NULL ||
- SepGetSDGroup(CapturedSecurityDescriptor) == NULL)
+ if (SepGetOwnerFromDescriptor(CapturedSecurityDescriptor) == NULL ||
+ SepGetGroupFromDescriptor(CapturedSecurityDescriptor) == NULL)
{
- DPRINT("Security Descriptor does not have a valid group or owner\n");
+ DPRINT1("Security Descriptor does not have a valid group or
owner\n");
SeReleaseSecurityDescriptor(CapturedSecurityDescriptor,
PreviousMode,
FALSE);
+ SeReleaseObjectTypeList(CapturedObjectTypeList, PreviousMode);
ObDereferenceObject(Token);
return STATUS_INVALID_SECURITY_DESCR;
}
+ /* Capture the principal self SID if we have one */
+ if (PrincipalSelfSid)
+ {
+ Status = SepCaptureSid(PrincipalSelfSid,
+ PreviousMode,
+ PagedPool,
+ TRUE,
+ &CapturedPrincipalSelfSid);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("Failed to capture the principal self SID (Status
0x%08lx)\n", Status);
+ SeReleaseSecurityDescriptor(CapturedSecurityDescriptor,
+ PreviousMode,
+ FALSE);
+ SeReleaseObjectTypeList(CapturedObjectTypeList, PreviousMode);
+ ObDereferenceObject(Token);
+ return Status;
+ }
+ }
+
/* Set up the subject context, and lock it */
SeCaptureSubjectContext(&SubjectSecurityContext);
@@ -1245,37 +1855,60 @@ NtAccessCheck(
if (DesiredAccess == 0)
{
- *GrantedAccess = PreviouslyGrantedAccess;
- *AccessStatus = STATUS_SUCCESS;
+ /*
+ * Propagate the access and status for the whole hierarchy
+ * of the list or just to single target object.
+ */
+ if (UseResultList)
+ {
+ for (ResultListIndex = 0; ResultListIndex < ObjectTypeListLength;
ResultListIndex++)
+ {
+ AccessStatus[ResultListIndex] = STATUS_SUCCESS;
+ GrantedAccess[ResultListIndex] = PreviouslyGrantedAccess;
+ }
+ }
+ else
+ {
+ *GrantedAccess = PreviouslyGrantedAccess;
+ *AccessStatus = STATUS_SUCCESS;
+ }
}
else
{
/* Now perform the access check */
- SepAccessCheck(CapturedSecurityDescriptor,
- Token,
- &SubjectSecurityContext.PrimaryToken,
- NULL,
- DesiredAccess,
- NULL,
- 0,
- PreviouslyGrantedAccess,
- GenericMapping,
- PreviousMode,
- FALSE,
- NULL,
- GrantedAccess,
- AccessStatus);
+ SepAccessCheckWorker(CapturedSecurityDescriptor,
+ Token,
+ &SubjectSecurityContext.PrimaryToken,
+ CapturedPrincipalSelfSid,
+ DesiredAccess,
+ CapturedObjectTypeList,
+ ObjectTypeListLength,
+ PreviouslyGrantedAccess,
+ GenericMapping,
+ PreviousMode,
+ UseResultList,
+ NULL,
+ GrantedAccess,
+ AccessStatus);
}
/* Release subject context and unlock the token */
SeReleaseSubjectContext(&SubjectSecurityContext);
SepReleaseTokenLock(Token);
+ /* Release the caputed principal self SID */
+ SepReleaseSid(CapturedPrincipalSelfSid,
+ PreviousMode,
+ TRUE);
+
/* Release the captured security descriptor */
SeReleaseSecurityDescriptor(CapturedSecurityDescriptor,
PreviousMode,
FALSE);
+ /* Release the object type list */
+ SeReleaseObjectTypeList(CapturedObjectTypeList, PreviousMode);
+
/* Dereference the token */
ObDereferenceObject(Token);
@@ -1283,127 +1916,384 @@ NtAccessCheck(
return STATUS_SUCCESS;
}
+/* PUBLIC FUNCTIONS ***********************************************************/
+
/**
* @brief
- * Determines whether security access could be granted or not on
- * an object by the requestor who wants such access through type.
+ * Determines whether security access rights can be given to an object
+ * depending on the security descriptor and other security context
+ * entities, such as an owner.
*
* @param[in] SecurityDescriptor
- * A security descriptor with information data for auditing.
- *
- * @param[in] PrincipalSelfSid
- * A principal self user SID.
+ * Security descriptor of the object that is being accessed.
*
- * @param[in] ClientToken
- * A client access token.
+ * @param[in] SubjectSecurityContext
+ * The captured subject security context.
+ *
+ * @param[in] SubjectContextLocked
+ * If set to TRUE, the caller acknowledges that the subject context
+ * has already been locked by the caller himself. If set to FALSE,
+ * the function locks the subject context.
*
* @param[in] DesiredAccess
- * The desired access masks rights requested by the caller.
+ * Access right bitmask that the calling thread wants to acquire.
*
- * @param[in] ObjectTypeList
- * A list of object types.
+ * @param[in] PreviouslyGrantedAccess
+ * The access rights previously acquired in the past.
*
- * @param[in] ObjectTypeLength
- * The length size of the list.
+ * @param[out] Privileges
+ * The returned set of privileges.
*
* @param[in] GenericMapping
- * The generic mapping list of access masks rights.
- *
- * @param[in] PrivilegeSet
- * An array set of privileges.
+ * The generic mapping of access rights of an object type.
*
- * @param[in,out] PrivilegeSetLength
- * The length size of the array set of privileges.
+ * @param[in] AccessMode
+ * The processor request level mode.
*
* @param[out] GrantedAccess
- * The returned granted access rights.
+ * A list of granted access rights.
*
* @param[out] AccessStatus
- * The returned NTSTATUS code indicating the final results
- * of auditing.
+ * The returned status code specifying why access cannot be made
+ * onto an object (if said access is denied in the first place).
*
* @return
- * To be added...
+ * Returns TRUE if access onto the specific object is allowed, FALSE
+ * otherwise.
*/
-NTSTATUS
+BOOLEAN
NTAPI
-NtAccessCheckByType(
+SeAccessCheck(
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
- _In_ PSID PrincipalSelfSid,
- _In_ HANDLE ClientToken,
+ _In_ PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
+ _In_ BOOLEAN SubjectContextLocked,
_In_ ACCESS_MASK DesiredAccess,
- _In_ POBJECT_TYPE_LIST ObjectTypeList,
- _In_ ULONG ObjectTypeLength,
+ _In_ ACCESS_MASK PreviouslyGrantedAccess,
+ _Out_ PPRIVILEGE_SET* Privileges,
_In_ PGENERIC_MAPPING GenericMapping,
- _In_ PPRIVILEGE_SET PrivilegeSet,
- _Inout_ PULONG PrivilegeSetLength,
+ _In_ KPROCESSOR_MODE AccessMode,
_Out_ PACCESS_MASK GrantedAccess,
_Out_ PNTSTATUS AccessStatus)
{
- UNIMPLEMENTED;
- return STATUS_NOT_IMPLEMENTED;
+ BOOLEAN ret;
+
+ PAGED_CODE();
+
+ /* Check if this is kernel mode */
+ if (AccessMode == KernelMode)
+ {
+ /* Check if kernel wants everything */
+ if (DesiredAccess & MAXIMUM_ALLOWED)
+ {
+ /* Give it */
+ *GrantedAccess = GenericMapping->GenericAll;
+ *GrantedAccess |= (DesiredAccess &~ MAXIMUM_ALLOWED);
+ *GrantedAccess |= PreviouslyGrantedAccess;
+ }
+ else
+ {
+ /* Give the desired and previous access */
+ *GrantedAccess = DesiredAccess | PreviouslyGrantedAccess;
+ }
+
+ /* Success */
+ *AccessStatus = STATUS_SUCCESS;
+ return TRUE;
+ }
+
+ /* Check if we didn't get an SD */
+ if (!SecurityDescriptor)
+ {
+ /* Automatic failure */
+ *AccessStatus = STATUS_ACCESS_DENIED;
+ return FALSE;
+ }
+
+ /* Check for invalid impersonation */
+ if ((SubjectSecurityContext->ClientToken) &&
+ (SubjectSecurityContext->ImpersonationLevel < SecurityImpersonation))
+ {
+ *AccessStatus = STATUS_BAD_IMPERSONATION_LEVEL;
+ return FALSE;
+ }
+
+ /* Acquire the lock if needed */
+ if (!SubjectContextLocked)
+ SeLockSubjectContext(SubjectSecurityContext);
+
+ /* Check if the token is the owner and grant WRITE_DAC and READ_CONTROL rights */
+ if (DesiredAccess & (WRITE_DAC | READ_CONTROL | MAXIMUM_ALLOWED))
+ {
+ PACCESS_TOKEN Token = SubjectSecurityContext->ClientToken ?
+ SubjectSecurityContext->ClientToken :
SubjectSecurityContext->PrimaryToken;
+
+ if (SepTokenIsOwner(Token,
+ SecurityDescriptor,
+ FALSE))
+ {
+ if (DesiredAccess & MAXIMUM_ALLOWED)
+ PreviouslyGrantedAccess |= (WRITE_DAC | READ_CONTROL);
+ else
+ PreviouslyGrantedAccess |= (DesiredAccess & (WRITE_DAC |
READ_CONTROL));
+
+ DesiredAccess &= ~(WRITE_DAC | READ_CONTROL);
+ }
+ }
+
+ if (DesiredAccess == 0)
+ {
+ *GrantedAccess = PreviouslyGrantedAccess;
+ if (PreviouslyGrantedAccess == 0)
+ {
+ DPRINT1("Request for zero access to an object. Denying.\n");
+ *AccessStatus = STATUS_ACCESS_DENIED;
+ ret = FALSE;
+ }
+ else
+ {
+ *AccessStatus = STATUS_SUCCESS;
+ ret = TRUE;
+ }
+ }
+ else
+ {
+ /* Call the internal function */
+ ret = SepAccessCheckWorker(SecurityDescriptor,
+ SubjectSecurityContext->ClientToken,
+ SubjectSecurityContext->PrimaryToken,
+ NULL,
+ DesiredAccess,
+ NULL,
+ 0,
+ PreviouslyGrantedAccess,
+ GenericMapping,
+ AccessMode,
+ FALSE,
+ Privileges,
+ GrantedAccess,
+ AccessStatus);
+ }
+
+ /* Release the lock if needed */
+ if (!SubjectContextLocked)
+ SeUnlockSubjectContext(SubjectSecurityContext);
+
+ return ret;
}
/**
* @brief
- * Determines whether security access could be granted or not on
- * an object by the requestor who wants such access through
- * type list.
+ * Determines whether security access rights can be given to an object
+ * depending on the security descriptor. Unlike the regular access check
+ * procedure in the NT kernel, the fast traverse check is a faster way
+ * to quickly check if access can be made into an object.
*
* @param[in] SecurityDescriptor
- * A security descriptor with information data for auditing.
- *
- * @param[in] PrincipalSelfSid
- * A principal self user SID.
+ * Security descriptor of the object that is being accessed.
*
- * @param[in] ClientToken
- * A client access token.
+ * @param[in] AccessState
+ * An access state to determine if the access token in the current
+ * security context of the object is an restricted token.
*
* @param[in] DesiredAccess
- * The desired access masks rights requested by the caller.
- *
- * @param[in] ObjectTypeList
- * A list of object types.
- *
- * @param[in] ObjectTypeLength
- * The length size of the list.
- *
- * @param[in] GenericMapping
- * The generic mapping list of access masks rights.
- *
- * @param[in] PrivilegeSet
- * An array set of privileges.
- *
- * @param[in,out] PrivilegeSetLength
- * The length size of the array set of privileges.
- *
- * @param[out] GrantedAccess
- * The returned granted access rights.
+ * The access right bitmask where the calling thread wants to acquire.
*
- * @param[out] AccessStatus
- * The returned NTSTATUS code indicating the final results
- * of auditing.
+ * @param[in] AccessMode
+ * Process level request mode.
*
* @return
- * To be added...
+ * Returns TRUE if access onto the specific object is allowed, FALSE
+ * otherwise.
+ */
+BOOLEAN
+NTAPI
+SeFastTraverseCheck(
+ _In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
+ _In_ PACCESS_STATE AccessState,
+ _In_ ACCESS_MASK DesiredAccess,
+ _In_ KPROCESSOR_MODE AccessMode)
+{
+ PACL Dacl;
+ ULONG AceIndex;
+ PKNOWN_ACE Ace;
+
+ PAGED_CODE();
+
+ ASSERT(AccessMode != KernelMode);
+
+ if (SecurityDescriptor == NULL)
+ return FALSE;
+
+ /* Get DACL */
+ Dacl = SepGetDaclFromDescriptor(SecurityDescriptor);
+ /* If no DACL, grant access */
+ if (Dacl == NULL)
+ return TRUE;
+
+ /* No ACE -> Deny */
+ if (!Dacl->AceCount)
+ return FALSE;
+
+ /* Can't perform the check on restricted token */
+ if (AccessState->Flags & TOKEN_IS_RESTRICTED)
+ return FALSE;
+
+ /* Browse the ACEs */
+ for (AceIndex = 0, Ace = (PKNOWN_ACE)((ULONG_PTR)Dacl + sizeof(ACL));
+ AceIndex < Dacl->AceCount;
+ AceIndex++, Ace = (PKNOWN_ACE)((ULONG_PTR)Ace + Ace->Header.AceSize))
+ {
+ if (Ace->Header.AceFlags & INHERIT_ONLY_ACE)
+ continue;
+
+ /* If access-allowed ACE */
+ if (Ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
+ {
+ /* Check if all accesses are granted */
+ if (!(Ace->Mask & DesiredAccess))
+ continue;
+
+ /* Check SID and grant access if matching */
+ if (RtlEqualSid(SeWorldSid, &(Ace->SidStart)))
+ return TRUE;
+ }
+ /* If access-denied ACE */
+ else if (Ace->Header.AceType == ACCESS_DENIED_ACE_TYPE)
+ {
+ /* Here, only check if it denies any access wanted and deny if so */
+ if (Ace->Mask & DesiredAccess)
+ return FALSE;
+ }
+ }
+
+ /* Faulty, deny */
+ return FALSE;
+}
+
+/* SYSTEM CALLS ***************************************************************/
+
+/**
+ * @brief
+ * Determines whether security access can be granted to a client
+ * that requests such access on an object.
+ *
+ * @remarks
+ * For more documentation details about the parameters and
+ * overall function behavior, see SepAccessCheck.
*/
NTSTATUS
NTAPI
-NtAccessCheckByTypeResultList(
+NtAccessCheck(
+ _In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
+ _In_ HANDLE ClientToken,
+ _In_ ACCESS_MASK DesiredAccess,
+ _In_ PGENERIC_MAPPING GenericMapping,
+ _Out_writes_bytes_(*PrivilegeSetLength) PPRIVILEGE_SET PrivilegeSet,
+ _Inout_ PULONG PrivilegeSetLength,
+ _Out_ PACCESS_MASK GrantedAccess,
+ _Out_ PNTSTATUS AccessStatus)
+{
+ PAGED_CODE();
+
+ /* Invoke the internal function to do the job */
+ return SepAccessCheck(SecurityDescriptor,
+ ClientToken,
+ NULL,
+ DesiredAccess,
+ GenericMapping,
+ PrivilegeSet,
+ PrivilegeSetLength,
+ NULL,
+ 0,
+ FALSE,
+ GrantedAccess,
+ AccessStatus);
+}
+
+/**
+ * @brief
+ * Determines whether security access can be granted to a client
+ * that requests such access on the object type list. The access
+ * is either granted or denied for the whole object hierarchy
+ * in the list.
+ *
+ * @remarks
+ * For more documentation details about the parameters and
+ * overall function behavior, see SepAccessCheck.
+ */
+NTSTATUS
+NTAPI
+NtAccessCheckByType(
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
- _In_ PSID PrincipalSelfSid,
+ _In_opt_ PSID PrincipalSelfSid,
_In_ HANDLE ClientToken,
_In_ ACCESS_MASK DesiredAccess,
- _In_ POBJECT_TYPE_LIST ObjectTypeList,
- _In_ ULONG ObjectTypeLength,
+ _In_reads_opt_(ObjectTypeListLength) POBJECT_TYPE_LIST ObjectTypeList,
+ _In_ ULONG ObjectTypeListLength,
_In_ PGENERIC_MAPPING GenericMapping,
- _In_ PPRIVILEGE_SET PrivilegeSet,
+ _Out_writes_bytes_(*PrivilegeSetLength) PPRIVILEGE_SET PrivilegeSet,
_Inout_ PULONG PrivilegeSetLength,
_Out_ PACCESS_MASK GrantedAccess,
_Out_ PNTSTATUS AccessStatus)
{
- UNIMPLEMENTED;
- return STATUS_NOT_IMPLEMENTED;
+ PAGED_CODE();
+
+ /* Invoke the internal function to do the job */
+ return SepAccessCheck(SecurityDescriptor,
+ ClientToken,
+ PrincipalSelfSid,
+ DesiredAccess,
+ GenericMapping,
+ PrivilegeSet,
+ PrivilegeSetLength,
+ ObjectTypeList,
+ ObjectTypeListLength,
+ FALSE,
+ GrantedAccess,
+ AccessStatus);
+}
+
+/**
+ * @brief
+ * Determines whether security access can be granted to a client
+ * that requests such access on the object type list. Unlike the
+ * NtAccessCheckByType variant, this function will grant or deny
+ * access to each individual object and sub-object in the list.
+ *
+ * @remarks
+ * For more documentation details about the parameters and
+ * overall function behavior, see SepAccessCheck.
+ */
+NTSTATUS
+NTAPI
+NtAccessCheckByTypeResultList(
+ _In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
+ _In_opt_ PSID PrincipalSelfSid,
+ _In_ HANDLE ClientToken,
+ _In_ ACCESS_MASK DesiredAccess,
+ _In_reads_(ObjectTypeListLength) POBJECT_TYPE_LIST ObjectTypeList,
+ _In_ ULONG ObjectTypeListLength,
+ _In_ PGENERIC_MAPPING GenericMapping,
+ _Out_writes_bytes_(*PrivilegeSetLength) PPRIVILEGE_SET PrivilegeSet,
+ _Inout_ PULONG PrivilegeSetLength,
+ _Out_writes_(ObjectTypeListLength) PACCESS_MASK GrantedAccess,
+ _Out_writes_(ObjectTypeListLength) PNTSTATUS AccessStatus)
+{
+ PAGED_CODE();
+
+ /* Invoke the internal function to do the job */
+ return SepAccessCheck(SecurityDescriptor,
+ ClientToken,
+ PrincipalSelfSid,
+ DesiredAccess,
+ GenericMapping,
+ PrivilegeSet,
+ PrivilegeSetLength,
+ ObjectTypeList,
+ ObjectTypeListLength,
+ TRUE,
+ GrantedAccess,
+ AccessStatus);
}
/* EOF */