Windows2000/private/ntos/rtl/acledit.c
2020-09-30 17:12:32 +02:00

2833 lines
62 KiB
C

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
Acledit.c
Abstract:
This Module implements the Acl rtl editing functions that are defined in
ntseapi.h
Author:
Gary Kimura (GaryKi) 9-Nov-1989
Environment:
Pure Runtime Library Routine
Revision History:
--*/
#include <ntrtlp.h>
#include <seopaque.h>
// Define the local macros and procedure for this module
// Return a pointer to the first Ace in an Acl (even if the Acl is empty).
// PACE_HEADER
// FirstAce (
// IN PACL Acl
// );
#define FirstAce(Acl) ((PVOID)((PUCHAR)(Acl) + sizeof(ACL)))
// Return a pointer to the next Ace in a sequence (even if the input
// Ace is the one in the sequence).
// PACE_HEADER
// NextAce (
// IN PACE_HEADER Ace
// );
#define NextAce(Ace) ((PVOID)((PUCHAR)(Ace) + ((PACE_HEADER)(Ace))->AceSize))
#define LongAligned( ptr ) (LongAlign(ptr) == ((PVOID)(ptr)))
#define WordAligned( ptr ) (WordAlign(ptr) == ((PVOID)(ptr)))
VOID
RtlpAddData (
IN PVOID From,
IN ULONG FromSize,
IN PVOID To,
IN ULONG ToSize
);
VOID
RtlpDeleteData (
IN PVOID Data,
IN ULONG RemoveSize,
IN ULONG TotalSize
);
#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
#pragma alloc_text(PAGE,RtlpAddData)
#pragma alloc_text(PAGE,RtlpDeleteData)
#pragma alloc_text(PAGE,RtlCreateAcl)
#pragma alloc_text(PAGE,RtlValidAcl)
#pragma alloc_text(PAGE,RtlQueryInformationAcl)
#pragma alloc_text(PAGE,RtlSetInformationAcl)
#pragma alloc_text(PAGE,RtlAddAce)
#pragma alloc_text(PAGE,RtlDeleteAce)
#pragma alloc_text(PAGE,RtlGetAce)
#pragma alloc_text(PAGE,RtlAddAccessAllowedAce)
#pragma alloc_text(PAGE,RtlAddAccessAllowedAceEx)
#pragma alloc_text(PAGE,RtlAddAccessAllowedObjectAce)
#pragma alloc_text(PAGE,RtlAddAccessDeniedAce)
#pragma alloc_text(PAGE,RtlAddAccessDeniedAceEx)
#pragma alloc_text(PAGE,RtlAddAccessDeniedObjectAce)
#pragma alloc_text(PAGE,RtlAddAuditAccessAce)
#pragma alloc_text(PAGE,RtlAddAuditAccessAceEx)
#pragma alloc_text(PAGE,RtlAddAuditAccessObjectAce)
#pragma alloc_text(PAGE,RtlFirstFreeAce)
#endif
NTSTATUS
RtlCreateAcl (
IN PACL Acl,
IN ULONG AclLength,
IN ULONG AclRevision
)
/*++
Routine Description:
This routine initializes an ACL data structure. After initialization
it is an ACL with no ACE (i.e., a deny all access type ACL)
Arguments:
Acl - Supplies the buffer containing the ACL being initialized
AclLength - Supplies the length of the ace buffer in bytes
AclRevision - Supplies the revision for this Acl
Return Value:
NTSTATUS - STATUS_SUCCESS if successful
STATUS_BUFFER_TOO_SMALL if the AclLength is too small,
STATUS_INVALID_PARAMETER if the revision is out of range
--*/
{
RTL_PAGED_CODE();
// Check to see the size of the buffer is large enough to hold at
// least the ACL header
if (AclLength < sizeof(ACL)) {
// Buffer to small even for the ACL header
return STATUS_BUFFER_TOO_SMALL;
}
// Check to see if the revision is currently valid. Later versions
// of this procedure might accept more revision levels
if (AclRevision < MIN_ACL_REVISION || AclRevision > MAX_ACL_REVISION) {
// Revision not current
return STATUS_INVALID_PARAMETER;
}
if ( AclLength > MAXUSHORT ) {
return STATUS_INVALID_PARAMETER;
}
// Initialize the ACL
Acl->AclRevision = (UCHAR)AclRevision; // Used to hardwire ACL_REVISION2 here
Acl->Sbz1 = 0;
Acl->AclSize = (USHORT) (AclLength & 0xfffc);
Acl->AceCount = 0;
Acl->Sbz2 = 0;
// And return to our caller
return STATUS_SUCCESS;
}
BOOLEAN
RtlValidAcl (
IN PACL Acl
)
/*++
Routine Description:
This procedure validates an ACL.
This involves validating the revision level of the ACL and ensuring
that the number of ACEs specified in the AceCount fit in the space
specified by the AclSize field of the ACL header.
Arguments:
Acl - Pointer to the ACL structure to validate.
Return Value:
BOOLEAN - TRUE if the structure of Acl is valid.
--*/
{
RTL_PAGED_CODE();
try {
PACE_HEADER Ace;
PISID Sid;
PISID Sid2;
ULONG i;
UCHAR AclRevision = ACL_REVISION2;
// Check the ACL revision level
if (!ValidAclRevision(Acl)) {
return(FALSE);
}
if (!WordAligned(&Acl->AclSize)) {
return(FALSE);
}
if (Acl->AclSize < sizeof(ACL)) {
return(FALSE);
}
// Validate all of the ACEs.
Ace = ((PVOID)((PUCHAR)(Acl) + sizeof(ACL)));
for (i = 0; i < Acl->AceCount; i++) {
// Check to make sure we haven't overrun the Acl buffer
// with our ace pointer. Make sure the ACE_HEADER is in
// the ACL also.
if ((PUCHAR)Ace + sizeof(ACE_HEADER) >= ((PUCHAR)Acl + Acl->AclSize)) {
return(FALSE);
}
if (!WordAligned(&Ace->AceSize)) {
return(FALSE);
}
if ((PUCHAR)Ace + Ace->AceSize > ((PUCHAR)Acl + Acl->AclSize)) {
return(FALSE);
}
// It is now safe to reference fields in the ACE header.
// The ACE header fits into the ACL, if this is a known type of ACE,
// make sure the SID is within the bounds of the ACE
if (IsKnownAceType(Ace)) {
if (!LongAligned(Ace->AceSize)) {
return(FALSE);
}
if (Ace->AceSize < sizeof(KNOWN_ACE) - sizeof(ULONG) + sizeof(SID) - sizeof(ULONG)) {
return(FALSE);
}
// It's now safe to reference the parts of the SID structure, though
// not the SID itself.
Sid = (PISID) & (((PKNOWN_ACE)Ace)->SidStart);
if (Sid->Revision != SID_REVISION) {
return(FALSE);
}
if (Sid->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) {
return(FALSE);
}
// SeLengthSid computes the size of the SID based on the subauthority count,
// so it is safe to use even though we don't know that the body of the SID
// is safe to reference.
if (Ace->AceSize < sizeof(KNOWN_ACE) - sizeof(ULONG) + SeLengthSid( Sid )) {
return(FALSE);
}
// If it's a compound ACE, then perform roughly the same set of tests, but
// check the validity of both SIDs.
} else if (IsCompoundAceType(Ace)) {
// Compound ACEs became valid in revision 3
if ( Acl->AclRevision < ACL_REVISION3 ) {
return FALSE;
}
if (!LongAligned(Ace->AceSize)) {
return(FALSE);
}
if (Ace->AceSize < sizeof(KNOWN_COMPOUND_ACE) - sizeof(ULONG) + sizeof(SID)) {
return(FALSE);
}
// The only currently defined Compound ACE is an Impersonation ACE.
if (((PKNOWN_COMPOUND_ACE)Ace)->CompoundAceType != COMPOUND_ACE_IMPERSONATION) {
return(FALSE);
}
// Examine the first SID and make sure it's structurally valid,
// and it lies within the boundaries of the ACE.
Sid = (PISID) & (((PKNOWN_COMPOUND_ACE)Ace)->SidStart);
if (Sid->Revision != SID_REVISION) {
return(FALSE);
}
if (Sid->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) {
return(FALSE);
}
// Compound ACEs contain two SIDs. Make sure this ACE is large enough to contain
// not only the first SID, but the body of the 2nd.
if (Ace->AceSize < sizeof(KNOWN_COMPOUND_ACE) - sizeof(ULONG) + SeLengthSid( Sid ) + sizeof(SID)) {
return(FALSE);
}
// It is safe to reference the interior of the 2nd SID.
Sid2 = (PISID) ((PUCHAR)Sid + SeLengthSid( Sid ));
if (Sid2->Revision != SID_REVISION) {
return(FALSE);
}
if (Sid2->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) {
return(FALSE);
}
if (Ace->AceSize < sizeof(KNOWN_COMPOUND_ACE) - sizeof(ULONG) + SeLengthSid( Sid ) + SeLengthSid( Sid2 )) {
return(FALSE);
}
// If it's an object ACE, then perform roughly the same set of tests.
} else if (IsObjectAceType(Ace)) {
ULONG GuidSize=0;
// Object ACEs became valid in revision 4
if ( Acl->AclRevision < ACL_REVISION4 ) {
return FALSE;
}
if (!LongAligned(Ace->AceSize)) {
return(FALSE);
}
// Ensure there is room for the ACE header.
if (Ace->AceSize < sizeof(KNOWN_OBJECT_ACE) - sizeof(ULONG)) {
return(FALSE);
}
// Ensure there is room for the GUIDs and SID header
if ( RtlObjectAceObjectTypePresent( Ace ) ) {
GuidSize += sizeof(GUID);
}
if ( RtlObjectAceInheritedObjectTypePresent( Ace ) ) {
GuidSize += sizeof(GUID);
}
if (Ace->AceSize < sizeof(KNOWN_OBJECT_ACE) - sizeof(ULONG) + GuidSize + sizeof(SID)) {
return(FALSE);
}
// It's now safe to reference the parts of the SID structure, though
// not the SID itself.
Sid = (PISID) RtlObjectAceSid( Ace );
if (Sid->Revision != SID_REVISION) {
return(FALSE);
}
if (Sid->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) {
return(FALSE);
}
if (Ace->AceSize < sizeof(KNOWN_OBJECT_ACE) - sizeof(ULONG) + GuidSize + SeLengthSid( Sid ) ) {
return(FALSE);
}
}
// And move Ace to the next ace position
Ace = ((PVOID)((PUCHAR)(Ace) + ((PACE_HEADER)(Ace))->AceSize));
}
return(TRUE);
} except(EXCEPTION_EXECUTE_HANDLER) {
return FALSE;
}
}
NTSTATUS
RtlQueryInformationAcl (
IN PACL Acl,
OUT PVOID AclInformation,
IN ULONG AclInformationLength,
IN ACL_INFORMATION_CLASS AclInformationClass
)
/*++
Routine Description:
This routine returns to the caller information about an ACL. The requested
information can be AclRevisionInformation, or AclSizeInformation.
Arguments:
Acl - Supplies the Acl being examined
AclInformation - Supplies the buffer to receive the information being
requested
AclInformationLength - Supplies the length of the AclInformation buffer
in bytes
AclInformationClass - Supplies the type of information being requested
Return Value:
NTSTATUS - STATUS_SUCCESS if successful and an appropriate error
status otherwise
--*/
{
PACL_REVISION_INFORMATION RevisionInfo;
PACL_SIZE_INFORMATION SizeInfo;
PVOID FirstFree;
NTSTATUS Status;
RTL_PAGED_CODE();
// Check the ACL revision level
if (!ValidAclRevision( Acl )) {
return STATUS_INVALID_PARAMETER;
}
// Case on the information class being requested
switch (AclInformationClass) {
case AclRevisionInformation:
// Make sure the buffer size is correct
if (AclInformationLength < sizeof(ACL_REVISION_INFORMATION)) {
return STATUS_BUFFER_TOO_SMALL;
}
// Get the Acl revision and return
RevisionInfo = (PACL_REVISION_INFORMATION)AclInformation;
RevisionInfo->AclRevision = Acl->AclRevision;
break;
case AclSizeInformation:
// Make sure the buffer size is correct
if (AclInformationLength < sizeof(ACL_SIZE_INFORMATION)) {
return STATUS_BUFFER_TOO_SMALL;
}
// Locate the first free spot in the Acl
if (!RtlFirstFreeAce( Acl, &FirstFree )) {
// The input Acl is ill-formed
return STATUS_INVALID_PARAMETER;
}
// Given a pointer to the first free spot we can now easily compute
// the number of free bytes and used bytes in the Acl.
SizeInfo = (PACL_SIZE_INFORMATION)AclInformation;
SizeInfo->AceCount = Acl->AceCount;
if (FirstFree == NULL) {
// With a null first free we don't have any free space in the Acl
SizeInfo->AclBytesInUse = Acl->AclSize;
SizeInfo->AclBytesFree = 0;
} else {
// The first free is not null so we have some free room left in
// the acl
SizeInfo->AclBytesInUse = (ULONG)((PUCHAR)FirstFree - (PUCHAR)Acl);
SizeInfo->AclBytesFree = Acl->AclSize - SizeInfo->AclBytesInUse;
}
break;
default:
return STATUS_INVALID_INFO_CLASS;
}
// and return to our caller
return STATUS_SUCCESS;
}
NTSTATUS
RtlSetInformationAcl (
IN PACL Acl,
IN PVOID AclInformation,
IN ULONG AclInformationLength,
IN ACL_INFORMATION_CLASS AclInformationClass
)
/*++
Routine Description:
This routine sets the state of an ACL. For now only the revision
level can be set and for now only a revision level of 1 is accepted
so this procedure is rather simple
Arguments:
Acl - Supplies the Acl being altered
AclInformation - Supplies the buffer containing the information being
set
AclInformationLength - Supplies the length of the Acl information buffer
AclInformationClass - Supplies the type of information begin set
Return Value:
NTSTATUS - STATUS_SUCCESS if successful and an appropriate error
status otherwise
--*/
{
PACL_REVISION_INFORMATION RevisionInfo;
RTL_PAGED_CODE();
// Check the ACL revision level
if (!ValidAclRevision( Acl )) {
return STATUS_INVALID_PARAMETER;
}
// Case on the information class being requested
switch (AclInformationClass) {
case AclRevisionInformation:
// Make sure the buffer size is correct
if (AclInformationLength < sizeof(ACL_REVISION_INFORMATION)) {
return STATUS_BUFFER_TOO_SMALL;
}
// Get the Acl requested ACL revision level
RevisionInfo = (PACL_REVISION_INFORMATION)AclInformation;
// Don't let them lower the revision of an ACL.
if (RevisionInfo->AclRevision < Acl->AclRevision ) {
return STATUS_INVALID_PARAMETER;
}
// Assign the new revision.
Acl->AclRevision = (UCHAR)RevisionInfo->AclRevision;
break;
default:
return STATUS_INVALID_INFO_CLASS;
}
// and return to our caller
return STATUS_SUCCESS;
}
NTSTATUS
RtlAddAce (
IN OUT PACL Acl,
IN ULONG AceRevision,
IN ULONG StartingAceIndex,
IN PVOID AceList,
IN ULONG AceListLength
)
/*++
Routine Description:
This routine adds a string of ACEs to an ACL.
Arguments:
Acl - Supplies the Acl being modified
AceRevision - Supplies the Acl/Ace revision of the ACE being added
StartingAceIndex - Supplies the ACE index which will be the index of
the first ace inserted in the acl. 0 for the beginning of the list
and MAXULONG for the end of the list.
AceList - Supplies the list of Aces to be added to the Acl
AceListLength - Supplies the size, in bytes, of the AceList buffer
Return Value:
NTSTATUS - STATUS_SUCCESS if successful, and an appropriate error
status otherwise
--*/
{
PVOID FirstFree;
PACE_HEADER Ace;
ULONG NewAceCount;
PVOID AcePosition;
ULONG i;
UCHAR NewRevision;
RTL_PAGED_CODE();
// Check the ACL structure
if (!RtlValidAcl(Acl)) {
return STATUS_INVALID_PARAMETER;
}
// Locate the first free ace and check to see that the Acl is
// well formed.
if (!RtlFirstFreeAce( Acl, &FirstFree )) {
return STATUS_INVALID_PARAMETER;
}
// If the AceRevision is greater than the ACL revision, then we want to
// increase the ACL revision to be the same as the new ACE revision.
// We can do this because our previously defined ACE types ( 0 -> 3 ) have
// not changed structure nor been discontinued in the new revision. So
// we can bump the revision and the older types will not be misinterpreted.
// Compute what the final revision of the ACL is going to be, and save it
// for later so we can update it once we know we're going to succeed.
NewRevision = (UCHAR)AceRevision > Acl->AclRevision ? (UCHAR)AceRevision : Acl->AclRevision;
// Check that the AceList is well formed, we do this by simply zooming
// down the Ace list until we're equal to or have exceeded the ace list
// length. If we are equal to the length then we're well formed otherwise
// we're ill-formed. We'll also calculate how many Ace's there are
// in the AceList
// In addition, now we have to make sure that we haven't been handed an
// ACE type that is inappropriate for the AceRevision that was passed
// in.
for (Ace = AceList, NewAceCount = 0;
Ace < (PACE_HEADER)((PUCHAR)AceList + AceListLength);
Ace = NextAce( Ace ), NewAceCount++) {
// Ensure the ACL revision allows this ACE type.
if ( Ace->AceType <= ACCESS_MAX_MS_V2_ACE_TYPE ) {
// V2 ACE are always valid.
} else if ( Ace->AceType <= ACCESS_MAX_MS_V3_ACE_TYPE ) {
if ( AceRevision < ACL_REVISION3 ) {
return STATUS_INVALID_PARAMETER;
}
} else if ( Ace->AceType <= ACCESS_MAX_MS_V4_ACE_TYPE ) {
if ( AceRevision < ACL_REVISION4 ) {
return STATUS_INVALID_PARAMETER;
}
}
}
// Check to see if we've exceeded the ace list length
if (Ace > (PACE_HEADER)((PUCHAR)AceList + AceListLength)) {
return STATUS_INVALID_PARAMETER;
}
// Check to see if there is enough room in the Acl to store the additional
// Ace list
if (FirstFree == NULL ||
(PUCHAR)FirstFree + AceListLength > (PUCHAR)Acl + Acl->AclSize) {
return STATUS_BUFFER_TOO_SMALL;
}
// All of the input has checked okay, we now need to locate the position
// where to insert the new ace list. We won't check the acl for
// validity because we did earlier when got the first free ace position.
AcePosition = FirstAce( Acl );
for (i = 0; i < StartingAceIndex && i < Acl->AceCount; i++) {
AcePosition = NextAce( AcePosition );
}
// Now Ace points to where we want to insert the ace list, We do the
// insertion by adding ace list to the acl and shoving over the remainder
// of the list down the acl. We know this will work because we earlier
// check to make sure the new acl list will fit in the acl size
RtlpAddData( AceList, AceListLength,
AcePosition, (ULONG) ((PUCHAR)FirstFree - (PUCHAR)AcePosition));
// Update the Acl Header
Acl->AceCount = (USHORT)(Acl->AceCount + NewAceCount);
Acl->AclRevision = NewRevision;
// And return to our caller
return STATUS_SUCCESS;
}
NTSTATUS
RtlDeleteAce (
IN OUT PACL Acl,
IN ULONG AceIndex
)
/*++
Routine Description:
This routine deletes one ACE from an ACL.
Arguments:
Acl - Supplies the Acl being modified
AceIndex - Supplies the index of the Ace to delete.
Return Value:
NTSTATUS - STATUS_SUCCESS if successful and an appropriate error
status otherwise
--*/
{
PVOID FirstFree;
PACE_HEADER Ace;
ULONG i;
RTL_PAGED_CODE();
// Check the ACL structure
if (!RtlValidAcl(Acl)) {
return STATUS_INVALID_PARAMETER;
}
// Make sure the AceIndex is within proper range, it's ulong so we know
// it can't be negative
if (AceIndex >= Acl->AceCount) {
return STATUS_INVALID_PARAMETER;
}
// Locate the first free spot, this will tell us how much data
// we'll need to colapse. If the results is false then the acl is
// ill-formed
if (!RtlFirstFreeAce( Acl, &FirstFree )) {
return STATUS_INVALID_PARAMETER;
}
// Now locate the ace that we're going to delete. This loop
// doesn't need to check the acl for being well formed.
Ace = FirstAce( Acl );
for (i = 0; i < AceIndex; i++) {
Ace = NextAce( Ace );
}
// We've found the ace to delete to simply copy over the rest of
// the acl over this ace. The delete data procedure also deletes
// rest of the string that it's moving over so we don't have to
RtlpDeleteData( Ace, Ace->AceSize, (ULONG) ((PUCHAR)FirstFree - (PUCHAR)Ace));
// Update the Acl header
Acl->AceCount--;
// And return to our caller
return STATUS_SUCCESS;
}
NTSTATUS
RtlGetAce (
IN PACL Acl,
ULONG AceIndex,
OUT PVOID *Ace
)
/*++
Routine Description:
This routine returns a pointer to an ACE in an ACl referenced by
ACE index
Arguments:
Acl - Supplies the ACL being queried
AceIndex - Supplies the Ace index to locate
Ace - Receives the address of the ACE within the ACL
Return Value:
NTSTATUS - STATUS_SUCCESS if successful and an appropriate error
status otherwise
--*/
{
ULONG i;
RTL_PAGED_CODE();
// Check the ACL revision level
if (!ValidAclRevision(Acl)) {
return STATUS_INVALID_PARAMETER;
}
// Check the AceIndex against the Ace count of the Acl, it's ulong so
// we know it can't be negative
if (AceIndex >= Acl->AceCount) {
return STATUS_INVALID_PARAMETER;
}
// To find the Ace requested by zooming down the Ace List.
*Ace = FirstAce( Acl );
for (i = 0; i < AceIndex; i++) {
// Check to make sure we haven't overrun the Acl buffer
// with our ace pointer. If we have then our input is bogus
if (*Ace >= (PVOID)((PUCHAR)Acl + Acl->AclSize)) {
return STATUS_INVALID_PARAMETER;
}
// And move Ace to the next ace position
*Ace = NextAce( *Ace );
}
// Now Ace points to the Ace we're after, but make sure we aren't
// beyond the Acl.
if (*Ace >= (PVOID)((PUCHAR)Acl + Acl->AclSize)) {
return STATUS_INVALID_PARAMETER;
}
// The Ace is still within the Acl so return success to our caller
return STATUS_SUCCESS;
}
NTSTATUS
RtlAddCompoundAce (
IN PACL Acl,
IN ULONG AceRevision,
IN UCHAR CompoundAceType,
IN ACCESS_MASK AccessMask,
IN PSID ServerSid,
IN PSID ClientSid
)
/*++
Routine Description:
This routine adds a KNOWN_COMPOUND_ACE to an ACL. This is
expected to be a common form of ACL modification.
Arguments:
Acl - Supplies the Acl being modified
AceRevision - Supplies the Acl/Ace revision of the ACE being added
CompoundAceType - Supplies the type of compound ACE being added.
Currently the only defined type is COMPOUND_ACE_IMPERSONATION.
AccessMask - The mask of accesses to be granted to the specified SID pair.
ServerSid - Pointer to the Server SID to be placed in the ACE.
ClientSid - Pointer to the Client SID to be placed in the ACE.
Return Value:
NTSTATUS - STATUS_SUCCESS if successful and an appropriate error
status otherwise
STATUS_INVALID_PARAMETER - The AceFlags parameter was invalid.
--*/
{
PVOID FirstFree;
USHORT AceSize;
PKNOWN_COMPOUND_ACE GrantAce;
UCHAR NewRevision;
RTL_PAGED_CODE();
// Validate the structure of the SID
if (!RtlValidSid(ServerSid) || !RtlValidSid(ClientSid)) {
return STATUS_INVALID_SID;
}
// Check the ACL & ACE revision levels
// Compund ACEs become valid in version 3.
if ( Acl->AclRevision > ACL_REVISION4 ||
AceRevision < ACL_REVISION3 ||
AceRevision > ACL_REVISION4 ) {
return STATUS_REVISION_MISMATCH;
}
// Calculate the new revision of the ACL. The new revision is the maximum
// of the old revision and and new ACE's revision. This is possible because
// the format of previously defined ACEs did not change across revisions.
NewRevision = Acl->AclRevision > (UCHAR)AceRevision ? Acl->AclRevision : (UCHAR)AceRevision;
// Locate the first free ace and check to see that the Acl is
// well formed.
if (!RtlValidAcl( Acl )) {
return STATUS_INVALID_ACL;
}
if (!RtlFirstFreeAce( Acl, &FirstFree )) {
return STATUS_INVALID_ACL;
}
// Check to see if there is enough room in the Acl to store the new
// ACE
AceSize = (USHORT)(sizeof(KNOWN_COMPOUND_ACE) -
sizeof(ULONG) +
SeLengthSid(ClientSid) +
SeLengthSid(ServerSid)
);
if ( FirstFree == NULL ||
((PUCHAR)FirstFree + AceSize > ((PUCHAR)Acl + Acl->AclSize))
) {
return STATUS_ALLOTTED_SPACE_EXCEEDED;
}
// Add the ACE to the end of the ACL
GrantAce = (PKNOWN_COMPOUND_ACE)FirstFree;
GrantAce->Header.AceFlags = 0;
GrantAce->Header.AceType = ACCESS_ALLOWED_COMPOUND_ACE_TYPE;
GrantAce->Header.AceSize = AceSize;
GrantAce->Mask = AccessMask;
GrantAce->CompoundAceType = CompoundAceType;
RtlCopySid( SeLengthSid(ServerSid), (PSID)(&GrantAce->SidStart), ServerSid );
RtlCopySid( SeLengthSid(ClientSid), (PSID)(((PCHAR)&GrantAce->SidStart) + SeLengthSid(ServerSid)), ClientSid );
// Increment the number of ACEs by 1.
Acl->AceCount += 1;
// Adjust the Acl revision, if necessary
Acl->AclRevision = NewRevision;
// And return to our caller
return STATUS_SUCCESS;
}
NTSTATUS
RtlpAddKnownAce (
IN OUT PACL Acl,
IN ULONG AceRevision,
IN ULONG AceFlags,
IN ACCESS_MASK AccessMask,
IN PSID Sid,
IN UCHAR NewType
)
/*++
Routine Description:
This routine adds KNOWN_ACE to an ACL. This is
expected to be a common form of ACL modification.
A very bland ACE header is placed in the ACE. It provides no
inheritance and no ACE flags. The type is specified by the caller.
Arguments:
Acl - Supplies the Acl being modified
AceRevision - Supplies the Acl/Ace revision of the ACE being added
AceFlags - Supplies the inherit flags for the ACE.
AccessMask - The mask of accesses to be denied to the specified SID.
Sid - Pointer to the SID being denied access.
NewType - Type of ACE to be added.
Return Value:
STATUS_SUCCESS - The ACE was successfully added.
STATUS_INVALID_ACL - The specified ACL is not properly formed.
STATUS_REVISION_MISMATCH - The specified revision is not known
or is incompatible with that of the ACL.
STATUS_ALLOTTED_SPACE_EXCEEDED - The new ACE does not fit into the
ACL. A larger ACL buffer is required.
STATUS_INVALID_SID - The provided SID is not a structurally valid
SID.
STATUS_INVALID_PARAMETER - The AceFlags parameter was invalid.
--*/
{
PVOID FirstFree;
USHORT AceSize;
PKNOWN_ACE GrantAce;
UCHAR NewRevision;
ULONG TestedAceFlags;
RTL_PAGED_CODE();
// Validate the structure of the SID
if (!RtlValidSid(Sid)) {
return STATUS_INVALID_SID;
}
// Check the ACL & ACE revision levels
if ( Acl->AclRevision > ACL_REVISION4 || AceRevision > ACL_REVISION4 ) {
return STATUS_REVISION_MISMATCH;
}
// Calculate the new revision of the ACL. The new revision is the maximum
// of the old revision and and new ACE's revision. This is possible because
// the format of previously defined ACEs did not change across revisions.
NewRevision = Acl->AclRevision > (UCHAR)AceRevision ? Acl->AclRevision : (UCHAR)AceRevision;
// Validate the AceFlags.
TestedAceFlags = AceFlags & ~VALID_INHERIT_FLAGS;
if ( TestedAceFlags != 0 ) {
if ( NewType == SYSTEM_AUDIT_ACE_TYPE ) {
TestedAceFlags &=
~(SUCCESSFUL_ACCESS_ACE_FLAG|FAILED_ACCESS_ACE_FLAG);
}
if ( TestedAceFlags != 0 ) {
return STATUS_INVALID_PARAMETER;
}
}
// Locate the first free ace and check to see that the Acl is
// well formed.
if (!RtlValidAcl( Acl )) {
return STATUS_INVALID_ACL;
}
if (!RtlFirstFreeAce( Acl, &FirstFree )) {
return STATUS_INVALID_ACL;
}
// Check to see if there is enough room in the Acl to store the new
// ACE
AceSize = (USHORT)(sizeof(ACE_HEADER) +
sizeof(ACCESS_MASK) +
SeLengthSid(Sid));
if ( FirstFree == NULL ||
((PUCHAR)FirstFree + AceSize > ((PUCHAR)Acl + Acl->AclSize))
) {
return STATUS_ALLOTTED_SPACE_EXCEEDED;
}
// Add the ACE to the end of the ACL
GrantAce = (PKNOWN_ACE)FirstFree;
GrantAce->Header.AceFlags = (UCHAR)AceFlags;
GrantAce->Header.AceType = NewType;
GrantAce->Header.AceSize = AceSize;
GrantAce->Mask = AccessMask;
RtlCopySid( SeLengthSid(Sid), (PSID)(&GrantAce->SidStart), Sid );
// Increment the number of ACEs by 1.
Acl->AceCount += 1;
// Adjust the Acl revision, if necessary
Acl->AclRevision = NewRevision;
// And return to our caller
return STATUS_SUCCESS;
}
NTSTATUS
RtlpAddKnownObjectAce (
IN OUT PACL Acl,
IN ULONG AceRevision,
IN ULONG AceFlags,
IN ACCESS_MASK AccessMask,
IN GUID *ObjectTypeGuid OPTIONAL,
IN GUID *InheritedObjectTypeGuid OPTIONAL,
IN PSID Sid,
IN UCHAR NewType
)
/*++
Routine Description:
This routine adds KNOWN_ACE to an ACL. This is
expected to be a common form of ACL modification.
A very bland ACE header is placed in the ACE. It provides no
inheritance and no ACE flags. The type is specified by the caller.
Arguments:
Acl - Supplies the Acl being modified
AceRevision - Supplies the Acl/Ace revision of the ACE being added
AceFlags - Supplies the inherit flags for the ACE.
AccessMask - The mask of accesses to be denied to the specified SID.
ObjectTypeGuid - Supplies the GUID of the object this ACE applies to.
If NULL, no object type GUID is placed in the ACE.
InheritedObjectTypeGuid - Supplies the GUID of the object type that will
inherit this ACE. If NULL, no inherited object type GUID is placed in
the ACE.
Sid - Pointer to the SID being denied access.
NewType - Type of ACE to be added.
Return Value:
STATUS_SUCCESS - The ACE was successfully added.
STATUS_INVALID_ACL - The specified ACL is not properly formed.
STATUS_REVISION_MISMATCH - The specified revision is not known
or is incompatible with that of the ACL.
STATUS_ALLOTTED_SPACE_EXCEEDED - The new ACE does not fit into the
ACL. A larger ACL buffer is required.
STATUS_INVALID_SID - The provided SID is not a structurally valid
SID.
STATUS_INVALID_PARAMETER - The AceFlags parameter was invalid.
--*/
{
PVOID FirstFree;
USHORT AceSize;
PKNOWN_OBJECT_ACE GrantAce;
UCHAR NewRevision;
ULONG TestedAceFlags;
ULONG AceObjectFlags = 0;
ULONG SidSize;
PCHAR Where;
RTL_PAGED_CODE();
// Validate the structure of the SID
if (!RtlValidSid(Sid)) {
return STATUS_INVALID_SID;
}
// Check the ACL & ACE revision levels
// Object ACEs became valid in version 4.
if ( Acl->AclRevision > ACL_REVISION4 || AceRevision != ACL_REVISION4 ) {
return STATUS_REVISION_MISMATCH;
}
// Calculate the new revision of the ACL. The new revision is the maximum
// of the old revision and and new ACE's revision. This is possible because
// the format of previously defined ACEs did not change across revisions.
NewRevision = Acl->AclRevision > (UCHAR)AceRevision ? Acl->AclRevision : (UCHAR)AceRevision;
// Validate the AceFlags.
TestedAceFlags = AceFlags & ~VALID_INHERIT_FLAGS;
if ( TestedAceFlags != 0 ) {
if ( NewType == SYSTEM_AUDIT_ACE_TYPE ||
NewType == SYSTEM_AUDIT_OBJECT_ACE_TYPE ) {
TestedAceFlags &=
~(SUCCESSFUL_ACCESS_ACE_FLAG|FAILED_ACCESS_ACE_FLAG);
}
if ( TestedAceFlags != 0 ) {
return STATUS_INVALID_PARAMETER;
}
}
// Locate the first free ace and check to see that the Acl is
// well formed.
if (!RtlValidAcl( Acl )) {
return STATUS_INVALID_ACL;
}
if (!RtlFirstFreeAce( Acl, &FirstFree )) {
return STATUS_INVALID_ACL;
}
// Check to see if there is enough room in the Acl to store the new
// ACE
SidSize = SeLengthSid(Sid);
AceSize = (USHORT)(sizeof(ACE_HEADER) +
sizeof(ACCESS_MASK) +
sizeof(ULONG) +
SidSize);
if ( ARGUMENT_PRESENT(ObjectTypeGuid) ) {
AceObjectFlags |= ACE_OBJECT_TYPE_PRESENT;
AceSize += sizeof(GUID);
}
if ( ARGUMENT_PRESENT(InheritedObjectTypeGuid) ) {
AceObjectFlags |= ACE_INHERITED_OBJECT_TYPE_PRESENT;
AceSize += sizeof(GUID);
}
if ( FirstFree == NULL ||
((PUCHAR)FirstFree + AceSize > ((PUCHAR)Acl + Acl->AclSize))
) {
return STATUS_ALLOTTED_SPACE_EXCEEDED;
}
// Add the ACE to the end of the ACL
GrantAce = (PKNOWN_OBJECT_ACE)FirstFree;
GrantAce->Header.AceFlags = (UCHAR) AceFlags;
GrantAce->Header.AceType = NewType;
GrantAce->Header.AceSize = AceSize;
GrantAce->Mask = AccessMask;
GrantAce->Flags = AceObjectFlags;
Where = (PCHAR) (&GrantAce->SidStart);
if ( ARGUMENT_PRESENT(ObjectTypeGuid) ) {
RtlCopyMemory( Where, ObjectTypeGuid, sizeof(GUID) );
Where += sizeof(GUID);
}
if ( ARGUMENT_PRESENT(InheritedObjectTypeGuid) ) {
RtlCopyMemory( Where, InheritedObjectTypeGuid, sizeof(GUID) );
Where += sizeof(GUID);
}
RtlCopySid( SidSize, (PSID)Where, Sid );
Where += SidSize;
// Increment the number of ACEs by 1.
Acl->AceCount += 1;
// Adjust the Acl revision, if necessary
Acl->AclRevision = NewRevision;
// And return to our caller
return STATUS_SUCCESS;
}
NTSTATUS
RtlAddAccessAllowedAce (
IN OUT PACL Acl,
IN ULONG AceRevision,
IN ACCESS_MASK AccessMask,
IN PSID Sid
)
/*++
Routine Description:
This routine adds an ACCESS_ALLOWED ACE to an ACL. This is
expected to be a common form of ACL modification.
A very bland ACE header is placed in the ACE. It provides no
inheritance and no ACE flags.
Arguments:
Acl - Supplies the Acl being modified
AceRevision - Supplies the Acl/Ace revision of the ACE being added
AccessMask - The mask of accesses to be granted to the specified SID.
Sid - Pointer to the SID being granted access.
Return Value:
STATUS_SUCCESS - The ACE was successfully added.
STATUS_INVALID_ACL - The specified ACL is not properly formed.
STATUS_REVISION_MISMATCH - The specified revision is not known
or is incompatible with that of the ACL.
STATUS_ALLOTTED_SPACE_EXCEEDED - The new ACE does not fit into the
ACL. A larger ACL buffer is required.
STATUS_INVALID_SID - The provided SID is not a structurally valid
SID.
--*/
{
RTL_PAGED_CODE();
return RtlpAddKnownAce (
Acl,
AceRevision,
0, // No inherit flags
AccessMask,
Sid,
ACCESS_ALLOWED_ACE_TYPE
);
}
NTSTATUS
RtlAddAccessAllowedAceEx (
IN OUT PACL Acl,
IN ULONG AceRevision,
IN ULONG AceFlags,
IN ACCESS_MASK AccessMask,
IN PSID Sid
)
/*++
Routine Description:
This routine adds an ACCESS_ALLOWED ACE to an ACL. This is
expected to be a common form of ACL modification.
Arguments:
Acl - Supplies the Acl being modified
AceRevision - Supplies the Acl/Ace revision of the ACE being added
AceFlags - Supplies the inherit flags for the ACE.
AccessMask - The mask of accesses to be granted to the specified SID.
Sid - Pointer to the SID being granted access.
Return Value:
STATUS_SUCCESS - The ACE was successfully added.
STATUS_INVALID_ACL - The specified ACL is not properly formed.
STATUS_REVISION_MISMATCH - The specified revision is not known
or is incompatible with that of the ACL.
STATUS_ALLOTTED_SPACE_EXCEEDED - The new ACE does not fit into the
ACL. A larger ACL buffer is required.
STATUS_INVALID_SID - The provided SID is not a structurally valid
SID.
STATUS_INVALID_PARAMETER - The AceFlags parameter was invalid.
--*/
{
RTL_PAGED_CODE();
return RtlpAddKnownAce (
Acl,
AceRevision,
AceFlags,
AccessMask,
Sid,
ACCESS_ALLOWED_ACE_TYPE
);
}
NTSTATUS
RtlAddAccessDeniedAce (
IN OUT PACL Acl,
IN ULONG AceRevision,
IN ACCESS_MASK AccessMask,
IN PSID Sid
)
/*++
Routine Description:
This routine adds an ACCESS_DENIED ACE to an ACL. This is
expected to be a common form of ACL modification.
A very bland ACE header is placed in the ACE. It provides no
inheritance and no ACE flags.
Arguments:
Acl - Supplies the Acl being modified
AceRevision - Supplies the Acl/Ace revision of the ACE being added
AccessMask - The mask of accesses to be denied to the specified SID.
Sid - Pointer to the SID being denied access.
Return Value:
STATUS_SUCCESS - The ACE was successfully added.
STATUS_INVALID_ACL - The specified ACL is not properly formed.
STATUS_REVISION_MISMATCH - The specified revision is not known
or is incompatible with that of the ACL.
STATUS_ALLOTTED_SPACE_EXCEEDED - The new ACE does not fit into the
ACL. A larger ACL buffer is required.
STATUS_INVALID_SID - The provided SID is not a structurally valid
SID.
--*/
{
RTL_PAGED_CODE();
return RtlpAddKnownAce (
Acl,
AceRevision,
0, // No inherit flags
AccessMask,
Sid,
ACCESS_DENIED_ACE_TYPE
);
}
NTSTATUS
RtlAddAccessDeniedAceEx (
IN OUT PACL Acl,
IN ULONG AceRevision,
IN ULONG AceFlags,
IN ACCESS_MASK AccessMask,
IN PSID Sid
)
/*++
Routine Description:
This routine adds an ACCESS_DENIED ACE to an ACL. This is
expected to be a common form of ACL modification.
Arguments:
Acl - Supplies the Acl being modified
AceRevision - Supplies the Acl/Ace revision of the ACE being added
AceFlags - Supplies the inherit flags for the ACE.
AccessMask - The mask of accesses to be denied to the specified SID.
Sid - Pointer to the SID being denied access.
Return Value:
STATUS_SUCCESS - The ACE was successfully added.
STATUS_INVALID_ACL - The specified ACL is not properly formed.
STATUS_REVISION_MISMATCH - The specified revision is not known
or is incompatible with that of the ACL.
STATUS_ALLOTTED_SPACE_EXCEEDED - The new ACE does not fit into the
ACL. A larger ACL buffer is required.
STATUS_INVALID_SID - The provided SID is not a structurally valid
SID.
STATUS_INVALID_PARAMETER - The AceFlags parameter was invalid.
--*/
{
RTL_PAGED_CODE();
return RtlpAddKnownAce (
Acl,
AceRevision,
AceFlags,
AccessMask,
Sid,
ACCESS_DENIED_ACE_TYPE
);
}
NTSTATUS
RtlAddAuditAccessAce (
IN OUT PACL Acl,
IN ULONG AceRevision,
IN ACCESS_MASK AccessMask,
IN PSID Sid,
IN BOOLEAN AuditSuccess,
IN BOOLEAN AuditFailure
)
/*++
Routine Description:
This routine adds a SYSTEM_AUDIT ACE to an ACL. This is
expected to be a common form of ACL modification.
A very bland ACE header is placed in the ACE. It provides no
inheritance.
Parameters are used to indicate whether auditing is to be performed
on success, failure, or both.
Arguments:
Acl - Supplies the Acl being modified
AceRevision - Supplies the Acl/Ace revision of the ACE being added
AccessMask - The mask of accesses to be denied to the specified SID.
Sid - Pointer to the SID to be audited.
AuditSuccess - If TRUE, indicates successful access attempts are to be
audited.
AuditFailure - If TRUE, indicated failed access attempts are to be
audited.
Return Value:
STATUS_SUCCESS - The ACE was successfully added.
STATUS_INVALID_ACL - The specified ACL is not properly formed.
STATUS_REVISION_MISMATCH - The specified revision is not known
or is incompatible with that of the ACL.
STATUS_ALLOTTED_SPACE_EXCEEDED - The new ACE does not fit into the
ACL. A larger ACL buffer is required.
STATUS_INVALID_SID - The provided SID is not a structurally valid
SID.
--*/
{
ULONG AceFlags = 0;
RTL_PAGED_CODE();
if (AuditSuccess) {
AceFlags |= SUCCESSFUL_ACCESS_ACE_FLAG;
}
if (AuditFailure) {
AceFlags |= FAILED_ACCESS_ACE_FLAG;
}
return RtlpAddKnownAce (
Acl,
AceRevision,
AceFlags,
AccessMask,
Sid,
SYSTEM_AUDIT_ACE_TYPE );
}
NTSTATUS
RtlAddAuditAccessAceEx (
IN OUT PACL Acl,
IN ULONG AceRevision,
IN ULONG AceFlags,
IN ACCESS_MASK AccessMask,
IN PSID Sid,
IN BOOLEAN AuditSuccess,
IN BOOLEAN AuditFailure
)
/*++
Routine Description:
This routine adds a SYSTEM_AUDIT ACE to an ACL. This is
expected to be a common form of ACL modification.
A very bland ACE header is placed in the ACE. It provides no
inheritance.
Parameters are used to indicate whether auditing is to be performed
on success, failure, or both.
Arguments:
Acl - Supplies the Acl being modified
AceRevision - Supplies the Acl/Ace revision of the ACE being added
AceFlags - Supplies the inherit flags for the ACE.
AccessMask - The mask of accesses to be denied to the specified SID.
Sid - Pointer to the SID to be audited.
AuditSuccess - If TRUE, indicates successful access attempts are to be
audited.
AuditFailure - If TRUE, indicated failed access attempts are to be
audited.
Return Value:
STATUS_SUCCESS - The ACE was successfully added.
STATUS_INVALID_ACL - The specified ACL is not properly formed.
STATUS_REVISION_MISMATCH - The specified revision is not known
or is incompatible with that of the ACL.
STATUS_ALLOTTED_SPACE_EXCEEDED - The new ACE does not fit into the
ACL. A larger ACL buffer is required.
STATUS_INVALID_SID - The provided SID is not a structurally valid
SID.
STATUS_INVALID_PARAMETER - The AceFlags parameter was invalid.
--*/
{
RTL_PAGED_CODE();
if (AuditSuccess) {
AceFlags |= SUCCESSFUL_ACCESS_ACE_FLAG;
}
if (AuditFailure) {
AceFlags |= FAILED_ACCESS_ACE_FLAG;
}
return RtlpAddKnownAce (
Acl,
AceRevision,
AceFlags,
AccessMask,
Sid,
SYSTEM_AUDIT_ACE_TYPE );
}
NTSTATUS
RtlAddAccessAllowedObjectAce (
IN OUT PACL Acl,
IN ULONG AceRevision,
IN ULONG AceFlags,
IN ACCESS_MASK AccessMask,
IN GUID *ObjectTypeGuid OPTIONAL,
IN GUID *InheritedObjectTypeGuid OPTIONAL,
IN PSID Sid
)
/*++
Routine Description:
This routine adds an object specific ACCESS_ALLOWED ACE to an ACL. This is
expected to be a common form of ACL modification.
Arguments:
Acl - Supplies the Acl being modified
AceRevision - Supplies the Acl/Ace revision of the ACE being added
AceFlags - Supplies the inherit flags for the ACE.
AccessMask - The mask of accesses to be granted to the specified SID.
ObjectTypeGuid - Supplies the GUID of the object this ACE applies to.
If NULL, no object type GUID is placed in the ACE.
InheritedObjectTypeGuid - Supplies the GUID of the object type that will
inherit this ACE. If NULL, no inherited object type GUID is placed in
the ACE.
Sid - Pointer to the SID being granted access.
Return Value:
STATUS_SUCCESS - The ACE was successfully added.
STATUS_INVALID_ACL - The specified ACL is not properly formed.
STATUS_REVISION_MISMATCH - The specified revision is not known
or is incompatible with that of the ACL.
STATUS_ALLOTTED_SPACE_EXCEEDED - The new ACE does not fit into the
ACL. A larger ACL buffer is required.
STATUS_INVALID_SID - The provided SID is not a structurally valid
SID.
STATUS_INVALID_PARAMETER - The AceFlags parameter was invalid.
--*/
{
RTL_PAGED_CODE();
// If no object types are specified,
// build a non-object ACE.
if (ObjectTypeGuid == NULL && InheritedObjectTypeGuid == NULL ) {
return RtlpAddKnownAce (
Acl,
AceRevision,
AceFlags,
AccessMask,
Sid,
ACCESS_ALLOWED_ACE_TYPE
);
}
return RtlpAddKnownObjectAce (
Acl,
AceRevision,
AceFlags,
AccessMask,
ObjectTypeGuid,
InheritedObjectTypeGuid,
Sid,
ACCESS_ALLOWED_OBJECT_ACE_TYPE
);
}
NTSTATUS
RtlAddAccessDeniedObjectAce (
IN OUT PACL Acl,
IN ULONG AceRevision,
IN ULONG AceFlags,
IN ACCESS_MASK AccessMask,
IN GUID *ObjectTypeGuid OPTIONAL,
IN GUID *InheritedObjectTypeGuid OPTIONAL,
IN PSID Sid
)
/*++
Routine Description:
This routine adds an object specific ACCESS_DENIED ACE to an ACL. This is
expected to be a common form of ACL modification.
Arguments:
Acl - Supplies the Acl being modified
AceRevision - Supplies the Acl/Ace revision of the ACE being added
AceFlags - Supplies the inherit flags for the ACE.
AccessMask - The mask of accesses to be granted to the specified SID.
ObjectTypeGuid - Supplies the GUID of the object this ACE applies to.
If NULL, no object type GUID is placed in the ACE.
InheritedObjectTypeGuid - Supplies the GUID of the object type that will
inherit this ACE. If NULL, no inherited object type GUID is placed in
the ACE.
Sid - Pointer to the SID being denied access.
Return Value:
STATUS_SUCCESS - The ACE was successfully added.
STATUS_INVALID_ACL - The specified ACL is not properly formed.
STATUS_REVISION_MISMATCH - The specified revision is not known
or is incompatible with that of the ACL.
STATUS_ALLOTTED_SPACE_EXCEEDED - The new ACE does not fit into the
ACL. A larger ACL buffer is required.
STATUS_INVALID_SID - The provided SID is not a structurally valid
SID.
STATUS_INVALID_PARAMETER - The AceFlags parameter was invalid.
--*/
{
RTL_PAGED_CODE();
// If no object types are specified,
// build a non-object ACE.
if (ObjectTypeGuid == NULL && InheritedObjectTypeGuid == NULL ) {
return RtlpAddKnownAce (
Acl,
AceRevision,
AceFlags,
AccessMask,
Sid,
ACCESS_DENIED_ACE_TYPE
);
}
return RtlpAddKnownObjectAce (
Acl,
AceRevision,
AceFlags,
AccessMask,
ObjectTypeGuid,
InheritedObjectTypeGuid,
Sid,
ACCESS_DENIED_OBJECT_ACE_TYPE
);
}
NTSTATUS
RtlAddAuditAccessObjectAce (
IN OUT PACL Acl,
IN ULONG AceRevision,
IN ULONG AceFlags,
IN ACCESS_MASK AccessMask,
IN GUID *ObjectTypeGuid OPTIONAL,
IN GUID *InheritedObjectTypeGuid OPTIONAL,
IN PSID Sid,
IN BOOLEAN AuditSuccess,
IN BOOLEAN AuditFailure
)
/*++
Routine Description:
This routine adds an object specific ACCESS_DENIED ACE to an ACL. This is
expected to be a common form of ACL modification.
Arguments:
Acl - Supplies the Acl being modified
AceRevision - Supplies the Acl/Ace revision of the ACE being added
AceFlags - Supplies the inherit flags for the ACE.
AccessMask - The mask of accesses to be granted to the specified SID.
ObjectTypeGuid - Supplies the GUID of the object this ACE applies to.
If NULL, no object type GUID is placed in the ACE.
InheritedObjectTypeGuid - Supplies the GUID of the object type that will
inherit this ACE. If NULL, no inherited object type GUID is placed in
the ACE.
Sid - Pointer to the SID to be audited.
AuditSuccess - If TRUE, indicates successful access attempts are to be
audited.
AuditFailure - If TRUE, indicated failed access attempts are to be
audited.
Return Value:
STATUS_SUCCESS - The ACE was successfully added.
STATUS_INVALID_ACL - The specified ACL is not properly formed.
STATUS_REVISION_MISMATCH - The specified revision is not known
or is incompatible with that of the ACL.
STATUS_ALLOTTED_SPACE_EXCEEDED - The new ACE does not fit into the
ACL. A larger ACL buffer is required.
STATUS_INVALID_SID - The provided SID is not a structurally valid
SID.
STATUS_INVALID_PARAMETER - The AceFlags parameter was invalid.
--*/
{
RTL_PAGED_CODE();
if (AuditSuccess) {
AceFlags |= SUCCESSFUL_ACCESS_ACE_FLAG;
}
if (AuditFailure) {
AceFlags |= FAILED_ACCESS_ACE_FLAG;
}
// If no object types are specified,
// build a non-object ACE.
if (ObjectTypeGuid == NULL && InheritedObjectTypeGuid == NULL ) {
return RtlpAddKnownAce (
Acl,
AceRevision,
AceFlags,
AccessMask,
Sid,
SYSTEM_AUDIT_ACE_TYPE
);
}
return RtlpAddKnownObjectAce (
Acl,
AceRevision,
AceFlags,
AccessMask,
ObjectTypeGuid,
InheritedObjectTypeGuid,
Sid,
SYSTEM_AUDIT_OBJECT_ACE_TYPE
);
}
#if 0
NTSTATUS
RtlMakePosixAcl(
IN ULONG AclRevision,
IN PSID UserSid,
IN PSID GroupSid,
IN ACCESS_MASK UserAccess,
IN ACCESS_MASK GroupAccess,
IN ACCESS_MASK OtherAccess,
IN ULONG AclLength,
OUT PACL Acl,
OUT PULONG ReturnLength
)
/*++
Routine Description:
NOTE: THIS ROUTINE IS STILL BEING SPEC'D.
Make an ACL representing Posix protection from AccessMask and
security account ID (SID) information.
Arguments:
AclRevision - Indicates the ACL revision level of the access masks
provided. The ACL generated will be revision compatible with this
value and will not be a higher revision than this value.
UserSid - Provides the SID of the user (owner).
GroupSid - Provides the SID of the primary group.
UserAccess - Specifies the accesses to be given to the user (owner).
GroupAccess - Specifies the accesses to be given to the primary group.
OtherAccess - Specifies the accesses to be given to others (WORLD).
AclLength - Provides the length (in bytes) of the Acl buffer.
Acl - Points to a buffer to receive the generated ACL.
ReturnLength - Returns the actual length needed to store the resultant
ACL. If this length is greater than that specified in AclLength,
then STATUS_BUFFER_TOO_SMALL is returned and no ACL is generated.
Return Values:
STATUS_SUCCESS - The service completed successfully.
STATUS_UNKNOWN_REVISION - The revision level specified is not supported
by this service.
STATUS_BUFFER_TOO_SMALL - Indicates the length of the output buffer
wasn't large enough to hold the generated ACL. The length needed
is returned via the ReturnLength parameter.
--*/
{
SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
ULONG UserSidLength;
ULONG GroupSidLength;
ULONG WorldSidLength;
ULONG RequiredAclSize;
ULONG AceSize;
ULONG CurrentAce;
PACCESS_ALLOWED_ACE Ace;
NTSTATUS Status;
RTL_PAGED_CODE();
if (!RtlValidSid( UserSid ) || !RtlValidSid( GroupSid )) {
return( STATUS_INVALID_SID );
}
UserSidLength = SeLengthSid( UserSid );
GroupSidLength = SeLengthSid( GroupSid );
WorldSidLength = RtlLengthRequiredSid( 1 );
// Figure out how much room we need for an ACL and three
// ACCESS_ALLOWED Ace's
RequiredAclSize = sizeof( ACL );
AceSize = sizeof( ACCESS_ALLOWED_ACE ) - sizeof( ULONG );
RequiredAclSize += (AceSize * 3) +
UserSidLength +
GroupSidLength +
WorldSidLength ;
if (RequiredAclSize > AclLength) {
*ReturnLength = RequiredAclSize;
return( STATUS_BUFFER_TOO_SMALL );
}
// The passed buffer is big enough, build the ACL in it.
Status = RtlCreateAcl(
Acl,
RequiredAclSize,
AclRevision
);
if (!NT_SUCCESS( Status )) {
return( Status );
}
CurrentAce = (ULONG)Acl + sizeof( ACL );
Ace = (PACCESS_ALLOWED_ACE)CurrentAce;
// Build the user (owner) ACE
Ace->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
Ace->Header.AceSize = (USHORT)(UserSidLength + AceSize);
Ace->Header.AceFlags = 0;
Ace->Mask = UserAccess;
RtlMoveMemory(
(PVOID)(Ace->SidStart),
UserSid,
UserSidLength
);
CurrentAce += (ULONG)(Ace->Header.AceSize);
Ace = (PACCESS_ALLOWED_ACE)CurrentAce;
// Build the group ACE
Ace->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
Ace->Header.AceSize = (USHORT)(GroupSidLength + AceSize);
Ace->Header.AceFlags = 0;
Ace->Mask = GroupAccess;
RtlMoveMemory(
(PVOID)(Ace->SidStart),
GroupSid,
GroupSidLength
);
CurrentAce += (ULONG)(Ace->Header.AceSize);
Ace = (PACCESS_ALLOWED_ACE)CurrentAce;
// Build the World ACE
Ace->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
Ace->Header.AceSize = (USHORT)(GroupSidLength + AceSize);
Ace->Header.AceFlags = 0;
Ace->Mask = OtherAccess;
RtlInitializeSid(
(PSID)(Ace->SidStart),
&WorldSidAuthority,
1
);
*(RtlSubAuthoritySid((PSID)(Ace->SidStart), 0 )) = SECURITY_WORLD_RID;
return( STATUS_SUCCESS );
}
NTSTATUS
RtlInterpretPosixAcl(
IN ULONG AclRevision,
IN PSID UserSid,
IN PSID GroupSid,
IN PACL Acl,
OUT PACCESS_MASK UserAccess,
OUT PACCESS_MASK GroupAccess,
OUT PACCESS_MASK OtherAccess
)
/*++
Routine Description:
NOTE: THIS ROUTINE IS STILL BEING SPEC'D.
Interpret an ACL representing Posix protection, returning AccessMasks.
Use security account IDs (SIDs) for object owner and primary group
identification.
This algorithm will pick up the first match of a given SID and ignore
all further matches of that SID. The first unrecognized SID becomes
the "other" SID.
Arguments:
AclRevision - Indicates the ACL revision level of the access masks to
be returned.
UserSid - Provides the SID of the user (owner).
GroupSid - Provides the SID of the primary group.
Acl - Points to a buffer containing the ACL to interpret.
UserAccess - Receives the accesses allowed for the user (owner).
GroupAccess - Receives the accesses allowed for the primary group.
OtherAccess - Receives the accesses allowed for others (WORLD).
Return Values:
STATUS_SUCCESS - The service completed successfully.
STATUS_UNKNOWN_REVISION - The revision level specified is not supported
by this service.
STATUS_EXTRENEOUS_INFORMATION - This warning status value indicates the
ACL contained protection or other information unrelated to Posix
style protection. This is a warning only. The interpretation was
otherwise successful and all access masks were returned.
STATUS_COULD_NOT_INTERPRET - Indicates the ACL does not contain
sufficient Posix style (user/group) protection information. The
ACL could not be interpreted.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
BOOLEAN UserFound = FALSE;
BOOLEAN GroupFound = FALSE;
BOOLEAN OtherFound = FALSE;
ULONG i;
PKNOWN_ACE Ace;
RTL_PAGED_CODE();
if (AclRevision != ACL_REVISION2) {
return( STATUS_UNKNOWN_REVISION );
}
if (Acl->AceCount > 3) {
Status = STATUS_EXTRANEOUS_INFORMATION;
}
for (i=0, Ace = FirstAce( Acl );
(i < Acl->AceCount) && (!UserFound || !GroupFound || !OtherFound);
i++, Ace = NextAce( Ace )) {
if (Ace->Header.AceType != ACCESS_ALLOWED_ACE_TYPE) {
Status = STATUS_EXTRANEOUS_INFORMATION;
continue;
}
if (RtlEqualSid(
(PSID)(Ace->SidStart),
UserSid
) && !UserFound) {
*UserAccess = Ace->Mask;
UserFound = TRUE;
continue;
}
if (RtlEqualSid(
(PSID)(Ace->SidStart),
GroupSid
) && !GroupFound) {
*GroupAccess = Ace->Mask;
GroupFound = TRUE;
continue;
}
// It isn't the user, and it isn't the group, pick it up
// as "other"
if (!OtherFound) {
*OtherAccess = Ace->Mask;
OtherFound = TRUE;
continue;
}
}
// Make sure we got everything we need, error otherwise
if (!UserFound || !GroupFound || !OtherFound) {
Status = STATUS_COULD_NOT_INTERPRET;
}
return( Status );
}
#endif // 0
// Internal support routine
BOOLEAN
RtlFirstFreeAce (
IN PACL Acl,
OUT PVOID *FirstFree
)
/*++
Routine Description:
This routine returns a pointer to the first free byte in an Acl
or NULL if the acl is ill-formed. If the Acl is full then the
return pointer is to the byte immediately following the acl, and
TRUE will be returned.
Arguments:
Acl - Supplies a pointer to the Acl to examine
FirstFree - Receives a pointer to the first free position in the Acl
Return Value:
BOOLEAN - TRUE if the Acl is well formed and FALSE otherwise
--*/
{
PACE_HEADER Ace;
ULONG i;
RTL_PAGED_CODE();
// To find the first free spot in the Acl we need to search for
// the last ace. We do this by zooming down the list until
// we've exhausted the ace count or the ace size (which ever comes
// first). In the following loop Ace points to the next spot
// for an Ace and I is the ace index
*FirstFree = NULL;
for ( i=0, Ace = FirstAce( Acl );
i < Acl->AceCount;
i++, Ace = NextAce( Ace )) {
// Check to make sure we haven't overrun the Acl buffer
// with our Ace pointer. If we have then our input is bogus.
if (Ace >= (PACE_HEADER)((PUCHAR)Acl + Acl->AclSize)) {
return FALSE;
}
}
// Now Ace points to the first free spot in the Acl so set the
// output variable and check to make sure it is still in the Acl
// or just one beyond the end of the acl (i.e., the acl is full).
if (Ace <= (PACE_HEADER)((PUCHAR)Acl + Acl->AclSize)) {
*FirstFree = Ace;
}
// The Acl is well formed so return the first free spot we've found
// (or NULL if there is no free space for another ACE)
return TRUE;
}
// Internal support routine
VOID
RtlpAddData (
IN PVOID From,
IN ULONG FromSize,
IN PVOID To,
IN ULONG ToSize
)
/*++
Routine Description:
This routine copies data to a string of bytes. It does this by moving
over data in the to string so that the from string will fit. It also
assumes that the checks that the data will fit in memory have already
been done. Pictorally the results are as follows.
Before:
From -> ffffffffff
To -> tttttttttttttttt
After:
From -> ffffffffff
To -> fffffffffftttttttttttttttt
Arguments:
From - Supplies a pointer to the source buffer
FromSize - Supplies the size of the from buffer in bytes
To - Supplies a pointer to the destination buffer
ToSize - Supplies the size of the to buffer in bytes
Return Value:
None
--*/
{
LONG i;
// Shift over the To buffer enough to fit in the From buffer
for (i = ToSize - 1; i >= 0; i--) {
((PUCHAR)To)[i+FromSize] = ((PUCHAR)To)[i];
}
// Now copy over the From buffer
for (i = 0; (ULONG)i < FromSize; i += 1) {
((PUCHAR)To)[i] = ((PUCHAR)From)[i];
}
// and return to our caller
return;
}
// Internal support routine
VOID
RtlpDeleteData (
IN PVOID Data,
IN ULONG RemoveSize,
IN ULONG TotalSize
)
/*++
Routine Description:
This routine deletes a string of bytes from the front of a data buffer
and compresses the data. It also zeros out the part of the string
that is no longer in use. Pictorially the results are as follows
Before:
Data = DDDDDddddd
RemoveSize = 5
TotalSize = 10
After:
Data = ddddd00000
Arguments:
Data - Supplies a pointer to the data being altered
RemoveSize - Supplies the number of bytes to delete from the front
of the data buffer
TotalSize - Supplies the total number of bytes in the data buffer
before the delete operation
Return Value:
None
--*/
{
ULONG i;
// Shift over the buffer to remove the amount
for (i = RemoveSize; i < TotalSize; i++) {
((PUCHAR)Data)[i-RemoveSize] = ((PUCHAR)Data)[i];
}
// Now as a safety precaution we'll zero out the rest of the string
for (i = TotalSize - RemoveSize; i < TotalSize; i++) {
((PUCHAR)Data)[i] = 0;
}
// And return to our caller
return;
}