mirror of
https://github.com/reactos/reactos.git
synced 2024-08-30 15:18:33 +00:00
![George Bișoc](/assets/img/avatar_default.png)
Object ACEs are supported starting from Revision 4, the current code checks if the revision is above Revision 4. An Object ACE has to be strictly set on that revision, whereas Object ACLs can be of any revision starting from ACL_REVISION4.
1032 lines
30 KiB
C
1032 lines
30 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS system libraries
|
|
* PURPOSE: Security manager
|
|
* FILE: lib/rtl/acl.c
|
|
* PROGRAMER: David Welch <welch@cwcom.net>
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include <rtl.h>
|
|
#include <../../ntoskrnl/include/internal/se.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
RtlFirstFreeAce(IN PACL Acl,
|
|
OUT PACE* FirstFreeAce)
|
|
{
|
|
PACE Current;
|
|
ULONG_PTR AclEnd;
|
|
ULONG i;
|
|
PAGED_CODE_RTL();
|
|
|
|
/* Assume failure */
|
|
*FirstFreeAce = NULL;
|
|
|
|
/* Get the start and end pointers */
|
|
Current = (PACE)(Acl + 1);
|
|
AclEnd = (ULONG_PTR)Acl + Acl->AclSize;
|
|
|
|
/* Loop all the ACEs */
|
|
for (i = 0; i < Acl->AceCount; i++)
|
|
{
|
|
/* If any is beyond the DACL, bail out, otherwise keep going */
|
|
if ((ULONG_PTR)Current >= AclEnd) return FALSE;
|
|
Current = (PACE)((ULONG_PTR)Current + Current->Header.AceSize);
|
|
}
|
|
|
|
/* If the last spot is empty and still valid, return it */
|
|
if ((ULONG_PTR)Current <= AclEnd) *FirstFreeAce = Current;
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
RtlpAddData(IN PVOID AceList,
|
|
IN ULONG AceListLength,
|
|
IN PVOID Ace,
|
|
IN ULONG Offset)
|
|
{
|
|
/* Shift the buffer down */
|
|
if (Offset > 0)
|
|
{
|
|
RtlCopyMemory((PVOID)((ULONG_PTR)Ace + AceListLength),
|
|
Ace,
|
|
Offset);
|
|
}
|
|
|
|
/* Copy the new data in */
|
|
if (AceListLength) RtlCopyMemory(Ace, AceList, AceListLength);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
RtlpDeleteData(IN PVOID Ace,
|
|
IN ULONG AceSize,
|
|
IN ULONG Offset)
|
|
{
|
|
/* Move the data up */
|
|
if (AceSize < Offset)
|
|
{
|
|
RtlMoveMemory(Ace,
|
|
(PVOID)((ULONG_PTR)Ace + AceSize),
|
|
Offset - AceSize);
|
|
}
|
|
|
|
/* Zero the rest */
|
|
if ((Offset - AceSize) < Offset)
|
|
{
|
|
RtlZeroMemory((PVOID)((ULONG_PTR)Ace + Offset - AceSize), AceSize);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlpAddKnownAce(IN PACL Acl,
|
|
IN ULONG Revision,
|
|
IN ULONG Flags,
|
|
IN ACCESS_MASK AccessMask,
|
|
IN PSID Sid,
|
|
IN UCHAR Type)
|
|
{
|
|
PKNOWN_ACE Ace;
|
|
ULONG AceSize, InvalidFlags;
|
|
PAGED_CODE_RTL();
|
|
|
|
/* Check the validity of the SID */
|
|
if (!RtlValidSid(Sid)) return STATUS_INVALID_SID;
|
|
|
|
/* Check the validity of the revision */
|
|
if ((Acl->AclRevision > ACL_REVISION4) || (Revision > ACL_REVISION4))
|
|
{
|
|
return STATUS_REVISION_MISMATCH;
|
|
}
|
|
|
|
/* Pick the smallest of the revisions */
|
|
if (Revision < Acl->AclRevision) Revision = Acl->AclRevision;
|
|
|
|
/* Validate the flags */
|
|
if (Type == SYSTEM_AUDIT_ACE_TYPE)
|
|
{
|
|
InvalidFlags = Flags & ~(VALID_INHERIT_FLAGS |
|
|
SUCCESSFUL_ACCESS_ACE_FLAG |
|
|
FAILED_ACCESS_ACE_FLAG);
|
|
}
|
|
else
|
|
{
|
|
InvalidFlags = Flags & ~VALID_INHERIT_FLAGS;
|
|
}
|
|
|
|
/* If flags are invalid, bail out */
|
|
if (InvalidFlags != 0) return STATUS_INVALID_PARAMETER;
|
|
|
|
/* If ACL is invalid, bail out */
|
|
if (!RtlValidAcl(Acl)) return STATUS_INVALID_ACL;
|
|
|
|
/* If there's no free ACE, bail out */
|
|
if (!RtlFirstFreeAce(Acl, (PACE*)&Ace)) return STATUS_INVALID_ACL;
|
|
|
|
/* Calculate the size of the ACE and bail out if it's too small */
|
|
AceSize = RtlLengthSid(Sid) + sizeof(ACE);
|
|
if (!(Ace) || ((ULONG_PTR)Ace + AceSize > (ULONG_PTR)Acl + Acl->AclSize))
|
|
{
|
|
return STATUS_ALLOTTED_SPACE_EXCEEDED;
|
|
}
|
|
|
|
/* Initialize the header and common fields */
|
|
Ace->Header.AceFlags = (BYTE)Flags;
|
|
Ace->Header.AceType = Type;
|
|
Ace->Header.AceSize = (WORD)AceSize;
|
|
Ace->Mask = AccessMask;
|
|
|
|
/* Copy the SID */
|
|
RtlCopySid(RtlLengthSid(Sid), &Ace->SidStart, Sid);
|
|
|
|
/* Fill out the ACL header and return */
|
|
Acl->AceCount++;
|
|
Acl->AclRevision = (BYTE)Revision;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlpAddKnownObjectAce(IN PACL Acl,
|
|
IN ULONG Revision,
|
|
IN ULONG Flags,
|
|
IN ACCESS_MASK AccessMask,
|
|
IN GUID *ObjectTypeGuid OPTIONAL,
|
|
IN GUID *InheritedObjectTypeGuid OPTIONAL,
|
|
IN PSID Sid,
|
|
IN UCHAR Type)
|
|
{
|
|
PKNOWN_OBJECT_ACE Ace;
|
|
ULONG_PTR SidStart;
|
|
ULONG AceSize, InvalidFlags, AceObjectFlags = 0;
|
|
PAGED_CODE_RTL();
|
|
|
|
/* Check the validity of the SID */
|
|
if (!RtlValidSid(Sid)) return STATUS_INVALID_SID;
|
|
|
|
/* Check the validity of the revision */
|
|
if ((Acl->AclRevision > ACL_REVISION4) || (Revision != ACL_REVISION4))
|
|
{
|
|
return STATUS_REVISION_MISMATCH;
|
|
}
|
|
|
|
/* Pick the smallest of the revisions */
|
|
if (Revision < Acl->AclRevision) Revision = Acl->AclRevision;
|
|
|
|
/* Validate the flags */
|
|
if ((Type == SYSTEM_AUDIT_OBJECT_ACE_TYPE) ||
|
|
(Type == SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE))
|
|
{
|
|
InvalidFlags = Flags & ~(VALID_INHERIT_FLAGS |
|
|
SUCCESSFUL_ACCESS_ACE_FLAG | FAILED_ACCESS_ACE_FLAG);
|
|
}
|
|
else
|
|
{
|
|
InvalidFlags = Flags & ~VALID_INHERIT_FLAGS;
|
|
}
|
|
|
|
/* If flags are invalid, bail out */
|
|
if (InvalidFlags != 0) return STATUS_INVALID_PARAMETER;
|
|
|
|
/* If ACL is invalid, bail out */
|
|
if (!RtlValidAcl(Acl)) return STATUS_INVALID_ACL;
|
|
|
|
/* If there's no free ACE, bail out */
|
|
if (!RtlFirstFreeAce(Acl, (PACE*)&Ace)) return STATUS_INVALID_ACL;
|
|
|
|
/* Calculate the size of the ACE */
|
|
AceSize = RtlLengthSid(Sid) + sizeof(ACE) + sizeof(ULONG);
|
|
|
|
/* Add-in the size of the GUIDs if any and update flags as needed */
|
|
if (ObjectTypeGuid)
|
|
{
|
|
AceObjectFlags |= ACE_OBJECT_TYPE_PRESENT;
|
|
AceSize += sizeof(GUID);
|
|
}
|
|
if (InheritedObjectTypeGuid)
|
|
{
|
|
AceObjectFlags |= ACE_INHERITED_OBJECT_TYPE_PRESENT;
|
|
AceSize += sizeof(GUID);
|
|
}
|
|
|
|
/* Bail out if there's not enough space in the ACL */
|
|
if (!(Ace) || ((ULONG_PTR)Ace + AceSize > (ULONG_PTR)Acl + Acl->AclSize))
|
|
{
|
|
return STATUS_ALLOTTED_SPACE_EXCEEDED;
|
|
}
|
|
|
|
/* Initialize the header and common fields */
|
|
Ace->Header.AceFlags = (BYTE)Flags;
|
|
Ace->Header.AceType = Type;
|
|
Ace->Header.AceSize = (WORD)AceSize;
|
|
Ace->Mask = AccessMask;
|
|
Ace->Flags = AceObjectFlags;
|
|
|
|
/* Copy the GUIDs */
|
|
SidStart = (ULONG_PTR)&Ace->SidStart;
|
|
if (ObjectTypeGuid )
|
|
{
|
|
RtlCopyMemory((PVOID)SidStart, ObjectTypeGuid, sizeof(GUID));
|
|
SidStart += sizeof(GUID);
|
|
}
|
|
if (InheritedObjectTypeGuid)
|
|
{
|
|
RtlCopyMemory((PVOID)SidStart, InheritedObjectTypeGuid, sizeof(GUID));
|
|
SidStart += sizeof(GUID);
|
|
}
|
|
|
|
/* Copy the SID */
|
|
RtlCopySid(RtlLengthSid(Sid), (PSID)SidStart, Sid);
|
|
|
|
/* Fill out the ACL header and return */
|
|
Acl->AceCount++;
|
|
Acl->AclRevision = (BYTE)Revision;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlAddAccessAllowedAce(IN OUT PACL Acl,
|
|
IN ULONG Revision,
|
|
IN ACCESS_MASK AccessMask,
|
|
IN PSID Sid)
|
|
{
|
|
PAGED_CODE_RTL();
|
|
|
|
/* Call the worker function */
|
|
return RtlpAddKnownAce(Acl,
|
|
Revision,
|
|
0,
|
|
AccessMask,
|
|
Sid,
|
|
ACCESS_ALLOWED_ACE_TYPE);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlAddAccessAllowedAceEx(IN OUT PACL Acl,
|
|
IN ULONG Revision,
|
|
IN ULONG Flags,
|
|
IN ACCESS_MASK AccessMask,
|
|
IN PSID Sid)
|
|
{
|
|
PAGED_CODE_RTL();
|
|
|
|
/* Call the worker function */
|
|
return RtlpAddKnownAce(Acl,
|
|
Revision,
|
|
Flags,
|
|
AccessMask,
|
|
Sid,
|
|
ACCESS_ALLOWED_ACE_TYPE);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlAddAccessAllowedObjectAce(IN OUT PACL Acl,
|
|
IN ULONG Revision,
|
|
IN ULONG Flags,
|
|
IN ACCESS_MASK AccessMask,
|
|
IN GUID *ObjectTypeGuid OPTIONAL,
|
|
IN GUID *InheritedObjectTypeGuid OPTIONAL,
|
|
IN PSID Sid)
|
|
{
|
|
PAGED_CODE_RTL();
|
|
|
|
/* Is there no object data? */
|
|
if (!(ObjectTypeGuid) && !(InheritedObjectTypeGuid))
|
|
{
|
|
/* Use the usual routine */
|
|
return RtlpAddKnownAce(Acl,
|
|
Revision,
|
|
Flags,
|
|
AccessMask,
|
|
Sid,
|
|
ACCESS_ALLOWED_ACE_TYPE);
|
|
}
|
|
|
|
/* Use the object routine */
|
|
return RtlpAddKnownObjectAce(Acl,
|
|
Revision,
|
|
Flags,
|
|
AccessMask,
|
|
ObjectTypeGuid,
|
|
InheritedObjectTypeGuid,
|
|
Sid,
|
|
ACCESS_ALLOWED_OBJECT_ACE_TYPE);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlAddAccessDeniedAce(IN PACL Acl,
|
|
IN ULONG Revision,
|
|
IN ACCESS_MASK AccessMask,
|
|
IN PSID Sid)
|
|
{
|
|
PAGED_CODE_RTL();
|
|
|
|
/* Call the worker function */
|
|
return RtlpAddKnownAce(Acl,
|
|
Revision,
|
|
0,
|
|
AccessMask,
|
|
Sid,
|
|
ACCESS_DENIED_ACE_TYPE);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlAddAccessDeniedAceEx(IN OUT PACL Acl,
|
|
IN ULONG Revision,
|
|
IN ULONG Flags,
|
|
IN ACCESS_MASK AccessMask,
|
|
IN PSID Sid)
|
|
{
|
|
PAGED_CODE_RTL();
|
|
|
|
/* Call the worker function */
|
|
return RtlpAddKnownAce(Acl,
|
|
Revision,
|
|
Flags,
|
|
AccessMask,
|
|
Sid,
|
|
ACCESS_DENIED_ACE_TYPE);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlAddAccessDeniedObjectAce(IN OUT PACL Acl,
|
|
IN ULONG Revision,
|
|
IN ULONG Flags,
|
|
IN ACCESS_MASK AccessMask,
|
|
IN GUID *ObjectTypeGuid OPTIONAL,
|
|
IN GUID *InheritedObjectTypeGuid OPTIONAL,
|
|
IN PSID Sid)
|
|
{
|
|
PAGED_CODE_RTL();
|
|
|
|
/* Is there no object data? */
|
|
if (!(ObjectTypeGuid) && !(InheritedObjectTypeGuid))
|
|
{
|
|
/* Use the usual routine */
|
|
return RtlpAddKnownAce(Acl,
|
|
Revision,
|
|
Flags,
|
|
AccessMask,
|
|
Sid,
|
|
ACCESS_DENIED_ACE_TYPE);
|
|
}
|
|
|
|
/* There's object data, use the object routine */
|
|
return RtlpAddKnownObjectAce(Acl,
|
|
Revision,
|
|
Flags,
|
|
AccessMask,
|
|
ObjectTypeGuid,
|
|
InheritedObjectTypeGuid,
|
|
Sid,
|
|
ACCESS_DENIED_OBJECT_ACE_TYPE);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlAddAuditAccessAce(IN PACL Acl,
|
|
IN ULONG Revision,
|
|
IN ACCESS_MASK AccessMask,
|
|
IN PSID Sid,
|
|
IN BOOLEAN Success,
|
|
IN BOOLEAN Failure)
|
|
{
|
|
ULONG Flags = 0;
|
|
PAGED_CODE_RTL();
|
|
|
|
/* Add flags */
|
|
if (Success) Flags |= SUCCESSFUL_ACCESS_ACE_FLAG;
|
|
if (Failure) Flags |= FAILED_ACCESS_ACE_FLAG;
|
|
|
|
/* Call the worker routine */
|
|
return RtlpAddKnownAce(Acl,
|
|
Revision,
|
|
Flags,
|
|
AccessMask,
|
|
Sid,
|
|
SYSTEM_AUDIT_ACE_TYPE);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlAddAuditAccessAceEx(IN PACL Acl,
|
|
IN ULONG Revision,
|
|
IN ULONG Flags,
|
|
IN ACCESS_MASK AccessMask,
|
|
IN PSID Sid,
|
|
IN BOOLEAN Success,
|
|
IN BOOLEAN Failure)
|
|
{
|
|
PAGED_CODE_RTL();
|
|
|
|
/* Add flags */
|
|
if (Success) Flags |= SUCCESSFUL_ACCESS_ACE_FLAG;
|
|
if (Failure) Flags |= FAILED_ACCESS_ACE_FLAG;
|
|
|
|
/* Call the worker routine */
|
|
return RtlpAddKnownAce(Acl,
|
|
Revision,
|
|
Flags,
|
|
AccessMask,
|
|
Sid,
|
|
SYSTEM_AUDIT_ACE_TYPE);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlAddAuditAccessObjectAce(IN PACL Acl,
|
|
IN ULONG Revision,
|
|
IN ULONG Flags,
|
|
IN ACCESS_MASK AccessMask,
|
|
IN GUID *ObjectTypeGuid OPTIONAL,
|
|
IN GUID *InheritedObjectTypeGuid OPTIONAL,
|
|
IN PSID Sid,
|
|
IN BOOLEAN Success,
|
|
IN BOOLEAN Failure)
|
|
{
|
|
/* Add flags */
|
|
if (Success) Flags |= SUCCESSFUL_ACCESS_ACE_FLAG;
|
|
if (Failure) Flags |= FAILED_ACCESS_ACE_FLAG;
|
|
|
|
/* Is there no object data? */
|
|
if (!(ObjectTypeGuid) && !(InheritedObjectTypeGuid))
|
|
{
|
|
/* Call the normal routine */
|
|
return RtlpAddKnownAce(Acl,
|
|
Revision,
|
|
Flags,
|
|
AccessMask,
|
|
Sid,
|
|
SYSTEM_AUDIT_ACE_TYPE);
|
|
}
|
|
|
|
/* There's object data, use the object routine */
|
|
return RtlpAddKnownObjectAce(Acl,
|
|
Revision,
|
|
Flags,
|
|
AccessMask,
|
|
ObjectTypeGuid,
|
|
InheritedObjectTypeGuid,
|
|
Sid,
|
|
SYSTEM_AUDIT_OBJECT_ACE_TYPE);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlGetAce(IN PACL Acl,
|
|
IN ULONG AceIndex,
|
|
OUT PVOID *Ace)
|
|
{
|
|
ULONG i;
|
|
PAGED_CODE_RTL();
|
|
|
|
/* Bail out if the revision or the index are invalid */
|
|
if ((Acl->AclRevision < MIN_ACL_REVISION) ||
|
|
(Acl->AclRevision > MAX_ACL_REVISION) ||
|
|
(AceIndex >= Acl->AceCount))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Loop through the ACEs */
|
|
*Ace = (PVOID)((PACE)(Acl + 1));
|
|
for (i = 0; i < AceIndex; i++)
|
|
{
|
|
/* Bail out if an invalid ACE is ever found */
|
|
if ((ULONG_PTR)*Ace >= (ULONG_PTR)Acl + Acl->AclSize)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Keep going */
|
|
*Ace = (PVOID)((PACE)((ULONG_PTR)(*Ace) + ((PACE)(*Ace))->Header.AceSize));
|
|
}
|
|
|
|
/* Check if the last ACE is still valid */
|
|
if ((ULONG_PTR)*Ace >= (ULONG_PTR)Acl + Acl->AclSize)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* All good, return */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlAddAce(IN PACL Acl,
|
|
IN ULONG AclRevision,
|
|
IN ULONG StartingIndex,
|
|
IN PVOID AceList,
|
|
IN ULONG AceListLength)
|
|
{
|
|
PACE Ace, FreeAce;
|
|
USHORT NewAceCount;
|
|
ULONG Index;
|
|
PAGED_CODE_RTL();
|
|
|
|
/* Bail out if the ACL is invalid */
|
|
if (!RtlValidAcl(Acl)) return STATUS_INVALID_PARAMETER;
|
|
|
|
/* Bail out if there's no space */
|
|
if (!RtlFirstFreeAce(Acl, &FreeAce)) return STATUS_INVALID_PARAMETER;
|
|
|
|
/* Loop over all the ACEs, keeping track of new ACEs as we go along */
|
|
for (Ace = AceList, NewAceCount = 0;
|
|
Ace < (PACE)((ULONG_PTR)AceList + AceListLength);
|
|
NewAceCount++)
|
|
{
|
|
/* Make sure that the revision of this ACE is valid in this list.
|
|
The initial check looks strange, but it is what Windows does. */
|
|
if (Ace->Header.AceType <= ACCESS_MAX_MS_ACE_TYPE)
|
|
{
|
|
if (Ace->Header.AceType > ACCESS_MAX_MS_V3_ACE_TYPE)
|
|
{
|
|
if (AclRevision < ACL_REVISION4) return STATUS_INVALID_PARAMETER;
|
|
}
|
|
else if (Ace->Header.AceType > ACCESS_MAX_MS_V2_ACE_TYPE)
|
|
{
|
|
if (AclRevision < ACL_REVISION3) return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
/* Move to the next ACE */
|
|
Ace = (PACE)((ULONG_PTR)Ace + Ace->Header.AceSize);
|
|
}
|
|
|
|
/* Bail out if there's no more space for us */
|
|
if ((ULONG_PTR)Ace > ((ULONG_PTR)AceList + AceListLength))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Bail out if there's no free ACE spot, or if we would overflow it */
|
|
if (!(FreeAce) ||
|
|
((ULONG_PTR)FreeAce + AceListLength > (ULONG_PTR)Acl + Acl->AclSize))
|
|
{
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
/* Go down the list until we find our index */
|
|
Ace = (PACE)(Acl + 1);
|
|
for (Index = 0; (Index < StartingIndex) && (Index < Acl->AceCount); Index++)
|
|
{
|
|
Ace = (PACE)((ULONG_PTR)Ace + Ace->Header.AceSize);
|
|
}
|
|
|
|
/* Found where we want to do, add us to the list */
|
|
RtlpAddData(AceList,
|
|
AceListLength,
|
|
Ace,
|
|
(ULONG_PTR)FreeAce - (ULONG_PTR)Ace);
|
|
|
|
/* Update the header and return */
|
|
Acl->AceCount += NewAceCount;
|
|
Acl->AclRevision = (UCHAR)min(Acl->AclRevision, AclRevision);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlDeleteAce(IN PACL Acl,
|
|
IN ULONG AceIndex)
|
|
{
|
|
PACE FreeAce, Ace;
|
|
PAGED_CODE_RTL();
|
|
|
|
/* Bail out if the ACL is invalid */
|
|
if (!RtlValidAcl(Acl)) return STATUS_INVALID_PARAMETER;
|
|
|
|
/* Bail out if there's no space or if we're full */
|
|
if ((Acl->AceCount <= AceIndex) || !(RtlFirstFreeAce(Acl, &FreeAce)))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Enumerate until the indexed ACE is reached */
|
|
Ace = (PACE)(Acl + 1);
|
|
while (AceIndex--) Ace = (PACE)((ULONG_PTR)Ace + Ace->Header.AceSize);
|
|
|
|
/* Delete this ACE */
|
|
RtlpDeleteData(Ace,
|
|
Ace->Header.AceSize,
|
|
(ULONG)((ULONG_PTR)FreeAce - (ULONG_PTR)Ace));
|
|
|
|
/* Decrease an ACE and return success */
|
|
Acl->AceCount--;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlCreateAcl(IN PACL Acl,
|
|
IN ULONG AclSize,
|
|
IN ULONG AclRevision)
|
|
{
|
|
PAGED_CODE_RTL();
|
|
|
|
/* Bail out if too small */
|
|
if (AclSize < sizeof(ACL)) return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
/* Bail out if too large or invalid revision */
|
|
if ((AclRevision < MIN_ACL_REVISION) ||
|
|
(AclRevision > MAX_ACL_REVISION) ||
|
|
(AclSize > MAXUSHORT))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Setup the header */
|
|
Acl->AclSize = (USHORT)ROUND_UP(AclSize, 4);
|
|
Acl->AclRevision = (UCHAR)AclRevision;
|
|
Acl->AceCount = 0;
|
|
Acl->Sbz1 = 0;
|
|
Acl->Sbz2 = 0;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlQueryInformationAcl(IN PACL Acl,
|
|
IN PVOID Information,
|
|
IN ULONG InformationLength,
|
|
IN ACL_INFORMATION_CLASS InformationClass)
|
|
{
|
|
PACE Ace;
|
|
PACL_REVISION_INFORMATION RevisionInfo;
|
|
PACL_SIZE_INFORMATION SizeInfo;
|
|
PAGED_CODE_RTL();
|
|
|
|
/* Validate the ACL revision */
|
|
if ((Acl->AclRevision < MIN_ACL_REVISION) ||
|
|
(Acl->AclRevision > MAX_ACL_REVISION))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Check what the caller is querying */
|
|
switch (InformationClass)
|
|
{
|
|
/* Revision data */
|
|
case AclRevisionInformation:
|
|
|
|
/* Bail out if the buffer is too small */
|
|
if (InformationLength < sizeof(ACL_REVISION_INFORMATION))
|
|
{
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
/* Return the current revision */
|
|
RevisionInfo = (PACL_REVISION_INFORMATION)Information;
|
|
RevisionInfo->AclRevision = Acl->AclRevision;
|
|
break;
|
|
|
|
/* Size data */
|
|
case AclSizeInformation:
|
|
|
|
/* Bail out if the buffer is too small */
|
|
if (InformationLength < sizeof(ACL_SIZE_INFORMATION))
|
|
{
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
/* Bail out if there's no space in the ACL */
|
|
if (!RtlFirstFreeAce(Acl, &Ace)) return STATUS_INVALID_PARAMETER;
|
|
|
|
/* Read the number of ACEs and check if there was a free ACE */
|
|
SizeInfo = (PACL_SIZE_INFORMATION)Information;
|
|
SizeInfo->AceCount = Acl->AceCount;
|
|
if (Ace)
|
|
{
|
|
/* Return how much space there is in the ACL */
|
|
SizeInfo->AclBytesInUse = (ULONG_PTR)Ace - (ULONG_PTR)Acl;
|
|
SizeInfo->AclBytesFree = Acl->AclSize - SizeInfo->AclBytesInUse;
|
|
}
|
|
else
|
|
{
|
|
/* No free ACE, means the whole ACL is full */
|
|
SizeInfo->AclBytesInUse = Acl->AclSize;
|
|
SizeInfo->AclBytesFree = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* Anything else is illegal */
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
/* All done */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
RtlSetInformationAcl(IN PACL Acl,
|
|
IN PVOID Information,
|
|
IN ULONG InformationLength,
|
|
IN ACL_INFORMATION_CLASS InformationClass)
|
|
{
|
|
PACL_REVISION_INFORMATION Info ;
|
|
PAGED_CODE_RTL();
|
|
|
|
/* Validate the ACL revision */
|
|
if ((Acl->AclRevision < MIN_ACL_REVISION) ||
|
|
(Acl->AclRevision > MAX_ACL_REVISION))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* What is the caller trying to set? */
|
|
switch (InformationClass)
|
|
{
|
|
/* This is the only info class */
|
|
case AclRevisionInformation:
|
|
|
|
/* Make sure the buffer is large enough */
|
|
if (InformationLength < sizeof(ACL_REVISION_INFORMATION))
|
|
{
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
/* Make sure the new revision is within the acceptable bounds*/
|
|
Info = (PACL_REVISION_INFORMATION)Information;
|
|
if (Acl->AclRevision >= Info->AclRevision)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Set the new revision */
|
|
Acl->AclRevision = (BYTE)Info->AclRevision;
|
|
break;
|
|
|
|
default:
|
|
/* Anything else is invalid */
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
/* All good */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
RtlValidAcl(IN PACL Acl)
|
|
{
|
|
PACE_HEADER Ace;
|
|
PISID Sid;
|
|
ULONG i;
|
|
USHORT RequiredObjectAceSize;
|
|
PULONG Flags;
|
|
ULONG GuidSize;
|
|
PAGED_CODE_RTL();
|
|
|
|
_SEH2_TRY
|
|
{
|
|
/* First, validate the revision */
|
|
if ((Acl->AclRevision < MIN_ACL_REVISION) ||
|
|
(Acl->AclRevision > MAX_ACL_REVISION))
|
|
{
|
|
DPRINT1("Invalid ACL revision: %u\n", Acl->AclRevision);
|
|
_SEH2_YIELD(return FALSE);
|
|
}
|
|
|
|
/* Next, validate that the ACL is USHORT-aligned */
|
|
if (ROUND_DOWN(Acl->AclSize, sizeof(USHORT)) != Acl->AclSize)
|
|
{
|
|
DPRINT1("Misaligned ACL size: %u\n", Acl->AclSize);
|
|
_SEH2_YIELD(return FALSE);
|
|
}
|
|
|
|
/* And that it's big enough */
|
|
if (Acl->AclSize < sizeof(ACL))
|
|
{
|
|
DPRINT1("Too small ACL size: %u\n", Acl->AclSize);
|
|
_SEH2_YIELD(return FALSE);
|
|
}
|
|
|
|
/* Loop each ACE */
|
|
Ace = (PACE_HEADER)((ULONG_PTR)Acl + sizeof(ACL));
|
|
for (i = 0; i < Acl->AceCount; i++)
|
|
{
|
|
/* Validate we have space for this ACE header */
|
|
if (((ULONG_PTR)Ace + sizeof(ACE_HEADER)) >= ((ULONG_PTR)Acl + Acl->AclSize))
|
|
{
|
|
DPRINT1("Invalid ACE size\n");
|
|
_SEH2_YIELD(return FALSE);
|
|
}
|
|
|
|
/* Validate the length of this ACE */
|
|
if (ROUND_DOWN(Ace->AceSize, sizeof(USHORT)) != Ace->AceSize)
|
|
{
|
|
DPRINT1("Invalid ACE size: %lx\n", Ace->AceSize);
|
|
_SEH2_YIELD(return FALSE);
|
|
}
|
|
|
|
/* Validate we have space for the entire ACE */
|
|
if (((ULONG_PTR)Ace + Ace->AceSize) > ((ULONG_PTR)Acl + Acl->AclSize))
|
|
{
|
|
DPRINT1("Invalid ACE size %lx %lx\n", Ace->AceSize, Acl->AclSize);
|
|
_SEH2_YIELD(return FALSE);
|
|
}
|
|
|
|
/* Check what kind of ACE this is */
|
|
if (Ace->AceType <= ACCESS_MAX_MS_V2_ACE_TYPE)
|
|
{
|
|
/* Validate the length of this ACE */
|
|
if (ROUND_DOWN(Ace->AceSize, sizeof(ULONG)) != Ace->AceSize)
|
|
{
|
|
DPRINT1("Invalid ACE size\n");
|
|
_SEH2_YIELD(return FALSE);
|
|
}
|
|
|
|
/* The ACE size should at least have enough for the header */
|
|
if (Ace->AceSize < sizeof(ACE_HEADER))
|
|
{
|
|
DPRINT1("Invalid ACE size: %lx %lx\n", Ace->AceSize, sizeof(ACE_HEADER));
|
|
_SEH2_YIELD(return FALSE);
|
|
}
|
|
|
|
/* Check if the SID revision is valid */
|
|
Sid = (PISID)&((PKNOWN_ACE)Ace)->SidStart;
|
|
if (Sid->Revision != SID_REVISION)
|
|
{
|
|
DPRINT1("Invalid SID\n");
|
|
_SEH2_YIELD(return FALSE);
|
|
}
|
|
|
|
/* Check if the SID is out of bounds */
|
|
if (Sid->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES)
|
|
{
|
|
DPRINT1("Invalid SID\n");
|
|
_SEH2_YIELD(return FALSE);
|
|
}
|
|
|
|
/* The ACE size should at least have enough for the header and SID */
|
|
if (Ace->AceSize < (sizeof(ACE_HEADER) + RtlLengthSid(Sid)))
|
|
{
|
|
DPRINT1("Invalid ACE size\n");
|
|
_SEH2_YIELD(return FALSE);
|
|
}
|
|
}
|
|
else if (Ace->AceType == ACCESS_ALLOWED_OBJECT_ACE_TYPE ||
|
|
Ace->AceType == ACCESS_DENIED_OBJECT_ACE_TYPE)
|
|
{
|
|
/* Object ACEs are supported starting with Revision 4 */
|
|
if (Acl->AclRevision < ACL_REVISION4)
|
|
{
|
|
DPRINT1("Invalid ACL revision for Object ACE: %u\n", Acl->AclRevision);
|
|
_SEH2_YIELD(return FALSE);
|
|
}
|
|
|
|
/* Validate the length of this ACE */
|
|
if (ROUND_DOWN(Ace->AceSize, sizeof(ULONG)) != Ace->AceSize)
|
|
{
|
|
DPRINT1("Misaligned Object ACE size: %lx\n", Ace->AceSize);
|
|
_SEH2_YIELD(return FALSE);
|
|
}
|
|
|
|
/* The ACE size should at least have enough space for the known object ACE header */
|
|
if (Ace->AceSize < sizeof(KNOWN_OBJECT_ACE))
|
|
{
|
|
DPRINT1("Too small Object ACE size to hold KNOWN_OBJECT_ACE header: %lx\n", Ace->AceSize);
|
|
_SEH2_YIELD(return FALSE);
|
|
}
|
|
|
|
/* This ACL may have multiple Object ACEs so reset the size counter */
|
|
GuidSize = 0;
|
|
|
|
/* If we have GUIDs include them */
|
|
Flags = (PULONG)&((PKNOWN_OBJECT_ACE)Ace)->Flags;
|
|
if (*Flags & ACE_OBJECT_TYPE_PRESENT)
|
|
{
|
|
GuidSize += sizeof(GUID);
|
|
}
|
|
|
|
if (*Flags & ACE_INHERITED_OBJECT_TYPE_PRESENT)
|
|
{
|
|
GuidSize += sizeof(GUID);
|
|
}
|
|
|
|
/* Check if the SID revision is valid */
|
|
Sid = (PISID)((ULONG_PTR)&((PKNOWN_OBJECT_ACE)Ace)->SidStart + GuidSize);
|
|
if (Sid->Revision != SID_REVISION)
|
|
{
|
|
DPRINT1("Object ACE SID has invalid revision: %u\n", Sid->Revision);
|
|
_SEH2_YIELD(return FALSE);
|
|
}
|
|
|
|
/* Check if the SID is out of bounds */
|
|
if (Sid->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES)
|
|
{
|
|
DPRINT1("Object ACE SID's sub-authority count is out of bounds: %u\n", Sid->SubAuthorityCount);
|
|
_SEH2_YIELD(return FALSE);
|
|
}
|
|
|
|
/* The ACE size should at least have enough space for the known object ACE header, GUIDs and the SID */
|
|
RequiredObjectAceSize = (sizeof(KNOWN_OBJECT_ACE) - sizeof(ULONG)) + GuidSize + RtlLengthSid(Sid);
|
|
if (Ace->AceSize < RequiredObjectAceSize)
|
|
{
|
|
DPRINT1("Too small Object ACE size: AceSize %u RequiredSize %u\n", Ace->AceSize, RequiredObjectAceSize);
|
|
_SEH2_YIELD(return FALSE);
|
|
}
|
|
}
|
|
else if (Ace->AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE)
|
|
{
|
|
DPRINT1("Unsupported ACE in ReactOS, assuming valid\n");
|
|
}
|
|
else if ((Ace->AceType >= ACCESS_MIN_MS_OBJECT_ACE_TYPE) &&
|
|
(Ace->AceType <= ACCESS_MAX_MS_OBJECT_ACE_TYPE))
|
|
{
|
|
DPRINT1("Unsupported ACE in ReactOS, assuming valid\n");
|
|
}
|
|
else
|
|
{
|
|
/* Unknown ACE, see if it's as big as a header at least */
|
|
if (Ace->AceSize < sizeof(ACE_HEADER))
|
|
{
|
|
DPRINT1("Unknown ACE\n");
|
|
_SEH2_YIELD(return FALSE);
|
|
}
|
|
}
|
|
|
|
/* Move to the next ace */
|
|
Ace = (PACE_HEADER)((ULONG_PTR)Ace + Ace->AceSize);
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Something was invalid, fail */
|
|
_SEH2_YIELD(return FALSE);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* The ACL looks ok */
|
|
return TRUE;
|
|
}
|
|
|
|
/* EOF */
|