11591 lines
319 KiB
C
11591 lines
319 KiB
C
/*++
|
||
|
||
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);
|
||
}
|