windows-nt/Source/XPSP1/NT/base/ntos/rtl/sertl.c

11591 lines
319 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
sertl.c
Abstract:
This Module implements many security rtl routines defined in ntseapi.h
Author:
Jim Kelly (JimK) 23-Mar-1990
Robert Reichel (RobertRe) 1-Mar-1991
Environment:
Pure Runtime Library Routine
Revision History:
--*/
#include "ntrtlp.h"
#include <winerror.h>
#ifndef BLDR_KERNEL_RUNTIME
#include <stdio.h>
#include "seopaque.h"
#include "sertlp.h"
#ifdef NTOS_KERNEL_RUNTIME
#include <..\se\sep.h>
#else // NTOS_KERNEL_RUNTIME
#include <..\ntdll\ldrp.h>
#endif // NTOS_KERNEL_RUNTIME
#undef RtlEqualLuid
NTSYSAPI
BOOLEAN
NTAPI
RtlEqualLuid (
PLUID Luid1,
PLUID Luid2
);
NTSTATUS
RtlpConvertAclToAutoInherit (
IN PACL ParentAcl OPTIONAL,
IN PACL ChildAcl,
IN GUID *ObjectType OPTIONAL,
IN BOOLEAN IsDirectoryObject,
IN PSID OwnerSid,
IN PSID GroupSid,
IN PGENERIC_MAPPING GenericMapping,
OUT PACL *NewAcl,
OUT PULONG NewGenericControl
);
BOOLEAN
RtlpCopyEffectiveAce (
IN PACE_HEADER OldAce,
IN BOOLEAN AutoInherit,
IN BOOLEAN WillGenerateInheritAce,
IN PSID ClientOwnerSid,
IN PSID ClientGroupSid,
IN PSID ServerOwnerSid OPTIONAL,
IN PSID ServerGroupSid OPTIONAL,
IN PGENERIC_MAPPING GenericMapping,
IN GUID **pNewObjectType OPTIONAL,
IN ULONG GuidCount,
IN OUT PVOID *AcePosition,
OUT PULONG NewAceLength,
OUT PACL NewAcl,
OUT PBOOLEAN ObjectAceInherited OPTIONAL,
OUT PBOOLEAN EffectiveAceMapped,
OUT PBOOLEAN AclOverflowed
);
typedef enum {
CopyInheritedAces,
CopyNonInheritedAces,
CopyAllAces } ACE_TYPE_TO_COPY;
NTSTATUS
RtlpCopyAces(
IN PACL Acl,
IN PGENERIC_MAPPING GenericMapping,
IN ACE_TYPE_TO_COPY AceTypeToCopy,
IN UCHAR AceFlagsToReset,
IN BOOLEAN MapSids,
IN PSID ClientOwnerSid,
IN PSID ClientGroupSid,
IN PSID ServerOwnerSid OPTIONAL,
IN PSID ServerGroupSid OPTIONAL,
IN BOOLEAN IsDirectoryObject,
IN BOOLEAN RetainInheritedAceBit,
OUT PULONG NewAclSizeParam,
OUT PACL NewAcl
);
NTSTATUS
RtlpGenerateInheritedAce (
IN PACE_HEADER OldAce,
IN BOOLEAN IsDirectoryObject,
IN BOOLEAN AutoInherit,
IN PSID ClientOwnerSid,
IN PSID ClientGroupSid,
IN PSID ServerOwnerSid OPTIONAL,
IN PSID ServerGroupSid OPTIONAL,
IN PGENERIC_MAPPING GenericMapping,
IN GUID **pNewObjectType OPTIONAL,
IN ULONG GuidCount,
OUT PULONG NewAceLength,
OUT PACL NewAcl,
OUT PULONG NewAceExtraLength,
OUT PBOOLEAN ObjectAceInherited
);
NTSTATUS
RtlpGenerateInheritAcl(
IN PACL Acl,
IN BOOLEAN IsDirectoryObject,
IN BOOLEAN AutoInherit,
IN PSID ClientOwnerSid,
IN PSID ClientGroupSid,
IN PSID ServerOwnerSid OPTIONAL,
IN PSID ServerGroupSid OPTIONAL,
IN PGENERIC_MAPPING GenericMapping,
IN GUID **pNewObjectType OPTIONAL,
IN ULONG GuidCount,
OUT PULONG NewAclSizeParam,
OUT PACL NewAcl,
OUT PBOOLEAN ObjectAceInherited
);
NTSTATUS
RtlpInheritAcl2 (
IN PACL DirectoryAcl,
IN PACL ChildAcl,
IN ULONG ChildGenericControl,
IN BOOLEAN IsDirectoryObject,
IN BOOLEAN AutoInherit,
IN BOOLEAN DefaultDescriptorForObject,
IN PSID OwnerSid,
IN PSID GroupSid,
IN PSID ServerOwnerSid OPTIONAL,
IN PSID ServerGroupSid OPTIONAL,
IN PGENERIC_MAPPING GenericMapping,
IN BOOLEAN IsSacl,
IN GUID **pNewObjectType OPTIONAL,
IN ULONG GuidCount,
IN PULONG AclBufferSize,
IN OUT PUCHAR AclBuffer,
OUT PBOOLEAN NewAclExplicitlyAssigned,
OUT PULONG NewGenericControl
);
NTSTATUS
RtlpComputeMergedAcl (
IN PACL CurrentAcl,
IN ULONG CurrentGenericControl,
IN PACL ModificationAcl,
IN ULONG ModificationGenericControl,
IN PSID ClientOwnerSid,
IN PSID ClientGroupSid,
IN PGENERIC_MAPPING GenericMapping,
IN BOOLEAN IsSacl,
OUT PACL *NewAcl,
OUT PULONG NewGenericControl
);
NTSTATUS
RtlpComputeMergedAcl2 (
IN PACL CurrentAcl,
IN ULONG CurrentGenericControl,
IN PACL ModificationAcl,
IN ULONG ModificationGenericControl,
IN PSID ClientOwnerSid,
IN PSID ClientGroupSid,
IN PGENERIC_MAPPING GenericMapping,
IN BOOLEAN IsSacl,
IN PULONG AclBufferSize,
IN OUT PUCHAR AclBuffer,
OUT PULONG NewGenericControl
);
BOOLEAN
RtlpCompareAces(
IN PKNOWN_ACE InheritedAce,
IN PKNOWN_ACE ChildAce,
IN PSID OwnerSid,
IN PSID GroupSid
);
BOOLEAN
RtlpCompareKnownObjectAces(
IN PKNOWN_OBJECT_ACE InheritedAce,
IN PKNOWN_OBJECT_ACE ChildAce,
IN PSID OwnerSid OPTIONAL,
IN PSID GroupSid OPTIONAL
);
BOOLEAN
RtlpCompareKnownAces(
IN PKNOWN_ACE InheritedAce,
IN PKNOWN_ACE ChildAce,
IN PSID OwnerSid OPTIONAL,
IN PSID GroupSid OPTIONAL
);
BOOLEAN
RtlpIsDuplicateAce(
IN PACL Acl,
IN PKNOWN_ACE NewAce
);
BOOLEAN
RtlpGuidPresentInGuidList(
IN GUID *InheritedObjectType,
IN GUID **pNewObjectType,
IN ULONG GuidCount
);
NTSTATUS
RtlpCreateServerAcl(
IN PACL Acl,
IN BOOLEAN AclUntrusted,
IN PSID ServerSid,
OUT PACL *ServerAcl,
OUT BOOLEAN *ServerAclAllocated
);
NTSTATUS
RtlpGetDefaultsSubjectContext(
HANDLE ClientToken,
OUT PTOKEN_OWNER *OwnerInfo,
OUT PTOKEN_PRIMARY_GROUP *GroupInfo,
OUT PTOKEN_DEFAULT_DACL *DefaultDaclInfo,
OUT PTOKEN_OWNER *ServerOwner,
OUT PTOKEN_PRIMARY_GROUP *ServerGroup
);
BOOLEAN RtlpValidateSDOffsetAndSize (
IN ULONG Offset,
IN ULONG Length,
IN ULONG MinLength,
OUT PULONG MaxLength
);
BOOLEAN
RtlValidRelativeSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptorInput,
IN ULONG SecurityDescriptorLength,
IN SECURITY_INFORMATION RequiredInformation
);
#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
#pragma alloc_text(PAGE,RtlRunEncodeUnicodeString)
#pragma alloc_text(PAGE,RtlRunDecodeUnicodeString)
#pragma alloc_text(PAGE,RtlEraseUnicodeString)
#pragma alloc_text(PAGE,RtlAdjustPrivilege)
#pragma alloc_text(PAGE,RtlValidSid)
#pragma alloc_text(PAGE,RtlEqualSid)
#pragma alloc_text(PAGE,RtlEqualPrefixSid)
#pragma alloc_text(PAGE,RtlLengthRequiredSid)
#pragma alloc_text(PAGE,RtlInitializeSid)
#pragma alloc_text(PAGE,RtlIdentifierAuthoritySid)
#pragma alloc_text(PAGE,RtlSubAuthoritySid)
#pragma alloc_text(PAGE,RtlSubAuthorityCountSid)
#pragma alloc_text(PAGE,RtlLengthSid)
#pragma alloc_text(PAGE,RtlCopySid)
#pragma alloc_text(PAGE,RtlCopySidAndAttributesArray)
#pragma alloc_text(PAGE,RtlLengthSidAsUnicodeString)
#pragma alloc_text(PAGE,RtlConvertSidToUnicodeString)
#pragma alloc_text(PAGE,RtlEqualLuid)
#pragma alloc_text(PAGE,RtlCopyLuid)
#pragma alloc_text(PAGE,RtlCopyLuidAndAttributesArray)
#pragma alloc_text(PAGE,RtlCreateSecurityDescriptor)
#pragma alloc_text(PAGE,RtlCreateSecurityDescriptorRelative)
#pragma alloc_text(PAGE,RtlValidSecurityDescriptor)
#pragma alloc_text(PAGE,RtlLengthSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetAttributesSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetControlSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetControlSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetDaclSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetDaclSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetSaclSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetSaclSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetOwnerSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetOwnerSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetGroupSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetGroupSecurityDescriptor)
#pragma alloc_text(PAGE,RtlAreAllAccessesGranted)
#pragma alloc_text(PAGE,RtlAreAnyAccessesGranted)
#pragma alloc_text(PAGE,RtlMapGenericMask)
#pragma alloc_text(PAGE,RtlImpersonateSelf)
#pragma alloc_text(PAGE,RtlpApplyAclToObject)
#pragma alloc_text(PAGE,RtlpCopyEffectiveAce)
#pragma alloc_text(PAGE,RtlpCopyAces)
#pragma alloc_text(PAGE,RtlpGuidPresentInGuidList)
#pragma alloc_text(PAGE,RtlpInheritAcl2)
#pragma alloc_text(PAGE,RtlpInheritAcl)
#pragma alloc_text(PAGE,RtlpGenerateInheritedAce)
#pragma alloc_text(PAGE,RtlpGenerateInheritAcl)
#pragma alloc_text(PAGE,RtlpComputeMergedAcl2)
#pragma alloc_text(PAGE,RtlpComputeMergedAcl)
#pragma alloc_text(PAGE,RtlpConvertToAutoInheritSecurityObject)
#pragma alloc_text(PAGE,RtlpCompareAces)
#pragma alloc_text(PAGE,RtlpCompareKnownAces)
#pragma alloc_text(PAGE,RtlpCompareKnownObjectAces)
#pragma alloc_text(PAGE,RtlpConvertAclToAutoInherit)
#pragma alloc_text(PAGE,RtlpIsDuplicateAce)
#pragma alloc_text(PAGE,RtlpCreateServerAcl)
#pragma alloc_text(PAGE,RtlpNewSecurityObject)
#pragma alloc_text(PAGE,RtlpSetSecurityObject)
#pragma alloc_text(PAGE,RtlpValidateSDOffsetAndSize)
#pragma alloc_text(PAGE,RtlValidRelativeSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetSecurityDescriptorRMControl)
#pragma alloc_text(PAGE,RtlSetSecurityDescriptorRMControl)
#endif
///////////////////////////////////////////////////////////////////////////////
// //
// Local Macros and Symbols //
// //
///////////////////////////////////////////////////////////////////////////////
#define CREATOR_SID_SIZE 12
#define max(a,b) (((a) > (b)) ? (a) : (b))
//
// Define an array mapping all ACE types to their base type.
//
// For instance, all allowed ACE types are similar. As are all denied ACE types.
//
#if defined(ALLOC_DATA_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
#pragma const_seg("PAGECONST")
#endif
const UCHAR RtlBaseAceType[] = {
ACCESS_ALLOWED_ACE_TYPE, // ACCESS_ALLOWED_ACE_TYPE (0x0)
ACCESS_DENIED_ACE_TYPE, // ACCESS_DENIED_ACE_TYPE (0x1)
SYSTEM_AUDIT_ACE_TYPE, // SYSTEM_AUDIT_ACE_TYPE (0x2)
SYSTEM_ALARM_ACE_TYPE, // SYSTEM_ALARM_ACE_TYPE (0x3)
ACCESS_ALLOWED_ACE_TYPE, // ACCESS_ALLOWED_COMPOUND_ACE_TYPE (0x4)
ACCESS_ALLOWED_ACE_TYPE, // ACCESS_ALLOWED_OBJECT_ACE_TYPE (0x5)
ACCESS_DENIED_ACE_TYPE, // ACCESS_DENIED_OBJECT_ACE_TYPE (0x6)
SYSTEM_AUDIT_ACE_TYPE, // SYSTEM_AUDIT_OBJECT_ACE_TYPE (0x7)
SYSTEM_ALARM_ACE_TYPE // SYSTEM_ALARM_OBJECT_ACE_TYPE (0x8)
};
//
// Define an array defining whether an ACE is a system ACE
//
const UCHAR RtlIsSystemAceType[] = {
FALSE, // ACCESS_ALLOWED_ACE_TYPE (0x0)
FALSE, // ACCESS_DENIED_ACE_TYPE (0x1)
TRUE, // SYSTEM_AUDIT_ACE_TYPE (0x2)
TRUE, // SYSTEM_ALARM_ACE_TYPE (0x3)
FALSE, // ACCESS_ALLOWED_COMPOUND_ACE_TYPE (0x4)
FALSE, // ACCESS_ALLOWED_OBJECT_ACE_TYPE (0x5)
FALSE, // ACCESS_DENIED_OBJECT_ACE_TYPE (0x6)
TRUE, // SYSTEM_AUDIT_OBJECT_ACE_TYPE (0x7)
TRUE // SYSTEM_ALARM_OBJECT_ACE_TYPE (0x8)
};
#if DBG
BOOLEAN RtlpVerboseConvert = FALSE;
#endif // DBG
#define SE_VALID_CONTROL_BITS ( SE_DACL_UNTRUSTED | \
SE_SERVER_SECURITY | \
SE_DACL_AUTO_INHERIT_REQ | \
SE_SACL_AUTO_INHERIT_REQ | \
SE_DACL_AUTO_INHERITED | \
SE_SACL_AUTO_INHERITED | \
SE_DACL_PROTECTED | \
SE_SACL_PROTECTED )
///////////////////////////////////////////////////////////////////////////////
// //
// Null DACL assertions //
// //
///////////////////////////////////////////////////////////////////////////////
#if DBG
#define ASSERT_ON_NULL_DACL 1
#endif
#ifdef ASSERT_ON_NULL_DACL
ULONG RtlpAssertOnNullDacls;
#endif // ASSERT_ON_NULL_DACL
///////////////////////////////////////////////////////////////////////////////
// //
// Exported Procedures //
// //
///////////////////////////////////////////////////////////////////////////////
VOID
RtlRunEncodeUnicodeString(
PUCHAR Seed OPTIONAL,
PUNICODE_STRING String
)
/*++
Routine Description:
This function performs a trivial XOR run-encoding of a string.
The purpose of this run-encoding is to change the character values
to appear somewhat random and typically not printable. This is
useful for transforming passwords that you don't want to be easily
distinguishable by visually scanning a paging file or memory dump.
Arguments:
Seed - Points to a seed value to use in the encoding. If the
pointed to value is zero, then this routine will assign
a value.
String - The string to encode. This string may be decode
by passing it and the seed value to RtlRunDecodeUnicodeString().
Return Value:
None - Nothing can really go wrong unless the caller passes bogus
parameters. In this case, the caller can catch the access
violation.
--*/
{
LARGE_INTEGER Time;
PUCHAR LocalSeed;
NTSTATUS Status;
ULONG i;
PSTRING S;
RTL_PAGED_CODE();
//
// Typecast so we can work on bytes rather than WCHARs
//
S = (PSTRING)((PVOID)String);
//
// If a seed wasn't passed, use the 2nd byte of current time.
// This byte seems to be sufficiently random (by observation).
//
if ((*Seed) == 0) {
Status = NtQuerySystemTime ( &Time );
ASSERT(NT_SUCCESS(Status));
LocalSeed = (PUCHAR)((PVOID)&Time);
i = 1;
(*Seed) = LocalSeed[ i ];
//
// Occasionally, this byte could be zero. That would cause the
// string to become un-decodable, since 0 is the magic value that
// causes us to re-gen the seed. This loop makes sure that we
// never end up with a zero byte (unless time is zero, as well).
//
while ( ((*Seed) == 0) && ( i < sizeof( Time ) ) )
{
(*Seed) |= LocalSeed[ i++ ] ;
}
if ( (*Seed) == 0 )
{
(*Seed) = 1;
}
}
//
// Transform the initial byte.
// The funny constant just keeps the first byte from propagating
// into the second byte in the next step. Without a funny constant
// this would happen for many languages (which typically have every
// other byte zero.
//
//
if (S->Length >= 1) {
S->Buffer[0] ^= ((*Seed) | 0X43);
}
//
// Now transform the rest of the string
//
for (i=1; i<S->Length; i++) {
//
// There are export issues that cause us to want to
// keep this algorithm simple. Please don't change it
// without checking with JimK first. Thanks.
//
//
// In order to be compatible with zero terminated unicode strings,
// this algorithm is designed to not produce a wide character of
// zero as long a the seed is not zero.
//
//
// Simple running XOR with the previous byte and the
// seed value.
//
S->Buffer[i] ^= (S->Buffer[i-1]^(*Seed));
}
return;
}
VOID
RtlRunDecodeUnicodeString(
UCHAR Seed,
PUNICODE_STRING String
)
/*++
Routine Description:
This function performs the inverse of the function performed
by RtlRunEncodeUnicodeString(). Please see RtlRunEncodeUnicodeString()
for details.
Arguments:
Seed - The seed value to use in RtlRunEncodeUnicodeString().
String - The string to reveal.
Return Value:
None - Nothing can really go wrong unless the caller passes bogus
parameters. In this case, the caller can catch the access
violation.
--*/
{
ULONG
i;
PSTRING
S;
RTL_PAGED_CODE();
//
// Typecast so we can work on bytes rather than WCHARs
//
S = (PSTRING)((PVOID)String);
//
// Transform the end of the string
//
for (i=S->Length; i>1; i--) {
//
// a simple running XOR with the previous byte and the
// seed value.
//
S->Buffer[i-1] ^= (S->Buffer[i-2]^Seed);
}
//
// Finally, transform the initial byte
//
if (S->Length >= 1) {
S->Buffer[0] ^= (Seed | 0X43);
}
return;
}
VOID
RtlEraseUnicodeString(
PUNICODE_STRING String
)
/*++
Routine Description:
This function scrubs the passed string by over-writing all
characters in the string. The entire string (i.e., MaximumLength)
is erased, not just the current length.
Argumen ts:
String - The string to be erased.
Return Value:
None - Nothing can really go wrong unless the caller passes bogus
parameters. In this case, the caller can catch the access
violation.
--*/
{
RTL_PAGED_CODE();
if ((String->Buffer == NULL) || (String->MaximumLength == 0)) {
return;
}
RtlZeroMemory( (PVOID)String->Buffer, (ULONG)String->MaximumLength );
String->Length = 0;
return;
}
NTSTATUS
RtlAdjustPrivilege(
ULONG Privilege,
BOOLEAN Enable,
BOOLEAN Client,
PBOOLEAN WasEnabled
)
/*++
Routine Description:
This procedure enables or disables a privilege process-wide.
Arguments:
Privilege - The lower 32-bits of the privilege ID to be enabled or
disabled. The upper 32-bits is assumed to be zero.
Enable - A boolean indicating whether the privilege is to be enabled
or disabled. TRUE indicates the privilege is to be enabled.
FALSE indicates the privilege is to be disabled.
Client - A boolean indicating whether the privilege should be adjusted
in a client token or the process's own token. TRUE indicates
the client's token should be used (and an error returned if there
is no client token). FALSE indicates the process's token should
be used.
WasEnabled - points to a boolean to receive an indication of whether
the privilege was previously enabled or disabled. TRUE indicates
the privilege was previously enabled. FALSE indicates the privilege
was previoulsy disabled. This value is useful for returning the
privilege to its original state after using it.
Return Value:
STATUS_SUCCESS - The privilege has been sucessfully enabled or disabled.
STATUS_PRIVILEGE_NOT_HELD - The privilege is not held by the specified context.
Other status values as may be returned by:
NtOpenProcessToken()
NtAdjustPrivilegesToken()
--*/
{
NTSTATUS
Status,
TmpStatus;
HANDLE
Token;
LUID
LuidPrivilege;
PTOKEN_PRIVILEGES
NewPrivileges,
OldPrivileges;
ULONG
Length;
UCHAR
Buffer1[sizeof(TOKEN_PRIVILEGES)+
((1-ANYSIZE_ARRAY)*sizeof(LUID_AND_ATTRIBUTES))],
Buffer2[sizeof(TOKEN_PRIVILEGES)+
((1-ANYSIZE_ARRAY)*sizeof(LUID_AND_ATTRIBUTES))];
RTL_PAGED_CODE();
NewPrivileges = (PTOKEN_PRIVILEGES)Buffer1;
OldPrivileges = (PTOKEN_PRIVILEGES)Buffer2;
//
// Open the appropriate token...
//
if (Client == TRUE) {
Status = NtOpenThreadToken(
NtCurrentThread(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
FALSE,
&Token
);
} else {
Status = NtOpenProcessToken(
NtCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&Token
);
}
if (!NT_SUCCESS(Status)) {
return(Status);
}
//
// Initialize the privilege adjustment structure
//
LuidPrivilege = RtlConvertUlongToLuid(Privilege);
NewPrivileges->PrivilegeCount = 1;
NewPrivileges->Privileges[0].Luid = LuidPrivilege;
NewPrivileges->Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0;
//
// Adjust the privilege
//
Status = NtAdjustPrivilegesToken(
Token, // TokenHandle
FALSE, // DisableAllPrivileges
NewPrivileges, // NewPrivileges
sizeof(Buffer1), // BufferLength
OldPrivileges, // PreviousState (OPTIONAL)
&Length // ReturnLength
);
TmpStatus = NtClose(Token);
ASSERT(NT_SUCCESS(TmpStatus));
//
// Map the success code NOT_ALL_ASSIGNED to an appropriate error
// since we're only trying to adjust the one privilege.
//
if (Status == STATUS_NOT_ALL_ASSIGNED) {
Status = STATUS_PRIVILEGE_NOT_HELD;
}
if (NT_SUCCESS(Status)) {
//
// If there are no privileges in the previous state, there were
// no changes made. The previous state of the privilege
// is whatever we tried to change it to.
//
if (OldPrivileges->PrivilegeCount == 0) {
(*WasEnabled) = Enable;
} else {
(*WasEnabled) =
(OldPrivileges->Privileges[0].Attributes & SE_PRIVILEGE_ENABLED)
? TRUE : FALSE;
}
}
return(Status);
}
BOOLEAN
RtlValidSid (
IN PSID Sid
)
/*++
Routine Description:
This procedure validates an SID's structure.
Arguments:
Sid - Pointer to the SID structure to validate.
Return Value:
BOOLEAN - TRUE if the structure of Sid is valid.
--*/
{
PISID Isid = (PISID) Sid;
RTL_PAGED_CODE();
//
// Make sure revision is SID_REVISION and sub authority count is not
// greater than maximum number of allowed sub-authorities.
//
try {
if ( Isid != NULL && (Isid->Revision & 0x0f) == SID_REVISION) {
if (Isid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES) {
//
// Verify the memory actually contains the last subauthority
//
#ifndef NTOS_KERNEL_RUNTIME
#define ProbeAndReadUlongUM(Address) \
(*(volatile ULONG *)(Address))
if (Isid->SubAuthorityCount > 0) {
ProbeAndReadUlongUM(
&Isid->SubAuthority[Isid->SubAuthorityCount-1]
);
}
#endif // !NTOS_KERNEL_RUNTIME
return TRUE;
}
}
} except(EXCEPTION_EXECUTE_HANDLER) {
return FALSE;
}
return FALSE;
}
BOOLEAN
RtlEqualSid (
IN PSID Sid1,
IN PSID Sid2
)
/*++
Routine Description:
This procedure tests two SID values for equality.
Arguments:
Sid1, Sid2 - Supply pointers to the two SID values to compare.
The SID structures are assumed to be valid.
Return Value:
BOOLEAN - TRUE if the value of Sid1 is equal to Sid2, and FALSE
otherwise.
--*/
{
ULONG SidLength;
RTL_PAGED_CODE();
//
// Make sure they are the same revision
//
if ( ((SID *)Sid1)->Revision == ((SID *)Sid2)->Revision ) {
//
// Check the SubAuthorityCount first, because it's fast and
// can help us exit faster.
//
if ( *RtlSubAuthorityCountSid( Sid1 ) == *RtlSubAuthorityCountSid( Sid2 )) {
SidLength = SeLengthSid( Sid1 );
return( (BOOLEAN)RtlEqualMemory( Sid1, Sid2, SidLength) );
}
}
return( FALSE );
}
BOOLEAN
RtlEqualPrefixSid (
IN PSID Sid1,
IN PSID Sid2
)
/*++
Routine Description:
This procedure tests two SID prefix values for equality.
An SID prefix is the entire SID except for the last sub-authority
value.
Arguments:
Sid1, Sid2 - Supply pointers to the two SID values to compare.
The SID structures are assumed to be valid.
Return Value:
BOOLEAN - TRUE if the prefix value of Sid1 is equal to Sid2, and FALSE
otherwise.
--*/
{
LONG Index;
//
// Typecast to the opaque SID structures.
//
SID *ISid1 = Sid1;
SID *ISid2 = Sid2;
RTL_PAGED_CODE();
//
// Make sure they are the same revision
//
if (ISid1->Revision == ISid2->Revision ) {
//
// Compare IdentifierAuthority values
//
if ( (ISid1->IdentifierAuthority.Value[0] ==
ISid2->IdentifierAuthority.Value[0]) &&
(ISid1->IdentifierAuthority.Value[1]==
ISid2->IdentifierAuthority.Value[1]) &&
(ISid1->IdentifierAuthority.Value[2] ==
ISid2->IdentifierAuthority.Value[2]) &&
(ISid1->IdentifierAuthority.Value[3] ==
ISid2->IdentifierAuthority.Value[3]) &&
(ISid1->IdentifierAuthority.Value[4] ==
ISid2->IdentifierAuthority.Value[4]) &&
(ISid1->IdentifierAuthority.Value[5] ==
ISid2->IdentifierAuthority.Value[5])
) {
//
// Compare SubAuthorityCount values
//
if (ISid1->SubAuthorityCount == ISid2->SubAuthorityCount) {
if (ISid1->SubAuthorityCount == 0) {
return TRUE;
}
Index = 0;
while (Index < (ISid1->SubAuthorityCount - 1)) {
if ((ISid1->SubAuthority[Index]) != (ISid2->SubAuthority[Index])) {
//
// Found some SubAuthority values that weren't equal.
//
return FALSE;
}
Index += 1;
}
//
// All SubAuthority values are equal.
//
return TRUE;
}
}
}
//
// Either the Revision, SubAuthorityCount, or IdentifierAuthority values
// weren't equal.
//
return FALSE;
}
ULONG
RtlLengthRequiredSid (
IN ULONG SubAuthorityCount
)
/*++
Routine Description:
This routine returns the length, in bytes, required to store an SID
with the specified number of Sub-Authorities.
Arguments:
SubAuthorityCount - The number of sub-authorities to be stored in the SID.
Return Value:
ULONG - The length, in bytes, required to store the SID.
--*/
{
RTL_PAGED_CODE();
return (8L + (4 * SubAuthorityCount));
}
#ifndef NTOS_KERNEL_RUNTIME
NTSTATUS
RtlAllocateAndInitializeSid(
IN PSID_IDENTIFIER_AUTHORITY IdentifierAuthority,
IN UCHAR SubAuthorityCount,
IN ULONG SubAuthority0,
IN ULONG SubAuthority1,
IN ULONG SubAuthority2,
IN ULONG SubAuthority3,
IN ULONG SubAuthority4,
IN ULONG SubAuthority5,
IN ULONG SubAuthority6,
IN ULONG SubAuthority7,
OUT PSID *Sid
)
/*++
Routine Description:
This function allocates and initializes a sid with the specified
number of sub-authorities (up to 8). A sid allocated with this
routine must be freed using RtlFreeSid().
THIS ROUTINE IS CURRENTLY NOT CALLABLE FROM KERNEL MODE.
Arguments:
IdentifierAuthority - Pointer to the Identifier Authority value to
set in the SID.
SubAuthorityCount - The number of sub-authorities to place in the SID.
This also identifies how many of the SubAuthorityN parameters
have meaningful values. This must contain a value from 0 through
8.
SubAuthority0-7 - Provides the corresponding sub-authority value to
place in the SID. For example, a SubAuthorityCount value of 3
indicates that SubAuthority0, SubAuthority1, and SubAuthority0
have meaningful values and the rest are to be ignored.
Sid - Receives a pointer to the SID data structure to initialize.
Return Value:
STATUS_SUCCESS - The SID has been allocated and initialized.
STATUS_NO_MEMORY - The attempt to allocate memory for the SID
failed.
STATUS_INVALID_SID - The number of sub-authorities specified did
not fall in the valid range for this api (0 through 8).
--*/
{
PISID ISid;
RTL_PAGED_CODE();
if ( SubAuthorityCount > 8 ) {
return( STATUS_INVALID_SID );
}
ISid = RtlAllocateHeap( RtlProcessHeap(), 0,
RtlLengthRequiredSid(SubAuthorityCount)
);
if (ISid == NULL) {
return(STATUS_NO_MEMORY);
}
ISid->SubAuthorityCount = (UCHAR)SubAuthorityCount;
ISid->Revision = 1;
ISid->IdentifierAuthority = *IdentifierAuthority;
switch (SubAuthorityCount) {
case 8:
ISid->SubAuthority[7] = SubAuthority7;
case 7:
ISid->SubAuthority[6] = SubAuthority6;
case 6:
ISid->SubAuthority[5] = SubAuthority5;
case 5:
ISid->SubAuthority[4] = SubAuthority4;
case 4:
ISid->SubAuthority[3] = SubAuthority3;
case 3:
ISid->SubAuthority[2] = SubAuthority2;
case 2:
ISid->SubAuthority[1] = SubAuthority1;
case 1:
ISid->SubAuthority[0] = SubAuthority0;
case 0:
;
}
(*Sid) = ISid;
return( STATUS_SUCCESS );
}
#endif // NTOS_KERNEL_RUNTIME
NTSTATUS
RtlInitializeSid(
IN PSID Sid,
IN PSID_IDENTIFIER_AUTHORITY IdentifierAuthority,
IN UCHAR SubAuthorityCount
)
/*++
Routine Description:
This function initializes an SID data structure. It does not, however,
set the sub-authority values. This must be done separately.
Arguments:
Sid - Pointer to the SID data structure to initialize.
IdentifierAuthority - Pointer to the Identifier Authority value to
set in the SID.
SubAuthorityCount - The number of sub-authorities that will be placed in
the SID (a separate action).
Return Value:
--*/
{
PISID ISid;
RTL_PAGED_CODE();
//
// Typecast to the opaque SID
//
ISid = (PISID)Sid;
if ( SubAuthorityCount > SID_MAX_SUB_AUTHORITIES ) {
return( STATUS_INVALID_PARAMETER );
}
ISid->SubAuthorityCount = (UCHAR)SubAuthorityCount;
ISid->Revision = 1;
ISid->IdentifierAuthority = *IdentifierAuthority;
return( STATUS_SUCCESS );
}
#ifndef NTOS_KERNEL_RUNTIME
PVOID
RtlFreeSid(
IN PSID Sid
)
/*++
Routine Description:
This function is used to free a SID previously allocated using
RtlAllocateAndInitializeSid().
THIS ROUTINE IS CURRENTLY NOT CALLABLE FROM KERNEL MODE.
Arguments:
Sid - Pointer to the SID to free.
Return Value:
None.
--*/
{
RTL_PAGED_CODE();
if (RtlFreeHeap( RtlProcessHeap(), 0, Sid ))
return NULL;
else
return Sid;
}
#endif // NTOS_KERNEL_RUNTIME
PSID_IDENTIFIER_AUTHORITY
RtlIdentifierAuthoritySid(
IN PSID Sid
)
/*++
Routine Description:
This function returns the address of an SID's IdentifierAuthority field.
Arguments:
Sid - Pointer to the SID data structure.
Return Value:
--*/
{
PISID ISid;
RTL_PAGED_CODE();
//
// Typecast to the opaque SID
//
ISid = (PISID)Sid;
return &(ISid->IdentifierAuthority);
}
PULONG
RtlSubAuthoritySid(
IN PSID Sid,
IN ULONG SubAuthority
)
/*++
Routine Description:
This function returns the address of a sub-authority array element of
an SID.
Arguments:
Sid - Pointer to the SID data structure.
SubAuthority - An index indicating which sub-authority is being specified.
This value is not compared against the number of sub-authorities in the
SID for validity.
Return Value:
--*/
{
RTL_PAGED_CODE();
return RtlpSubAuthoritySid( Sid, SubAuthority );
}
PUCHAR
RtlSubAuthorityCountSid(
IN PSID Sid
)
/*++
Routine Description:
This function returns the address of the sub-authority count field of
an SID.
Arguments:
Sid - Pointer to the SID data structure.
Return Value:
--*/
{
PISID ISid;
RTL_PAGED_CODE();
//
// Typecast to the opaque SID
//
ISid = (PISID)Sid;
return &(ISid->SubAuthorityCount);
}
ULONG
RtlLengthSid (
IN PSID Sid
)
/*++
Routine Description:
This routine returns the length, in bytes, of a structurally valid SID.
Arguments:
Sid - Points to the SID whose length is to be returned. The
SID's structure is assumed to be valid.
Return Value:
ULONG - The length, in bytes, of the SID.
--*/
{
RTL_PAGED_CODE();
return SeLengthSid(Sid);
}
NTSTATUS
RtlCopySid (
IN ULONG DestinationSidLength,
OUT PSID DestinationSid,
IN PSID SourceSid
)
/*++
Routine Description:
This routine copies the value of the source SID to the destination
SID.
Arguments:
DestinationSidLength - Indicates the length, in bytes, of the
destination SID buffer.
DestinationSid - Pointer to a buffer to receive a copy of the
source Sid value.
SourceSid - Supplies the Sid value to be copied.
Return Value:
STATUS_SUCCESS - Indicates the SID was successfully copied.
STATUS_BUFFER_TOO_SMALL - Indicates the target buffer wasn't
large enough to receive a copy of the SID.
--*/
{
ULONG SidLength = SeLengthSid(SourceSid);
RTL_PAGED_CODE();
if (SidLength > DestinationSidLength) {
return STATUS_BUFFER_TOO_SMALL;
}
//
// Buffer is large enough
//
RtlMoveMemory( DestinationSid, SourceSid, SidLength );
return STATUS_SUCCESS;
}
NTSTATUS
RtlCopySidAndAttributesArray (
IN ULONG ArrayLength,
IN PSID_AND_ATTRIBUTES Source,
IN ULONG TargetSidBufferSize,
OUT PSID_AND_ATTRIBUTES TargetArrayElement,
OUT PSID TargetSid,
OUT PSID *NextTargetSid,
OUT PULONG RemainingTargetSidBufferSize
)
/*++
Routine Description:
This routine copies the value of the source SID_AND_ATTRIBUTES array
to the target. The actual SID values are placed according to a separate
parameter. This allows multiple arrays to be merged using this service
to copy each.
Arguments:
ArrayLength - Number of elements in the source array to copy.
Source - Pointer to the source array.
TargetSidBufferSize - Indicates the length, in bytes, of the buffer
to receive the actual SID values. If this value is less than
the actual amount needed, then STATUS_BUFFER_TOO_SMALL is returned.
TargetArrayElement - Indicates where the array elements are to be
copied to (but not the SID values themselves).
TargetSid - Indicates where the target SID values s are to be copied. This
is assumed to be ULONG aligned. Each SID value will be copied
into this buffer. Each SID will be ULONG aligned.
NextTargetSid - On completion, will be set to point to the ULONG
aligned address following the last SID copied.
RemainingTargetSidBufferSize - On completion, receives an indicatation
of how much of the SID buffer is still unused.
Return Value:
STATUS_SUCCESS - The call completed successfully.
STATUS_BUFFER_TOO_SMALL - Indicates the buffer to receive the SID
values wasn't large enough.
--*/
{
ULONG Index = 0;
PSID NextSid = TargetSid;
ULONG NextSidLength;
ULONG AlignedSidLength;
ULONG RemainingLength = TargetSidBufferSize;
RTL_PAGED_CODE();
while (Index < ArrayLength) {
NextSidLength = SeLengthSid( Source[Index].Sid );
AlignedSidLength = PtrToUlong(LongAlign(NextSidLength));
if (NextSidLength > RemainingLength) {
return STATUS_BUFFER_TOO_SMALL;
}
RemainingLength -= AlignedSidLength;
TargetArrayElement[Index].Sid = NextSid;
TargetArrayElement[Index].Attributes = Source[Index].Attributes;
RtlCopySid( NextSidLength, NextSid, Source[Index].Sid );
NextSid = (PSID)((PCHAR)NextSid + AlignedSidLength);
Index += 1;
} //end_while
(*NextTargetSid) = NextSid;
(*RemainingTargetSidBufferSize) = RemainingLength;
return STATUS_SUCCESS;
}
NTSTATUS
RtlLengthSidAsUnicodeString(
PSID Sid,
PULONG StringLength
)
/*++
Routine Description:
This function returns the maximum length of the string needed
to represent the SID supplied. The actual string may be shorter,
but this is intended to be a quick calculation.
Arguments:
Sid - Supplies the SID that is to be converted to unicode.
StringLength - Receives the max length required in bytes.
Return Value:
SUCCESS - The conversion was successful
STATUS_INVALID_SID - The sid provided does not have a valid structure,
or has too many sub-authorities (more than SID_MAX_SUB_AUTHORITIES).
--*/
{
ULONG i ;
PISID iSid = (PISID)Sid; // pointer to opaque structure
RTL_PAGED_CODE();
if ( RtlValidSid( Sid ) != TRUE)
{
return(STATUS_INVALID_SID);
}
//
// if the SID's IA value has 5 or 6 significant bytes, the
// representation will be in hex, with a 0x preceding. Otherwise
// it will be in decimal, with at most 10 characters.
//
if ( (iSid->IdentifierAuthority.Value[0] != 0) ||
(iSid->IdentifierAuthority.Value[1] != 0) )
{
i = 14 ; // 0x665544332211
}
else
{
i = 10 ; // 4294967295 is the max ulong, at 10 chars
}
i += 4 ; // room for the S-1-
//
// for each sub authority, it is a max of 10 chars (for a ulong),
// plus the - separator
//
i += 11 * iSid->SubAuthorityCount ;
*StringLength = i * sizeof( WCHAR );
return STATUS_SUCCESS ;
}
NTSTATUS
RtlConvertSidToUnicodeString(
PUNICODE_STRING UnicodeString,
PSID Sid,
BOOLEAN AllocateDestinationString
)
/*++
Routine Description:
This function generates a printable unicode string representation
of a SID.
The resulting string will take one of two forms. If the
IdentifierAuthority value is not greater than 2^32, then
the SID will be in the form:
S-1-281736-12-72-9-110
^ ^^ ^^ ^ ^^^
| | | | |
+-----+--+-+--+---- Decimal
Otherwise it will take the form:
S-1-0x173495281736-12-72-9-110
^^^^^^^^^^^^^^ ^^ ^^ ^ ^^^
Hexidecimal | | | |
+--+-+--+---- Decimal
Arguments:
UnicodeString - Returns a unicode string that is equivalent to
the SID. The maximum length field is only set if
AllocateDestinationString is TRUE.
Sid - Supplies the SID that is to be converted to unicode.
AllocateDestinationString - Supplies a flag that controls whether or
not this API allocates the buffer space for the destination
string. If it does, then the buffer must be deallocated using
RtlFreeUnicodeString (note that only storage for
DestinationString->Buffer is allocated by this API).
Return Value:
SUCCESS - The conversion was successful
STATUS_INVALID_SID - The sid provided does not have a valid structure,
or has too many sub-authorities (more than SID_MAX_SUB_AUTHORITIES).
STATUS_NO_MEMORY - There was not sufficient memory to allocate the
target string. This is returned only if AllocateDestinationString
is specified as TRUE.
STATUS_BUFFER_OVERFLOW - This is returned only if
AllocateDestinationString is specified as FALSE.
--*/
{
NTSTATUS Status;
WCHAR UniBuffer[ 256 ];
PWSTR Offset ;
UNICODE_STRING LocalString ;
UCHAR i;
ULONG Tmp;
LARGE_INTEGER Auth ;
PISID iSid = (PISID)Sid; // pointer to opaque structure
RTL_PAGED_CODE();
if (RtlValidSid( Sid ) != TRUE) {
return(STATUS_INVALID_SID);
}
if ( iSid->Revision != SID_REVISION )
{
return STATUS_INVALID_SID ;
}
wcscpy( UniBuffer, L"S-1-" );
Offset = &UniBuffer[ 4 ];
if ( (iSid->IdentifierAuthority.Value[0] != 0) ||
(iSid->IdentifierAuthority.Value[1] != 0) ){
//
// Ugly hex dump.
//
Auth.HighPart = (LONG) (iSid->IdentifierAuthority.Value[ 0 ] << 8) +
(LONG) iSid->IdentifierAuthority.Value[ 1 ] ;
Auth.LowPart = (ULONG)iSid->IdentifierAuthority.Value[5] +
(ULONG)(iSid->IdentifierAuthority.Value[4] << 8) +
(ULONG)(iSid->IdentifierAuthority.Value[3] << 16) +
(ULONG)(iSid->IdentifierAuthority.Value[2] << 24);
Status = RtlLargeIntegerToUnicode(
&Auth,
16,
256 - (LONG) (Offset - UniBuffer),
Offset );
} else {
Tmp = (ULONG)iSid->IdentifierAuthority.Value[5] +
(ULONG)(iSid->IdentifierAuthority.Value[4] << 8) +
(ULONG)(iSid->IdentifierAuthority.Value[3] << 16) +
(ULONG)(iSid->IdentifierAuthority.Value[2] << 24);
Status = RtlIntegerToUnicode(
Tmp,
10,
256 - (LONG) (Offset - UniBuffer),
Offset );
}
if ( !NT_SUCCESS( Status ) )
{
return Status ;
}
for (i=0;i<iSid->SubAuthorityCount ;i++ ) {
while ( *Offset && ( Offset < &UniBuffer[ 255 ] ) )
{
Offset++ ;
}
*Offset++ = L'-' ;
Status = RtlIntegerToUnicode(
iSid->SubAuthority[ i ],
10,
256 - (LONG) (Offset - UniBuffer),
Offset );
if ( !NT_SUCCESS( Status ) )
{
return Status ;
}
}
if ( AllocateDestinationString )
{
if ( RtlCreateUnicodeString( UnicodeString,
UniBuffer ) )
{
Status = STATUS_SUCCESS ;
}
else
{
Status = STATUS_NO_MEMORY ;
}
}
else
{
while ( *Offset && ( Offset < &UniBuffer[ 255 ] ) )
{
Offset++ ;
}
Tmp = (ULONG) (Offset - UniBuffer) * sizeof( WCHAR );
if ( Tmp < UnicodeString->MaximumLength )
{
LocalString.Length = (USHORT) Tmp ;
LocalString.MaximumLength = LocalString.Length + sizeof( WCHAR );
LocalString.Buffer = UniBuffer ;
RtlCopyUnicodeString(
UnicodeString,
&LocalString );
Status = STATUS_SUCCESS ;
}
else
{
Status = STATUS_BUFFER_OVERFLOW ;
}
}
return(Status);
}
BOOLEAN
RtlEqualLuid (
IN PLUID Luid1,
IN PLUID Luid2
)
/*++
Routine Description:
This procedure test two LUID values for equality.
This routine is here for backwards compatibility only. New code
should use the macro.
Arguments:
Luid1, Luid2 - Supply pointers to the two LUID values to compare.
Return Value:
BOOLEAN - TRUE if the value of Luid1 is equal to Luid2, and FALSE
otherwise.
--*/
{
LUID UNALIGNED * TempLuid1;
LUID UNALIGNED * TempLuid2;
RTL_PAGED_CODE();
return((Luid1->HighPart == Luid2->HighPart) &&
(Luid1->LowPart == Luid2->LowPart));
}
VOID
RtlCopyLuid (
OUT PLUID DestinationLuid,
IN PLUID SourceLuid
)
/*++
Routine Description:
This routine copies the value of the source LUID to the
destination LUID.
Arguments:
DestinationLuid - Receives a copy of the source Luid value.
SourceLuid - Supplies the Luid value to be copied. This LUID is
assumed to be structurally valid.
Return Value:
None.
--*/
{
RTL_PAGED_CODE();
(*DestinationLuid) = (*SourceLuid);
return;
}
VOID
RtlCopyLuidAndAttributesArray (
IN ULONG ArrayLength,
IN PLUID_AND_ATTRIBUTES Source,
OUT PLUID_AND_ATTRIBUTES Target
)
/*++
Routine Description:
This routine copies the value of the source LUID_AND_ATTRIBUTES array
to the target.
Arguments:
ArrayLength - Number of elements in the source array to copy.
Source - The source array.
Target - Indicates where the array elements are to be copied to.
Return Value:
None.
--*/
{
ULONG Index = 0;
RTL_PAGED_CODE();
while (Index < ArrayLength) {
Target[Index] = Source[Index];
Index += 1;
} //end_while
return;
}
NTSTATUS
RtlCreateSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN ULONG Revision
)
/*++
Routine Description:
This procedure initializes a new "absolute format" security descriptor.
After the procedure call the security descriptor is initialized with no
system ACL, no discretionary ACL, no owner, no primary group and
all control flags set to false (null).
Arguments:
SecurityDescriptor - Supplies the security descriptor to
initialize.
Revision - Provides the revision level to assign to the security
descriptor. This should be one (1) for this release.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision level provided
is not supported by this routine.
--*/
{
RTL_PAGED_CODE();
//
// Check the requested revision
//
if (Revision == SECURITY_DESCRIPTOR_REVISION) {
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
RtlZeroMemory( ISecurityDescriptor, sizeof(SECURITY_DESCRIPTOR));
ISecurityDescriptor->Revision = SECURITY_DESCRIPTOR_REVISION;
return STATUS_SUCCESS;
}
return STATUS_UNKNOWN_REVISION;
}
NTSTATUS
RtlCreateSecurityDescriptorRelative (
IN PISECURITY_DESCRIPTOR_RELATIVE SecurityDescriptor,
IN ULONG Revision
)
/*++
Routine Description:
This procedure initializes a new "relative format" security descriptor.
After the procedure call the security descriptor is initialized with no
system ACL, no discretionary ACL, no owner, no primary group and
all control flags set to false (null).
Arguments:
SecurityDescriptor - Supplies the security descriptor to
initialize.
Revision - Provides the revision level to assign to the security
descriptor. This should be one (1) for this release.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision level provided
is not supported by this routine.
Note:
Warning, this code assume the caller allocated a relative security
descriptor rather than a relative one. Absolute is larger on systems
with 64-bit pointers.
--*/
{
RTL_PAGED_CODE();
//
// Check the requested revision
//
if (Revision == SECURITY_DESCRIPTOR_REVISION) {
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
RtlZeroMemory( SecurityDescriptor, sizeof(SECURITY_DESCRIPTOR_RELATIVE));
SecurityDescriptor->Revision = SECURITY_DESCRIPTOR_REVISION;
return STATUS_SUCCESS;
}
return STATUS_UNKNOWN_REVISION;
}
BOOLEAN
RtlValidSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor
)
/*++
Routine Description:
This procedure validates a SecurityDescriptor's structure. This
involves validating the revision levels of each component of the
security descriptor.
Arguments:
SecurityDescriptor - Pointer to the SECURITY_DESCRIPTOR structure
to validate.
Return Value:
BOOLEAN - TRUE if the structure of SecurityDescriptor is valid.
--*/
{
PSID Owner;
PSID Group;
PACL Dacl;
PACL Sacl;
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
RTL_PAGED_CODE();
try {
//
// known revision ?
//
if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
return FALSE;
}
//
// Validate each element contained in the security descriptor
//
Owner = RtlpOwnerAddrSecurityDescriptor( ISecurityDescriptor );
if (Owner != NULL) {
if (!RtlValidSid( Owner )) {
return FALSE;
}
}
Group = RtlpGroupAddrSecurityDescriptor( ISecurityDescriptor );
if (Group != NULL) {
if (!RtlValidSid( Group )) {
return FALSE;
}
}
Dacl = RtlpDaclAddrSecurityDescriptor( ISecurityDescriptor );
if (Dacl != NULL ) {
if (!RtlValidAcl( Dacl )) {
return FALSE;
}
}
Sacl = RtlpSaclAddrSecurityDescriptor( ISecurityDescriptor );
if ( Sacl != NULL ) {
if (!RtlValidAcl( Sacl )) {
return FALSE;
}
}
} except(EXCEPTION_EXECUTE_HANDLER) {
return FALSE;
}
//
// All components are valid
//
return TRUE;
}
ULONG
RtlLengthSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor
)
/*++
Routine Description:
This routine returns the length, in bytes, necessary to capture a
structurally valid SECURITY_DESCRIPTOR. The length includes the length
of all associated data structures (like SIDs and ACLs). The length also
takes into account the alignment requirements of each component.
The minimum length of a security descriptor (one which has no associated
SIDs or ACLs) is SECURITY_DESCRIPTOR_MIN_LENGTH.
Arguments:
SecurityDescriptor - Points to the SECURITY_DESCRIPTOR whose
length is to be returned. The SECURITY_DESCRIPTOR's
structure is assumed to be valid.
Return Value:
ULONG - The length, in bytes, of the SECURITY_DESCRIPTOR.
--*/
{
ULONG sum;
PVOID Temp;
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor = (SECURITY_DESCRIPTOR *)SecurityDescriptor;
RTL_PAGED_CODE();
//
// The length is the sum of the following:
//
// SECURITY_DESCRIPTOR_MIN_LENGTH (or sizeof(SECURITY_DESCRIPTOR))
// length of Owner SID (if present)
// length of Group SID (if present)
// length of Discretionary ACL (if present and non-null)
// length of System ACL (if present and non-null)
//
sum = ISecurityDescriptor->Control & SE_SELF_RELATIVE ?
sizeof(SECURITY_DESCRIPTOR_RELATIVE) :
sizeof(SECURITY_DESCRIPTOR);
//
// Add in length of Owner SID
//
Temp = RtlpOwnerAddrSecurityDescriptor(ISecurityDescriptor);
if (Temp != NULL) {
sum += LongAlignSize(SeLengthSid(Temp));
}
//
// Add in length of Group SID
//
Temp = RtlpGroupAddrSecurityDescriptor(ISecurityDescriptor);
if (Temp != NULL) {
sum += LongAlignSize(SeLengthSid(Temp));
}
//
// Add in used length of Discretionary ACL
//
Temp = RtlpDaclAddrSecurityDescriptor(ISecurityDescriptor);
if ( Temp != NULL ) {
sum += LongAlignSize(((PACL) Temp)->AclSize );
}
//
// Add in used length of System Acl
//
Temp = RtlpSaclAddrSecurityDescriptor(ISecurityDescriptor);
if ( Temp != NULL ) {
sum += LongAlignSize(((PACL) Temp)->AclSize );
}
return sum;
}
NTSTATUS
RtlSetAttributesSecurityDescriptor(
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN SECURITY_DESCRIPTOR_CONTROL Control,
OUT PULONG Revision
)
{
RTL_PAGED_CODE();
//
// Always return the revision value - even if this isn't a valid
// security descriptor
//
*Revision = ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Revision;
if ( ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Revision
!= SECURITY_DESCRIPTOR_REVISION ) {
return STATUS_UNKNOWN_REVISION;
}
// This is a worthless API. There is no way to turn any of the bits off.
// Use the newer RtlSetControlSecurityDescriptor.
Control &= SE_VALID_CONTROL_BITS;
return RtlSetControlSecurityDescriptor ( SecurityDescriptor, Control, Control );
}
NTSTATUS
RtlGetControlSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
OUT PSECURITY_DESCRIPTOR_CONTROL Control,
OUT PULONG Revision
)
/*++
Routine Description:
This procedure retrieves the control information from a security descriptor.
Arguments:
SecurityDescriptor - Supplies the security descriptor.
Control - Receives the control information.
Revision - Receives the revision of the security descriptor.
This value will always be returned, even if an error
is returned by this routine.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision of the security
descriptor is not known to the routine. It may be a newer
revision than the routine knows about.
--*/
{
RTL_PAGED_CODE();
//
// Always return the revision value - even if this isn't a valid
// security descriptor
//
*Revision = ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Revision;
if ( ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Revision
!= SECURITY_DESCRIPTOR_REVISION ) {
return STATUS_UNKNOWN_REVISION;
}
*Control = ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Control;
return STATUS_SUCCESS;
}
NTSTATUS
RtlSetControlSecurityDescriptor (
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
IN SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest,
IN SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet
)
/*++
Routine Description:
This procedure sets the control information in a security descriptor.
For instance,
SetSecurityDescriptorControl( &SecDesc,
SE_DACL_PROTECTED,
SE_DACL_PROTECTED );
marks the DACL on the security descriptor as protected. And
SetSecurityDescriptorControl( &SecDesc,
SE_DACL_PROTECTED,
0 );
marks the DACL as not protected.
Arguments:
pSecurityDescriptor - Supplies the security descriptor.
ControlBitsOfInterest - A mask of the control bits being changed, set,
or reset by this call. The mask is the logical OR of one or more of
the following flags:
SE_DACL_UNTRUSTED
SE_SERVER_SECURITY
SE_DACL_AUTO_INHERIT_REQ
SE_SACL_AUTO_INHERIT_REQ
SE_DACL_AUTO_INHERITED
SE_SACL_AUTO_INHERITED
SE_DACL_PROTECTED
SE_SACL_PROTECTED
ControlBitsToSet - A mask indicating what the bits specified by ControlBitsOfInterest
should be set to.
Return Value:
Returns TRUE for success, FALSE for failure. Extended error status
is available using GetLastError.
--*/
{
//
// Ensure the caller passed valid bits.
//
if ( (ControlBitsOfInterest & ~SE_VALID_CONTROL_BITS) != 0 ||
(ControlBitsToSet & ~ControlBitsOfInterest) != 0 ) {
return STATUS_INVALID_PARAMETER;
}
((SECURITY_DESCRIPTOR *)pSecurityDescriptor)->Control &= ~ControlBitsOfInterest;
((SECURITY_DESCRIPTOR *)pSecurityDescriptor)->Control |= ControlBitsToSet;
return STATUS_SUCCESS;
}
NTSTATUS
RtlSetDaclSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN BOOLEAN DaclPresent,
IN PACL Dacl OPTIONAL,
IN BOOLEAN DaclDefaulted OPTIONAL
)
/*++
Routine Description:
This procedure sets the discretionary ACL information of an absolute
format security descriptor. If there is already a discretionary ACL
present in the security descriptor, it is superseded.
Arguments:
SecurityDescriptor - Supplies the security descriptor to be which
the discretionary ACL is to be added.
DaclPresent - If FALSE, indicates the DaclPresent flag in the
security descriptor should be set to FALSE. In this case,
the remaining optional parameters are ignored. Otherwise,
the DaclPresent control flag in the security descriptor is
set to TRUE and the remaining optional parameters are not
ignored.
Dacl - Supplies the discretionary ACL for the security
descriptor. If this optional parameter is not passed, then a
null ACL is assigned to the security descriptor. A null
discretionary ACL unconditionally grants access. The ACL is
referenced by, not copied into, by the security descriptor.
DaclDefaulted - When set, indicates the discretionary ACL was
picked up from some default mechanism (rather than explicitly
specified by a user). This value is set in the DaclDefaulted
control flag in the security descriptor. If this optional
parameter is not passed, then the DaclDefaulted flag will be
cleared.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision of the security
descriptor is not known to the routine. It may be a newer
revision than the routine knows about.
STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
is not an absolute format security descriptor.
--*/
{
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
RTL_PAGED_CODE();
//
// Check the revision
//
if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
return STATUS_UNKNOWN_REVISION;
}
//
// Make sure the descriptor is absolute format
//
if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
return STATUS_INVALID_SECURITY_DESCR;
}
//
// Assign the DaclPresent flag value passed
//
if (DaclPresent) {
ISecurityDescriptor->Control |= SE_DACL_PRESENT;
//
// Assign the ACL address if passed, otherwise set to null.
//
ISecurityDescriptor->Dacl = NULL;
if (ARGUMENT_PRESENT(Dacl)) {
ISecurityDescriptor->Dacl = Dacl;
}
//
// Assign DaclDefaulted flag if passed, otherwise clear it.
//
ISecurityDescriptor->Control &= ~SE_DACL_DEFAULTED;
if (DaclDefaulted == TRUE) {
ISecurityDescriptor->Control |= SE_DACL_DEFAULTED;
}
} else {
ISecurityDescriptor->Control &= ~SE_DACL_PRESENT;
}
return STATUS_SUCCESS;
}
NTSTATUS
RtlGetDaclSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
OUT PBOOLEAN DaclPresent,
OUT PACL *Dacl,
OUT PBOOLEAN DaclDefaulted
)
/*++
Routine Description:
This procedure retrieves the discretionary ACL information of a
security descriptor.
Arguments:
SecurityDescriptor - Supplies the security descriptor.
DaclPresent - If TRUE, indicates that the security descriptor
does contain a discretionary ACL. In this case, the
remaining OUT parameters will receive valid values.
Otherwise, the security descriptor does not contain a
discretionary ACL and the remaining OUT parameters will not
receive valid values.
Dacl - This value is returned only if the value returned for the
DaclPresent flag is TRUE. In this case, the Dacl parameter
receives the address of the security descriptor's
discretionary ACL. If this value is returned as null, then
the security descriptor has a null discretionary ACL.
DaclDefaulted - This value is returned only if the value returned
for the DaclPresent flag is TRUE. In this case, the
DaclDefaulted parameter receives the value of the security
descriptor's DaclDefaulted control flag.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision of the security
descriptor is not known to the routine. It may be a newer
revision than the routine knows about.
--*/
{
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
RTL_PAGED_CODE();
//
// Check the revision
//
if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
return STATUS_UNKNOWN_REVISION;
}
//
// Assign the DaclPresent flag value
//
*DaclPresent = RtlpAreControlBitsSet( ISecurityDescriptor, SE_DACL_PRESENT );
if (*DaclPresent) {
//
// Assign the ACL address.
//
*Dacl = RtlpDaclAddrSecurityDescriptor(ISecurityDescriptor);
//
// Assign DaclDefaulted flag.
//
*DaclDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_DACL_DEFAULTED );
}
return STATUS_SUCCESS;
}
NTSTATUS
RtlSetSaclSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN BOOLEAN SaclPresent,
IN PACL Sacl OPTIONAL,
IN BOOLEAN SaclDefaulted OPTIONAL
)
/*++
Routine Description:
This procedure sets the system ACL information of an absolute security
descriptor. If there is already a system ACL present in the
security descriptor, it is superseded.
Arguments:
SecurityDescriptor - Supplies the security descriptor to be which
the system ACL is to be added.
SaclPresent - If FALSE, indicates the SaclPresent flag in the
security descriptor should be set to FALSE. In this case,
the remaining optional parameters are ignored. Otherwise,
the SaclPresent control flag in the security descriptor is
set to TRUE and the remaining optional parameters are not
ignored.
Sacl - Supplies the system ACL for the security descriptor. If
this optional parameter is not passed, then a null ACL is
assigned to the security descriptor. The ACL is referenced
by, not copied into, by the security descriptor.
SaclDefaulted - When set, indicates the system ACL was picked up
from some default mechanism (rather than explicitly specified
by a user). This value is set in the SaclDefaulted control
flag in the security descriptor. If this optional parameter
is not passed, then the SaclDefaulted flag will be cleared.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision of the security
descriptor is not known to the routine. It may be a newer
revision than the routine knows about.
STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
is not an absolute format security descriptor.
--*/
{
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
RTL_PAGED_CODE();
//
// Check the revision
//
if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
return STATUS_UNKNOWN_REVISION;
}
//
// Make sure the descriptor is absolute format
//
if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
return STATUS_INVALID_SECURITY_DESCR;
}
//
// Assign the SaclPresent flag value passed
//
if (SaclPresent) {
ISecurityDescriptor->Control |= SE_SACL_PRESENT;
//
// Assign the ACL address if passed, otherwise set to null.
//
ISecurityDescriptor->Sacl = NULL;
if (ARGUMENT_PRESENT(Sacl)) {
ISecurityDescriptor->Sacl = Sacl;
}
//
// Assign SaclDefaulted flag if passed, otherwise clear it.
//
ISecurityDescriptor->Control &= ~ SE_SACL_DEFAULTED;
if (ARGUMENT_PRESENT(SaclDefaulted)) {
ISecurityDescriptor->Control |= SE_SACL_DEFAULTED;
}
} else {
ISecurityDescriptor->Control &= ~SE_SACL_PRESENT;
}
return STATUS_SUCCESS;
}
NTSTATUS
RtlGetSaclSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
OUT PBOOLEAN SaclPresent,
OUT PACL *Sacl,
OUT PBOOLEAN SaclDefaulted
)
/*++
Routine Description:
This procedure retrieves the system ACL information of a security
descriptor.
Arguments:
SecurityDescriptor - Supplies the security descriptor.
SaclPresent - If TRUE, indicates that the security descriptor
does contain a system ACL. In this case, the remaining OUT
parameters will receive valid values. Otherwise, the
security descriptor does not contain a system ACL and the
remaining OUT parameters will not receive valid values.
Sacl - This value is returned only if the value returned for the
SaclPresent flag is TRUE. In this case, the Sacl parameter
receives the address of the security descriptor's system ACL.
If this value is returned as null, then the security
descriptor has a null system ACL.
SaclDefaulted - This value is returned only if the value returned
for the SaclPresent flag is TRUE. In this case, the
SaclDefaulted parameter receives the value of the security
descriptor's SaclDefaulted control flag.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision of the security
descriptor is not known to the routine. It may be a newer
revision than the routine knows about.
--*/
{
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
RTL_PAGED_CODE();
//
// Check the revision
//
if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
return STATUS_UNKNOWN_REVISION;
}
//
// Assign the SaclPresent flag value
//
*SaclPresent = RtlpAreControlBitsSet( ISecurityDescriptor, SE_SACL_PRESENT );
if (*SaclPresent) {
//
// Assign the ACL address.
//
*Sacl = RtlpSaclAddrSecurityDescriptor(ISecurityDescriptor);
//
// Assign SaclDefaulted flag.
//
*SaclDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_SACL_DEFAULTED );
}
return STATUS_SUCCESS;
}
NTSTATUS
RtlSetOwnerSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN PSID Owner OPTIONAL,
IN BOOLEAN OwnerDefaulted OPTIONAL
)
/*++
Routine Description:
This procedure sets the owner information of an absolute security
descriptor. If there is already an owner present in the security
descriptor, it is superseded.
Arguments:
SecurityDescriptor - Supplies the security descriptor in which
the owner is to be set. If the security descriptor already
includes an owner, it will be superseded by the new owner.
Owner - Supplies the owner SID for the security descriptor. If
this optional parameter is not passed, then the owner is
cleared (indicating the security descriptor has no owner).
The SID is referenced by, not copied into, the security
descriptor.
OwnerDefaulted - When set, indicates the owner was picked up from
some default mechanism (rather than explicitly specified by a
user). This value is set in the OwnerDefaulted control flag
in the security descriptor. If this optional parameter is
not passed, then the SaclDefaulted flag will be cleared.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision of the security
descriptor is not known to the routine. It may be a newer
revision than the routine knows about.
STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
is not an absolute format security descriptor.
--*/
{
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
RTL_PAGED_CODE();
//
// Check the revision
//
if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
return STATUS_UNKNOWN_REVISION;
}
//
// Make sure the descriptor is absolute format
//
if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
return STATUS_INVALID_SECURITY_DESCR;
}
//
// Assign the Owner field if passed, otherwise clear it.
//
ISecurityDescriptor->Owner = NULL;
if (ARGUMENT_PRESENT(Owner)) {
ISecurityDescriptor->Owner = Owner;
}
//
// Assign the OwnerDefaulted flag if passed, otherwise clear it.
//
ISecurityDescriptor->Control &= ~SE_OWNER_DEFAULTED;
if (OwnerDefaulted == TRUE) {
ISecurityDescriptor->Control |= SE_OWNER_DEFAULTED;
}
return STATUS_SUCCESS;
}
NTSTATUS
RtlGetOwnerSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
OUT PSID *Owner,
OUT PBOOLEAN OwnerDefaulted
)
/*++
Routine Description:
This procedure retrieves the owner information of a security
descriptor.
Arguments:
SecurityDescriptor - Supplies the security descriptor.
Owner - Receives a pointer to the owner SID. If the security
descriptor does not currently contain an owner, then this
value will be returned as null. In this case, the remaining
OUT parameters are not given valid return values. Otherwise,
this parameter points to an SID and the remaining OUT
parameters are provided valid return values.
OwnerDefaulted - This value is returned only if the value
returned for the Owner parameter is not null. In this case,
the OwnerDefaulted parameter receives the value of the
security descriptor's OwnerDefaulted control flag.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision of the security
descriptor is not known to the routine. It may be a newer
revision than the routine knows about.
--*/
{
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
RTL_PAGED_CODE();
//
// Check the revision
//
if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
return STATUS_UNKNOWN_REVISION;
}
//
// Return the Owner field value.
//
*Owner = RtlpOwnerAddrSecurityDescriptor(ISecurityDescriptor);
//
// Return the OwnerDefaulted flag value.
//
*OwnerDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_OWNER_DEFAULTED );
return STATUS_SUCCESS;
}
NTSTATUS
RtlSetGroupSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN PSID Group OPTIONAL,
IN BOOLEAN GroupDefaulted OPTIONAL
)
/*++
Routine Description:
This procedure sets the primary group information of an absolute security
descriptor. If there is already an primary group present in the
security descriptor, it is superseded.
Arguments:
SecurityDescriptor - Supplies the security descriptor in which
the primary group is to be set. If the security descriptor
already includes a primary group, it will be superseded by
the new group.
Group - Supplies the primary group SID for the security
descriptor. If this optional parameter is not passed, then
the primary group is cleared (indicating the security
descriptor has no primary group). The SID is referenced by,
not copied into, the security descriptor.
GroupDefaulted - When set, indicates the owner was picked up from
some default mechanism (rather than explicitly specified by a
user). This value is set in the OwnerDefaulted control flag
in the security descriptor. If this optional parameter is
not passed, then the SaclDefaulted flag will be cleared.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision of the security
descriptor is not known to the routine. It may be a newer
revision than the routine knows about.
STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
is not an absolute format security descriptor.
--*/
{
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
RTL_PAGED_CODE();
//
// Check the revision
//
if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
return STATUS_UNKNOWN_REVISION;
}
//
// Make sure the descriptor is absolute format
//
if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
return STATUS_INVALID_SECURITY_DESCR;
}
//
// Assign the Group field if passed, otherwise clear it.
//
ISecurityDescriptor->Group = NULL;
if (ARGUMENT_PRESENT(Group)) {
ISecurityDescriptor->Group = Group;
}
//
// Assign the GroupDefaulted flag if passed, otherwise clear it.
//
ISecurityDescriptor->Control &= ~SE_GROUP_DEFAULTED;
if (ARGUMENT_PRESENT(GroupDefaulted)) {
ISecurityDescriptor->Control |= SE_GROUP_DEFAULTED;
}
return STATUS_SUCCESS;
}
NTSTATUS
RtlGetGroupSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
OUT PSID *Group,
OUT PBOOLEAN GroupDefaulted
)
/*++
Routine Description:
This procedure retrieves the primary group information of a
security descriptor.
Arguments:
SecurityDescriptor - Supplies the security descriptor.
Group - Receives a pointer to the primary group SID. If the
security descriptor does not currently contain a primary
group, then this value will be returned as null. In this
case, the remaining OUT parameters are not given valid return
values. Otherwise, this parameter points to an SID and the
remaining OUT parameters are provided valid return values.
GroupDefaulted - This value is returned only if the value
returned for the Group parameter is not null. In this case,
the GroupDefaulted parameter receives the value of the
security descriptor's GroupDefaulted control flag.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision of the security
descriptor is not known to the routine. It may be a newer
revision than the routine knows about.
--*/
{
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor =
(SECURITY_DESCRIPTOR *)SecurityDescriptor;
RTL_PAGED_CODE();
//
// Check the revision
//
if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
return STATUS_UNKNOWN_REVISION;
}
//
// Return the Group field value.
//
*Group = RtlpGroupAddrSecurityDescriptor(ISecurityDescriptor);
//
// Return the GroupDefaulted flag value.
//
*GroupDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_GROUP_DEFAULTED );
return STATUS_SUCCESS;
}
BOOLEAN
RtlAreAllAccessesGranted(
IN ACCESS_MASK GrantedAccess,
IN ACCESS_MASK DesiredAccess
)
/*++
Routine Description:
This routine is used to check a desired access mask against a
granted access mask. It is used by the Object Management
component when dereferencing a handle.
Arguments:
GrantedAccess - Specifies the granted access mask.
DesiredAccess - Specifies the desired access mask.
Return Value:
BOOLEAN - TRUE if the GrantedAccess mask has all the bits set
that the DesiredAccess mask has set. That is, TRUE is
returned if all of the desired accesses have been granted.
--*/
{
RTL_PAGED_CODE();
return ((BOOLEAN)((~(GrantedAccess) & (DesiredAccess)) == 0));
}
BOOLEAN
RtlAreAnyAccessesGranted(
IN ACCESS_MASK GrantedAccess,
IN ACCESS_MASK DesiredAccess
)
/*++
Routine Description:
This routine is used to test whether any of a set of desired
accesses are granted by a granted access mask. It is used by
components other than the the Object Management component for
checking access mask subsets.
Arguments:
GrantedAccess - Specifies the granted access mask.
DesiredAccess - Specifies the desired access mask.
Return Value:
BOOLEAN - TRUE if the GrantedAccess mask contains any of the bits
specified in the DesiredAccess mask. That is, if any of the
desired accesses have been granted, TRUE is returned.
--*/
{
RTL_PAGED_CODE();
return ((BOOLEAN)(((GrantedAccess) & (DesiredAccess)) != 0));
}
VOID
RtlMapGenericMask(
IN OUT PACCESS_MASK AccessMask,
IN PGENERIC_MAPPING GenericMapping
)
/*++
Routine Description:
This routine maps all generic accesses in the provided access mask
to specific and standard accesses according to the provided
GenericMapping.
Arguments:
AccessMask - Points to the access mask to be mapped.
GenericMapping - The mapping of generic to specific and standard
access types.
Return Value:
None.
--*/
{
RTL_PAGED_CODE();
// //
// // Make sure the pointer is properly aligned
// //
//
// ASSERT( ((ULONG)AccessMask >> 2) << 2 == (ULONG)AccessMask );
if (*AccessMask & GENERIC_READ) {
*AccessMask |= GenericMapping->GenericRead;
}
if (*AccessMask & GENERIC_WRITE) {
*AccessMask |= GenericMapping->GenericWrite;
}
if (*AccessMask & GENERIC_EXECUTE) {
*AccessMask |= GenericMapping->GenericExecute;
}
if (*AccessMask & GENERIC_ALL) {
*AccessMask |= GenericMapping->GenericAll;
}
//
// Now clear the generic flags
//
*AccessMask &= ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);
return;
}
NTSTATUS
RtlImpersonateSelf(
IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
)
/*++
Routine Description:
This routine may be used to obtain an Impersonation token representing
your own process's context. This may be useful for enabling a privilege
for a single thread rather than for the entire process; or changing
the default DACL for a single thread.
The token is assigned to the callers thread.
Arguments:
ImpersonationLevel - The level to make the impersonation token.
Return Value:
STATUS_SUCCESS - The thread is now impersonating the calling process.
Other - Status values returned by:
NtOpenProcessToken()
NtDuplicateToken()
NtSetInformationThread()
--*/
{
NTSTATUS
Status,
IgnoreStatus;
HANDLE
Token1,
Token2;
OBJECT_ATTRIBUTES
ObjectAttributes;
SECURITY_QUALITY_OF_SERVICE
Qos;
RTL_PAGED_CODE();
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL);
Qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
Qos.ImpersonationLevel = ImpersonationLevel;
Qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
Qos.EffectiveOnly = FALSE;
ObjectAttributes.SecurityQualityOfService = &Qos;
Status = NtOpenProcessToken( NtCurrentProcess(), TOKEN_DUPLICATE, &Token1 );
if (NT_SUCCESS(Status)) {
Status = NtDuplicateToken(
Token1,
TOKEN_IMPERSONATE,
&ObjectAttributes,
FALSE, //EffectiveOnly
TokenImpersonation,
&Token2
);
if (NT_SUCCESS(Status)) {
Status = NtSetInformationThread(
NtCurrentThread(),
ThreadImpersonationToken,
&Token2,
sizeof(HANDLE)
);
IgnoreStatus = NtClose( Token2 );
}
IgnoreStatus = NtClose( Token1 );
}
return(Status);
}
#ifndef WIN16
#ifndef NTOS_KERNEL_RUNTIME
BOOLEAN
RtlpValidOwnerSubjectContext(
IN HANDLE Token,
IN PSID Owner,
IN BOOLEAN ServerObject,
OUT PNTSTATUS ReturnStatus
)
/*++
Routine Description:
This routine checks to see whether the provided SID is one the subject
is authorized to assign as the owner of objects.
Arguments:
Token - Points to the subject's effective token
Owner - Points to the SID to be checked.
ServerObject - Boolean indicating whether or not this is a server
object, meaning it is protected by a primary-client combination.
ReturnStatus - Status to be passed back to the caller on failure.
Return Value:
FALSE on failure.
--*/
{
NTSTATUS Status;
ULONG Index;
BOOLEAN Found;
ULONG ReturnLength;
PTOKEN_GROUPS GroupIds = NULL;
PTOKEN_USER UserId = NULL;
PVOID HeapHandle;
HANDLE TokenToUse;
BOOLEAN HasPrivilege;
PRIVILEGE_SET PrivilegeSet;
RTL_PAGED_CODE();
//
// Get the handle to the current process heap
//
if ( Owner == NULL ) {
*ReturnStatus = STATUS_INVALID_OWNER;
return(FALSE);
}
//
// If it's not a server object, check the owner against the contents of the
// client token. If it is a server object, the owner must be valid in the
// primary token.
//
if (!ServerObject) {
TokenToUse = Token;
} else {
*ReturnStatus = NtOpenProcessToken(
NtCurrentProcess(),
TOKEN_QUERY,
&TokenToUse
);
if (!NT_SUCCESS( *ReturnStatus )) {
return( FALSE );
}
}
HeapHandle = RtlProcessHeap();
//
// Get the User from the Token
//
*ReturnStatus = NtQueryInformationToken(
TokenToUse,
TokenUser,
UserId,
0,
&ReturnLength
);
if (!NT_SUCCESS( *ReturnStatus ) && (STATUS_BUFFER_TOO_SMALL != *ReturnStatus)) {
if (ServerObject) {
NtClose( TokenToUse );
}
return( FALSE );
}
UserId = RtlAllocateHeap( HeapHandle, 0, ReturnLength );
if (UserId == NULL) {
*ReturnStatus = STATUS_NO_MEMORY;
if (ServerObject) {
NtClose( TokenToUse );
}
return( FALSE );
}
*ReturnStatus = NtQueryInformationToken(
TokenToUse,
TokenUser,
UserId,
ReturnLength,
&ReturnLength
);
if (!NT_SUCCESS( *ReturnStatus )) {
RtlFreeHeap( HeapHandle, 0, (PVOID)UserId );
if (ServerObject) {
NtClose( TokenToUse );
}
return( FALSE );
}
if ( RtlEqualSid( Owner, UserId->User.Sid ) ) {
RtlFreeHeap( HeapHandle, 0, (PVOID)UserId );
if (ServerObject) {
NtClose( TokenToUse );
}
return( TRUE );
}
RtlFreeHeap( HeapHandle, 0, (PVOID)UserId );
//
// Get the groups from the Token
//
*ReturnStatus = NtQueryInformationToken(
TokenToUse,
TokenGroups,
GroupIds,
0,
&ReturnLength
);
if (!NT_SUCCESS( *ReturnStatus ) && (STATUS_BUFFER_TOO_SMALL != *ReturnStatus)) {
if (ServerObject) {
NtClose( TokenToUse );
}
return( FALSE );
}
GroupIds = RtlAllocateHeap( HeapHandle, 0, ReturnLength );
if (GroupIds == NULL) {
*ReturnStatus = STATUS_NO_MEMORY;
if (ServerObject) {
NtClose( TokenToUse );
}
return( FALSE );
}
*ReturnStatus = NtQueryInformationToken(
TokenToUse,
TokenGroups,
GroupIds,
ReturnLength,
&ReturnLength
);
if (ServerObject) {
NtClose( TokenToUse );
}
if (!NT_SUCCESS( *ReturnStatus )) {
RtlFreeHeap( HeapHandle, 0, GroupIds );
return( FALSE );
}
//
// Walk through the list of group IDs looking for a match to
// the specified SID. If one is found, make sure it may be
// assigned as an owner.
//
// This code is similar to that performed to set the default
// owner of a token (NtSetInformationToken).
//
Index = 0;
while (Index < GroupIds->GroupCount) {
Found = RtlEqualSid(
Owner,
GroupIds->Groups[Index].Sid
);
if ( Found ) {
if ( RtlpIdAssignableAsOwner(GroupIds->Groups[Index])) {
RtlFreeHeap( HeapHandle, 0, GroupIds );
return TRUE;
} else {
break;
} //endif assignable
} //endif Found
Index++;
} //endwhile
RtlFreeHeap( HeapHandle, 0, GroupIds );
//
// If we are going to fail this call, check for Restore privilege,
// and succeed if he has it.
//
//
// Check for appropriate Privileges
//
// Audit/Alarm messages need to be generated due to the attempt
// to perform a privileged operation.
//
PrivilegeSet.PrivilegeCount = 1;
PrivilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY;
PrivilegeSet.Privilege[0].Luid = RtlConvertLongToLuid(SE_RESTORE_PRIVILEGE);
PrivilegeSet.Privilege[0].Attributes = 0;
Status = NtPrivilegeCheck(
Token,
&PrivilegeSet,
&HasPrivilege
);
if (!NT_SUCCESS( Status )) {
HasPrivilege = FALSE;
}
if ( HasPrivilege ) {
return TRUE;
} else {
*ReturnStatus = STATUS_INVALID_OWNER;
return FALSE;
}
}
#endif // NTOS_KERNEL_RUNTIME
#endif // WIN16
VOID
RtlpApplyAclToObject (
IN PACL Acl,
IN PGENERIC_MAPPING GenericMapping
)
/*++
Routine Description:
This is a private routine that maps Access Masks of an ACL so that
they are applicable to the object type the ACL is being applied to.
Only known DSA ACEs are mapped. Unknown ACE types are ignored.
Only access types in the GenericAll mapping for the target object
type will be non-zero upon return.
Arguments:
Acl - Supplies the acl being applied.
GenericMapping - Specifies the generic mapping to use.
Return Value:
None.
--*/
{
ULONG i;
PACE_HEADER Ace;
RTL_PAGED_CODE();
//
// First check if the acl is null
//
if (Acl == NULL) {
return;
}
//
// Now walk the ACL, mapping each ACE as we go.
//
for (i = 0, Ace = FirstAce(Acl);
i < Acl->AceCount;
i += 1, Ace = NextAce(Ace)) {
if (IsMSAceType( Ace )) {
RtlApplyAceToObject( Ace, GenericMapping );
}
}
return;
}
BOOLEAN
RtlpCopyEffectiveAce (
IN PACE_HEADER OldAce,
IN BOOLEAN AutoInherit,
IN BOOLEAN WillGenerateInheritAce,
IN PSID ClientOwnerSid,
IN PSID ClientGroupSid,
IN PSID ServerOwnerSid OPTIONAL,
IN PSID ServerGroupSid OPTIONAL,
IN PGENERIC_MAPPING GenericMapping,
IN GUID **pNewObjectType OPTIONAL,
IN ULONG GuidCount,
IN OUT PVOID *AcePosition,
OUT PULONG NewAceLength,
OUT PACL NewAcl,
OUT PBOOLEAN ObjectAceInherited OPTIONAL,
OUT PBOOLEAN EffectiveAceMapped,
OUT PBOOLEAN AclOverflowed
)
/*++
Routine Description:
This routine copy a specified ACE into an ACL as an effective ACE.
The resultant ACE has all the inheritance bits turned of.
The resultant ACE has the SID mapped from a generic SID to a specific SID
(e.g., From "creator owner" to the passed in owner sid).
Arguments:
OldAce - Supplies the ace being inherited
AutoInherit - Specifies if the inheritance is an "automatic inheritance".
As such, the inherited ACEs will be marked as such.
WillGenerateInheritAce - Specifies if the caller intends to generate an
inheritable ACE the corresponds to OldAce. If TRUE, this routine will
try to not map the effective ACE (increasing the likelyhood that
EffectiveAceMapped will return FALSE),
ClientOwnerSid - Specifies the owner Sid to use
ClientGroupSid - Specifies the new Group Sid to use
ServerSid - Optionally specifies the Server Sid to use in compound ACEs.
ClientSid - Optionally specifies the Client Sid to use in compound ACEs.
GenericMapping - Specifies the generic mapping to use
pNewObjectType - List of types of object being inherited to. If not
specified, the object has no object type.
GuidCount - Number of object types in the list.
AcePosition - On entry and exit, specifies location of the next available ACE
position in NewAcl.
A NULL ACE position means there is no room at all in NewAcl.
NewAceLength - Returns the length (in bytes) needed in NewAcl to
copy the specified ACE. This might be zero to indicate that the ACE
need not be copied at all.
NewAcl - Provides a pointer to the ACL into which the ACE is to be
inherited.
ObjectAceInherited - Returns true if one or more object ACEs were inherited
based on NewObjectType
If NULL, NewObjectType is ignored and the object ACE is always inherited
EffectiveAceMapped - Return TRUE if the SID, guid, or access mask of Old Ace
was modifed when copying the ACE.
AclOverflowed - Returns TRUE if NewAcl wasn't long enough to contain NewAceLength.
Return Value:
TRUE - No problem was detected.
FALSE - Indicates something went wrong preventing
the ACE from being compied. This generally represents a bugcheck
situation when returned from this call.
--*/
{
ULONG LengthRequired;
ACCESS_MASK LocalMask;
BOOLEAN GuidOptimizationPossible = FALSE;
PSID LocalServerOwner;
PSID LocalServerGroup;
NTSTATUS Status;
ULONG CreatorSid[CREATOR_SID_SIZE];
SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
RTL_PAGED_CODE();
//
// Allocate and initialize the universal SIDs we're going to need
// to look for inheritable ACEs.
//
ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);
Status = RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
if ( !NT_SUCCESS(Status) ) {
return FALSE;
}
*(RtlpSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_OWNER_RID;
LocalServerOwner = ARGUMENT_PRESENT(ServerOwnerSid) ? ServerOwnerSid : ClientOwnerSid;
LocalServerGroup = ARGUMENT_PRESENT(ServerGroupSid) ? ServerGroupSid : ClientGroupSid;
//
// Initialization
//
*EffectiveAceMapped = FALSE;
if ( ARGUMENT_PRESENT(ObjectAceInherited)) {
*ObjectAceInherited = FALSE;
}
*AclOverflowed = FALSE;
LengthRequired = (ULONG)OldAce->AceSize;
//
// Process all MS ACE types specially
//
if ( IsMSAceType(OldAce) ) {
ULONG Rid;
PSID SidToCopy = NULL;
ULONG AceHeaderToCopyLength;
PACE_HEADER AceHeaderToCopy = OldAce;
PSID ServerSidToCopy = NULL;
UCHAR DummyAce[sizeof(KNOWN_OBJECT_ACE)+sizeof(GUID)];
//
// Grab the Sid pointer and access mask as a function of the ACE type
//
if (IsKnownAceType( OldAce ) ) {
SidToCopy = &((PKNOWN_ACE)OldAce)->SidStart;
AceHeaderToCopyLength = FIELD_OFFSET(KNOWN_ACE, SidStart);
} else if (IsCompoundAceType(OldAce)) {
SidToCopy = RtlCompoundAceClientSid( OldAce );
AceHeaderToCopyLength = FIELD_OFFSET(KNOWN_COMPOUND_ACE, SidStart);
ASSERT( FIELD_OFFSET(KNOWN_COMPOUND_ACE, Mask) ==
FIELD_OFFSET(KNOWN_ACE, Mask) );
//
// Compound ACEs have two SIDs (Map one now).
//
ServerSidToCopy = RtlCompoundAceServerSid( OldAce );
if (RtlEqualPrefixSid ( ServerSidToCopy, CreatorSid )) {
Rid = *RtlpSubAuthoritySid( ServerSidToCopy, 0 );
switch (Rid) {
case SECURITY_CREATOR_OWNER_RID:
ServerSidToCopy = ClientOwnerSid;
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientOwnerSid);
*EffectiveAceMapped = TRUE;
break;
case SECURITY_CREATOR_GROUP_RID:
if ( ClientGroupSid != NULL ) {
ServerSidToCopy = ClientGroupSid;
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientGroupSid);
*EffectiveAceMapped = TRUE;
}
break;
case SECURITY_CREATOR_OWNER_SERVER_RID:
ServerSidToCopy = LocalServerOwner;
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(LocalServerOwner);
*EffectiveAceMapped = TRUE;
break;
case SECURITY_CREATOR_GROUP_SERVER_RID:
ServerSidToCopy = LocalServerGroup;
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(LocalServerGroup);
*EffectiveAceMapped = TRUE;
break;
}
//
// If we don't know what this SID is, just copy the original.
//
if ( !*EffectiveAceMapped ) {
AceHeaderToCopyLength += SeLengthSid( ServerSidToCopy );
ServerSidToCopy = NULL;
}
} else {
//
// We don't know what this SID is, just copy the original.
//
AceHeaderToCopyLength += SeLengthSid( ServerSidToCopy );
ServerSidToCopy = NULL;
}
//
// Handle Object ACEs
//
} else {
GUID *InheritedObjectType;
SidToCopy = RtlObjectAceSid( OldAce );
AceHeaderToCopyLength = (ULONG) ((PUCHAR)SidToCopy - (PUCHAR)OldAce);
ASSERT( FIELD_OFFSET(KNOWN_OBJECT_ACE, Mask) ==
FIELD_OFFSET(KNOWN_ACE, Mask) );
//
// Handle ACEs that are only inherited for a specific object type,
//
InheritedObjectType = RtlObjectAceInheritedObjectType( OldAce );
if ( ARGUMENT_PRESENT(ObjectAceInherited) && InheritedObjectType != NULL ) {
//
// If the object type doesn't match the inherited object type,
// don't inherit the ACE.
//
if ( pNewObjectType == NULL ||
!RtlpGuidPresentInGuidList( InheritedObjectType,
pNewObjectType,
GuidCount ) ) {
LengthRequired = 0;
//
// If the object type matches the inherited object type,
// Inherit an ACE with no inherited object type.
//
} else {
//
// Tell the caller we inherited an object type specific ACE.
//
*ObjectAceInherited = TRUE;
//
// If the caller is not going to generate an inheritable ACE,
// deleting the inherited object type GUID for the effective ACE.
//
// Otherwise, leave it so the caller can merge the two ACEs.
//
if ( !WillGenerateInheritAce ) {
*EffectiveAceMapped = TRUE;
//
// If an object type GUID is present,
// simply delete the inherited object type GUID.
//
if ( RtlObjectAceObjectTypePresent( OldAce )) {
LengthRequired -= sizeof(GUID);
AceHeaderToCopyLength -= sizeof(GUID);
RtlCopyMemory( DummyAce, OldAce, AceHeaderToCopyLength );
AceHeaderToCopy = (PACE_HEADER)DummyAce;
((PKNOWN_OBJECT_ACE)AceHeaderToCopy)->Flags &= ~ACE_INHERITED_OBJECT_TYPE_PRESENT;
//
// If an object type GUID is not present,
// convert the ACE to non-object type specific.
//
} else {
AceHeaderToCopyLength = AceHeaderToCopyLength -
sizeof(GUID) +
sizeof(KNOWN_ACE) -
sizeof(KNOWN_OBJECT_ACE);
LengthRequired = LengthRequired -
sizeof(GUID) +
sizeof(KNOWN_ACE) -
sizeof(KNOWN_OBJECT_ACE);
RtlCopyMemory( DummyAce, OldAce, AceHeaderToCopyLength );
AceHeaderToCopy = (PACE_HEADER)DummyAce;
AceHeaderToCopy->AceType = RtlBaseAceType[ OldAce->AceType ];
}
} else {
GuidOptimizationPossible = TRUE;
}
}
}
}
//
// Only proceed if we've not already determined to drop the ACE.
//
if ( LengthRequired != 0 ) {
//
// If after mapping the access mask, the access mask
// is empty, then drop the ACE.
//
// This is incompatible with NT 4.0 which simply mapped and left
// undefined access bits set.
LocalMask = ((PKNOWN_ACE)(OldAce))->Mask;
RtlApplyGenericMask( OldAce, &LocalMask, GenericMapping);
if ( LocalMask != ((PKNOWN_ACE)(OldAce))->Mask ) {
*EffectiveAceMapped = TRUE;
}
//
// Mask off any bits that aren't meaningful
//
LocalMask &= ( STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL | ACCESS_SYSTEM_SECURITY );
if (LocalMask == 0) {
LengthRequired = 0;
} else {
//
// See if the SID in the ACE is one of the various CREATOR_* SIDs by
// comparing identifier authorities.
//
if (RtlEqualPrefixSid ( SidToCopy, CreatorSid )) {
Rid = *RtlpSubAuthoritySid( SidToCopy, 0 );
switch (Rid) {
case SECURITY_CREATOR_OWNER_RID:
SidToCopy = ClientOwnerSid;
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientOwnerSid);
*EffectiveAceMapped = TRUE;
break;
case SECURITY_CREATOR_GROUP_RID:
if ( ClientGroupSid != NULL ) {
SidToCopy = ClientGroupSid;
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientGroupSid);
*EffectiveAceMapped = TRUE;
}
break;
case SECURITY_CREATOR_OWNER_SERVER_RID:
SidToCopy = LocalServerOwner;
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(LocalServerOwner);
*EffectiveAceMapped = TRUE;
break;
case SECURITY_CREATOR_GROUP_SERVER_RID:
SidToCopy = LocalServerGroup;
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(LocalServerGroup);
*EffectiveAceMapped = TRUE;
break;
default :
//
// We don't know what this SID is, just copy the original.
//
break;
}
}
//
// In cases where effective ace has been mapped because of
// a. CreatorOwner/Group OR
// b. Generic flags
// AND
// this is an object type ace which will generate an IO ace
// we can save space for a guid.
//
if (GuidOptimizationPossible && *EffectiveAceMapped) {
//
// If an object type GUID is present,
// simply delete the inherited object type GUID.
//
if ( RtlObjectAceObjectTypePresent( OldAce )) {
LengthRequired -= sizeof(GUID);
AceHeaderToCopyLength -= sizeof(GUID);
RtlCopyMemory( DummyAce, OldAce, AceHeaderToCopyLength );
AceHeaderToCopy = (PACE_HEADER)DummyAce;
((PKNOWN_OBJECT_ACE)AceHeaderToCopy)->Flags &= ~ACE_INHERITED_OBJECT_TYPE_PRESENT;
//
// If an object type GUID is not present,
// convert the ACE to non-object type specific.
//
} else {
AceHeaderToCopyLength = AceHeaderToCopyLength -
sizeof(GUID) +
sizeof(KNOWN_ACE) -
sizeof(KNOWN_OBJECT_ACE);
LengthRequired = LengthRequired -
sizeof(GUID) +
sizeof(KNOWN_ACE) -
sizeof(KNOWN_OBJECT_ACE);
RtlCopyMemory( DummyAce, OldAce, AceHeaderToCopyLength );
AceHeaderToCopy = (PACE_HEADER)DummyAce;
AceHeaderToCopy->AceType = RtlBaseAceType[ OldAce->AceType ];
}
}
//
// If the ACE doesn't fit,
// just note the fact and don't copy the ACE.
//
if ( *AcePosition == NULL ||
LengthRequired > (ULONG)NewAcl->AclSize - ((PUCHAR)(*AcePosition) - (PUCHAR)NewAcl) ) {
*AclOverflowed = TRUE;
} else {
PUCHAR Target;
//
// Copy individual parts of the ACE separately.
//
Target = (PUCHAR)*AcePosition;
RtlCopyMemory(
Target,
AceHeaderToCopy,
AceHeaderToCopyLength );
Target += AceHeaderToCopyLength;
//
// Now copy the correct server SID
//
if ( ServerSidToCopy != NULL ) {
RtlCopyMemory(
Target,
ServerSidToCopy,
SeLengthSid(ServerSidToCopy)
);
Target += SeLengthSid(ServerSidToCopy);
}
//
// Now copy the correct SID
//
RtlCopyMemory(
Target,
SidToCopy,
SeLengthSid(SidToCopy)
);
Target += SeLengthSid(SidToCopy);
//
// Set the size of the ACE accordingly
//
if ( LengthRequired < (ULONG)(Target - (PUCHAR)*AcePosition) ) {
return FALSE;
}
LengthRequired = (ULONG)(Target - (PUCHAR)*AcePosition);
((PKNOWN_ACE)*AcePosition)->Header.AceSize =
(USHORT)LengthRequired;
//
// Put the mapped access mask in the new ACE
//
((PKNOWN_ACE)*AcePosition)->Mask = LocalMask;
}
}
}
} else {
//
// If the ACE doesn't fit,
// just note the fact and don't copy the ACE.
//
if ( LengthRequired > (ULONG)NewAcl->AclSize - ((PUCHAR)*AcePosition - (PUCHAR)NewAcl) ) {
*AclOverflowed = TRUE;
} else {
//
// Not a known ACE type, copy ACE as is
//
RtlCopyMemory(
*AcePosition,
OldAce,
LengthRequired );
}
}
//
// If the ACE was actually kept, clear all the inherit flags
// and update the ACE count of the ACL.
//
if ( !*AclOverflowed && LengthRequired != 0 ) {
((PACE_HEADER)*AcePosition)->AceFlags &= ~VALID_INHERIT_FLAGS;
if ( AutoInherit ) {
((PACE_HEADER)*AcePosition)->AceFlags |= INHERITED_ACE;
}
NewAcl->AceCount += 1;
}
//
// We have the length of the new ACE, but we've calculated
// it with a ULONG. It must fit into a USHORT. See if it
// does.
//
if (LengthRequired > 0xFFFF) {
return FALSE;
}
//
// Move the Ace Position to where the next ACE goes.
//
if ( !*AclOverflowed ) {
*AcePosition = ((PUCHAR)*AcePosition) + LengthRequired;
}
//
// Now return to our caller
//
(*NewAceLength) = LengthRequired;
return TRUE;
}
#ifndef WIN16
NTSTATUS
RtlpCopyAces(
IN PACL Acl,
IN PGENERIC_MAPPING GenericMapping,
IN ACE_TYPE_TO_COPY AceTypeToCopy,
IN UCHAR AceFlagsToReset,
IN BOOLEAN MapSids,
IN PSID ClientOwnerSid,
IN PSID ClientGroupSid,
IN PSID ServerOwnerSid OPTIONAL,
IN PSID ServerGroupSid OPTIONAL,
IN BOOLEAN IsDirectoryObject,
IN BOOLEAN RetainInheritedAceBit,
OUT PULONG NewAclSizeParam,
OUT PACL NewAcl
)
/*++
Routine Description:
Copy ACEs from of an ACL and perform generic mapping. Only ACEs specified
by 'AceFilter' are copied.
Arguments:
Acl - Supplies the ACL to copy from.
GenericMapping - Specifies the generic mapping to use.
AceTypeToCopy - Describes which aces to copy.
AceFlagsToReset - Bit mask of ACE flags to reset (if set) on each ACE.
MapSids - TRUE if the SID in the ACE is to be mapped to the corresponding
actual SID.
ClientOwnerSid - Specifies the owner Sid to use
ClientGroupSid - Specifies the new Group Sid to use
ServerOwnerSid - Optionally specifies the Server Sid to use in compound ACEs.
ServerGroupSid - Optionally specifies the Server group Sid to use in compound ACEs.
IsDirectoryObject - Whether the object is a container or a non-container
RetainInheritedAceBit - Whether to retain INHERITED_ACE bit for effective aces.
NewAclSizeParam - Receives the cumulatiave length of the copies ACEs
NewAcl - Provides a pointer to the ACL to copy to.
This ACL must already be initialized.
Return Value:
STATUS_SUCCESS - An inheritable ACL has been generated.
STATUS_BUFFER_TOO_SMALL - The ACL specified by NewAcl is too small for the
copied ACEs. The required size is returned in NewAceLength.
--*/
{
NTSTATUS Status;
ULONG i;
PACE_HEADER OldAce;
ULONG NewAclSize, NewAceSize;
BOOLEAN AclOverflowed = FALSE;
BOOLEAN CopyAce;
PVOID AcePosition;
BOOLEAN LocalAutoInherit = FALSE;
RTL_PAGED_CODE();
//
// Validate the ACL.
//
if ( !ValidAclRevision(NewAcl) ) {
return STATUS_UNKNOWN_REVISION;
}
//
// Find where the first ACE goes.
//
if (!RtlFirstFreeAce( NewAcl, &AcePosition )) {
return STATUS_BAD_INHERITANCE_ACL;
}
//
// Walk through the original ACL copying ACEs.
//
NewAclSize = 0;
for (i = 0, OldAce = FirstAce(Acl);
i < Acl->AceCount;
i += 1, OldAce = NextAce(OldAce)) {
//
// If the ACE wasn't inherited,
// copy it.
//
switch (AceTypeToCopy) {
case CopyInheritedAces:
CopyAce = AceInherited(OldAce);
break;
case CopyNonInheritedAces:
CopyAce = !AceInherited(OldAce);
break;
case CopyAllAces:
CopyAce = TRUE;
break;
default:
CopyAce = FALSE;
break;
}
if ( CopyAce ) {
//
// If SIDs are to be mapped,
// do so (and potentially create up to two ACEs).
//
if ( MapSids ) {
PVOID TempAcePosition;
ULONG EffectiveAceSize = 0;
BOOLEAN EffectiveAceMapped;
BOOLEAN GenerateInheritAce;
//
// Remember where the next ACE will be copied.
//
TempAcePosition = AcePosition;
NewAceSize = 0;
GenerateInheritAce = IsDirectoryObject &&
((((PACE_HEADER)OldAce)->AceFlags & (OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE)) != 0);
//
// If the orginal ACE is an effective ACE,
// create an effective ACE.
//
if ( !(((PACE_HEADER)OldAce)->AceFlags & INHERIT_ONLY_ACE)) {
BOOLEAN LocalAclOverflowed;
//
// If the ace has INHERITED_ACE bit and the caller has requested
// preservation of the bit, copy the effective ace as an
// INHERITED_ACE.
//
LocalAutoInherit = FALSE;
if ( RetainInheritedAceBit ) {
if ( OldAce->AceFlags & INHERITED_ACE) {
LocalAutoInherit = TRUE;
}
}
//
// Copy the effective ACE into the ACL.
//
if ( !RtlpCopyEffectiveAce (
OldAce,
LocalAutoInherit,
GenerateInheritAce,
ClientOwnerSid,
ClientGroupSid,
ServerOwnerSid,
ServerGroupSid,
GenericMapping,
NULL, // Always copy object ACES
0,
&TempAcePosition,
&EffectiveAceSize,
NewAcl,
NULL, // Always copy object ACES
&EffectiveAceMapped,
&LocalAclOverflowed ) ) {
return STATUS_BAD_INHERITANCE_ACL;
}
if (LocalAclOverflowed) {
AclOverflowed = TRUE;
}
NewAceSize += EffectiveAceSize;
//
// Reset any undesirable AceFlags.
//
if ( !AclOverflowed ) {
((PACE_HEADER)AcePosition)->AceFlags &= ~AceFlagsToReset;
}
}
//
// If the original ACE is inheritable,
// create an inheritable ACE.
//
// ASSERT: AcePosition points to where the effective ACE was copied
// ASSERT: TempAcePosition points to where the inheritable ACE should be copied
//
if ( GenerateInheritAce ) {
//
// If a effective ACE was created,
// and it wasn't mapped,
// avoid generating another ACE and simply merge the inheritance bits into
// the effective ACE.
//
if ( EffectiveAceSize != 0 && !EffectiveAceMapped ) {
//
// Copy the inherit bits from the original ACE.
//
if ( !AclOverflowed ) {
((PACE_HEADER)AcePosition)->AceFlags |=
((PACE_HEADER)OldAce)->AceFlags & (VALID_INHERIT_FLAGS);
((PACE_HEADER)AcePosition)->AceFlags &= ~AceFlagsToReset;
}
//
// Otherwise, generate an explicit inheritance ACE.
//
// But only if the access mask isn't zero.
//
} else if ( !IsMSAceType(OldAce) || ((PKNOWN_ACE)(OldAce))->Mask != 0 ) {
//
// Account for the new ACE being added to the ACL.
//
NewAceSize += (ULONG)(((PACE_HEADER)OldAce)->AceSize);
if (NewAceSize > 0xFFFF) {
return STATUS_BAD_INHERITANCE_ACL;
}
//
// If the ACE doesn't fit,
// just note the fact and don't copy the ACE.
//
if ( ((PACE_HEADER)OldAce)->AceSize > NewAcl->AclSize - ((PUCHAR)TempAcePosition - (PUCHAR)NewAcl) ) {
AclOverflowed = TRUE;
} else {
//
// copy it as is, but make sure the InheritOnly bit is set.
//
if ( !AclOverflowed ) {
RtlCopyMemory(
TempAcePosition,
OldAce,
((PACE_HEADER)OldAce)->AceSize
);
((PACE_HEADER)TempAcePosition)->AceFlags |= INHERIT_ONLY_ACE;
((PACE_HEADER)TempAcePosition)->AceFlags &= ~AceFlagsToReset;
NewAcl->AceCount += 1;
}
}
}
}
} else {
NewAceSize = (ULONG)OldAce->AceSize;
//
// If the ACE doesn't fit,
// just note the fact and don't copy the ACE.
//
if ( AcePosition == NULL ||
NewAceSize > (ULONG)NewAcl->AclSize - ((PUCHAR)AcePosition - (PUCHAR)NewAcl) ) {
AclOverflowed = TRUE;
} else if ( !AclOverflowed ) {
//
// Copy the ACE.
//
RtlCopyMemory(
AcePosition,
OldAce,
NewAceSize );
//
// Map the generic bits.
//
// Is it really right to map the generic bits on an ACE
// that's both effective and inheritable. Shouldn't this
// be split into two ACEs in that case? Or just skip the mapping?
//
if (IsMSAceType( AcePosition )) {
RtlApplyAceToObject( (PACE_HEADER)AcePosition, GenericMapping );
}
//
// Reset any undesirable AceFlags.
//
((PACE_HEADER)AcePosition)->AceFlags &= ~AceFlagsToReset;
//
// Account for the new ACE.
//
NewAcl->AceCount += 1;
}
}
//
// Move the Ace Position to where the next ACE goes.
//
if ( !AclOverflowed ) {
AcePosition = ((PUCHAR)AcePosition) + NewAceSize;
} else {
// On overflow, ensure no other ACEs are actually output to the buffer
AcePosition = ((PUCHAR)NewAcl) + NewAcl->AclSize;
}
NewAclSize += NewAceSize;
}
}
//
// We have the length of the new ACE, but we've calculated
// it with a ULONG. It must fit into a USHORT. See if it
// does.
//
if (NewAclSize > 0xFFFF) {
return STATUS_BAD_INHERITANCE_ACL;
}
(*NewAclSizeParam) = NewAclSize;
return AclOverflowed ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
}
NTSTATUS
RtlpInheritAcl2 (
IN PACL DirectoryAcl,
IN PACL ChildAcl,
IN ULONG ChildGenericControl,
IN BOOLEAN IsDirectoryObject,
IN BOOLEAN AutoInherit,
IN BOOLEAN DefaultDescriptorForObject,
IN PSID OwnerSid,
IN PSID GroupSid,
IN PSID ServerOwnerSid OPTIONAL,
IN PSID ServerGroupSid OPTIONAL,
IN PGENERIC_MAPPING GenericMapping,
IN BOOLEAN IsSacl,
IN GUID **pNewObjectType OPTIONAL,
IN ULONG GuidCount,
IN PULONG AclBufferSize,
IN OUT PUCHAR AclBuffer,
OUT PBOOLEAN NewAclExplicitlyAssigned,
OUT PULONG NewGenericControl
)
/*++
Routine Description:
This is a private routine that produces an inherited acl from
a parent acl according to the rules of inheritance
Arguments:
DirectoryAcl - Supplies the acl being inherited.
ChildAcl - Supplies the acl associated with the object. This
is either the current acl on the object or the acl being assigned
to the object.
ChildGenericControl - Specifies the control flags from the SecurityDescriptor
describing the ChildAcl:
SEP_ACL_PRESENT: Specifies that the child ACL is explictly supplied by
the caller.
SEP_ACL_DEFAULTED: Specifies that the child ACL was supplied by some
defaulting mechanism.
SEP_ACL_PROTECTED: Specifies that the child ACL is protected and
should not inherit any ACE from the DirectoryACL
IsDirectoryObject - Specifies if the new acl is for a directory.
AutoInherit - Specifies if the inheritance is an "automatic inheritance".
As such, the non-inherited ACEs from the ChildAcl will be preserved and
the inherited ACEs from the DirectoryAcl will be marked as such.
DefaultDescriptorForObject - If set, the CreatorDescriptor
is the default descriptor for ObjectType. As such, the
CreatorDescriptor will be ignored if any ObjectType specific
ACEs are inherited from the parent. If not such ACEs are inherited,
the CreatorDescriptor is handled as though this flag were not
specified.
OwnerSid - Specifies the owner Sid to use.
GroupSid - Specifies the group SID to use.
GenericMapping - Specifies the generic mapping to use.
IsSacl - True if this is the SACL. False if this is the DACL.
pNewObjectType - List of types of object being inherited to. If not
specified, the object has no object type.
GuidCount - Number of object types in the list.
AclBufferSize - On input, specifies the size of AclBuffer.
On output, on success, returns the used size of AclBuffer.
On output, if the buffer is too small, returns the required size of AclBuffer.
AclBuffer - Receives a pointer to the new (inherited) acl.
NewAclExplicitlyAssigned - Returns true to indicate that some portion of
"NewAcl" was derived from an the explicit ChildAcl
NewGenericControl - Specifies the control flags for the newly
generated ACL.
SEP_ACL_AUTO_INHERITED: Set if the ACL was generated using the
Automatic Inheritance algorithm.
SEP_ACL_PROTECTED: Specifies that the ACL is protected and
was not inherited from the parent ACL.
Return Value:
STATUS_SUCCESS - An inheritable ACL was successfully generated.
STATUS_NO_INHERITANCE - An inheritable ACL was not successfully generated.
This is a warning completion status. The caller should use the default
ACL.
STATUS_BAD_INHERITANCE_ACL - Indicates the acl built was not a valid ACL.
This can becaused by a number of things. One of the more probable
causes is the replacement of a CreatorId with an SID that didn't fit
into the ACE or ACL.
STATUS_UNKNOWN_REVISION - Indicates the source ACL is a revision that
is unknown to this routine.
STATUS_BUFFER_TOO_SMALL - The ACL specified by NewAcl is too small for the
inheritance ACEs. The required size is returned in AclBufferSize.
--*/
{
NTSTATUS Status;
ULONG ChildNewAclSize = 0;
ULONG UsedChildNewAclSize = 0;
ULONG DirectoryNewAclSize = 0;
ULONG AclRevision;
USHORT ChildAceCount;
PVOID ChildAcePosition;
PVOID DirectoryAcePosition;
BOOLEAN AclOverflowed = FALSE;
BOOLEAN AclProtected = FALSE;
BOOLEAN NullAclOk = TRUE;
BOOLEAN ObjectAceInherited;
RTL_PAGED_CODE();
//
// Assume the ACL revision.
//
AclRevision = ACL_REVISION;
RtlCreateAcl( (PACL)AclBuffer, *AclBufferSize, AclRevision );
*NewAclExplicitlyAssigned = FALSE;
*NewGenericControl = AutoInherit ? SEP_ACL_AUTO_INHERITED : 0;
//
// If the a current child ACL is not defaulted,
// the non-inherited ACEs from the current child ACL are to be preserved.
//
if ( (ChildGenericControl & SEP_ACL_DEFAULTED) == 0 ) {
//
// The resultant ACL should be protected if the input ACL is
// protected.
//
if ( ChildGenericControl & SEP_ACL_PROTECTED ) {
AclProtected = TRUE;
*NewGenericControl |= SEP_ACL_PROTECTED;
}
//
// Only copy ACEs if the child ACL is actually present.
//
if ( (ChildGenericControl & (SEP_ACL_PRESENT|SEP_ACL_PROTECTED)) != 0 ) {
if ( ChildAcl != NULL ) {
ACE_TYPE_TO_COPY AceTypeToCopy;
UCHAR AceFlagsToReset;
BOOLEAN MapSids;
AclRevision = max( AclRevision, ChildAcl->AclRevision );
//
// Since we're explicitly using the ACL specified by the caller,
// we never want to return a NULL ACL.
// Rather, if we have an ACL with no ACEs,
// we'll return exactly that. For a DACL, that results
// in a DACL that grants no access rather than a DACL
// that grants all access.
//
NullAclOk = FALSE;
//
// If the caller doesn't understand auto inheritance,
// simply preserve the specified ACL 100% intact.
//
if ( !AutoInherit ) {
AceTypeToCopy = CopyAllAces;
AceFlagsToReset = 0; // Don't turn off any ACE Flags
MapSids = FALSE; // For backward compatibility
//
// If the child is protected,
// keep all of the ACEs turning off the INHERITED ACE flags.
//
} else if ( ChildGenericControl & SEP_ACL_PROTECTED ) {
AceTypeToCopy = CopyAllAces;
AceFlagsToReset = INHERITED_ACE; // Turn off all INHERITED_ACE flags
MapSids = TRUE;
//
// If the child is not protected,
// just copy the non-inherited ACEs.
//
// (The inherited ACEs will be recomputed from the parent.)
//
} else {
AceTypeToCopy = CopyNonInheritedAces;
AceFlagsToReset = 0; // Don't turn off any ACE Flags
MapSids = TRUE;
}
//
// Copy the requested ACEs.
//
Status = RtlpCopyAces(
ChildAcl,
GenericMapping,
AceTypeToCopy,
AceFlagsToReset,
MapSids,
OwnerSid,
GroupSid,
ServerOwnerSid,
ServerGroupSid,
IsDirectoryObject,
FALSE, // Do not retain INHERITED_ACE bit for effective aces
&ChildNewAclSize,
(PACL)AclBuffer );
UsedChildNewAclSize = ChildNewAclSize;
if ( Status == STATUS_BUFFER_TOO_SMALL ) {
AclOverflowed = TRUE;
Status = STATUS_SUCCESS;
}
if ( !NT_SUCCESS(Status) ) {
return Status;
}
//
// If this ACL might be ignored later,
// remember the current state of the ACL.
//
if ( DefaultDescriptorForObject && ChildNewAclSize != 0 ) {
ChildAceCount = ((PACL)AclBuffer)->AceCount;
if (!RtlFirstFreeAce( (PACL)AclBuffer, &ChildAcePosition ) ) {
return STATUS_BAD_INHERITANCE_ACL;
}
}
//
// If the ACL isn't protected,
// don't allow NULL ACL semantics.
// (those semantics are ambiguous for auto inheritance)
//
} else if ( AutoInherit &&
!IsSacl &&
(ChildGenericControl & (SEP_ACL_PRESENT|SEP_ACL_PROTECTED)) == SEP_ACL_PRESENT ) {
return STATUS_INVALID_ACL;
}
*NewAclExplicitlyAssigned = TRUE;
}
}
//
// Inherit ACEs from the Directory ACL in any of the following cases:
// If !AutoInheriting,
// Inherit if there is no explicit child ACL (ignoring a defaulted child).
// If AutoInheriting,
// observe the protected flag.
//
if ( (!AutoInherit &&
(ChildGenericControl & SEP_ACL_PRESENT) == 0 ||
(ChildGenericControl & SEP_ACL_DEFAULTED) != 0) ||
(AutoInherit && !AclProtected) ) {
//
// If there is no directory ACL,
// don't inherit from it.
//
if ( DirectoryAcl != NULL ) {
//
// If the DirectoryAcl is used,
// the revision of the Directory ACL is picked up.
//
if ( !ValidAclRevision(DirectoryAcl) ) {
return STATUS_UNKNOWN_REVISION;
}
AclRevision = max( AclRevision, DirectoryAcl->AclRevision );
//
// Inherit the Parent's ACL.
//
Status = RtlpGenerateInheritAcl(
DirectoryAcl,
IsDirectoryObject,
AutoInherit,
OwnerSid,
GroupSid,
ServerOwnerSid,
ServerGroupSid,
GenericMapping,
pNewObjectType,
GuidCount,
&DirectoryNewAclSize,
(PACL)AclBuffer,
&ObjectAceInherited );
if ( Status == STATUS_BUFFER_TOO_SMALL ) {
AclOverflowed = TRUE;
Status = STATUS_SUCCESS;
}
if ( !NT_SUCCESS(Status) ) {
return Status;
}
//
// If the default descriptor for the object should be ditched,
// because object specific ACEs were inherited from the directory,
// ditch them now.
//
if ( DefaultDescriptorForObject &&
ChildNewAclSize != 0 &&
ObjectAceInherited &&
!AclOverflowed ) {
//
// Compute the last used byte of the combined ACL
//
if (!RtlFirstFreeAce( (PACL)AclBuffer, &DirectoryAcePosition ) ) {
return STATUS_BAD_INHERITANCE_ACL;
}
if ( DirectoryAcePosition == NULL ) {
DirectoryAcePosition = AclBuffer + ((PACL)AclBuffer)->AclSize;
}
//
// Move all the inherited ACEs to the front of the ACL.
//
RtlMoveMemory( FirstAce( AclBuffer ),
ChildAcePosition,
(ULONG)(((PUCHAR)DirectoryAcePosition) -
(PUCHAR)ChildAcePosition) );
//
// Adjust the ACE count to remove the deleted ACEs
//
((PACL)AclBuffer)->AceCount -= ChildAceCount;
//
// Save the number of bytes of the Child ACL that were
// actually used.
//
UsedChildNewAclSize = 0;
}
}
}
//
// If this routine didn't build the ACL,
// tell the caller.
//
if ( DirectoryNewAclSize + UsedChildNewAclSize == 0) {
//
// If the ACL was not explicitly assigned,
// tell the caller to default the ACL.
//
if ( !(*NewAclExplicitlyAssigned) ) {
*AclBufferSize = 0;
return STATUS_NO_INHERITANCE;
//
// If the Acl was explictly assigned,
// generate a NULL ACL based on the path taken above.
//
} else if ( NullAclOk ) {
*AclBufferSize = 0;
return STATUS_SUCCESS;
}
// DbgBreakPoint();
}
//
// And make sure we don't exceed the length limitations of an ACL (WORD)
//
if ( DirectoryNewAclSize + UsedChildNewAclSize + sizeof(ACL) > 0xFFFF) {
return(STATUS_BAD_INHERITANCE_ACL);
}
// The caller has to allocate a buffer large enough for
// ChildNewAclSize rather than UsedChildNewAclSize. Due to the nature of
// my algorithm above.
(*AclBufferSize) = DirectoryNewAclSize + ChildNewAclSize + sizeof(ACL);
if ( AclOverflowed ) {
return STATUS_BUFFER_TOO_SMALL;
}
//
// Patch the real ACL size and revision into the ACL
//
((PACL)AclBuffer)->AclSize = (USHORT)
(DirectoryNewAclSize + UsedChildNewAclSize + sizeof(ACL));
((PACL)AclBuffer)->AclRevision = (UCHAR) AclRevision;
return STATUS_SUCCESS;
}
NTSTATUS
RtlpInheritAcl (
IN PACL DirectoryAcl,
IN PACL ChildAcl,
IN ULONG ChildGenericControl,
IN BOOLEAN IsDirectoryObject,
IN BOOLEAN AutoInherit,
IN BOOLEAN DefaultDescriptorForObject,
IN PSID OwnerSid,
IN PSID GroupSid,
IN PSID ServerOwnerSid OPTIONAL,
IN PSID ServerGroupSid OPTIONAL,
IN PGENERIC_MAPPING GenericMapping,
IN BOOLEAN IsSacl,
IN GUID **pNewObjectType OPTIONAL,
IN ULONG GuidCount,
OUT PACL *NewAcl,
OUT PBOOLEAN NewAclExplicitlyAssigned,
OUT PULONG NewGenericControl
)
/*++
Routine Description:
This is a private routine that produces an inherited acl from
a parent acl according to the rules of inheritance
Arguments:
DirectoryAcl - Supplies the acl being inherited.
ChildAcl - Supplies the acl associated with the object. This
is either the current acl on the object or the acl being assigned
to the object.
ChildGenericControl - Specifies the control flags from the SecurityDescriptor
describing the ChildAcl:
SEP_ACL_PRESENT: Specifies that the child ACL is explictly supplied by
the caller.
SEP_ACL_DEFAULTED: Specifies that the child ACL was supplied by some
defaulting mechanism.
SEP_ACL_PROTECTED: Specifies that the child ACL is protected and
should not inherit any ACE from the DirectoryACL
IsDirectoryObject - Specifies if the new acl is for a directory.
AutoInherit - Specifies if the inheritance is an "automatic inheritance".
As such, the non-inherited ACEs from the ChildAcl will be preserved and
the inherited ACEs from the DirectoryAcl will be marked as such.
DefaultDescriptorForObject - If set, the CreatorDescriptor
is the default descriptor for ObjectType. As such, the
CreatorDescriptor will be ignored if any ObjectType specific
ACEs are inherited from the parent. If not such ACEs are inherited,
the CreatorDescriptor is handled as though this flag were not
specified.
OwnerSid - Specifies the owner Sid to use.
GroupSid - Specifies the group SID to use.
GenericMapping - Specifies the generic mapping to use.
IsSacl - True if this is the SACL. False if this is the DACL.
pNewObjectType - List of types of object being inherited to. If not
specified, the object has no object type.
GuidCount - Number of object types in the list.
NewAcl - Receives a pointer to the new (inherited) acl.
NewAclExplicitlyAssigned - Returns true to indicate that some portion of
"NewAcl" was derived from an the explicit ChildAcl
NewGenericControl - Specifies the control flags for the newly
generated ACL.
SEP_ACL_AUTO_INHERITED: Set if the ACL was generated using the
Automatic Inheritance algorithm.
SEP_ACL_PROTECTED: Specifies that the ACL is protected and
was not inherited from the parent ACL.
Return Value:
STATUS_SUCCESS - An inheritable ACL was successfully generated.
STATUS_NO_INHERITANCE - An inheritable ACL was not successfully generated.
This is a warning completion status.
STATUS_BAD_INHERITANCE_ACL - Indicates the acl built was not a valid ACL.
This can becaused by a number of things. One of the more probable
causes is the replacement of a CreatorId with an SID that didn't fit
into the ACE or ACL.
STATUS_UNKNOWN_REVISION - Indicates the source ACL is a revision that
is unknown to this routine.
--*/
{
//////////////////////////////////////////////////////////////////////////////
// //
// The logic in the ACL inheritance code must mirror the code for //
// inheritance in the executive (in seassign.c). Do not make changes //
// here without also making changes in that module. //
// //
//////////////////////////////////////////////////////////////////////////////
NTSTATUS Status;
ULONG AclBufferSize;
ULONG i;
#ifndef NTOS_KERNEL_RUNTIME
PVOID HeapHandle;
#endif // NTOS_KERNEL_RUNTIME
RTL_PAGED_CODE();
//
// Get the handle to the current process heap
//
#ifndef NTOS_KERNEL_RUNTIME
HeapHandle = RtlProcessHeap();
#endif // NTOS_KERNEL_RUNTIME
//
// Implement a two pass strategy.
//
// First try to create the ACL in a fixed length buffer.
// If that is too small,
// then use the buffer size determined on the first pass
//
AclBufferSize = 1024; // Typical maximum size of an ACL
for ( i=0; i<2 ; i++ ) {
//
// Allocate heap for the new ACL.
//
#ifdef NTOS_KERNEL_RUNTIME
(*NewAcl) = ExAllocatePoolWithTag(
PagedPool,
AclBufferSize,
'cAeS' );
#else // NTOS_KERNEL_RUNTIME
(*NewAcl) = RtlAllocateHeap(
HeapHandle,
MAKE_TAG(SE_TAG),
AclBufferSize );
#endif // NTOS_KERNEL_RUNTIME
if ((*NewAcl) == NULL ) {
return( STATUS_NO_MEMORY );
}
//
// Actually build the inherited ACL.
//
Status = RtlpInheritAcl2 (
DirectoryAcl,
ChildAcl,
ChildGenericControl,
IsDirectoryObject,
AutoInherit,
DefaultDescriptorForObject,
OwnerSid,
GroupSid,
ServerOwnerSid,
ServerGroupSid,
GenericMapping,
IsSacl,
pNewObjectType,
GuidCount,
&AclBufferSize,
(PUCHAR) *NewAcl,
NewAclExplicitlyAssigned,
NewGenericControl );
if ( NT_SUCCESS(Status) ) {
//
// If a NULL ACL should be used,
// tell the caller.
//
if ( AclBufferSize == 0 ) {
#ifdef NTOS_KERNEL_RUNTIME
ExFreePool( *NewAcl );
#else // NTOS_KERNEL_RUNTIME
RtlFreeHeap( HeapHandle, 0, *NewAcl );
#endif // NTOS_KERNEL_RUNTIME
*NewAcl = NULL;
}
break;
} else {
#ifdef NTOS_KERNEL_RUNTIME
ExFreePool( *NewAcl );
#else // NTOS_KERNEL_RUNTIME
RtlFreeHeap( HeapHandle, 0, *NewAcl );
#endif // NTOS_KERNEL_RUNTIME
*NewAcl = NULL;
if ( Status != STATUS_BUFFER_TOO_SMALL ) {
break;
}
}
}
return Status;
}
NTSTATUS
RtlpGenerateInheritedAce (
IN PACE_HEADER OldAce,
IN BOOLEAN IsDirectoryObject,
IN BOOLEAN AutoInherit,
IN PSID ClientOwnerSid,
IN PSID ClientGroupSid,
IN PSID ServerOwnerSid OPTIONAL,
IN PSID ServerGroupSid OPTIONAL,
IN PGENERIC_MAPPING GenericMapping,
IN GUID **pNewObjectType OPTIONAL,
IN ULONG GuidCount,
OUT PULONG NewAceLength,
OUT PACL NewAcl,
OUT PULONG NewAceExtraLength,
OUT PBOOLEAN ObjectAceInherited
)
/*++
Routine Description:
This is a private routine that checks if the input ace is inheritable
and produces 0, 1, or 2 inherited aces in the given buffer.
Arguments:
OldAce - Supplies the ace being inherited
IsDirectoryObject - Specifies if the new ACE is for a directory
AutoInherit - Specifies if the inheritance is an "automatic inheritance".
As such, the inherited ACEs will be marked as such.
ClientOwnerSid - Specifies the owner Sid to use
ClientGroupSid - Specifies the new Group Sid to use
ServerSid - Optionally specifies the Server Sid to use in compound ACEs.
ClientSid - Optionally specifies the Client Sid to use in compound ACEs.
GenericMapping - Specifies the generic mapping to use
pNewObjectType - List of types of object being inherited to. If not
specified, the object has no object type.
GuidCount - Number of object types in the list.
NewAceLength - Receives the length (number of bytes) needed to allow for
the inheritance of the specified ACE. This might be zero.
NewAcl - Provides a pointer to the ACL into which the ACE is to be
inherited.
NewAceExtraLength - Receives a length (number of bytes) temporarily used
in the ACL for the inheritance ACE. This might be zero
ObjectAceInherited - Returns true if one or more object ACEs were inherited
based on NewObjectType
Return Value:
STATUS_SUCCESS - The ACE was inherited successfully.
STATUS_BAD_INHERITANCE_ACL - Indicates something went wrong preventing
the ACE from being inherited. This generally represents a bugcheck
situation when returned from this call.
STATUS_BUFFER_TOO_SMALL - The ACL specified by NewAcl is too small for the
inheritance ACEs. The required size is returned in NewAceLength.
--*/
{
///////////////////////////////////////////////////////////////////////////
// //
// !!!!!!!!! This is tricky !!!!!!!!!! //
// //
// The inheritence flags AND the sid of the ACE determine whether //
// we need 0, 1, or 2 ACEs. //
// //
// BE CAREFUL WHEN CHANGING THIS CODE. READ THE DSA ACL ARCHITECTURE //
// SECTION COVERING INHERITENCE BEFORE ASSUMING YOU KNOW WHAT YOU ARE //
// DOING!!!! //
// //
// The general gist of the algorithm is: //
// //
// if ( (container && ContainerInherit) || //
// (!container && ObjectInherit) ) { //
// GenerateEffectiveAce; //
// } //
// //
// //
// if (Container && Propagate) { //
// Propogate copy of ACE and set InheritOnly; //
// } //
// //
// //
// A slightly more accurate description of this algorithm is: //
// //
// IO === InheritOnly flag //
// CI === ContainerInherit flag //
// OI === ObjectInherit flag //
// NPI === NoPropagateInherit flag //
// //
// if ( (container && CI) || //
// (!container && OI) ) { //
// Copy Header of ACE; //
// Clear IO, NPI, CI, OI; //
// //
// if (KnownAceType) { //
// if (SID is a creator ID) { //
// Copy appropriate creator SID; //
// } else { //
// Copy SID of original; //
// } //
// //
// Copy AccessMask of original; //
// MapGenericAccesses; //
// if (AccessMask == 0) { //
// discard new ACE; //
// } //
// //
// } else { //
// Copy body of ACE; //
// } //
// //
// } //
// //
// if (!NPI) { //
// Copy ACE as is; //
// Set IO; //
// } //
// //
// //
// //
///////////////////////////////////////////////////////////////////////////
ULONG LengthRequired = 0;
ULONG ExtraLengthRequired = 0;
PVOID AcePosition;
PVOID EffectiveAcePosition;
ULONG EffectiveAceSize = 0;
BOOLEAN EffectiveAceMapped;
BOOLEAN AclOverflowed = FALSE;
BOOLEAN GenerateInheritAce;
RTL_PAGED_CODE();
//
// This is gross and ugly, but it's better than allocating
// virtual memory to hold the ClientSid, because that can
// fail, and propogating the error back is a tremendous pain
//
ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);
*ObjectAceInherited = FALSE;
GenerateInheritAce = IsDirectoryObject && Propagate(OldAce);
//
// Allocate and initialize the universal SIDs we're going to need
// to look for inheritable ACEs.
//
if (!RtlFirstFreeAce( NewAcl, &AcePosition ) ) {
return STATUS_BAD_INHERITANCE_ACL;
}
//
// check to see if we will have a effective ACE (one mapped to
// the target object type).
//
if ( (IsDirectoryObject && ContainerInherit(OldAce)) ||
(!IsDirectoryObject && ObjectInherit(OldAce)) ) {
//
// Remember where the effective ACE will be copied to.
//
EffectiveAcePosition = AcePosition;
//
// Copy the effective ACE into the ACL.
//
if ( !RtlpCopyEffectiveAce (
OldAce,
AutoInherit,
GenerateInheritAce,
ClientOwnerSid,
ClientGroupSid,
ServerOwnerSid,
ServerGroupSid,
GenericMapping,
pNewObjectType,
GuidCount,
&AcePosition,
&EffectiveAceSize,
NewAcl,
ObjectAceInherited,
&EffectiveAceMapped,
&AclOverflowed ) ) {
return STATUS_BAD_INHERITANCE_ACL;
}
//
// If the effective ACE is a duplicate of existing inherited ACEs,
// Don't really generate it.
//
if ( !AclOverflowed &&
EffectiveAceSize > 0 &&
EffectiveAcePosition != NULL &&
RtlpIsDuplicateAce(
NewAcl,
EffectiveAcePosition ) ) {
//
// Truncate the ACE we just added.
//
NewAcl->AceCount--;
AcePosition = EffectiveAcePosition;
ExtraLengthRequired = max( ExtraLengthRequired, EffectiveAceSize );
EffectiveAceSize = 0;
}
LengthRequired += EffectiveAceSize;
}
//
// If we are inheriting onto a container, then we may need to
// propagate the inheritance as well.
//
if ( GenerateInheritAce ) {
//
// If a effective ACE was created,
// and it wasn't mapped,
// avoid generating another ACE and simply merge the inheritance bits into
// the effective ACE.
//
if ( EffectiveAceSize != 0 && !EffectiveAceMapped ) {
//
// Copy the inherit bits from the original ACE.
//
if ( !AclOverflowed ) {
((PACE_HEADER)EffectiveAcePosition)->AceFlags |=
((PACE_HEADER)OldAce)->AceFlags & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE);
if ( AutoInherit ) {
((PACE_HEADER)EffectiveAcePosition)->AceFlags |= INHERITED_ACE;
}
}
//
// Otherwise, generate an explicit inheritance ACE.
//
// But only if the access mask isn't zero.
//
} else if ( !IsMSAceType(OldAce) || ((PKNOWN_ACE)(OldAce))->Mask != 0 ) {
//
// Account for the new ACE being added to the ACL.
//
LengthRequired += (ULONG)(((PACE_HEADER)OldAce)->AceSize);
if (LengthRequired > 0xFFFF) {
return STATUS_BAD_INHERITANCE_ACL;
}
//
// If the ACE doesn't fit,
// just note the fact and don't copy the ACE.
//
if ( ((PACE_HEADER)OldAce)->AceSize > NewAcl->AclSize - ((PUCHAR)AcePosition - (PUCHAR)NewAcl) ) {
AclOverflowed = TRUE;
} else if (!AclOverflowed){
//
// copy it as is, but make sure the InheritOnly bit is set.
//
RtlCopyMemory(
AcePosition,
OldAce,
((PACE_HEADER)OldAce)->AceSize
);
((PACE_HEADER)AcePosition)->AceFlags |= INHERIT_ONLY_ACE;
NewAcl->AceCount += 1;
if ( AutoInherit ) {
((PACE_HEADER)AcePosition)->AceFlags |= INHERITED_ACE;
//
// If the inheritance ACE is a duplicate of existing inherited ACEs,
// Don't really generate it.
//
if ( RtlpIsDuplicateAce(
NewAcl,
AcePosition ) ) {
//
// Truncate the ACE we just added.
//
NewAcl->AceCount--;
ExtraLengthRequired = max( ExtraLengthRequired,
((PACE_HEADER)OldAce)->AceSize );
LengthRequired -= (ULONG)(((PACE_HEADER)OldAce)->AceSize);
}
}
}
}
}
//
// Now return to our caller
//
(*NewAceLength) = LengthRequired;
(*NewAceExtraLength) = ExtraLengthRequired;
return AclOverflowed ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
}
NTSTATUS
RtlpGenerateInheritAcl(
IN PACL Acl,
IN BOOLEAN IsDirectoryObject,
IN BOOLEAN AutoInherit,
IN PSID ClientOwnerSid,
IN PSID ClientGroupSid,
IN PSID ServerOwnerSid OPTIONAL,
IN PSID ServerGroupSid OPTIONAL,
IN PGENERIC_MAPPING GenericMapping,
IN GUID **pNewObjectType OPTIONAL,
IN ULONG GuidCount,
OUT PULONG NewAclSizeParam,
OUT PACL NewAcl,
OUT PBOOLEAN ObjectAceInherited
)
/*++
Routine Description:
This is a private routine that produces an inheritable ACL.
The buffer to contain the inherted ACL is passed in. If the buffer is
too small, the corect size is computed and STATUS_BUFFER_TOO_SMALL is
returned.
Arguments:
Acl - Supplies the acl being inherited.
IsDirectoryObject - Specifies if the new acl is for a directory.
AutoInherit - Specifies if the inheritance is an "automatic inheritance".
As such, the inherited ACEs will be marked as such.
OwnerSid - Specifies the owner Sid to use.
GroupSid - Specifies the group SID to use.
GenericMapping - Specifies the generic mapping to use.
pNewObjectType - List of types of object being inherited to. If not
specified, the object has no object type.
GuidCount - Number of object types in the list.
NewAclSizeParam - Receives the length of the inherited ACL.
NewAcl - Provides a pointer to the buffer to receive the new
(inherited) acl. This ACL must already be initialized.
ObjectAceInherited - Returns true if one or more object ACEs were inherited
based on NewObjectType
Return Value:
STATUS_SUCCESS - An inheritable ACL has been generated.
STATUS_BAD_INHERITANCE_ACL - Indicates the acl built was not a valid ACL.
This can becaused by a number of things. One of the more probable
causes is the replacement of a CreatorId with an SID that didn't fit
into the ACE or ACL.
STATUS_BUFFER_TOO_SMALL - The ACL specified by NewAcl is too small for the
inheritance ACEs. The required size is returned in NewAceLength.
--*/
{
NTSTATUS Status;
ULONG i;
PACE_HEADER OldAce;
ULONG NewAclSize, NewAceSize;
ULONG NewAclExtraSize, NewAceExtraSize;
BOOLEAN AclOverflowed = FALSE;
BOOLEAN LocalObjectAceInherited;
RTL_PAGED_CODE();
//
// Walk through the original ACL generating any necessary
// inheritable ACEs.
//
NewAclSize = 0;
NewAclExtraSize = 0;
*ObjectAceInherited = FALSE;
for (i = 0, OldAce = FirstAce(Acl);
i < Acl->AceCount;
i += 1, OldAce = NextAce(OldAce)) {
//
// RtlpGenerateInheritedAce() will generate the ACE(s) necessary
// to inherit a single ACE. This may be 0, 1, or more ACEs.
//
Status = RtlpGenerateInheritedAce(
OldAce,
IsDirectoryObject,
AutoInherit,
ClientOwnerSid,
ClientGroupSid,
ServerOwnerSid,
ServerGroupSid,
GenericMapping,
pNewObjectType,
GuidCount,
&NewAceSize,
NewAcl,
&NewAceExtraSize,
&LocalObjectAceInherited
);
if ( Status == STATUS_BUFFER_TOO_SMALL ) {
AclOverflowed = TRUE;
Status = STATUS_SUCCESS;
}
if ( !NT_SUCCESS(Status) ) {
return Status;
}
if ( LocalObjectAceInherited ) {
*ObjectAceInherited = TRUE;
}
//
// Make room in the ACL for the new ACE
//
NewAclSize += NewAceSize;
//
// If a previous ACE needed 'extra' space,
// reduce that requirement by the size of this ACE.
//
// The previous ACE can use this ACE's space temporarily
//
if ( NewAceSize > NewAclExtraSize ) {
NewAclExtraSize = 0 ;
} else {
NewAclExtraSize -= NewAceSize;
}
//
// The 'extra' space needed is the larger of that needed by any
// previous ACE and that need by this ACE
//
NewAclExtraSize = max( NewAclExtraSize, NewAceExtraSize );
}
//
// We only need to include the "ExtraSize" if we've overflowed.
// In those cases, the caller will allocate the size we requested and
// try again. Otherwise, the caller won't call back so we don't care
// if it knows about the extra size.
//
if ( AclOverflowed ) {
(*NewAclSizeParam) = NewAclSize + NewAclExtraSize;
return STATUS_BUFFER_TOO_SMALL;
} else {
(*NewAclSizeParam) = NewAclSize;
return STATUS_SUCCESS;
}
}
NTSTATUS
RtlpComputeMergedAcl2 (
IN PACL CurrentAcl,
IN ULONG CurrentGenericControl,
IN PACL ModificationAcl,
IN ULONG ModificationGenericControl,
IN PSID ClientOwnerSid,
IN PSID ClientGroupSid,
IN PGENERIC_MAPPING GenericMapping,
IN BOOLEAN IsSacl,
IN PULONG AclBufferSize,
IN OUT PUCHAR AclBuffer,
OUT PULONG NewGenericControl
)
/*++
Routine Description:
This routine implements the 'set' semantics for auto inheritance.
This routine builds the actual ACL that should be set on an object.
The built ACL is a composite of the previous ACL on an object and
the newly set ACL on the object. The New ACL is built as follows:
If SEP_ACL_PROTECTED is set in neither CurrentAcl nor ModificationAcl,
the NewAcl is constructed from the inherited ACEs from the
CurrentAcl and the non-inherited ACEs from the ModificationAcl.
(That is, it is impossible to edit an inherited ACE by changing the
ACL on an object.)
If SEP_ACL_PROTECTED is set on ModificationAcl, CurrentAcl is ignored.
NewAcl is built as a copy of ModificationAcl with any INHERITED_ACE
bits turned off.
If SEP_ACL_PROTECTED is set on CurrentAcl and not ModificationAcl, the
CurrentAcl is ignored. NewAcl is built as a copy of
ModificationDescriptor. It is the callers responsibility to ensure
that the correct ACEs have the INHERITED_ACE bit turned on.
Arguments:
CurrentAcl - The current ACL on the object.
CurrentGenericControl - Specifies the control flags from the SecurityDescriptor
describing the CurrentAcl.
ModificationAcl - The ACL being applied to the object.
ModificationGenericControl - Specifies the control flags from the SecurityDescriptor
describing the CurrentAcl.
ClientOwnerSid - Specifies the owner Sid to use
ClientGroupSid - Specifies the new Group Sid to use
GenericMapping - The mapping of generic to specific and standard
access types.
IsSacl - True if this is the SACL. False if this is the DACL.
AclBufferSize - On input, specifies the size of AclBuffer.
On output, on success, returns the used size of AclBuffer.
On output, if the buffer is too small, returns the required size of AclBuffer.
AclBuffer - Receives a pointer to the new (inherited) acl.
NewGenericControl - Specifies the control flags for the newly
generated ACL.
Only the Protected and AutoInherited bits are returned.
Return Value:
STATUS_SUCCESS - An ACL was successfully generated.
STATUS_UNKNOWN_REVISION - Indicates the source ACL is a revision that
is unknown to this routine.
--*/
{
NTSTATUS Status;
ULONG ModificationNewAclSize = 0;
ULONG CurrentNewAclSize = 0;
ULONG AclRevision;
BOOLEAN AclOverflowed = FALSE;
BOOLEAN NullAclOk = TRUE;
RTL_PAGED_CODE();
//
// Assume the ACL revision.
//
AclRevision = ACL_REVISION;
RtlCreateAcl( (PACL)AclBuffer, *AclBufferSize, AclRevision );
//
// This routine is only called for the AutoInheritance case.
//
*NewGenericControl = SEP_ACL_AUTO_INHERITED;
//
// If the new ACL is protected,
// simply use the new ACL with the INHERITED_ACE bits turned off.
//
if ( (ModificationGenericControl & SEP_ACL_PROTECTED) != 0 ) {
//
// Set the Control bits for the resultant descriptor.
//
*NewGenericControl |= SEP_ACL_PROTECTED;
//
// Only copy the ACL if it is actually present
//
if ( ModificationAcl != NULL ) {
AclRevision = max( AclRevision, ModificationAcl->AclRevision );
//
// Copy all ACES, turn off the inherited bit, and generic map them.
//
Status = RtlpCopyAces(
ModificationAcl,
GenericMapping,
CopyAllAces,
INHERITED_ACE, // Turn off all INHERITED_ACE flags
TRUE, // Map sids as needed
ClientOwnerSid,
ClientGroupSid,
ClientOwnerSid, // Not technically correct. s.b. server sid
ClientGroupSid, // Not technically correct. s.b. server sid
TRUE, // Assume container and skip optimization
FALSE, // Do not retain INHERITED_ACE bit for effective aces
&ModificationNewAclSize,
(PACL)AclBuffer );
if ( Status == STATUS_BUFFER_TOO_SMALL ) {
AclOverflowed = TRUE;
Status = STATUS_SUCCESS;
}
if ( !NT_SUCCESS(Status) ) {
return Status;
}
//
// If the caller specified an ACL with no ACES,
// make sure we generate an ACL with no ACES.
//
NullAclOk = FALSE;
}
//
// If the old ACL is protected but the new one isn't,
// simply use the new ACL as is.
//
// Rely on the caller to get the INHERITED_ACE bits right.
//
} else if ( (CurrentGenericControl & SEP_ACL_PROTECTED) != 0 ) {
//
// Only do the copy if the new ACL is specified.
//
if ( ModificationAcl != NULL ) {
AclRevision = max( AclRevision, ModificationAcl->AclRevision );
//
// Copy all ACES, and generic map them.
//
Status = RtlpCopyAces(
ModificationAcl,
GenericMapping,
CopyAllAces,
0,
TRUE, // Map sids as needed
ClientOwnerSid,
ClientGroupSid,
ClientOwnerSid, // Not technically correct. s.b. server sid
ClientGroupSid, // Not technically correct. s.b. server sid
TRUE, // Assume container and skip optimization
TRUE, // Retain INHERITED_ACE bit for effective aces
&ModificationNewAclSize,
(PACL)AclBuffer );
if ( Status == STATUS_BUFFER_TOO_SMALL ) {
AclOverflowed = TRUE;
Status = STATUS_SUCCESS;
}
if ( !NT_SUCCESS(Status) ) {
return Status;
}
//
// If the caller specified an ACL with no ACES,
// make sure we generate an ACL with no ACES.
//
NullAclOk = FALSE;
//
// Since the ACL isn't protected,
// don't allow NULL ACL semantics.
// (those semantics are ambiguous for auto inheritance)
//
} else if ( !IsSacl ) {
return STATUS_INVALID_ACL;
}
//
// If neither are protected,
// use the non-inherited ACEs from the new ACL, and
// preserve the inherited ACEs from the old ACL.
//
} else {
//
// NULL ACLs are always OK for a SACL.
// NULL ACLs are never OK for a non-protected DACL.
//
NullAclOk = IsSacl;
//
// Only do the copy if the new ACL is specified.
//
if ( ModificationAcl != NULL ) {
AclRevision = max( AclRevision, ModificationAcl->AclRevision );
//
// Copy the non-inherited ACES, and generic map them.
//
Status = RtlpCopyAces(
ModificationAcl,
GenericMapping,
CopyNonInheritedAces,
0,
TRUE, // Map sids as needed
ClientOwnerSid,
ClientGroupSid,
ClientOwnerSid, // Not technically correct. s.b. server sid
ClientGroupSid, // Not technically correct. s.b. server sid
TRUE, // Assume container and skip optimization
FALSE, // Do not retain INHERITED_ACE bit for effective aces
&ModificationNewAclSize,
(PACL)AclBuffer );
if ( Status == STATUS_BUFFER_TOO_SMALL ) {
AclOverflowed = TRUE;
Status = STATUS_SUCCESS;
}
if ( !NT_SUCCESS(Status) ) {
return Status;
}
//
// If the caller specified an ACL with no ACES,
// make sure we generate an ACL with no ACES.
//
// If inherited aces were deleted, leave the flag alone allowing
// a NULL SACL to be generated.
//
if ( ModificationAcl->AceCount == 0 ) {
NullAclOk = FALSE;
}
//
// Since the ACL isn't protected,
// don't allow NULL ACL semantics.
// (those semantics are ambiguous for auto inheritance)
//
} else if ( !IsSacl ) {
return STATUS_INVALID_ACL;
}
//
// Only do the copy if the old ACL is specified.
//
if ( CurrentAcl != NULL ) {
AclRevision = max( AclRevision, CurrentAcl->AclRevision );
//
// Copy the inherited ACES, and generic map them.
//
// Don't bother mapping the sids in these ACEs. They got mapped
// during inheritance.
//
Status = RtlpCopyAces(
CurrentAcl,
GenericMapping,
CopyInheritedAces,
0,
FALSE, // Don't map the sids,
NULL,
NULL,
NULL,
NULL,
TRUE, // Assume container and skip optimization
FALSE, // Do not retain INHERITED_ACE bit for effective aces
&CurrentNewAclSize,
(PACL)AclBuffer );
if ( Status == STATUS_BUFFER_TOO_SMALL ) {
AclOverflowed = TRUE;
Status = STATUS_SUCCESS;
}
if ( !NT_SUCCESS(Status) ) {
return Status;
}
}
}
//
// If this routine didn't build the ACL,
// tell the caller to use an explict NULL ACL
//
if ( ModificationNewAclSize + CurrentNewAclSize == 0) {
//
// If the Acl was explictly assigned,
// generate a NULL ACL based on the path taken above.
//
if ( NullAclOk ) {
*AclBufferSize = 0;
return STATUS_SUCCESS;
}
}
//
// And make sure we don't exceed the length limitations of an ACL (WORD)
//
if ( ModificationNewAclSize + CurrentNewAclSize + sizeof(ACL) > 0xFFFF) {
return(STATUS_BAD_INHERITANCE_ACL);
}
(*AclBufferSize) = ModificationNewAclSize + CurrentNewAclSize + sizeof(ACL);
if ( AclOverflowed ) {
return STATUS_BUFFER_TOO_SMALL;
}
//
// Patch the real ACL size and revision into the ACL
//
((PACL)AclBuffer)->AclSize = (USHORT) *AclBufferSize;
((PACL)AclBuffer)->AclRevision = (UCHAR) AclRevision;
return STATUS_SUCCESS;
}
NTSTATUS
RtlpComputeMergedAcl (
IN PACL CurrentAcl,
IN ULONG CurrentGenericControl,
IN PACL ModificationAcl,
IN ULONG ModificationGenericControl,
IN PSID ClientOwnerSid,
IN PSID ClientGroupSid,
IN PGENERIC_MAPPING GenericMapping,
IN BOOLEAN IsSacl,
OUT PACL *NewAcl,
OUT PULONG NewGenericControl
)
/*++
Routine Description:
This routine builds the actual ACL that should be set on an object.
The built ACL is a composite of the previous ACL on an object and
the newly set ACL on the object. The New ACL is built as follows:
If SEP_ACL_PROTECTED is set in neither CurrentAcl nor ModificationAcl,
the NewAcl is constructed from the inherited ACEs from the
CurrentAcl and the non-inherited ACEs from the ModificationAcl.
(That is, it is impossible to edit an inherited ACE by changing the
ACL on an object.)
If SEP_ACL_PROTECTED is set on ModificationAcl, CurrentAcl is ignored.
NewAcl is built as a copy of ModificationAcl with any INHERITED_ACE
bits turned off.
If SEP_ACL_PROTECTED is set on CurrentAcl and not ModificationAcl, the
CurrentAcl is ignored. NewAcl is built as a copy of
ModificationDescriptor. It is the callers responsibility to ensure
that the correct ACEs have the INHERITED_ACE bit turned on.
Arguments:
CurrentAcl - The current ACL on the object.
CurrentGenericControl - Specifies the control flags from the SecurityDescriptor
describing the CurrentAcl.
ModificationAcl - The ACL being applied to the object.
ModificationGenericControl - Specifies the control flags from the SecurityDescriptor
describing the CurrentAcl.
ClientOwnerSid - Specifies the owner Sid to use
ClientGroupSid - Specifies the new Group Sid to use
GenericMapping - The mapping of generic to specific and standard
access types.
IsSacl - True if this is the SACL. False if this is the DACL.
NewAcl - Receives a pointer to the new resultant acl.
NewGenericControl - Specifies the control flags for the newly
generated ACL.
Only the Protected and AutoInherited bits are returned.
Return Value:
STATUS_SUCCESS - An ACL was successfully generated.
STATUS_UNKNOWN_REVISION - Indicates the source ACL is a revision that
is unknown to this routine.
--*/
{
NTSTATUS Status;
ULONG AclBufferSize;
ULONG i;
#ifndef NTOS_KERNEL_RUNTIME
PVOID HeapHandle;
#endif // NTOS_KERNEL_RUNTIME
RTL_PAGED_CODE();
//
// Get the handle to the current process heap
//
#ifndef NTOS_KERNEL_RUNTIME
HeapHandle = RtlProcessHeap();
#endif // NTOS_KERNEL_RUNTIME
//
// Implement a two pass strategy.
//
// First try to create the ACL in a fixed length buffer.
// If that is too small,
// then use the buffer size determined on the first pass
//
AclBufferSize = 1024;
for ( i=0; i<2 ; i++ ) {
//
// Allocate heap for the new ACL.
//
#ifdef NTOS_KERNEL_RUNTIME
(*NewAcl) = ExAllocatePoolWithTag(
PagedPool,
AclBufferSize,
'cAeS' );
#else // NTOS_KERNEL_RUNTIME
(*NewAcl) = RtlAllocateHeap( HeapHandle, 0, AclBufferSize );
#endif // NTOS_KERNEL_RUNTIME
if ((*NewAcl) == NULL ) {
return( STATUS_NO_MEMORY );
}
//
// Merge the ACLs
//
Status = RtlpComputeMergedAcl2 (
CurrentAcl,
CurrentGenericControl,
ModificationAcl,
ModificationGenericControl,
ClientOwnerSid,
ClientGroupSid,
GenericMapping,
IsSacl,
&AclBufferSize,
(PUCHAR) *NewAcl,
NewGenericControl );
if ( NT_SUCCESS(Status) ) {
//
// If a NULL ACL should be used,
// tell the caller.
//
if ( AclBufferSize == 0 ) {
#ifdef NTOS_KERNEL_RUNTIME
ExFreePool( *NewAcl );
#else // NTOS_KERNEL_RUNTIME
RtlFreeHeap( HeapHandle, 0, *NewAcl );
#endif // NTOS_KERNEL_RUNTIME
*NewAcl = NULL;
}
break;
} else {
#ifdef NTOS_KERNEL_RUNTIME
ExFreePool( *NewAcl );
#else // NTOS_KERNEL_RUNTIME
RtlFreeHeap( HeapHandle, 0, *NewAcl );
#endif // NTOS_KERNEL_RUNTIME
*NewAcl = NULL;
if ( Status != STATUS_BUFFER_TOO_SMALL ) {
break;
}
}
}
return Status;
}
#endif // WIN16
#if DBG
NTSTATUS
RtlDumpUserSid(
VOID
)
{
NTSTATUS Status;
HANDLE TokenHandle;
CHAR Buffer[200];
ULONG ReturnLength;
PSID pSid;
UNICODE_STRING SidString;
PTOKEN_USER User;
//
// Attempt to open the impersonation token first
//
Status = NtOpenThreadToken(
NtCurrentThread(),
GENERIC_READ,
FALSE,
&TokenHandle
);
if (!NT_SUCCESS( Status )) {
DbgPrint("Not impersonating, status = %X, trying process token\n",Status);
Status = NtOpenProcessToken(
NtCurrentProcess(),
GENERIC_READ,
&TokenHandle
);
if (!NT_SUCCESS( Status )) {
DbgPrint("Unable to open process token, status = %X\n",Status);
return( Status );
}
}
Status = NtQueryInformationToken (
TokenHandle,
TokenUser,
Buffer,
200,
&ReturnLength
);
if (!NT_SUCCESS( Status )) {
DbgPrint("Unable to query user sid, status = %X \n",Status);
NtClose(TokenHandle);
return( Status );
}
User = (PTOKEN_USER)Buffer;
pSid = User->User.Sid;
Status = RtlConvertSidToUnicodeString( &SidString, pSid, TRUE );
if (!NT_SUCCESS( Status )) {
DbgPrint("Unable to format sid string, status = %X \n",Status);
NtClose(TokenHandle);
return( Status );
}
DbgPrint("Current Sid = %wZ \n",&SidString);
RtlFreeUnicodeString( &SidString );
return( STATUS_SUCCESS );
}
#endif
NTSTATUS
RtlpConvertToAutoInheritSecurityObject(
IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL,
IN PSECURITY_DESCRIPTOR CurrentSecurityDescriptor,
OUT PSECURITY_DESCRIPTOR *NewSecurityDescriptor,
IN GUID *ObjectType OPTIONAL,
IN BOOLEAN IsDirectoryObject,
IN PGENERIC_MAPPING GenericMapping
)
/*++
Routine Description:
This routine a converts a security descriptor whose ACLs are not marked
as AutoInherit to a security descriptor whose ACLs are marked as
AutoInherit.
See comments for RtlConvertToAutoInheritSecurityObject.
Arguments:
ParentDescriptor - Supplies the Security Descriptor for the parent
directory under which a object exists. If there is
no parent directory, then this argument is specified as NULL.
CurrentSecurityDescriptor - Supplies a pointer to the objects security descriptor
that is going to be altered by this procedure.
NewSecurityDescriptor Points to a pointer that is to be made to point to the
newly allocated self-relative security descriptor. When no
longer needed, this descriptor must be freed using
DestroyPrivateObjectSecurity().
ObjectType - GUID of the object type being created. If the object being
created has no GUID associated with it, then this argument is
specified as NULL.
IsDirectoryObject - Specifies if the object is a
directory object. A value of TRUE indicates the object is a
container of other objects.
GenericMapping - Supplies a pointer to a generic mapping array denoting
the mapping between each generic right to specific rights.
Return Value:
STATUS_SUCCESS - The operation was successful.
See comments for RtlConvertToAutoInheritSecurityObject.
--*/
{
NTSTATUS Status;
PISECURITY_DESCRIPTOR CurrentDescriptor;
PACL CurrentSacl;
PACL CurrentDacl;
PSID NewOwner;
PSID NewGroup;
PACL NewSacl = NULL;
ULONG NewSaclControl = 0;
BOOLEAN NewSaclAllocated = FALSE;
PACL NewDacl = NULL;
ULONG NewDaclControl = 0;
BOOLEAN NewDaclAllocated = FALSE;
PACL TemplateInheritedDacl = NULL;
ULONG GenericControl;
ULONG AllocationSize;
ULONG NewOwnerSize;
ULONG NewGroupSize;
ULONG NewSaclSize;
ULONG NewDaclSize;
PCHAR Field;
PCHAR Base;
PISECURITY_DESCRIPTOR_RELATIVE INewDescriptor = NULL;
ULONG ReturnLength;
NTSTATUS PassedStatus;
HANDLE PrimaryToken;
#ifndef NTOS_KERNEL_RUNTIME
PVOID HeapHandle;
#endif // NTOS_KERNEL_RUNTIME
RTL_PAGED_CODE();
//
// Get the handle to the current process heap
//
#ifndef NTOS_KERNEL_RUNTIME
HeapHandle = RtlProcessHeap();
#endif // NTOS_KERNEL_RUNTIME
//
//
CurrentDescriptor = CurrentSecurityDescriptor;
//
// Validate the incoming security descriptor.
//
if (!RtlValidSecurityDescriptor ( CurrentDescriptor )) {
Status = STATUS_INVALID_SECURITY_DESCR;
goto Cleanup;
}
NewOwner = RtlpOwnerAddrSecurityDescriptor( CurrentDescriptor );
if ( NewOwner == NULL ) {
Status = STATUS_INVALID_SECURITY_DESCR;
goto Cleanup;
}
NewGroup = RtlpGroupAddrSecurityDescriptor( CurrentDescriptor );
//
// Handle the SACL.
//
//
// If the SACL isn't present,
// special case it.
//
CurrentSacl = RtlpSaclAddrSecurityDescriptor( CurrentDescriptor );
if ( CurrentSacl == NULL ) {
PACL ParentSacl;
// Preserve the Acl Present bit and protected bit from the existing descriptor.
NewSaclControl |= CurrentDescriptor->Control & (SE_SACL_PROTECTED|SE_SACL_PRESENT);
// Always set the autoinherited bit.
NewSaclControl |= SE_SACL_AUTO_INHERITED;
//
// If the Parent also has a NULL SACL,
// just consider this SACL as inherited.
// otherwise, this SACL is protected.
//
ParentSacl = ARGUMENT_PRESENT(ParentDescriptor) ?
RtlpSaclAddrSecurityDescriptor( ((SECURITY_DESCRIPTOR *)ParentDescriptor)) :
NULL;
if ( ParentSacl != NULL) {
NewSaclControl |= SE_SACL_PROTECTED;
}
//
// If the SACL is already converted,
// or if this object is at the root of the tree,
// simply leave it alone.
//
// Don't force the Protect bit on at the root of the tree since it is semantically
// a no-op and gets in the way if the object is ever moved.
//
} else if ( RtlpAreControlBitsSet( CurrentDescriptor, SE_SACL_AUTO_INHERITED) ||
RtlpAreControlBitsSet( CurrentDescriptor, SE_SACL_PROTECTED ) ||
!ARGUMENT_PRESENT(ParentDescriptor) ) {
// Preserve the Acl Present bit and protected bit from the existing descriptor.
NewSaclControl |= CurrentDescriptor->Control & (SE_SACL_PROTECTED|SE_SACL_PRESENT);
// Always set the autoinherited bit.
NewSaclControl |= SE_SACL_AUTO_INHERITED;
NewSacl = CurrentSacl;
//
// If the SACL is present,
// compute a new SACL with appropriate ACEs marked as inherited.
//
} else {
Status = RtlpConvertAclToAutoInherit (
ARGUMENT_PRESENT(ParentDescriptor) ?
RtlpSaclAddrSecurityDescriptor(
((SECURITY_DESCRIPTOR *)ParentDescriptor)) :
NULL,
RtlpSaclAddrSecurityDescriptor(CurrentDescriptor),
ObjectType,
IsDirectoryObject,
RtlpOwnerAddrSecurityDescriptor(CurrentDescriptor),
RtlpGroupAddrSecurityDescriptor(CurrentDescriptor),
GenericMapping,
&NewSacl,
&GenericControl );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
NewSaclAllocated = TRUE;
NewSaclControl |= SE_SACL_PRESENT | SeControlGenericToSacl( GenericControl );
}
//
// Handle the DACL.
//
//
// If the DACL isn't present,
// special case it.
//
CurrentDacl = RtlpDaclAddrSecurityDescriptor( CurrentDescriptor );
if ( CurrentDacl == NULL ) {
// Preserve the Dacl Present bit from the existing descriptor.
NewDaclControl |= CurrentDescriptor->Control & SE_DACL_PRESENT;
// Always set the autoinherited bit.
// Force it protected.
NewDaclControl |= SE_DACL_AUTO_INHERITED | SE_DACL_PROTECTED;
//
// If the DACL is already converted,
// or if this object is at the root of the tree,
// simply leave it alone.
//
// Don't force the Protect bit on at the root of the tree since it is semantically
// a no-op and gets in the way if the object is ever moved.
//
} else if ( RtlpAreControlBitsSet( CurrentDescriptor, SE_DACL_AUTO_INHERITED) ||
RtlpAreControlBitsSet( CurrentDescriptor, SE_DACL_PROTECTED ) ||
!ARGUMENT_PRESENT(ParentDescriptor) ) {
// Preserve the Acl Present bit and protected bit from the existing descriptor.
NewDaclControl |= CurrentDescriptor->Control & (SE_DACL_PROTECTED|SE_DACL_PRESENT);
// Always set the autoinherited bit.
NewDaclControl |= SE_DACL_AUTO_INHERITED;
NewDacl = CurrentDacl;
//
// If the DACL is present,
// compute a new DACL with appropriate ACEs marked as inherited.
//
} else {
Status = RtlpConvertAclToAutoInherit (
ARGUMENT_PRESENT(ParentDescriptor) ?
RtlpDaclAddrSecurityDescriptor(
((SECURITY_DESCRIPTOR *)ParentDescriptor)) :
NULL,
RtlpDaclAddrSecurityDescriptor(CurrentDescriptor),
ObjectType,
IsDirectoryObject,
RtlpOwnerAddrSecurityDescriptor(CurrentDescriptor),
RtlpGroupAddrSecurityDescriptor(CurrentDescriptor),
GenericMapping,
&NewDacl,
&GenericControl );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
NewDaclAllocated = TRUE;
NewDaclControl |= SE_DACL_PRESENT | SeControlGenericToDacl( GenericControl );
}
//
// Build the resultant security descriptor
//
// Also map the ACEs for application to the target object
// type, if they haven't already been mapped.
//
NewOwnerSize = LongAlignSize(SeLengthSid(NewOwner));
if ( NewGroup != NULL ) {
NewGroupSize = LongAlignSize(SeLengthSid(NewGroup));
} else {
NewGroupSize = 0;
}
if (NewSacl != NULL) {
NewSaclSize = LongAlignSize(NewSacl->AclSize);
} else {
NewSaclSize = 0;
}
if (NewDacl != NULL) {
NewDaclSize = LongAlignSize(NewDacl->AclSize);
} else {
NewDaclSize = 0;
}
AllocationSize = LongAlignSize(sizeof(SECURITY_DESCRIPTOR_RELATIVE)) +
NewOwnerSize +
NewGroupSize +
NewSaclSize +
NewDaclSize;
//
// Allocate and initialize the security descriptor as
// self-relative form.
//
#ifdef NTOS_KERNEL_RUNTIME
INewDescriptor = ExAllocatePoolWithTag(
PagedPool,
AllocationSize,
'dSeS' );
#else // NTOS_KERNEL_RUNTIME
INewDescriptor = RtlAllocateHeap(
HeapHandle,
MAKE_TAG(SE_TAG),
AllocationSize );
#endif // NTOS_KERNEL_RUNTIME
if ( INewDescriptor == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
//
// Initialize the security descriptor as self-relative form.
//
RtlCreateSecurityDescriptorRelative(
INewDescriptor,
SECURITY_DESCRIPTOR_REVISION
);
RtlpSetControlBits( INewDescriptor, SE_SELF_RELATIVE );
Base = (PCHAR)(INewDescriptor);
Field = Base + sizeof(SECURITY_DESCRIPTOR_RELATIVE);
//
// Copy the Sacl
//
RtlpSetControlBits( INewDescriptor, NewSaclControl );
if (NewSacl != NULL ) {
RtlCopyMemory( Field, NewSacl, NewSacl->AclSize );
INewDescriptor->Sacl = RtlPointerToOffset(Base,Field);
Field += NewSaclSize;
} else {
INewDescriptor->Sacl = 0;
}
//
// Copy the Dacl
//
RtlpSetControlBits( INewDescriptor, NewDaclControl );
if (NewDacl != NULL ) {
RtlCopyMemory( Field, NewDacl, NewDacl->AclSize );
INewDescriptor->Dacl = RtlPointerToOffset(Base,Field);
Field += NewDaclSize;
} else {
INewDescriptor->Dacl = 0;
}
//
// Assign the owner
//
RtlCopyMemory( Field, NewOwner, SeLengthSid(NewOwner) );
INewDescriptor->Owner = RtlPointerToOffset(Base,Field);
Field += NewOwnerSize;
if ( NewGroup != NULL ) {
RtlCopyMemory( Field, NewGroup, SeLengthSid(NewGroup) );
INewDescriptor->Group = RtlPointerToOffset(Base,Field);
}
Status = STATUS_SUCCESS;
//
// Cleanup any locally used resources.
//
Cleanup:
if (NewDaclAllocated) {
#ifdef NTOS_KERNEL_RUNTIME
ExFreePool( NewDacl );
#else // NTOS_KERNEL_RUNTIME
RtlFreeHeap( HeapHandle, 0, NewDacl );
#endif // NTOS_KERNEL_RUNTIME
}
if (NewSaclAllocated) {
#ifdef NTOS_KERNEL_RUNTIME
ExFreePool( NewSacl );
#else // NTOS_KERNEL_RUNTIME
RtlFreeHeap( HeapHandle, 0, NewSacl );
#endif // NTOS_KERNEL_RUNTIME
}
*NewSecurityDescriptor = (PSECURITY_DESCRIPTOR) INewDescriptor;
return Status;
}
//
// Local macro to classify the ACE flags in an ACE.
//
// Returns one or more of the following ACE flags:
//
// CONTAINER_INHERIT_ACE - ACE is inherited to child containers
// OBJECT_INHERIT_ACE - ACE is inherited to child leaf objects
// EFFECTIVE_ACE - ACE is used during access validation
//
#define MAX_CHILD_SID_GROUP_SIZE 3 // Number of bits in above list
#define EFFECTIVE_ACE INHERIT_ONLY_ACE
#define AceFlagsInAce( _Ace) \
(((PACE_HEADER)(_Ace))->AceFlags & (OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE) | \
(((PACE_HEADER)(_Ace))->AceFlags & INHERIT_ONLY_ACE) ^ INHERIT_ONLY_ACE )
BOOLEAN
RtlpCompareAces(
IN PKNOWN_ACE InheritedAce,
IN PKNOWN_ACE ChildAce,
IN PSID OwnerSid,
IN PSID GroupSid
)
/*++
Routine Description:
Compare two aces to see if they are "substantially" the same.
Arguments:
InheritedAce - Computed ACE as inherited from DirectoryAcl.
ChildAce - The current acl on the object. This ACL must be a revision 2 ACL.
ObjectType - GUID of the object type being created. If the object being
created has no GUID associated with it, then this argument is
specified as NULL.
OwnerSid - Specifies the owner Sid to use.
If not specified, the owner sid is not treated as special.
GroupSid - Specifies the group SID to use.
If not specified, the group sid is not treated as special.
Return Value:
TRUE - The ACEs are substantially the same.
FALSE - The ACEs are not substantially the same.
--*/
{
BOOLEAN AcesCompare = FALSE;
if (IsObjectAceType(InheritedAce) && IsObjectAceType(ChildAce)) {
AcesCompare = RtlpCompareKnownObjectAces( (PKNOWN_OBJECT_ACE)InheritedAce,
(PKNOWN_OBJECT_ACE)ChildAce,
OwnerSid,
GroupSid
);
} else {
if (!IsObjectAceType(InheritedAce) && !IsObjectAceType(ChildAce)) {
AcesCompare = RtlpCompareKnownAces( InheritedAce,
ChildAce,
OwnerSid,
GroupSid
);
}
}
return( AcesCompare );
}
BOOLEAN
RtlpCompareKnownAces(
IN PKNOWN_ACE InheritedAce,
IN PKNOWN_ACE ChildAce,
IN PSID OwnerSid OPTIONAL,
IN PSID GroupSid OPTIONAL
)
/*++
Routine Description:
Compare two aces to see if they are "substantially" the same.
Arguments:
InheritedAce - Computed ACE as inherited from DirectoryAcl.
ChildAce - The current acl on the object. This ACL must be a revision 2 ACL.
OwnerSid - Specifies the owner Sid to use.
If not specified, the owner sid is not treated as special.
GroupSid - Specifies the group SID to use.
If not specified, the group sid is not treated as special.
Return Value:
TRUE - The ACEs are substantially the same.
FALSE - The ACEs are not substantially the same.
--*/
{
NTSTATUS Status;
ACE_HEADER volatile *InheritedAceHdr = &InheritedAce->Header;
RTL_PAGED_CODE();
ASSERT(!IsObjectAceType(InheritedAce));
ASSERT(!IsObjectAceType(ChildAce));
//
// If the Ace types are different,
// we don't match.
//
if ( RtlBaseAceType[ChildAce->Header.AceType] != RtlBaseAceType[InheritedAceHdr->AceType] ) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("AceType mismatch"));
}
#endif // DBG
return FALSE;
}
//
// If this is a system ACE,
// ensure the SUCCESS/FAILURE flags match.
//
if ( RtlIsSystemAceType[ChildAce->Header.AceType] ) {
if ( (ChildAce->Header.AceFlags & (SUCCESSFUL_ACCESS_ACE_FLAG|FAILED_ACCESS_ACE_FLAG)) !=
(InheritedAceHdr->AceFlags & (SUCCESSFUL_ACCESS_ACE_FLAG|FAILED_ACCESS_ACE_FLAG)) ) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("System ace success/fail mismatch"));
}
#endif // DBG
return FALSE;
}
}
//
// If the SID of the inherited ACE doesn't match,
// we don't match.
//
if ( !RtlEqualSid( (PSID)&ChildAce->SidStart, (PSID)&InheritedAce->SidStart )) {
//
// The inheritance algorithm only does SID mapping when building the effective
// ace. So, we only check for a mapped SID if the child ACE is an effective ACE.
//
if ( AceFlagsInAce(ChildAce) != EFFECTIVE_ACE ) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("SID mismatch"));
}
#endif // DBG
return FALSE;
}
//
// In the case of CreatorOwner and CreatorGroup, the SIDs don't have to
// exactly match. When the InheritedAce was generated, care was taken
// to NOT map these sids. The SID may (or may not) have been mapped in
// the ChildAce. We want to compare equal in both cases.
//
// If the InheritedAce contains a CreatorOwner/Group SID,
// do the another comparison of the SID in the child ACE with the
// real owner/group from the child security descriptor.
//
if ( OwnerSid != NULL || GroupSid != NULL ) {
SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
ULONG CreatorSid[CREATOR_SID_SIZE];
//
// Allocate and initialize the universal SIDs we're going to need
// to look for inheritable ACEs.
//
ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);
Status = RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
if ( !NT_SUCCESS(Status) ) {
return FALSE;
}
*(RtlpSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_OWNER_RID;
if (RtlEqualPrefixSid ( (PSID)&InheritedAce->SidStart, CreatorSid )) {
ULONG Rid;
Rid = *RtlpSubAuthoritySid( (PSID)&InheritedAce->SidStart, 0 );
switch (Rid) {
case SECURITY_CREATOR_OWNER_RID:
if ( OwnerSid == NULL ||
!RtlEqualSid( (PSID)&ChildAce->SidStart, OwnerSid )) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("SID mismatch (Creator Owner)"));
}
#endif // DBG
return FALSE;
}
break;
case SECURITY_CREATOR_GROUP_RID:
if ( GroupSid == NULL ||
!RtlEqualSid( (PSID)&ChildAce->SidStart, GroupSid )) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("SID mismatch (Creator Group)"));
}
#endif // DBG
return FALSE;
}
break;
default:
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("SID mismatch (Creator)"));
}
#endif // DBG
return FALSE;
}
} else {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("SID mismatch"));
}
#endif // DBG
return FALSE;
}
} else {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("SID mismatch"));
}
#endif // DBG
return FALSE;
}
}
return TRUE;
}
BOOLEAN
RtlpCompareKnownObjectAces(
IN PKNOWN_OBJECT_ACE InheritedAce,
IN PKNOWN_OBJECT_ACE ChildAce,
IN PSID OwnerSid OPTIONAL,
IN PSID GroupSid OPTIONAL
)
/*++
Routine Description:
Compare two aces to see if they are "substantially" the same.
Arguments:
InheritedAce - Computed ACE as inherited from DirectoryAcl.
ChildAce - The current acl on the object. This ACL must be a revision 2 ACL.
ObjectType - GUID of the object type being created. If the object being
created has no GUID associated with it, then this argument is
specified as NULL.
OwnerSid - Specifies the owner Sid to use.
If not specified, the owner sid is not treated as special.
GroupSid - Specifies the group SID to use.
If not specified, the group sid is not treated as special.
Return Value:
TRUE - The ACEs are substantially the same.
FALSE - The ACEs are not substantially the same.
--*/
{
NTSTATUS Status;
BOOLEAN DoingObjectAces;
GUID *ChildObjectGuid;
GUID *InhObjectGuid;
GUID *ChildInheritedObjectGuid;
GUID *InhInheritedObjectGuid;
ACE_HEADER volatile *InheritedAceHdr = &InheritedAce->Header;
RTL_PAGED_CODE();
ASSERT(IsObjectAceType(InheritedAce));
ASSERT(IsObjectAceType(ChildAce));
//
// If the Ace types are different,
// we don't match.
//
if ( RtlBaseAceType[ChildAce->Header.AceType] != RtlBaseAceType[InheritedAceHdr->AceType] ) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("AceType mismatch"));
}
#endif // DBG
return FALSE;
}
//
// If this is a system ACE,
// ensure the SUCCESS/FAILURE flags match.
//
if ( RtlIsSystemAceType[ChildAce->Header.AceType] ) {
if ( (ChildAce->Header.AceFlags & (SUCCESSFUL_ACCESS_ACE_FLAG|FAILED_ACCESS_ACE_FLAG)) !=
(InheritedAceHdr->AceFlags & (SUCCESSFUL_ACCESS_ACE_FLAG|FAILED_ACCESS_ACE_FLAG)) ) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("System ace success/fail mismatch"));
}
#endif // DBG
return FALSE;
}
}
//
// Get the GUIDs from the Object Aces
//
ChildObjectGuid = RtlObjectAceObjectType(ChildAce);
ChildInheritedObjectGuid = RtlObjectAceInheritedObjectType(ChildAce);
InhObjectGuid = RtlObjectAceObjectType(InheritedAce);
InhInheritedObjectGuid = RtlObjectAceInheritedObjectType(InheritedAce);
//
// If the InheritedObjectGuid is present in either ACE,
// they must be equal.
//
if ( ChildInheritedObjectGuid != NULL || InhInheritedObjectGuid != NULL ) {
if ( ChildInheritedObjectGuid == NULL ||
InhInheritedObjectGuid == NULL ||
!RtlpIsEqualGuid( ChildInheritedObjectGuid, InhInheritedObjectGuid )) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("InheritedObject GUID mismatch"));
}
#endif // DBG
return FALSE;
}
}
//
// If the ObjectGUID is present in either ACE,
// they must be equal.
//
// Any missing object GUID defaults to the passed in object GUID.
//
if ( (ChildObjectGuid != NULL) && (InhObjectGuid != NULL) ) {
if (!RtlpIsEqualGuid( ChildObjectGuid, InhObjectGuid )) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("Object GUID mismatch"));
}
#endif // DBG
return( FALSE );
}
} else {
//
// One or both is NULL, if it's only one, they don't match.
//
if ( !((ChildObjectGuid == NULL) && (InhObjectGuid == NULL)) ) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("Object GUID mismatch"));
}
#endif // DBG
return( FALSE );
}
}
//
// If the SID of the inherited ACE doesn't match,
// we don't match.
//
if ( !RtlEqualSid( RtlObjectAceSid(ChildAce), RtlObjectAceSid(InheritedAce))) {
//
// The inheritance algorithm only does SID mapping when building the effective
// ace. So, we only check for a mapped SID if the child ACE is an effective ACE.
//
if ( AceFlagsInAce(ChildAce) != EFFECTIVE_ACE ) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("SID mismatch"));
}
#endif // DBG
return FALSE;
}
//
// In the case of CreatorOwner and CreatorGroup, the SIDs don't have to
// exactly match. When the InheritedAce was generated, care was taken
// to NOT map these sids. The SID may (or may not) have been mapped in
// the ChildAce. We want to compare equal in both cases.
//
// If the InheritedAce contains a CreatorOwner/Group SID,
// do the another comparison of the SID in the child ACE with the
// real owner/group from the child security descriptor.
//
if ( OwnerSid != NULL || GroupSid != NULL ) {
SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
ULONG CreatorSid[CREATOR_SID_SIZE];
//
// Allocate and initialize the universal SIDs we're going to need
// to look for inheritable ACEs.
//
ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);
Status = RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
if ( !NT_SUCCESS(Status) ) {
return FALSE;
}
*(RtlpSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_OWNER_RID;
if (RtlEqualPrefixSid ( RtlObjectAceSid(InheritedAce), CreatorSid )) {
ULONG Rid;
Rid = *RtlpSubAuthoritySid( RtlObjectAceSid(InheritedAce), 0 );
switch (Rid) {
case SECURITY_CREATOR_OWNER_RID:
if ( OwnerSid == NULL ||
!RtlEqualSid( RtlObjectAceSid(ChildAce), OwnerSid )) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("SID mismatch (Creator Owner)"));
}
#endif // DBG
return FALSE;
}
break;
case SECURITY_CREATOR_GROUP_RID:
if ( GroupSid == NULL ||
!RtlEqualSid( RtlObjectAceSid(ChildAce), GroupSid )) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("SID mismatch (Creator Group)"));
}
#endif // DBG
return FALSE;
}
break;
default:
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("SID mismatch (Creator)"));
}
#endif // DBG
return FALSE;
}
} else {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("SID mismatch"));
}
#endif // DBG
return FALSE;
}
} else {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("SID mismatch"));
}
#endif // DBG
return FALSE;
}
}
return TRUE;
}
NTSTATUS
RtlpConvertAclToAutoInherit (
IN PACL ParentAcl OPTIONAL,
IN PACL ChildAcl,
IN GUID *ObjectType OPTIONAL,
IN BOOLEAN IsDirectoryObject,
IN PSID OwnerSid,
IN PSID GroupSid,
IN PGENERIC_MAPPING GenericMapping,
OUT PACL *NewAcl,
OUT PULONG NewGenericControl
)
/*++
Routine Description:
This is a private routine that produces an auto inherited acl from
a ChildAcl that is not marked as auto inherited. The passed in InheritedAcl
is computed as the pure inherited ACL of Parent ACL of the object.
See comments for RtlConvertToAutoInheritSecurityObject.
Arguments:
ParentAcl - Supplies the ACL of the parent object.
ChildAcl - Supplies the acl associated with the object. This
is the current acl on the object.
ObjectType - GUID of the object type being created. If the object being
created has no GUID associated with it, then this argument is
specified as NULL.
IsDirectoryObject - Specifies if the object is a
directory object. A value of TRUE indicates the object is a
container of other objects.
OwnerSid - Specifies the owner Sid to use.
GroupSid - Specifies the group SID to use.
GenericMapping - Specifies the generic mapping to use.
NewAcl - Receives a pointer to the new (auto inherited) acl.
The ACL must be deallocated using the pool (kernel mode) or
heap (user mode) deallocator.
NewGenericControl - Specifies the control flags for the newly
generated ACL.
SEP_ACL_PRESENT: Specifies that the ACL is explictly supplied by
the caller. ?? Ever set?
SEP_ACL_DEFAULTED: Specifies that the ACL was supplied by some
defaulting mechanism. ?? Ever set
SEP_ACL_AUTO_INHERITED: Set if the ACL was generated using the
Automatic Inheritance algorithm.
SEP_ACL_PROTECTED: Specifies that the ACL is protected and
was not inherited from the parent ACL.
Return Value:
STATUS_SUCCESS - An inheritable ACL was successfully generated.
STATUS_UNKNOWN_REVISION - Indicates the source ACL is a revision that
is unknown to this routine.
STATUS_INVALID_ACL - The structure of one of the ACLs in invalid.
--*/
{
NTSTATUS Status;
PACL InheritedAcl = NULL;
PACL RealInheritedAcl = NULL;
BOOLEAN AclExplicitlyAssigned;
ULONG GenericControl;
PKNOWN_ACE ChildAce = NULL;
PKNOWN_ACE InheritedAce;
LONG ChildAceIndex;
LONG InheritedAceIndex;
BOOLEAN InheritedAllowFound;
BOOLEAN InheritedDenyFound;
BOOLEAN AcesCompare;
ACCESS_MASK InheritedContainerInheritMask;
ACCESS_MASK InheritedObjectInheritMask;
ACCESS_MASK InheritedEffectiveMask;
ACCESS_MASK OriginalInheritedContainerInheritMask;
ACCESS_MASK OriginalInheritedObjectInheritMask;
ACCESS_MASK OriginalInheritedEffectiveMask;
ULONG InheritedAceFlags;
ULONG MatchedFlags;
ULONG NonInheritedAclSize;
PACE_HEADER AceHeader;
PUCHAR Where;
// ULONG i;
//
// This routine maintains an array of the structure below (one element per ACE in
// the ChildAcl).
//
// The ACE is broken down into its component parts. The access mask is triplicated.
// That is, if the ACE is a ContainerInherit ACE, the access mask is remembered as
// being a "ContainerInheritMask". The same is true if the ACE is an ObjectInherit ACE
// on an effective ACE. This is done since each of the resultant 96 bits are
// individually matched against corresponding bits in the computed inherited ACE.
//
// Each of the above mentioned masks are maintained in two forms. The first is never
// changed and represents the bits as the originally appeared in the child ACL.
// This second is modified as the corresponding bits are matched in the inherited ACL.
// When the algorithm is completed, bits that haven't been matched represent ACEs
// that weren't inherited from the parent.
//
typedef struct {
ACCESS_MASK OriginalContainerInheritMask;
ACCESS_MASK OriginalObjectInheritMask;
ACCESS_MASK OriginalEffectiveMask;
ACCESS_MASK ContainerInheritMask;
ACCESS_MASK ObjectInheritMask;
ACCESS_MASK EffectiveMask;
} ACE_INFO, *PACE_INFO;
PACE_INFO ChildAceInfo = NULL;
ULONG CreatorOwnerSid[CREATOR_SID_SIZE];
ULONG CreatorGroupSid[CREATOR_SID_SIZE];
SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
#ifndef NTOS_KERNEL_RUNTIME
PVOID HeapHandle;
#endif // NTOS_KERNEL_RUNTIME
RTL_PAGED_CODE();
//
// Get the handle to the current process heap
//
#ifndef NTOS_KERNEL_RUNTIME
HeapHandle = RtlProcessHeap();
#endif // NTOS_KERNEL_RUNTIME
//
// Allocate and initialize the universal SIDs we're going to need
// to look for inheritable ACEs.
//
ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);
Status = RtlInitializeSid( (PSID)CreatorOwnerSid, &CreatorSidAuthority, 1 );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
*(RtlpSubAuthoritySid( (PSID)CreatorOwnerSid, 0 )) = SECURITY_CREATOR_OWNER_RID;
Status = RtlInitializeSid( (PSID)CreatorGroupSid, &CreatorSidAuthority, 1 );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
*(RtlpSubAuthoritySid( (PSID)CreatorGroupSid, 0 )) = SECURITY_CREATOR_GROUP_RID;
//
// Ensure the passed in ACLs are valid.
//
*NewGenericControl = SEP_ACL_AUTO_INHERITED;
*NewAcl = NULL;
if ( ParentAcl != NULL && !RtlValidAcl( ParentAcl ) ) {
Status = STATUS_INVALID_ACL;
goto Cleanup;
}
if (!RtlValidAcl( ChildAcl ) ) {
Status = STATUS_INVALID_ACL;
goto Cleanup;
}
//
// Compute what the inherited ACL "should" look like.
//
// The inherited ACL is computed to NOT SID-map Creator Owner and Creator Group.
// This allows use to later recognize the constant SIDs and special case them
// rather than mistakenly confuse them with the mapped SID.
//
Status = RtlpInheritAcl (
ParentAcl,
NULL, // No explicit child ACL
0, // No Child Generic Control
IsDirectoryObject,
TRUE, // AutoInherit the DACL
FALSE, // Not default descriptor for object
CreatorOwnerSid, // Subsitute a constant SID
CreatorGroupSid, // Subsitute a constant SID
CreatorOwnerSid, // Server Owner (Technically incorrect, but OK since we don't support compound ACEs)
CreatorGroupSid, // Server Group
GenericMapping,
TRUE, // Is a SACL
ObjectType ? &ObjectType : NULL,
ObjectType ? 1 : 0,
&InheritedAcl,
&AclExplicitlyAssigned,
&GenericControl );
if ( Status == STATUS_NO_INHERITANCE ) {
*NewGenericControl |= SEP_ACL_PROTECTED;
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("NO_INHERITANCE of the parent ACL\n" ));
}
#endif // DBG
Status = STATUS_SUCCESS;
goto Cleanup;
}
if ( !NT_SUCCESS(Status) ) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("Can't build inherited ACL %lX\n", Status ));
}
#endif // DBG
goto Cleanup;
}
//
// Allocate a work buffer describing the ChildAcl
//
#ifdef NTOS_KERNEL_RUNTIME
ChildAceInfo = ExAllocatePoolWithTag(
PagedPool,
ChildAcl->AceCount * sizeof(ACE_INFO),
'cAeS' );
#else // NTOS_KERNEL_RUNTIME
ChildAceInfo = RtlAllocateHeap(
HeapHandle,
MAKE_TAG(SE_TAG),
ChildAcl->AceCount * sizeof(ACE_INFO) );
#endif // NTOS_KERNEL_RUNTIME
if (ChildAceInfo == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
for (ChildAceIndex = 0, ChildAce = FirstAce(ChildAcl);
ChildAceIndex < ChildAcl->AceCount;
ChildAceIndex += 1, ChildAce = NextAce(ChildAce)) {
ACCESS_MASK LocalMask;
ULONG ChildAceFlags;
if ( !IsV4AceType(ChildAce) || IsCompoundAceType(ChildAce)) {
*NewGenericControl |= SEP_ACL_PROTECTED;
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("Inherited Ace type (%ld) not known\n", ChildAce->Header.AceType ));
}
#endif // DBG
Status = STATUS_SUCCESS;
goto Cleanup;
}
//
// Compute the generic mapped mask for use in all comparisons. The
// generic mapping will be undone if needed later.
//
// All V4 aces have an access mask in the same location.
//
LocalMask = ((PKNOWN_ACE)(ChildAce))->Mask;
RtlApplyGenericMask( ChildAce, &LocalMask, GenericMapping);
//
// Break the ACE into its component parts.
//
ChildAceFlags = AceFlagsInAce( ChildAce );
if ( ChildAceFlags & CONTAINER_INHERIT_ACE ) {
ChildAceInfo[ChildAceIndex].OriginalContainerInheritMask = LocalMask;
ChildAceInfo[ChildAceIndex].ContainerInheritMask = LocalMask;
} else {
ChildAceInfo[ChildAceIndex].OriginalContainerInheritMask = 0;
ChildAceInfo[ChildAceIndex].ContainerInheritMask = 0;
}
if ( ChildAceFlags & OBJECT_INHERIT_ACE ) {
ChildAceInfo[ChildAceIndex].OriginalObjectInheritMask = LocalMask;
ChildAceInfo[ChildAceIndex].ObjectInheritMask = LocalMask;
} else {
ChildAceInfo[ChildAceIndex].OriginalObjectInheritMask = 0;
ChildAceInfo[ChildAceIndex].ObjectInheritMask = 0;
}
if ( ChildAceFlags & EFFECTIVE_ACE ) {
ChildAceInfo[ChildAceIndex].OriginalEffectiveMask = LocalMask;
ChildAceInfo[ChildAceIndex].EffectiveMask = LocalMask;
} else {
ChildAceInfo[ChildAceIndex].OriginalEffectiveMask = 0;
ChildAceInfo[ChildAceIndex].EffectiveMask = 0;
}
}
//
// Walk through the computed inherited ACL one ACE at a time.
//
for (InheritedAceIndex = 0, InheritedAce = FirstAce(InheritedAcl);
InheritedAceIndex < InheritedAcl->AceCount;
InheritedAceIndex += 1, InheritedAce = NextAce(InheritedAce)) {
ACCESS_MASK LocalMask;
//
// If the ACE isn't a valid version 4 ACE,
// this isn't an ACL we're interested in handling.
//
if ( !IsV4AceType(InheritedAce) || IsCompoundAceType(InheritedAce)) {
*NewGenericControl |= SEP_ACL_PROTECTED;
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("Inherited Ace type (%ld) not known\n", InheritedAce->Header.AceType ));
}
#endif // DBG
Status = STATUS_SUCCESS;
goto Cleanup;
}
//
// Compute the generic mapped mask for use in all comparisons. The
// generic mapping will be undone if needed later.
//
// All V4 aces have an access mask in the same location.
//
LocalMask = ((PKNOWN_ACE)(InheritedAce))->Mask;
RtlApplyGenericMask( InheritedAce, &LocalMask, GenericMapping);
if ( LocalMask == 0 ) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("Worthless INH ACE: %ld 0x%8.8lx\n", InheritedAceIndex, LocalMask ));
}
#endif // DBG
continue;
}
//
// This ACE is some combination of an effective ACE, a container
// inherit ACE and an object inherit ACE. Process each of those
// attributes separately since they might be represented separately
// in the ChildAcl.
//
InheritedAceFlags = AceFlagsInAce( InheritedAce );
if ( InheritedAceFlags == 0 ) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("Worthless INH ACE: %ld 0x%lx\n", InheritedAceIndex, InheritedAceFlags ));
}
#endif // DBG
continue;
}
if ( InheritedAceFlags & CONTAINER_INHERIT_ACE ) {
OriginalInheritedContainerInheritMask = InheritedContainerInheritMask = LocalMask;
} else {
OriginalInheritedContainerInheritMask = InheritedContainerInheritMask = 0;
}
if ( InheritedAceFlags & OBJECT_INHERIT_ACE ) {
OriginalInheritedObjectInheritMask = InheritedObjectInheritMask = LocalMask;
} else {
OriginalInheritedObjectInheritMask = InheritedObjectInheritMask = 0;
}
if ( InheritedAceFlags & EFFECTIVE_ACE ) {
OriginalInheritedEffectiveMask = InheritedEffectiveMask = LocalMask;
} else {
OriginalInheritedEffectiveMask = InheritedEffectiveMask = 0;
}
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("Doing INH ACE: %ld %8.8lX %8.8lX %8.8lX\n", InheritedAceIndex, InheritedEffectiveMask, InheritedContainerInheritMask, InheritedObjectInheritMask ));
}
#endif // DBG
//
// Loop through the entire child ACL comparing each inherited ACE with
// each child ACE. Don't stop simply because we've matched once.
// Multiple ACEs in the one ACL may have been condensed into a single ACE
// in the other ACL in any combination (by any of our friendly ACL editors).
// In all cases, it is better to compute a resultant auto inherited ACL
// than it is to compute a protected ACL.
//
for (ChildAceIndex = 0, ChildAce = FirstAce(ChildAcl);
ChildAceIndex < ChildAcl->AceCount;
ChildAceIndex += 1, ChildAce = NextAce(ChildAce)) {
//
// Ensure the ACE represents the same principal and object,
//
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("Compare Child Ace: %ld ", ChildAceIndex ));
}
#endif // DBG
if ( !RtlpCompareAces( InheritedAce,
ChildAce,
OwnerSid,
GroupSid ) ) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("\n" ));
}
#endif // DBG
continue;
}
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("\n" ));
}
#endif // DBG
//
// Match as many access bits in the INH ACE as possible.
//
// Don't pay any attention to whether the bits have been previously matched
// in the CHILD ACE. To do so, would imply that there is a one-to-one
// correspondance between bits in the INH ACL and Child ACL. Unfortunately,
// ACL editors feel free to compress out duplicate bits in both
// the CHILD ACL and PARENT ACL as they see fit.
//
InheritedEffectiveMask &= ~ChildAceInfo[ChildAceIndex].OriginalEffectiveMask;
InheritedContainerInheritMask &= ~ChildAceInfo[ChildAceIndex].OriginalContainerInheritMask;
InheritedObjectInheritMask &= ~ChildAceInfo[ChildAceIndex].OriginalObjectInheritMask;
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("New INH MASKs %ld %8.8lX %8.8lX %8.8lX\n", InheritedAceIndex, InheritedEffectiveMask, InheritedContainerInheritMask, InheritedObjectInheritMask ));
}
#endif // DBG
//
// Match as many access bits in the child ACE as possible.
//
// Same reasoning as above.
//
ChildAceInfo[ChildAceIndex].EffectiveMask &= ~OriginalInheritedEffectiveMask;
ChildAceInfo[ChildAceIndex].ContainerInheritMask &= ~OriginalInheritedContainerInheritMask;
ChildAceInfo[ChildAceIndex].ObjectInheritMask &= ~OriginalInheritedObjectInheritMask;
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("New Child MASKs %ld %8.8lX %8.8lX %8.8lX\n", ChildAceIndex, ChildAceInfo[ChildAceIndex].EffectiveMask, ChildAceInfo[ChildAceIndex].ContainerInheritMask, ChildAceInfo[ChildAceIndex].ObjectInheritMask ));
}
#endif // DBG
}
//
// If we couldn't process this inherited ACE,
// then the child ACL wasn't inherited.
//
if ( (InheritedEffectiveMask | InheritedContainerInheritMask | InheritedObjectInheritMask) != 0 ) {
*NewGenericControl |= SEP_ACL_PROTECTED;
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("INH ACE not completely matched: %ld %8.8lX %8.8lX %8.8lX\n", InheritedAceIndex, InheritedEffectiveMask, InheritedContainerInheritMask, InheritedObjectInheritMask ));
}
#endif // DBG
Status = STATUS_SUCCESS;
goto Cleanup;
}
}
//
// ASSERT: All of the inherited ACEs have been processed.
//
//
// Loop through the Child ACL ensuring we can build a valid auto inherited ACL
//
InheritedAllowFound = FALSE;
InheritedDenyFound = FALSE;
NonInheritedAclSize = 0;
for (ChildAceIndex = 0, ChildAce = FirstAce(ChildAcl);
ChildAceIndex < ChildAcl->AceCount;
ChildAceIndex += 1, ChildAce = NextAce(ChildAce)) {
ACCESS_MASK ResultantMask;
//
// Any Child ACE access bits not eliminated above required than an
// explicit non-inherited ACE by built. That ACE will have an
// access mask that is the combined access mask of the unmatched bit
// in the effective, container inherit, and object inherit categories.
// Even though, the combined mask may include access bits not absolutely
// required (since they were already inherited), this strategy prevents
// us from having to build multiple ACEs (one for each category) for this
// single ACE.
//
ResultantMask =
ChildAceInfo[ChildAceIndex].EffectiveMask |
ChildAceInfo[ChildAceIndex].ContainerInheritMask |
ChildAceInfo[ChildAceIndex].ObjectInheritMask;
//
// Handle an inherited ACE
//
if ( ResultantMask == 0 ) {
//
// Keep track of whether inherited "allow" and "deny" ACEs are found.
//
if ( RtlBaseAceType[ChildAce->Header.AceType] == ACCESS_ALLOWED_ACE_TYPE ) {
InheritedAllowFound = TRUE;
}
if ( RtlBaseAceType[ChildAce->Header.AceType] == ACCESS_DENIED_ACE_TYPE ) {
InheritedDenyFound = TRUE;
}
//
// Handle a non-inherited ACE
//
} else {
//
// Keep a running tab of the size of the non-inherited ACEs.
//
NonInheritedAclSize += ChildAce->Header.AceSize;
//
// Since non-inherited ACEs will be moved to the front of the ACL,
// we have to be careful that we don't move a deny ACE in front of a
// previously found inherited allow ACE (and vice-versa). To do so would
// change the semantics of the ACL.
//
if ( RtlBaseAceType[ChildAce->Header.AceType] == ACCESS_ALLOWED_ACE_TYPE && InheritedDenyFound ) {
*NewGenericControl |= SEP_ACL_PROTECTED;
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("Previous deny found Child ACE: %ld\n", ChildAceIndex ));
}
#endif // DBG
Status = STATUS_SUCCESS;
goto Cleanup;
}
if ( RtlBaseAceType[ChildAce->Header.AceType] == ACCESS_DENIED_ACE_TYPE && InheritedAllowFound ) {
*NewGenericControl |= SEP_ACL_PROTECTED;
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("Previous allow found Child ACE: %ld\n", ChildAceIndex ));
}
#endif // DBG
Status = STATUS_SUCCESS;
goto Cleanup;
}
}
}
//
// The resultant ACL is composed of the non-inherited ACEs followed by
// the inherited ACE. The inherited ACEs are built by running the
// inheritance algorithm over the Parent ACL.
//
// The Inherited ACL computed below is almost identical to InhertedAcl.
// However, InheritedAcl didn't properly substitute the correct owner and
// group SID.
//
Status = RtlpInheritAcl (
ParentAcl,
NULL, // No explicit child ACL
0, // No Child Generic Control
IsDirectoryObject,
TRUE, // AutoInherit the DACL
FALSE, // Not default descriptor for object
OwnerSid, // Subsitute a constant SID
GroupSid, // Subsitute a constant SID
OwnerSid, // Server Owner (Technically incorrect, but OK since we don't support compound ACEs)
GroupSid, // Server Group
GenericMapping,
TRUE, // Is a SACL
ObjectType ? &ObjectType : NULL,
ObjectType ? 1 : 0,
&RealInheritedAcl,
&AclExplicitlyAssigned,
&GenericControl );
if ( !NT_SUCCESS(Status) ) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("Can't build real inherited ACL %lX\n", Status ));
}
#endif // DBG
goto Cleanup;
}
//
// Allocate a buffer for the inherited ACL
//
#ifdef NTOS_KERNEL_RUNTIME
*NewAcl = ExAllocatePoolWithTag(
PagedPool,
RealInheritedAcl->AclSize + NonInheritedAclSize,
'cAeS' );
#else // NTOS_KERNEL_RUNTIME
*NewAcl = RtlAllocateHeap(
HeapHandle,
MAKE_TAG(SE_TAG),
RealInheritedAcl->AclSize + NonInheritedAclSize );
#endif // NTOS_KERNEL_RUNTIME
if ( *NewAcl == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
//
// All non-inherited ACEs are copied first.
// The inherited ACES are grabbed from real inherited ACL.
//
// Build an ACL Header.
//
Status = RtlCreateAcl( *NewAcl,
RealInheritedAcl->AclSize + NonInheritedAclSize,
max( RealInheritedAcl->AclRevision, ChildAcl->AclRevision ) );
if ( !NT_SUCCESS(Status) ) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("Can't create final ACL %lX\n", Status ));
}
#endif // DBG
//
// The only reason for failure would be if the combined ACL is too large.
// So just create a protected ACL (better than a failure).
//
*NewGenericControl |= SEP_ACL_PROTECTED;
Status = STATUS_SUCCESS;
goto Cleanup;
}
//
// Copy the non-inherited ACES.
//
Where = ((PUCHAR)(*NewAcl)) + sizeof(ACL);
for (ChildAceIndex = 0, ChildAce = FirstAce(ChildAcl);
ChildAceIndex < ChildAcl->AceCount;
ChildAceIndex += 1, ChildAce = NextAce(ChildAce)) {
ACCESS_MASK ResultantMask;
//
// Copy the non-inherited ACE from the Child only if there's a non-zero access mask.
//
ResultantMask =
ChildAceInfo[ChildAceIndex].EffectiveMask |
ChildAceInfo[ChildAceIndex].ContainerInheritMask |
ChildAceInfo[ChildAceIndex].ObjectInheritMask;
if ( ResultantMask != 0 ) {
PKNOWN_ACE NewAce;
ULONG GenericBitToTry;
//
// Use the original ChildAce as the template.
//
RtlCopyMemory( Where, ChildAce, ChildAce->Header.AceSize );
NewAce = (PKNOWN_ACE)Where;
NewAce->Header.AceFlags &= ~INHERITED_ACE; // Clear stray bits
Where += ChildAce->Header.AceSize;
(*NewAcl)->AceCount ++;
//
// The AccessMask on the ACE are those access bits that didn't get matched
// by inherited ACEs.
//
NewAce->Mask = ChildAce->Mask & ResultantMask;
ResultantMask &= ~ChildAce->Mask;
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("Original non-inherited: %ld %8.8lX %8.8lX\n", ChildAceIndex, NewAce->Mask, ResultantMask ));
}
#endif // DBG
//
// Map any remaining bits back to generic access bits.
// Doing so might expand the ResultantMask to beyond what was computed above.
// Doing so will never expand the computed ACE to beyond what the original
// ChildAce granted.
//
ASSERT( GENERIC_WRITE == (GENERIC_READ >> 1));
ASSERT( GENERIC_EXECUTE == (GENERIC_WRITE >> 1));
ASSERT( GENERIC_ALL == (GENERIC_EXECUTE >> 1));
GenericBitToTry = GENERIC_READ;
while ( ResultantMask && GenericBitToTry >= GENERIC_ALL ) {
//
// Only map generic bits that are in the ChildAce.
//
if ( GenericBitToTry & ChildAce->Mask ) {
ACCESS_MASK GenericMask;
//
// Compute the real access mask corresponding to the Generic bit.
//
GenericMask = GenericBitToTry;
RtlMapGenericMask( &GenericMask, GenericMapping );
//
// If the current generic bit matches any of the bits remaining,
// set the generic bit in the current ACE.
//
if ( (ResultantMask & GenericMask) != 0 ) {
NewAce->Mask |= GenericBitToTry;
ResultantMask &= ~GenericMask;
}
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("Generic non-inherited: %ld %8.8lX %8.8lX\n", ChildAceIndex, NewAce->Mask, ResultantMask ));
}
#endif // DBG
}
//
// Try the next Generic bit.
//
GenericBitToTry >>= 1;
}
//
// This is really an internal error, but press on regardless.
//
ASSERT(ResultantMask == 0 );
NewAce->Mask |= ResultantMask;
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("Final non-inherited: %ld %8.8lX %8.8lX\n", ChildAceIndex, NewAce->Mask, ResultantMask ));
}
#endif // DBG
}
}
//
// Copy the inherited ACES.
// Simply copy computed Inherited ACL.
//
RtlCopyMemory( Where,
FirstAce(RealInheritedAcl),
RealInheritedAcl->AclSize - (ULONG)(((PUCHAR)FirstAce(RealInheritedAcl)) - (PUCHAR)RealInheritedAcl));
Where += RealInheritedAcl->AclSize - (ULONG)(((PUCHAR)FirstAce(RealInheritedAcl)) - (PUCHAR)RealInheritedAcl);
(*NewAcl)->AceCount += RealInheritedAcl->AceCount;
ASSERT( (*NewAcl)->AclSize == Where - (PUCHAR)(*NewAcl) );
Status = STATUS_SUCCESS;
Cleanup:
//
// If successful,
// build the resultant autoinherited ACL.
//
if ( NT_SUCCESS(Status) ) {
//
// If the Child ACL is protected,
// just build it as a copy of the original ACL
//
if ( *NewGenericControl & SEP_ACL_PROTECTED ) {
//
// If we've already allocated a new ACL (and couldn't finish it for some reason),
// free it.
if ( *NewAcl != NULL) {
#ifdef NTOS_KERNEL_RUNTIME
ExFreePool( *NewAcl );
#else // NTOS_KERNEL_RUNTIME
RtlFreeHeap( HeapHandle, 0, *NewAcl );
#endif // NTOS_KERNEL_RUNTIME
*NewAcl = NULL;
}
//
// Allocate a buffer for the protected ACL.
//
#ifdef NTOS_KERNEL_RUNTIME
*NewAcl = ExAllocatePoolWithTag(
PagedPool,
ChildAcl->AclSize,
'cAeS' );
#else // NTOS_KERNEL_RUNTIME
*NewAcl = RtlAllocateHeap(
HeapHandle,
MAKE_TAG(SE_TAG),
ChildAcl->AclSize );
#endif // NTOS_KERNEL_RUNTIME
if ( *NewAcl == NULL ) {
Status = STATUS_NO_MEMORY;
} else {
RtlCopyMemory( *NewAcl, ChildAcl, ChildAcl->AclSize );
}
}
}
if ( ChildAceInfo != NULL) {
#ifdef NTOS_KERNEL_RUNTIME
ExFreePool( ChildAceInfo );
#else // NTOS_KERNEL_RUNTIME
RtlFreeHeap( HeapHandle, 0, ChildAceInfo );
#endif // NTOS_KERNEL_RUNTIME
}
if ( InheritedAcl != NULL) {
#ifdef NTOS_KERNEL_RUNTIME
ExFreePool( InheritedAcl );
#else // NTOS_KERNEL_RUNTIME
RtlFreeHeap( HeapHandle, 0, InheritedAcl );
#endif // NTOS_KERNEL_RUNTIME
}
if ( RealInheritedAcl != NULL) {
#ifdef NTOS_KERNEL_RUNTIME
ExFreePool( RealInheritedAcl );
#else // NTOS_KERNEL_RUNTIME
RtlFreeHeap( HeapHandle, 0, RealInheritedAcl );
#endif // NTOS_KERNEL_RUNTIME
}
return Status;
}
BOOLEAN
RtlpIsDuplicateAce(
IN PACL Acl,
IN PKNOWN_ACE NewAce
)
/*++
Routine Description:
This routine determine if an ACE is a duplicate of an ACE already in an
ACL. If so, the NewAce can be removed from the end of the ACL.
This routine currently only detects duplicate version 4 ACEs. If the
ACE isn't version 4, the ACE will be declared to be a non-duplicate.
This routine only detects duplicate INHERTED ACEs.
Arguments:
Acl - Existing ACL
NewAce - Ace to determine if it is already in Acl.
NewAce is expected to be the last ACE in "Acl".
Return Value:
TRUE - NewAce is a duplicate of another ACE on the Acl
FALSE - NewAce is NOT a duplicate of another ACE on the Acl
--*/
{
NTSTATUS Status;
BOOLEAN RetVal = FALSE;
LONG AceIndex;
ACCESS_MASK NewAceContainerInheritMask;
ACCESS_MASK NewAceObjectInheritMask;
ACCESS_MASK NewAceEffectiveMask;
ACCESS_MASK LocalMask;
PKNOWN_ACE AceFromAcl;
RTL_PAGED_CODE();
//
// Ensure the passed in ACE is one this routine understands
//
if ( !IsV4AceType(NewAce) || IsCompoundAceType(NewAce)) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("New Ace type (%ld) not known\n", NewAce->Header.AceType ));
}
#endif // DBG
RetVal = FALSE;
goto Cleanup;
}
//
// This routine only works for ACEs marked as INHERITED.
//
if ( (NewAce->Header.AceFlags & INHERITED_ACE ) == 0 ) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("New Ace type isn't inherited\n" ));
}
#endif // DBG
RetVal = FALSE;
goto Cleanup;
}
//
// Break the new ACE into its component parts.
//
// All V4 aces have an access mask in the same location.
//
LocalMask = ((PKNOWN_ACE)(NewAce))->Mask;
if ( NewAce->Header.AceFlags & CONTAINER_INHERIT_ACE ) {
NewAceContainerInheritMask = LocalMask;
} else {
NewAceContainerInheritMask = 0;
}
if ( NewAce->Header.AceFlags & OBJECT_INHERIT_ACE ) {
NewAceObjectInheritMask = LocalMask;
} else {
NewAceObjectInheritMask = 0;
}
if ( (NewAce->Header.AceFlags & INHERIT_ONLY_ACE) == 0 ) {
NewAceEffectiveMask = LocalMask;
} else {
NewAceEffectiveMask = 0;
}
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("Starting MASKs: %8.8lX %8.8lX %8.8lX", NewAceEffectiveMask, NewAceContainerInheritMask, NewAceObjectInheritMask ));
}
#endif // DBG
//
// Walk through the ACL one ACE at a time.
//
for (AceIndex = 0, AceFromAcl = FirstAce(Acl);
AceIndex < Acl->AceCount-1; // NewAce is the last ACE
AceIndex += 1, AceFromAcl = NextAce(AceFromAcl)) {
//
// If the ACE isn't a valid version 4 ACE,
// this isn't an ACE we're interested in handling.
//
if ( !IsV4AceType(AceFromAcl) || IsCompoundAceType(AceFromAcl)) {
continue;
}
//
// This routine only works for ACEs marked as INHERITED.
//
if ( (AceFromAcl->Header.AceFlags & INHERITED_ACE ) == 0 ) {
continue;
}
//
// Compare the Ace from the ACL with the New ACE
//
// Don't stop simply because we've matched once.
// Multiple ACEs in the one ACL may have been condensed into a single ACE
// in the other ACL in any combination (by any of our friendly ACL editors).
//
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("Compare Ace: %ld ", AceIndex ));
}
#endif // DBG
if ( RtlpCompareAces( AceFromAcl,
NewAce,
NULL,
NULL ) ) {
//
// Match the bits from the current ACE with bits from the New ACE.
//
// All V4 aces have an access mask in the same location.
//
LocalMask = ((PKNOWN_ACE)(AceFromAcl))->Mask;
if ( AceFromAcl->Header.AceFlags & CONTAINER_INHERIT_ACE ) {
NewAceContainerInheritMask &= ~LocalMask;
}
if ( AceFromAcl->Header.AceFlags & OBJECT_INHERIT_ACE ) {
NewAceObjectInheritMask &= ~LocalMask;
}
if ( (AceFromAcl->Header.AceFlags & INHERIT_ONLY_ACE) == 0 ) {
NewAceEffectiveMask &= ~LocalMask;
}
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("Remaining MASKs: %8.8lX %8.8lX %8.8lX", NewAceEffectiveMask, NewAceContainerInheritMask, NewAceObjectInheritMask ));
}
#endif // DBG
//
// If all bits have been matched in the New Ace,
// then this is a duplicate ACE.
//
if ( (NewAceEffectiveMask | NewAceContainerInheritMask | NewAceObjectInheritMask) == 0 ) {
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("\n"));
}
#endif // DBG
RetVal = TRUE;
goto Cleanup;
}
}
#if DBG
if ( RtlpVerboseConvert ) {
KdPrint(("\n"));
}
#endif // DBG
}
//
// All of the ACEs of the ACL have been processed.
//
// We haven't matched all of the bits in the New Ace so this is not a duplicate ACE.
//
RetVal = FALSE;
Cleanup:
return RetVal;
}
NTSTATUS
RtlpCreateServerAcl(
IN PACL Acl,
IN BOOLEAN AclUntrusted,
IN PSID ServerSid,
OUT PACL *ServerAcl,
OUT BOOLEAN *ServerAclAllocated
)
/*++
Routine Description:
This routine takes an ACL and converts it into a server ACL.
Currently, that means converting all of the GRANT ACEs into
Compount Grants, and if necessary sanitizing any Compound
Grants that are encountered.
Arguments:
Return Value:
--*/
{
USHORT RequiredSize = sizeof(ACL);
USHORT AceSizeAdjustment;
USHORT ServerSidSize;
PACE_HEADER Ace;
ULONG i;
PVOID Target;
PVOID AcePosition;
PSID UntrustedSid;
PSID ClientSid;
NTSTATUS Status;
RTL_PAGED_CODE();
if (Acl == NULL) {
*ServerAclAllocated = FALSE;
*ServerAcl = NULL;
return( STATUS_SUCCESS );
}
AceSizeAdjustment = sizeof( KNOWN_COMPOUND_ACE ) - sizeof( KNOWN_ACE );
ASSERT( sizeof( KNOWN_COMPOUND_ACE ) >= sizeof( KNOWN_ACE ) );
ServerSidSize = (USHORT)SeLengthSid( ServerSid );
//
// Do this in two passes. First, determine how big the final
// result is going to be, and then allocate the space and make
// the changes.
//
for (i = 0, Ace = FirstAce(Acl);
i < Acl->AceCount;
i += 1, Ace = NextAce(Ace)) {
//
// If it's an ACCESS_ALLOWED_ACE_TYPE, we'll need to add in the
// size of the Server SID.
//
if (Ace->AceType == ACCESS_ALLOWED_ACE_TYPE) {
//
// Simply add the size of the new Server SID plus whatever
// adjustment needs to be made to increase the size of the ACE.
//
RequiredSize += ( ServerSidSize + AceSizeAdjustment );
} else {
if (AclUntrusted && Ace->AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE ) {
//
// Since the Acl is untrusted, we don't care what is in the
// server SID, we're going to replace it.
//
UntrustedSid = RtlCompoundAceServerSid( Ace );
if ((USHORT)SeLengthSid(UntrustedSid) > ServerSidSize) {
RequiredSize += ((USHORT)SeLengthSid(UntrustedSid) - ServerSidSize);
} else {
RequiredSize += (ServerSidSize - (USHORT)SeLengthSid(UntrustedSid));
}
}
}
RequiredSize += Ace->AceSize;
}
#ifdef NTOS_KERNEL_RUNTIME
(*ServerAcl) = (PACL)ExAllocatePoolWithTag( PagedPool, RequiredSize, 'cAeS' );
#else // NTOS_KERNEL_RUNTIME
(*ServerAcl) = (PACL)RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( SE_TAG ), RequiredSize );
#endif // NTOS_KERNEL_RUNTIME
if ((*ServerAcl) == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Mark as allocated so caller knows to free it.
//
*ServerAclAllocated = TRUE;
Status = RtlCreateAcl( (*ServerAcl), RequiredSize, ACL_REVISION3 );
ASSERT( NT_SUCCESS( Status ));
for (i = 0, Ace = FirstAce(Acl), Target=FirstAce( *ServerAcl );
i < Acl->AceCount;
i += 1, Ace = NextAce(Ace)) {
//
// If it's an ACCESS_ALLOWED_ACE_TYPE, convert to a Server ACE.
//
if (Ace->AceType == ACCESS_ALLOWED_ACE_TYPE ||
(AclUntrusted && Ace->AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE )) {
AcePosition = Target;
if (Ace->AceType == ACCESS_ALLOWED_ACE_TYPE) {
ClientSid = &((PKNOWN_ACE)Ace)->SidStart;
} else {
ClientSid = RtlCompoundAceClientSid( Ace );
}
//
// Copy up to the access mask.
//
RtlCopyMemory(
Target,
Ace,
FIELD_OFFSET(KNOWN_ACE, SidStart)
);
//
// Now copy the correct Server SID
//
Target = ((PCHAR)Target + (UCHAR)(FIELD_OFFSET(KNOWN_COMPOUND_ACE, SidStart)));
RtlCopyMemory(
Target,
ServerSid,
SeLengthSid(ServerSid)
);
Target = ((PCHAR)Target + (UCHAR)SeLengthSid(ServerSid));
//
// Now copy in the correct client SID. We can copy this right out of
// the original ACE.
//
RtlCopyMemory(
Target,
ClientSid,
SeLengthSid(ClientSid)
);
Target = ((PCHAR)Target + SeLengthSid(ClientSid));
//
// Set the size of the ACE accordingly
//
((PKNOWN_COMPOUND_ACE)AcePosition)->Header.AceSize =
(USHORT)FIELD_OFFSET(KNOWN_COMPOUND_ACE, SidStart) +
(USHORT)SeLengthSid(ServerSid) +
(USHORT)SeLengthSid(ClientSid);
//
// Set the type
//
((PKNOWN_COMPOUND_ACE)AcePosition)->Header.AceType = ACCESS_ALLOWED_COMPOUND_ACE_TYPE;
((PKNOWN_COMPOUND_ACE)AcePosition)->CompoundAceType = COMPOUND_ACE_IMPERSONATION;
} else {
//
// Just copy the ACE as is.
//
RtlCopyMemory( Target, Ace, Ace->AceSize );
Target = ((PCHAR)Target + Ace->AceSize);
}
}
(*ServerAcl)->AceCount = Acl->AceCount;
return( STATUS_SUCCESS );
}
#ifndef NTOS_KERNEL_RUNTIME
NTSTATUS
RtlpGetDefaultsSubjectContext(
HANDLE ClientToken,
OUT PTOKEN_OWNER *OwnerInfo,
OUT PTOKEN_PRIMARY_GROUP *GroupInfo,
OUT PTOKEN_DEFAULT_DACL *DefaultDaclInfo,
OUT PTOKEN_OWNER *ServerOwner,
OUT PTOKEN_PRIMARY_GROUP *ServerGroup
)
{
HANDLE PrimaryToken;
PVOID HeapHandle;
NTSTATUS Status;
ULONG ServerGroupInfoSize;
ULONG ServerOwnerInfoSize;
ULONG TokenDaclInfoSize;
ULONG TokenGroupInfoSize;
ULONG TokenOwnerInfoSize;
BOOLEAN ClosePrimaryToken = FALSE;
*OwnerInfo = NULL;
*GroupInfo = NULL;
*DefaultDaclInfo = NULL;
*ServerOwner = NULL;
*ServerGroup = NULL;
HeapHandle = RtlProcessHeap();
//
// If the caller doesn't know the client token,
// simply don't return any information.
//
if ( ClientToken != NULL ) {
//
// Obtain the default owner from the client.
//
Status = NtQueryInformationToken(
ClientToken, // Handle
TokenOwner, // TokenInformationClass
NULL, // TokenInformation
0, // TokenInformationLength
&TokenOwnerInfoSize // ReturnLength
);
if ( STATUS_BUFFER_TOO_SMALL != Status ) {
goto Cleanup;
}
*OwnerInfo = RtlAllocateHeap( HeapHandle, MAKE_TAG( SE_TAG ), TokenOwnerInfoSize );
if ( *OwnerInfo == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
Status = NtQueryInformationToken(
ClientToken, // Handle
TokenOwner, // TokenInformationClass
*OwnerInfo, // TokenInformation
TokenOwnerInfoSize, // TokenInformationLength
&TokenOwnerInfoSize // ReturnLength
);
if (!NT_SUCCESS( Status )) {
goto Cleanup;
}
//
// Obtain the default group from the client token.
//
Status = NtQueryInformationToken(
ClientToken, // Handle
TokenPrimaryGroup, // TokenInformationClass
*GroupInfo, // TokenInformation
0, // TokenInformationLength
&TokenGroupInfoSize // ReturnLength
);
if ( STATUS_BUFFER_TOO_SMALL != Status ) {
goto Cleanup;
}
*GroupInfo = RtlAllocateHeap( HeapHandle, MAKE_TAG( SE_TAG ), TokenGroupInfoSize );
if ( *GroupInfo == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
Status = NtQueryInformationToken(
ClientToken, // Handle
TokenPrimaryGroup, // TokenInformationClass
*GroupInfo, // TokenInformation
TokenGroupInfoSize, // TokenInformationLength
&TokenGroupInfoSize // ReturnLength
);
if (!NT_SUCCESS( Status )) {
goto Cleanup;
}
Status = NtQueryInformationToken(
ClientToken, // Handle
TokenDefaultDacl, // TokenInformationClass
*DefaultDaclInfo, // TokenInformation
0, // TokenInformationLength
&TokenDaclInfoSize // ReturnLength
);
if ( STATUS_BUFFER_TOO_SMALL != Status ) {
goto Cleanup;
}
*DefaultDaclInfo = RtlAllocateHeap( HeapHandle, MAKE_TAG( SE_TAG ), TokenDaclInfoSize );
if ( *DefaultDaclInfo == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
Status = NtQueryInformationToken(
ClientToken, // Handle
TokenDefaultDacl, // TokenInformationClass
*DefaultDaclInfo, // TokenInformation
TokenDaclInfoSize, // TokenInformationLength
&TokenDaclInfoSize // ReturnLength
);
if (!NT_SUCCESS( Status )) {
goto Cleanup;
}
}
//
// Now open the primary token to determine how to substitute for
// ServerOwner and ServerGroup.
//
Status = NtOpenProcessToken(
NtCurrentProcess(),
TOKEN_QUERY,
&PrimaryToken
);
if (!NT_SUCCESS( Status )) {
ClosePrimaryToken = FALSE;
goto Cleanup;
} else {
ClosePrimaryToken = TRUE;
}
Status = NtQueryInformationToken(
PrimaryToken, // Handle
TokenOwner, // TokenInformationClass
NULL, // TokenInformation
0, // TokenInformationLength
&ServerOwnerInfoSize // ReturnLength
);
if ( STATUS_BUFFER_TOO_SMALL != Status ) {
goto Cleanup;
}
*ServerOwner = RtlAllocateHeap( HeapHandle, MAKE_TAG( SE_TAG ), ServerOwnerInfoSize );
if ( *ServerOwner == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
Status = NtQueryInformationToken(
PrimaryToken, // Handle
TokenOwner, // TokenInformationClass
*ServerOwner, // TokenInformation
ServerOwnerInfoSize, // TokenInformationLength
&ServerOwnerInfoSize // ReturnLength
);
if (!NT_SUCCESS( Status )) {
goto Cleanup;
}
//
// Find the server group.
//
Status = NtQueryInformationToken(
PrimaryToken, // Handle
TokenPrimaryGroup, // TokenInformationClass
*ServerGroup, // TokenInformation
0, // TokenInformationLength
&ServerGroupInfoSize // ReturnLength
);
if ( STATUS_BUFFER_TOO_SMALL != Status ) {
goto Cleanup;
}
*ServerGroup = RtlAllocateHeap( HeapHandle, MAKE_TAG( SE_TAG ), ServerGroupInfoSize );
if ( *ServerGroup == NULL ) {
goto Cleanup;
}
Status = NtQueryInformationToken(
PrimaryToken, // Handle
TokenPrimaryGroup, // TokenInformationClass
*ServerGroup, // TokenInformation
ServerGroupInfoSize, // TokenInformationLength
&ServerGroupInfoSize // ReturnLength
);
if (!NT_SUCCESS( Status )) {
goto Cleanup;
}
NtClose( PrimaryToken );
return( STATUS_SUCCESS );
Cleanup:
if (*OwnerInfo != NULL) {
RtlFreeHeap( HeapHandle, 0, (PVOID)*OwnerInfo );
*OwnerInfo = NULL;
}
if (*GroupInfo != NULL) {
RtlFreeHeap( HeapHandle, 0, (PVOID)*GroupInfo );
*GroupInfo = NULL;
}
if (*DefaultDaclInfo != NULL) {
RtlFreeHeap( HeapHandle, 0, (PVOID)*DefaultDaclInfo );
*DefaultDaclInfo = NULL;
}
if (*ServerOwner != NULL) {
RtlFreeHeap( HeapHandle, 0, (PVOID)*ServerOwner );
*ServerOwner = NULL;
}
if (*ServerGroup != NULL) {
RtlFreeHeap( HeapHandle, 0, (PVOID)*ServerGroup );
*ServerGroup = NULL;
}
if (ClosePrimaryToken == TRUE) {
NtClose( PrimaryToken );
}
return( Status );
}
#endif // NTOS_KERNEL_RUNTIME
NTSTATUS
RtlpNewSecurityObject (
IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL,
IN PSECURITY_DESCRIPTOR CreatorDescriptor OPTIONAL,
OUT PSECURITY_DESCRIPTOR * NewDescriptor,
IN GUID **pObjectType OPTIONAL,
IN ULONG GuidCount,
IN BOOLEAN IsDirectoryObject,
IN ULONG AutoInheritFlags,
IN HANDLE Token OPTIONAL,
IN PGENERIC_MAPPING GenericMapping
)
/*++
Routine Description:
The procedure is used to allocate and initialize a self-relative
Security Descriptor for a new protected server's object. It is called
when a new protected server object is being created. The generated
security descriptor will be in self-relative form.
This procedure, called only from user mode, is used to establish a
security descriptor for a new protected server's object. Memory is
allocated to hold each of the security descriptor's components (using
NtAllocateVirtualMemory()). The final security descriptor generated by
this procedure is produced according to the rules stated in ???
System and Discretionary ACL Assignment
---------------------------------------
The assignment of system and discretionary ACLs is governed by the
logic illustrated in the following table:
| Explicit | Explicit |
| (non-default) | Default | No
| Acl | Acl | Acl
| Specified | Specified | Specified
-------------+----------------+---------------+--------------
| | |
Inheritable | Assign | Assign | Assign
Acl From | Specified | Inherited | Inherited
Parent | Acl(1)(2) | Acl | Acl
| | |
-------------+----------------+---------------+--------------
No | | |
Inheritable | Assign | Assign | Assign
Acl From | Specified | Default | No Acl
Parent | Acl(1) | Acl |
| | |
-------------+----------------+---------------+--------------
(1) Any ACEs with the INHERITED_ACE bit set are NOT copied to the assigned
security descriptor.
(2) If the AutoInheritFlags is flagged to automatically inherit ACEs from
parent (SEF_DACL_AUTO_INHERIT or SEF_SACL_AUTO_INHERIT), inherited
ACEs from the parent will be appended after explicit ACEs from the
CreatorDescriptor.
Note that an explicitly specified ACL, whether a default ACL or
not, may be empty or null.
If the caller is explicitly assigning a system acl, default or
non-default, the caller must either be a kernel mode client or
must be appropriately privileged.
Owner and Group Assignment
--------------------------
The assignment of the new object's owner and group is governed
by the following logic:
1) If the passed security descriptor includes an owner, it
is assigned as the new object's owner. Otherwise, the
caller's token is looked in for the owner. Within the
token, if there is a default owner, it is assigned.
Otherwise, the caller's user ID is assigned.
2) If the passed security descriptor includes a group, it
is assigned as the new object's group. Otherwise, the
caller's token is looked in for the group. Within the
token, if there is a default group, it is assigned.
Otherwise, the caller's primary group ID is assigned.
Arguments:
ParentDescriptor - Supplies the Security Descriptor for the parent
directory under which a new object is being created. If there is
no parent directory, then this argument is specified as NULL.
CreatorDescriptor - (Optionally) Points to a security descriptor
presented by the creator of the object. If the creator of the
object did not explicitly pass security information for the new
object, then a null pointer should be passed.
NewDescriptor - Points to a pointer that is to be made to point to the
newly allocated self-relative security descriptor.
ObjectType - GUID of the object type being created. If the object being
created has no GUID associated with it, then this argument is
specified as NULL.
IsDirectoryObject - Specifies if the new object is going to be a
directory object. A value of TRUE indicates the object is a
container of other objects.
AutoInheritFlags - Controls automatic inheritance of ACES from the Parent
Descriptor. Valid values are a bits mask of the logical OR of
one or more of the following bits:
SEF_DACL_AUTO_INHERIT - If set, inherit ACEs from the
DACL ParentDescriptor are inherited to NewDescriptor in addition
to any explicit ACEs specified by the CreatorDescriptor.
SEF_SACL_AUTO_INHERIT - If set, inherit ACEs from the
SACL ParentDescriptor are inherited to NewDescriptor in addition
to any explicit ACEs specified by the CreatorDescriptor.
SEF_DEFAULT_DESCRIPTOR_FOR_OBJECT - If set, the CreatorDescriptor
is the default descriptor for ObjectType. As such, the
CreatorDescriptor will be ignored if any ObjectType specific
ACEs are inherited from the parent. If no such ACEs are inherited,
the CreatorDescriptor is handled as though this flag were not
specified.
SEF_AVOID_PRIVILEGE_CHECK - If set, no privilege checking is done by this
routine. This flag is useful while implementing automatic inheritance
to avoid checking privileges on each child updated.
SEF_AVOID_OWNER_CHECK - If set, no owner checking is done by this routine.
SEF_DEFAULT_OWNER_FROM_PARENT - If set, the owner of NewDescriptor will
default to the owner from ParentDescriptor. If not set, the owner
of NewDescriptor will default to the user specified in Token.
In either case, the owner of NewDescriptor is set to the owner from
the CreatorDescriptor if that field is specified.
SEF_DEFAULT_GROUP_FROM_PARENT - If set, the group of NewDescriptor will
default to the group from ParentDescriptor. If not set, the group
of NewDescriptor will default to the group specified in Token.
In either case, the group of NewDescriptor is set to the group from
the CreatorDescriptor if that field is specified.
Token - Supplies the token for the client on whose behalf the
object is being created. If it is an impersonation token,
then it must be at SecurityIdentification level or higher. If
it is not an impersonation token, the operation proceeds
normally.
A client token is used to retrieve default security
information for the new object, such as default owner, primary
group, and discretionary access control. The token must be
open for TOKEN_QUERY access.
For calls from the kernel, Supplies the security context of the subject creating the
object. This is used to retrieve default security information for the
new object, such as default owner, primary group, and discretionary
access control.
If not specified, the Owner and Primary group must be specified in the
CreatorDescriptor.
GenericMapping - Supplies a pointer to a generic mapping array denoting
the mapping between each generic right to specific rights.
Return Value:
STATUS_SUCCESS - The operation was successful.
STATUS_INVALID_OWNER - The owner SID provided as the owner of the
target security descriptor is not one the subject is authorized to
assign as the owner of an object.
STATUS_NO_CLIENT_TOKEN - Indicates a client token was not explicitly
provided and the caller is not currently impersonating a client.
STATUS_PRIVILEGE_NOT_HELD - The caller does not have the privilege
necessary to explicitly assign the specified system ACL.
SeSecurityPrivilege privilege is needed to explicitly assign
system ACLs to objects.
--*/
{
SECURITY_DESCRIPTOR *CapturedDescriptor;
SECURITY_DESCRIPTOR InCaseOneNotPassed;
BOOLEAN SecurityDescriptorPassed;
NTSTATUS Status;
PACL NewSacl = NULL;
BOOLEAN NewSaclInherited = FALSE;
PACL NewDacl = NULL;
PACL ServerDacl = NULL;
BOOLEAN NewDaclInherited = FALSE;
PSID NewOwner = NULL;
PSID NewGroup = NULL;
BOOLEAN SaclExplicitlyAssigned = FALSE;
BOOLEAN OwnerExplicitlyAssigned = FALSE;
BOOLEAN DaclExplicitlyAssigned = FALSE;
BOOLEAN ServerDaclAllocated = FALSE;
BOOLEAN ServerObject;
BOOLEAN DaclUntrusted;
BOOLEAN HasPrivilege;
PRIVILEGE_SET PrivilegeSet;
PSID SubjectContextOwner = NULL;
PSID SubjectContextGroup = NULL;
PSID ServerOwner = NULL;
PSID ServerGroup = NULL;
PACL SubjectContextDacl = NULL;
ULONG AllocationSize;
ULONG NewOwnerSize, OwnerSize;
ULONG NewGroupSize, GroupSize;
ULONG NewSaclSize;
ULONG NewDaclSize;
PCHAR Field;
PCHAR Base;
PISECURITY_DESCRIPTOR_RELATIVE INewDescriptor = NULL;
NTSTATUS PassedStatus;
KPROCESSOR_MODE RequestorMode;
ULONG GenericControl;
ULONG NewControlBits = SE_SELF_RELATIVE;
#ifndef NTOS_KERNEL_RUNTIME
PTOKEN_OWNER TokenOwnerInfo = NULL;
PTOKEN_PRIMARY_GROUP TokenPrimaryGroupInfo = NULL;
PTOKEN_DEFAULT_DACL TokenDefaultDaclInfo = NULL;
PTOKEN_OWNER ServerOwnerInfo = NULL;
PTOKEN_PRIMARY_GROUP ServerGroupInfo = NULL;
PVOID HeapHandle;
#else
//
// For kernel mode callers, the Token parameter is really
// a pointer to a subject context structure.
//
PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
PVOID SubjectContextInfo = NULL;
SubjectSecurityContext = (PSECURITY_SUBJECT_CONTEXT)Token;
#endif // NTOS_KERNEL_RUNTIME
RTL_PAGED_CODE();
#ifdef NTOS_KERNEL_RUNTIME
//
// Get the previous mode of the caller
//
RequestorMode = KeGetPreviousMode();
#else // NTOS_KERNEL_RUNTIME
RequestorMode = UserMode;
//
// Get the handle to the current process heap
//
HeapHandle = RtlProcessHeap();
//
// Ensure the token is an impersonation token.
//
if ( Token != NULL ) {
TOKEN_STATISTICS ThreadTokenStatistics;
ULONG ReturnLength;
Status = NtQueryInformationToken(
Token, // Handle
TokenStatistics, // TokenInformationClass
&ThreadTokenStatistics, // TokenInformation
sizeof(TOKEN_STATISTICS), // TokenInformationLength
&ReturnLength // ReturnLength
);
if (!NT_SUCCESS( Status )) {
return( Status );
}
//
// If it is an impersonation token, then make sure it is at a
// high enough level.
//
if (ThreadTokenStatistics.TokenType == TokenImpersonation) {
if (ThreadTokenStatistics.ImpersonationLevel < SecurityIdentification ) {
return( STATUS_BAD_IMPERSONATION_LEVEL );
}
}
}
#endif // NTOS_KERNEL_RUNTIME
//
// The desired end result is to build a self-relative security descriptor.
// This means that a single block of memory will be allocated and all
// security information copied into it. To minimize work along the way,
// it is desirable to reference (rather than copy) each field as we
// determine its source. This can not be done with inherited ACLs, however,
// since they must be built from another ACL. So, explicitly assigned
// and defaulted SIDs and ACLs are just referenced until they are copied
// into the self-relative descriptor. Inherited ACLs are built in a
// temporary buffer which must be deallocated after being copied to the
// self-relative descriptor.
//
//
// If a security descriptor has been passed, capture it, otherwise
// cobble up a fake one to simplify the code that follows.
//
if (ARGUMENT_PRESENT(CreatorDescriptor)) {
CapturedDescriptor = CreatorDescriptor;
SecurityDescriptorPassed = TRUE;
} else {
//
// No descriptor passed, make a fake one
//
SecurityDescriptorPassed = FALSE;
RtlCreateSecurityDescriptor(&InCaseOneNotPassed,
SECURITY_DESCRIPTOR_REVISION);
CapturedDescriptor = &InCaseOneNotPassed;
}
if ( CapturedDescriptor->Control & SE_SERVER_SECURITY ) {
ServerObject = TRUE;
} else {
ServerObject = FALSE;
}
if ( CapturedDescriptor->Control & SE_DACL_UNTRUSTED ) {
DaclUntrusted = TRUE;
} else {
DaclUntrusted = FALSE;
}
//
// Get the required information from the token.
//
//
// Grab pointers to the default owner, primary group, and
// discretionary ACL.
//
if ( Token != NULL || ServerObject ) {
#ifdef NTOS_KERNEL_RUNTIME
PSID TmpSubjectContextOwner = NULL;
PSID TmpSubjectContextGroup = NULL;
PSID TmpServerOwner = NULL;
PSID TmpServerGroup = NULL;
PACL TmpSubjectContextDacl = NULL;
SIZE_T SubjectContextInfoSize = 0;
//
// Lock the subject context for read access so that the pointers
// we copy out of it don't disappear on us at random
//
SeLockSubjectContext( SubjectSecurityContext );
SepGetDefaultsSubjectContext(
SubjectSecurityContext,
&TmpSubjectContextOwner,
&TmpSubjectContextGroup,
&TmpServerOwner,
&TmpServerGroup,
&TmpSubjectContextDacl
);
//
// We can't keep the subject context locked, because
// we may have to do a privilege check later, which calls
// PsLockProcessSecurityFields, which can cause a deadlock
// with PsImpersonateClient, which takes them in the reverse
// order.
//
// Since we're giving up our read lock on the token, we
// need to copy all the stuff that we just got back. Since
// it's not going to change, we can save some cycles and copy
// it all into a single chunck of memory.
//
SubjectContextInfoSize = SeLengthSid( TmpSubjectContextOwner ) +
SeLengthSid( TmpServerOwner ) +
(TmpSubjectContextGroup != NULL ? SeLengthSid( TmpSubjectContextGroup ) : 0) +
(TmpServerGroup != NULL ? SeLengthSid( TmpServerGroup ) : 0) +
(TmpSubjectContextDacl != NULL ? TmpSubjectContextDacl->AclSize : 0);
SubjectContextInfo = ExAllocatePoolWithTag( PagedPool, SubjectContextInfoSize, 'dSeS');
if (SubjectContextInfo) {
//
// Copy in the data
//
Base = SubjectContextInfo;
//
// There will always be an owner.
//
SubjectContextOwner = (PSID)Base;
RtlCopySid( SeLengthSid( TmpSubjectContextOwner), Base, TmpSubjectContextOwner );
Base += SeLengthSid( TmpSubjectContextOwner);
//
// Groups may be NULL
//
if (TmpSubjectContextGroup != NULL) {
SubjectContextGroup = (PSID)Base;
RtlCopySid( SeLengthSid( TmpSubjectContextGroup), Base, TmpSubjectContextGroup );
Base += SeLengthSid( TmpSubjectContextGroup );
} else {
SubjectContextGroup = NULL;
}
ServerOwner = (PSID)Base;
RtlCopySid( SeLengthSid( TmpServerOwner ), Base, TmpServerOwner );
Base += SeLengthSid( TmpServerOwner );
//
// Groups may be NULL
//
if (TmpServerGroup != NULL) {
ServerGroup = (PSID)Base;
RtlCopySid( SeLengthSid( TmpServerGroup ), Base, TmpServerGroup );
Base += SeLengthSid( TmpServerGroup );
} else {
ServerGroup = NULL;
}
if (TmpSubjectContextDacl != NULL) {
SubjectContextDacl = (PACL)Base;
RtlCopyMemory( Base, TmpSubjectContextDacl, TmpSubjectContextDacl->AclSize );
// Base += TmpSubjectContextDacl->AclSize;
} else {
SubjectContextDacl = NULL;
}
} else {
SeUnlockSubjectContext( SubjectSecurityContext );
return( STATUS_INSUFFICIENT_RESOURCES );
}
SeUnlockSubjectContext( SubjectSecurityContext );
#else // NTOS_KERNEL_RUNTIME
Status = RtlpGetDefaultsSubjectContext(
Token,
&TokenOwnerInfo,
&TokenPrimaryGroupInfo,
&TokenDefaultDaclInfo,
&ServerOwnerInfo,
&ServerGroupInfo
);
if (!NT_SUCCESS( Status )) {
return( Status );
}
SubjectContextOwner = TokenOwnerInfo->Owner;
SubjectContextGroup = TokenPrimaryGroupInfo->PrimaryGroup;
SubjectContextDacl = TokenDefaultDaclInfo->DefaultDacl;
ServerOwner = ServerOwnerInfo->Owner;
ServerGroup = ServerGroupInfo->PrimaryGroup;
#endif // NTOS_KERNEL_RUNTIME
}
//
// Establish an owner SID
//
NewOwner = RtlpOwnerAddrSecurityDescriptor(CapturedDescriptor);
if ((NewOwner) != NULL) {
//
// Use the specified owner
//
OwnerExplicitlyAssigned = TRUE;
} else {
//
// If the caller said to default the owner from the parent descriptor,
// grab it now.
//
if ( AutoInheritFlags & SEF_DEFAULT_OWNER_FROM_PARENT) {
if ( !ARGUMENT_PRESENT(ParentDescriptor) ) {
Status = STATUS_INVALID_OWNER;
goto Cleanup;
}
NewOwner = RtlpOwnerAddrSecurityDescriptor((SECURITY_DESCRIPTOR *)ParentDescriptor);
OwnerExplicitlyAssigned = TRUE;
if ( NewOwner == NULL ) {
Status = STATUS_INVALID_OWNER;
goto Cleanup;
}
} else {
//
// Pick up the default from the subject's security context.
//
// This does NOT constitute explicit assignment of owner
// and does not have to be checked as an ID that can be
// assigned as owner. This is because a default can not
// be established in a token unless the user of the token
// can assign it as an owner.
//
//
// If we've been asked to create a ServerObject, we need to
// make sure to pick up the new owner from the Primary token,
// not the client token. If we're not impersonating, they will
// end up being the same.
//
NewOwner = ServerObject ? ServerOwner : SubjectContextOwner;
//
// Ensure an owner is now defined.
//
if ( NewOwner == NULL ) {
Status = STATUS_NO_TOKEN;
goto Cleanup;
}
}
}
//
// Establish a Group SID
//
NewGroup = RtlpGroupAddrSecurityDescriptor(CapturedDescriptor);
if (NewGroup == NULL) {
//
// If the caller said to default the group from the parent descriptor,
// grab it now.
//
if ( AutoInheritFlags & SEF_DEFAULT_GROUP_FROM_PARENT) {
if ( !ARGUMENT_PRESENT(ParentDescriptor) ) {
Status = STATUS_INVALID_PRIMARY_GROUP;
goto Cleanup;
}
NewGroup = RtlpGroupAddrSecurityDescriptor((SECURITY_DESCRIPTOR *)ParentDescriptor);
} else {
//
// Pick up the primary group from the subject's security context
//
// If we're creating a Server object, use the group from the server
// context.
//
NewGroup = ServerObject ? ServerGroup : SubjectContextGroup;
}
}
if (NewGroup != NULL) {
if (!RtlValidSid( NewGroup )) {
Status = STATUS_INVALID_PRIMARY_GROUP;
goto Cleanup;
}
} else {
Status = STATUS_INVALID_PRIMARY_GROUP;
goto Cleanup;
}
//
// Establish System Acl
//
Status = RtlpInheritAcl (
ARGUMENT_PRESENT(ParentDescriptor) ?
RtlpSaclAddrSecurityDescriptor(
((SECURITY_DESCRIPTOR *)ParentDescriptor)) :
NULL,
RtlpSaclAddrSecurityDescriptor(CapturedDescriptor),
SeControlSaclToGeneric( CapturedDescriptor->Control ),
IsDirectoryObject,
(BOOLEAN)((AutoInheritFlags & SEF_SACL_AUTO_INHERIT) != 0),
(BOOLEAN)((AutoInheritFlags & SEF_DEFAULT_DESCRIPTOR_FOR_OBJECT) != 0),
NewOwner,
NewGroup,
ServerOwner,
ServerGroup,
GenericMapping,
TRUE, // Is a SACL
pObjectType,
GuidCount,
&NewSacl,
&SaclExplicitlyAssigned,
&GenericControl );
if ( NT_SUCCESS(Status) ) {
NewSaclInherited = TRUE;
NewControlBits |= SE_SACL_PRESENT | SeControlGenericToSacl( GenericControl );
} else if ( Status == STATUS_NO_INHERITANCE ) {
//
// Always set the auto inherit bit if the caller requested it.
//
if ( AutoInheritFlags & SEF_SACL_AUTO_INHERIT) {
NewControlBits |= SE_SACL_AUTO_INHERITED;
}
//
// No inheritable ACL - check for a defaulted one.
//
if ( RtlpAreControlBitsSet( CapturedDescriptor,
SE_SACL_PRESENT | SE_SACL_DEFAULTED ) ) {
//
// Reference the default ACL
//
NewSacl = RtlpSaclAddrSecurityDescriptor(CapturedDescriptor);
NewControlBits |= SE_SACL_PRESENT;
NewControlBits |= (CapturedDescriptor->Control & SE_SACL_PROTECTED);
//
// This counts as an explicit assignment.
//
SaclExplicitlyAssigned = TRUE;
}
} else {
//
// Some unusual error occured
//
goto Cleanup;
}
//
// Establish Discretionary Acl
//
Status = RtlpInheritAcl (
ARGUMENT_PRESENT(ParentDescriptor) ?
RtlpDaclAddrSecurityDescriptor(
((SECURITY_DESCRIPTOR *)ParentDescriptor)) :
NULL,
RtlpDaclAddrSecurityDescriptor(CapturedDescriptor),
SeControlDaclToGeneric( CapturedDescriptor->Control ),
IsDirectoryObject,
(BOOLEAN)((AutoInheritFlags & SEF_DACL_AUTO_INHERIT) != 0),
(BOOLEAN)((AutoInheritFlags & SEF_DEFAULT_DESCRIPTOR_FOR_OBJECT) != 0),
NewOwner,
NewGroup,
ServerOwner,
ServerGroup,
GenericMapping,
FALSE, // Is a DACL
pObjectType,
GuidCount,
&NewDacl,
&DaclExplicitlyAssigned,
&GenericControl );
if ( NT_SUCCESS(Status) ) {
NewDaclInherited = TRUE;
NewControlBits |= SE_DACL_PRESENT | SeControlGenericToDacl( GenericControl );
} else if ( Status == STATUS_NO_INHERITANCE ) {
//
// Always set the auto inherit bit if the caller requested it.
//
if ( AutoInheritFlags & SEF_DACL_AUTO_INHERIT) {
NewControlBits |= SE_DACL_AUTO_INHERITED;
}
//
// No inheritable ACL - check for a defaulted one.
//
if ( RtlpAreControlBitsSet( CapturedDescriptor,
SE_DACL_PRESENT | SE_DACL_DEFAULTED ) ) {
//
// Reference the default ACL
//
NewDacl = RtlpDaclAddrSecurityDescriptor(CapturedDescriptor);
NewControlBits |= SE_DACL_PRESENT;
NewControlBits |= (CapturedDescriptor->Control & SE_DACL_PROTECTED);
//
// This counts as an explicit assignment.
//
DaclExplicitlyAssigned = TRUE;
//
// Default to the DACL on the token.
//
} else if (ARGUMENT_PRESENT(SubjectContextDacl)) {
NewDacl = SubjectContextDacl;
NewControlBits |= SE_DACL_PRESENT;
}
} else {
//
// Some unusual error occured
//
goto Cleanup;
}
#ifdef ASSERT_ON_NULL_DACL
//
// Culprit will probably be the caller NtCreate*, or
// RtlNewSecurityObject. Note that although this will not always occur
// because of explicit user action it still must be corrected.
//
if (RtlpAssertOnNullDacls) {
ASSERT(("NULL DACLs are NOT allowed!", NewDacl != NULL));
}
#endif // ASSERT_ON_NULL_DACL
//
// If auto inheriting and the computed child DACL is NULL,
// mark it as protected.
//
// NULL DACLs are problematic when ACEs are actually inherited from the
// parent DACL. It is better to mark them as protected NOW (even if we don't
// end up inheriting any ACEs) to avoid confusion later.
//
if ( (AutoInheritFlags & SEF_DACL_AUTO_INHERIT) != 0 &&
NewDacl == NULL ) {
NewControlBits |= SE_DACL_PROTECTED;
}
//
// Now make sure that the caller has the right to assign
// everything in the descriptor. The requestor is subjected
// to privilege and restriction tests for some assignments.
//
if (RequestorMode == UserMode) {
//
// Anybody can assign any Discretionary ACL or group that they want to.
//
//
// See if the system ACL was explicitly specified
//
if ( SaclExplicitlyAssigned &&
(AutoInheritFlags & SEF_AVOID_PRIVILEGE_CHECK) == 0 ) {
//
// Require a Token if we're to do the privilege check.
//
if ( Token == NULL ) {
Status = STATUS_NO_TOKEN;
goto Cleanup;
}
#ifdef NTOS_KERNEL_RUNTIME
//
// Check for appropriate Privileges
// Audit/Alarm messages need to be generated due to the attempt
// to perform a privileged operation.
//
//
// Note: be sure to do the privilege check against
// the passed subject context!
//
PrivilegeSet.PrivilegeCount = 1;
PrivilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY;
PrivilegeSet.Privilege[0].Luid = SeSecurityPrivilege;
PrivilegeSet.Privilege[0].Attributes = 0;
HasPrivilege = SePrivilegeCheck(
&PrivilegeSet,
SubjectSecurityContext,
RequestorMode
);
if ( RequestorMode != KernelMode ) {
SePrivilegedServiceAuditAlarm (
NULL,
SubjectSecurityContext,
&PrivilegeSet,
HasPrivilege
);
}
#else // NTOS_KERNEL_RUNTIME
//
// Check for appropriate Privileges
//
// Audit/Alarm messages need to be generated due to the attempt
// to perform a privileged operation.
//
PrivilegeSet.PrivilegeCount = 1;
PrivilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY;
PrivilegeSet.Privilege[0].Luid = RtlConvertLongToLuid(SE_SECURITY_PRIVILEGE);
PrivilegeSet.Privilege[0].Attributes = 0;
Status = NtPrivilegeCheck(
Token,
&PrivilegeSet,
&HasPrivilege
);
if (!NT_SUCCESS( Status )) {
goto Cleanup;
}
#endif // NTOS_KERNEL_RUNTIME
if ( !HasPrivilege ) {
Status = STATUS_PRIVILEGE_NOT_HELD;
goto Cleanup;
}
}
//
// See if the owner field is one the requestor can assign
//
if (OwnerExplicitlyAssigned &&
(AutoInheritFlags & SEF_AVOID_OWNER_CHECK) == 0 ) {
#ifdef NTOS_KERNEL_RUNTIME
if (!SepValidOwnerSubjectContext(
SubjectSecurityContext,
NewOwner,
ServerObject)
) {
Status = STATUS_INVALID_OWNER;
goto Cleanup;
}
#else // NTOS_KERNEL_RUNTIME
//
// Require a Token if we're to do the privilege check.
//
if ( Token == NULL ) {
Status = STATUS_NO_TOKEN;
goto Cleanup;
}
if (!RtlpValidOwnerSubjectContext(
Token,
NewOwner,
ServerObject,
&PassedStatus) ) {
Status = PassedStatus;
goto Cleanup;
}
#endif // NTOS_KERNEL_RUNTIME
}
//
// If the DACL was explictly assigned and this is a server object,
// convert the DACL to be a server DACL
//
if (DaclExplicitlyAssigned && ServerObject) {
Status = RtlpCreateServerAcl(
NewDacl,
DaclUntrusted,
ServerOwner,
&ServerDacl,
&ServerDaclAllocated
);
if (!NT_SUCCESS( Status )) {
goto Cleanup;
}
NewDacl = ServerDacl;
}
}
//
// Everything is assignable by the requestor.
// Calculate the memory needed to house all the information in
// a self-relative security descriptor.
//
// Also map the ACEs for application to the target object
// type, if they haven't already been mapped.
//
OwnerSize = SeLengthSid(NewOwner);
NewOwnerSize = LongAlignSize(OwnerSize);
if (NewGroup != NULL) {
GroupSize = SeLengthSid(NewGroup);
NewGroupSize = LongAlignSize(GroupSize);
}
if ((NewControlBits & SE_SACL_PRESENT) && (NewSacl != NULL)) {
NewSaclSize = LongAlignSize(NewSacl->AclSize);
} else {
NewSaclSize = 0;
}
if ( (NewControlBits & SE_DACL_PRESENT) && (NewDacl != NULL)) {
NewDaclSize = LongAlignSize(NewDacl->AclSize);
} else {
NewDaclSize = 0;
}
AllocationSize = LongAlignSize(sizeof(SECURITY_DESCRIPTOR_RELATIVE)) +
NewOwnerSize +
NewGroupSize +
NewSaclSize +
NewDaclSize;
//
// Allocate and initialize the security descriptor as
// self-relative form.
//
#ifdef NTOS_KERNEL_RUNTIME
INewDescriptor = (PSECURITY_DESCRIPTOR)ExAllocatePoolWithTag( PagedPool, AllocationSize, 'dSeS');
#else // NTOS_KERNEL_RUNTIME
INewDescriptor = RtlAllocateHeap( HeapHandle, MAKE_TAG( SE_TAG ), AllocationSize );
#endif // NTOS_KERNEL_RUNTIME
if ( INewDescriptor == NULL ) {
#ifdef NTOS_KERNEL_RUNTIME
Status = STATUS_INSUFFICIENT_RESOURCES;
#else // NTOS_KERNEL_RUNTIME
Status = STATUS_NO_MEMORY;
#endif // NTOS_KERNEL_RUNTIME
goto Cleanup;
}
RtlCreateSecurityDescriptorRelative(
INewDescriptor,
SECURITY_DESCRIPTOR_REVISION
);
RtlpSetControlBits( INewDescriptor, NewControlBits );
Base = (PCHAR)(INewDescriptor);
Field = Base + sizeof(SECURITY_DESCRIPTOR_RELATIVE);
//
// Map and Copy in the Sacl
//
if (NewControlBits & SE_SACL_PRESENT) {
if (NewSacl != NULL) {
RtlCopyMemory( Field, NewSacl, NewSacl->AclSize );
if (!NewSaclInherited) {
RtlpApplyAclToObject( (PACL)Field, GenericMapping );
}
INewDescriptor->Sacl = RtlPointerToOffset(Base,Field);
if (NewSaclSize > NewSacl->AclSize) {
RtlZeroMemory (Field + NewSacl->AclSize, NewSaclSize - NewSacl->AclSize);
}
Field += NewSaclSize;
} else {
INewDescriptor->Sacl = 0;
}
}
//
// Map and Copy in the Dacl
//
if (NewControlBits & SE_DACL_PRESENT) {
if (NewDacl != NULL) {
RtlCopyMemory( Field, NewDacl, NewDacl->AclSize );
if (!NewDaclInherited) {
RtlpApplyAclToObject( (PACL)Field, GenericMapping );
}
INewDescriptor->Dacl = RtlPointerToOffset(Base,Field);
if (NewDaclSize > NewDacl->AclSize) {
RtlZeroMemory (Field + NewDacl->AclSize, NewDaclSize - NewDacl->AclSize);
}
Field += NewDaclSize;
} else {
INewDescriptor->Dacl = 0;
}
}
//
// Assign the owner
//
RtlCopyMemory( Field, NewOwner, OwnerSize );
if (NewOwnerSize > OwnerSize) {
RtlZeroMemory (Field + OwnerSize, NewOwnerSize - OwnerSize);
}
INewDescriptor->Owner = RtlPointerToOffset(Base,Field);
Field += NewOwnerSize;
if (NewGroup != NULL) {
RtlCopyMemory( Field, NewGroup, GroupSize );
if (NewGroupSize > GroupSize) {
RtlZeroMemory (Field + GroupSize, NewGroupSize - GroupSize);
}
INewDescriptor->Group = RtlPointerToOffset(Base,Field);
}
Status = STATUS_SUCCESS;
Cleanup:
//
// If we allocated memory for a Server DACL, free it now.
//
if (ServerDaclAllocated) {
#ifdef NTOS_KERNEL_RUNTIME
ExFreePool( ServerDacl );
#else // NTOS_KERNEL_RUNTIME
RtlFreeHeap(RtlProcessHeap(), 0, ServerDacl );
#endif // NTOS_KERNEL_RUNTIME
}
//
// Either an error was encountered or the assignment has completed
// successfully. In either case, we have to clean up any memory.
//
#ifdef NTOS_KERNEL_RUNTIME
// if ( SubjectSecurityContext != NULL ) {
// SeUnlockSubjectContext( SubjectSecurityContext );
// }
if (SubjectContextInfo != NULL) {
ExFreePool( SubjectContextInfo );
}
#else // NTOS_KERNEL_RUNTIME
RtlFreeHeap( HeapHandle, 0, (PVOID)TokenOwnerInfo );
RtlFreeHeap( HeapHandle, 0, (PVOID)TokenPrimaryGroupInfo );
RtlFreeHeap( HeapHandle, 0, (PVOID)TokenDefaultDaclInfo );
RtlFreeHeap( HeapHandle, 0, (PVOID)ServerOwnerInfo );
RtlFreeHeap( HeapHandle, 0, (PVOID)ServerGroupInfo );
#endif // NTOS_KERNEL_RUNTIME
if (NewSaclInherited && NewSacl != NULL ) {
#ifdef NTOS_KERNEL_RUNTIME
ExFreePool( NewSacl );
#else // NTOS_KERNEL_RUNTIME
RtlFreeHeap( HeapHandle, 0, (PVOID)NewSacl );
#endif // NTOS_KERNEL_RUNTIME
}
if (NewDaclInherited && NewDacl != NULL ) {
#ifdef NTOS_KERNEL_RUNTIME
ExFreePool( NewDacl );
#else // NTOS_KERNEL_RUNTIME
RtlFreeHeap( HeapHandle, 0, (PVOID)NewDacl );
#endif // NTOS_KERNEL_RUNTIME
}
*NewDescriptor = (PSECURITY_DESCRIPTOR) INewDescriptor;
return Status;
}
NTSTATUS
RtlpSetSecurityObject (
IN PVOID Object OPTIONAL,
IN SECURITY_INFORMATION SecurityInformation,
IN PSECURITY_DESCRIPTOR ModificationDescriptor,
IN OUT PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor,
IN ULONG AutoInheritFlags,
IN ULONG PoolType,
IN PGENERIC_MAPPING GenericMapping,
IN HANDLE Token OPTIONAL
)
/*++
Routine Description:
Modify an object's existing self-relative form security descriptor.
This procedure, called only from user mode, is used to update a
security descriptor on an existing protected server's object. It
applies changes requested by a new security descriptor to the existing
security descriptor. If necessary, this routine will allocate
additional memory to produce a larger security descriptor. All access
checking is expected to be done before calling this routine. This
includes checking for WRITE_OWNER, WRITE_DAC, and privilege to assign a
system ACL as appropriate.
The caller of this routine must not be impersonating a client.
- - WARNING - -
This service is for use by protected subsystems that project their own
type of object. This service is explicitly not for use by the
executive for executive objects and must not be called from kernel
mode.
Arguments:
Object - Optionally supplies the object whose security is
being adjusted. This is used to update security quota
information.
SecurityInformation - Indicates which security information is
to be applied to the object. The value(s) to be assigned are
passed in the ModificationDescriptor parameter.
ModificationDescriptor - Supplies the input security descriptor to be
applied to the object. The caller of this routine is expected
to probe and capture the passed security descriptor before calling
and release it after calling.
ObjectsSecurityDescriptor - Supplies the address of a pointer to
the objects security descriptor that is going to be altered by
this procedure. This security descriptor must be in self-
relative form or an error will be returned.
AutoInheritFlags - Controls automatic inheritance of ACES.
Valid values are a bits mask of the logical OR of
one or more of the following bits:
SEF_DACL_AUTO_INHERIT - If set, inherited ACEs from the
DACL in the ObjectsSecurityDescriptor are preserved and inherited ACEs from
the ModificationDescriptor are ignored. Inherited ACEs are not supposed
to be modified; so preserving them across this call is appropriate.
If a protected server does not itself implement auto inheritance, it should
not set this bit. The caller of the protected server may implement
auto inheritance and my indeed be modifying inherited ACEs.
SEF_SACL_AUTO_INHERIT - If set, inherited ACEs from the
SACL in the ObjectsSecurityDescriptor are preserved and inherited ACEs from
the ModificationDescriptor are ignored. Inherited ACEs are not supposed
to be modified; so preserving them across this call is appropriate.
If a protected server does not itself implement auto inheritance, it should
not set this bit. The caller of the protected server may implement
auto inheritance and my indeed be modifying inherited ACEs.
SEF_AVOID_PRIVILEGE_CHECK - If set, the Token in not used to ensure the
Owner passed in ModificationDescriptor is valid.
PoolType - Specifies the type of pool to allocate for the objects
security descriptor.
GenericMapping - This argument provides the mapping of generic to
specific/standard access types for the object being accessed.
This mapping structure is expected to be safe to access
(i.e., captured if necessary) prior to be passed to this routine.
Token - (optionally) Supplies the token for the client on whose
behalf the security is being modified. This parameter is only
required to ensure that the client has provided a legitimate
value for a new owner SID. The token must be open for
TOKEN_QUERY access.
Return Value:
STATUS_SUCCESS - The operation was successful.
STATUS_INVALID_OWNER - The owner SID provided as the new owner of the
target security descriptor is not one the caller is authorized to
assign as the owner of an object, or the client did not pass
a token at all.
STATUS_NO_CLIENT_TOKEN - Indicates a client token was not explicitly
provided and the caller is not currently impersonating a client.
STATUS_BAD_DESCRIPTOR_FORMAT - Indicates the provided object's security
descriptor was not in self-relative format.
--*/
{
BOOLEAN NewGroupPresent = FALSE;
BOOLEAN NewOwnerPresent = FALSE;
BOOLEAN ServerAclAllocated = FALSE;
BOOLEAN LocalDaclAllocated = FALSE;
BOOLEAN LocalSaclAllocated = FALSE;
BOOLEAN ServerObject;
BOOLEAN DaclUntrusted;
PCHAR Field;
PCHAR Base;
PISECURITY_DESCRIPTOR_RELATIVE NewDescriptor = NULL;
NTSTATUS Status;
TOKEN_STATISTICS ThreadTokenStatistics;
ULONG ReturnLength;
PSID NewGroup;
PSID NewOwner;
PACL NewDacl;
PACL LocalDacl;
PACL NewSacl;
PACL LocalSacl;
ULONG NewDaclSize;
ULONG NewSaclSize;
ULONG NewOwnerSize, OwnerSize;
ULONG NewGroupSize, GroupSize;
ULONG AllocationSize;
ULONG ServerOwnerInfoSize;
HANDLE PrimaryToken;
ULONG GenericControl;
ULONG NewControlBits = SE_SELF_RELATIVE;
PACL ServerDacl;
SECURITY_SUBJECT_CONTEXT SubjectContext;
//
// Typecast to internal representation of security descriptor.
// Note that the internal one is not a pointer to a pointer.
// It is just a pointer to a security descriptor.
//
PISECURITY_DESCRIPTOR IModificationDescriptor =
(PISECURITY_DESCRIPTOR)ModificationDescriptor;
PISECURITY_DESCRIPTOR *IObjectsSecurityDescriptor =
(PISECURITY_DESCRIPTOR *)(ObjectsSecurityDescriptor);
#ifndef NTOS_KERNEL_RUNTIME
PVOID HeapHandle;
#endif // NTOS_KERNEL_RUNTIME
RTL_PAGED_CODE();
//
// Get the handle to the current process heap
//
#ifndef NTOS_KERNEL_RUNTIME
HeapHandle = RtlProcessHeap();
#endif // NTOS_KERNEL_RUNTIME
//
// Validate that the provided SD is in self-relative form
//
if ( !RtlpAreControlBitsSet(*IObjectsSecurityDescriptor, SE_SELF_RELATIVE) ) {
Status = STATUS_BAD_DESCRIPTOR_FORMAT;
goto Cleanup;
}
//
// Check to see if we need to edit the passed acl
// either because we're creating a server object, or because
// we were passed an untrusted ACL.
//
if (ARGUMENT_PRESENT(ModificationDescriptor)) {
if ( RtlpAreControlBitsSet(IModificationDescriptor, SE_SERVER_SECURITY)) {
ServerObject = TRUE;
} else {
ServerObject = FALSE;
}
if ( RtlpAreControlBitsSet(IModificationDescriptor, SE_DACL_UNTRUSTED)) {
DaclUntrusted = TRUE;
} else {
DaclUntrusted = FALSE;
}
} else {
ServerObject = FALSE;
DaclUntrusted = FALSE;
}
//
// For each item specified in the SecurityInformation, extract it
// and get it to the point where it can be copied into a new
// descriptor.
//
//
// if he's setting the owner field, make sure he's
// allowed to set that value as an owner.
//
if (SecurityInformation & OWNER_SECURITY_INFORMATION) {
NewOwner = RtlpOwnerAddrSecurityDescriptor( IModificationDescriptor );
NewOwnerPresent = TRUE;
if ((AutoInheritFlags & SEF_AVOID_PRIVILEGE_CHECK) == 0 ) {
#ifdef NTOS_KERNEL_RUNTIME
SeCaptureSubjectContext( &SubjectContext );
if (!SepValidOwnerSubjectContext( &SubjectContext, NewOwner, ServerObject ) ) {
SeReleaseSubjectContext( &SubjectContext );
return( STATUS_INVALID_OWNER );
} else {
SeReleaseSubjectContext( &SubjectContext );
}
#else // NTOS_KERNEL_RUNTIME
if ( ARGUMENT_PRESENT( Token )) {
Status = NtQueryInformationToken(
Token, // Handle
TokenStatistics, // TokenInformationClass
&ThreadTokenStatistics, // TokenInformation
sizeof(TOKEN_STATISTICS), // TokenInformationLength
&ReturnLength // ReturnLength
);
if (!NT_SUCCESS( Status )) {
goto Cleanup;
}
//
// If it is an impersonation token, then make sure it is at a
// high enough level.
//
if (ThreadTokenStatistics.TokenType == TokenImpersonation) {
if (ThreadTokenStatistics.ImpersonationLevel < SecurityIdentification ) {
Status = STATUS_BAD_IMPERSONATION_LEVEL;
goto Cleanup;
}
}
} else {
Status = STATUS_INVALID_OWNER;
goto Cleanup;
}
if (!RtlpValidOwnerSubjectContext(
Token,
NewOwner,
ServerObject,
&Status) ) {
Status = STATUS_INVALID_OWNER;
goto Cleanup;
}
#endif // NTOS_KERNEL_RUNTIME
}
} else {
NewOwner = RtlpOwnerAddrSecurityDescriptor ( *IObjectsSecurityDescriptor );
if (NewOwner == NULL) {
Status = STATUS_INVALID_OWNER;
goto Cleanup;
}
}
ASSERT( NewOwner != NULL );
if (!RtlValidSid( NewOwner )) {
Status = STATUS_INVALID_OWNER;
goto Cleanup;
}
if (SecurityInformation & GROUP_SECURITY_INFORMATION) {
NewGroup = RtlpGroupAddrSecurityDescriptor(IModificationDescriptor);
NewGroupPresent = TRUE;
} else {
NewGroup = RtlpGroupAddrSecurityDescriptor( *IObjectsSecurityDescriptor );
}
if (NewGroup != NULL) {
if (!RtlValidSid( NewGroup )) {
Status = STATUS_INVALID_PRIMARY_GROUP;
goto Cleanup;
}
} else {
Status = STATUS_INVALID_PRIMARY_GROUP;
goto Cleanup;
}
if (SecurityInformation & DACL_SECURITY_INFORMATION) {
#ifdef ASSERT_ON_NULL_DACL
//
// Culprit will probably be the caller NtSetSecurityObject, or
// RtlSetSecurityObject.
//
if (RtlpAssertOnNullDacls) {
ASSERT(("NULL DACLs are NOT allowed!",
RtlpDaclAddrSecurityDescriptor(IModificationDescriptor) != NULL));
}
#endif // ASSERT_ON_NULL_DACL
//
// If AutoInherit is requested,
// build a merged ACL.
//
if ( AutoInheritFlags & SEF_DACL_AUTO_INHERIT ) {
Status = RtlpComputeMergedAcl(
RtlpDaclAddrSecurityDescriptor( *IObjectsSecurityDescriptor ),
SeControlDaclToGeneric( (*IObjectsSecurityDescriptor)->Control ),
RtlpDaclAddrSecurityDescriptor( IModificationDescriptor ),
SeControlDaclToGeneric( IModificationDescriptor->Control ),
NewOwner,
NewGroup,
GenericMapping,
FALSE, // Not a SACL
&LocalDacl,
&GenericControl );
if ( !NT_SUCCESS(Status)) {
goto Cleanup;
}
LocalDaclAllocated = TRUE;
NewDacl = LocalDacl;
NewControlBits |= SE_DACL_PRESENT;
NewControlBits |= SeControlGenericToDacl( GenericControl );
//
// If AutoInherit isn't requested,
// just grab a copy of the input DACL.
//
} else {
NewDacl = RtlpDaclAddrSecurityDescriptor( IModificationDescriptor );
NewControlBits |= SE_DACL_PRESENT;
NewControlBits |= IModificationDescriptor->Control & SE_DACL_PROTECTED;
//
// If the original caller claims he understands auto inheritance,
// preserve the AutoInherited flag.
//
if ( RtlpAreControlBitsSet(IModificationDescriptor, SE_DACL_AUTO_INHERIT_REQ|SE_DACL_AUTO_INHERITED) ) {
NewControlBits |= SE_DACL_AUTO_INHERITED;
}
}
if (ServerObject) {
#ifdef NTOS_KERNEL_RUNTIME
PSID SubjectContextOwner;
PSID SubjectContextGroup;
PSID SubjectContextServerOwner;
PSID SubjectContextServerGroup;
PACL SubjectContextDacl;
SeCaptureSubjectContext( &SubjectContext );
SepGetDefaultsSubjectContext(
&SubjectContext,
&SubjectContextOwner,
&SubjectContextGroup,
&SubjectContextServerOwner,
&SubjectContextServerGroup,
&SubjectContextDacl
);
Status = RtlpCreateServerAcl(
NewDacl,
DaclUntrusted,
SubjectContextServerOwner,
&ServerDacl,
&ServerAclAllocated
);
SeReleaseSubjectContext( &SubjectContext );
#else // NTOS_KERNEL_RUNTIME
PTOKEN_OWNER ServerSid;
//
// Obtain the default Server SID to substitute in the
// ACL if necessary.
//
ServerOwnerInfoSize = RtlLengthRequiredSid( SID_MAX_SUB_AUTHORITIES );
ServerSid = RtlAllocateHeap( HeapHandle, MAKE_TAG( SE_TAG ), ServerOwnerInfoSize );
if (ServerSid == NULL) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
Status = NtOpenProcessToken(
NtCurrentProcess(),
TOKEN_QUERY,
&PrimaryToken
);
if (!NT_SUCCESS( Status )) {
RtlFreeHeap( HeapHandle, 0, ServerSid );
goto Cleanup;
}
Status = NtQueryInformationToken(
PrimaryToken, // Handle
TokenOwner, // TokenInformationClass
ServerSid, // TokenInformation
ServerOwnerInfoSize, // TokenInformationLength
&ServerOwnerInfoSize // ReturnLength
);
NtClose( PrimaryToken );
if (!NT_SUCCESS( Status )) {
RtlFreeHeap( HeapHandle, 0, ServerSid );
goto Cleanup;
}
Status = RtlpCreateServerAcl(
NewDacl,
DaclUntrusted,
ServerSid->Owner,
&ServerDacl,
&ServerAclAllocated
);
RtlFreeHeap( HeapHandle, 0, ServerSid );
#endif // NTOS_KERNEL_RUNTIME
if (!NT_SUCCESS( Status )) {
goto Cleanup;
}
NewDacl = ServerDacl;
}
} else {
NewDacl = RtlpDaclAddrSecurityDescriptor( *IObjectsSecurityDescriptor );
}
if (SecurityInformation & SACL_SECURITY_INFORMATION) {
//
// If AutoInherit is requested,
// build a merged ACL.
//
if ( AutoInheritFlags & SEF_SACL_AUTO_INHERIT ) {
Status = RtlpComputeMergedAcl(
RtlpSaclAddrSecurityDescriptor( *IObjectsSecurityDescriptor ),
SeControlSaclToGeneric( (*IObjectsSecurityDescriptor)->Control ),
RtlpSaclAddrSecurityDescriptor( IModificationDescriptor ),
SeControlSaclToGeneric( IModificationDescriptor->Control ),
NewOwner,
NewGroup,
GenericMapping,
TRUE, // Is a SACL
&LocalSacl,
&GenericControl );
if ( !NT_SUCCESS(Status)) {
goto Cleanup;
}
LocalSaclAllocated = TRUE;
NewSacl = LocalSacl;
NewControlBits |= SE_SACL_PRESENT;
NewControlBits |= SeControlGenericToSacl( GenericControl );
} else {
NewSacl = RtlpSaclAddrSecurityDescriptor( IModificationDescriptor );
NewControlBits |= SE_SACL_PRESENT;
NewControlBits |= IModificationDescriptor->Control & SE_SACL_PROTECTED;
//
// If the original caller claims he understands auto inheritance,
// preserve the AutoInherited flag.
//
if ( RtlpAreControlBitsSet(IModificationDescriptor, SE_SACL_AUTO_INHERIT_REQ|SE_SACL_AUTO_INHERITED) ) {
NewControlBits |= SE_SACL_AUTO_INHERITED;
}
}
} else {
NewSacl = RtlpSaclAddrSecurityDescriptor( *IObjectsSecurityDescriptor );
}
//
// Everything is assignable by the requestor.
// Calculate the memory needed to house all the information in
// a self-relative security descriptor.
//
// Also map the ACEs for application to the target object
// type, if they haven't already been mapped.
//
OwnerSize = SeLengthSid(NewOwner);
NewOwnerSize = LongAlignSize(OwnerSize);
if (NewGroup != NULL) {
GroupSize = SeLengthSid(NewGroup);
} else {
GroupSize = 0;
}
NewGroupSize = LongAlignSize(GroupSize);
if (NewSacl != NULL) {
NewSaclSize = LongAlignSize(NewSacl->AclSize);
} else {
NewSaclSize = 0;
}
if (NewDacl !=NULL) {
NewDaclSize = LongAlignSize(NewDacl->AclSize);
} else {
NewDaclSize = 0;
}
AllocationSize = LongAlignSize(sizeof(SECURITY_DESCRIPTOR_RELATIVE)) +
NewOwnerSize +
NewGroupSize +
NewSaclSize +
NewDaclSize;
//
// Allocate and initialize the security descriptor as
// self-relative form.
//
#ifdef NTOS_KERNEL_RUNTIME
NewDescriptor = ExAllocatePoolWithTag(PoolType, AllocationSize, 'dSeS');
#else // NTOS_KERNEL_RUNTIME
NewDescriptor = RtlAllocateHeap( HeapHandle, MAKE_TAG( SE_TAG ), AllocationSize );
#endif // NTOS_KERNEL_RUNTIME
if ( NewDescriptor == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
Status = RtlCreateSecurityDescriptorRelative(
NewDescriptor,
SECURITY_DESCRIPTOR_REVISION
);
ASSERT( NT_SUCCESS( Status ) );
#ifdef NTOS_KERNEL_RUNTIME
//
// We must check to make sure that the Group and Dacl size
// do not exceed the quota preallocated for this object's
// security when it was created.
//
// Update SeComputeSecurityQuota if this changes.
//
if (ARGUMENT_PRESENT( Object )) {
Status = ObValidateSecurityQuota(
Object,
NewGroupSize + NewDaclSize
);
if (!NT_SUCCESS( Status )) {
//
// The new information is too big.
//
ExFreePool( NewDescriptor );
goto Cleanup;
}
}
#endif // NTOS_KERNEL_RUNTIME
Base = (PCHAR)NewDescriptor;
Field = Base + sizeof(SECURITY_DESCRIPTOR_RELATIVE);
//
// Map and Copy in the Sacl
//
// if new item {
// PRESENT=TRUE
// DEFAULTED=FALSE
// if (NULL) {
// set new pointer to NULL
// } else {
// copy into new SD
// }
// } else {
// copy PRESENT bit
// copy DEFAULTED bit
// if (NULL) {
// set new pointer to NULL
// } else {
// copy old one into new SD
// }
// }
RtlpSetControlBits( NewDescriptor, NewControlBits );
if (IModificationDescriptor->Control & SE_RM_CONTROL_VALID) {
NewDescriptor->Sbz1 = IModificationDescriptor->Sbz1;
NewDescriptor->Control |= SE_RM_CONTROL_VALID;
}
if (NewSacl == NULL) {
NewDescriptor->Sacl = 0;
} else {
RtlCopyMemory( Field, NewSacl, NewSacl->AclSize );
RtlpApplyAclToObject( (PACL)Field, GenericMapping );
NewDescriptor->Sacl = RtlPointerToOffset(Base,Field);
if (NewSaclSize > NewSacl->AclSize) {
RtlZeroMemory( Field + NewSacl->AclSize, NewSaclSize - NewSacl->AclSize);
}
Field += NewSaclSize;
}
if ( (NewControlBits & SE_SACL_PRESENT) == 0 ) {
//
// Propagate the SE_SACL_DEFAULTED and SE_SACL_PRESENT
// bits from the old security descriptor into the new
// one.
//
RtlpPropagateControlBits(
NewDescriptor,
*IObjectsSecurityDescriptor,
SE_SACL_DEFAULTED | SE_SACL_PRESENT | SE_SACL_PROTECTED
);
}
//
// Fill in Dacl field in new SD
//
if (NewDacl == NULL) {
NewDescriptor->Dacl = 0;
} else {
RtlCopyMemory( Field, NewDacl, NewDacl->AclSize );
RtlpApplyAclToObject( (PACL)Field, GenericMapping );
NewDescriptor->Dacl = RtlPointerToOffset(Base,Field);
if (NewDaclSize > NewDacl->AclSize) {
RtlZeroMemory( Field + NewDacl->AclSize, NewDaclSize - NewDacl->AclSize);
}
Field += NewDaclSize;
}
if ( (NewControlBits & SE_DACL_PRESENT) == 0 ) {
//
// Propagate the SE_DACL_DEFAULTED and SE_DACL_PRESENT
// bits from the old security descriptor into the new
// one.
//
RtlpPropagateControlBits(
NewDescriptor,
*IObjectsSecurityDescriptor,
SE_DACL_DEFAULTED | SE_DACL_PRESENT | SE_DACL_PROTECTED
);
}
// if new item {
// PRESENT=TRUE
// DEFAULTED=FALSE
// if (NULL) {
// set new pointer to NULL
// } else {
// copy into new SD
// }
// } else {
// copy PRESENT bit
// copy DEFAULTED bit
// if (NULL) {
// set new pointer to NULL
// } else {
// copy old one into new SD
// }
// }
//
// Fill in Owner field in new SD
//
RtlCopyMemory( Field, NewOwner, OwnerSize );
if (OwnerSize < NewOwnerSize) {
RtlZeroMemory( Field + OwnerSize, NewOwnerSize - OwnerSize );
}
NewDescriptor->Owner = RtlPointerToOffset(Base,Field);
Field += NewOwnerSize;
if (!NewOwnerPresent) {
//
// Propagate the SE_OWNER_DEFAULTED bit from the old SD.
// If a new owner is being assigned, we want to leave
// SE_OWNER_DEFAULTED off, which means leave it alone.
//
RtlpPropagateControlBits(
NewDescriptor,
*IObjectsSecurityDescriptor,
SE_OWNER_DEFAULTED
);
} else {
ASSERT( !RtlpAreControlBitsSet( NewDescriptor, SE_OWNER_DEFAULTED ) );
}
//
// Fill in Group field in new SD
//
if ( NewGroup != NULL) {
RtlCopyMemory( Field, NewGroup, GroupSize );
if (GroupSize < NewGroupSize) {
RtlZeroMemory( Field + GroupSize, NewGroupSize - GroupSize);
}
NewDescriptor->Group = RtlPointerToOffset(Base,Field);
}
if (!NewGroupPresent) {
//
// Propagate the SE_GROUP_DEFAULTED bit from the old SD
// If a new owner is being assigned, we want to leave
// SE_GROUP_DEFAULTED off, which means leave it alone.
//
RtlpPropagateControlBits(
NewDescriptor,
*IObjectsSecurityDescriptor,
SE_GROUP_DEFAULTED
);
} else {
ASSERT( !RtlpAreControlBitsSet( NewDescriptor, SE_GROUP_DEFAULTED ) );
}
//
// Free old descriptor
//
// Kernel version doesn't free the old descriptor
#ifndef NTOS_KERNEL_RUNTIME
RtlFreeHeap( HeapHandle, 0, (PVOID) *IObjectsSecurityDescriptor );
#endif // NTOS_KERNEL_RUNTIME
*ObjectsSecurityDescriptor = (PSECURITY_DESCRIPTOR)NewDescriptor;
Status = STATUS_SUCCESS;
Cleanup:
if ( LocalDaclAllocated ) {
#ifdef NTOS_KERNEL_RUNTIME
ExFreePool( LocalDacl );
#else // NTOS_KERNEL_RUNTIME
RtlFreeHeap( HeapHandle, 0, LocalDacl );
#endif // NTOS_KERNEL_RUNTIME
}
if ( LocalSaclAllocated ) {
#ifdef NTOS_KERNEL_RUNTIME
ExFreePool( LocalSacl );
#else // NTOS_KERNEL_RUNTIME
RtlFreeHeap( HeapHandle, 0, LocalSacl );
#endif // NTOS_KERNEL_RUNTIME
}
if (ServerAclAllocated) {
#ifdef NTOS_KERNEL_RUNTIME
ExFreePool( ServerDacl );
#else // NTOS_KERNEL_RUNTIME
RtlFreeHeap( HeapHandle, 0, ServerDacl );
#endif // NTOS_KERNEL_RUNTIME
}
return( Status );
}
BOOLEAN RtlpValidateSDOffsetAndSize (
IN ULONG Offset,
IN ULONG Length,
IN ULONG MinLength,
OUT PULONG MaxLength
)
/*++
Routine Description:
This procedure validates offsets within a SecurityDescriptor.
It checks that the structure can have the minimum length,
not overlap with the fixed header and returns the maximum size
of the item and longword alignment.
Arguments:
Offset - Offset from start of SD of structure to validate
Length - Total size of SD
MinLength - Minimum size this structure can be
MaxLength - Retuns the maximum length this item can be given by
the enclosing structure.
Return Value:
BOOLEAN - TRUE if the item is valid
--*/
{
ULONG Left;
*MaxLength = 0;
//
// Don't allow overlap with header just in case caller modifies control bits etc
//
if (Offset < sizeof (SECURITY_DESCRIPTOR_RELATIVE)) {
return FALSE;
}
//
// Don't allow offsets beyond the end of the buffer
//
if (Offset >= Length) {
return FALSE;
}
//
// Calculate maximim size of segment and check its limits
//
Left = Length - Offset;
if (Left < MinLength) {
return FALSE;
}
//
// Reject unaligned offsets
//
if (Offset & (sizeof (ULONG) - 1)) {
return FALSE;
}
*MaxLength = Left;
return TRUE;
}
BOOLEAN
RtlValidRelativeSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptorInput,
IN ULONG SecurityDescriptorLength,
IN SECURITY_INFORMATION RequiredInformation
)
/*++
Routine Description:
This procedure validates a SecurityDescriptor's structure
contained within a flat buffer. This involves validating
the revision levels of each component of the security
descriptor.
Arguments:
SecurityDescriptor - Pointer to the SECURITY_DESCRIPTOR structure
to validate.
SecurityDescriptorLength - Size of flat buffer containing the security
descriptor.
RequiredInformation - Which SD components must be present to be valid.
OWNER_SECURITY_INFORMATION etc as a bit mask.
OWNER_SECURITY_INFORMATION - There must be a valid owner SID
GROUP_SECURITY_INFORMATION - There must be a valid group SID
DACL_SECURITY_INFORMATION - Ignored
SACL_SECURITY_INFORMATION - Ignored
Return Value:
BOOLEAN - TRUE if the structure of SecurityDescriptor is valid.
--*/
{
PISECURITY_DESCRIPTOR_RELATIVE SecurityDescriptor;
PISID OwnerSid;
PISID GroupSid;
PACE_HEADER Ace;
PACL Dacl;
PACL Sacl;
ULONG MaxOwnerSidLength;
ULONG MaxGroupSidLength;
ULONG MaxDaclLength;
ULONG MaxSaclLength;
if (SecurityDescriptorLength < sizeof(SECURITY_DESCRIPTOR_RELATIVE)) {
return FALSE;
}
//
// Check the revision information.
//
if (((PISECURITY_DESCRIPTOR) SecurityDescriptorInput)->Revision !=
SECURITY_DESCRIPTOR_REVISION) {
return FALSE;
}
//
// Make sure the passed SecurityDescriptor is in self-relative form
//
if (!(((PISECURITY_DESCRIPTOR) SecurityDescriptorInput)->Control & SE_SELF_RELATIVE)) {
return FALSE;
}
SecurityDescriptor = (PISECURITY_DESCRIPTOR_RELATIVE) SecurityDescriptorInput;
//
// Validate the owner if it's there and see if its allowed to be missing
//
if (SecurityDescriptor->Owner == 0) {
if (RequiredInformation & OWNER_SECURITY_INFORMATION) {
return FALSE;
}
} else {
if (!RtlpValidateSDOffsetAndSize (SecurityDescriptor->Owner,
SecurityDescriptorLength,
sizeof (SID),
&MaxOwnerSidLength)) {
return FALSE;
}
//
// It is safe to reference the owner's SubAuthorityCount, compute the
// expected length of the SID
//
OwnerSid = (PSID)RtlOffsetToPointer (SecurityDescriptor,
SecurityDescriptor->Owner);
if (OwnerSid->Revision != SID_REVISION) {
return FALSE;
}
if (OwnerSid->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) {
return FALSE;
}
if (MaxOwnerSidLength < (ULONG) SeLengthSid (OwnerSid)) {
return FALSE;
}
}
//
// The owner appears to be a structurally valid SID that lies within
// the bounds of the security descriptor. Do the same for the Group
// if there is one.
//
//
// Validate the group if it's there and see if its allowed to be missing
//
if (SecurityDescriptor->Group == 0) {
if (RequiredInformation & GROUP_SECURITY_INFORMATION) {
return FALSE;
}
} else {
if (!RtlpValidateSDOffsetAndSize (SecurityDescriptor->Group,
SecurityDescriptorLength,
sizeof (SID),
&MaxGroupSidLength)) {
return FALSE;
}
//
// It is safe to reference the group's SubAuthorityCount, compute the
// expected length of the SID
//
GroupSid = (PSID)RtlOffsetToPointer (SecurityDescriptor,
SecurityDescriptor->Group);
if (GroupSid->Revision != SID_REVISION) {
return FALSE;
}
if (GroupSid->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) {
return FALSE;
}
if (MaxGroupSidLength < (ULONG) SeLengthSid (GroupSid)) {
return FALSE;
}
}
//
// Validate the DACL if it's there and check if its allowed to be missing.
//
if (!RtlpAreControlBitsSet (SecurityDescriptor, SE_DACL_PRESENT)) {
//
// Some code does this kind of thing:
//
// InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
// RegSetKeySecurity(hKey, DACL_SECURITY_INFORMATION, &sd) )
//
// With the current system this works the same as passing in a NULL DACL but it looks
// almost by accident
//
// if (RequiredInformation & DACL_SECURITY_INFORMATION) {
// return FALSE;
// }
} else if (SecurityDescriptor->Dacl) {
if (!RtlpValidateSDOffsetAndSize (SecurityDescriptor->Dacl,
SecurityDescriptorLength,
sizeof (ACL),
&MaxDaclLength)) {
return FALSE;
}
Dacl = (PACL) RtlOffsetToPointer (SecurityDescriptor,
SecurityDescriptor->Dacl);
//
// Make sure the DACL length fits within the bounds of the security descriptor.
//
if (MaxDaclLength < Dacl->AclSize) {
return FALSE;
}
//
// Make sure the ACL is structurally valid.
//
if (!RtlValidAcl (Dacl)) {
return FALSE;
}
}
//
// Validate the SACL if it's there and check if its allowed to be missing.
//
if (!RtlpAreControlBitsSet (SecurityDescriptor, SE_SACL_PRESENT)) {
// if (RequiredInformation & SACL_SECURITY_INFORMATION) {
// return FALSE;
// }
} else if (SecurityDescriptor->Sacl) {
if (!RtlpValidateSDOffsetAndSize (SecurityDescriptor->Sacl,
SecurityDescriptorLength,
sizeof (ACL),
&MaxSaclLength)) {
return FALSE;
}
Sacl = (PACL) RtlOffsetToPointer (SecurityDescriptor,
SecurityDescriptor->Sacl);
//
// Make sure the SACL length fits within the bounds of the security descriptor.
//
if (MaxSaclLength < Sacl->AclSize) {
return FALSE;
}
//
// Make sure the ACL is structurally valid.
//
if (!RtlValidAcl (Sacl)) {
return FALSE;
}
}
return TRUE;
}
BOOLEAN
RtlGetSecurityDescriptorRMControl(
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
OUT PUCHAR RMControl
)
/*++
Routine Description:
This procedure returns the RM Control flags from a SecurityDescriptor if
SE_RM_CONTROL_VALID flags is present in the control field.
Arguments:
SecurityDescriptor - Pointer to the SECURITY_DESCRIPTOR structure
RMControl - Returns the flags in the SecurityDescriptor if
SE_RM_CONTROL_VALID is set in the control bits of the
SecurityDescriptor.
Return Value:
BOOLEAN - TRUE if SE_RM_CONTROL_VALID is set in the Control bits of the
SecurityDescriptor.
Note:
Parameter validation has already been done in Advapi.
--*/
{
PISECURITY_DESCRIPTOR ISecurityDescriptor = (PISECURITY_DESCRIPTOR) SecurityDescriptor;
if (!(ISecurityDescriptor->Control & SE_RM_CONTROL_VALID))
{
*RMControl = 0;
return FALSE;
}
*RMControl = ISecurityDescriptor->Sbz1;
return TRUE;
}
BOOLEAN
RtlpGuidPresentInGuidList(
IN GUID *InheritedObjectType,
IN GUID **pNewObjectType,
IN ULONG GuidCount
)
/*++
Routine Description:
This routine returns whether a given guid is present in a list of guids.
Arguments:
InheritedObjectType - Guid from the ace that will be compared against
the object types for the object.
pNewObjectType - List of types of object being inherited to.
GuidCount - Number of object types in the list.
Return Value:
Returns TRUE if the given guid is present in the list of guids.
FALSE otherwise.
--*/
{
ULONG i;
for (i = 0; i < GuidCount; i++) {
if (RtlEqualMemory(
InheritedObjectType,
pNewObjectType[i],
sizeof(GUID) ) ) {
return TRUE;
}
}
return FALSE;
}
VOID
RtlSetSecurityDescriptorRMControl(
IN OUT PSECURITY_DESCRIPTOR SecurityDescriptor,
IN PUCHAR RMControl OPTIONAL
)
/*++
Routine Description:
This procedure sets the RM Control flag in the control field of
SecurityDescriptor and sets Sbz1 to the the byte to which RMContol points.
If RMControl is NULL then the bits are cleared.
Arguments:
SecurityDescriptor - Pointer to the SECURITY_DESCRIPTOR structure
RMControl - Pointer to the flags to set. If NULL then the bits
are cleared.
Note:
Parameter validation has already been done in Advapi.
--*/
{
PISECURITY_DESCRIPTOR ISecurityDescriptor = (PISECURITY_DESCRIPTOR) SecurityDescriptor;
if (ARGUMENT_PRESENT(RMControl)) {
ISecurityDescriptor->Control |= SE_RM_CONTROL_VALID;
ISecurityDescriptor->Sbz1 = *RMControl;
} else {
ISecurityDescriptor->Control &= ~SE_RM_CONTROL_VALID;
ISecurityDescriptor->Sbz1 = 0;
}
}
#endif // #ifndef BLDR_KERNEL_RUNTIME
NTSTATUS
RtlMapSecurityErrorToNtStatus(
IN SECURITY_STATUS Error
)
/*++
Routine Description:
This procedure maps a security HRESULT to the proper NTSTATUS code.
Arguments:
Error - a security HRESULT
Return Value: The NTSTATUS code corresponding to the HRESULT. If no
status code can be mapped, the original error is returned.
Note:
--*/
{
NTSTATUS Status;
switch(Error) {
case SEC_E_INSUFFICIENT_MEMORY : Status = STATUS_INSUFFICIENT_RESOURCES; break;
case SEC_E_INVALID_HANDLE : Status = STATUS_INVALID_HANDLE; break;
case SEC_E_UNSUPPORTED_FUNCTION : Status = STATUS_NOT_SUPPORTED; break;
case SEC_E_TARGET_UNKNOWN : Status = STATUS_BAD_NETWORK_PATH; break;
case SEC_E_INTERNAL_ERROR : Status = STATUS_INTERNAL_ERROR; break;
case SEC_E_SECPKG_NOT_FOUND : Status = STATUS_NO_SUCH_PACKAGE; break;
case SEC_E_NOT_OWNER : Status = STATUS_PRIVILEGE_NOT_HELD; break;
case SEC_E_CANNOT_INSTALL : Status = STATUS_NO_SUCH_PACKAGE; break;
case SEC_E_INVALID_TOKEN : Status = STATUS_INVALID_PARAMETER; break;
case SEC_E_CANNOT_PACK : Status = STATUS_INVALID_PARAMETER; break;
case SEC_E_QOP_NOT_SUPPORTED : Status = STATUS_NOT_SUPPORTED; break;
case SEC_E_NO_IMPERSONATION : Status = STATUS_CANNOT_IMPERSONATE; break;
case SEC_E_LOGON_DENIED : Status = STATUS_LOGON_FAILURE; break;
case SEC_E_UNKNOWN_CREDENTIALS : Status = STATUS_NO_SUCH_LOGON_SESSION; break;
case SEC_E_NO_CREDENTIALS : Status = STATUS_NO_SUCH_LOGON_SESSION; break;
case SEC_E_MESSAGE_ALTERED : Status = STATUS_ACCESS_DENIED; break;
case SEC_E_OUT_OF_SEQUENCE : Status = STATUS_ACCESS_DENIED; break;
case SEC_E_NO_AUTHENTICATING_AUTHORITY : Status = STATUS_NO_LOGON_SERVERS; break;
case SEC_E_BAD_PKGID : Status = STATUS_NO_SUCH_PACKAGE; break;
case SEC_E_TIME_SKEW : Status = STATUS_TIME_DIFFERENCE_AT_DC; break;
default: Status = (NTSTATUS) Error;
}
return(Status);
}