2014-02-08 14:48:15 +00:00
|
|
|
/*
|
2023-03-07 10:42:22 +00:00
|
|
|
* PROJECT: ReactOS Kernel
|
|
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
|
|
* PURPOSE: Security access check control implementation
|
|
|
|
* COPYRIGHT: Copyright 2014 Timo Kreuzer <timo.kreuzer@reactos.org>
|
|
|
|
* Copyright 2014 Eric Kohl
|
|
|
|
* Copyright 2022-2023 George Bișoc <george.bisoc@reactos.org>
|
2014-02-08 14:48:15 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
|
|
|
|
#include <ntoskrnl.h>
|
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
|
|
|
|
2022-02-05 21:38:22 +00:00
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Analyzes an access control entry that is present in a discretionary
|
|
|
|
* access control list (DACL) for access right masks of each entry with
|
|
|
|
* the purpose to judge whether the calling thread can be warranted
|
|
|
|
* access check to a certain object or not.
|
|
|
|
*
|
|
|
|
* @param[in] ActionType
|
|
|
|
* The type of analysis to be done against an access entry. This type
|
|
|
|
* influences how access rights are gathered. This can either be AccessCheckMaximum
|
|
|
|
* which means the algorithm will perform analysis against ACEs on behalf of the
|
|
|
|
* requestor that gave us the acknowledgement that he desires MAXIMUM_ALLOWED access
|
|
|
|
* right or AccessCheckRegular if the requestor wants a subset of access rights.
|
|
|
|
*
|
2023-03-07 10:42:22 +00:00
|
|
|
* @param[in] RemainingAccess
|
|
|
|
* The remaining access rights that have yet to be granted to the calling thread
|
|
|
|
* whomst requests access to a certain object. This parameter mustn't be 0 as
|
|
|
|
* the remaining rights are left to be addressed. This is the case if we have
|
|
|
|
* to address the remaining rights on a regular subset basis (the requestor
|
|
|
|
* didn't ask for MAXIMUM_ALLOWED). Otherwise this parameter can be 0.
|
|
|
|
*
|
2022-02-05 21:38:22 +00:00
|
|
|
* @param[in] Dacl
|
|
|
|
* The discretionary access control list to be given to this function. This DACL
|
|
|
|
* must have at least one ACE currently present in the list.
|
|
|
|
*
|
|
|
|
* @param[in] AccessToken
|
|
|
|
* A pointer to an access token, where an equality comparison check is performed if
|
|
|
|
* the security identifier (SID) from a ACE of a certain object is present in this
|
|
|
|
* token. This token represents the effective (calling thread) token of the caller.
|
|
|
|
*
|
|
|
|
* @param[in] PrimaryAccessToken
|
|
|
|
* A pointer to an access token, represented as an access token associated with the
|
|
|
|
* primary calling process. This token describes the primary security context of the
|
|
|
|
* main process.
|
|
|
|
*
|
|
|
|
* @param[in] IsTokenRestricted
|
|
|
|
* If this parameter is set to TRUE, the function considers the token pointed by
|
|
|
|
* AccessToken parameter argument as restricted. That is, the token has restricted
|
|
|
|
* SIDs therefore the function will act accordingly against that token by checking
|
|
|
|
* for restricted SIDs only when doing an equaility comparison check between the
|
|
|
|
* two identifiers.
|
|
|
|
*
|
|
|
|
* @param[in] PrincipalSelfSid
|
|
|
|
* A pointer to a security identifier that represents a principal. A principal
|
|
|
|
* identifies a user object which is associated with its own security descriptor.
|
|
|
|
*
|
|
|
|
* @param[in] GenericMapping
|
|
|
|
* A pointer to a generic mapping that is associated with the object in question
|
|
|
|
* being checked for access. If certain set of desired access rights have
|
|
|
|
* a generic access right, this parameter is needed to map generic rights.
|
|
|
|
*
|
|
|
|
* @param[in] ObjectTypeList
|
|
|
|
* A pointer to a list array of object types. If such array is provided to the
|
|
|
|
* function, the algorithm will perform a different approach by doing analysis
|
|
|
|
* against ACEs each sub-object of an object of primary level (level 0) or sub-objects
|
|
|
|
* of a sub-object of an object. If this parameter is NULL, the function will normally
|
|
|
|
* analyze the ACEs of a DACL of the target object itself.
|
|
|
|
*
|
|
|
|
* @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.
|
|
|
|
*
|
2023-03-07 10:42:22 +00:00
|
|
|
* @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!
|
2022-02-05 21:38:22 +00:00
|
|
|
*/
|
2023-03-07 10:42:22 +00:00
|
|
|
VOID
|
2022-02-05 21:38:22 +00:00
|
|
|
SepAnalyzeAcesFromDacl(
|
|
|
|
_In_ ACCESS_CHECK_RIGHT_TYPE ActionType,
|
2023-03-07 10:42:22 +00:00
|
|
|
_In_ ACCESS_MASK RemainingAccess,
|
2022-02-05 21:38:22 +00:00
|
|
|
_In_ PACL Dacl,
|
|
|
|
_In_ PACCESS_TOKEN AccessToken,
|
|
|
|
_In_ PACCESS_TOKEN PrimaryAccessToken,
|
|
|
|
_In_ BOOLEAN IsTokenRestricted,
|
|
|
|
_In_opt_ PSID PrincipalSelfSid,
|
|
|
|
_In_ PGENERIC_MAPPING GenericMapping,
|
|
|
|
_In_opt_ POBJECT_TYPE_LIST ObjectTypeList,
|
|
|
|
_In_ ULONG ObjectTypeListLength,
|
2023-03-07 10:42:22 +00:00
|
|
|
_Inout_ PACCESS_CHECK_RIGHTS AccessCheckRights)
|
2022-02-05 21:38:22 +00:00
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
PACE CurrentAce;
|
|
|
|
ULONG AceIndex;
|
|
|
|
PSID Sid;
|
|
|
|
ACCESS_MASK Access;
|
|
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
|
|
/* These parameters are really needed */
|
|
|
|
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);
|
|
|
|
|
|
|
|
/* Determine how we should analyze the ACEs */
|
|
|
|
switch (ActionType)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We got the acknowledgement the calling thread desires
|
|
|
|
* maximum rights (as according to MAXIMUM_ALLOWED access
|
|
|
|
* mask). Analyze the ACE of the given DACL.
|
|
|
|
*/
|
|
|
|
case AccessCheckMaximum:
|
|
|
|
{
|
|
|
|
/* Loop over the DACL to retrieve ACEs */
|
|
|
|
for (AceIndex = 0; AceIndex < Dacl->AceCount; AceIndex++)
|
|
|
|
{
|
|
|
|
/* Obtain a ACE now */
|
|
|
|
Status = RtlGetAce(Dacl, AceIndex, (PVOID*)&CurrentAce);
|
|
|
|
|
|
|
|
/* Getting this ACE is important, otherwise something is seriously wrong */
|
|
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now it's time to analyze it based upon the
|
|
|
|
* type of this ACE we're being given.
|
|
|
|
*/
|
|
|
|
if (!(CurrentAce->Header.AceFlags & INHERIT_ONLY_ACE))
|
|
|
|
{
|
|
|
|
if (CurrentAce->Header.AceType == ACCESS_DENIED_ACE_TYPE)
|
|
|
|
{
|
|
|
|
/* Get the SID from this ACE */
|
|
|
|
Sid = SepGetSidFromAce(ACCESS_DENIED_ACE_TYPE, CurrentAce);
|
2023-03-07 10:42:22 +00:00
|
|
|
ASSERT(Sid);
|
2022-02-05 21:38:22 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Deny access rights that have not been granted yet */
|
2023-03-07 10:42:22 +00:00
|
|
|
AccessCheckRights->DeniedAccessRights |= (Access & ~AccessCheckRights->GrantedAccessRights);
|
|
|
|
DPRINT("SepAnalyzeAcesFromDacl(): DeniedAccessRights 0x%08lx\n", AccessCheckRights->DeniedAccessRights);
|
2022-02-05 21:38:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (CurrentAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
|
|
|
|
{
|
|
|
|
/* Get the SID from this ACE */
|
|
|
|
Sid = SepGetSidFromAce(ACCESS_ALLOWED_ACE_TYPE, CurrentAce);
|
2023-03-07 10:42:22 +00:00
|
|
|
ASSERT(Sid);
|
2022-02-05 21:38:22 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Grant access rights that have not been denied yet */
|
2023-03-07 10:42:22 +00:00
|
|
|
AccessCheckRights->GrantedAccessRights |= (Access & ~AccessCheckRights->DeniedAccessRights);
|
|
|
|
DPRINT("SepAnalyzeAcesFromDacl(): GrantedAccessRights 0x%08lx\n", AccessCheckRights->GrantedAccessRights);
|
2022-02-05 21:38:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DPRINT1("SepAnalyzeAcesFromDacl(): Unsupported ACE type 0x%lx\n", CurrentAce->Header.AceType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We're done here */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We got the acknowledgement the calling thread desires
|
|
|
|
* only a subset of rights therefore we have to act a little
|
|
|
|
* different here.
|
|
|
|
*/
|
|
|
|
case AccessCheckRegular:
|
|
|
|
{
|
|
|
|
/* Cache the remaining access rights to be addressed */
|
2023-03-07 10:42:22 +00:00
|
|
|
ASSERT(RemainingAccess != 0);
|
|
|
|
AccessCheckRights->RemainingAccessRights = RemainingAccess;
|
2022-02-05 21:38:22 +00:00
|
|
|
|
|
|
|
/* Loop over the DACL to retrieve ACEs */
|
|
|
|
for (AceIndex = 0; AceIndex < Dacl->AceCount; AceIndex++)
|
|
|
|
{
|
|
|
|
/* Obtain a ACE now */
|
|
|
|
Status = RtlGetAce(Dacl, AceIndex, (PVOID*)&CurrentAce);
|
|
|
|
|
|
|
|
/* Getting this ACE is important, otherwise something is seriously wrong */
|
|
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now it's time to analyze it based upon the
|
|
|
|
* type of this ACE we're being given.
|
|
|
|
*/
|
|
|
|
if (!(CurrentAce->Header.AceFlags & INHERIT_ONLY_ACE))
|
|
|
|
{
|
|
|
|
if (CurrentAce->Header.AceType == ACCESS_DENIED_ACE_TYPE)
|
|
|
|
{
|
|
|
|
/* Get the SID from this ACE */
|
|
|
|
Sid = SepGetSidFromAce(ACCESS_DENIED_ACE_TYPE, CurrentAce);
|
2023-03-07 10:42:22 +00:00
|
|
|
ASSERT(Sid);
|
2022-02-05 21:38:22 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The caller requests a right that cannot be
|
|
|
|
* granted. Access is implicitly denied for
|
|
|
|
* the calling thread. Track this access right.
|
|
|
|
*/
|
2023-03-07 10:42:22 +00:00
|
|
|
if (AccessCheckRights->RemainingAccessRights & Access)
|
2022-02-05 21:38:22 +00:00
|
|
|
{
|
|
|
|
DPRINT("SepAnalyzeAcesFromDacl(): Refuted access 0x%08lx\n", Access);
|
2023-03-07 10:42:22 +00:00
|
|
|
AccessCheckRights->DeniedAccessRights |= Access;
|
2022-02-05 21:38:22 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (CurrentAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
|
|
|
|
{
|
|
|
|
/* Get the SID from this ACE */
|
|
|
|
Sid = SepGetSidFromAce(ACCESS_ALLOWED_ACE_TYPE, CurrentAce);
|
2023-03-07 10:42:22 +00:00
|
|
|
ASSERT(Sid);
|
2022-02-05 21:38:22 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-03-07 10:42:22 +00:00
|
|
|
/* Remove the remaining rights */
|
|
|
|
DPRINT("SepAnalyzeAcesFromDacl(): RemainingAccessRights 0x%08lx Access 0x%08lx\n", AccessCheckRights->RemainingAccessRights, Access);
|
|
|
|
AccessCheckRights->RemainingAccessRights &= ~Access;
|
|
|
|
DPRINT("SepAnalyzeAcesFromDacl(): RemainingAccessRights 0x%08lx\n", AccessCheckRights->RemainingAccessRights);
|
2022-02-05 21:38:22 +00:00
|
|
|
|
|
|
|
/* Track the granted access right */
|
2023-03-07 10:42:22 +00:00
|
|
|
AccessCheckRights->GrantedAccessRights |= Access;
|
2022-02-05 21:38:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DPRINT1("SepAnalyzeAcesFromDacl(): Unsupported ACE type 0x%lx\n", CurrentAce->Header.AceType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We're done here */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We shouldn't reach here */
|
|
|
|
DEFAULT_UNREACHABLE;
|
|
|
|
}
|
|
|
|
}
|
2014-02-08 14:48:15 +00:00
|
|
|
|
2021-08-11 14:24:45 +00:00
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Private function that determines whether security access rights can be given
|
2022-02-05 21:38:22 +00:00
|
|
|
* 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.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2022-02-05 21:38:22 +00:00
|
|
|
* @param[in] ClientAccessToken
|
|
|
|
* A pointer to a client (thread) access token that requests access rights
|
|
|
|
* of an object or subset of multiple objects.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2022-02-05 21:38:22 +00:00
|
|
|
* @param[in] PrimaryAccessToken
|
|
|
|
* A pointer to a primary access token that describes the primary security
|
|
|
|
* context of the main calling process.
|
|
|
|
*
|
|
|
|
* @param[in] PrincipalSelfSid
|
|
|
|
* A pointer to a security identifier that represents a security principal,
|
|
|
|
* that is, a user object associated with its security descriptor.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] DesiredAccess
|
2022-02-05 21:38:22 +00:00
|
|
|
* The access rights desired by the calling thread to acquire in order to
|
|
|
|
* access an object.
|
|
|
|
*
|
|
|
|
* @param[in] ObjectTypeList
|
|
|
|
* An array list of object types to be checked against for access. The function
|
|
|
|
* will act accordingly in this case by checking each sub-object of an object
|
|
|
|
* of primary level and such. If this parameter is NULL, the function will
|
|
|
|
* perform a normal access check against the target object itself.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] ObjectTypeListLength
|
2022-02-05 21:38:22 +00:00
|
|
|
* The length of a object type list. Such length represents the number of
|
|
|
|
* elements in this list.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] PreviouslyGrantedAccess
|
2022-02-05 21:38:22 +00:00
|
|
|
* The access rights previously acquired in the past. If this parameter is 0,
|
|
|
|
* it is deemed that the calling thread hasn't acquired any rights. Access checks
|
|
|
|
* are more tighten in this case.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] GenericMapping
|
2022-02-05 21:38:22 +00:00
|
|
|
* A pointer to a generic mapping of access rights of the target object.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] AccessMode
|
|
|
|
* The processor request level mode.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2022-02-05 21:38:22 +00:00
|
|
|
* @param[in] UseResultList
|
|
|
|
* If set to TRUE, the function will return a list of granted access rights
|
|
|
|
* of each sub-object as well as status code for each. If this parameter is
|
|
|
|
* set to FALSE, then the function will just return only the granted access
|
|
|
|
* rights and status code for single object that's been target for access
|
|
|
|
* checks.
|
|
|
|
*
|
|
|
|
* @param[out] Privileges
|
|
|
|
* A pointer to a definite set of privileges that have been audited
|
|
|
|
* whilst doing access check procedures. Such set of privileges are
|
|
|
|
* optionally returned to the caller. This can be set to NULL if
|
|
|
|
* the caller doesn't want to obtain a set of privileges.
|
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[out] GrantedAccessList
|
2022-02-05 21:38:22 +00:00
|
|
|
* A list of granted access rights returned to the caller. This list
|
|
|
|
* can comprehend multiple elements which represent the sub-objects
|
|
|
|
* that have been checked or a single element which is the target
|
|
|
|
* object itself.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[out] AccessStatusList
|
2022-02-05 21:38:22 +00:00
|
|
|
* A list of access status codes returned to the caller. This list
|
|
|
|
* can comprehend multiple elements which represent the sub-objects
|
|
|
|
* that have been checked or a single element which is the target
|
|
|
|
* object itself.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @return
|
|
|
|
* Returns TRUE if access onto the specific object is allowed, FALSE
|
|
|
|
* otherwise.
|
2016-11-04 17:52:32 +00:00
|
|
|
*/
|
2022-02-05 21:38:22 +00:00
|
|
|
BOOLEAN
|
|
|
|
NTAPI
|
2021-08-20 09:48:19 +00:00
|
|
|
SepAccessCheck(
|
|
|
|
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
2022-02-05 21:38:22 +00:00
|
|
|
_In_opt_ PACCESS_TOKEN ClientAccessToken,
|
|
|
|
_In_ PACCESS_TOKEN PrimaryAccessToken,
|
|
|
|
_In_opt_ PSID PrincipalSelfSid,
|
2021-08-20 09:48:19 +00:00
|
|
|
_In_ ACCESS_MASK DesiredAccess,
|
2022-02-05 21:38:22 +00:00
|
|
|
_In_opt_ POBJECT_TYPE_LIST ObjectTypeList,
|
2021-08-20 09:48:19 +00:00
|
|
|
_In_ ULONG ObjectTypeListLength,
|
|
|
|
_In_ ACCESS_MASK PreviouslyGrantedAccess,
|
|
|
|
_In_ PGENERIC_MAPPING GenericMapping,
|
|
|
|
_In_ KPROCESSOR_MODE AccessMode,
|
2022-02-05 21:38:22 +00:00
|
|
|
_In_ BOOLEAN UseResultList,
|
|
|
|
_Out_opt_ PPRIVILEGE_SET* Privileges,
|
2021-08-20 09:48:19 +00:00
|
|
|
_Out_ PACCESS_MASK GrantedAccessList,
|
2022-02-05 21:38:22 +00:00
|
|
|
_Out_ PNTSTATUS AccessStatusList)
|
2014-02-08 14:48:15 +00:00
|
|
|
{
|
|
|
|
ACCESS_MASK RemainingAccess;
|
2022-02-05 21:38:22 +00:00
|
|
|
ULONG ResultListLength;
|
|
|
|
ULONG ResultListIndex;
|
2014-02-08 14:48:15 +00:00
|
|
|
PACL Dacl;
|
|
|
|
BOOLEAN Present;
|
|
|
|
BOOLEAN Defaulted;
|
|
|
|
NTSTATUS Status;
|
2022-11-06 16:47:30 +00:00
|
|
|
PACCESS_TOKEN Token = NULL;
|
2023-03-07 10:42:22 +00:00
|
|
|
ACCESS_CHECK_RIGHTS AccessCheckRights = {0};
|
2022-02-05 21:38:22 +00:00
|
|
|
|
2014-02-08 14:48:15 +00:00
|
|
|
PAGED_CODE();
|
|
|
|
|
2022-02-05 21:38:22 +00:00
|
|
|
/* A security descriptor must be expected for access checks */
|
|
|
|
ASSERT(SecurityDescriptor);
|
|
|
|
|
2014-02-08 14:48:15 +00:00
|
|
|
/* Check for no access desired */
|
|
|
|
if (!DesiredAccess)
|
|
|
|
{
|
|
|
|
/* Check if we had no previous access */
|
|
|
|
if (!PreviouslyGrantedAccess)
|
|
|
|
{
|
|
|
|
/* Then there's nothing to give */
|
2022-02-05 21:38:22 +00:00
|
|
|
DPRINT1("SepAccessCheck(): The caller has no previously granted access gained!\n");
|
2014-02-09 16:57:42 +00:00
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
|
|
goto ReturnCommonStatus;
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the previous access only */
|
2014-02-09 16:57:42 +00:00
|
|
|
Status = STATUS_SUCCESS;
|
2014-02-08 14:48:15 +00:00
|
|
|
*Privileges = NULL;
|
2014-02-09 16:57:42 +00:00
|
|
|
goto ReturnCommonStatus;
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Map given accesses */
|
|
|
|
RtlMapGenericMask(&DesiredAccess, GenericMapping);
|
|
|
|
if (PreviouslyGrantedAccess)
|
|
|
|
RtlMapGenericMask(&PreviouslyGrantedAccess, GenericMapping);
|
|
|
|
|
|
|
|
/* Initialize remaining access rights */
|
|
|
|
RemainingAccess = DesiredAccess;
|
|
|
|
|
2022-02-05 21:38:22 +00:00
|
|
|
/*
|
|
|
|
* Obtain the token provided by the caller. Client (or also
|
|
|
|
* called impersonation or thread) token takes precedence over
|
|
|
|
* the primary token which is the token associated with the security
|
|
|
|
* context of the main calling process. This is because it is the
|
|
|
|
* client itself that requests access of an object or subset of
|
|
|
|
* multiple objects. Otherwise obtain the security context of the
|
|
|
|
* main process (the actual primary token).
|
|
|
|
*/
|
|
|
|
Token = ClientAccessToken ? ClientAccessToken : PrimaryAccessToken;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We should at least expect a primary token
|
|
|
|
* to be present if client token is not
|
|
|
|
* available.
|
|
|
|
*/
|
|
|
|
ASSERT(Token);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for ACCESS_SYSTEM_SECURITY and WRITE_OWNER access.
|
|
|
|
* Write down a set of privileges that have been checked
|
|
|
|
* if the caller wants it.
|
|
|
|
*/
|
2014-02-08 20:01:09 +00:00
|
|
|
Status = SePrivilegePolicyCheck(&RemainingAccess,
|
|
|
|
&PreviouslyGrantedAccess,
|
|
|
|
NULL,
|
|
|
|
Token,
|
2022-02-05 21:38:22 +00:00
|
|
|
Privileges,
|
|
|
|
AccessMode);
|
2014-02-08 20:01:09 +00:00
|
|
|
if (!NT_SUCCESS(Status))
|
2014-02-08 14:48:15 +00:00
|
|
|
{
|
2014-02-09 16:57:42 +00:00
|
|
|
goto ReturnCommonStatus;
|
2014-02-08 20:01:09 +00:00
|
|
|
}
|
2014-02-08 14:48:15 +00:00
|
|
|
|
2014-02-08 20:01:09 +00:00
|
|
|
/* Succeed if there are no more rights to grant */
|
|
|
|
if (RemainingAccess == 0)
|
|
|
|
{
|
2014-02-09 16:57:42 +00:00
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
goto ReturnCommonStatus;
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the DACL */
|
|
|
|
Status = RtlGetDaclSecurityDescriptor(SecurityDescriptor,
|
|
|
|
&Present,
|
|
|
|
&Dacl,
|
|
|
|
&Defaulted);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
2014-02-09 16:57:42 +00:00
|
|
|
goto ReturnCommonStatus;
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
|
2022-02-05 21:38:22 +00:00
|
|
|
/* Grant desired access if the object is unprotected */
|
2014-02-08 14:48:15 +00:00
|
|
|
if (Present == FALSE || Dacl == NULL)
|
|
|
|
{
|
2014-02-09 16:57:42 +00:00
|
|
|
PreviouslyGrantedAccess |= RemainingAccess;
|
|
|
|
if (RemainingAccess & MAXIMUM_ALLOWED)
|
2014-02-08 14:48:15 +00:00
|
|
|
{
|
2014-02-09 16:57:42 +00:00
|
|
|
PreviouslyGrantedAccess &= ~MAXIMUM_ALLOWED;
|
|
|
|
PreviouslyGrantedAccess |= GenericMapping->GenericAll;
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
|
2014-02-09 16:57:42 +00:00
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
goto ReturnCommonStatus;
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Deny access if the DACL is empty */
|
|
|
|
if (Dacl->AceCount == 0)
|
|
|
|
{
|
|
|
|
if (RemainingAccess == MAXIMUM_ALLOWED && PreviouslyGrantedAccess != 0)
|
|
|
|
{
|
2014-02-09 16:57:42 +00:00
|
|
|
Status = STATUS_SUCCESS;
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-02-05 21:38:22 +00:00
|
|
|
DPRINT1("SepAccessCheck(): The DACL has no ACEs and the caller has no previously granted access!\n");
|
2014-02-09 16:57:42 +00:00
|
|
|
PreviouslyGrantedAccess = 0;
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
2014-02-09 16:57:42 +00:00
|
|
|
goto ReturnCommonStatus;
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Determine the MAXIMUM_ALLOWED access rights according to the DACL */
|
|
|
|
if (DesiredAccess & MAXIMUM_ALLOWED)
|
|
|
|
{
|
2022-02-05 21:38:22 +00:00
|
|
|
/* Perform access checks against ACEs from this DACL */
|
2023-03-07 10:42:22 +00:00
|
|
|
SepAnalyzeAcesFromDacl(AccessCheckMaximum,
|
|
|
|
0,
|
|
|
|
Dacl,
|
|
|
|
Token,
|
|
|
|
PrimaryAccessToken,
|
|
|
|
FALSE,
|
|
|
|
PrincipalSelfSid,
|
|
|
|
GenericMapping,
|
|
|
|
ObjectTypeList,
|
|
|
|
ObjectTypeListLength,
|
|
|
|
&AccessCheckRights);
|
2014-02-08 14:48:15 +00:00
|
|
|
|
2022-02-05 21:38:22 +00:00
|
|
|
/*
|
|
|
|
* Perform further access checks if this token
|
|
|
|
* has restricted SIDs.
|
|
|
|
*/
|
|
|
|
if (SeTokenIsRestricted(Token))
|
|
|
|
{
|
2023-03-07 10:42:22 +00:00
|
|
|
SepAnalyzeAcesFromDacl(AccessCheckMaximum,
|
|
|
|
0,
|
|
|
|
Dacl,
|
|
|
|
Token,
|
|
|
|
PrimaryAccessToken,
|
|
|
|
TRUE,
|
|
|
|
PrincipalSelfSid,
|
|
|
|
GenericMapping,
|
|
|
|
ObjectTypeList,
|
|
|
|
ObjectTypeListLength,
|
|
|
|
&AccessCheckRights);
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Fail if some rights have not been granted */
|
2023-03-07 10:42:22 +00:00
|
|
|
RemainingAccess &= ~(MAXIMUM_ALLOWED | AccessCheckRights.GrantedAccessRights);
|
2014-02-08 14:48:15 +00:00
|
|
|
if (RemainingAccess != 0)
|
|
|
|
{
|
2022-02-05 21:38:22 +00:00
|
|
|
DPRINT1("SepAccessCheck(): Failed to grant access rights. RemainingAccess = 0x%08lx DesiredAccess = 0x%08lx\n", RemainingAccess, DesiredAccess);
|
2014-02-09 16:57:42 +00:00
|
|
|
PreviouslyGrantedAccess = 0;
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
|
|
goto ReturnCommonStatus;
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set granted access right and access status */
|
2023-03-07 10:42:22 +00:00
|
|
|
PreviouslyGrantedAccess |= AccessCheckRights.GrantedAccessRights;
|
2014-02-09 16:57:42 +00:00
|
|
|
if (PreviouslyGrantedAccess != 0)
|
2014-02-08 14:48:15 +00:00
|
|
|
{
|
2014-02-09 16:57:42 +00:00
|
|
|
Status = STATUS_SUCCESS;
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-02-05 21:38:22 +00:00
|
|
|
DPRINT1("SepAccessCheck(): Failed to grant access rights. PreviouslyGrantedAccess == 0 DesiredAccess = %08lx\n", DesiredAccess);
|
2014-02-09 16:57:42 +00:00
|
|
|
Status = STATUS_ACCESS_DENIED;
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
2022-02-05 21:38:22 +00:00
|
|
|
|
|
|
|
/* We have successfully granted all the rights */
|
2014-02-09 16:57:42 +00:00
|
|
|
goto ReturnCommonStatus;
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
|
2022-02-05 21:38:22 +00:00
|
|
|
/* Grant rights according to the DACL */
|
2023-03-07 10:42:22 +00:00
|
|
|
SepAnalyzeAcesFromDacl(AccessCheckRegular,
|
|
|
|
RemainingAccess,
|
|
|
|
Dacl,
|
|
|
|
Token,
|
|
|
|
PrimaryAccessToken,
|
|
|
|
FALSE,
|
|
|
|
PrincipalSelfSid,
|
|
|
|
GenericMapping,
|
|
|
|
ObjectTypeList,
|
|
|
|
ObjectTypeListLength,
|
|
|
|
&AccessCheckRights);
|
2014-02-08 14:48:15 +00:00
|
|
|
|
|
|
|
/* Fail if some rights have not been granted */
|
2023-03-07 10:42:22 +00:00
|
|
|
if (AccessCheckRights.RemainingAccessRights != 0)
|
2014-02-08 14:48:15 +00:00
|
|
|
{
|
2023-03-07 10:42:22 +00:00
|
|
|
DPRINT1("SepAccessCheck(): Failed to grant access rights. RemainingAccess = 0x%08lx DesiredAccess = 0x%08lx\n", AccessCheckRights.RemainingAccessRights, DesiredAccess);
|
2022-02-05 21:38:22 +00:00
|
|
|
PreviouslyGrantedAccess = 0;
|
2014-02-09 16:57:42 +00:00
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
|
|
goto ReturnCommonStatus;
|
2022-02-05 21:38:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Perform further access checks if this token
|
|
|
|
* has restricted SIDs.
|
|
|
|
*/
|
|
|
|
if (SeTokenIsRestricted(Token))
|
|
|
|
{
|
2023-03-07 10:42:22 +00:00
|
|
|
SepAnalyzeAcesFromDacl(AccessCheckRegular,
|
|
|
|
RemainingAccess,
|
|
|
|
Dacl,
|
|
|
|
Token,
|
|
|
|
PrimaryAccessToken,
|
|
|
|
TRUE,
|
|
|
|
PrincipalSelfSid,
|
|
|
|
GenericMapping,
|
|
|
|
ObjectTypeList,
|
|
|
|
ObjectTypeListLength,
|
|
|
|
&AccessCheckRights);
|
2022-02-05 21:38:22 +00:00
|
|
|
|
|
|
|
/* Fail if some rights have not been granted */
|
2023-03-07 10:42:22 +00:00
|
|
|
if (AccessCheckRights.RemainingAccessRights != 0)
|
2022-02-05 21:38:22 +00:00
|
|
|
{
|
2023-03-07 10:42:22 +00:00
|
|
|
DPRINT1("SepAccessCheck(): Failed to grant access rights. RemainingAccess = 0x%08lx DesiredAccess = 0x%08lx\n", AccessCheckRights.RemainingAccessRights, DesiredAccess);
|
2022-02-05 21:38:22 +00:00
|
|
|
PreviouslyGrantedAccess = 0;
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
|
|
goto ReturnCommonStatus;
|
|
|
|
}
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set granted access rights */
|
2014-02-09 16:57:42 +00:00
|
|
|
PreviouslyGrantedAccess |= DesiredAccess;
|
2014-02-08 14:48:15 +00:00
|
|
|
|
|
|
|
/* Fail if no rights have been granted */
|
2014-02-09 16:57:42 +00:00
|
|
|
if (PreviouslyGrantedAccess == 0)
|
2014-02-08 14:48:15 +00:00
|
|
|
{
|
2022-02-05 21:38:22 +00:00
|
|
|
DPRINT1("SepAccessCheck(): Failed to grant access rights. PreviouslyGrantedAccess == 0 DesiredAccess = %08lx\n", DesiredAccess);
|
2014-02-09 16:57:42 +00:00
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
|
|
goto ReturnCommonStatus;
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
|
2022-02-05 21:38:22 +00:00
|
|
|
/*
|
|
|
|
* If we're here then we granted all the desired
|
|
|
|
* access rights the caller wanted.
|
|
|
|
*/
|
2014-02-09 16:57:42 +00:00
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
|
|
|
|
ReturnCommonStatus:
|
|
|
|
ResultListLength = UseResultList ? ObjectTypeListLength : 1;
|
2022-02-05 21:38:22 +00:00
|
|
|
for (ResultListIndex = 0; ResultListIndex < ResultListLength; ResultListIndex++)
|
2014-02-09 16:57:42 +00:00
|
|
|
{
|
2022-02-05 21:38:22 +00:00
|
|
|
GrantedAccessList[ResultListIndex] = PreviouslyGrantedAccess;
|
|
|
|
AccessStatusList[ResultListIndex] = Status;
|
2014-02-09 16:57:42 +00:00
|
|
|
}
|
|
|
|
|
2022-11-06 16:47:30 +00:00
|
|
|
#if DBG
|
|
|
|
/* Dump security debug info on access denied case */
|
|
|
|
if (Status == STATUS_ACCESS_DENIED)
|
|
|
|
{
|
|
|
|
SepDumpSdDebugInfo(SecurityDescriptor);
|
|
|
|
SepDumpTokenDebugInfo(Token);
|
2023-03-07 10:42:22 +00:00
|
|
|
SepDumpAccessRightsStats(&AccessCheckRights);
|
2022-11-06 16:47:30 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-02-09 16:57:42 +00:00
|
|
|
return NT_SUCCESS(Status);
|
|
|
|
}
|
|
|
|
|
2021-08-11 14:24:45 +00:00
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Retrieves the main user from a security descriptor.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] SecurityDescriptor
|
|
|
|
* A valid allocated security descriptor structure where the owner
|
|
|
|
* is to be retrieved.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @return
|
|
|
|
* Returns a SID that represents the main user (owner).
|
|
|
|
*/
|
2014-02-08 14:48:15 +00:00
|
|
|
static PSID
|
2021-08-20 09:48:19 +00:00
|
|
|
SepGetSDOwner(
|
|
|
|
_In_ PSECURITY_DESCRIPTOR _SecurityDescriptor)
|
2014-02-08 14:48:15 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-08-11 14:24:45 +00:00
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Retrieves the group from a security descriptor.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] SecurityDescriptor
|
|
|
|
* A valid allocated security descriptor structure where the group
|
|
|
|
* is to be retrieved.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @return
|
|
|
|
* Returns a SID that represents a group.
|
|
|
|
*/
|
2014-02-08 14:48:15 +00:00
|
|
|
static PSID
|
2021-08-20 09:48:19 +00:00
|
|
|
SepGetSDGroup(
|
|
|
|
_In_ PSECURITY_DESCRIPTOR _SecurityDescriptor)
|
2014-02-08 14:48:15 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-08-11 14:24:45 +00:00
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Retrieves the length size of a set list of privileges structure.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] PrivilegeSet
|
|
|
|
* A valid set of privileges.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @return
|
|
|
|
* Returns the total length of a set of privileges.
|
|
|
|
*/
|
2017-06-13 20:58:10 +00:00
|
|
|
static
|
|
|
|
ULONG
|
2021-08-20 09:48:19 +00:00
|
|
|
SepGetPrivilegeSetLength(
|
|
|
|
_In_ PPRIVILEGE_SET PrivilegeSet)
|
2017-06-13 20:58:10 +00:00
|
|
|
{
|
|
|
|
if (PrivilegeSet == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (PrivilegeSet->PrivilegeCount == 0)
|
|
|
|
return (ULONG)(sizeof(PRIVILEGE_SET) - sizeof(LUID_AND_ATTRIBUTES));
|
|
|
|
|
|
|
|
return (ULONG)(sizeof(PRIVILEGE_SET) +
|
|
|
|
(PrivilegeSet->PrivilegeCount - 1) * sizeof(LUID_AND_ATTRIBUTES));
|
|
|
|
}
|
2014-02-08 14:48:15 +00:00
|
|
|
|
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
|
2021-08-11 14:24:45 +00:00
|
|
|
/**
|
|
|
|
* @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.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] SecurityDescriptor
|
|
|
|
* Security descriptor of the object that is being accessed.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] SubjectSecurityContext
|
|
|
|
* The captured subject security context.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] SubjectContextLocked
|
2022-02-05 21:38:22 +00:00
|
|
|
* 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.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] DesiredAccess
|
|
|
|
* Access right bitmask that the calling thread wants to acquire.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] PreviouslyGrantedAccess
|
|
|
|
* The access rights previously acquired in the past.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[out] Privileges
|
|
|
|
* The returned set of privileges.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] GenericMapping
|
|
|
|
* The generic mapping of access rights of an object type.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] AccessMode
|
|
|
|
* The processor request level mode.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[out] GrantedAccess
|
|
|
|
* A list of granted access rights.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @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).
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @return
|
|
|
|
* Returns TRUE if access onto the specific object is allowed, FALSE
|
|
|
|
* otherwise.
|
2014-02-08 14:48:15 +00:00
|
|
|
*/
|
|
|
|
BOOLEAN
|
|
|
|
NTAPI
|
2021-08-20 09:48:19 +00:00
|
|
|
SeAccessCheck(
|
|
|
|
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
|
|
_In_ PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
|
|
|
|
_In_ BOOLEAN SubjectContextLocked,
|
|
|
|
_In_ ACCESS_MASK DesiredAccess,
|
|
|
|
_In_ ACCESS_MASK PreviouslyGrantedAccess,
|
|
|
|
_Out_ PPRIVILEGE_SET* Privileges,
|
|
|
|
_In_ PGENERIC_MAPPING GenericMapping,
|
|
|
|
_In_ KPROCESSOR_MODE AccessMode,
|
|
|
|
_Out_ PACCESS_MASK GrantedAccess,
|
|
|
|
_Out_ PNTSTATUS AccessStatus)
|
2014-02-08 14:48:15 +00:00
|
|
|
{
|
|
|
|
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;
|
2015-11-27 12:06:59 +00:00
|
|
|
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;
|
|
|
|
}
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Call the internal function */
|
|
|
|
ret = SepAccessCheck(SecurityDescriptor,
|
2022-02-05 21:38:22 +00:00
|
|
|
SubjectSecurityContext->ClientToken,
|
|
|
|
SubjectSecurityContext->PrimaryToken,
|
|
|
|
NULL,
|
2014-02-08 14:48:15 +00:00
|
|
|
DesiredAccess,
|
2016-11-04 17:52:32 +00:00
|
|
|
NULL,
|
|
|
|
0,
|
2014-02-08 14:48:15 +00:00
|
|
|
PreviouslyGrantedAccess,
|
|
|
|
GenericMapping,
|
|
|
|
AccessMode,
|
2022-02-05 21:38:22 +00:00
|
|
|
FALSE,
|
|
|
|
Privileges,
|
2014-02-08 14:48:15 +00:00
|
|
|
GrantedAccess,
|
2022-02-05 21:38:22 +00:00
|
|
|
AccessStatus);
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Release the lock if needed */
|
|
|
|
if (!SubjectContextLocked)
|
|
|
|
SeUnlockSubjectContext(SubjectSecurityContext);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-08-11 14:24:45 +00:00
|
|
|
/**
|
|
|
|
* @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.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] SecurityDescriptor
|
|
|
|
* Security descriptor of the object that is being accessed.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] AccessState
|
|
|
|
* An access state to determine if the access token in the current
|
|
|
|
* security context of the object is an restricted token.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] DesiredAccess
|
|
|
|
* The access right bitmask where the calling thread wants to acquire.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] AccessMode
|
|
|
|
* Process level request mode.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @return
|
|
|
|
* Returns TRUE if access onto the specific object is allowed, FALSE
|
|
|
|
* otherwise.
|
2014-02-08 14:48:15 +00:00
|
|
|
*/
|
|
|
|
BOOLEAN
|
|
|
|
NTAPI
|
2021-08-20 09:48:19 +00:00
|
|
|
SeFastTraverseCheck(
|
|
|
|
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
|
|
_In_ PACCESS_STATE AccessState,
|
|
|
|
_In_ ACCESS_MASK DesiredAccess,
|
|
|
|
_In_ KPROCESSOR_MODE AccessMode)
|
2014-02-08 14:48:15 +00:00
|
|
|
{
|
|
|
|
PACL Dacl;
|
|
|
|
ULONG AceIndex;
|
|
|
|
PKNOWN_ACE Ace;
|
|
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
|
2015-09-01 01:45:59 +00:00
|
|
|
ASSERT(AccessMode != KernelMode);
|
2014-02-08 14:48:15 +00:00
|
|
|
|
|
|
|
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 */
|
2015-09-02 09:19:52 +00:00
|
|
|
if (Ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
|
2014-02-08 14:48:15 +00:00
|
|
|
{
|
|
|
|
/* 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 */
|
2015-09-02 09:19:52 +00:00
|
|
|
else if (Ace->Header.AceType == ACCESS_DENIED_ACE_TYPE)
|
2014-02-08 14:48:15 +00:00
|
|
|
{
|
2015-09-02 09:19:52 +00:00
|
|
|
/* Here, only check if it denies any access wanted and deny if so */
|
2014-02-08 14:48:15 +00:00
|
|
|
if (Ace->Mask & DesiredAccess)
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Faulty, deny */
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* SYSTEM CALLS ***************************************************************/
|
|
|
|
|
2021-08-11 14:24:45 +00:00
|
|
|
/**
|
|
|
|
* @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.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] SecurityDescriptor
|
|
|
|
* Security descriptor of the object that is being accessed.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] TokenHandle
|
|
|
|
* A handle to a token.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] DesiredAccess
|
|
|
|
* The access right bitmask where the calling thread wants to acquire.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] GenericMapping
|
|
|
|
* The generic mapping of access rights of an object type.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[out] PrivilegeSet
|
|
|
|
* The returned set of privileges.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in,out] PrivilegeSetLength
|
|
|
|
* The total length size of a set of privileges.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[out] GrantedAccess
|
|
|
|
* A list of granted access rights.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @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).
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @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.
|
2014-02-08 14:48:15 +00:00
|
|
|
*/
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
2021-08-20 09:48:19 +00:00
|
|
|
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)
|
2014-02-08 14:48:15 +00:00
|
|
|
{
|
|
|
|
PSECURITY_DESCRIPTOR CapturedSecurityDescriptor = NULL;
|
|
|
|
SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
|
|
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
|
|
ACCESS_MASK PreviouslyGrantedAccess = 0;
|
2017-06-13 20:58:10 +00:00
|
|
|
PPRIVILEGE_SET Privileges = NULL;
|
|
|
|
ULONG CapturedPrivilegeSetLength, RequiredPrivilegeSetLength;
|
2014-02-08 14:48:15 +00:00
|
|
|
PTOKEN Token;
|
|
|
|
NTSTATUS Status;
|
2022-02-05 21:38:22 +00:00
|
|
|
|
2014-02-08 14:48:15 +00:00
|
|
|
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));
|
2014-02-11 19:15:53 +00:00
|
|
|
|
2017-06-13 20:58:10 +00:00
|
|
|
/* Capture the privilege set length and the mapping */
|
|
|
|
CapturedPrivilegeSetLength = *PrivilegeSetLength;
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
_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;
|
|
|
|
}
|
|
|
|
|
2017-06-13 20:58:10 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2014-02-08 14:48:15 +00:00
|
|
|
/* 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 */
|
2021-09-12 14:07:44 +00:00
|
|
|
if (SepGetSDOwner(CapturedSecurityDescriptor) == NULL ||
|
|
|
|
SepGetSDGroup(CapturedSecurityDescriptor) == NULL)
|
2014-02-08 14:48:15 +00:00
|
|
|
{
|
|
|
|
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))
|
|
|
|
{
|
2021-09-12 14:07:44 +00:00
|
|
|
if (SepTokenIsOwner(Token, CapturedSecurityDescriptor, FALSE))
|
2014-02-08 14:48:15 +00:00
|
|
|
{
|
|
|
|
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 */
|
2021-09-12 14:07:44 +00:00
|
|
|
SepAccessCheck(CapturedSecurityDescriptor,
|
2022-02-05 21:38:22 +00:00
|
|
|
Token,
|
|
|
|
&SubjectSecurityContext.PrimaryToken,
|
|
|
|
NULL,
|
2014-02-08 14:48:15 +00:00
|
|
|
DesiredAccess,
|
2016-11-04 17:52:32 +00:00
|
|
|
NULL,
|
|
|
|
0,
|
2014-02-08 14:48:15 +00:00
|
|
|
PreviouslyGrantedAccess,
|
|
|
|
GenericMapping,
|
|
|
|
PreviousMode,
|
2022-02-05 21:38:22 +00:00
|
|
|
FALSE,
|
|
|
|
NULL,
|
2014-02-08 14:48:15 +00:00
|
|
|
GrantedAccess,
|
2022-02-05 21:38:22 +00:00
|
|
|
AccessStatus);
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2021-08-11 14:24:45 +00:00
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Determines whether security access could be granted or not on
|
|
|
|
* an object by the requestor who wants such access through type.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] SecurityDescriptor
|
|
|
|
* A security descriptor with information data for auditing.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] PrincipalSelfSid
|
|
|
|
* A principal self user SID.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] ClientToken
|
|
|
|
* A client access token.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] DesiredAccess
|
|
|
|
* The desired access masks rights requested by the caller.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] ObjectTypeList
|
|
|
|
* A list of object types.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] ObjectTypeLength
|
|
|
|
* The length size of the list.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] GenericMapping
|
|
|
|
* The generic mapping list of access masks rights.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] PrivilegeSet
|
|
|
|
* An array set of privileges.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in,out] PrivilegeSetLength
|
|
|
|
* The length size of the array set of privileges.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[out] GrantedAccess
|
|
|
|
* The returned granted access rights.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[out] AccessStatus
|
|
|
|
* The returned NTSTATUS code indicating the final results
|
|
|
|
* of auditing.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @return
|
|
|
|
* To be added...
|
|
|
|
*/
|
2014-02-08 14:48:15 +00:00
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
2021-08-20 09:48:19 +00:00
|
|
|
NtAccessCheckByType(
|
|
|
|
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
|
|
_In_ PSID PrincipalSelfSid,
|
|
|
|
_In_ HANDLE ClientToken,
|
|
|
|
_In_ ACCESS_MASK DesiredAccess,
|
|
|
|
_In_ POBJECT_TYPE_LIST ObjectTypeList,
|
|
|
|
_In_ ULONG ObjectTypeLength,
|
|
|
|
_In_ PGENERIC_MAPPING GenericMapping,
|
|
|
|
_In_ PPRIVILEGE_SET PrivilegeSet,
|
|
|
|
_Inout_ PULONG PrivilegeSetLength,
|
|
|
|
_Out_ PACCESS_MASK GrantedAccess,
|
|
|
|
_Out_ PNTSTATUS AccessStatus)
|
2014-02-08 14:48:15 +00:00
|
|
|
{
|
|
|
|
UNIMPLEMENTED;
|
|
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
2021-08-11 14:24:45 +00:00
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Determines whether security access could be granted or not on
|
|
|
|
* an object by the requestor who wants such access through
|
|
|
|
* type list.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] SecurityDescriptor
|
|
|
|
* A security descriptor with information data for auditing.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] PrincipalSelfSid
|
|
|
|
* A principal self user SID.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] ClientToken
|
|
|
|
* A client access token.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] DesiredAccess
|
|
|
|
* The desired access masks rights requested by the caller.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] ObjectTypeList
|
|
|
|
* A list of object types.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] ObjectTypeLength
|
|
|
|
* The length size of the list.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] GenericMapping
|
|
|
|
* The generic mapping list of access masks rights.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in] PrivilegeSet
|
|
|
|
* An array set of privileges.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[in,out] PrivilegeSetLength
|
|
|
|
* The length size of the array set of privileges.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[out] GrantedAccess
|
|
|
|
* The returned granted access rights.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @param[out] AccessStatus
|
|
|
|
* The returned NTSTATUS code indicating the final results
|
|
|
|
* of auditing.
|
2021-09-13 01:33:14 +00:00
|
|
|
*
|
2021-08-11 14:24:45 +00:00
|
|
|
* @return
|
|
|
|
* To be added...
|
|
|
|
*/
|
2014-02-08 14:48:15 +00:00
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
2021-08-20 09:48:19 +00:00
|
|
|
NtAccessCheckByTypeResultList(
|
|
|
|
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
|
|
_In_ PSID PrincipalSelfSid,
|
|
|
|
_In_ HANDLE ClientToken,
|
|
|
|
_In_ ACCESS_MASK DesiredAccess,
|
|
|
|
_In_ POBJECT_TYPE_LIST ObjectTypeList,
|
|
|
|
_In_ ULONG ObjectTypeLength,
|
|
|
|
_In_ PGENERIC_MAPPING GenericMapping,
|
|
|
|
_In_ PPRIVILEGE_SET PrivilegeSet,
|
|
|
|
_Inout_ PULONG PrivilegeSetLength,
|
|
|
|
_Out_ PACCESS_MASK GrantedAccess,
|
|
|
|
_Out_ PNTSTATUS AccessStatus)
|
2014-02-08 14:48:15 +00:00
|
|
|
{
|
|
|
|
UNIMPLEMENTED;
|
|
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* EOF */
|