/*++ Copyright (c) 1990 Microsoft Corporation Module Name: attr.c Abstract: This file contains services that manipulate SAM object attributes. WARNING: Terminology can sometimes be confusing. SAM objects have attributes (e.g., users have LogonHours, FullName, AcctName, et cetera). These attributes are stored in the registry in registry-key-attributes. There is NOT a one-to-one correllation between object-attributes and registry-key- attributes. For example, all the fixed-length attributes of an object are stored in a single registry-key-attribute (whose name is pointed to by SampFixedAttributeName). Author: Jim Kelly (JimK) 26-June-1992 Environment: User Mode - Win32 Revision History: --*/ /* Each SAM object-type has an Object-type descriptor. This is in a data structure called SAMP_OBJECT_INFORMATION. This structure contains information that applies to all instances of that object type. This includes things like a mask of write operations for the object type, and a name for the object type to be used in auditing. Each instance of an open SAM object has another data structure used to identify it (called SAMP_OBJECT). The header of this structure contains information that is common to all object-types and is there to allow unified object manipulation. This includes things like the handle to the object's registry key. There are fields in each of these structures that are there to allow generic object-attribute support routines to operate. In SAMP_OBJECT, there is a pointer to a block of allocated memory housing a copy of the object's attributes as they are stored on-disk. These attributes are arbitrarily divided into two groups: fixed-length and variable-length. One of the fields in SAMP_OBJECT_INFORMATION indicates whether the fixed-length and variable-length attributes for that object-type are stored together in a single registry-key-attribute or separately in two registry-key-attributes. The registry api for querying and setting registry-key attributes are rather peculiar in that they require the I/O buffer to include a description of the data. Even the simplest data structure for reading attribute values (KEY_VALUE_PARTIAL_INFORMATION) includes 3 ULONGs before the actual data (TitleIndex, value Type, data length, and then, finally, the data). To efficiently perform registry i/o, the in-memory copy of the on-disk object attributes includes room for this information preceeding the fixed and variable-length attribute sections of the data. NOTE: For object classes that store fixed and variable-length data together, only the KEY_VALUE_PARTIAL_INFORMATION structure preceeding the fixed-length attributes is used. The one preceeding the variable-length attributes is #ifdef'd out. The structures related to object-attributes look like: On-Disk Image +-------------+ SAMP_OBJECT_INFORMATION +-->|KEY_VALUE_ | +-----------------------+ SAMP_OBJECT | |PARTIAL_ | | | +-----------+ | |INFORMATION | | (header) | | | | |-------------| | | | (header) | | | Fixed-Length|<-----+ | | | | | | Attributes | | |-----------------------| |-----------| | | | +-------|-< FixedAttrsOffset | | OnDisk >-|--+ |-------------+ |-----------------------| |-----------| |KEY_VALUE_ |<-------------|-< VariableBuffOffset | | OnDisk | |PARTIAL_ | |-----------------------| | Control | |INFORMATION | +-------|-< VariableArrayOffset | | Flags | |(Optional) | | |-----------------------| |-----------| |-------------| | +----|-< VariableDataOffset | | | | Variable- |<-----+ | |-----------------------| | type- | | Length | | |VariableAttributeCount | | specific | | Attributes | | |-----------------------| | body | | Array | | |FixedStoredSeparately | | | |-------------| | |-----------------------| +-----------+ | Variable- |<--------+ | | | Length | | | | Attributes | | o | | Data | | o | | | +-----------------------+ | | +-------------+ The KEY_VALUE_PARTIAL_INFORMATION preceeding the VariableLengthAttributes array is marked optional because it is only present if fixed-length and variable-length attribute information is stored separately. In this case, the VariableBufferOffset field in the SAMP_OBJECT_INFORMATION structure is set to be zero. */ /////////////////////////////////////////////////////////////////////////////// // // // Includes // // // /////////////////////////////////////////////////////////////////////////////// #include #include #include // // This value indicates the minumum size block of memory to allocate // when retrieving object attributes from disk. #define SAMP_MINIMUM_ATTRIBUTE_ALLOC (1000) // // This value is used when growing the size of the buffer containing // object attributes. It represents the amount of free space that // should be left (approximately) for future growth in the buffer. // #define SAMP_MINIMUM_ATTRIBUTE_PAD (200) // // The following type is used to identify which grouping of attribute // (fixed or variable-length) are being refered to in a number of api. // #define SAMP_FIXED_ATTRIBUTES (0L) #define SAMP_VARIABLE_ATTRIBUTES (1L) // // The following line enables attribute debugging code // //#define SAM_DEBUG_ATTRIBUTES //#ifdef SAM_DEBUG_ATTRIBUTES //Boolean that allows us to turn off debugging output //BOOLEAN SampDebugAttributes = FALSE; //#endif /////////////////////////////////////////////////////////////////////////////// // // // private macros // // // /////////////////////////////////////////////////////////////////////////////// // // Macro to round up a ULONG value to be dword aligned // (i.e., be a multipleof 4). // Note, this does not handle the case where the Ulong is greater // than 0xfffffffc. // #define SampDwordAlignUlong( v ) (((v)+3) & 0xfffffffc) // // Make sure an object type and corresponding variable-length attribute // index are legitimate. // // // Make sure it is a legitimate object type // And a legitimate attribute index for the object type // #define SampValidateAttributeIndex( c, i ) { \ ASSERT( ((c)->ObjectType < SampUnknownObjectType) ); \ ASSERT(((i) < SampObjectInformation[(c)->ObjectType].VariableAttributeCount) ); \ } // // Test to see if an object's fixed or variable-length attributes // are in memory. // #define SampFixedAttributesValid( c ) ((c)->FixedValid) #define SampVariableAttributesValid( c ) ((c)->VariableValid) // // Get the number of variable-length attributes defined for the // specified object // #define SampVariableAttributeCount( c ) \ (SampObjectInformation[(c)->ObjectType].VariableAttributeCount) // // Get the offset of the beginning of the attribute buffers // #define SampFixedBufferOffset( c ) \ ( \ SampObjectInformation[(c)->ObjectType].FixedAttributesOffset \ ) #define SampVariableBufferOffset( c ) \ ( \ SampObjectInformation[(c)->ObjectType].VariableBufferOffset \ ) // // Get the offset of the beginning of the variable data i/o buffer. // If the fixed and variable-length attributes are stored separately, // then this will be the lower half of the buffer. // Otherwise, there is only one buffer, so it is the entire allocated buffer. // #define SampFixedBufferAddress( c ) \ ( \ ((PUCHAR)((c)->OnDisk)) + SampFixedBufferOffset( c ) \ ) #define SampVariableBufferAddress( c ) \ ( \ ((PUCHAR)((c)->OnDisk)) + SampVariableBufferOffset( c ) \ ) // // Get the offset of the beginning of the variable-length // attributes discriptors array. This address is dword-aligned. // #define SampVariableArrayOffset( c ) \ ( \ SampObjectInformation[(c)->ObjectType].VariableArrayOffset \ ) // // Calculate the address of the beginning of the variable-length // attributes array. // #define SampVariableArrayAddress( c ) \ ( \ (PSAMP_VARIABLE_LENGTH_ATTRIBUTE)((PUCHAR)((c)->OnDisk) + \ SampVariableArrayOffset( c ) ) \ ) // // Get the offset of the beginning of the variable-length // attributes data. // #define SampVariableDataOffset( c ) \ ( \ SampObjectInformation[(c)->ObjectType].VariableDataOffset \ ) // // Get the length of the on-disk buffer for holding the variable-length // attribute array and data. If the fixed and variable-length attributes // are stored separately, then this will be the lower half of the buffer. // Otherwise, there is only one buffer, so it is the entire allocated buffer. // #define SampFixedBufferLength( c ) \ ( \ SampObjectInformation[(c)->ObjectType].FixedLengthSize \ ) #define SampVariableBufferLength( c ) \ ( \ (c)->OnDiskAllocated - SampVariableBufferOffset( c ) \ ) // // Return the address of a Qualifier field within the variable-length // attribute descriptor array. // #define SampVariableQualifier( c, i ) \ ( \ SampVariableArrayAddress( c ) + \ (sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE) * i) \ + FIELD_OFFSET(SAMP_VARIABLE_LENGTH_ATTRIBUTE, Qualifier) \ ) // // Return the address of the first byte of free space // in an object's attribute data buffer. // This will be dword aligned. // #define SampFirstFreeVariableAddress( c ) \ (PUCHAR)(((PUCHAR)((c)->OnDisk)) + (c)->OnDiskUsed) // // Get the number of bytes needed to store the entire variable-length // attribute information on disk. // #define SampVariableBufferUsedLength( c ) \ ( \ (PUCHAR)SampFirstFreeVariableAddress(c) - \ (PUCHAR)SampVariableArrayAddress(c) \ ) /////////////////////////////////////////////////////////////////////////////// // // // private service prototypes // // // /////////////////////////////////////////////////////////////////////////////// NTSTATUS SampValidateAttributes( IN PSAMP_OBJECT Context, IN ULONG AttributeGroup ); PUCHAR SampObjectAttributeAddress( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex ); ULONG SampObjectAttributeLength( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex ); PULONG SampObjectAttributeQualifier( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex ); NTSTATUS SampGetAttributeBufferReadInfo( IN PSAMP_OBJECT Context, IN ULONG AttributeGroup, OUT PUCHAR *Buffer, OUT PULONG BufferLength, OUT PUNICODE_STRING *KeyAttributeName ); NTSTATUS SampExtendAttributeBuffer( IN PSAMP_OBJECT Context, IN ULONG NewSize ); NTSTATUS SampReadRegistryAttribute( IN HANDLE Key, IN PUCHAR Buffer, IN ULONG BufferLength, IN PUNICODE_STRING AttributeName, OUT PULONG RequiredLength ); NTSTATUS SampSetVariableAttribute( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex, IN ULONG Qualifier, IN PUCHAR Buffer, IN ULONG Length ); NTSTATUS SampUpgradeToCurrentRevision( IN PSAMP_OBJECT Context, IN ULONG AttributeGroup, IN PUCHAR Buffer, IN ULONG LengthOfDataRead, IN PULONG TotalRequiredLength ); #ifdef SAM_DEBUG_ATTRIBUTES VOID SampDumpAttributes( IN PSAMP_OBJECT Context ); VOID SampDumpData( IN PVOID Buffer, IN ULONG Length ); #endif /////////////////////////////////////////////////////////////////////////////// // // // Public Routines // // // /////////////////////////////////////////////////////////////////////////////// VOID SampInitObjectInfoAttributes( ) /*++ This API initializes the attribute field information of the various object information structures. Attribute information includes: FixedStoredSeparately (BOOLEAN) FixedAttributeOffset (ULONG) VariableBufferOffset (ULONG) VariableArrayOffset (ULONG) VariableDataOffset (ULONG) FixedLengthSize (ULONG) VariableAttributeCount (ULONG) Parameters: None. Return Values: None. --*/ { // // Define the size of the header that is in front of our data when // we read it back out of the registry. // #define KEY_VALUE_HEADER_SIZE (SampDwordAlignUlong( \ FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data))) PSAMP_OBJECT_INFORMATION Object; // // SERVER object attribute information // Object = &SampObjectInformation[SampServerObjectType]; Object->FixedStoredSeparately = SAMP_SERVER_STORED_SEPARATELY; Object->FixedAttributesOffset = KEY_VALUE_HEADER_SIZE; Object->FixedLengthSize = sizeof(SAMP_V1_FIXED_LENGTH_SERVER); #if SAMP_SERVER_STORED_SEPARATELY Object->VariableBufferOffset = Object->FixedAttributesOffset + SampDwordAlignUlong(Object->FixedLengthSize); Object->VariableArrayOffset = Object->VariableBufferOffset + KEY_VALUE_HEADER_SIZE; #else Object->VariableBufferOffset = 0; Object->VariableArrayOffset = Object->FixedAttributesOffset + SampDwordAlignUlong(Object->FixedLengthSize); #endif //SAMP_SERVER_STORED_SEPARATELY Object->VariableAttributeCount = SAMP_SERVER_VARIABLE_ATTRIBUTES; Object->VariableDataOffset = SampDwordAlignUlong( Object->VariableArrayOffset + (SAMP_SERVER_VARIABLE_ATTRIBUTES * sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) ); // // DOMAIN object attribute information // Object = &SampObjectInformation[SampDomainObjectType]; Object->FixedStoredSeparately = SAMP_DOMAIN_STORED_SEPARATELY; Object->FixedAttributesOffset = KEY_VALUE_HEADER_SIZE; Object->FixedLengthSize = sizeof(SAMP_V1_0A_FIXED_LENGTH_DOMAIN); #if SAMP_DOMAIN_STORED_SEPARATELY Object->VariableBufferOffset = Object->FixedAttributesOffset + SampDwordAlignUlong(Object->FixedLengthSize); Object->VariableArrayOffset = Object->VariableBufferOffset + KEY_VALUE_HEADER_SIZE; #else Object->VariableBufferOffset = 0; Object->VariableArrayOffset = Object->FixedAttributesOffset + SampDwordAlignUlong(Object->FixedLengthSize); #endif //SAMP_DOMAIN_STORED_SEPARATELY Object->VariableAttributeCount = SAMP_DOMAIN_VARIABLE_ATTRIBUTES; Object->VariableDataOffset = SampDwordAlignUlong( Object->VariableArrayOffset + (SAMP_DOMAIN_VARIABLE_ATTRIBUTES * sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) ); // // USER object attribute information // Object = &SampObjectInformation[SampUserObjectType]; Object->FixedStoredSeparately = SAMP_USER_STORED_SEPARATELY; Object->FixedAttributesOffset = KEY_VALUE_HEADER_SIZE; Object->FixedLengthSize = sizeof(SAMP_V1_0A_FIXED_LENGTH_USER); #if SAMP_USER_STORED_SEPARATELY Object->VariableBufferOffset = Object->FixedAttributesOffset + SampDwordAlignUlong(Object->FixedLengthSize); Object->VariableArrayOffset = Object->VariableBufferOffset + KEY_VALUE_HEADER_SIZE; #else Object->VariableBufferOffset = 0; Object->VariableArrayOffset = Object->FixedAttributesOffset + SampDwordAlignUlong(Object->FixedLengthSize); #endif //SAMP_USER_STORED_SEPARATELY Object->VariableAttributeCount = SAMP_USER_VARIABLE_ATTRIBUTES; Object->VariableDataOffset = SampDwordAlignUlong( Object->VariableArrayOffset + (SAMP_USER_VARIABLE_ATTRIBUTES * sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) ); // // GROUP object attribute information // Object = &SampObjectInformation[SampGroupObjectType]; Object->FixedStoredSeparately = SAMP_GROUP_STORED_SEPARATELY; Object->FixedAttributesOffset = KEY_VALUE_HEADER_SIZE; Object->FixedLengthSize = sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP); #if SAMP_GROUP_STORED_SEPARATELY Object->VariableBufferOffset = Object->FixedAttributesOffset + SampDwordAlignUlong(Object->FixedLengthSize); Object->VariableArrayOffset = Object->VariableBufferOffset + KEY_VALUE_HEADER_SIZE; #else Object->VariableBufferOffset = 0; Object->VariableArrayOffset = Object->FixedAttributesOffset + SampDwordAlignUlong(Object->FixedLengthSize); #endif //SAMP_GROUP_STORED_SEPARATELY Object->VariableAttributeCount = SAMP_GROUP_VARIABLE_ATTRIBUTES; Object->VariableDataOffset = SampDwordAlignUlong( Object->VariableArrayOffset + (SAMP_GROUP_VARIABLE_ATTRIBUTES * sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) ); // // ALIAS object attribute information // Object = &SampObjectInformation[SampAliasObjectType]; Object->FixedStoredSeparately = SAMP_ALIAS_STORED_SEPARATELY; Object->FixedAttributesOffset = KEY_VALUE_HEADER_SIZE; Object->FixedLengthSize = sizeof(SAMP_V1_FIXED_LENGTH_ALIAS); #if SAMP_ALIAS_STORED_SEPARATELY Object->VariableBufferOffset = Object->FixedAttributesOffset + SampDwordAlignUlong(Object->FixedLengthSize); Object->VariableArrayOffset = Object->VariableBufferOffset + KEY_VALUE_HEADER_SIZE; #else Object->VariableBufferOffset = 0; Object->VariableArrayOffset = Object->FixedAttributesOffset + SampDwordAlignUlong(Object->FixedLengthSize); #endif //SAMP_ALIAS_STORED_SEPARATELY Object->VariableAttributeCount = SAMP_ALIAS_VARIABLE_ATTRIBUTES; Object->VariableDataOffset = SampDwordAlignUlong( Object->VariableArrayOffset + (SAMP_ALIAS_VARIABLE_ATTRIBUTES * sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) ); return; } NTSTATUS SampStoreObjectAttributes( IN PSAMP_OBJECT Context, IN BOOLEAN UseKeyHandle ) /*++ This API is used to store an object's attributes onto backing store. The object attributes are not flushed to disk with this routine. They are just added to the RXACT. Parameters: Context - Pointer to an object context block. UseKeyHandle - If TRUE, the RootKey in the context block is passed to the transaction code - this assumes that the key will still be open when the transaction is committed. If FALSE, the RootKey will be ignored and the transaction code will open a key for itself. Return Values: STATUS_SUCCESS - The service completed successfully. Other status values as may be returned by the RXACT services. --*/ { NTSTATUS NtStatus; BOOLEAN FlushFixed = FALSE, FlushVariable = FALSE; HANDLE RootKey; // // See if anything is dirty and needs to be stored // if (Context->FixedValid && Context->FixedDirty) { FlushFixed = TRUE; } if (Context->VariableValid && Context->VariableDirty) { FlushVariable = TRUE; } if (!(FlushFixed || FlushVariable)) { return(STATUS_SUCCESS); } // // Calculate the RootKey to pass to the transaction code // if (UseKeyHandle) { RootKey = Context->RootKey; } else { RootKey = INVALID_HANDLE_VALUE; } // // We keep an open domain context that is used to modify the change // count whenever a change is made. But if this is a domain change // here, then that change will overwrite this one. Check for that // case, and copy this fixed data to the open domain context. Note // that the open domain's variable data never gets changed. // if ( ( Context->ObjectType == SampDomainObjectType ) && ( Context != SampDefinedDomains[Context->DomainIndex].Context ) ) { PSAMP_OBJECT DefinedContext; // // Get a pointer to the corresponding open defined domain. // No changes should have been made to its data. // DefinedContext = SampDefinedDomains[Context->DomainIndex].Context; ASSERT( DefinedContext->FixedValid == TRUE ); ASSERT( DefinedContext->FixedDirty == FALSE ); #if DBG if ( DefinedContext->VariableValid ) { ASSERT( DefinedContext->VariableDirty == FALSE ); } #endif DefinedContext->VariableDirty = FALSE; // // Copy our fixed data over the defined domain's fixed data. // Note that we're assuming that the fixed and variable data are // stored separately. // ASSERT(SampObjectInformation[SampDomainObjectType].FixedStoredSeparately); RtlCopyMemory( SampFixedBufferAddress( DefinedContext ), SampFixedBufferAddress( Context ), SampFixedBufferLength( Context ) ); // // No need to flush this context's fixed data, since the commit // code will flush the same stuff (plus an altered modified count). // FlushFixed = FALSE; Context->FixedDirty = FALSE; } // // One or more of the attributes needs to be stored. // if (!SampObjectInformation[Context->ObjectType].FixedStoredSeparately) { // // fixed and variable-length attributes stored together. // Note - strip off the partial key info struct from the start // NtStatus = RtlAddAttributeActionToRXact( SampRXactContext, RtlRXactOperationSetValue, &(Context->RootName), RootKey, &SampCombinedAttributeName, REG_BINARY, SampFixedBufferAddress(Context), Context->OnDiskUsed - SampFixedBufferOffset(Context) ); #if SAMP_DIAGNOSTICS if (!NT_SUCCESS(NtStatus)) { SampDiagPrint( DISPLAY_STORAGE_FAIL, ("SAM: Failed to add action to RXact (0x%lx)\n", NtStatus) ); IF_SAMP_GLOBAL( BREAK_ON_STORAGE_FAIL ) { ASSERT(NT_SUCCESS(NtStatus)); } } #endif //SAMP_DIAGNOSTICS if ( NT_SUCCESS( NtStatus ) ) { Context->FixedDirty = FALSE; Context->VariableDirty = FALSE; } } else { // // fixed and variable-length attributes stored separately. // Only update the one(s) we need to. // NtStatus = STATUS_SUCCESS; if (FlushFixed) { NtStatus = RtlAddAttributeActionToRXact( SampRXactContext, RtlRXactOperationSetValue, &(Context->RootName), RootKey, &SampFixedAttributeName, REG_BINARY, SampFixedBufferAddress(Context), SampVariableBufferOffset(Context) - SampFixedBufferOffset(Context) ); #if SAMP_DIAGNOSTICS if (!NT_SUCCESS(NtStatus)) { SampDiagPrint( DISPLAY_STORAGE_FAIL, ("SAM: Failed to add action to RXact (0x%lx)\n", NtStatus) ); IF_SAMP_GLOBAL( BREAK_ON_STORAGE_FAIL ) { ASSERT(NT_SUCCESS(NtStatus)); } } #endif //SAMP_DIAGNOSTICS if ( NT_SUCCESS( NtStatus ) ) { Context->FixedDirty = FALSE; } } if (NT_SUCCESS(NtStatus) && FlushVariable) { NtStatus = RtlAddAttributeActionToRXact( SampRXactContext, RtlRXactOperationSetValue, &(Context->RootName), RootKey, &SampVariableAttributeName, REG_BINARY, SampVariableArrayAddress( Context ), SampVariableBufferUsedLength(Context) ); #if SAMP_DIAGNOSTICS if (!NT_SUCCESS(NtStatus)) { SampDiagPrint( DISPLAY_STORAGE_FAIL, ("SAM: Failed to add action to RXact (0x%lx)\n", NtStatus) ); IF_SAMP_GLOBAL( BREAK_ON_STORAGE_FAIL ) { ASSERT(NT_SUCCESS(NtStatus)); } } #endif //SAMP_DIAGNOSTICS if ( NT_SUCCESS( NtStatus ) ) { Context->VariableDirty = FALSE; } } } return(NtStatus); } NTSTATUS SampDeleteAttributeKeys( IN PSAMP_OBJECT Context ) /*++ This API is used to delete the attribute keys that are created in the registry underneath a SAM object. Parameters: Context - Pointer to an object context block. Return Values: STATUS_SUCCESS - The service completed successfully. Error status may be returned by registry calls. --*/ { UNICODE_STRING KeyName; NTSTATUS NtStatus; if (SampObjectInformation[Context->ObjectType].FixedStoredSeparately) { // // Must delete both fixed and variable attribute keys. // NtStatus = SampBuildAccountSubKeyName( SampUserObjectType, &KeyName, Context->TypeBody.User.Rid, &SampFixedAttributeName ); if (NT_SUCCESS(NtStatus)) { NtStatus = RtlAddActionToRXact( SampRXactContext, RtlRXactOperationDelete, &KeyName, 0, NULL, 0 ); SampFreeUnicodeString( &KeyName ); NtStatus = SampBuildAccountSubKeyName( SampUserObjectType, &KeyName, Context->TypeBody.User.Rid, &SampVariableAttributeName ); if (NT_SUCCESS(NtStatus)) { NtStatus = RtlAddActionToRXact( SampRXactContext, RtlRXactOperationDelete, &KeyName, 0, NULL, 0 ); SampFreeUnicodeString( &KeyName ); } } } else { // // Must delete the combined attribute key. // NtStatus = SampBuildAccountSubKeyName( SampUserObjectType, &KeyName, Context->TypeBody.User.Rid, &SampCombinedAttributeName ); if (NT_SUCCESS(NtStatus)) { NtStatus = RtlAddActionToRXact( SampRXactContext, RtlRXactOperationDelete, &KeyName, 0, NULL, 0 ); SampFreeUnicodeString( &KeyName ); } } return( NtStatus ); } NTSTATUS SampGetFixedAttributes( IN PSAMP_OBJECT Context, IN BOOLEAN MakeCopy, OUT PVOID *FixedData ) /*++ This API is used to get a pointer to the fixed-length attributes. Parameters: Context - Pointer to an object context block. FixedData - Receives a pointer to the fixed-length data. Return Values: STATUS_SUCCESS - The service completed successfully. STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not be allocated. --*/ { NTSTATUS NtStatus; // // Make the data valid // NtStatus = SampValidateAttributes( Context, SAMP_FIXED_ATTRIBUTES ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } // // Return a pointer to the fixed-length attributes. // if (MakeCopy == FALSE) { *FixedData = (PVOID)SampFixedBufferAddress( Context ); return(STATUS_SUCCESS); } // // Need to make a copy of the fixed data // *FixedData = (PVOID)MIDL_user_allocate( SampFixedBufferLength( Context ) ); if ((*FixedData) == NULL) { return(STATUS_NO_MEMORY); } RtlCopyMemory( *FixedData, SampFixedBufferAddress( Context ), SampFixedBufferLength( Context ) ); return(STATUS_SUCCESS); } NTSTATUS SampSetFixedAttributes( IN PSAMP_OBJECT Context, IN PVOID FixedData ) /*++ This API is used to replace the fixed-length data attribute. Parameters: Context - Pointer to an object context block. Return Values: STATUS_SUCCESS - The service completed successfully. --*/ { NTSTATUS NtStatus; // // Make the fixed-length data valid // NtStatus = SampValidateAttributes( Context, SAMP_FIXED_ATTRIBUTES ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } if ( FixedData != SampFixedBufferAddress( Context ) ) { // // The caller had a copy of the data, so we must copy the changes // over our data buffer. // RtlCopyMemory( SampFixedBufferAddress( Context ), FixedData, SampFixedBufferLength( Context ) ); } // // Mark the buffer dirty now and it will get flushed when the // changes are committed. // Context->FixedDirty = TRUE; return( NtStatus ); } NTSTATUS SampGetUnicodeStringAttribute( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex, IN BOOLEAN MakeCopy, OUT PUNICODE_STRING UnicodeAttribute ) /*++ This API is used to get a copy of a UNICODE_STRING attribute or a pointer to the attribute. If a pointer to the attribute is sought, care must be taken to ensure the pointer is not used after it becomes invalid. Actions that may cause an attribute pointer to become invalid include setting an attribute value or dereferencing and then referencing the object again. If MakeCopy is FALSE, indicating the string is to be referenced rather than copied, then only the body of the string is referenced. The lengths and pointer are set in the provided argument. Parameters: Context - Pointer to an object context block. AttributeIndex - Indicates the index (into the variable length attribute array) of the attribute to be retrieved as a UNICODE_STRING. MakeCopy - If TRUE, indicates that a copy of the attribute is to be made. If FALSE, indicates a pointer to the attribute is desired without making a copy. WARNING, if this is FALSE, the pointer is only valid while the in-memory copy of the attribute remains in place. Addition or replacement of any variable length attribute may cause the attribute to be moved, and previously returned pointers invalidated. UnicodeAttribute - Receives a pointer to the UNICODE_STRING. If MakeCopy was specified as TRUE, then this pointer points to a block of memory allocated with MIDL_user_allocate() which the caller is responsible for freeing (using MIDL_user_free()). Return Values: STATUS_SUCCESS - The service completed successfully. STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not be allocated. --*/ { NTSTATUS NtStatus; ULONG Length; // // Make sure the requested attribute exists for the specified // object type. // SampValidateAttributeIndex( Context, AttributeIndex ); // // Make the data valid // NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } // // Get the length of the attribute // Length = SampObjectAttributeLength( Context, AttributeIndex ); ASSERT(Length <= 0xFFFF); UnicodeAttribute->MaximumLength = (USHORT)Length; UnicodeAttribute->Length = (USHORT)Length; // // If we are not to allocate memory, then just return a pointer // to the attribute. // if (MakeCopy == FALSE) { UnicodeAttribute->Buffer = (PWSTR)SampObjectAttributeAddress( Context, AttributeIndex ); return(STATUS_SUCCESS); } // // Need to make a copy of the attribute // // NOTE: We should test for zero length here and return a NULL pointer // in that case, but this change would require verification of all of the // callers of this routine, so I'm leaving it as is. // UnicodeAttribute->Buffer = (PSID)MIDL_user_allocate( Length ); if ((UnicodeAttribute->Buffer) == NULL) { return(STATUS_NO_MEMORY); } RtlCopyMemory( UnicodeAttribute->Buffer, SampObjectAttributeAddress( Context, AttributeIndex ), Length ); return(STATUS_SUCCESS); } NTSTATUS SampSetUnicodeStringAttribute( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex, IN PUNICODE_STRING Attribute ) /*++ This API is used to replace a UNICODE_STRING attribute in an object's variable length attributes. Parameters: Context - Pointer to an object context block. AttributeIndex - Indicates the index (into the variable length attribute array) of the attribute to be set as a UNICODE_STRING. Attribute - Points to the new UNICODE_STRING value. Return Values: STATUS_SUCCESS - The service completed successfully. STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not be allocated. --*/ { NTSTATUS NtStatus; // // Make sure the requested attribute exists for the specified // object type. // SampValidateAttributeIndex( Context, AttributeIndex ); // // Make the variable-length data valid // NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } // // Set the new attribute value... // NtStatus = SampSetVariableAttribute( Context, AttributeIndex, 0, // Qualifier not used (PUCHAR)Attribute->Buffer, Attribute->Length ); return(NtStatus); } NTSTATUS SampGetSidAttribute( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex, IN BOOLEAN MakeCopy, OUT PSID *Sid ) /*++ This API is used to get a copy of a SID attribute or a pointer to the attribute. If a pointer to the attribute is sought, care must be taken to ensure the pointer is not used after it becomes invalid. Actions that may cause an attribute pointer to become invalid include setting an attribute value or dereferencing and then referencing the object again. Parameters: Context - Pointer to an object context block. AttributeIndex - Indicates the index (into the variable length attribute array) of the attribute to be retrieved as a SID. MakeCopy - If TRUE, indicates that a copy of the SID is to be made. If FALSE, indicates a pointer to the SID is desired without making a copy. WARNING, if this is FALSE, the pointer is only valid while the in-memory copy of the SID remains in place. Addition or replacement of any variable length attribute may cause the SID to be moved, and previously returned pointers invalidated. Sid - Receives a pointer to the SID. If MakeCopy was specified, then this pointer points to a block of memory allocated with MIDL_user_allocate() which the caller is responsible for freeing (using MIDL_user_free()). Return Values: STATUS_SUCCESS - The service completed successfully. STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not be allocated. --*/ { NTSTATUS NtStatus; ULONG Length; PSID SidAttribute; // // Make sure the requested attribute exists for the specified // object type. // SampValidateAttributeIndex( Context, AttributeIndex ); // // Make the variable-length data valid // NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } // // Get the address of the attribute in question // SidAttribute = (PSID)SampObjectAttributeAddress( Context, AttributeIndex ); // // If we are not to allocate memory, then just return a pointer // to the attribute. // if (MakeCopy == FALSE) { (*Sid) = SidAttribute; return(STATUS_SUCCESS); } // // Need to make a copy of the SID // Length = SampObjectAttributeLength( Context, AttributeIndex ); ASSERT(Length == RtlLengthSid( SidAttribute ) ); (*Sid) = (PSID)MIDL_user_allocate( Length ); if ((*Sid) == NULL) { return(STATUS_NO_MEMORY); } NtStatus = RtlCopySid( Length, (*Sid), SidAttribute ); ASSERT(NT_SUCCESS(NtStatus)); return(NtStatus); } NTSTATUS SampSetSidAttribute( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex, IN PSID Attribute ) /*++ This API is used to replace a SID attribute in an object's variable length attributes. Parameters: Context - Pointer to an object context block. AttributeIndex - Indicates the index (into the variable length attribute array) of the attribute to be set as a SID. Attribute - Points to the new SID value. Length - The length of the new attribute value (in bytes). Return Values: STATUS_SUCCESS - The service completed successfully. STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not be allocated. --*/ { NTSTATUS NtStatus; // // Validate the passed SID // ASSERT(RtlValidSid(Attribute)); // // Make sure the requested attribute exists for the specified // object type. // SampValidateAttributeIndex( Context, AttributeIndex ); // // Make the variable-length data valid // NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } // // Set the new attribute value... // NtStatus = SampSetVariableAttribute( Context, AttributeIndex, 0, // Qualifier not used (PUCHAR)Attribute, RtlLengthSid(Attribute) ); return(NtStatus); } NTSTATUS SampGetAccessAttribute( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex, IN BOOLEAN MakeCopy, OUT PULONG Revision, OUT PSECURITY_DESCRIPTOR *SecurityDescriptor ) /*++ This API is used to get a copy of the object access information. This includes the security descriptor and revision level of the object. Parameters: Context - Pointer to an object context block. AttributeIndex - Indicates the index (into the variable length attribute array) of the attribute to be retrieved. MakeCopy - If TRUE, indicates that a copy of the attribute is to be made. If FALSE, indicates a pointer to the attribute is desired without making a copy. WARNING, if this is FALSE, the pointer is only valid while the in-memory copy of the attribute remains in place. Addition or replacement of any variable length attribute may cause the attribute to be moved, and previously returned pointers invalidated. Revision - Receives the revision level from the access information. SecurityDescriptor - Receives a pointer to the attribute. If MakeCopy was specified as TRUE, then this pointer points to a block of memory allocated with MIDL_user_allocate() which the caller is responsible for freeing (using MIDL_user_free()). Return Values: STATUS_SUCCESS - The service completed successfully. STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not be allocated. --*/ { NTSTATUS NtStatus; ULONG Length; PVOID RawAttribute; // // Make sure the requested attribute exists for the specified // object type. // SampValidateAttributeIndex( Context, AttributeIndex ); // // Make the data valid // NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } // // Get the revision level from the qualifier field of the variable // array entry. // (*Revision) = *(SampObjectAttributeQualifier( Context, AttributeIndex )); // // Get the address of the attribute in question // RawAttribute = (PVOID)SampObjectAttributeAddress( Context, AttributeIndex ); // // If we are not to allocate memory, then just return a pointer // to the attribute. // if (MakeCopy == FALSE) { (*SecurityDescriptor) = (PSECURITY_DESCRIPTOR)RawAttribute; return(STATUS_SUCCESS); } // // Need to make a copy of the attribute // Length = SampObjectAttributeLength( Context, AttributeIndex ); (*SecurityDescriptor) = (PSECURITY_DESCRIPTOR)MIDL_user_allocate( Length ); if ((*SecurityDescriptor) == NULL) { return(STATUS_NO_MEMORY); } RtlCopyMemory( (*SecurityDescriptor), RawAttribute, Length ); return(STATUS_SUCCESS); } NTSTATUS SampSetAccessAttribute( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex, IN PSECURITY_DESCRIPTOR Attribute, IN ULONG Length ) /*++ This API is used to replace a SECURITY_DESCRIPTOR attribute in an object's variable length attributes. Parameters: Context - Pointer to an object context block. AttributeIndex - Indicates the index (into the variable length attribute array) of the attribute to be set as a SECURITY_DESCRIPTOR. Attribute - Points to the new SECURITY_DESCRIPTOR value. Length - The length of the new attribute value (in bytes). Return Values: STATUS_SUCCESS - The service completed successfully. STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not be allocated. --*/ { NTSTATUS NtStatus; // // Make sure the requested attribute exists for the specified // object type. // SampValidateAttributeIndex( Context, AttributeIndex ); // // Make the variable-length data valid // NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } // // Set the new attribute value... // NtStatus = SampSetVariableAttribute( Context, AttributeIndex, SAMP_REVISION, (PUCHAR)Attribute, Length ); return(NtStatus); } NTSTATUS SampGetUlongArrayAttribute( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex, IN BOOLEAN MakeCopy, OUT PULONG *UlongArray, OUT PULONG UsedCount, OUT PULONG LengthCount ) /*++ This API is used to get a copy of an array of ULONGs attribute or a pointer to the attribute. If a pointer to the attribute is sought, care must be taken to ensure the pointer is not used after it becomes invalid. Actions that may cause an attribute pointer to become invalid include setting an attribute value or dereferencing and then referencing the object again. Parameters: Context - Pointer to an object context block. AttributeIndex - Indicates the index (into the variable length attribute array) of the attribute to be retrieved as a ULONG array. MakeCopy - If TRUE, indicates that a copy of the attribute is to be made. If FALSE, indicates a pointer to the attribute is desired without making a copy. WARNING, if this is FALSE, the pointer is only valid while the in-memory copy of the attribute remains in place. Addition or replacement of any variable length attribute may cause the attribute to be moved, and previously returned pointers invalidated. UlongArray - Receives a pointer to the array of ULONGS. If MakeCopy was specified as TRUE, then this pointer points to a block of memory allocated with MIDL_user_allocate() which the caller is responsible for freeing (using MIDL_user_free()). UsedCount - Receives the number of elements used in the array. LengthCount - Receives the total number of elements in the array (some at the end may be unused). If this value is zero, then UlongArray will be returned as NULL. Return Values: STATUS_SUCCESS - The service completed successfully. STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not be allocated. --*/ { NTSTATUS NtStatus; ULONG Length; // // Make sure the requested attribute exists for the specified // object type. // SampValidateAttributeIndex( Context, AttributeIndex ); // // Make the data valid // NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } // // Get the count of array elements. // If this is zero, then return will a null buffer pointer. // (*UsedCount) = *(SampObjectAttributeQualifier( Context, AttributeIndex)); // // Get the length of the attribute // Length = SampObjectAttributeLength( Context, AttributeIndex ); (*LengthCount) = Length / sizeof(ULONG); ASSERT( (*UsedCount) <= (*LengthCount) ); if ((*LengthCount) == 0) { (*UlongArray) = NULL; return(STATUS_SUCCESS); } // // If we are not to allocate memory, then just return a pointer // to the attribute. // if (MakeCopy == FALSE) { (*UlongArray) = (PULONG)SampObjectAttributeAddress( Context, AttributeIndex ); return(STATUS_SUCCESS); } // // Need to make a copy of the attribute // (*UlongArray) = (PULONG)MIDL_user_allocate( Length ); if ((*UlongArray) == NULL) { return(STATUS_NO_MEMORY); } RtlCopyMemory( (*UlongArray), SampObjectAttributeAddress( Context, AttributeIndex ), Length ); return(STATUS_SUCCESS); } NTSTATUS SampSetUlongArrayAttribute( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex, IN PULONG Attribute, IN ULONG UsedCount, IN ULONG LengthCount ) /*++ This API is used to replace a ULONG array attribute in an object's variable length attributes. Parameters: Context - Pointer to an object context block. AttributeIndex - Indicates the index (into the variable length attribute array) of the attribute to be set. Attribute - Points to the new ULONG array value. UsedCount - The number of used elements in the array. LengthCount - the total number of elements in the array (some at the end may be unused). Return Values: STATUS_SUCCESS - The service completed successfully. STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not be allocated. --*/ { NTSTATUS NtStatus; ASSERT( LengthCount >= UsedCount ); // // Make sure the requested attribute exists for the specified // object type. // SampValidateAttributeIndex( Context, AttributeIndex ); // // Make the variable-length data valid // NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } // // Set the new attribute value... // NtStatus = SampSetVariableAttribute( Context, AttributeIndex, UsedCount, // Qualifier contains used element count (PUCHAR)Attribute, (LengthCount * sizeof(ULONG)) ); return(NtStatus); } NTSTATUS SampGetLargeIntArrayAttribute( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex, IN BOOLEAN MakeCopy, OUT PLARGE_INTEGER *LargeIntArray, OUT PULONG Count ) /*++ This API is used to get a copy of an array of LARGE_INTEGERs attribute or a pointer to the attribute. If a pointer to the attribute is sought, care must be taken to ensure the pointer is not used after it becomes invalid. Actions that may cause an attribute pointer to become invalid include setting an attribute value or dereferencing and then referencing the object again. Parameters: Context - Pointer to an object context block. AttributeIndex - Indicates the index (into the variable length attribute array) of the attribute to be retrieved. MakeCopy - If TRUE, indicates that a copy of the attribute is to be made. If FALSE, indicates a pointer to the attribute is desired without making a copy. WARNING, if this is FALSE, the pointer is only valid while the in-memory copy of the attribute remains in place. Addition or replacement of any variable length attribute may cause the attribute to be moved, and previously returned pointers invalidated. LargeIntArray - Receives a pointer to the array of ULONGS. If MakeCopy was specified as TRUE, then this pointer points to a block of memory allocated with MIDL_user_allocate() which the caller is responsible for freeing (using MIDL_user_free()). Count - Receives the number of elements in the array. If this value is zero, then LargeIntArray will be returned as NULL. Return Values: STATUS_SUCCESS - The service completed successfully. STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not be allocated. --*/ { NTSTATUS NtStatus; ULONG Length; // // Make sure the requested attribute exists for the specified // object type. // SampValidateAttributeIndex( Context, AttributeIndex ); // // Make the data valid // NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } // // Get the count of array elements. // If this is zero, then return will a null buffer pointer. // (*Count) = *(SampObjectAttributeQualifier( Context, AttributeIndex)); if ((*Count) == 0) { (*LargeIntArray) = NULL; return(STATUS_SUCCESS); } // // Get the length of the attribute // Length = SampObjectAttributeLength( Context, AttributeIndex ); ASSERT((*Count) == (Length / sizeof(LARGE_INTEGER)) ); // // If we are not to allocate memory, then just return a pointer // to the attribute. // if (MakeCopy == FALSE) { (*LargeIntArray) = (PLARGE_INTEGER)SampObjectAttributeAddress( Context, AttributeIndex ); return(STATUS_SUCCESS); } // // Need to make a copy of the attribute // (*LargeIntArray) = (PLARGE_INTEGER)MIDL_user_allocate( Length ); if ((*LargeIntArray) == NULL) { return(STATUS_NO_MEMORY); } RtlCopyMemory( (*LargeIntArray), SampObjectAttributeAddress( Context, AttributeIndex ), Length ); return(STATUS_SUCCESS); } NTSTATUS SampSetLargeIntArrayAttribute( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex, IN PLARGE_INTEGER Attribute, IN ULONG Count ) /*++ This API is used to replace a LARGE_INTEGER array attribute in an object's variable length attributes. Parameters: Context - Pointer to an object context block. AttributeIndex - Indicates the index (into the variable length attribute array) of the attribute to be set. Attribute - Points to the new LARGE_INTEGER array value. Count - The number of elements in the array. Return Values: STATUS_SUCCESS - The service completed successfully. STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not be allocated. --*/ { NTSTATUS NtStatus; // // Make sure the requested attribute exists for the specified // object type. // SampValidateAttributeIndex( Context, AttributeIndex ); // // Make the variable-length data valid // NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } // // Set the new attribute value... // NtStatus = SampSetVariableAttribute( Context, AttributeIndex, Count, // Qualifier contains element count (PUCHAR)Attribute, (Count * sizeof(LARGE_INTEGER)) ); return(NtStatus); } NTSTATUS SampGetSidArrayAttribute( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex, IN BOOLEAN MakeCopy, OUT PSID *SidArray, OUT PULONG Length, OUT PULONG Count ) /*++ This API is used to get a copy of an array of SIDs attribute or a pointer to the attribute. If a pointer to the attribute is sought, care must be taken to ensure the pointer is not used after it becomes invalid. Actions that may cause an attribute pointer to become invalid include setting an attribute value or dereferencing and then referencing the object again. NOTE: This routine does not define the structure of a SID array, so this effectively is a GetRawDataAttribute routine. Parameters: Context - Pointer to an object context block. AttributeIndex - Indicates the index (into the variable length attribute array) of the attribute to be retrieved. MakeCopy - If TRUE, indicates that a copy of the attribute is to be made. If FALSE, indicates a pointer to the attribute is desired without making a copy. WARNING, if this is FALSE, the pointer is only valid while the in-memory copy of the attribute remains in place. Addition or replacement of any variable length attribute may cause the attribute to be moved, and previously returned pointers invalidated. SidArray - Receives a pointer to the array of SIDs. If MakeCopy was specified as TRUE, then this pointer points to a block of memory allocated with MIDL_user_allocate() which the caller is responsible for freeing (using MIDL_user_free()). Count - Receives the number of elements in the array. If this value is zero, then SidArray will be returned as NULL. Return Values: STATUS_SUCCESS - The service completed successfully. STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not be allocated. --*/ { NTSTATUS NtStatus; // // Make sure the requested attribute exists for the specified // object type. // SampValidateAttributeIndex( Context, AttributeIndex ); // // Make the data valid // NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } // // Get the count of array elements. // If this is zero, then return will a null buffer pointer. // (*Count) = *(SampObjectAttributeQualifier( Context, AttributeIndex)); if ((*Count) == 0) { (*SidArray) = NULL; (*Length) = 0; return(STATUS_SUCCESS); } // // Get the length of the attribute // (*Length) = SampObjectAttributeLength( Context, AttributeIndex ); // // If we are not to allocate memory, then just return a pointer // to the attribute. // if (MakeCopy == FALSE) { (*SidArray) = (PSID)SampObjectAttributeAddress( Context, AttributeIndex ); return(STATUS_SUCCESS); } // // Need to make a copy of the attribute // (*SidArray) = (PSID)MIDL_user_allocate( (*Length) ); if ((*SidArray) == NULL) { return(STATUS_NO_MEMORY); } RtlCopyMemory( (*SidArray), SampObjectAttributeAddress( Context, AttributeIndex ), (*Length) ); return(STATUS_SUCCESS); } NTSTATUS SampSetSidArrayAttribute( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex, IN PSID Attribute, IN ULONG Length, IN ULONG Count ) /*++ This API is used to replace a SID array attribute in an object's variable length attributes. Parameters: Context - Pointer to an object context block. AttributeIndex - Indicates the index (into the variable length attribute array) of the attribute to be set. Attribute - Points to the new SID array value. Length - Number of byte in the attribute buffer. Count - Number of SIDs in the array. Return Values: STATUS_SUCCESS - The service completed successfully. STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not be allocated. --*/ { NTSTATUS NtStatus; // // Make sure the requested attribute exists for the specified // object type. // SampValidateAttributeIndex( Context, AttributeIndex ); // // Make the variable-length data valid // NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } // // Set the new attribute value... // NtStatus = SampSetVariableAttribute( Context, AttributeIndex, Count, // Qualifier contains element count (PUCHAR)Attribute, Length ); return(NtStatus); } NTSTATUS SampGetLogonHoursAttribute( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex, IN BOOLEAN MakeCopy, OUT PLOGON_HOURS LogonHours ) /*++ This API is used to get a copy of a logon hours attribute or a pointer to the attribute. If a pointer to the attribute is sought, care must be taken to ensure the pointer is not used after it becomes invalid. Actions that may cause an attribute pointer to become invalid include setting an attribute value or dereferencing and then referencing the object again. Parameters: Context - Pointer to an object context block. AttributeIndex - Indicates the index (into the variable length attribute array) of the attribute to be retrieved. MakeCopy - If TRUE, indicates that a copy of the attribute is to be made. If FALSE, indicates a pointer to the attribute is desired without making a copy. WARNING, if this is FALSE, the pointer is only valid while the in-memory copy of the attribute remains in place. Addition or replacement of any variable length attribute may cause the attribute to be moved, and previously returned pointers invalidated. LogonHours - Receives the logon hours information. If MakeCopy is TRUE then the bitmap pointed to from within this structure will be a copy of the attribute and must be deallocated uing MIDL_user_free(). Otherwise, this same field will point to the bitmap in the on-disk buffer. Return Values: STATUS_SUCCESS - The service completed successfully. STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not be allocated. --*/ { NTSTATUS NtStatus; ULONG Length, Units; // // Make sure the requested attribute exists for the specified // object type. // SampValidateAttributeIndex( Context, AttributeIndex ); // // Make the data valid // NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } // // Get the time units. // If this is zero, then return will a null buffer pointer. // Units = *(SampObjectAttributeQualifier( Context, AttributeIndex)); ASSERT(Units <= 0xFFFF); LogonHours->UnitsPerWeek = (USHORT)Units; if (Units == 0) { LogonHours->LogonHours = NULL; return(STATUS_SUCCESS); } // // If we are not to allocate memory, then just return a pointer // to the attribute. // if (MakeCopy == FALSE) { LogonHours->LogonHours = (PUCHAR)SampObjectAttributeAddress( Context, AttributeIndex ); return(STATUS_SUCCESS); } // // Get the length of the attribute // Length = SampObjectAttributeLength( Context, AttributeIndex ); ASSERT(Length <= 0xFFFF); // // Need to make a copy of the attribute // LogonHours->LogonHours = (PUCHAR)MIDL_user_allocate( Length ); if (LogonHours->LogonHours == NULL) { return(STATUS_NO_MEMORY); } RtlCopyMemory( LogonHours->LogonHours, SampObjectAttributeAddress( Context, AttributeIndex ), Length ); return(STATUS_SUCCESS); } NTSTATUS SampSetLogonHoursAttribute( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex, IN PLOGON_HOURS Attribute ) /*++ This API is used to replace a LOGON_HOURS attribute in an object's variable length attributes. UnitsPerWeek are stored in the Qualifier field of the attribute. Parameters: Context - Pointer to an object context block. AttributeIndex - Indicates the index (into the variable length attribute array) of the attribute to be set. Attribute - Points to the new LOGON_HOURS value. Return Values: STATUS_SUCCESS - The service completed successfully. STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not be allocated. --*/ { NTSTATUS NtStatus; PUCHAR LogonHours; ULONG Length; USHORT UnitsPerWeek; // // Make sure the requested attribute exists for the specified // object type. // SampValidateAttributeIndex( Context, AttributeIndex ); // // Make the variable-length data valid // NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } // // Grab the UnitsPerWeek value for the logon_hours structure. // We use this to calculate the length of the data. // if ( Attribute == NULL ) { UnitsPerWeek = 0; LogonHours = NULL; } else { UnitsPerWeek = Attribute->UnitsPerWeek; LogonHours = Attribute->LogonHours; } // // Validate the data - make sure that if the units per week are non-zero // then the logon hours buffer is non-NULL. // if ( (UnitsPerWeek != 0) && (LogonHours == NULL) ) { return(STATUS_INVALID_PARAMETER); } // // Calculate length of logon_hours structure // Length = (ULONG)((UnitsPerWeek + 7) / 8); // // Set the new attribute value... // NtStatus = SampSetVariableAttribute( Context, AttributeIndex, (ULONG)UnitsPerWeek, // Qualifier contains units per week LogonHours, Length ); return(NtStatus); } /////////////////////////////////////////////////////////////////////////////// // // // private routines // // // /////////////////////////////////////////////////////////////////////////////// NTSTATUS SampValidateAttributes( IN PSAMP_OBJECT Context, IN ULONG AttributeGroup ) /*++ Ensure specified attributes are in-memory. If they are not, then read them from backing store. Parameters: Context - Pointer to an object context block. AttributeGroup - identifies which kind of attributes are being validated (SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE_ATTRIBUTES). Return Values: STATUS_SUCCESS - The attributes are in-memory. STATUS_NO_MEMORY - Memory could not be allocated to retrieve the attributes. Other values as may be returned by registry API trying to retrieve the attributes from backing store. --*/ { NTSTATUS NtStatus; ULONG RequiredLength, TotalRequiredLength, BufferLength; PUCHAR Buffer; PUNICODE_STRING KeyAttributeName; BOOLEAN CreatedObject = FALSE; // // The data might already be in memory. // if (AttributeGroup == SAMP_FIXED_ATTRIBUTES) { if (SampFixedAttributesValid( Context )) { ASSERT(Context->OnDisk != NULL); return(STATUS_SUCCESS); } } else { ASSERT( AttributeGroup == SAMP_VARIABLE_ATTRIBUTES ); if (SampVariableAttributesValid( Context )) { ASSERT(Context->OnDisk != NULL); return(STATUS_SUCCESS); } } // // Retrieve it from the registry, or allocate it if new. // NtStatus = SampGetAttributeBufferReadInfo( Context, AttributeGroup, &Buffer, &BufferLength, &KeyAttributeName ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } if ( Context->RootKey != INVALID_HANDLE_VALUE ) { // // Account exists on disk, so read in the attributes. // NtStatus = SampReadRegistryAttribute( Context->RootKey, Buffer, BufferLength, KeyAttributeName, &RequiredLength ); RequiredLength = SampDwordAlignUlong(RequiredLength); if ( ( SampObjectInformation[Context->ObjectType].FixedStoredSeparately ) && ( AttributeGroup == SAMP_VARIABLE_ATTRIBUTES ) ) { // // RequiredLength was returned to us as the length of the // variable attributes on the disk. However, we're going // to be using it to determine the total buffer size as well // as to set how much of the buffer is in use, so we must add // the size of the fixed stuff that preceeds the variable // buffer. // TotalRequiredLength = RequiredLength + SampVariableBufferOffset( Context ); } else { // // Either the attribute groups are read together, or we're // reading in the fixed attribute group. Either way, we // already have the total size we need. // TotalRequiredLength = RequiredLength; } if ((NtStatus == STATUS_BUFFER_TOO_SMALL) || ( NtStatus == STATUS_BUFFER_OVERFLOW ) ) { NtStatus = SampExtendAttributeBuffer( Context, TotalRequiredLength ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } NtStatus = SampGetAttributeBufferReadInfo( Context, AttributeGroup, &Buffer, &BufferLength, &KeyAttributeName ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } NtStatus = SampReadRegistryAttribute( Context->RootKey, Buffer, BufferLength, KeyAttributeName, &RequiredLength ); } } else { // // We're creating a new object. // // Initialize the requiredlength to the amount of the buffer // we have used when we created the empty attributes. This will // be the value stored in OnDiskUsed. // // Note OnDiskUsed is only used by operations on the variable // length attributes. // TotalRequiredLength = SampVariableDataOffset(Context); ASSERT(TotalRequiredLength <= Context->OnDiskAllocated); CreatedObject = TRUE; } // // if we read something, indicate that the corresponding buffer // (and maybe both) are now valid. // // Also set the used and free information for the buffer if necessary. // if (NT_SUCCESS(NtStatus)) { if (SampObjectInformation[Context->ObjectType].FixedStoredSeparately) { // // only one attribute group was read in // if (AttributeGroup == SAMP_FIXED_ATTRIBUTES) { Context->FixedValid = TRUE; Context->FixedDirty = FALSE; } else { ASSERT(AttributeGroup == SAMP_VARIABLE_ATTRIBUTES); Context->VariableValid = TRUE; Context->VariableDirty = FALSE; Context->OnDiskUsed = SampDwordAlignUlong(TotalRequiredLength); Context->OnDiskFree = Context->OnDiskAllocated - Context->OnDiskUsed; } } else { // // Both attribute groups read in. // Context->FixedValid = TRUE; Context->FixedDirty = FALSE; Context->VariableValid = TRUE; Context->VariableDirty = FALSE; Context->OnDiskUsed = SampDwordAlignUlong(TotalRequiredLength); Context->OnDiskFree = Context->OnDiskAllocated - Context->OnDiskUsed; } } if (NT_SUCCESS(NtStatus) && !CreatedObject) { // // make any adjustments necessary to bring the data // just read in up to current revision format. // NtStatus = SampUpgradeToCurrentRevision( Context, AttributeGroup, Buffer, RequiredLength, &TotalRequiredLength ); } #ifdef SAM_DEBUG_ATTRIBUTES if (SampDebugAttributes) { DbgPrint("SampValidateAttributes - initialized the context :\n\n"); SampDumpAttributes(Context); } #endif return(NtStatus); } NTSTATUS SampUpgradeToCurrentRevision( IN PSAMP_OBJECT Context, IN ULONG AttributeGroup, IN OUT PUCHAR Buffer, IN ULONG LengthOfDataRead, IN OUT PULONG TotalRequiredLength ) /*++ Make any changes necessary bring attributes just read in from disk up to the current revision level format. When we upgrade our attribute format, we don't bother changing all data on disk. We take a lazy update approach, and only change the data as it is changed for other operations. This means that data we read from disk may be from revision 1. When this is detected, the data is copied into a current revision structure, and a pointer to that buffer is returned. NOTE: For future reference, GROUP and ALIAS objects have a revision level stored as a "Qualifier" value associated with the security descriptor attribute. The SERVER object stores the revision level in its fixed length attributes. Parameters: Context - Pointer to an object context block. AttributeGroup - identifies which kind of attributes are being validated (SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE_ATTRIBUTES). Buffer - Pointer to the buffer containing the attributes. LengthOfDataRead - This is an important value. It must be the value returned from the registry on the read operation. This tells us exactly how many bytes of data were retrieved from disk. TotalRequiredLength - Will be left unchanged if no update was was required. If an updated was made, this will be adjusted to reflect the new length of the data. Return Values: None. --*/ { LARGE_INTEGER ZeroModifiedCount = {0,0}; PULONG Pointer; NTSTATUS NtStatus = STATUS_SUCCESS; // // Note that Buffer points inside a buffer that is // hung off the Context block. We don't need to re-allocate // a new attributes buffer because we are only changing // fixed-length attributes in this release (and the variable // length attributes were placed beyond the end of the new // format fixed-length data). // // The approach we take is to copy the current fixed-length // contents into a temporary buffer, and then copy them back // into the attribute buffer. This can be done with stack // variables. // // // Switch on the type of objects that have gone through revision // changes. // switch (Context->ObjectType) { case SampDomainObjectType: // // Domain FIXED_LENGTH attributes have had the following // revisions: // // Revision 0x00010001 - NT1.0 (Revision NOT stored in ) // (record. ) // (Must ascertain revision ) // (by record length. ) // // Revision 0x00010002 - NT1.0a (Revision is first ULONG ) // (in record. ) if (LengthOfDataRead == (sizeof(SAMP_V1_0_FIXED_LENGTH_DOMAIN) + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)) ) { PSAMP_V1_0A_FIXED_LENGTH_DOMAIN V1aFixed; SAMP_V1_0_FIXED_LENGTH_DOMAIN V1Fixed, *OldV1Fixed; // // Update from revision 0x00010001 // // First, copy the current buffer contents into a temporary // buffer. // OldV1Fixed = (PSAMP_V1_0_FIXED_LENGTH_DOMAIN)(Buffer + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)); RtlMoveMemory(&V1Fixed, OldV1Fixed, sizeof(SAMP_V1_0_FIXED_LENGTH_DOMAIN)); // // Now copy it back in the new format // V1aFixed = (PSAMP_V1_0A_FIXED_LENGTH_DOMAIN)OldV1Fixed; V1aFixed->CreationTime = V1Fixed.CreationTime; V1aFixed->ModifiedCount = V1Fixed.ModifiedCount; V1aFixed->MaxPasswordAge = V1Fixed.MaxPasswordAge; V1aFixed->MinPasswordAge = V1Fixed.MinPasswordAge; V1aFixed->ForceLogoff = V1Fixed.ForceLogoff; V1aFixed->NextRid = V1Fixed.NextRid; V1aFixed->PasswordProperties = V1Fixed.PasswordProperties; V1aFixed->MinPasswordLength = V1Fixed.MinPasswordLength; V1aFixed->PasswordHistoryLength = V1Fixed.PasswordHistoryLength; V1aFixed->ServerState = V1Fixed.ServerState; V1aFixed->ServerRole = V1Fixed.ServerRole; V1aFixed->UasCompatibilityRequired = V1Fixed.UasCompatibilityRequired; // // And initialize fields new for this revision // V1aFixed->Revision = SAMP_REVISION; V1aFixed->LockoutDuration.LowPart = 0xCF1DCC00; // 30 minutes - low part V1aFixed->LockoutDuration.HighPart = 0XFFFFFFFB; // 30 minutes - high part V1aFixed->LockoutObservationWindow.LowPart = 0xCF1DCC00; // 30 minutes - low part V1aFixed->LockoutObservationWindow.HighPart = 0XFFFFFFFB; // 30 minutes - high part V1aFixed->LockoutThreshold = 0; // Disabled if (V1aFixed->ServerRole == DomainServerRolePrimary) { V1aFixed->ModifiedCountAtLastPromotion = V1Fixed.ModifiedCount; } else { V1aFixed->ModifiedCountAtLastPromotion = ZeroModifiedCount; } } break; //out of switch case SampUserObjectType: // // User FIXED_LENGTH attributes have had the following // revisions: // // Revision 0x00010001 - NT1.0 (Revision NOT stored in ) // (record. ) // (Must ascertain revision ) // (by record length. ) // // Revision 0x00010002 - NT1.0a (Revision is first ULONG ) // (in record. ) // Revision 0x00010002a - NT3.5 (Revision is first ULONG ) // (in record, still ) // (0x00010002. Must ) // (ascertain revison by ) // (by record length ) if (LengthOfDataRead == (sizeof(SAMP_V1_FIXED_LENGTH_USER) + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)) ) { PSAMP_V1_0A_FIXED_LENGTH_USER V1aFixed; SAMP_V1_FIXED_LENGTH_USER V1Fixed, *OldV1Fixed; // // Update from revision 0x00010001 // // First, copy the current buffer contents into a temporary // buffer. // OldV1Fixed = (PSAMP_V1_FIXED_LENGTH_USER)(Buffer + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)); RtlMoveMemory(&V1Fixed, OldV1Fixed, sizeof(SAMP_V1_FIXED_LENGTH_USER)); // // Now copy it back in the new format // V1aFixed = (PSAMP_V1_0A_FIXED_LENGTH_USER)OldV1Fixed; V1aFixed->LastLogon = V1Fixed.LastLogon; V1aFixed->LastLogoff = V1Fixed.LastLogoff; V1aFixed->PasswordLastSet = V1Fixed.PasswordLastSet; V1aFixed->AccountExpires = V1Fixed.AccountExpires; V1aFixed->UserId = V1Fixed.UserId; V1aFixed->PrimaryGroupId = V1Fixed.PrimaryGroupId; V1aFixed->UserAccountControl = V1Fixed.UserAccountControl; V1aFixed->CountryCode = V1Fixed.CountryCode; V1aFixed->CodePage = V1Fixed.CodePage; V1aFixed->BadPasswordCount = V1Fixed.BadPasswordCount; V1aFixed->LogonCount = V1Fixed.LogonCount; V1aFixed->AdminCount = V1Fixed.AdminCount; // // And initialize fields new for this revision // V1aFixed->Revision = SAMP_REVISION; V1aFixed->LastBadPasswordTime = SampHasNeverTime; V1aFixed->OperatorCount = 0; V1aFixed->Unused2 = 0; } else if ((LengthOfDataRead == (sizeof(SAMP_V1_0_FIXED_LENGTH_USER) + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)) ) && (AttributeGroup == SAMP_FIXED_ATTRIBUTES)) { PSAMP_V1_0A_FIXED_LENGTH_USER V1aFixed; // // Update from revision 0x00010002 // // Just set the added field at the end to 0. // V1aFixed = (PSAMP_V1_0A_FIXED_LENGTH_USER)(Buffer + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)); V1aFixed->OperatorCount = 0; V1aFixed->Unused2 = 0; } break; //out of switch case SampGroupObjectType: // // Group FIXED_LENGTH attributes have had the following // revisions: // // Revision 0x00010001 - NT1.0 (Revision NOT stored in ) // (record. ) // (Must ascertain revision ) // (by first few ULONGs. ) // // Revision 0x00010002 - NT1.0a (Revision is first ULONG ) // (in record. ) Pointer = (PULONG) (Buffer + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)); // // The old fixed length group had a RID in the first ULONG and // an attributes field in the second. The attributes are in the // first and last nibble of the field. Currently, the RID is in // the second ULONG. Since all RIDs are more than one nibble, // a rid will always have something set in the middle six nibbles. // if ( ( Pointer[0] != SAMP_REVISION ) && ( ( Pointer[1] & 0x0ffffff0 ) == 0 ) ) { PSAMP_V1_0A_FIXED_LENGTH_GROUP V1aFixed; SAMP_V1_FIXED_LENGTH_GROUP V1Fixed, *OldV1Fixed; ULONG TotalLengthRequired; // // Calculate the length required for the new group information. // It is the size of the old group plus enough space for the // new fields in the new fixed attributes. // TotalLengthRequired = SampDwordAlignUlong( LengthOfDataRead + sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP) - sizeof(SAMP_V1_FIXED_LENGTH_GROUP) ); NtStatus = SampExtendAttributeBuffer( Context, TotalLengthRequired ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } // // Get the new buffer pointer // Buffer = Context->OnDisk; // // Move the variable information up to make space for the // fixed information // RtlMoveMemory( Buffer + SampFixedBufferOffset( Context ) + sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP), Buffer + SampFixedBufferOffset( Context) + sizeof(SAMP_V1_FIXED_LENGTH_GROUP), LengthOfDataRead - SampFixedBufferOffset( Context) - sizeof(SAMP_V1_FIXED_LENGTH_GROUP) ); // // Update from revision 0x00010001 // // First, copy the current buffer contents into a temporary // buffer. // OldV1Fixed = (PSAMP_V1_FIXED_LENGTH_GROUP)(Buffer + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)); RtlCopyMemory(&V1Fixed, OldV1Fixed, sizeof(SAMP_V1_FIXED_LENGTH_GROUP)); // // Now copy it back in the new format // V1aFixed = (PSAMP_V1_0A_FIXED_LENGTH_GROUP)OldV1Fixed; V1aFixed->Revision = SAMP_REVISION; V1aFixed->Unused1 = 0; V1aFixed->RelativeId = V1Fixed.RelativeId; V1aFixed->Attributes = V1Fixed.Attributes; V1aFixed->AdminCount = (V1Fixed.AdminGroup) ? TRUE : FALSE; V1aFixed->OperatorCount = 0; // // Update the indicator of how long the on disk structure // is. // Context->OnDiskUsed += (sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP) - sizeof(SAMP_V1_FIXED_LENGTH_GROUP)); Context->OnDiskFree = Context->OnDiskAllocated - Context->OnDiskUsed; } break; default: // // The rest of the object types have not changed format // and so need not be updated. // break; //out of switch } return(NtStatus); } PUCHAR SampObjectAttributeAddress( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex ) /*++ Retrieve the address of a variable-length attribute. The attributes are assumed to already be in-memory. Parameters: Context - Pointer to an object context block. AttributeIndex - Indicates the index (into the variable length attribute array) of the attribute to be retrieved. Return Values: STATUS_SUCCESS - The attributes are in-memory. STATUS_NO_MEMORY - Memory could not be allocated to retrieve the attributes. Other values as may be returned by registry API trying to retrieve the attributes from backing store. --*/ { PSAMP_VARIABLE_LENGTH_ATTRIBUTE AttributeArray; PUCHAR AttributeAddress; ASSERT( SampVariableAttributesValid( Context ) ); AttributeArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE) SampVariableArrayAddress( Context ); AttributeAddress = (PUCHAR)Context->OnDisk + ( SampVariableDataOffset( Context ) + AttributeArray[AttributeIndex].Offset ); return( AttributeAddress ); } ULONG SampObjectAttributeLength( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex ) /*++ Retrieve the length of a variable-length attribute. The attributes are assumed to already be in-memory. Parameters: Context - Pointer to an object context block. AttributeIndex - Indicates the index (into the variable length attribute array) of the attribute whose length is to be retrieved. Return Values: The length of the attribute (in bytes). --*/ { PSAMP_VARIABLE_LENGTH_ATTRIBUTE AttributeArray; ASSERT( SampVariableAttributesValid( Context ) ); AttributeArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE) SampVariableArrayAddress( Context ); return( AttributeArray[AttributeIndex].Length ); } PULONG SampObjectAttributeQualifier( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex ) /*++ Retrieve the address of the qualifier field of a variable-length attribute. The attributes are assumed to already be in-memory. Parameters: Context - Pointer to an object context block. AttributeIndex - Indicates the index (into the variable length attribute array) of the attribute whose qualifier address is to be returned. Return Values: The address of the specifed attribute's qualifier field. --*/ { PSAMP_VARIABLE_LENGTH_ATTRIBUTE AttributeArray; ASSERT( SampVariableAttributesValid( Context ) ); AttributeArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE) SampVariableArrayAddress( Context ); return( &(AttributeArray[AttributeIndex].Qualifier) ); } NTSTATUS SampGetAttributeBufferReadInfo( IN PSAMP_OBJECT Context, IN ULONG AttributeGroup, OUT PUCHAR *Buffer, OUT PULONG BufferLength, OUT PUNICODE_STRING *KeyAttributeName ) /*++ Get attribute buffer information needed to read data from backing store. If there is currently no attribute buffer, then allocate one. Parameters: Context - Pointer to an object context block. AttributeGroup - Indicates which attribute grouping you are interested in. This is only interesting if the fixed and variable-length attributes are stored separately. Buffer - Receives a pointer to the beginning of the appropriate buffer (fixed or variable). This will be dword aligned. If the attributes are stored together, this will point to the beginning of the fixed-length attributes. BufferLength - Receives the number of bytes in the buffer. KeyAttributeName - Receives a pointer to the unicode name of the attribute to read the attributes from. Return Values: STATUS_SUCCESS - The attributes have been read. STATUS_NO_MEMORY - Memory could not be allocated to receive the data from disk. Other values as may be returned reading from disk. --*/ { NTSTATUS NtStatus = STATUS_SUCCESS; // // If the context block currently has no buffer info, then // "extend" (create) it so we can return buffer information. // if (Context->OnDiskAllocated == 0) { NtStatus = SampExtendAttributeBuffer( Context, SAMP_MINIMUM_ATTRIBUTE_ALLOC ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } } // // Get the buffer address and length // if (SampObjectInformation[Context->ObjectType].FixedStoredSeparately) { // // stored separately. Address and length is dependent upon // what is being asked for. Source registry attribute name // is also. // if (AttributeGroup == SAMP_FIXED_ATTRIBUTES) { (*Buffer) = Context->OnDisk; (*BufferLength) = SampVariableBufferOffset( Context ); (*KeyAttributeName) = &SampFixedAttributeName; } else { (*Buffer) = SampVariableBufferAddress( Context ); (*BufferLength) = SampVariableBufferLength( Context ); (*KeyAttributeName) = &SampVariableAttributeName; } } else { // // Attributes stored together - doesn't matter which is being // asked for. // (*Buffer) = Context->OnDisk; (*BufferLength) = Context->OnDiskAllocated; (*KeyAttributeName) = &SampCombinedAttributeName; } return(NtStatus); } NTSTATUS SampExtendAttributeBuffer( IN PSAMP_OBJECT Context, IN ULONG NewSize ) /*++ This routine extends (or creates) an attribute buffer by allocating a larger one. It then copies the existing buffer's contents into the new buffer, if there is an existing buffer. If a new buffer can not be allocated, then the context block is returned with the old buffer intact. If this call succeeds, the buffer will be at least as large as that asked for (and perhaps larger). Parameters: Context - Pointer to an object context block. NewSize - The number of bytes to allocate for the new buffer. This value can not be less than the number of bytes currently in use. Return Values: STATUS_SUCCESS - The attributes are in-memory. STATUS_NO_MEMORY - Memory could not be allocated to retrieve the attributes. Other values as may be returned by registry API trying to retrieve the attributes from backing store. --*/ { PUCHAR OldBuffer; ULONG AllocationSize; #if DBG if ( Context->VariableValid ) { ASSERT(NewSize >= Context->OnDiskUsed); } #endif // // Is an allocation necessary? // if (NewSize <= Context->OnDiskAllocated) { return(STATUS_SUCCESS); } OldBuffer = Context->OnDisk; // // Pad the extend to allow for future edits efficiently. // AllocationSize = SampDwordAlignUlong(NewSize + SAMP_MINIMUM_ATTRIBUTE_PAD); Context->OnDisk = RtlAllocateHeap( RtlProcessHeap(), 0, AllocationSize ); if (Context->OnDisk == NULL) { Context->OnDisk = OldBuffer; return(STATUS_NO_MEMORY); } // // Set the new allocated size Context->OnDiskAllocated = AllocationSize; // // If there was no buffer originally, then zero the new buffer, mark // it as being invalid, and return. // if (OldBuffer == NULL) { RtlZeroMemory( (PVOID)Context->OnDisk, AllocationSize ); Context->FixedDirty = TRUE; Context->VariableDirty = TRUE; Context->FixedValid = FALSE; Context->VariableValid = FALSE; return(STATUS_SUCCESS); } // // Set the free size. Note that this information is only set if // the variable data is valid. // Used size remains the same. // if (Context->VariableValid == TRUE) { Context->OnDiskFree = AllocationSize - Context->OnDiskUsed; ASSERT(Context->OnDiskUsed == SampDwordAlignUlong(Context->OnDiskUsed)); } // // There was an old buffer (or else we would have exited earlier). // If any data in it was valid, copy it to the new buffer. Free the // old buffer. // if ( Context->FixedValid ) { RtlCopyMemory( Context->OnDisk, OldBuffer, SampFixedBufferLength( Context ) + SampFixedBufferOffset( Context ) ); } // // Note: in thise case we may copy the fixed data twice, since if the // variable data is not stored separately then SampVariableBufferOffset // is zero. // if ( Context->VariableValid ) { RtlCopyMemory( SampVariableBufferAddress( Context ), OldBuffer + SampVariableBufferOffset( Context ), Context->OnDiskUsed - SampVariableBufferOffset( Context ) ); } RtlFreeHeap( RtlProcessHeap(), 0, OldBuffer ); return(STATUS_SUCCESS); } NTSTATUS SampReadRegistryAttribute( IN HANDLE Key, IN PUCHAR Buffer, IN ULONG BufferLength, IN PUNICODE_STRING AttributeName, OUT PULONG RequiredLength ) /*++ Retrieve the address of a variable-length attribute. The attributes are assumed to already be in-memory. Parameters: Key - Handle to the key whose attribute is to be read. Buffer - Pointer to the buffer to receive the information. BufferLength - Length of the buffer receiving the information. AttributeName - The name of the attribute. Return Values: STATUS_SUCCESS - Successful completion. STATUS_BUFFER_TOO_SMALL - The data could not be read because the buffer was too small. Other values as may be returned by registry API trying to retrieve the attribute from backing store. --*/ { NTSTATUS NtStatus; // // Try to read the attribute // NtStatus = NtQueryValueKey( Key, AttributeName, //ValueName, KeyValuePartialInformation, //KeyValueInformationClass (PVOID)Buffer, BufferLength, RequiredLength ); return(NtStatus); } NTSTATUS SampSetVariableAttribute( IN PSAMP_OBJECT Context, IN ULONG AttributeIndex, IN ULONG Qualifier, IN PUCHAR Buffer, IN ULONG Length ) /*++ This API is used to set a new attribute value. The new attribute value may be longer, shorter, or the same size as the current attribute. The data in the attribute buffer will be shifted to make room for a larger attribute value or to fill in room left by a smaller attribute value. PERFORMANCE CONCERN: If you have a lot of attributes to set, it is worthwhile to start with the smallest indexed attribute and work up to the largest indexed attribute. Parameters: Context - Pointer to an object context block. AttributeIndex - Indicates the index (into the variable length attribute array) of the attribute to be set. Typically, all attributes beyond this one will have their data shifted. Buffer - The address of the buffer containing the new attribute value. May be NULL if Length is zero. Length - The length (in bytes) of the new attribute value. Return Values: STATUS_SUCCESS - The service completed successfully. STATUS_NO_MEMORY - Memory to expand the attribute buffer could not be allocated. --*/ { NTSTATUS NtStatus; ULONG OriginalAttributeLength, AdditionalSpaceNeeded, NewBufferLength, MaximumAttributeIndex, MoveLength, i; LONG OffsetDelta; PSAMP_VARIABLE_LENGTH_ATTRIBUTE AttributeArray; PUCHAR NewStart, OriginalStart; // // Make sure the requested attribute exists for the specified // object type. // SampValidateAttributeIndex( Context, AttributeIndex ); // // Make the data valid // NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } // // Allocate a new buffer if necessary // OriginalAttributeLength = SampObjectAttributeLength(Context, AttributeIndex); if (OriginalAttributeLength < Length) { AdditionalSpaceNeeded = Length - OriginalAttributeLength; if (Context->OnDiskFree < AdditionalSpaceNeeded) { NewBufferLength = Context->OnDiskUsed + AdditionalSpaceNeeded; ASSERT(NewBufferLength > Context->OnDiskAllocated); NtStatus = SampExtendAttributeBuffer( Context, NewBufferLength ); if (!NT_SUCCESS(NtStatus)) { return(NtStatus); } } } // // Get the address of the attribute array. // AttributeArray = SampVariableArrayAddress( Context ); // // Now shift following attribute values // OffsetDelta = (LONG)(SampDwordAlignUlong(Length) - SampDwordAlignUlong(OriginalAttributeLength)); MaximumAttributeIndex = SampVariableAttributeCount( Context ); if ((OffsetDelta != 0) && (AttributeIndex+1 < MaximumAttributeIndex)) { // // Shift all attributes above this one up or down by the OffsetDelta // MoveLength = Context->OnDiskUsed - ( SampVariableDataOffset( Context ) + AttributeArray[AttributeIndex+1].Offset ); // // Shift the data (if there is any) // if (MoveLength != 0) { OriginalStart = SampObjectAttributeAddress( Context, AttributeIndex+1); NewStart = (PUCHAR)(OriginalStart + OffsetDelta); RtlMoveMemory( NewStart, OriginalStart, MoveLength ); } // // Adjust the offset pointers // for ( i=AttributeIndex+1; iOnDiskUsed += OffsetDelta; Context->OnDiskFree -= OffsetDelta; ASSERT(Context->OnDiskFree == Context->OnDiskAllocated - Context->OnDiskUsed); // // Mark the variable attributes dirty // Context->VariableDirty = TRUE; #ifdef SAM_DEBUG_ATTRIBUTES if (SampDebugAttributes) { DbgPrint("SampSetVariableAttribute %d to length %#x, qualifier %#x:\n", AttributeIndex, Length, Qualifier); SampDumpAttributes(Context); } #endif return(STATUS_SUCCESS); } VOID SampFreeAttributeBuffer( IN PSAMP_OBJECT Context ) /*++ Free the buffer used to keep in-memory copies of the on-disk object attributes. Parameters: Context - Pointer to the object context whose buffer is to be freed. Return Values: None. --*/ { #if DBG if ( Context->FixedValid ) { ASSERT(Context->FixedDirty == FALSE); } if ( Context->VariableValid) { ASSERT(Context->VariableDirty == FALSE); } ASSERT(Context->OnDisk != NULL); ASSERT(Context->OnDiskAllocated != 0); #endif RtlFreeHeap( RtlProcessHeap(), 0, Context->OnDisk ); Context->OnDisk = NULL; Context->OnDiskAllocated = 0; // // Mark all attributes as invalid // Context->FixedValid = FALSE; Context->VariableValid = FALSE; return; } #ifdef SAM_DEBUG_ATTRIBUTES VOID SampDumpAttributes( IN PSAMP_OBJECT Context ) /*++ This is a debug-only API to dump out the attributes for a context to the kernel debugger. Parameters: Context - Pointer to an object context block. Return Values: None. --*/ { ULONG Index; PSAMP_OBJECT_INFORMATION ObjectTypeInfo = &SampObjectInformation[Context->ObjectType]; PSAMP_VARIABLE_LENGTH_ATTRIBUTE AttributeArray; AttributeArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE) SampVariableArrayAddress( Context ); DbgPrint("Dumping context 0x%x\n", Context); DbgPrint("\n"); DbgPrint("TYPE INFO\n"); DbgPrint("Object type name = %wZ\n", &ObjectTypeInfo->ObjectTypeName); DbgPrint("Fixed stored separately = %s\n", ObjectTypeInfo->FixedStoredSeparately ? "TRUE" : "FALSE"); DbgPrint("Fixed attributes offset = %#x\n", ObjectTypeInfo->FixedAttributesOffset); DbgPrint("Fixed attributes size = %#x\n", ObjectTypeInfo->FixedLengthSize); DbgPrint("Variable buffer offset = %#x\n", ObjectTypeInfo->VariableBufferOffset); DbgPrint("Variable array offset = %#x\n", ObjectTypeInfo->VariableArrayOffset); DbgPrint("Variable data offset = %#x\n", ObjectTypeInfo->VariableDataOffset); DbgPrint("Variable attribute count = %d\n", ObjectTypeInfo->VariableAttributeCount); DbgPrint("\n"); DbgPrint("INSTANCE INFO\n"); DbgPrint("RootName = %wZ\n", &Context->RootName); DbgPrint("Fixed Valid = %s\n", Context->FixedValid ? "TRUE" : "FALSE"); DbgPrint("Variable Valid = %s\n", Context->VariableValid ? "TRUE" : "FALSE"); DbgPrint("Fixed Dirty = %s\n", Context->FixedDirty ? "TRUE" : "FALSE"); DbgPrint("Variable Dirty = %s\n", Context->VariableDirty ? "TRUE" : "FALSE"); DbgPrint("OnDiskAllocated = %#x\n", Context->OnDiskAllocated); DbgPrint("OnDiskUsed = %#x\n", Context->OnDiskUsed); DbgPrint("OnDiskFree = %#x\n", Context->OnDiskFree); DbgPrint("\n"); if ( Context->VariableValid ) { for (Index = 0; Index < ObjectTypeInfo->VariableAttributeCount; Index ++) { DbgPrint("Attr %d: Qualifier = %#6x, Offset = %#6x, Length = %#6x\n", Index, AttributeArray[Index].Qualifier, AttributeArray[Index].Offset, AttributeArray[Index].Length ); SampDumpData(SampObjectAttributeAddress(Context, Index), SampObjectAttributeLength(Context, Index)); } } DbgPrint("\n\n"); } VOID SampDumpData( IN PVOID Buffer, IN ULONG Length ) /*++ This is a debug-only API to dump out a buffer in hex Parameters: Buffer - Pointer to data Length - number of bytes in data Return Values: None. --*/ { ULONG Index; for (Index = 0; Index < Length; Index ++) { ULONG Value = (ULONG)(((PBYTE)Buffer)[Index]); if ((Index % 16) == 0) { DbgPrint("\n "); } DbgPrint("%02x ", Value & 0xff); } if (Length > 0) { DbgPrint("\n\n"); } } #endif