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; + /* 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; + } + else + { + DPRINT1("Failed to grant access rights. PreviouslyGrantedAccess == 0 DesiredAccess = %08lx\n", DesiredAccess); + Status = STATUS_ACCESS_DENIED; + } + + /* We are done here */ goto ReturnCommonStatus; } - - /* Set granted access right and access status */ - PreviouslyGrantedAccess |= AccessCheckRights.GrantedAccessRights; - if (PreviouslyGrantedAccess != 0) + else if (!UseResultList) { - Status = STATUS_SUCCESS; + /* + * 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; + } + } + + /* 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 { - DPRINT1("SepAccessCheck(): Failed to grant access rights. PreviouslyGrantedAccess == 0 DesiredAccess = %08lx\n", DesiredAccess); - Status = STATUS_ACCESS_DENIED; - } + /* + * 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; + } - /* We have successfully granted all the rights */ - goto ReturnCommonStatus; + /* 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,6 +1464,458 @@ SepGetPrivilegeSetLength( (PrivilegeSet->PrivilegeCount - 1) * sizeof(LUID_AND_ATTRIBUTES)); } +/** + * @brief + * 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 + * 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] ClientToken + * A handle to an access token, that identifies the client of which + * requests access to the target object. + * + * @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 + * 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[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 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 + * 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 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. + */ +static +NTSTATUS +SepAccessCheck( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ HANDLE ClientToken, + _In_opt_ PSID PrincipalSelfSid, + _In_ ACCESS_MASK DesiredAccess, + _In_ PGENERIC_MAPPING GenericMapping, + _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) +{ + 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 (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)); + + /* + * Probe the access and status list based on the way + * we are going to fill data in. + */ + if (UseResultList) + { + /* Bail out on an empty list */ + if (!ObjectTypeListLength) + { + DPRINT1("The object type list is empty\n"); + _SEH2_YIELD(return STATUS_INVALID_PARAMETER); + } + + ProbeForWrite(GrantedAccess, sizeof(ACCESS_MASK) * ObjectTypeListLength, sizeof(ULONG)); + ProbeForWrite(AccessStatus, sizeof(NTSTATUS) * ObjectTypeListLength, sizeof(ULONG)); + } + else + { + 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) + { + /* Return the exception code */ + _SEH2_YIELD(return _SEH2_GetExceptionCode()); + } + _SEH2_END; + + /* 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(ClientToken, + TOKEN_QUERY, + SeTokenObjectType, + PreviousMode, + (PVOID*)&Token, + NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to reference token (Status 0x%08lx)\n", Status); + return Status; + } + + /* Check token type */ + if (Token->TokenType != TokenImpersonation) + { + DPRINT("No impersonation token\n"); + ObDereferenceObject(Token); + return STATUS_NO_IMPERSONATION_TOKEN; + } + + /* Check the impersonation level */ + if (Token->ImpersonationLevel < SecurityIdentification) + { + 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, + NULL, + Token, + &Privileges, + PreviousMode); + if (!NT_SUCCESS(Status)) + { + DPRINT1("SePrivilegePolicyCheck failed (Status 0x%08lx)\n", Status); + SeReleaseObjectTypeList(CapturedObjectTypeList, PreviousMode); + ObDereferenceObject(Token); + + /* + * 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; + } + + /* Check the size of the privilege set and return the privileges */ + if (Privileges != NULL) + { + DPRINT("Privileges != NULL\n"); + + /* Calculate the required privilege set buffer size */ + RequiredPrivilegeSetLength = SepGetPrivilegeSetLength(Privileges); + + /* Fail if the privilege set buffer is too small */ + if (CapturedPrivilegeSetLength < RequiredPrivilegeSetLength) + { + SeFreePrivileges(Privileges); + SeReleaseObjectTypeList(CapturedObjectTypeList, PreviousMode); + ObDereferenceObject(Token); + *PrivilegeSetLength = RequiredPrivilegeSetLength; + return STATUS_BUFFER_TOO_SMALL; + } + + /* Copy the privilege set to the caller */ + RtlCopyMemory(PrivilegeSet, + Privileges, + RequiredPrivilegeSetLength); + + /* Free the local privilege set */ + SeFreePrivileges(Privileges); + } + else + { + DPRINT("Privileges == NULL\n"); + + /* 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; + } + + /* Initialize the privilege set */ + PrivilegeSet->PrivilegeCount = 0; + PrivilegeSet->Control = 0; + } + + /* Capture the security descriptor */ + Status = SeCaptureSecurityDescriptor(SecurityDescriptor, + PreviousMode, + PagedPool, + FALSE, + &CapturedSecurityDescriptor); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to capture the Security Descriptor\n"); + SeReleaseObjectTypeList(CapturedObjectTypeList, PreviousMode); + ObDereferenceObject(Token); + return Status; + } + + /* Check the captured security descriptor */ + if (CapturedSecurityDescriptor == NULL) + { + 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 (SepGetOwnerFromDescriptor(CapturedSecurityDescriptor) == NULL || + SepGetGroupFromDescriptor(CapturedSecurityDescriptor) == NULL) + { + 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); + + /* Lock the token */ + SepAcquireTokenLockShared(Token); + + /* Check if the token is the owner and grant WRITE_DAC and READ_CONTROL rights */ + if (DesiredAccess & (WRITE_DAC | READ_CONTROL | MAXIMUM_ALLOWED)) + { + if (SepTokenIsOwner(Token, CapturedSecurityDescriptor, 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) + { + /* + * 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 */ + 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); + + /* Check succeeded */ + return STATUS_SUCCESS; +} + /* PUBLIC FUNCTIONS ***********************************************************/ /** @@ -878,20 +2058,20 @@ SeAccessCheck( else { /* Call the internal function */ - ret = SepAccessCheck(SecurityDescriptor, - SubjectSecurityContext->ClientToken, - SubjectSecurityContext->PrimaryToken, - NULL, - DesiredAccess, - NULL, - 0, - PreviouslyGrantedAccess, - GenericMapping, - AccessMode, - FALSE, - Privileges, - GrantedAccess, - AccessStatus); + ret = SepAccessCheckWorker(SecurityDescriptor, + SubjectSecurityContext->ClientToken, + SubjectSecurityContext->PrimaryToken, + NULL, + DesiredAccess, + NULL, + 0, + PreviouslyGrantedAccess, + GenericMapping, + AccessMode, + FALSE, + Privileges, + GrantedAccess, + AccessStatus); } /* Release the lock if needed */ @@ -994,416 +2174,126 @@ SeFastTraverseCheck( /** * @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. + * Determines whether security access can be granted to a client + * that requests such access on an object. * - * @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. + * @remarks + * For more documentation details about the parameters and + * overall function behavior, see SepAccessCheck. */ NTSTATUS NTAPI NtAccessCheck( _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, - _In_ HANDLE TokenHandle, + _In_ HANDLE ClientToken, _In_ ACCESS_MASK DesiredAccess, _In_ PGENERIC_MAPPING GenericMapping, - _Out_opt_ PPRIVILEGE_SET PrivilegeSet, + _Out_writes_bytes_(*PrivilegeSetLength) 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) - { - /* Return the exception code */ - _SEH2_YIELD(return _SEH2_GetExceptionCode()); - } - _SEH2_END; - - /* Check for unmapped access rights */ - if (DesiredAccess & (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL)) - return STATUS_GENERIC_NOT_MAPPED; - - /* Reference the token */ - Status = ObReferenceObjectByHandle(TokenHandle, - TOKEN_QUERY, - SeTokenObjectType, - PreviousMode, - (PVOID*)&Token, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT("Failed to reference token (Status %lx)\n", Status); - return Status; - } - - /* Check token type */ - if (Token->TokenType != TokenImpersonation) - { - DPRINT("No impersonation token\n"); - ObDereferenceObject(Token); - return STATUS_NO_IMPERSONATION_TOKEN; - } - - /* Check the impersonation level */ - if (Token->ImpersonationLevel < SecurityIdentification) - { - DPRINT("Impersonation level < SecurityIdentification\n"); - ObDereferenceObject(Token); - return STATUS_BAD_IMPERSONATION_LEVEL; - } - - /* Check for ACCESS_SYSTEM_SECURITY and WRITE_OWNER access */ - Status = SePrivilegePolicyCheck(&DesiredAccess, - &PreviouslyGrantedAccess, - NULL, - Token, - &Privileges, - PreviousMode); - if (!NT_SUCCESS(Status)) - { - DPRINT("SePrivilegePolicyCheck failed (Status 0x%08lx)\n", Status); - ObDereferenceObject(Token); - *AccessStatus = Status; - *GrantedAccess = 0; - return STATUS_SUCCESS; - } - - /* Check the size of the privilege set and return the privileges */ - if (Privileges != NULL) - { - DPRINT("Privileges != NULL\n"); - - /* Calculate the required privilege set buffer size */ - RequiredPrivilegeSetLength = SepGetPrivilegeSetLength(Privileges); - - /* Fail if the privilege set buffer is too small */ - if (CapturedPrivilegeSetLength < RequiredPrivilegeSetLength) - { - ObDereferenceObject(Token); - SeFreePrivileges(Privileges); - *PrivilegeSetLength = RequiredPrivilegeSetLength; - return STATUS_BUFFER_TOO_SMALL; - } - - /* Copy the privilege set to the caller */ - RtlCopyMemory(PrivilegeSet, - Privileges, - RequiredPrivilegeSetLength); - - /* Free the local privilege set */ - SeFreePrivileges(Privileges); - } - else - { - DPRINT("Privileges == NULL\n"); - - /* Fail if the privilege set buffer is too small */ - if (CapturedPrivilegeSetLength < sizeof(PRIVILEGE_SET)) - { - ObDereferenceObject(Token); - *PrivilegeSetLength = sizeof(PRIVILEGE_SET); - return STATUS_BUFFER_TOO_SMALL; - } - - /* Initialize the privilege set */ - PrivilegeSet->PrivilegeCount = 0; - PrivilegeSet->Control = 0; - } - - /* Capture the security descriptor */ - Status = SeCaptureSecurityDescriptor(SecurityDescriptor, - PreviousMode, - PagedPool, - FALSE, - &CapturedSecurityDescriptor); - if (!NT_SUCCESS(Status)) - { - DPRINT("Failed to capture the Security Descriptor\n"); - ObDereferenceObject(Token); - return Status; - } - - /* Check the captured security descriptor */ - if (CapturedSecurityDescriptor == NULL) - { - DPRINT("Security Descriptor is NULL\n"); - ObDereferenceObject(Token); - return STATUS_INVALID_SECURITY_DESCR; - } - - /* Check security descriptor for valid owner and group */ - if (SepGetSDOwner(CapturedSecurityDescriptor) == NULL || - SepGetSDGroup(CapturedSecurityDescriptor) == NULL) - { - DPRINT("Security Descriptor does not have a valid group or owner\n"); - SeReleaseSecurityDescriptor(CapturedSecurityDescriptor, - PreviousMode, - FALSE); - ObDereferenceObject(Token); - return STATUS_INVALID_SECURITY_DESCR; - } - - /* Set up the subject context, and lock it */ - SeCaptureSubjectContext(&SubjectSecurityContext); - - /* Lock the token */ - SepAcquireTokenLockShared(Token); - - /* Check if the token is the owner and grant WRITE_DAC and READ_CONTROL rights */ - if (DesiredAccess & (WRITE_DAC | READ_CONTROL | MAXIMUM_ALLOWED)) - { - if (SepTokenIsOwner(Token, CapturedSecurityDescriptor, 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; - *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); - } - - /* Release subject context and unlock the token */ - SeReleaseSubjectContext(&SubjectSecurityContext); - SepReleaseTokenLock(Token); - - /* Release the captured security descriptor */ - SeReleaseSecurityDescriptor(CapturedSecurityDescriptor, - PreviousMode, - FALSE); - - /* Dereference the token */ - ObDereferenceObject(Token); - - /* Check succeeded */ - return STATUS_SUCCESS; + /* 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 could be granted or not on - * an object by the requestor who wants such access through type. + * 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. * - * @param[in] SecurityDescriptor - * A security descriptor with information data for auditing. - * - * @param[in] PrincipalSelfSid - * A principal self user SID. - * - * @param[in] ClientToken - * A client access 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. - * - * @param[out] AccessStatus - * The returned NTSTATUS code indicating the final results - * of auditing. - * - * @return - * To be added... + * @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 could be granted or not on - * an object by the requestor who wants such access through - * type list. + * 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. * - * @param[in] SecurityDescriptor - * A security descriptor with information data for auditing. - * - * @param[in] PrincipalSelfSid - * A principal self user SID. - * - * @param[in] ClientToken - * A client access 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. - * - * @param[out] AccessStatus - * The returned NTSTATUS code indicating the final results - * of auditing. - * - * @return - * To be added... + * @remarks + * For more documentation details about the parameters and + * overall function behavior, see SepAccessCheck. */ NTSTATUS NTAPI NtAccessCheckByTypeResultList( _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_(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) + _Out_writes_(ObjectTypeListLength) PACCESS_MASK GrantedAccess, + _Out_writes_(ObjectTypeListLength) 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, + TRUE, + GrantedAccess, + AccessStatus); } /* EOF */