7095 lines
277 KiB
C
7095 lines
277 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
|
|
--*/
|
|
|
|
#include "ntrtlp.h"
|
|
#include <stdio.h>
|
|
#include "seopaque.h"
|
|
#include "sertlp.h"
|
|
#ifdef NTOS_KERNEL_RUNTIME
|
|
#include <..\se\sep.h>
|
|
#else // NTOS_KERNEL_RUNTIME
|
|
#include <..\dll\ldrp.h>
|
|
#endif // NTOS_KERNEL_RUNTIME
|
|
|
|
// BUG, BUG does anybody use this routine - no prototype in ntrtl.h
|
|
|
|
ULONG RtlLengthUsedSecurityDescriptor(IN PSECURITY_DESCRIPTOR SecurityDescriptor);
|
|
|
|
#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 * NewObjectType OPTIONAL,
|
|
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,
|
|
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 * NewObjectType OPTIONAL,
|
|
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 * NewObjectType OPTIONAL,
|
|
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 * NewObjectType OPTIONAL,
|
|
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, IN GUID * ObjectType OPTIONAL);
|
|
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,RtlFreeSid)
|
|
#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,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,RtlLengthUsedSecurityDescriptor)
|
|
#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,RtlGetSecurityDescriptorRMControl)
|
|
#pragma alloc_text(PAGE,RtlSetSecurityDescriptorRMControl)
|
|
#pragma alloc_text(PAGE,RtlAreAllAccessesGranted)
|
|
#pragma alloc_text(PAGE,RtlAreAnyAccessesGranted)
|
|
#pragma alloc_text(PAGE,RtlMapGenericMask)
|
|
#pragma alloc_text(PAGE,RtlpApplyAclToObject)
|
|
#pragma alloc_text(PAGE,RtlpContainsCreatorGroupSid)
|
|
#pragma alloc_text(PAGE,RtlpContainsCreatorOwnerSid)
|
|
#pragma alloc_text(PAGE,RtlpCopyEffectiveAce)
|
|
#pragma alloc_text(PAGE,RtlpCopyAces)
|
|
#pragma alloc_text(PAGE,RtlpGenerateInheritAcl)
|
|
#pragma alloc_text(PAGE,RtlpGenerateInheritedAce)
|
|
#pragma alloc_text(PAGE,RtlpInheritAcl)
|
|
#pragma alloc_text(PAGE,RtlpInheritAcl2)
|
|
#pragma alloc_text(PAGE,RtlpComputeMergedAcl)
|
|
#pragma alloc_text(PAGE,RtlpComputeMergedAcl2)
|
|
#pragma alloc_text(PAGE,RtlpValidOwnerSubjectContext)
|
|
#pragma alloc_text(PAGE,RtlpConvertToAutoInheritSecurityObject)
|
|
#pragma alloc_text(PAGE,RtlpCompareAces)
|
|
#pragma alloc_text(PAGE,RtlpCompareKnownAces)
|
|
#pragma alloc_text(PAGE,RtlpCompareKnownObjectAces)
|
|
#pragma alloc_text(PAGE,RtlpIsDuplicateAce)
|
|
#pragma alloc_text(PAGE,RtlpConvertAclToAutoInherit)
|
|
#pragma alloc_text(PAGE,RtlSetSecurityObjectEx)
|
|
#pragma alloc_text(PAGE,RtlpCreateServerAcl)
|
|
#pragma alloc_text(PAGE,RtlValidRelativeSecurityDescriptor)
|
|
#pragma alloc_text(PAGE,RtlpValidateSDOffsetAndSize)
|
|
#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.
|
|
|
|
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
|
|
|
|
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)
|
|
};
|
|
|
|
BOOLEAN RtlpVerboseConvert = FALSE;
|
|
|
|
#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 )
|
|
|
|
|
|
|
|
|
|
// 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));
|
|
}
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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).
|
|
--*/
|
|
{
|
|
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.
|
|
--*/
|
|
{
|
|
PISID ISid;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
ISid = (PISID)Sid;// Typecast to the opaque 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.
|
|
--*/
|
|
{
|
|
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.
|
|
--*/
|
|
{
|
|
PISID ISid;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
ISid = (PISID)Sid;// Typecast to the opaque 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;
|
|
}
|
|
|
|
RtlMoveMemory(DestinationSid, SourceSid, SidLength);// Buffer is large enough
|
|
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.
|
|
--*/
|
|
{
|
|
RTL_PAGED_CODE();
|
|
|
|
(*DestinationLuid) = (*SourceLuid);
|
|
}
|
|
|
|
|
|
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.
|
|
--*/
|
|
{
|
|
ULONG Index = 0;
|
|
|
|
RTL_PAGED_CODE();
|
|
|
|
while (Index < ArrayLength) {
|
|
Target[Index] = Source[Index];
|
|
Index += 1;
|
|
} //end_while
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
ULONG RtlLengthUsedSecurityDescriptor(IN PSECURITY_DESCRIPTOR SecurityDescriptor)
|
|
/*++
|
|
Routine Description:
|
|
This routine returns the length, in bytes, in use in a structurally valid SECURITY_DESCRIPTOR.
|
|
|
|
This is the number of bytes necessary to capture the security descriptor, which may be less the the current actual length of the security descriptor
|
|
(RtlLengthSecurityDescriptor() is used to retrieve the actual length).
|
|
|
|
Notice that the used length and actual length may differ if either the SACL or DACL include padding bytes.
|
|
|
|
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 used length is to be returned.
|
|
The SECURITY_DESCRIPTOR's structure is assumed to be valid.
|
|
Return Value:
|
|
ULONG - Number of bytes of the SECURITY_DESCRIPTOR that are in use.
|
|
--*/
|
|
{
|
|
ULONG sum;
|
|
ACL_SIZE_INFORMATION AclSize;
|
|
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) {
|
|
RtlQueryInformationAcl(Temp, (PVOID)&AclSize, sizeof(AclSize), AclSizeInformation);
|
|
sum += LongAlignSize(AclSize.AclBytesInUse);
|
|
}
|
|
|
|
// Add in used length of System Acl
|
|
Temp = RtlpSaclAddrSecurityDescriptor(ISecurityDescriptor);
|
|
if (Temp != NULL) {
|
|
RtlQueryInformationAcl(Temp, (PVOID)&AclSize, sizeof(AclSize), AclSizeInformation);
|
|
sum += LongAlignSize(AclSize.AclBytesInUse);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// BUGBUG: 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) {
|
|
*Dacl = RtlpDaclAddrSecurityDescriptor(ISecurityDescriptor);// Assign the ACL address.
|
|
*DaclDefaulted = RtlpAreControlBitsSet(ISecurityDescriptor, SE_DACL_DEFAULTED);// Assign DaclDefaulted flag.
|
|
}
|
|
|
|
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) {
|
|
*Sacl = RtlpSaclAddrSecurityDescriptor(ISecurityDescriptor);// Assign the ACL address.
|
|
*SaclDefaulted = RtlpAreControlBitsSet(ISecurityDescriptor, SE_SACL_DEFAULTED);// Assign SaclDefaulted flag.
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
*Owner = RtlpOwnerAddrSecurityDescriptor(ISecurityDescriptor);// Return the Owner field value.
|
|
*OwnerDefaulted = RtlpAreControlBitsSet(ISecurityDescriptor, SE_OWNER_DEFAULTED);// Return the OwnerDefaulted flag value.
|
|
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.
|
|
--*/
|
|
{
|
|
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);
|
|
}
|
|
|
|
|
|
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.
|
|
--*/
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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 * NewObjectType OPTIONAL,
|
|
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
|
|
NewObjectType - Type of object being inherited to. If not specified, the object has no object type.
|
|
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;
|
|
PSID LocalServerOwner;
|
|
PSID LocalServerGroup;
|
|
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);
|
|
RtlInitializeSid((PSID)CreatorSid, &CreatorSidAuthority, 1);
|
|
*(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));
|
|
|
|
ServerSidToCopy = RtlCompoundAceServerSid(OldAce);// Compound ACEs have two SIDs (Map one now).
|
|
|
|
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 (NewObjectType == NULL || !RtlEqualMemory(InheritedObjectType, NewObjectType, sizeof(GUID))) {
|
|
LengthRequired = 0;
|
|
|
|
// If the object type matches the inherited object type, Inherit an ACE with no inherited object type.
|
|
} else {
|
|
*ObjectAceInherited = TRUE;// Tell the caller we inherited an object type specific ACE.
|
|
|
|
// 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];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
((PKNOWN_ACE)*AcePosition)->Mask = LocalMask;// Put the mapped access mask in the new ACE
|
|
}
|
|
}
|
|
}
|
|
} 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 {
|
|
RtlCopyMemory(*AcePosition, OldAce, LengthRequired);// Not a known ACE type, copy ACE as is
|
|
}
|
|
}
|
|
|
|
// 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,
|
|
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.
|
|
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;
|
|
|
|
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 = (((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;
|
|
|
|
// Copy the effective ACE into the ACL.
|
|
if (!RtlpCopyEffectiveAce(
|
|
OldAce,
|
|
FALSE, // Don't set the auto inherit bit
|
|
GenerateInheritAce,
|
|
ClientOwnerSid,
|
|
ClientGroupSid,
|
|
ServerOwnerSid,
|
|
ServerGroupSid,
|
|
GenericMapping,
|
|
NULL, // Always copy object ACES
|
|
&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 * NewObjectType OPTIONAL,
|
|
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.
|
|
NewObjectType - Type of object being inherited to. If not specified, the object has no object type.
|
|
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, &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,
|
|
NewObjectType,
|
|
&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);
|
|
}
|
|
|
|
// BUGBUG: 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 * NewObjectType OPTIONAL,
|
|
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.
|
|
NewObjectType - Type of object being inherited to. If not specified, the object has no object type.
|
|
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,
|
|
NewObjectType,
|
|
&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 * NewObjectType OPTIONAL,
|
|
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
|
|
NewObjectType - Type of object being inherited to. If not specified, the object has no object type.
|
|
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 THE HELL //
|
|
// 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,
|
|
NewObjectType,
|
|
&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, NewObjectType)) {
|
|
// 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, NewObjectType)) {
|
|
// 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 * NewObjectType OPTIONAL,
|
|
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.
|
|
NewObjectType - Type of object being inherited to. If not specified, the object has no object type.
|
|
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,
|
|
NewObjectType,
|
|
&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
|
|
&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
|
|
&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
|
|
&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,
|
|
&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);
|
|
RtlInitializeSid((PSID)CreatorSid, &CreatorSidAuthority, 1);
|
|
*(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);
|
|
RtlInitializeSid((PSID)CreatorSid, &CreatorSidAuthority, 1);
|
|
*(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);
|
|
RtlInitializeSid((PSID)CreatorOwnerSid, &CreatorSidAuthority, 1);
|
|
*(RtlpSubAuthoritySid((PSID)CreatorOwnerSid, 0)) = SECURITY_CREATOR_OWNER_RID;
|
|
|
|
RtlInitializeSid((PSID)CreatorGroupSid, &CreatorSidAuthority, 1);
|
|
*(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,
|
|
&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,
|
|
&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, IN GUID * ObjectType OPTIONAL)
|
|
/*++
|
|
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".
|
|
ObjectType - GUID of the object type represented by Acl. If the object has no GUID associated with it, then this argument is specified as NULL.
|
|
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.
|
|
--*/
|
|
{
|
|
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 * ObjectType OPTIONAL,
|
|
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;
|
|
ULONG NewGroupSize;
|
|
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
|
|
|
|
#ifdef NTOS_KERNEL_RUNTIME
|
|
RequestorMode = KeGetPreviousMode();// Get the previous mode of the caller
|
|
#else // NTOS_KERNEL_RUNTIME
|
|
RequestorMode = UserMode;
|
|
|
|
HeapHandle = RtlProcessHeap();// Get the handle to the current process heap
|
|
|
|
// 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) {
|
|
Base = SubjectContextInfo;// Copy in the data
|
|
|
|
// 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;
|
|
if (NewOwner == NULL) {// Ensure an owner is now defined.
|
|
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
|
|
ObjectType,
|
|
&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
|
|
ObjectType,
|
|
&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;
|
|
}
|
|
|
|
// 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, // BUGWARNING need service name
|
|
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.
|
|
NewOwnerSize = LongAlignSize(SeLengthSid(NewOwner));
|
|
if (NewGroup != NULL) {
|
|
NewGroupSize = LongAlignSize(SeLengthSid(NewGroup));
|
|
}
|
|
|
|
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);
|
|
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);
|
|
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:
|
|
// 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;
|
|
ULONG NewGroupSize;
|
|
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) {
|
|
// 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.
|
|
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
|
|
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);
|
|
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);
|
|
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, SeLengthSid(NewOwner));
|
|
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, SeLengthSid(NewGroup));
|
|
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;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
} |