/*++ 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 #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; iLength; 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;iSubAuthorityCount ;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