Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

5010 lines
125 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
sertl.c
Abstract:
This Module implements many security rtl routines defined in ntseapi.h
Author:
Jim Kelly (JimK) 23-Mar-1990
Robert Reichel (RobertRe) 1-Mar-1991
Environment:
Pure Runtime Library Routine
Revision History:
--*/
#include "ntrtlp.h"
#include <stdio.h>
#include "seopaque.h"
#include "sertlp.h"
//
// 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
);
#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,RtlAllocateAndInitializeSid)
#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,RtlValidSecurityDescriptor)
#pragma alloc_text(PAGE,RtlLengthSecurityDescriptor)
#pragma alloc_text(PAGE,RtlLengthUsedSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetControlSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetDaclSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetDaclSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetSaclSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetSaclSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetOwnerSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetOwnerSecurityDescriptor)
#pragma alloc_text(PAGE,RtlSetGroupSecurityDescriptor)
#pragma alloc_text(PAGE,RtlGetGroupSecurityDescriptor)
#pragma alloc_text(PAGE,RtlAreAllAccessesGranted)
#pragma alloc_text(PAGE,RtlAreAnyAccessesGranted)
#pragma alloc_text(PAGE,RtlMapGenericMask)
#pragma alloc_text(PAGE,RtlpApplyAclToObject)
#pragma alloc_text(PAGE,RtlpContainsCreatorGroupSid)
#pragma alloc_text(PAGE,RtlpContainsCreatorOwnerSid)
#pragma alloc_text(PAGE,RtlpGenerateInheritAcl)
#pragma alloc_text(PAGE,RtlpGenerateInheritedAce)
#pragma alloc_text(PAGE,RtlpInheritAcl)
#pragma alloc_text(PAGE,RtlpLengthInheritAcl)
#pragma alloc_text(PAGE,RtlpLengthInheritedAce)
#pragma alloc_text(PAGE,RtlpValidOwnerSubjectContext)
#endif
///////////////////////////////////////////////////////////////////////////////
// //
// Local Macros and Symbols //
// //
///////////////////////////////////////////////////////////////////////////////
#define CREATOR_SID_SIZE 12
///////////////////////////////////////////////////////////////////////////////
// //
// Exported Procedures //
// //
///////////////////////////////////////////////////////////////////////////////
VOID
RtlRunEncodeUnicodeString(
PUCHAR Seed OPTIONAL,
PUNICODE_STRING String
)
/*++
Routine Description:
This function performs a trivial XOR run-encoding of a string.
The purpose of this run-encoding is to change the character values
to appear somewhat random and typically not printable. This is
useful for transforming passwords that you don't want to be easily
distinguishable by visually scanning a paging file or memory dump.
Arguments:
Seed - Points to a seed value to use in the encoding. If the
pointed to value is zero, then this routine will assign
a value.
String - The string to encode. This string may be decode
by passing it and the seed value to RtlRunDecodeUnicodeString().
Return Value:
None - Nothing can really go wrong unless the caller passes bogus
parameters. In this case, the caller can catch the access
violation.
--*/
{
LARGE_INTEGER Time;
PUCHAR LocalSeed;
NTSTATUS Status;
ULONG i;
PSTRING S;
RTL_PAGED_CODE();
//
// Typecast so we can work on bytes rather than WCHARs
//
S = (PSTRING)((PVOID)String);
//
// If a seed wasn't passed, use the 2nd byte of current time.
// This byte seems to be sufficiently random (by observation).
//
if ((*Seed) == 0) {
Status = NtQuerySystemTime ( &Time );
ASSERT(NT_SUCCESS(Status));
LocalSeed = (PUCHAR)((PVOID)&Time);
i = 1;
(*Seed) = LocalSeed[ i ];
//
// Occasionally, this byte could be zero. That would cause the
// string to become un-decodable, since 0 is the magic value that
// causes us to re-gen the seed. This loop makes sure that we
// never end up with a zero byte (unless time is zero, as well).
//
while ( ((*Seed) == 0) && ( i < sizeof( Time ) ) )
{
(*Seed) |= LocalSeed[ i++ ] ;
}
if ( (*Seed) == 0 )
{
(*Seed) = 1;
}
}
//
// Transform the initial byte.
// The funny constant just keeps the first byte from propagating
// into the second byte in the next step. Without a funny constant
// this would happen for many languages (which typically have every
// other byte zero.
//
//
if (S->Length >= 1) {
S->Buffer[0] ^= ((*Seed) | 0X43);
}
//
// Now transform the rest of the string
//
for (i=1; i<S->Length; i++) {
//
// There are export issues that cause us to want to
// keep this algorithm simple. Please don't change it
// without checking with JimK first. Thanks.
//
//
// In order to be compatible with zero terminated unicode strings,
// this algorithm is designed to not produce a wide character of
// zero as long a the seed is not zero.
//
//
// Simple running XOR with the previous byte and the
// seed value.
//
S->Buffer[i] ^= (S->Buffer[i-1]^(*Seed));
}
return;
}
VOID
RtlRunDecodeUnicodeString(
UCHAR Seed,
PUNICODE_STRING String
)
/*++
Routine Description:
This function performs the inverse of the function performed
by RtlRunEncodeUnicodeString(). Please see RtlRunEncodeUnicodeString()
for details.
Arguments:
Seed - The seed value to use in RtlRunEncodeUnicodeString().
String - The string to reveal.
Return Value:
None - Nothing can really go wrong unless the caller passes bogus
parameters. In this case, the caller can catch the access
violation.
--*/
{
ULONG
i;
PSTRING
S;
RTL_PAGED_CODE();
//
// Typecast so we can work on bytes rather than WCHARs
//
S = (PSTRING)((PVOID)String);
//
// Transform the end of the string
//
for (i=S->Length; i>1; i--) {
//
// a simple running XOR with the previous byte and the
// seed value.
//
S->Buffer[i-1] ^= (S->Buffer[i-2]^Seed);
}
//
// Finally, transform the initial byte
//
if (S->Length >= 1) {
S->Buffer[0] ^= (Seed | 0X43);
}
return;
}
VOID
RtlEraseUnicodeString(
PUNICODE_STRING String
)
/*++
Routine Description:
This function scrubs the passed string by over-writing all
characters in the string. The entire string (i.e., MaximumLength)
is erased, not just the current length.
Argumen ts:
String - The string to be erased.
Return Value:
None - Nothing can really go wrong unless the caller passes bogus
parameters. In this case, the caller can catch the access
violation.
--*/
{
RTL_PAGED_CODE();
if ((String->Buffer == NULL) || (String->MaximumLength == 0)) {
return;
}
RtlZeroMemory( (PVOID)String->Buffer, (ULONG)String->MaximumLength );
String->Length = 0;
return;
}
NTSTATUS
RtlAdjustPrivilege(
ULONG Privilege,
BOOLEAN Enable,
BOOLEAN Client,
PBOOLEAN WasEnabled
)
/*++
Routine Description:
This procedure enables or disables a privilege process-wide.
Arguments:
Privilege - The lower 32-bits of the privilege ID to be enabled or
disabled. The upper 32-bits is assumed to be zero.
Enable - A boolean indicating whether the privilege is to be enabled
or disabled. TRUE indicates the privilege is to be enabled.
FALSE indicates the privilege is to be disabled.
Client - A boolean indicating whether the privilege should be adjusted
in a client token or the process's own token. TRUE indicates
the client's token should be used (and an error returned if there
is no client token). FALSE indicates the process's token should
be used.
WasEnabled - points to a boolean to receive an indication of whether
the privilege was previously enabled or disabled. TRUE indicates
the privilege was previously enabled. FALSE indicates the privilege
was previoulsy disabled. This value is useful for returning the
privilege to its original state after using it.
Return Value:
STATUS_SUCCESS - The privilege has been sucessfully enabled or disabled.
STATUS_PRIVILEGE_NOT_HELD - The privilege is not held by the specified context.
Other status values as may be returned by:
NtOpenProcessToken()
NtAdjustPrivilegesToken()
--*/
{
NTSTATUS
Status,
TmpStatus;
HANDLE
Token;
LUID
LuidPrivilege;
PTOKEN_PRIVILEGES
NewPrivileges,
OldPrivileges;
ULONG
Length;
UCHAR
Buffer1[sizeof(TOKEN_PRIVILEGES)+
((1-ANYSIZE_ARRAY)*sizeof(LUID_AND_ATTRIBUTES))],
Buffer2[sizeof(TOKEN_PRIVILEGES)+
((1-ANYSIZE_ARRAY)*sizeof(LUID_AND_ATTRIBUTES))];
RTL_PAGED_CODE();
NewPrivileges = (PTOKEN_PRIVILEGES)Buffer1;
OldPrivileges = (PTOKEN_PRIVILEGES)Buffer2;
//
// Open the appropriate token...
//
if (Client == TRUE) {
Status = NtOpenThreadToken(
NtCurrentThread(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
FALSE,
&Token
);
} else {
Status = NtOpenProcessToken(
NtCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&Token
);
}
if (!NT_SUCCESS(Status)) {
return(Status);
}
//
// Initialize the privilege adjustment structure
//
LuidPrivilege = RtlConvertUlongToLuid(Privilege);
NewPrivileges->PrivilegeCount = 1;
NewPrivileges->Privileges[0].Luid = LuidPrivilege;
NewPrivileges->Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0;
//
// Adjust the privilege
//
Status = NtAdjustPrivilegesToken(
Token, // TokenHandle
FALSE, // DisableAllPrivileges
NewPrivileges, // NewPrivileges
sizeof(Buffer1), // BufferLength
OldPrivileges, // PreviousState (OPTIONAL)
&Length // ReturnLength
);
TmpStatus = NtClose(Token);
ASSERT(NT_SUCCESS(TmpStatus));
//
// Map the success code NOT_ALL_ASSIGNED to an appropriate error
// since we're only trying to adjust the one privilege.
//
if (Status == STATUS_NOT_ALL_ASSIGNED) {
Status = STATUS_PRIVILEGE_NOT_HELD;
}
if (NT_SUCCESS(Status)) {
//
// If there are no privileges in the previous state, there were
// no changes made. The previous state of the privilege
// is whatever we tried to change it to.
//
if (OldPrivileges->PrivilegeCount == 0) {
(*WasEnabled) = Enable;
} else {
(*WasEnabled) =
(OldPrivileges->Privileges[0].Attributes & SE_PRIVILEGE_ENABLED)
? TRUE : FALSE;
}
}
return(Status);
}
BOOLEAN
RtlValidSid (
IN PSID Sid
)
/*++
Routine Description:
This procedure validates an SID's structure.
Arguments:
Sid - Pointer to the SID structure to validate.
Return Value:
BOOLEAN - TRUE if the structure of Sid is valid.
--*/
{
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 ((((SID *)Sid)->Revision & 0x0f) == SID_REVISION) {
if (((SID *)Sid)->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES) {
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));
}
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 );
}
NTSTATUS
RtlInitializeSid(
IN PSID Sid,
IN PSID_IDENTIFIER_AUTHORITY IdentifierAuthority,
IN UCHAR SubAuthorityCount
)
/*++
Routine Description:
This function initializes an SID data structure. It does not, however,
set the sub-authority values. This must be done separately.
Arguments:
Sid - Pointer to the SID data structure to initialize.
IdentifierAuthority - Pointer to the Identifier Authority value to
set in the SID.
SubAuthorityCount - The number of sub-authorities that will be placed in
the SID (a separate action).
Return Value:
--*/
{
PISID ISid;
RTL_PAGED_CODE();
//
// Typecast to the opaque SID
//
ISid = (PISID)Sid;
if ( SubAuthorityCount > SID_MAX_SUB_AUTHORITIES ) {
return( STATUS_INVALID_PARAMETER );
}
ISid->SubAuthorityCount = (UCHAR)SubAuthorityCount;
ISid->Revision = 1;
ISid->IdentifierAuthority = *IdentifierAuthority;
return( STATUS_SUCCESS );
}
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;
}
PSID_IDENTIFIER_AUTHORITY
RtlIdentifierAuthoritySid(
IN PSID Sid
)
/*++
Routine Description:
This function returns the address of an SID's IdentifierAuthority field.
Arguments:
Sid - Pointer to the SID data structure.
Return Value:
--*/
{
PISID ISid;
RTL_PAGED_CODE();
//
// Typecast to the opaque SID
//
ISid = (PISID)Sid;
return &(ISid->IdentifierAuthority);
}
PULONG
RtlSubAuthoritySid(
IN PSID Sid,
IN ULONG SubAuthority
)
/*++
Routine Description:
This function returns the address of a sub-authority array element of
an SID.
Arguments:
Sid - Pointer to the SID data structure.
SubAuthority - An index indicating which sub-authority is being specified.
This value is not compared against the number of sub-authorities in the
SID for validity.
Return Value:
--*/
{
PISID ISid;
RTL_PAGED_CODE();
//
// Typecast to the opaque SID
//
ISid = (PISID)Sid;
return &(ISid->SubAuthority[SubAuthority]);
}
PUCHAR
RtlSubAuthorityCountSid(
IN PSID Sid
)
/*++
Routine Description:
This function returns the address of the sub-authority count field of
an SID.
Arguments:
Sid - Pointer to the SID data structure.
Return Value:
--*/
{
PISID ISid;
RTL_PAGED_CODE();
//
// Typecast to the opaque SID
//
ISid = (PISID)Sid;
return &(ISid->SubAuthorityCount);
}
ULONG
RtlLengthSid (
IN PSID Sid
)
/*++
Routine Description:
This routine returns the length, in bytes, of a structurally valid SID.
Arguments:
Sid - Points to the SID whose length is to be returned. The
SID's structure is assumed to be valid.
Return Value:
ULONG - The length, in bytes, of the SID.
--*/
{
RTL_PAGED_CODE();
return SeLengthSid(Sid);
}
NTSTATUS
RtlCopySid (
IN ULONG DestinationSidLength,
OUT PSID DestinationSid,
IN PSID SourceSid
)
/*++
Routine Description:
This routine copies the value of the source SID to the destination
SID.
Arguments:
DestinationSidLength - Indicates the length, in bytes, of the
destination SID buffer.
DestinationSid - Pointer to a buffer to receive a copy of the
source Sid value.
SourceSid - Supplies the Sid value to be copied.
Return Value:
STATUS_SUCCESS - Indicates the SID was successfully copied.
STATUS_BUFFER_TOO_SMALL - Indicates the target buffer wasn't
large enough to receive a copy of the SID.
--*/
{
ULONG SidLength = SeLengthSid(SourceSid);
RTL_PAGED_CODE();
if (SidLength > DestinationSidLength) {
return STATUS_BUFFER_TOO_SMALL;
}
//
// Buffer is large enough
//
RtlMoveMemory( DestinationSid, SourceSid, SidLength );
return STATUS_SUCCESS;
}
NTSTATUS
RtlCopySidAndAttributesArray (
IN ULONG ArrayLength,
IN PSID_AND_ATTRIBUTES Source,
IN ULONG TargetSidBufferSize,
OUT PSID_AND_ATTRIBUTES TargetArrayElement,
OUT PSID TargetSid,
OUT PSID *NextTargetSid,
OUT PULONG RemainingTargetSidBufferSize
)
/*++
Routine Description:
This routine copies the value of the source SID_AND_ATTRIBUTES array
to the target. The actual SID values are placed according to a separate
parameter. This allows multiple arrays to be merged using this service
to copy each.
Arguments:
ArrayLength - Number of elements in the source array to copy.
Source - Pointer to the source array.
TargetSidBufferSize - Indicates the length, in bytes, of the buffer
to receive the actual SID values. If this value is less than
the actual amount needed, then STATUS_BUFFER_TOO_SMALL is returned.
TargetArrayElement - Indicates where the array elements are to be
copied to (but not the SID values themselves).
TargetSid - Indicates where the target SID values s are to be copied. This
is assumed to be ULONG aligned. Each SID value will be copied
into this buffer. Each SID will be ULONG aligned.
NextTargetSid - On completion, will be set to point to the ULONG
aligned address following the last SID copied.
RemainingTargetSidBufferSize - On completion, receives an indicatation
of how much of the SID buffer is still unused.
Return Value:
STATUS_SUCCESS - The call completed successfully.
STATUS_BUFFER_TOO_SMALL - Indicates the buffer to receive the SID
values wasn't large enough.
--*/
{
ULONG Index = 0;
PSID NextSid = TargetSid;
ULONG NextSidLength;
ULONG AlignedSidLength;
ULONG RemainingLength = TargetSidBufferSize;
RTL_PAGED_CODE();
while (Index < ArrayLength) {
NextSidLength = SeLengthSid( Source[Index].Sid );
AlignedSidLength = (ULONG)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)((ULONG)NextSid + AlignedSidLength);
Index += 1;
} //end_while
(*NextTargetSid) = NextSid;
(*RemainingTargetSidBufferSize) = RemainingLength;
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;
UCHAR Buffer[256];
UCHAR String[256];
UCHAR i;
ULONG Tmp;
PISID iSid = (PISID)Sid; // pointer to opaque structure
ANSI_STRING AnsiString;
RTL_PAGED_CODE();
if (RtlValidSid( Sid ) != TRUE) {
return(STATUS_INVALID_SID);
}
_snprintf(Buffer, sizeof(Buffer), "S-%u-", (USHORT)iSid->Revision );
strcpy(String, Buffer);
if ( (iSid->IdentifierAuthority.Value[0] != 0) ||
(iSid->IdentifierAuthority.Value[1] != 0) ){
_snprintf(Buffer, sizeof(Buffer), "0x%02hx%02hx%02hx%02hx%02hx%02hx",
(USHORT)iSid->IdentifierAuthority.Value[0],
(USHORT)iSid->IdentifierAuthority.Value[1],
(USHORT)iSid->IdentifierAuthority.Value[2],
(USHORT)iSid->IdentifierAuthority.Value[3],
(USHORT)iSid->IdentifierAuthority.Value[4],
(USHORT)iSid->IdentifierAuthority.Value[5] );
strcat(String, Buffer);
} 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);
_snprintf(Buffer, sizeof(Buffer), "%lu", Tmp);
strcat(String, Buffer);
}
for (i=0;i<iSid->SubAuthorityCount ;i++ ) {
_snprintf(Buffer, sizeof(Buffer), "-%lu", iSid->SubAuthority[i]);
strcat(String, Buffer);
}
//
// Convert the string to a Unicode String
//
RtlInitString(&AnsiString, (PSZ) String);
Status = RtlAnsiStringToUnicodeString( UnicodeString,
&AnsiString,
AllocateDestinationString
);
return(Status);
}
BOOLEAN
RtlEqualLuid (
IN PLUID Luid1,
IN PLUID Luid2
)
/*++
Routine Description:
This procedure test two LUID values for equality.
This routine is here for backwards compatibility only. New code
should use the macro.
Arguments:
Luid1, Luid2 - Supply pointers to the two LUID values to compare.
Return Value:
BOOLEAN - TRUE if the value of Luid1 is equal to Luid2, and FALSE
otherwise.
--*/
{
LUID UNALIGNED * TempLuid1;
LUID UNALIGNED * TempLuid2;
RTL_PAGED_CODE();
return((Luid1->HighPart == Luid2->HighPart) &&
(Luid1->LowPart == Luid2->LowPart));
}
VOID
RtlCopyLuid (
OUT PLUID DestinationLuid,
IN PLUID SourceLuid
)
/*++
Routine Description:
This routine copies the value of the source LUID to the
destination LUID.
Arguments:
DestinationLuid - Receives a copy of the source Luid value.
SourceLuid - Supplies the Luid value to be copied. This LUID is
assumed to be structurally valid.
Return Value:
None.
--*/
{
RTL_PAGED_CODE();
(*DestinationLuid) = (*SourceLuid);
return;
}
VOID
RtlCopyLuidAndAttributesArray (
IN ULONG ArrayLength,
IN PLUID_AND_ATTRIBUTES Source,
OUT PLUID_AND_ATTRIBUTES Target
)
/*++
Routine Description:
This routine copies the value of the source LUID_AND_ATTRIBUTES array
to the target.
Arguments:
ArrayLength - Number of elements in the source array to copy.
Source - The source array.
Target - Indicates where the array elements are to be copied to.
Return Value:
None.
--*/
{
ULONG Index = 0;
RTL_PAGED_CODE();
while (Index < ArrayLength) {
Target[Index] = Source[Index];
Index += 1;
} //end_while
return;
}
NTSTATUS
RtlCreateSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN ULONG Revision
)
/*++
Routine Description:
This procedure initializes a new "absolute format" security descriptor.
After the procedure call the security descriptor is initialized with no
system ACL, no discretionary ACL, no owner, no primary group and
all control flags set to false (null).
Arguments:
SecurityDescriptor - Supplies the security descriptor to
initialize.
Revision - Provides the revision level to assign to the security
descriptor. This should be one (1) for this release.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision level provided
is not supported by this routine.
--*/
{
RTL_PAGED_CODE();
//
// Check the requested revision
//
if (Revision == SECURITY_DESCRIPTOR_REVISION) {
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
ISecurityDescriptor->Revision = SECURITY_DESCRIPTOR_REVISION;
ISecurityDescriptor->Sbz1 = 0;
*(PUSHORT)(&ISecurityDescriptor->Control) = 0;
ISecurityDescriptor->Owner = NULL;
ISecurityDescriptor->Group = NULL;
ISecurityDescriptor->Sacl = NULL;
ISecurityDescriptor->Dacl = NULL;
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
//
if (ISecurityDescriptor->Owner != NULL) {
Owner = RtlpOwnerAddrSecurityDescriptor( ISecurityDescriptor );
if (!RtlValidSid( Owner )) {
return FALSE;
}
}
if (ISecurityDescriptor->Group != NULL) {
Group = RtlpGroupAddrSecurityDescriptor( ISecurityDescriptor );
if (!RtlValidSid( Group )) {
return FALSE;
}
}
if ( (ISecurityDescriptor->Control & SE_DACL_PRESENT) &&
(ISecurityDescriptor->Dacl != NULL) ) {
Dacl = RtlpDaclAddrSecurityDescriptor( ISecurityDescriptor );
if (!RtlValidAcl( Dacl )) {
return FALSE;
}
}
if ( (ISecurityDescriptor->Control & SE_SACL_PRESENT) &&
(ISecurityDescriptor->Sacl != NULL) ) {
Sacl = RtlpSaclAddrSecurityDescriptor( ISecurityDescriptor );
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;
//
// 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 = sizeof(SECURITY_DESCRIPTOR);
//
// Add in length of Owner SID
//
if (ISecurityDescriptor->Owner != NULL) {
sum += (ULONG)(LongAlign(SeLengthSid(RtlpOwnerAddrSecurityDescriptor(ISecurityDescriptor))));
}
//
// Add in length of Group SID
//
if (ISecurityDescriptor->Group != NULL) {
sum += (ULONG)(LongAlign(SeLengthSid(RtlpGroupAddrSecurityDescriptor(ISecurityDescriptor))));
}
//
// Add in used length of Discretionary ACL
//
if ( (ISecurityDescriptor->Control & SE_DACL_PRESENT) &&
(ISecurityDescriptor->Dacl != NULL) ) {
sum += (ULONG)(LongAlign(RtlpDaclAddrSecurityDescriptor(ISecurityDescriptor)->AclSize) );
}
//
// Add in used length of System Acl
//
if ( (ISecurityDescriptor->Control & SE_SACL_PRESENT) &&
(ISecurityDescriptor->Sacl != NULL) ) {
sum += (ULONG)(LongAlign(RtlpSaclAddrSecurityDescriptor(ISecurityDescriptor)->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;
//
// 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 = sizeof(SECURITY_DESCRIPTOR);
//
// Add in length of Owner SID
//
if (ISecurityDescriptor->Owner != NULL) {
sum += (ULONG)(LongAlign(SeLengthSid(RtlpOwnerAddrSecurityDescriptor(ISecurityDescriptor))));
}
//
// Add in length of Group SID
//
if (ISecurityDescriptor->Group != NULL) {
sum += (ULONG)(LongAlign(SeLengthSid(RtlpGroupAddrSecurityDescriptor(ISecurityDescriptor))));
}
//
// Add in used length of Discretionary ACL
//
if ( (ISecurityDescriptor->Control & SE_DACL_PRESENT) &&
(ISecurityDescriptor->Dacl != NULL) ) {
RtlQueryInformationAcl( RtlpDaclAddrSecurityDescriptor(ISecurityDescriptor),
(PVOID)&AclSize,
sizeof(AclSize),
AclSizeInformation );
sum += (ULONG)(LongAlign(AclSize.AclBytesInUse));
}
//
// Add in used length of System Acl
//
if ( (ISecurityDescriptor->Control & SE_SACL_PRESENT) &&
(ISecurityDescriptor->Sacl != NULL) ) {
RtlQueryInformationAcl( RtlpSaclAddrSecurityDescriptor(ISecurityDescriptor),
(PVOID)&AclSize,
sizeof(AclSize),
AclSizeInformation );
sum += (ULONG)(LongAlign(AclSize.AclBytesInUse));
}
return sum;
}
NTSTATUS
RtlSetAttributesSecurityDescriptor(
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN SECURITY_DESCRIPTOR_CONTROL Control,
IN 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;
}
//
// Only allow setting SE_SERVER_SECURITY and SE_DACL_UNTRUSTED
//
if ( Control & (SE_SERVER_SECURITY | SE_DACL_UNTRUSTED) != Control ) {
return STATUS_INVALID_PARAMETER ;
}
((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Control |= Control;
return STATUS_SUCCESS;
}
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
RtlSetDaclSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN BOOLEAN DaclPresent,
IN PACL Dacl OPTIONAL,
IN BOOLEAN DaclDefaulted OPTIONAL
)
/*++
Routine Description:
This procedure sets the discretionary ACL information of an absolute
format security descriptor. If there is already a discretionary ACL
present in the security descriptor, it is superseded.
Arguments:
SecurityDescriptor - Supplies the security descriptor to be which
the discretionary ACL is to be added.
DaclPresent - If FALSE, indicates the DaclPresent flag in the
security descriptor should be set to FALSE. In this case,
the remaining optional parameters are ignored. Otherwise,
the DaclPresent control flag in the security descriptor is
set to TRUE and the remaining optional parameters are not
ignored.
Dacl - Supplies the discretionary ACL for the security
descriptor. If this optional parameter is not passed, then a
null ACL is assigned to the security descriptor. A null
discretionary ACL unconditionally grants access. The ACL is
referenced by, not copied into, by the security descriptor.
DaclDefaulted - When set, indicates the discretionary ACL was
picked up from some default mechanism (rather than explicitly
specified by a user). This value is set in the DaclDefaulted
control flag in the security descriptor. If this optional
parameter is not passed, then the DaclDefaulted flag will be
cleared.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision of the security
descriptor is not known to the routine. It may be a newer
revision than the routine knows about.
STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
is not an absolute format security descriptor.
--*/
{
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
RTL_PAGED_CODE();
//
// Check the revision
//
if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
return STATUS_UNKNOWN_REVISION;
}
//
// Make sure the descriptor is absolute format
//
if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
return STATUS_INVALID_SECURITY_DESCR;
}
//
// Assign the DaclPresent flag value passed
//
if (DaclPresent) {
ISecurityDescriptor->Control |= SE_DACL_PRESENT;
//
// Assign the ACL address if passed, otherwise set to null.
//
ISecurityDescriptor->Dacl = NULL;
if (ARGUMENT_PRESENT(Dacl)) {
ISecurityDescriptor->Dacl = Dacl;
}
//
// Assign DaclDefaulted flag if passed, otherwise clear it.
//
ISecurityDescriptor->Control &= ~SE_DACL_DEFAULTED;
if (DaclDefaulted == TRUE) {
ISecurityDescriptor->Control |= SE_DACL_DEFAULTED;
}
} else {
ISecurityDescriptor->Control &= ~SE_DACL_PRESENT;
}
return STATUS_SUCCESS;
}
NTSTATUS
RtlGetDaclSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
OUT PBOOLEAN DaclPresent,
OUT PACL *Dacl,
OUT PBOOLEAN DaclDefaulted
)
/*++
Routine Description:
This procedure retrieves the discretionary ACL information of a
security descriptor.
Arguments:
SecurityDescriptor - Supplies the security descriptor.
DaclPresent - If TRUE, indicates that the security descriptor
does contain a discretionary ACL. In this case, the
remaining OUT parameters will receive valid values.
Otherwise, the security descriptor does not contain a
discretionary ACL and the remaining OUT parameters will not
receive valid values.
Dacl - This value is returned only if the value returned for the
DaclPresent flag is TRUE. In this case, the Dacl parameter
receives the address of the security descriptor's
discretionary ACL. If this value is returned as null, then
the security descriptor has a null discretionary ACL.
DaclDefaulted - This value is returned only if the value returned
for the DaclPresent flag is TRUE. In this case, the
DaclDefaulted parameter receives the value of the security
descriptor's DaclDefaulted control flag.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision of the security
descriptor is not known to the routine. It may be a newer
revision than the routine knows about.
--*/
{
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
RTL_PAGED_CODE();
//
// Check the revision
//
if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
return STATUS_UNKNOWN_REVISION;
}
//
// Assign the DaclPresent flag value
//
*DaclPresent = RtlpAreControlBitsSet( ISecurityDescriptor, SE_DACL_PRESENT );
if (*DaclPresent) {
//
// Assign the ACL address.
//
*Dacl = RtlpDaclAddrSecurityDescriptor(ISecurityDescriptor);
//
// Assign DaclDefaulted flag.
//
*DaclDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_DACL_DEFAULTED );
}
return STATUS_SUCCESS;
}
NTSTATUS
RtlSetSaclSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN BOOLEAN SaclPresent,
IN PACL Sacl OPTIONAL,
IN BOOLEAN SaclDefaulted OPTIONAL
)
/*++
Routine Description:
This procedure sets the system ACL information of an absolute security
descriptor. If there is already a system ACL present in the
security descriptor, it is superseded.
Arguments:
SecurityDescriptor - Supplies the security descriptor to be which
the system ACL is to be added.
SaclPresent - If FALSE, indicates the SaclPresent flag in the
security descriptor should be set to FALSE. In this case,
the remaining optional parameters are ignored. Otherwise,
the SaclPresent control flag in the security descriptor is
set to TRUE and the remaining optional parameters are not
ignored.
Sacl - Supplies the system ACL for the security descriptor. If
this optional parameter is not passed, then a null ACL is
assigned to the security descriptor. The ACL is referenced
by, not copied into, by the security descriptor.
SaclDefaulted - When set, indicates the system ACL was picked up
from some default mechanism (rather than explicitly specified
by a user). This value is set in the SaclDefaulted control
flag in the security descriptor. If this optional parameter
is not passed, then the SaclDefaulted flag will be cleared.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision of the security
descriptor is not known to the routine. It may be a newer
revision than the routine knows about.
STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
is not an absolute format security descriptor.
--*/
{
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
RTL_PAGED_CODE();
//
// Check the revision
//
if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
return STATUS_UNKNOWN_REVISION;
}
//
// Make sure the descriptor is absolute format
//
if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
return STATUS_INVALID_SECURITY_DESCR;
}
//
// Assign the SaclPresent flag value passed
//
if (SaclPresent) {
ISecurityDescriptor->Control |= SE_SACL_PRESENT;
//
// Assign the ACL address if passed, otherwise set to null.
//
ISecurityDescriptor->Sacl = NULL;
if (ARGUMENT_PRESENT(Sacl)) {
ISecurityDescriptor->Sacl = Sacl;
}
//
// Assign SaclDefaulted flag if passed, otherwise clear it.
//
ISecurityDescriptor->Control &= ~ SE_SACL_DEFAULTED;
if (ARGUMENT_PRESENT(SaclDefaulted)) {
ISecurityDescriptor->Control |= SE_SACL_DEFAULTED;
}
} else {
ISecurityDescriptor->Control &= ~SE_SACL_PRESENT;
}
return STATUS_SUCCESS;
}
NTSTATUS
RtlGetSaclSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
OUT PBOOLEAN SaclPresent,
OUT PACL *Sacl,
OUT PBOOLEAN SaclDefaulted
)
/*++
Routine Description:
This procedure retrieves the system ACL information of a security
descriptor.
Arguments:
SecurityDescriptor - Supplies the security descriptor.
SaclPresent - If TRUE, indicates that the security descriptor
does contain a system ACL. In this case, the remaining OUT
parameters will receive valid values. Otherwise, the
security descriptor does not contain a system ACL and the
remaining OUT parameters will not receive valid values.
Sacl - This value is returned only if the value returned for the
SaclPresent flag is TRUE. In this case, the Sacl parameter
receives the address of the security descriptor's system ACL.
If this value is returned as null, then the security
descriptor has a null system ACL.
SaclDefaulted - This value is returned only if the value returned
for the SaclPresent flag is TRUE. In this case, the
SaclDefaulted parameter receives the value of the security
descriptor's SaclDefaulted control flag.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision of the security
descriptor is not known to the routine. It may be a newer
revision than the routine knows about.
--*/
{
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
RTL_PAGED_CODE();
//
// Check the revision
//
if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
return STATUS_UNKNOWN_REVISION;
}
//
// Assign the SaclPresent flag value
//
*SaclPresent = RtlpAreControlBitsSet( ISecurityDescriptor, SE_SACL_PRESENT );
if (*SaclPresent) {
//
// Assign the ACL address.
//
*Sacl = RtlpSaclAddrSecurityDescriptor(ISecurityDescriptor);
//
// Assign SaclDefaulted flag.
//
*SaclDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_SACL_DEFAULTED );
}
return STATUS_SUCCESS;
}
NTSTATUS
RtlSetOwnerSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN PSID Owner OPTIONAL,
IN BOOLEAN OwnerDefaulted OPTIONAL
)
/*++
Routine Description:
This procedure sets the owner information of an absolute security
descriptor. If there is already an owner present in the security
descriptor, it is superseded.
Arguments:
SecurityDescriptor - Supplies the security descriptor in which
the owner is to be set. If the security descriptor already
includes an owner, it will be superseded by the new owner.
Owner - Supplies the owner SID for the security descriptor. If
this optional parameter is not passed, then the owner is
cleared (indicating the security descriptor has no owner).
The SID is referenced by, not copied into, the security
descriptor.
OwnerDefaulted - When set, indicates the owner was picked up from
some default mechanism (rather than explicitly specified by a
user). This value is set in the OwnerDefaulted control flag
in the security descriptor. If this optional parameter is
not passed, then the SaclDefaulted flag will be cleared.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision of the security
descriptor is not known to the routine. It may be a newer
revision than the routine knows about.
STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
is not an absolute format security descriptor.
--*/
{
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
RTL_PAGED_CODE();
//
// Check the revision
//
if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
return STATUS_UNKNOWN_REVISION;
}
//
// Make sure the descriptor is absolute format
//
if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
return STATUS_INVALID_SECURITY_DESCR;
}
//
// Assign the Owner field if passed, otherwise clear it.
//
ISecurityDescriptor->Owner = NULL;
if (ARGUMENT_PRESENT(Owner)) {
ISecurityDescriptor->Owner = Owner;
}
//
// Assign the OwnerDefaulted flag if passed, otherwise clear it.
//
ISecurityDescriptor->Control &= ~SE_OWNER_DEFAULTED;
if (OwnerDefaulted == TRUE) {
ISecurityDescriptor->Control |= SE_OWNER_DEFAULTED;
}
return STATUS_SUCCESS;
}
NTSTATUS
RtlGetOwnerSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
OUT PSID *Owner,
OUT PBOOLEAN OwnerDefaulted
)
/*++
Routine Description:
This procedure retrieves the owner information of a security
descriptor.
Arguments:
SecurityDescriptor - Supplies the security descriptor.
Owner - Receives a pointer to the owner SID. If the security
descriptor does not currently contain an owner, then this
value will be returned as null. In this case, the remaining
OUT parameters are not given valid return values. Otherwise,
this parameter points to an SID and the remaining OUT
parameters are provided valid return values.
OwnerDefaulted - This value is returned only if the value
returned for the Owner parameter is not null. In this case,
the OwnerDefaulted parameter receives the value of the
security descriptor's OwnerDefaulted control flag.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision of the security
descriptor is not known to the routine. It may be a newer
revision than the routine knows about.
--*/
{
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
RTL_PAGED_CODE();
//
// Check the revision
//
if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
return STATUS_UNKNOWN_REVISION;
}
//
// Return the Owner field value.
//
*Owner = RtlpOwnerAddrSecurityDescriptor(ISecurityDescriptor);
//
// Return the OwnerDefaulted flag value.
//
*OwnerDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_OWNER_DEFAULTED );
return STATUS_SUCCESS;
}
NTSTATUS
RtlSetGroupSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN PSID Group OPTIONAL,
IN BOOLEAN GroupDefaulted OPTIONAL
)
/*++
Routine Description:
This procedure sets the primary group information of an absolute security
descriptor. If there is already an primary group present in the
security descriptor, it is superseded.
Arguments:
SecurityDescriptor - Supplies the security descriptor in which
the primary group is to be set. If the security descriptor
already includes a primary group, it will be superseded by
the new group.
Group - Supplies the primary group SID for the security
descriptor. If this optional parameter is not passed, then
the primary group is cleared (indicating the security
descriptor has no primary group). The SID is referenced by,
not copied into, the security descriptor.
GroupDefaulted - When set, indicates the owner was picked up from
some default mechanism (rather than explicitly specified by a
user). This value is set in the OwnerDefaulted control flag
in the security descriptor. If this optional parameter is
not passed, then the SaclDefaulted flag will be cleared.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision of the security
descriptor is not known to the routine. It may be a newer
revision than the routine knows about.
STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
is not an absolute format security descriptor.
--*/
{
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
RTL_PAGED_CODE();
//
// Check the revision
//
if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
return STATUS_UNKNOWN_REVISION;
}
//
// Make sure the descriptor is absolute format
//
if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
return STATUS_INVALID_SECURITY_DESCR;
}
//
// Assign the Group field if passed, otherwise clear it.
//
ISecurityDescriptor->Group = NULL;
if (ARGUMENT_PRESENT(Group)) {
ISecurityDescriptor->Group = Group;
}
//
// Assign the GroupDefaulted flag if passed, otherwise clear it.
//
ISecurityDescriptor->Control &= ~SE_GROUP_DEFAULTED;
if (ARGUMENT_PRESENT(GroupDefaulted)) {
ISecurityDescriptor->Control |= SE_GROUP_DEFAULTED;
}
return STATUS_SUCCESS;
}
NTSTATUS
RtlGetGroupSecurityDescriptor (
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
OUT PSID *Group,
OUT PBOOLEAN GroupDefaulted
)
/*++
Routine Description:
This procedure retrieves the primary group information of a
security descriptor.
Arguments:
SecurityDescriptor - Supplies the security descriptor.
Group - Receives a pointer to the primary group SID. If the
security descriptor does not currently contain a primary
group, then this value will be returned as null. In this
case, the remaining OUT parameters are not given valid return
values. Otherwise, this parameter points to an SID and the
remaining OUT parameters are provided valid return values.
GroupDefaulted - This value is returned only if the value
returned for the Group parameter is not null. In this case,
the GroupDefaulted parameter receives the value of the
security descriptor's GroupDefaulted control flag.
Return Value:
STATUS_SUCCESS - Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION - Indicates the revision of the security
descriptor is not known to the routine. It may be a newer
revision than the routine knows about.
--*/
{
//
// Typecast to the opaque SECURITY_DESCRIPTOR structure.
//
SECURITY_DESCRIPTOR *ISecurityDescriptor =
(SECURITY_DESCRIPTOR *)SecurityDescriptor;
RTL_PAGED_CODE();
//
// Check the revision
//
if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
return STATUS_UNKNOWN_REVISION;
}
//
// Return the Group field value.
//
*Group = RtlpGroupAddrSecurityDescriptor(ISecurityDescriptor);
//
// Return the GroupDefaulted flag value.
//
*GroupDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_GROUP_DEFAULTED );
return STATUS_SUCCESS;
}
BOOLEAN
RtlAreAllAccessesGranted(
IN ACCESS_MASK GrantedAccess,
IN ACCESS_MASK DesiredAccess
)
/*++
Routine Description:
This routine is used to check a desired access mask against a
granted access mask. It is used by the Object Management
component when dereferencing a handle.
Arguments:
GrantedAccess - Specifies the granted access mask.
DesiredAccess - Specifies the desired access mask.
Return Value:
BOOLEAN - TRUE if the GrantedAccess mask has all the bits set
that the DesiredAccess mask has set. That is, TRUE is
returned if all of the desired accesses have been granted.
--*/
{
RTL_PAGED_CODE();
return ((BOOLEAN)((~(GrantedAccess) & (DesiredAccess)) == 0));
}
BOOLEAN
RtlAreAnyAccessesGranted(
IN ACCESS_MASK GrantedAccess,
IN ACCESS_MASK DesiredAccess
)
/*++
Routine Description:
This routine is used to test whether any of a set of desired
accesses are granted by a granted access mask. It is used by
components other than the the Object Management component for
checking access mask subsets.
Arguments:
GrantedAccess - Specifies the granted access mask.
DesiredAccess - Specifies the desired access mask.
Return Value:
BOOLEAN - TRUE if the GrantedAccess mask contains any of the bits
specified in the DesiredAccess mask. That is, if any of the
desired accesses have been granted, TRUE is returned.
--*/
{
RTL_PAGED_CODE();
return ((BOOLEAN)(((GrantedAccess) & (DesiredAccess)) != 0));
}
VOID
RtlMapGenericMask(
IN OUT PACCESS_MASK AccessMask,
IN PGENERIC_MAPPING GenericMapping
)
/*++
Routine Description:
This routine maps all generic accesses in the provided access mask
to specific and standard accesses according to the provided
GenericMapping.
Arguments:
AccessMask - Points to the access mask to be mapped.
GenericMapping - The mapping of generic to specific and standard
access types.
Return Value:
None.
--*/
{
RTL_PAGED_CODE();
// //
// // Make sure the pointer is properly aligned
// //
//
// ASSERT( ((ULONG)AccessMask >> 2) << 2 == (ULONG)AccessMask );
if (*AccessMask & GENERIC_READ) {
*AccessMask |= GenericMapping->GenericRead;
}
if (*AccessMask & GENERIC_WRITE) {
*AccessMask |= GenericMapping->GenericWrite;
}
if (*AccessMask & GENERIC_EXECUTE) {
*AccessMask |= GenericMapping->GenericExecute;
}
if (*AccessMask & GENERIC_ALL) {
*AccessMask |= GenericMapping->GenericAll;
}
//
// Now clear the generic flags
//
*AccessMask &= ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);
return;
}
NTSTATUS
RtlImpersonateSelf(
IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
)
/*++
Routine Description:
This routine may be used to obtain an Impersonation token representing
your own process's context. This may be useful for enabling a privilege
for a single thread rather than for the entire process; or changing
the default DACL for a single thread.
The token is assigned to the callers thread.
Arguments:
ImpersonationLevel - The level to make the impersonation token.
Return Value:
STATUS_SUCCESS - The thread is now impersonating the calling process.
Other - Status values returned by:
NtOpenProcessToken()
NtDuplicateToken()
NtSetInformationThread()
--*/
{
NTSTATUS
Status,
IgnoreStatus;
HANDLE
Token1,
Token2;
OBJECT_ATTRIBUTES
ObjectAttributes;
SECURITY_QUALITY_OF_SERVICE
Qos;
RTL_PAGED_CODE();
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL);
Qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
Qos.ImpersonationLevel = ImpersonationLevel;
Qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
Qos.EffectiveOnly = FALSE;
ObjectAttributes.SecurityQualityOfService = &Qos;
Status = NtOpenProcessToken( NtCurrentProcess(), TOKEN_DUPLICATE, &Token1 );
if (NT_SUCCESS(Status)) {
Status = NtDuplicateToken(
Token1,
TOKEN_IMPERSONATE,
&ObjectAttributes,
FALSE, //EffectiveOnly
TokenImpersonation,
&Token2
);
if (NT_SUCCESS(Status)) {
Status = NtSetInformationThread(
NtCurrentThread(),
ThreadImpersonationToken,
&Token2,
sizeof(HANDLE)
);
IgnoreStatus = NtClose( Token2 );
}
IgnoreStatus = NtClose( Token1 );
}
return(Status);
}
#ifndef WIN16
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.
--*/
{
ULONG Index;
BOOLEAN Found;
ULONG ReturnLength;
PTOKEN_GROUPS GroupIds = NULL;
PTOKEN_USER UserId = NULL;
BOOLEAN rc = FALSE;
PVOID HeapHandle;
HANDLE TokenToUse;
RTL_PAGED_CODE();
//
// Get the handle to the current process heap
//
if ( Owner == NULL ) {
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 )) {
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 {
RtlFreeHeap( HeapHandle, 0, GroupIds );
return( FALSE );
} //endif assignable
} //endif Found
Index++;
} //endwhile
RtlFreeHeap( HeapHandle, 0, GroupIds );
return ( FALSE );
}
#if 0
BOOLEAN
RtlpValidOwnerSubjectContext(
IN HANDLE Token,
IN PSID Owner,
BOOLEAN Dummy,
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.
Return Value:
none.
--*/
{
ULONG Index;
BOOLEAN Found;
ULONG ReturnLength;
PTOKEN_GROUPS GroupIds = NULL;
PTOKEN_USER UserId = NULL;
BOOLEAN rc = FALSE;
NTSTATUS Status;
PVOID HeapHandle;
RTL_PAGED_CODE();
//
// Get the handle to the current process heap
//
if ( Owner == NULL ) {
return(FALSE);
}
HeapHandle = RtlProcessHeap();
*ReturnStatus = STATUS_SUCCESS;
//
// Get the User from the Token
//
Status = NtQueryInformationToken(
Token, // Handle
TokenUser, // TokenInformationClass
UserId, // TokenInformation
0, // TokenInformationLength
&ReturnLength // ReturnLength
);
ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
UserId = RtlAllocateHeap( HeapHandle, 0, ReturnLength );
if (UserId == NULL) {
*ReturnStatus = STATUS_NO_MEMORY;
return( FALSE );
}
Status = NtQueryInformationToken(
Token, // Handle
TokenUser, // TokenInformationClass
UserId, // TokenInformation
ReturnLength, // TokenInformationLength
&ReturnLength // ReturnLength
);
if (!NT_SUCCESS( Status )) {
*ReturnStatus = Status;
return( FALSE );
}
if ( RtlEqualSid( Owner, UserId->User.Sid ) ) {
RtlFreeHeap( HeapHandle, 0, (PVOID)UserId );
return( TRUE );
}
RtlFreeHeap( HeapHandle, 0, (PVOID)UserId );
//
// Get the groups from the Token
//
Status = NtQueryInformationToken(
Token, // Handle
TokenGroups, // TokenInformationClass
GroupIds, // TokenInformation
0, // TokenInformationLength
&ReturnLength // ReturnLength
);
ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
GroupIds = RtlAllocateHeap( HeapHandle, 0, ReturnLength );
if (GroupIds == NULL) {
*ReturnStatus = STATUS_NO_MEMORY;
return( FALSE );
}
Status = NtQueryInformationToken(
Token, // Handle
TokenGroups, // TokenInformationClass
GroupIds, // TokenInformation
ReturnLength, // TokenInformationLength
&ReturnLength // ReturnLength
);
if (!NT_SUCCESS( Status )) {
RtlFreeHeap( HeapHandle, 0, GroupIds );
*ReturnStatus = Status;
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 {
RtlFreeHeap( HeapHandle, 0, GroupIds );
return( FALSE );
} //endif assignable
} //endif Found
Index++;
} //endwhile
RtlFreeHeap( HeapHandle, 0, GroupIds );
return ( FALSE );
}
#endif // WIN16
#endif
#if 0
BOOLEAN
RtlpContainsCreatorOwnerSid(
PKNOWN_ACE Ace
)
/*++
Routine Description:
Tests to see if the specified ACE contains the CreatorOwnerSid.
Arguments:
Ace - Pointer to the ACE whose SID is be compared to the Creator Sid.
This ACE is assumed to be valid and a known ACE type.
Return Value:
TRUE - The creator sid is in the ACE.
FALSE - The creator sid is not in the ACE.
--*/
{
BOOLEAN IsEqual;
ULONG CreatorSid[CREATOR_SID_SIZE];
SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
RTL_PAGED_CODE();
//
// This is gross and ugly, but it's better than allocating
// virtual memory to hold the CreatorSid, because that can
// fail, and propogating the error back is a tremendous pain
//
ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);
//
// Allocate and initialize the universal SIDs
//
RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
*(RtlSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_OWNER_RID;
IsEqual = RtlEqualSid(&Ace->SidStart, (PSID)CreatorSid );
return( IsEqual );
}
BOOLEAN
RtlpContainsCreatorGroupSid(
PKNOWN_ACE Ace
)
/*++
Routine Description:
Tests to see if the specified ACE contains the CreatorGroupSid.
Arguments:
Ace - Pointer to the ACE whose SID is be compared to the Creator Sid.
This ACE is assumed to be valid and a known ACE type.
Return Value:
TRUE - The creator sid is in the ACE.
FALSE - The creator sid is not in the ACE.
--*/
{
BOOLEAN IsEqual;
ULONG CreatorSid[CREATOR_SID_SIZE];
SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
RTL_PAGED_CODE();
//
// This is gross and ugly, but it's better than allocating
// virtual memory to hold the CreatorSid, because that can
// fail, and propogating the error back is a tremendous pain
//
ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);
//
// Allocate and initialize the universal SIDs
//
RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
*(RtlSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_GROUP_RID;
IsEqual = RtlEqualSid(&Ace->SidStart, (PSID)CreatorSid );
return( IsEqual );
}
BOOLEAN
RtlpContainsCreatorOwnerServerSid(
PKNOWN_COMPOUND_ACE Ace
)
/*++
Routine Description:
Tests to see if the specified ACE contains the CreatorClientSid.
Arguments:
Ace - Pointer to the compound ACE whose Client SID is be compared to the
Creator Client Sid. This ACE is assumed to be valid and a known
compound ACE type.
Return Value:
TRUE - The creator sid is in the ACE.
FALSE - The creator sid is not in the ACE.
--*/
{
BOOLEAN IsEqual;
ULONG CreatorSid[CREATOR_SID_SIZE];
SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
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);
//
// Allocate and initialize the universal SID
//
RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
*(RtlSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_OWNER_SERVER_RID;
IsEqual = RtlEqualSid(RtlCompoundAceClientSid( Ace ), (PSID)CreatorSid );
return( IsEqual );
}
BOOLEAN
RtlpContainsCreatorGroupServerSid(
PKNOWN_COMPOUND_ACE Ace
)
/*++
Routine Description:
Tests to see if the specified ACE contains the CreatorServerSid.
Arguments:
Ace - Pointer to the compound ACE whose Server SID is be compared to the
Creator Server Sid. This ACE is assumed to be valid and a known
compound ACE type.
Return Value:
TRUE - The creator Server sid is in the ACE.
FALSE - The creator Server sid is not in the ACE.
--*/
{
BOOLEAN IsEqual;
ULONG CreatorSid[CREATOR_SID_SIZE];
SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
RTL_PAGED_CODE();
//
// This is gross and ugly, but it's better than allocating
// virtual memory to hold the CreatorSid, because that can
// fail, and propogating the error back is a tremendous pain
//
ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);
//
// Allocate and initialize the universal SIDs
//
RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
*(RtlSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_GROUP_SERVER_RID;
IsEqual = RtlEqualSid(RtlCompoundAceServerSid(Ace), (PSID)CreatorSid );
return( IsEqual );
}
#endif
VOID
RtlpApplyAclToObject (
IN PACL Acl,
IN PGENERIC_MAPPING GenericMapping
)
/*++
Routine Description:
This is a private routine that maps Access Masks of an ACL so that
they are applicable to the object type the ACL is being applied to.
Only known DSA ACEs are mapped. Unknown ACE types are ignored.
Only access types in the GenericAll mapping for the target object
type will be non-zero upon return.
Arguments:
Acl - Supplies the acl being applied.
GenericMapping - Specifies the generic mapping to use.
Return Value:
None.
--*/
{
//////////////////////////////////////////////////////////////////////////////
// //
// 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. //
// //
//////////////////////////////////////////////////////////////////////////////
ULONG i;
PACE_HEADER Ace;
RTL_PAGED_CODE();
//
// First check if the acl is null
//
if (Acl == NULL) {
return;
}
//
// Now walk the ACL, mapping each ACE as we go.
//
for (i = 0, Ace = FirstAce(Acl);
i < Acl->AceCount;
i += 1, Ace = NextAce(Ace)) {
if (IsMSAceType( Ace )) {
RtlApplyAceToObject( Ace, GenericMapping );
}
}
return;
}
#ifndef WIN16
NTSTATUS
RtlpInheritAcl (
IN PACL Acl,
IN BOOLEAN IsDirectoryObject,
IN PSID OwnerSid,
IN PSID GroupSid,
IN PSID ServerOwnerSid OPTIONAL,
IN PSID ServerGroupSid OPTIONAL,
IN PGENERIC_MAPPING GenericMapping,
OUT PACL *NewAcl
)
/*++
Routine Description:
This is a private routine that produces an inherited acl from
a parent acl according to the rules of inheritance
Arguments:
Acl - Supplies the acl being inherited.
IsDirectoryObject - Specifies if the new acl is for a directory.
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 (inherited) 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 NewAclLength;
PVOID HeapHandle;
RTL_PAGED_CODE();
//
// Get the handle to the current process heap
//
HeapHandle = RtlProcessHeap();
//
// First check if the acl is null
//
if (Acl == NULL) {
return STATUS_NO_INHERITANCE;
}
if (Acl->AclRevision != ACL_REVISION2 && Acl->AclRevision != ACL_REVISION3) {
return STATUS_UNKNOWN_REVISION;
}
//
// Generating an inheritable ACL is a two-pass operation.
// First you must see if there is anything to inherit, and if so,
// allocate enough room to hold it. then you must actually copy
// the generated ACEs.
//
Status = RtlpLengthInheritAcl(
Acl,
IsDirectoryObject,
OwnerSid,
GroupSid,
ServerOwnerSid,
ServerGroupSid,
GenericMapping,
&NewAclLength
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
if (NewAclLength == 0) {
return STATUS_NO_INHERITANCE;
}
(*NewAcl) = RtlAllocateHeap( HeapHandle, 0, NewAclLength );
if ((*NewAcl) == NULL ) {
return( STATUS_NO_MEMORY );
}
RtlCreateAcl( (*NewAcl), NewAclLength, Acl->AclRevision ); // Used to be hardwired to ACL_REVISION2
Status = RtlpGenerateInheritAcl(
Acl,
IsDirectoryObject,
OwnerSid,
GroupSid,
ServerOwnerSid,
ServerGroupSid,
GenericMapping,
(*NewAcl)
);
if (!NT_SUCCESS(Status)) {
RtlFreeHeap( HeapHandle, 0, *NewAcl );
}
return Status;
}
NTSTATUS
RtlpLengthInheritAcl(
IN PACL Acl,
IN BOOLEAN IsDirectoryObject,
IN PSID ClientOwnerSid,
IN PSID ClientGroupSid,
IN PSID ServerOwnerSid OPTIONAL,
IN PSID ServerGroupSid OPTIONAL,
IN PGENERIC_MAPPING GenericMapping,
OUT PULONG NewAclLength
)
/*++
Routine Description:
This is a private routine that calculates the length needed to
produce an inheritable ACL.
Arguments:
Acl - Supplies the acl being inherited.
IsDirectoryObject - Specifies if the new acl is for a directory.
OwnerSid - Specifies the owner Sid to use.
GroupSid - Specifies the group SID to use.
GenericMapping - Specifies the generic mapping to use.
NewAclLength - Receives the length of the inherited ACL.
Return Value:
STATUS_SUCCESS - An inheritable ACL buffer successfully allocated.
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.
--*/
{
//////////////////////////////////////////////////////////////////////////////
// //
// 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 i;
ULONG NewAclSize, NewAceSize;
PACE_HEADER OldAce;
RTL_PAGED_CODE();
//
// Calculate the length needed to store any inherited ACEs
// (this doesn't include the ACL header itself).
//
for (i = 0, OldAce = FirstAce(Acl), NewAclSize = 0;
i < Acl->AceCount;
i += 1, OldAce = NextAce(OldAce)) {
//
// RtlpLengthInheritedAce will return the number of bytes needed
// to inherit a single ACE.
//
Status = RtlpLengthInheritedAce(
OldAce,
IsDirectoryObject,
ClientOwnerSid,
ClientGroupSid,
ServerOwnerSid,
ServerGroupSid,
GenericMapping,
&NewAceSize
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
NewAclSize += NewAceSize;
}
//
// Check to make sure there is something inheritable
//
if (NewAclSize == 0) {
return STATUS_NO_INHERITANCE;
}
//
// And make sure we don't exceed the length limitations of an ACL (WORD)
//
NewAclSize += sizeof(ACL);
if (NewAclSize > 0xFFFF) {
return(STATUS_BAD_INHERITANCE_ACL);
}
(*NewAclLength) = NewAclSize;
return STATUS_SUCCESS;
}
NTSTATUS
RtlpGenerateInheritAcl(
IN PACL Acl,
IN BOOLEAN IsDirectoryObject,
IN PSID ClientOwnerSid,
IN PSID ClientGroupSid,
IN PSID ServerOwnerSid OPTIONAL,
IN PSID ServerGroupSid OPTIONAL,
IN PGENERIC_MAPPING GenericMapping,
OUT PACL NewAcl
)
/*++
Routine Description:
This is a private routine that produces an inheritable ACL.
It is expected that RtlpLengthInheritAcl() has already been
called to validate the inheritance and to indicate the length
of buffer needed to perform the inheritance.
Arguments:
Acl - Supplies the acl being inherited.
IsDirectoryObject - Specifies if the new acl is for a directory.
OwnerSid - Specifies the owner Sid to use.
GroupSid - Specifies the group SID to use.
GenericMapping - Specifies the generic mapping to use.
NewAcl - Provides a pointer to the buffer to receive the new
(inherited) acl. This ACL must already be initialized.
Return Value:
STATUS_SUCCESS - An inheritable ACL has been 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.
--*/
{
//////////////////////////////////////////////////////////////////////////////
// //
// 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 i;
PACE_HEADER OldAce;
RTL_PAGED_CODE();
//
// Walk through the original ACL generating any necessary
// inheritable ACEs.
//
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,
ClientOwnerSid,
ClientGroupSid,
ServerOwnerSid,
ServerGroupSid,
GenericMapping,
NewAcl
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
}
return STATUS_SUCCESS;
}
#endif // WIN16
NTSTATUS
RtlpLengthInheritedAce (
IN PACE_HEADER Ace,
IN BOOLEAN IsDirectoryObject,
IN PSID ClientOwnerSid,
IN PSID ClientGroupSid,
IN PSID ServerOwnerSid OPTIONAL,
IN PSID ServerGroupSid OPTIONAL,
IN PGENERIC_MAPPING GenericMapping,
IN PULONG NewAceLength
)
/*++
Routine Description:
This is a private routine that calculates the number of bytes needed
to allow for the inheritance of the specified ACE. If the ACE is not
inheritable, or its AccessMask ends up with no accesses, then the
size may be returned as zero.
Arguments:
Ace - Supplies the ace being checked
IsDirectoryObject - Specifies if the new ace is for a directory
ClientOwnerSid - Pointer to Sid to be assigned as the new owner.
ClientGroupSid - Points to SID to be assigned as the new primary group.
ServerOwnerSid - Provides the SID of a server to substitute into
compound ACEs (if any that require editing are encountered).
If this parameter is not provided, the SID passed for ClientOwnerSid
will be used.
ServerGroupSid - Provides the SID of a client to substitute into
compound ACEs (if any that require editing are encountered).
If this parameter is not provided, the SID passed for ClientGroupSid
will be used.
GenericMapping - Specifies the generic mapping to use.
NewAceLength - Receives the length (number of bytes) needed to allow for
the inheritance of the specified ACE. This might be zero.
Return Value:
STATUS_SUCCESS - The length needed has been calculated.
STATUS_BAD_INHERITANCE_ACL - Indicates inheritance of the ace would
result in an invalid ACL structure. For example, an SID substitution
for a known ACE type which has a CreatorOwner SID might exceed the
length limits of an ACE.
STATUS_INVALID_PARAMETER - An optional parameter was required, but not
provided.
--*/
{
//////////////////////////////////////////////////////////////////////////////
// //
// 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. //
// //
//////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
// //
// !!!!!!!!! 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;
ACCESS_MASK LocalMask;
PSID LocalServerOwner;
PSID LocalServerGroup;
PSID Sid;
ULONG Rid;
ULONG CreatorSid[CREATOR_SID_SIZE];
ULONG GroupSid[CREATOR_SID_SIZE];
ULONG CreatorOwnerServerSid[CREATOR_SID_SIZE];
ULONG CreatorGroupServerSid[CREATOR_SID_SIZE];
SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
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);
//
// Allocate and initialize the universal SIDs we're going to need
// to look for inheritable ACEs.
//
RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
RtlInitializeSid( (PSID)GroupSid, &CreatorSidAuthority, 1 );
RtlInitializeSid( (PSID)CreatorOwnerServerSid, &CreatorSidAuthority, 1 );
RtlInitializeSid( (PSID)CreatorGroupServerSid, &CreatorSidAuthority, 1 );
*(RtlSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_OWNER_RID;
*(RtlSubAuthoritySid( (PSID)GroupSid, 0 )) = SECURITY_CREATOR_GROUP_RID;
*(RtlSubAuthoritySid( (PSID)CreatorOwnerServerSid, 0 )) = SECURITY_CREATOR_OWNER_SERVER_RID;
*(RtlSubAuthoritySid( (PSID)CreatorGroupServerSid, 0 )) = SECURITY_CREATOR_GROUP_SERVER_RID;
//
// Everywhere the pseudo-code above says "copy", the code in this
// routine simply calculates the length of. RtlpGenerateInheritedAce()
// will actually be used to do the "copy".
//
LocalServerOwner = ARGUMENT_PRESENT(ServerOwnerSid) ? ServerOwnerSid : ClientOwnerSid;
LocalServerGroup = ARGUMENT_PRESENT(ServerGroupSid) ? ServerGroupSid : ClientGroupSid;
//
// check to see if we will have a protection ACE (one mapped to
// the target object type).
//
if ( (IsDirectoryObject && ContainerInherit(Ace)) ||
(!IsDirectoryObject && ObjectInherit(Ace)) ) {
LengthRequired = (ULONG)Ace->AceSize;
if (IsKnownAceType( Ace ) ) {
//
// May need to adjust size due to SID substitution
//
PKNOWN_ACE KnownAce = (PKNOWN_ACE)Ace;
Sid = &KnownAce->SidStart;
if (RtlEqualPrefixSid ( Sid, CreatorSid )) {
Rid = *RtlSubAuthoritySid( Sid, 0 );
switch (Rid) {
case SECURITY_CREATOR_OWNER_RID:
{
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientOwnerSid);
break;
}
case SECURITY_CREATOR_GROUP_RID:
{
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientGroupSid);
break;
}
case SECURITY_CREATOR_OWNER_SERVER_RID:
{
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ServerOwnerSid);
break;
}
case SECURITY_CREATOR_GROUP_SERVER_RID:
{
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ServerGroupSid);
break;
}
default :
{
//
// We don't know what this SID is, do nothing and the original will be copied.
//
break;
}
}
}
//
// If after mapping the access mask, the access mask
// is empty, then drop the ACE.
//
LocalMask = ((PKNOWN_ACE)(Ace))->Mask;
RtlMapGenericMask( &LocalMask, GenericMapping);
//
// Mask off any bits that aren't meaningful
//
LocalMask &= ( STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL | ACCESS_SYSTEM_SECURITY );
if (LocalMask == 0) {
LengthRequired = 0;
}
} else {
if (IsCompoundAceType(Ace)) {
PKNOWN_COMPOUND_ACE KnownAce = (PKNOWN_COMPOUND_ACE)Ace;
PSID AceServerSid;
PSID AceClientSid;
AceServerSid = RtlCompoundAceServerSid( KnownAce );
AceClientSid = RtlCompoundAceClientSid( KnownAce );
if (RtlEqualPrefixSid ( AceClientSid, CreatorSid )) {
Rid = *RtlSubAuthoritySid( AceClientSid, 0 );
switch (Rid) {
case SECURITY_CREATOR_OWNER_RID:
{
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientOwnerSid);
break;
}
case SECURITY_CREATOR_GROUP_RID:
{
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientGroupSid);
break;
}
case SECURITY_CREATOR_OWNER_SERVER_RID:
{
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ServerOwnerSid);
break;
}
case SECURITY_CREATOR_GROUP_SERVER_RID:
{
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ServerGroupSid);
break;
}
default :
{
//
// We don't know what this SID is, do nothing and the original will be copied.
//
break;
}
}
}
if (RtlEqualPrefixSid ( AceServerSid, CreatorSid )) {
Rid = *RtlSubAuthoritySid( AceServerSid, 0 );
switch (Rid) {
case SECURITY_CREATOR_OWNER_RID:
{
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientOwnerSid);
break;
}
case SECURITY_CREATOR_GROUP_RID:
{
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientGroupSid);
break;
}
case SECURITY_CREATOR_OWNER_SERVER_RID:
{
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ServerOwnerSid);
break;
}
case SECURITY_CREATOR_GROUP_SERVER_RID:
{
LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ServerGroupSid);
break;
}
default :
{
//
// We don't know what this SID is, do nothing and the original will be copied.
//
break;
}
}
}
}
//
// If after mapping the access mask, the access mask
// is empty, then drop the ACE.
//
LocalMask = ((PKNOWN_COMPOUND_ACE)(Ace))->Mask;
RtlMapGenericMask( &LocalMask, GenericMapping);
//
// Mask off any bits that aren't meaningful
//
LocalMask &= ( STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL | ACCESS_SYSTEM_SECURITY );
if (LocalMask == 0) {
LengthRequired = 0;
}
}
//
// 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.
//
ASSERT(sizeof(Ace->AceSize) == 2);
if (LengthRequired > 0xFFFF) {
return STATUS_BAD_INHERITANCE_ACL;
}
}
//
// If we are inheriting onto a container, then we may need to
// propagate the inheritance as well.
//
if (IsDirectoryObject && Propagate(Ace)) {
LengthRequired += (ULONG)Ace->AceSize;
}
//
// Now return to our caller
//
(*NewAceLength) = LengthRequired;
return STATUS_SUCCESS;
}
NTSTATUS
RtlpGenerateInheritedAce (
IN PACE_HEADER OldAce,
IN BOOLEAN IsDirectoryObject,
IN PSID ClientOwnerSid,
IN PSID ClientGroupSid,
IN PSID ServerOwnerSid OPTIONAL,
IN PSID ServerGroupSid OPTIONAL,
IN PGENERIC_MAPPING GenericMapping,
OUT PACL NewAcl
)
/*++
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.
See RtlpLengthInheritedAce() for detailed information on ACE inheritance.
THE CODE IN THIS ROUTINE MUST MATCH THE CODE IN RtlpLengthInheritanceAce()!!!
Arguments:
OldAce - Supplies the ace being inherited
IsDirectoryObject - Specifies if the new ACE is for a directory
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
NewAcl - Provides a pointer to the ACL into which the ACE is to be
inherited.
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.
--*/
{
//////////////////////////////////////////////////////////////////////////////
// //
// 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. //
// //
//////////////////////////////////////////////////////////////////////////////
ACCESS_MASK LocalMask;
PVOID AcePosition;
PUCHAR Target;
BOOLEAN CreatorOwner, CreatorGroup;
BOOLEAN CreatorClient, CreatorServer;
BOOLEAN ProtectionAceCopied;
PSID LocalServerOwner;
PSID LocalServerGroup;
ULONG CreatorSid[CREATOR_SID_SIZE];
SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
ULONG Rid;
PSID SidToCopy;
PSID ClientSidToCopy;
PSID ServerSidToCopy;
PSID AceClientSid;
PSID AceServerSid;
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);
//
// Allocate and initialize the universal SIDs we're going to need
// to look for inheritable ACEs.
//
RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
*(RtlSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_OWNER_RID;
if (!RtlFirstFreeAce( NewAcl, &AcePosition ) ) {
return STATUS_BAD_INHERITANCE_ACL;
}
if (!ARGUMENT_PRESENT(ServerOwnerSid)) {
LocalServerOwner = ClientOwnerSid;
} else {
LocalServerOwner = ServerOwnerSid;
}
if (!ARGUMENT_PRESENT(ServerGroupSid)) {
LocalServerGroup = ClientGroupSid;
} else {
LocalServerGroup = ServerGroupSid;
}
//
// check to see if we will have a protection ACE (one mapped to
// the target object type).
//
if ( (IsDirectoryObject && ContainerInherit(OldAce)) ||
(!IsDirectoryObject && ObjectInherit(OldAce)) ) {
ProtectionAceCopied = TRUE;
if (IsKnownAceType( OldAce ) ) {
//
// If after mapping the access mask, the access mask
// is empty, then drop the ACE.
//
LocalMask = ((PKNOWN_ACE)(OldAce))->Mask;
RtlMapGenericMask( &LocalMask, GenericMapping);
//
// Mask off any bits that aren't meaningful
//
LocalMask &= ( STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL | ACCESS_SYSTEM_SECURITY );
if (LocalMask == 0) {
ProtectionAceCopied = FALSE;
} else {
PKNOWN_ACE KnownAce = (PKNOWN_ACE)OldAce;
Target = (PUCHAR)AcePosition;
//
// See if the SID in the ACE is one of the various CREATOR_* SIDs by
// comparing identifier authorities.
//
if (RtlEqualPrefixSid ( &KnownAce->SidStart, CreatorSid )) {
Rid = *RtlSubAuthoritySid( &KnownAce->SidStart, 0 );
switch (Rid) {
case SECURITY_CREATOR_OWNER_RID:
{
SidToCopy = ClientOwnerSid;
break;
}
case SECURITY_CREATOR_GROUP_RID:
{
SidToCopy = ClientGroupSid;
break;
}
case SECURITY_CREATOR_OWNER_SERVER_RID:
{
SidToCopy = LocalServerOwner;
break;
}
case SECURITY_CREATOR_GROUP_SERVER_RID:
{
SidToCopy = LocalServerGroup;
break;
}
default :
{
//
// We don't know what this SID is, just copy the original.
//
SidToCopy = &KnownAce->SidStart;
break;
}
}
//
// SID substitution required. Copy all except the SID.
//
RtlMoveMemory(
Target,
OldAce,
FIELD_OFFSET(KNOWN_ACE, SidStart)
);
Target = (PUCHAR)(Target + FIELD_OFFSET(KNOWN_ACE, SidStart));
//
// Now copy the correct SID
//
RtlMoveMemory(
Target,
SidToCopy,
SeLengthSid(SidToCopy)
);
//
// Set the size of the ACE accordingly
//
((PKNOWN_ACE)AcePosition)->Header.AceSize =
(USHORT)FIELD_OFFSET(KNOWN_ACE, SidStart) +
(USHORT)SeLengthSid(SidToCopy);
} else {
//
// No ID substitution, copy ACE as is.
//
RtlMoveMemory(
Target,
OldAce,
((PKNOWN_ACE)OldAce)->Header.AceSize
);
}
//
// Put the mapped access mask in the new ACE
//
((PKNOWN_ACE)AcePosition)->Mask = LocalMask;
}
} else if (IsCompoundAceType(OldAce)) {
//
// If after mapping the access mask, the access mask
// is empty, then drop the ACE.
//
LocalMask = ((PKNOWN_COMPOUND_ACE)(OldAce))->Mask;
RtlMapGenericMask( &LocalMask, GenericMapping);
//
// Mask off any bits that aren't meaningful
//
LocalMask &= ( STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL | ACCESS_SYSTEM_SECURITY );
if (LocalMask == 0) {
ProtectionAceCopied = FALSE;
} else {
Target = (PUCHAR)AcePosition;
//
// See if the SID in the ACE is one of the various CREATOR_* SIDs by
// comparing identifier authorities.
//
AceServerSid = RtlCompoundAceServerSid( OldAce );
AceClientSid = RtlCompoundAceClientSid( OldAce );
if (RtlEqualPrefixSid ( AceServerSid, CreatorSid )) {
Rid = *RtlSubAuthoritySid( AceServerSid, 0 );
switch (Rid) {
case SECURITY_CREATOR_OWNER_RID:
{
ServerSidToCopy = ClientOwnerSid;
break;
}
case SECURITY_CREATOR_GROUP_RID:
{
ServerSidToCopy = ClientGroupSid;
break;
}
case SECURITY_CREATOR_OWNER_SERVER_RID:
{
ServerSidToCopy = LocalServerOwner;
break;
}
case SECURITY_CREATOR_GROUP_SERVER_RID:
{
ServerSidToCopy = LocalServerGroup;
break;
}
default :
{
//
// We don't know what this SID is, just copy the original.
//
ServerSidToCopy = AceServerSid;
break;
}
}
} else {
ServerSidToCopy = AceServerSid;
}
if (RtlEqualPrefixSid ( AceClientSid, CreatorSid )) {
Rid = *RtlSubAuthoritySid( AceClientSid, 0 );
switch (Rid) {
case SECURITY_CREATOR_OWNER_RID:
{
ClientSidToCopy = ClientOwnerSid;
break;
}
case SECURITY_CREATOR_GROUP_RID:
{
ClientSidToCopy = ClientGroupSid;
break;
}
case SECURITY_CREATOR_OWNER_SERVER_RID:
{
ClientSidToCopy = LocalServerOwner;
break;
}
case SECURITY_CREATOR_GROUP_SERVER_RID:
{
ClientSidToCopy = LocalServerGroup;
break;
}
default :
{
//
// We don't know what this SID is, just copy the original.
//
ClientSidToCopy = AceClientSid;
break;
}
}
} else {
ClientSidToCopy = AceClientSid;
}
//
// Copy ACE in pieces. Body of ACE first.
//
RtlMoveMemory(
Target,
OldAce,
FIELD_OFFSET(KNOWN_ACE, Mask)
);
Target = (PUCHAR)(Target + FIELD_OFFSET(KNOWN_COMPOUND_ACE, SidStart));
//
// Now copy the correct Server SID
//
RtlMoveMemory(
Target,
ServerSidToCopy,
SeLengthSid(ServerSidToCopy)
);
Target = (PUCHAR)(Target + SeLengthSid(ServerSidToCopy));
//
// Now copy the correct Client SID
//
RtlMoveMemory(
Target,
ClientSidToCopy,
SeLengthSid(ClientSidToCopy)
);
//
// Set the size of the ACE accordingly
//
((PKNOWN_COMPOUND_ACE)AcePosition)->Header.AceSize =
(USHORT)FIELD_OFFSET(KNOWN_COMPOUND_ACE, SidStart) +
(USHORT)SeLengthSid(ServerSidToCopy) +
(USHORT)SeLengthSid(ClientSidToCopy);
//
// Put the mapped access mask in the new ACE and set the type information
//
((PKNOWN_COMPOUND_ACE)AcePosition)->Mask = LocalMask;
((PKNOWN_COMPOUND_ACE)AcePosition)->Header.AceType = ACCESS_ALLOWED_COMPOUND_ACE_TYPE;
((PKNOWN_COMPOUND_ACE)AcePosition)->CompoundAceType = COMPOUND_ACE_IMPERSONATION;
}
} else {
//
// Not a known ACE type, copy ACE as is
//
RtlMoveMemory(
AcePosition,
OldAce,
((PKNOWN_COMPOUND_ACE)OldAce)->Header.AceSize
);
}
//
// If the ACE was actually kept, clear all the inherit flags
// and update the ACE count of the ACL.
//
if (ProtectionAceCopied) {
((PACE_HEADER)AcePosition)->AceFlags &= ~VALID_INHERIT_FLAGS;
NewAcl->AceCount += 1;
}
}
//
// If we are inheriting onto a container, then we may need to
// propagate the inheritance as well.
//
if (IsDirectoryObject && Propagate(OldAce)) {
//
// copy it as is, but make sure the InheritOnly bit is set.
//
if (!RtlFirstFreeAce( NewAcl, &AcePosition ) ) {
return STATUS_BAD_INHERITANCE_ACL;
}
RtlMoveMemory(
AcePosition,
OldAce,
((PKNOWN_ACE)OldAce)->Header.AceSize
);
((PACE_HEADER)AcePosition)->AceFlags |= INHERIT_ONLY_ACE;
NewAcl->AceCount += 1;
}
//
// Now return to our caller
//
return STATUS_SUCCESS;
}
#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