mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2855 lines
65 KiB
2855 lines
65 KiB
/*++
|
|
|
|
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)
|
|
NTSTATUS
|
|
RtlpAddKnownAce (
|
|
IN OUT PACL Acl,
|
|
IN ULONG AceRevision,
|
|
IN ULONG AceFlags,
|
|
IN ACCESS_MASK AccessMask,
|
|
IN PSID Sid,
|
|
IN UCHAR NewType
|
|
);
|
|
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
|
|
);
|
|
#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,RtlAddCompoundAce)
|
|
#pragma alloc_text(PAGE,RtlpAddKnownAce)
|
|
#pragma alloc_text(PAGE,RtlpAddKnownObjectAce)
|
|
#pragma alloc_text(PAGE,RtlAddAccessAllowedAce)
|
|
#pragma alloc_text(PAGE,RtlAddAccessAllowedAceEx)
|
|
#pragma alloc_text(PAGE,RtlAddAccessDeniedAce)
|
|
#pragma alloc_text(PAGE,RtlAddAccessDeniedAceEx)
|
|
#pragma alloc_text(PAGE,RtlAddAuditAccessAce)
|
|
#pragma alloc_text(PAGE,RtlAddAuditAccessAceEx)
|
|
#pragma alloc_text(PAGE,RtlAddAccessAllowedObjectAce)
|
|
#pragma alloc_text(PAGE,RtlAddAccessDeniedObjectAce)
|
|
#pragma alloc_text(PAGE,RtlAddAuditAccessObjectAce)
|
|
#pragma alloc_text(PAGE,RtlFirstFreeAce)
|
|
#pragma alloc_text(PAGE,RtlpAddData)
|
|
#pragma alloc_text(PAGE,RtlpDeleteData)
|
|
#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;
|
|
|
|
RtlCopyMemory(
|
|
(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;
|
|
|
|
RtlCopyMemory(
|
|
(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;
|
|
|
|
}
|