diff options
Diffstat (limited to 'private/ntos/rtl/sertl.c')
-rw-r--r-- | private/ntos/rtl/sertl.c | 5010 |
1 files changed, 5010 insertions, 0 deletions
diff --git a/private/ntos/rtl/sertl.c b/private/ntos/rtl/sertl.c new file mode 100644 index 000000000..bf44bbf6e --- /dev/null +++ b/private/ntos/rtl/sertl.c @@ -0,0 +1,5010 @@ +/*++ + +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 |