2014-02-08 14:48:15 +00:00
|
|
|
/*
|
|
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
|
|
* PROJECT: ReactOS kernel
|
|
|
|
* FILE: ntoskrnl/se/accesschk.c
|
|
|
|
* PURPOSE: Security manager
|
|
|
|
*
|
|
|
|
* PROGRAMMERS: No programmer listed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
|
|
|
|
#include <ntoskrnl.h>
|
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
|
|
|
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
|
2016-11-04 17:52:32 +00:00
|
|
|
/*
|
|
|
|
* FIXME: Incomplete!
|
|
|
|
*/
|
2014-02-08 14:48:15 +00:00
|
|
|
BOOLEAN NTAPI
|
2016-11-04 17:52:32 +00:00
|
|
|
SepAccessCheck(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
2014-02-08 14:48:15 +00:00
|
|
|
IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
|
|
|
|
IN ACCESS_MASK DesiredAccess,
|
2014-02-09 16:57:42 +00:00
|
|
|
IN POBJECT_TYPE_LIST ObjectTypeList,
|
|
|
|
IN ULONG ObjectTypeListLength,
|
2014-02-08 14:48:15 +00:00
|
|
|
IN ACCESS_MASK PreviouslyGrantedAccess,
|
|
|
|
OUT PPRIVILEGE_SET* Privileges,
|
|
|
|
IN PGENERIC_MAPPING GenericMapping,
|
|
|
|
IN KPROCESSOR_MODE AccessMode,
|
2014-02-09 16:57:42 +00:00
|
|
|
OUT PACCESS_MASK GrantedAccessList,
|
|
|
|
OUT PNTSTATUS AccessStatusList,
|
|
|
|
IN BOOLEAN UseResultList)
|
2014-02-08 14:48:15 +00:00
|
|
|
{
|
|
|
|
ACCESS_MASK RemainingAccess;
|
|
|
|
ACCESS_MASK TempAccess;
|
|
|
|
ACCESS_MASK TempGrantedAccess = 0;
|
|
|
|
ACCESS_MASK TempDeniedAccess = 0;
|
|
|
|
PACCESS_TOKEN Token;
|
2014-02-09 16:57:42 +00:00
|
|
|
ULONG i, ResultListLength;
|
2014-02-08 14:48:15 +00:00
|
|
|
PACL Dacl;
|
|
|
|
BOOLEAN Present;
|
|
|
|
BOOLEAN Defaulted;
|
|
|
|
PACE CurrentAce;
|
|
|
|
PSID Sid;
|
|
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
2016-11-04 17:52:32 +00:00
|
|
|
DPRINT("SepAccessCheck()\n");
|
2014-10-26 18:39:58 +00:00
|
|
|
|
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 */
|
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;
|
|
|
|
|
|
|
|
Token = SubjectSecurityContext->ClientToken ?
|
2014-02-08 20:01:09 +00:00
|
|
|
SubjectSecurityContext->ClientToken : SubjectSecurityContext->PrimaryToken;
|
|
|
|
|
|
|
|
/* Check for ACCESS_SYSTEM_SECURITY and WRITE_OWNER access */
|
|
|
|
Status = SePrivilegePolicyCheck(&RemainingAccess,
|
|
|
|
&PreviouslyGrantedAccess,
|
|
|
|
NULL,
|
|
|
|
Token,
|
|
|
|
NULL,
|
|
|
|
UserMode);
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
/* RULE 1: Grant desired access if the object is unprotected */
|
|
|
|
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
|
|
|
|
{
|
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)
|
|
|
|
{
|
|
|
|
CurrentAce = (PACE)(Dacl + 1);
|
|
|
|
for (i = 0; i < Dacl->AceCount; i++)
|
|
|
|
{
|
|
|
|
if (!(CurrentAce->Header.AceFlags & INHERIT_ONLY_ACE))
|
|
|
|
{
|
|
|
|
Sid = (PSID)(CurrentAce + 1);
|
|
|
|
if (CurrentAce->Header.AceType == ACCESS_DENIED_ACE_TYPE)
|
|
|
|
{
|
|
|
|
if (SepSidInToken(Token, Sid))
|
|
|
|
{
|
|
|
|
/* Map access rights from the ACE */
|
|
|
|
TempAccess = CurrentAce->AccessMask;
|
|
|
|
RtlMapGenericMask(&TempAccess, GenericMapping);
|
|
|
|
|
|
|
|
/* Deny access rights that have not been granted yet */
|
|
|
|
TempDeniedAccess |= (TempAccess & ~TempGrantedAccess);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (CurrentAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
|
|
|
|
{
|
|
|
|
if (SepSidInToken(Token, Sid))
|
|
|
|
{
|
|
|
|
/* Map access rights from the ACE */
|
|
|
|
TempAccess = CurrentAce->AccessMask;
|
|
|
|
RtlMapGenericMask(&TempAccess, GenericMapping);
|
|
|
|
|
|
|
|
/* Grant access rights that have not been denied yet */
|
|
|
|
TempGrantedAccess |= (TempAccess & ~TempDeniedAccess);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DPRINT1("Unsupported ACE type 0x%lx\n", CurrentAce->Header.AceType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the next ACE */
|
|
|
|
CurrentAce = (PACE)((ULONG_PTR)CurrentAce + CurrentAce->Header.AceSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fail if some rights have not been granted */
|
|
|
|
RemainingAccess &= ~(MAXIMUM_ALLOWED | TempGrantedAccess);
|
|
|
|
if (RemainingAccess != 0)
|
|
|
|
{
|
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 */
|
2014-02-09 16:57:42 +00:00
|
|
|
PreviouslyGrantedAccess |= TempGrantedAccess;
|
|
|
|
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
|
|
|
|
{
|
2014-02-09 16:57:42 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
/* RULE 4: Grant rights according to the DACL */
|
|
|
|
CurrentAce = (PACE)(Dacl + 1);
|
|
|
|
for (i = 0; i < Dacl->AceCount; i++)
|
|
|
|
{
|
|
|
|
if (!(CurrentAce->Header.AceFlags & INHERIT_ONLY_ACE))
|
|
|
|
{
|
|
|
|
Sid = (PSID)(CurrentAce + 1);
|
|
|
|
if (CurrentAce->Header.AceType == ACCESS_DENIED_ACE_TYPE)
|
|
|
|
{
|
|
|
|
if (SepSidInToken(Token, Sid))
|
|
|
|
{
|
|
|
|
/* Map access rights from the ACE */
|
|
|
|
TempAccess = CurrentAce->AccessMask;
|
|
|
|
RtlMapGenericMask(&TempAccess, GenericMapping);
|
|
|
|
|
|
|
|
/* Leave if a remaining right must be denied */
|
|
|
|
if (RemainingAccess & TempAccess)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (CurrentAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
|
|
|
|
{
|
|
|
|
if (SepSidInToken(Token, Sid))
|
|
|
|
{
|
|
|
|
/* Map access rights from the ACE */
|
|
|
|
TempAccess = CurrentAce->AccessMask;
|
2014-10-26 18:39:58 +00:00
|
|
|
DPRINT("TempAccess 0x%08lx\n", TempAccess);
|
2014-02-08 14:48:15 +00:00
|
|
|
RtlMapGenericMask(&TempAccess, GenericMapping);
|
|
|
|
|
|
|
|
/* Remove granted rights */
|
2014-10-26 18:39:58 +00:00
|
|
|
DPRINT("RemainingAccess 0x%08lx TempAccess 0x%08lx\n", RemainingAccess, TempAccess);
|
2014-02-08 14:48:15 +00:00
|
|
|
RemainingAccess &= ~TempAccess;
|
2014-10-26 18:39:58 +00:00
|
|
|
DPRINT("RemainingAccess 0x%08lx\n", RemainingAccess);
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DPRINT1("Unsupported ACE type 0x%lx\n", CurrentAce->Header.AceType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the next ACE */
|
|
|
|
CurrentAce = (PACE)((ULONG_PTR)CurrentAce + CurrentAce->Header.AceSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
DPRINT("DesiredAccess %08lx\nPreviouslyGrantedAccess %08lx\nRemainingAccess %08lx\n",
|
|
|
|
DesiredAccess, PreviouslyGrantedAccess, RemainingAccess);
|
|
|
|
|
|
|
|
/* Fail if some rights have not been granted */
|
|
|
|
if (RemainingAccess != 0)
|
|
|
|
{
|
2015-02-15 00:26:41 +00:00
|
|
|
DPRINT("HACK: RemainingAccess = 0x%08lx DesiredAccess = 0x%08lx\n", RemainingAccess, DesiredAccess);
|
2014-10-26 18:39:58 +00:00
|
|
|
#if 0
|
|
|
|
/* HACK HACK HACK */
|
2014-02-09 16:57:42 +00:00
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
|
|
goto ReturnCommonStatus;
|
2014-10-26 18:39:58 +00:00
|
|
|
#endif
|
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
|
|
|
{
|
2014-10-26 18:39:58 +00:00
|
|
|
DPRINT1("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
|
|
|
}
|
|
|
|
|
2014-02-09 16:57:42 +00:00
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
goto ReturnCommonStatus;
|
|
|
|
|
|
|
|
ReturnCommonStatus:
|
|
|
|
ResultListLength = UseResultList ? ObjectTypeListLength : 1;
|
|
|
|
for (i = 0; i < ResultListLength; i++)
|
|
|
|
{
|
|
|
|
GrantedAccessList[i] = PreviouslyGrantedAccess;
|
|
|
|
AccessStatusList[i] = Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NT_SUCCESS(Status);
|
|
|
|
}
|
|
|
|
|
2014-02-08 14:48:15 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-06-13 20:58:10 +00:00
|
|
|
static
|
|
|
|
ULONG
|
|
|
|
SepGetPrivilegeSetLength(IN PPRIVILEGE_SET PrivilegeSet)
|
|
|
|
{
|
|
|
|
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 ***********************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
BOOLEAN
|
|
|
|
NTAPI
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
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,
|
|
|
|
SubjectSecurityContext,
|
|
|
|
DesiredAccess,
|
2016-11-04 17:52:32 +00:00
|
|
|
NULL,
|
|
|
|
0,
|
2014-02-08 14:48:15 +00:00
|
|
|
PreviouslyGrantedAccess,
|
|
|
|
Privileges,
|
|
|
|
GenericMapping,
|
|
|
|
AccessMode,
|
|
|
|
GrantedAccess,
|
2016-11-04 17:52:32 +00:00
|
|
|
AccessStatus,
|
|
|
|
FALSE);
|
2014-02-08 14:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Release the lock if needed */
|
|
|
|
if (!SubjectContextLocked)
|
|
|
|
SeUnlockSubjectContext(SubjectSecurityContext);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
BOOLEAN
|
|
|
|
NTAPI
|
|
|
|
SeFastTraverseCheck(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
|
|
IN PACCESS_STATE AccessState,
|
|
|
|
IN ACCESS_MASK DesiredAccess,
|
|
|
|
IN KPROCESSOR_MODE AccessMode)
|
|
|
|
{
|
|
|
|
PACL Dacl;
|
|
|
|
ULONG AceIndex;
|
|
|
|
PKNOWN_ACE Ace;
|
|
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
|
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 ***************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
NtAccessCheck(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
|
|
IN HANDLE TokenHandle,
|
|
|
|
IN ACCESS_MASK DesiredAccess,
|
|
|
|
IN PGENERIC_MAPPING GenericMapping,
|
|
|
|
OUT PPRIVILEGE_SET PrivilegeSet OPTIONAL,
|
|
|
|
IN OUT 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;
|
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;
|
|
|
|
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 */
|
|
|
|
if (SepGetSDOwner(SecurityDescriptor) == NULL || // FIXME: use CapturedSecurityDescriptor
|
|
|
|
SepGetSDGroup(SecurityDescriptor) == NULL) // FIXME: use CapturedSecurityDescriptor
|
|
|
|
{
|
|
|
|
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, SecurityDescriptor, FALSE)) // FIXME: use CapturedSecurityDescriptor
|
|
|
|
{
|
|
|
|
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(SecurityDescriptor, // FIXME: use CapturedSecurityDescriptor
|
|
|
|
&SubjectSecurityContext,
|
|
|
|
DesiredAccess,
|
2016-11-04 17:52:32 +00:00
|
|
|
NULL,
|
|
|
|
0,
|
2014-02-08 14:48:15 +00:00
|
|
|
PreviouslyGrantedAccess,
|
|
|
|
&PrivilegeSet, //FIXME
|
|
|
|
GenericMapping,
|
|
|
|
PreviousMode,
|
|
|
|
GrantedAccess,
|
2016-11-04 17:52:32 +00:00
|
|
|
AccessStatus,
|
|
|
|
FALSE);
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
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,
|
|
|
|
IN OUT PULONG PrivilegeSetLength,
|
|
|
|
OUT PACCESS_MASK GrantedAccess,
|
|
|
|
OUT PNTSTATUS AccessStatus)
|
|
|
|
{
|
|
|
|
UNIMPLEMENTED;
|
|
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
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,
|
|
|
|
IN OUT PULONG PrivilegeSetLength,
|
|
|
|
OUT PACCESS_MASK GrantedAccess,
|
|
|
|
OUT PNTSTATUS AccessStatus)
|
|
|
|
{
|
|
|
|
UNIMPLEMENTED;
|
|
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* EOF */
|