From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/ntos/rtl/acledit.c | 1970 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1970 insertions(+) create mode 100644 private/ntos/rtl/acledit.c (limited to 'private/ntos/rtl/acledit.c') diff --git a/private/ntos/rtl/acledit.c b/private/ntos/rtl/acledit.c new file mode 100644 index 000000000..3c502274a --- /dev/null +++ b/private/ntos/rtl/acledit.c @@ -0,0 +1,1970 @@ +/*++ + +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)) + + +// +// Test to determine if two ACLs are of the same revision. +// + +#define RevisionsMatch(Acl,AceRevision) ( (((Acl)->AclRevision == ACL_REVISION2) && ((AceRevision) == ACL_REVISION2)) || \ + (((Acl)->AclRevision == ACL_REVISION3) && ((AceRevision) == ACL_REVISION3)) \ + ) + 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,RtlAddAccessDeniedAce) +#pragma alloc_text(PAGE,RtlAddAuditAccessAce) +#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 equal to 2. Later versions + // of this procedure might accept more revision levels + // + + if (AclRevision != ACL_REVISION2 && AclRevision != ACL_REVISION3) { + + // + // 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. + +--*/ + +{ + + PVOID FirstFree; + + RTL_PAGED_CODE(); + + try { + + // + // Check the ACL revision level + // + + if (!ValidAclRevision( Acl )) { + + return FALSE; + + } + + // + // Check that the length is aligned. The kernel checks this, + // so we should too. + // + + if ((USHORT) LongAlign(Acl->AclSize) != Acl->AclSize) { + return FALSE; + } + // + // Check to see that the Acl is well formed. + // + + return ( RtlFirstFreeAce( Acl, &FirstFree )); + + } 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 = (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 revision level + // + + if (!ValidAclRevision(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++) { + + // + // Compound ACEs weren't defined before ACL_REVISION3. That means that + // if we got passed that type with a lower revision number, it means + // that someone stole one of our reserved types. Return an error. + // + + if (((PACE_HEADER)Ace)->AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE) { + + if (AceRevision < ACL_REVISION3) { + + 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, ((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 revision level + // + + if (!ValidAclRevision(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, (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 + +--*/ + + + + +{ + 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 + // + + if ( Acl->AclRevision > ACL_REVISION3 || AceRevision != ACL_REVISION3 ) { + 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 (!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) + + RtlLengthSid(ClientSid) + + RtlLengthSid(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( RtlLengthSid(ServerSid), (PSID)(&GrantAce->SidStart), ServerSid ); + RtlCopySid( RtlLengthSid(ClientSid), (PSID)(((ULONG)&GrantAce->SidStart) + RtlLengthSid(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 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 + + 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. + +--*/ + +{ + PVOID FirstFree; + USHORT AceSize; + PKNOWN_ACE GrantAce; + UCHAR NewRevision; + + 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_REVISION3 || AceRevision > ACL_REVISION3 ) { + + 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 (!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) + + RtlLengthSid(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 = 0; + GrantAce->Header.AceType = NewType; + GrantAce->Header.AceSize = AceSize; + GrantAce->Mask = AccessMask; + RtlCopySid( RtlLengthSid(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 +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, + 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, + 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. + +--*/ + +{ + PVOID FirstFree; + UCHAR AuditFlags; + USHORT AceSize; + + PSYSTEM_AUDIT_ACE AuditAce; + + RTL_PAGED_CODE(); + + AuditFlags = 0; + if (AuditSuccess) { + AuditFlags |= SUCCESSFUL_ACCESS_ACE_FLAG; + } + if (AuditFailure) { + AuditFlags |= FAILED_ACCESS_ACE_FLAG; + } + + // + // Validate the structure of the SID + // + + if (!RtlValidSid(Sid)) { + return STATUS_INVALID_SID; + } + + // + // Check the ACL & ACE revision levels + // + + if ( Acl->AclRevision > ACL_REVISION3 || AceRevision > ACL_REVISION3 ) { + + return STATUS_REVISION_MISMATCH; + } + + // + // Locate the first free ace and check to see that the Acl is + // well formed. + // + + 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) + + RtlLengthSid(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 + // + + AuditAce = (PSYSTEM_AUDIT_ACE)FirstFree; + AuditAce->Header.AceFlags = AuditFlags; + AuditAce->Header.AceType = SYSTEM_AUDIT_ACE_TYPE; + AuditAce->Header.AceSize = AceSize; + AuditAce->Mask = AccessMask; + RtlCopySid( RtlLengthSid(Sid), (PSID)(&AuditAce->SidStart), Sid ); + + // + // Increment the number of ACEs by 1. + // + + Acl->AceCount += 1; + + // + // And return to our caller + // + + return STATUS_SUCCESS; +} + +#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 = RtlLengthSid( UserSid ); + GroupSidLength = RtlLengthSid( 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; + + } + + if ( (Ace->AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE) && (Acl->AclRevision < ACL_REVISION3) ) { + + 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; + +} -- cgit v1.2.3