/*++ 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: ChrisMay 04-Jun-96 Added routines for DS data manipulation. ChrisMay 10-Jun-96 Rewrote SampStoreObjectAttributes to branch to either the registry or DS backing store, based on the value of Context->ObjectFlags. Note that when a context object is created, this member is set to indicate registry storage by default. ChrisMay 18-Jun-96 Set FlushVariable flag correctly in SampStoreDsObjectAttributes. Add- ed routines to validate DS data by making SampValidateAttributes a wrapper for SampValidateRegAttributes and SampValidateDsAttributes. Moved SAMP_FIXED/VARIABLE_ATTRIBUTES into dsutilp.h. ChrisMay 25-Jun-96 Added code to SampValidateDsAttributes to update the SAM context OnDisk member if the attributes are invalid. Added code to handle initial case when OnDisk is NULL (new context). ChrisMay 26-Jun-96 Added code to update the buffer lengths and offsets in the SAMP_- OBJECT and SAMP_OBJECT_INFORMATION structures after the attribute buffer (Context.OnDisk) has been updated during SampDsValidateAttri- butes. ChrisMay 28-Jun-96 Finished separating the attribute accessor macros to handle both the registry and DS versions of the attribute buffers. ChrisMay 02-Jul-96 Corrected attribute-address computation in SampObjectAttributeAddress for DS attributes. Corrected attribute-offset computation in Samp- VariableAttributeOffset for DS attributes. ChrisMay 19-Jul-96 Corrected buffer-length computation in SampDsUpdateContextFixed- Attributes. --*/ /* 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 #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 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 debugging display routine is enabled when ATTR_DBG_PRINTF = 1. #define ATTR_DBG_PRINTF 0 #if (ATTR_DBG_PRINTF == 1) #define DebugPrint printf #else #define DebugPrint #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. // #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 SampRegFixedBufferOffset( c ) \ ( \ SampObjectInformation[(c)->ObjectType].FixedAttributesOffset \ ) #define SampDsFixedBufferOffset( c ) \ ( \ SampObjectInformation[(c)->ObjectType].FixedDsAttributesOffset \ ) #define SampFixedBufferOffset( c ) \ ( \ (IsDsObject(c)) ? \ SampDsFixedBufferOffset(c) : SampRegFixedBufferOffset(c) \ ) #define SampRegVariableBufferOffset( c ) \ ( \ SampObjectInformation[(c)->ObjectType].VariableBufferOffset \ ) #define SampDsVariableBufferOffset( c ) \ ( \ SampObjectInformation[(c)->ObjectType].VariableDsBufferOffset \ ) #define SampVariableBufferOffset( c ) \ ( \ (IsDsObject(c)) ? \ SampDsVariableBufferOffset(c) : SampRegVariableBufferOffset(c) \ ) // // 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 SampRegFixedBufferAddress( c ) \ ( \ ((PUCHAR)((c)->OnDisk)) + SampFixedBufferOffset( c ) \ ) #define SampDsFixedBufferAddress( c ) \ ( \ ((PUCHAR)((c)->OnDisk)) + SampDsFixedBufferOffset( c ) \ ) #define SampFixedBufferAddress( c ) \ ( \ (IsDsObject(c)) ? \ SampDsFixedBufferAddress(c): SampRegFixedBufferAddress(c) \ ) #define SampRegVariableBufferAddress( c ) \ ( \ ((PUCHAR)((c)->OnDisk)) + SampVariableBufferOffset( c ) \ ) #define SampDsVariableBufferAddress( c ) \ ( \ ((PUCHAR)((c)->OnDisk)) + SampDsVariableBufferOffset( c ) \ ) #define SampVariableBufferAddress( c ) \ ( \ (IsDsObject(c)) ? \ SampDsVariableBufferAddress(c) : SampRegVariableBufferAddress(c)\ ) // // Get the offset of the beginning of the variable-length // attributes discriptors array. This address is dword-aligned. // #define SampRegVariableArrayOffset( c ) \ ( \ SampObjectInformation[(c)->ObjectType].VariableArrayOffset \ ) #define SampDsVariableArrayOffset( c ) \ ( \ SampObjectInformation[(c)->ObjectType].VariableDsArrayOffset \ ) #define SampVariableArrayOffset( c ) \ ( \ (IsDsObject(c)) ? \ SampDsVariableArrayOffset(c) : SampRegVariableArrayOffset(c) \ ) // // Calculate the address of the beginning of the variable-length // attributes array. // #define SampRegVariableArrayAddress( c ) \ ( \ (PSAMP_VARIABLE_LENGTH_ATTRIBUTE)((PUCHAR)((c)->OnDisk) + \ SampVariableArrayOffset( c ) ) \ ) #define SampDsVariableArrayAddress( c ) \ ( \ (PSAMP_VARIABLE_LENGTH_ATTRIBUTE)((PUCHAR)((c)->OnDisk) + \ SampDsVariableArrayOffset( c ) ) \ ) #define SampVariableArrayAddress( c ) \ ( \ (IsDsObject(c)) ? \ SampDsVariableArrayAddress(c) : SampRegVariableArrayAddress(c) \ ) // // Get the offset of the beginning of the variable-length // attributes data. // #define SampRegVariableDataOffset( c ) \ ( \ SampObjectInformation[(c)->ObjectType].VariableDataOffset \ ) #define SampDsVariableDataOffset( c ) \ ( \ SampObjectInformation[(c)->ObjectType].VariableDsDataOffset \ ) #define SampVariableDataOffset( c ) \ ( \ (IsDsObject(c)) ? \ SampDsVariableDataOffset(c) : SampRegVariableDataOffset(c) \ ) // // 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 SampRegFixedBufferLength( c ) \ ( \ SampObjectInformation[(c)->ObjectType].FixedLengthSize \ ) #define SampDsFixedBufferLength( c ) \ ( \ SampObjectInformation[(c)->ObjectType].FixedDsLengthSize \ ) #define SampFixedBufferLength( c ) \ ( \ (IsDsObject(c)) ? \ SampDsFixedBufferLength(c) : SampRegFixedBufferLength(c) \ ) #define SampRegVariableBufferLength( c ) \ ( \ (c)->OnDiskAllocated - SampVariableBufferOffset( c ) \ ) #define SampDsVariableBufferLength( c ) \ ( \ (c)->OnDiskAllocated - SampDsVariableBufferOffset( c ) \ ) #define SampVariableBufferLength( c ) \ ( \ (IsDsObject(c)) ? \ SampDsVariableBufferLength(c) : SampRegVariableBufferLength(c) \ ) // // Return the address of a Qualifier field within the variable-length // attribute descriptor array. // #define SampRegVariableQualifier( c, i ) \ ( \ SampVariableArrayAddress( c ) + \ (sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE) * i) \ + FIELD_OFFSET(SAMP_VARIABLE_LENGTH_ATTRIBUTE, Qualifier) \ ) #define SampDsVariableQualifier( c, i ) \ ( \ SampDsVariableArrayAddress( c ) + \ (sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE) * i) \ + FIELD_OFFSET(SAMP_VARIABLE_LENGTH_ATTRIBUTE, Qualifier) \ ) #define SampVariableQualifier( c, i ) \ ( \ (IsDsObject(c)) ? \ SampDsVariableQualifier(c, i) : SampRegVariableQualifier(c, i) \ ) // // Return the address of the first byte of free space // in an object's attribute data buffer. // This will be dword aligned. // #define SampRegFirstFreeVariableAddress( c ) \ (PUCHAR)(((PUCHAR)((c)->OnDisk)) + (c)->OnDiskUsed) #define SampDsFirstFreeVariableAddress( c ) \ (PUCHAR)(((PUCHAR)((c)->OnDisk)) + (c)->OnDiskUsed) #define SampFirstFreeVariableAddress( c ) \ ( \ (IsDsObject(c)) ? \ SampDsFirstFreeVariableAddress(c) : \ SampRegFirstFreeVariableAddress(c) \ ) // // Get the number of bytes needed to store the entire variable-length // attribute information on disk. // #define SampRegVariableBufferUsedLength( c ) \ ( \ (PUCHAR)SampFirstFreeVariableAddress(c) - \ (PUCHAR)SampVariableArrayAddress(c) \ ) #define SampDsVariableBufferUsedLength( c ) \ ( \ (PUCHAR)SampDsFirstFreeVariableAddress(c) - \ (PUCHAR)SampDsVariableArrayAddress(c) \ ) #define SampVariableBufferUsedLength( c ) \ ( \ (IsDsObject(c)) ? \ SampDsVariableBufferUsedLength(c): \ SampRegVariableBufferUsedLength(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 SampInitDsObjectInfoAttributes( ) /*++ Routine Description: This routine initializes the offset and length information fields of the SAM_OBJECT_INFORMATION structure for DS attributes. This structure con- tains offset and length information for two sets of attributes: -Those attributes stored in the registry (workstation account info) -Those attributes stored in the DS (domain or DC account info) The former set are initialized by SampInitObjectInfoAttriubtes, while this routine inializes the latter set of information. Regardless of whether the attributes are persistently stored in the registry or the DS, their in- memory representation always uses the SAM fixed-length and variable-length data buffers. Note that DS data buffers do not contain the KEY_VALUE_PARTIAL_INFORMATION data because this is registry-specific, hence unnecessary for the DS-based attributes. Parameters: None. Return Values: None. --*/ { PSAMP_OBJECT_INFORMATION Object; SAMTRACE("SampInitDsObjectInfoAttributes"); // // SERVER object attribute information // Object = &SampObjectInformation[SampServerObjectType]; // Object->FixedStoredSeparately = SAMP_SERVER_STORED_SEPARATELY; Object->FixedDsAttributesOffset = 0; Object->FixedDsLengthSize = sizeof(SAMP_V1_FIXED_LENGTH_SERVER); #if SAMP_SERVER_STORED_SEPARATELY Object->VariableDsBufferOffset = Object->FixedDsAttributesOffset + SampDwordAlignUlong(Object->FixedDsLengthSize); Object->VariableDsArrayOffset = Object->VariableDsBufferOffset + 0; #else Object->VariableDsBufferOffset = 0; Object->VariableDsArrayOffset = Object->FixedDsAttributesOffset + SampDwordAlignUlong(Object->FixedDsLengthSize); #endif //SAMP_SERVER_STORED_SEPARATELY // Object->VariableAttributeCount = SAMP_SERVER_VARIABLE_ATTRIBUTES; Object->VariableDsDataOffset = SampDwordAlignUlong( Object->VariableDsArrayOffset + (SAMP_SERVER_VARIABLE_ATTRIBUTES * sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) ); // // DOMAIN object attribute information // Object = &SampObjectInformation[SampDomainObjectType]; // Object->FixedStoredSeparately = SAMP_DOMAIN_STORED_SEPARATELY; Object->FixedDsAttributesOffset = 0; Object->FixedDsLengthSize = sizeof(SAMP_V1_0A_FIXED_LENGTH_DOMAIN); #if SAMP_DOMAIN_STORED_SEPARATELY Object->VariableDsBufferOffset = Object->FixedDsAttributesOffset + SampDwordAlignUlong(Object->FixedDsLengthSize); Object->VariableDsArrayOffset = Object->VariableDsBufferOffset + 0; #else Object->VariableDsBufferOffset = 0; Object->VariableDsArrayOffset = Object->FixedDsAttributesOffset + SampDwordAlignUlong(Object->FixedDsLengthSize); #endif //SAMP_DOMAIN_STORED_SEPARATELY // Object->VariableAttributeCount = SAMP_DOMAIN_VARIABLE_ATTRIBUTES; Object->VariableDsDataOffset = SampDwordAlignUlong( Object->VariableDsArrayOffset + (SAMP_DOMAIN_VARIABLE_ATTRIBUTES * sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) ); // // USER object attribute information // Object = &SampObjectInformation[SampUserObjectType]; // Object->FixedStoredSeparately = SAMP_USER_STORED_SEPARATELY; Object->FixedDsAttributesOffset = 0; Object->FixedDsLengthSize = sizeof(SAMP_V1_0A_FIXED_LENGTH_USER); #if SAMP_USER_STORED_SEPARATELY Object->VariableDsBufferOffset = Object->FixedDsAttributesOffset + SampDwordAlignUlong(Object->FixedDsLengthSize); Object->VariableDsArrayOffset = Object->VariableDsBufferOffset + 0; #else Object->VariableDsBufferOffset = 0; Object->VariableDsArrayOffset = Object->FixedDsAttributesOffset + SampDwordAlignUlong(Object->FixedDsLengthSize); #endif //SAMP_USER_STORED_SEPARATELY // Object->VariableAttributeCount = SAMP_USER_VARIABLE_ATTRIBUTES; Object->VariableDsDataOffset = SampDwordAlignUlong( Object->VariableDsArrayOffset + (SAMP_USER_VARIABLE_ATTRIBUTES * sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) ); // // GROUP object attribute information // Object = &SampObjectInformation[SampGroupObjectType]; // Object->FixedStoredSeparately = SAMP_GROUP_STORED_SEPARATELY; Object->FixedDsAttributesOffset = 0; Object->FixedDsLengthSize = sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP); #if SAMP_GROUP_STORED_SEPARATELY Object->VariableDsBufferOffset = Object->FixedDsAttributesOffset + SampDwordAlignUlong(Object->FixedDsLengthSize); Object->VariableDsArrayOffset = Object->VariableDsBufferOffset + 0; #else Object->VariableDsBufferOffset = 0; Object->VariableDsArrayOffset = Object->FixedDsAttributesOffset + SampDwordAlignUlong(Object->FixedDsLengthSize); #endif //SAMP_GROUP_STORED_SEPARATELY // Object->VariableAttributeCount = SAMP_GROUP_VARIABLE_ATTRIBUTES; Object->VariableDsDataOffset = SampDwordAlignUlong( Object->VariableDsArrayOffset + (SAMP_GROUP_VARIABLE_ATTRIBUTES * sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) ); // // ALIAS object attribute information // Object = &SampObjectInformation[SampAliasObjectType]; // Object->FixedStoredSeparately = SAMP_ALIAS_STORED_SEPARATELY; Object->FixedDsAttributesOffset = 0; Object->FixedDsLengthSize = sizeof(SAMP_V1_FIXED_LENGTH_ALIAS); #if SAMP_ALIAS_STORED_SEPARATELY Object->VariableDsBufferOffset = Object->FixedDsAttributesOffset + SampDwordAlignUlong(Object->FixedDsLengthSize); Object->VariableDsArrayOffset = Object->VariableDsBufferOffset + 0; #else Object->VariableDsBufferOffset = 0; Object->VariableDsArrayOffset = Object->FixedDsAttributesOffset + SampDwordAlignUlong(Object->FixedDsLengthSize); #endif //SAMP_ALIAS_STORED_SEPARATELY // Object->VariableAttributeCount = SAMP_ALIAS_VARIABLE_ATTRIBUTES; Object->VariableDsDataOffset = SampDwordAlignUlong( Object->VariableDsArrayOffset + (SAMP_ALIAS_VARIABLE_ATTRIBUTES * sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) ); return; } 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; SAMTRACE("SampInitObjectInfoAttributes"); // // 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)) ); // Initialize the DS-specific buffer offsets and lengths. SampInitDsObjectInfoAttributes(); return; } NTSTATUS SampStoreDsObjectAttributes( IN PSAMP_OBJECT Context ) /*++ Routine Description: This routine does the work of writing the SAM attributes out to the DS backing store. Determination is made as to whether the fixed, or vari- able, or both sets of attributes are dirty and valid. If so, then they are updated in the backing store. The SAM attributes are first converted into a DSATTRBLOCK so that they can be written to storage. The dirty flags are updated accordingly. Arguments: Context - Pointer, the object's SAM context. Return Value: STATUS_SUCCESS - storage was updated without a problem, otherwise an error code is returned. --*/ { NTSTATUS NtStatus = STATUS_SUCCESS; BOOLEAN FlushFixed = FALSE; BOOLEAN FlushVariable = FALSE; INT ObjectType = Context->ObjectType; ULONG Flags = 0; PDSNAME DsObjectName = Context->ObjectNameInDs; PDSATTRBLOCK AttributeBlock = NULL; SAMTRACE("SampStoreDsObjectAttributes"); // Determine which attributes (fixed or variable) need to be written to // storage. if (Context->FixedValid && Context->FixedDirty) { FlushFixed = TRUE; } if (Context->VariableValid && Context->VariableDirty) { FlushVariable = TRUE; } if (NULL != (Context->OnDisk)) { // Determine whether the attributes are stored separately or combined. if (TRUE == SampObjectInformation[ObjectType].FixedStoredSeparately) { if (TRUE == FlushFixed) { // Get a pointer to the start of the fixed-length attributes // (note that this should always be the same address as the // Context->OnDisk address for DS-based attributes) and con- // them into a DSATTRBLOCK. NtStatus = SampConvertFixedLengthAttributesToAttrBlock( ObjectType, SampDsFixedBufferAddress(Context), &AttributeBlock); if (NT_SUCCESS(NtStatus)) { if (NULL != AttributeBlock) { // Write the fixed-length attributes to storage. NtStatus = SampDsSetAttributes(DsObjectName, Flags, ObjectType, AttributeBlock); if (NT_SUCCESS(NtStatus)) { Context->FixedDirty = FALSE; } } else { NtStatus = STATUS_INTERNAL_ERROR; } } } if (NT_SUCCESS(NtStatus) && (TRUE == FlushVariable)) { // Get a pointer to the variable-length attributes and convert // them into a DSATTRBLOCK. NtStatus = SampConvertVarLengthAttributesToAttrBlock( ObjectType, SampDsVariableArrayAddress(Context), &AttributeBlock); if (NT_SUCCESS(NtStatus)) { if (NULL != AttributeBlock) { NtStatus = SampDsSetAttributes(DsObjectName, Flags, ObjectType, AttributeBlock); if (NT_SUCCESS(NtStatus)) { Context->VariableDirty = FALSE; } } else { NtStatus = STATUS_INTERNAL_ERROR; } } } } else { // The attributes are combined, check the flush status. if ((TRUE == FlushFixed) || (TRUE == FlushVariable)) { // Get a pointer to the combined (i.e. fixed and variable- // length) attributes and convert them into a DSATTRBLOCK. // BUG: Where is OnDiskAllocated, OnDiskUsed, etc. set? NtStatus = SampConvertCombinedAttributesToAttrBlock( ObjectType, Context->OnDisk, SampDsFixedBufferLength(Context), SampDsVariableBufferLength(Context), &AttributeBlock); if (NT_SUCCESS(NtStatus)) { if (NULL != AttributeBlock) { NtStatus = SampDsSetAttributes(DsObjectName, Flags, ObjectType, AttributeBlock); if (NT_SUCCESS(NtStatus)) { Context->FixedDirty = FALSE; Context->VariableDirty = FALSE; } } else { NtStatus = STATUS_INTERNAL_ERROR; } } } } } return(NtStatus); } NTSTATUS SampStoreRegObjectAttributes( 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; SAMTRACE("SampStoreRegObjectAttributes"); // // 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 // SampDumpRXact(SampRXactContext, RtlRXactOperationSetValue, &(Context->RootName), RootKey, &SampCombinedAttributeName, REG_BINARY, SampFixedBufferAddress(Context), Context->OnDiskUsed - SampFixedBufferOffset(Context), FixedBufferAddressFlag); 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) { SampDumpRXact(SampRXactContext, RtlRXactOperationSetValue, &(Context->RootName), RootKey, &SampFixedAttributeName, REG_BINARY, SampFixedBufferAddress(Context), SampVariableBufferOffset(Context) - SampFixedBufferOffset(Context), FixedBufferAddressFlag); 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) { SampDumpRXact(SampRXactContext, RtlRXactOperationSetValue, &(Context->RootName), RootKey, &SampVariableAttributeName, REG_BINARY, (PUCHAR)SampVariableArrayAddress(Context), SampVariableBufferUsedLength(Context), VARIABLE_LENGTH_ATTRIBUTE_FLAG); 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 SampStoreObjectAttributes( IN PSAMP_OBJECT Context, IN BOOLEAN UseKeyHandle ) /*++ Routine Description: This routine determines from the object context whether to update object attributes residing in the registry or in the DS backing store, and then calls the appropriate routine to do the work. Arguments: Context - Pointer, the object's SAM context. UseKeyHandle - Flag indicating that the registry key handle should be used (if this is a registry update--it is not used in DS updates). Return Value: STATUS_SUCCESS - storage was updated without a problem, otherwise an error code is returned. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; SAMTRACE("SampStoreObjectAttributes"); if (NULL != Context) { if (IsDsObject(Context)) { NtStatus = SampStoreDsObjectAttributes(Context); } else { NtStatus = SampStoreRegObjectAttributes(Context, UseKeyHandle); } } else { NtStatus = STATUS_INVALID_PARAMETER; } 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; SAMTRACE("SampDeleteAttributeKeys"); 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; SAMTRACE("SampGetFixedAttributes"); // // 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; SAMTRACE("SampSetFixedAttributes"); // // 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; SAMTRACE("SampGetUnicodeStringAttribute"); // // 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; SAMTRACE("SampSetUnicodeStringAttribute"); // // 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; SAMTRACE("SampGetSidAttribute"); // // 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; SAMTRACE("SampSetSidAttribute"); // // 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; SAMTRACE("SampGetAccessAttribute"); // // 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; SAMTRACE("SampSetAccessAttribute"); // // 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; SAMTRACE("SampGetUlongArrayAttribute"); // // 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; SAMTRACE("SampSetUlongArrayAttribute"); 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; SAMTRACE("SampGetLargeIntArrayAttribute"); // // 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; SAMTRACE("SampSetLargeIntArrayAttribute"); // // 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; SAMTRACE("SampGetSidArrayAttribute"); // // 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; SAMTRACE("SampSetSidArrayAttribute"); // // 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; SAMTRACE("SampGetLogonHoursAttribute"); // // 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; SAMTRACE("SampSetLogonHoursAttribute"); // // 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 // // // /////////////////////////////////////////////////////////////////////////////// BOOLEAN SampDsIsAlreadyValidData( IN PSAMP_OBJECT Context, IN ULONG AttributeGroup ) /*++ Description: This routine determines whether or not the attributes are in memory or not (i.e. valid or not). OnDisk should only be NULL for a newly created context that has just been initialized, otherwise it is non-NULL. Parameters: Context - Pointer to an object context block. AttributeGroup - Flag, either SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE- ATTRIBUTES. Return Values: This routine returns a flag, TRUE if the attributes are in memory, FALSE otherwise. --*/ { BOOLEAN Flag = FALSE; SAMTRACE("SampDsIsAlreadyValidData"); if (NULL != Context->OnDisk) { if (AttributeGroup == SAMP_FIXED_ATTRIBUTES) { if (SampFixedAttributesValid(Context)) { Flag = TRUE; } } else { ASSERT(AttributeGroup == SAMP_VARIABLE_ATTRIBUTES); if (SampVariableAttributesValid(Context)) { Flag = TRUE; } } } return(Flag); } NTSTATUS SampDsPrepareAttrBlock( IN ULONG FixedCount, IN PSAMP_FIXED_ATTRIBUTE_TYPE_INFO FixedAttrIds[], IN ULONG VarCount, IN PSAMP_VAR_ATTRIBUTE_TYPE_INFO VarAttrIds[], OUT PDSATTRBLOCK AttrBlock ) /*++ Description: This routine sets up a DSATTRBLOCK in preparation to read the DS. An attribute block is created containaing the attribute identifiers for the attributes to read. Parameters: FixedCount - Number of fixed-length attributes. FixedAttrIds - Array of attribute IDs of the fixed-length attributes. VarCount - Number of variable-length attributes. VarAttrIds - Array of attribute IDs of the fixed-length attributes. AttrBlock - Pointer, the generated attribute block with IDs. Return Values: STATUS_SUCCESS if successful, error otherwise. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; ULONG i = 0; ULONG AttrCount = FixedCount + VarCount; PDSATTR Attributes = NULL; SAMTRACE("SampDsPrepareAttrBlock"); Attributes = RtlAllocateHeap(RtlProcessHeap(), 0, AttrCount * sizeof(DSATTR)); if (NULL != Attributes) { RtlZeroMemory(Attributes, (AttrCount * sizeof(DSATTR))); // This loop is set up to handle the case where both fixed-length // and variable-length attribute buffers are passed in (which may // be needed for combined attribute conversion). for (i = 0; i < AttrCount; i++) { if (i < FixedCount) { // Set the fixed-length attribute type/id. Attributes[i].attrTyp = FixedAttrIds[i].Type; } else { ASSERT(0 <= (i - FixedCount)); // Set the variable-length attribute type/id. Attributes[i].attrTyp = VarAttrIds[i - FixedCount].Type; } // The read operation does not require setting up valCount or // pAVal. Attributes[i].AttrVal.valCount = 0; Attributes[i].AttrVal.pAVal = NULL; } // Hook up the attributes to the top-level attrblock and return. AttrBlock->attrCount = AttrCount; AttrBlock->pAttr = Attributes; NtStatus = STATUS_SUCCESS; } else { NtStatus = STATUS_NO_MEMORY; } return(NtStatus); } NTSTATUS SampDsMakeAttrBlock( IN INT ObjectType, IN ULONG AttributeGroup, OUT PDSATTRBLOCK AttrBlock ) /*++ Description: This routine determines the object type and sets the count of fixed- length and variable-length attributes for the object. SampDsPrepare- AttrBlock to set up the DSATTRBLOCK. Parameters: ObjectType - SAM object ID. AttributeGroup - Flag, either SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE- ATTRIBUTES. AttrBlock - Pointer, generated attribute block. Return Values: STATUS_SUCCESS if no errors. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; ULONG FixedLenAttrCount = 0; ULONG VarLenAttrCount = 0; SAMTRACE("SampDsMakeAttrBlock"); switch(ObjectType) { case SampServerObjectType: FixedLenAttrCount = SAMP_SERVER_FIXED_ATTR_COUNT; VarLenAttrCount = SAMP_SERVER_VARIABLE_ATTRIBUTES; break; case SampDomainObjectType: FixedLenAttrCount = SAMP_DOMAIN_FIXED_ATTR_COUNT; VarLenAttrCount = SAMP_DOMAIN_VARIABLE_ATTRIBUTES; break; case SampGroupObjectType: FixedLenAttrCount = SAMP_GROUP_FIXED_ATTR_COUNT; VarLenAttrCount = SAMP_GROUP_VARIABLE_ATTRIBUTES; break; case SampAliasObjectType: FixedLenAttrCount = SAMP_ALIAS_FIXED_ATTR_COUNT; VarLenAttrCount = SAMP_ALIAS_VARIABLE_ATTRIBUTES; break; case SampUserObjectType: FixedLenAttrCount = SAMP_USER_FIXED_ATTR_COUNT; VarLenAttrCount = SAMP_USER_VARIABLE_ATTRIBUTES; break; default: ASSERT(FALSE && "Invalid SampObjectType"); break; } if ((0 < FixedLenAttrCount) && (0 < VarLenAttrCount)) { if (SAMP_FIXED_ATTRIBUTES == AttributeGroup) { NtStatus = SampDsPrepareAttrBlock( FixedLenAttrCount, &SampFixedAttributeInfo[ObjectType][0], 0, NULL, AttrBlock); } else { NtStatus = SampDsPrepareAttrBlock( 0, NULL, VarLenAttrCount, &SampVarAttributeInfo[ObjectType][0], AttrBlock); } } return(NtStatus); } NTSTATUS SampDsConvertReadAttrBlock( IN INT ObjectType, IN ULONG AttributeGroup, IN PDSATTRBLOCK AttrBlock, OUT PVOID *SamAttributes, OUT PULONG FixedLength, OUT PULONG VariableLength ) /*++ Description: This routine converts an attribute block (DSATTRBLOCK) into a SAM attri- bute buffer. This is used to convert the resultant attributes from a DS read into the SAM attribute format. Parameters: ObjectType - SAM object ID. AttributeGroup - Flag, either SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE- ATTRIBUTES. AttrBlock - Pointer, input attribute block. SamAttributes - Pointer, returned SAM attributes. FixedLength - Pointer, byte count of the fixed-length attributes size. VariableLength - Pointer, byte count of the variable-length attributes size. Return Values: STATUS_SUCCESS if no error. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; SAMTRACE("SampDsConvertReadData"); // Initialize the returned lengths and buffer. *FixedLength = 0; *VariableLength = 0; *SamAttributes = NULL; if (SAMP_FIXED_ATTRIBUTES == AttributeGroup) { NtStatus = SampConvertAttrBlockToFixedLengthAttributes( ObjectType, AttrBlock, SamAttributes, FixedLength); } else { NtStatus = SampConvertAttrBlockToVarLengthAttributes( ObjectType, AttrBlock, (SAMP_VARIABLE_LENGTH_ATTRIBUTE**)SamAttributes, VariableLength); } return(NtStatus); } NTSTATUS SampDsUpdateContextFixedAttributes( IN PSAMP_OBJECT Context, IN ULONG FixedLength, IN PVOID SamAttributes, IN BOOLEAN FirstTimeInitialization ) /*++ Description: This routine updates the SAM context fixed-length attributes if the size of the attributes have changed. For fixed-length attributes, this only occurs when a revision of the fixed-length data structures has caused a size change in the structures. Parameters: Context - Pointer to an object context block. FixedLength - Byte count of the fixed-length attributes. SamAttributes - Pointer, the SAM fixed-length attributes. FirstTimeInitialization - Flag, the first time that the attributes are set, the SAM global buffer lengths, addresses, and offsets do not all accurately reflect the actual memory allocated, so skip certain calculations when this flag is set. Return Values: STATUS_SUCCESS if no error. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; ULONG NewLength = 0; PBYTE Buffer = NULL; PBYTE VariableData = NULL; ULONG VariableLength = 0; SAMTRACE("SampDsUpdateContextFixedAttributes"); // The first time through, the variable-buffer length will be zero, so // the new length is only the fixed length. Note that OnDiskAllocated is // also zero (a global) so don't attempt to use the variable-buffer // offset in the calculation. if (FirstTimeInitialization) { NewLength = FixedLength; } else { ASSERT(0 < Context->OnDiskAllocated); NewLength = FixedLength + SampDsVariableBufferLength(Context); DebugPrint("OnDiskAllocated = %lu\n", Context->OnDiskAllocated); DebugPrint("VarBufOffset = %lu\n", SampDsVariableBufferOffset(Context)); DebugPrint("VarBufLenth = %lu\n", SampDsVariableBufferLength(Context)); } Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, NewLength); DebugPrint("FixedLength = %lu\n", FixedLength); DebugPrint("NewLength = %lu\n", NewLength); DebugPrint("Buffer Addr = 0x%lx\n", Buffer); if (NULL != Buffer) { // Zero the new buffer and copy the fixed-length attributes into it. RtlZeroMemory(Buffer, NewLength); RtlCopyMemory(Buffer, SamAttributes, FixedLength); if (NULL != Context->OnDisk) { // Save the current address and length of the variable data, if // it exists. The first time through, the variable data is NULL. // Release the old buffer. VariableData = SampDsVariableBufferAddress(Context); VariableLength = SampDsVariableBufferLength(Context); RtlCopyMemory(Buffer + FixedLength, VariableData, VariableLength); // Free the old OnDisk buffer. RtlFreeHeap(RtlProcessHeap(), 0, Context->OnDisk); } // Reset the context attribute buffer to the new buffer. Context->OnDisk = Buffer; // Release the memory for the input buffer RtlFreeHeap(RtlProcessHeap(), 0, SamAttributes); NtStatus = STATUS_SUCCESS; } else { NtStatus = STATUS_NO_MEMORY; } return(NtStatus); } NTSTATUS SampDsUpdateContextVariableAttributes( IN PSAMP_OBJECT Context, IN ULONG VariableLength, IN PVOID SamAttributes ) /*++ Description: This routine updates the SAM context variable-length attributes if the size of the attributes has changed. Unlike the fixed-length attributes, this will occur frequently due to fact that these attributes are vari- able length. Parameters: Context - Pointer to an object context block. FixedLength - Byte count of the fixed-length attributes. SamAttributes - Pointer, the SAM fixed-length attributes. Return Values: STATUS_SUCCESS if no error. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; ULONG NewLength = 0; PBYTE Buffer = NULL; PBYTE FixedData = NULL; ULONG FixedLength = 0; SAMTRACE("SampDsUpdateContextVariableAttributes"); // Get the current fixed-buffer length, add the new variable length, and // allocate the new buffer. NewLength = SampDsFixedBufferLength(Context) + VariableLength; Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, NewLength); if (NULL != Buffer) { RtlZeroMemory(Buffer, NewLength); if (NULL != Context->OnDisk) { // Get the fixed-length buffer address and length... FixedData = SampDsFixedBufferAddress(Context); FixedLength = SampDsFixedBufferLength(Context); if ((NULL != FixedData) && (0 < FixedLength)) { // Copy the fixed data into the new buffer and append the // variable-length data. RtlCopyMemory(Buffer, FixedData, FixedLength); RtlCopyMemory(Buffer + FixedLength, SamAttributes, VariableLength); // Release the old attribute buffer and reset OnDisk to // point at the new buffer. RtlFreeHeap(RtlProcessHeap(), 0, Context->OnDisk); Context->OnDisk = Buffer; // Release the temporary attribute buffer. RtlFreeHeap(RtlProcessHeap(), 0, SamAttributes); NtStatus = STATUS_SUCCESS; } } // Context->OnDisk should never be NULL in this routine. ASSERT(NULL != Context->OnDisk); } else { NtStatus = STATUS_NO_MEMORY; } return(NtStatus); } NTSTATUS SampUpdateOffsets( IN PSAMP_OBJECT Context, IN BOOLEAN FirstTimeInitialization ) /*++ Description: This routine updates the buffer (OnDisk) offset and length information that is stored in the object information (SAMP_OBJECT_INFORMATION) and in the instance information (SAMP_OBJECT), after the attributes have been successfully updated. Parameters: Context - Pointer to an object context block. AttributeGroup - Flag, either SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE- ATTRIBUTES. Return Values: STATUS_SUCCESS if no error. --*/ { NTSTATUS NtStatus = STATUS_SUCCESS; INT ObjectType = Context->ObjectType; ULONG FixedDataLength = 0; ULONG VariableArrayLength = 0; ULONG VariableDataOffset = 0; ULONG VariableDataLength = 0; ULONG TotalBufferLength = 0; PSAMP_VARIABLE_LENGTH_ATTRIBUTE VariableArray = NULL; ULONG i = 0; ULONG AttributeCount = 0; // Determine the SAM object type and compute the length of the fixed- // length attributes and the length of the variable-length attribute // array, which will be used as buffer offsets. SAMTRACE("SampUpdateOffsets"); switch(ObjectType) { case SampServerObjectType: FixedDataLength = sizeof(SAMP_V1_FIXED_LENGTH_SERVER); AttributeCount = SAMP_SERVER_VARIABLE_ATTRIBUTES; break; case SampDomainObjectType: FixedDataLength = sizeof(SAMP_V1_0A_FIXED_LENGTH_DOMAIN); AttributeCount = SAMP_DOMAIN_VARIABLE_ATTRIBUTES; break; case SampGroupObjectType: FixedDataLength = sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP); AttributeCount = SAMP_GROUP_VARIABLE_ATTRIBUTES; break; case SampAliasObjectType: FixedDataLength = sizeof(SAMP_V1_FIXED_LENGTH_ALIAS); AttributeCount = SAMP_ALIAS_VARIABLE_ATTRIBUTES; break; case SampUserObjectType: FixedDataLength = sizeof(SAMP_V1_0A_FIXED_LENGTH_USER); AttributeCount = SAMP_USER_VARIABLE_ATTRIBUTES; break; default: // Invalid object type specified. NtStatus = STATUS_INTERNAL_ERROR; break; } VariableArrayLength = (AttributeCount * sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)); if (NT_SUCCESS(NtStatus)) { // First, update the object information offsets and lengths. SampObjectInformation[ObjectType].FixedDsAttributesOffset = 0; SampObjectInformation[ObjectType].FixedDsLengthSize = FixedDataLength; SampObjectInformation[ObjectType].VariableDsBufferOffset = FixedDataLength; SampObjectInformation[ObjectType].VariableDsArrayOffset = FixedDataLength; SampObjectInformation[ObjectType].VariableDsDataOffset = FixedDataLength + VariableArrayLength; if (FALSE == FirstTimeInitialization) { // Get a pointer to the array of variable-length information and // total up the lengths of these attributes. VariableArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE) ((PBYTE)(Context->OnDisk) + FixedDataLength); for (i = 0; i < AttributeCount; i++) { VariableDataLength = VariableDataLength + VariableArray[i].Length; } } else { // The first time through, the attribute buffer only contains // the fixed-length attributes, so make sure the lengths for // the variable attributes are zero. VariableArrayLength = 0; VariableDataLength = 0; } TotalBufferLength = FixedDataLength + VariableArrayLength + VariableDataLength; } // Finally, update the instance information of the object's context. Context->OnDiskAllocated = TotalBufferLength; Context->OnDiskUsed = TotalBufferLength; // The DS routines do not allocate extra space at the end of the SAM // OnDisk buffer, hence OnDiskFree is always zero. // BUG: Should allocate extra OnDisk buffer free space for growth. Context->OnDiskFree = 0; DebugPrint("OnDiskAllocated = %lu\n", Context->OnDiskAllocated); DebugPrint("OnDiskUsed = %lu\n", Context->OnDiskUsed); DebugPrint("OnDiskFree = %lu\n", Context->OnDiskFree); return(NtStatus); } NTSTATUS SampDsUpdateContextAttributes( IN PSAMP_OBJECT Context, IN ULONG AttributeGroup, IN PVOID SamAttributes, IN ULONG FixedLength, IN ULONG VariableLength ) /*++ Description: This routine updates an object's attribute buffer (OnDisk) for the given context. If the new buffer size is the same as the old size, as in the case of modifications, then a simple memory copy is performed, otherwise helper routines are called to resize the buffer and copy the data. A sub- sequent helper routine is called to update the context buffer lengths and offsets. Parameters: Context - Pointer to an object context block. Return Values: STATUS_SUCCESS --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; INT ObjectType = Context->ObjectType; PBYTE FixedLengthData = NULL; PBYTE VariableLengthData = NULL; BOOLEAN FirstTimeInitialization = FALSE; SAMTRACE("SampDsUpdateContextAttributes"); // BUG: OnDisk may only be set to fixed or variable attributes. // The current version of SAM contains a hack that resets all attributes // whenever the variable-length ones are asked for. This allows both the // fixed and variable attributes to be set even if FixedStoredSeparately // is TRUE. This routine does not maintain that behavior, which may create // problems for SAM. If so, the caller of this routine will need to call // it twice, once for fixed and once for variable attributes in the cases // where FixedStoredSeparately is TRUE. if (SAMP_FIXED_ATTRIBUTES == AttributeGroup) { // Update the fixed-length attributes. The first time through, OnDisk // will be NULL. if ((NULL != Context->OnDisk) && (SampDsFixedBufferLength(Context) == FixedLength)) { // The fixed-length data is the same size (i.e. not // doing an upgrade), so copy the new attributes and // release the buffer. FixedLengthData = SampDsFixedBufferAddress(Context); RtlCopyMemory(FixedLengthData, (PBYTE)SamAttributes, FixedLength); RtlFreeHeap(RtlProcessHeap(), 0, SamAttributes); NtStatus = STATUS_SUCCESS; } else { if (NULL == Context->OnDisk) { // If OnDisk is NULL, then this is the first time that the // attribute buffer of the context has been set. FirstTimeInitialization = TRUE; } // The new attributes are not the same size as the old ones. NtStatus = SampDsUpdateContextFixedAttributes(Context, FixedLength, SamAttributes, FirstTimeInitialization); // Fixed-attribute size changes when the context attributes // have been set for the first time (i.e. changed from zero to // actual sizes), or when the fixed-length structures are // changed in an upgrade scenario. if (NT_SUCCESS(NtStatus)) { NtStatus = SampUpdateOffsets(Context, FirstTimeInitialization); } } if (NT_SUCCESS(NtStatus)) { Context->FixedValid = TRUE; } else { Context->FixedValid = FALSE; } } else { // Update the variable-length attributes. In the event that the // attributes are stored separately, OnDisk will be NULL the first // time through (for variable-length attributes). if ((NULL != Context->OnDisk) && (SampDsVariableBufferLength(Context) == VariableLength)) { // The variable-length data is the same size, so copy the new // attributes and release the buffer. VariableLengthData = SampDsVariableBufferAddress(Context); RtlCopyMemory(VariableLengthData, (PBYTE)SamAttributes, VariableLength); RtlFreeHeap(RtlProcessHeap(), 0, SamAttributes); NtStatus = STATUS_SUCCESS; } else { if (NULL == Context->OnDisk) { FirstTimeInitialization = TRUE; } // The new attributes are not the same size as the old // ones. NtStatus = SampDsUpdateContextVariableAttributes( Context, VariableLength, SamAttributes); // Variable-attribute size changes when the context attributes // have been set for the first time (i.e. changed from zero to // actual sizes), or when the variable-length data has changed // size. if (NT_SUCCESS(NtStatus)) { NtStatus = SampUpdateOffsets(Context, FirstTimeInitialization); } } if (NT_SUCCESS(NtStatus)) { Context->VariableValid = TRUE; } else { Context->VariableValid = FALSE; } } return(NtStatus); } NTSTATUS SampValidateDsAttributes( IN PSAMP_OBJECT Context, IN ULONG AttributeGroup ) /*++ Description: Ensure specified attributes are in-memory. If they are not, then read them from the DS backing store. This routine fetches all of the stored attributes for a given SAM object. To read a single attribute, or a subset of attributes, SampDsRead should be used to selectively fetch attributes. Context->OnDisk is updated with the new 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). 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. This routine returns a flag, TRUE if the attributes are in memory, FALSE otherwise. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; ULONG Flags = 0; INT ObjectType = -1; DSATTRBLOCK ReadAttrBlock; DSATTRBLOCK ResultAttrBlock; ULONG FixedLength = 0; ULONG VariableLength = 0; PVOID SamAttributes = NULL; SAMTRACE("SampValidateDsAttributes"); // The data might already be in memory, so check it out. if (FALSE == SampDsIsAlreadyValidData(Context, AttributeGroup)) { if (NULL != Context->ObjectNameInDs) { ObjectType = Context->ObjectType; RtlZeroMemory(&ReadAttrBlock, sizeof(DSATTRBLOCK)); RtlZeroMemory(&ResultAttrBlock, sizeof(DSATTRBLOCK)); // Construct the input ATTRBLOCK used to specify which attributes // should be read from the DS. NtStatus = SampDsMakeAttrBlock(ObjectType, AttributeGroup, &ReadAttrBlock); if ((NT_SUCCESS(NtStatus)) && (NULL != ReadAttrBlock.pAttr)) { // Read the attributes from the DS, flags is currently unused. NtStatus = SampDsRead(Context->ObjectNameInDs, Flags, ObjectType, &ReadAttrBlock, &ResultAttrBlock); if ((NT_SUCCESS(NtStatus)) && (NULL != ResultAttrBlock.pAttr)) { // Convert the ATTRBLOCK into the appropriate SAM attri- // butes, returning them in the SamAttributes buffer. Note // that the returned lengths are as follows: // // FixedLength - The byte count of the returned fixed- // length buffer // // VariableLength - The byte count of the returned var- // able-length buffer. NtStatus = SampDsConvertReadAttrBlock(ObjectType, AttributeGroup, &ResultAttrBlock, &SamAttributes, &FixedLength, &VariableLength); if ((NT_SUCCESS(NtStatus)) && (NULL != SamAttributes)) { NtStatus = SampDsUpdateContextAttributes( Context, AttributeGroup, SamAttributes, FixedLength, VariableLength); } } } } } else { NtStatus = STATUS_SUCCESS; } return(NtStatus); } NTSTATUS SampValidateRegAttributes( 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; SAMTRACE("SampValidateRegAttributes"); // // 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 SampValidateAttributes( IN PSAMP_OBJECT Context, IN ULONG AttributeGroup ) /*++ Routine Description: This routine determines from the object context whether to validate object attributes residing in the registry or in the DS backing store, and then calls the appropriate routine to do the work. Arguments: Context - Pointer, the object's SAM context. AttributeGroup - identifies which kind of attributes are being validated (SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE_ATTRIBUTES). Return Value: STATUS_SUCCESS - attributes were checked and read from storage if neces- sary without a problem, otherwise an error code is returned. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; SAMTRACE("SampValidateAttributes"); if (NULL != Context) { if (IsDsObject(Context)) { if (NULL != Context->OnDisk) { // The SAM object attributes have been set atleast once, so // update them if needed. NtStatus = SampValidateDsAttributes(Context, AttributeGroup); } else { // The SAM object attributes have never been set because this // is a new context. First set the fixed-length attributes and // then the variable-length ones. ASSERT(SAMP_FIXED_ATTRIBUTES == AttributeGroup); // If the OnDisk buffer is NULL, make sure that the fixed- // length attributes are loaded first. NtStatus = SampValidateDsAttributes( Context, SAMP_FIXED_ATTRIBUTES); if (NT_SUCCESS(NtStatus)) { // BUG: Not setting up var-length attrs during init time. // The NT3.5 - NT4.0 SAM attempts to set both the fixed- // length and variable-length attributes if the OnDisk // buffer is NULL (i.e. this is the first time that the // buffer is set). This may not be necessary in NT5 SAM, // so explicitly loading the variable attributes now is // skipped. They are lazily loaded into memory whenever // any variable-length attribute is asked for (see the // implementation of SampValidateAttributes). THIS MEANS // THAT THE VARIABLE-LENGTH ATTRIBUTES CAN ONLY BE LOADED // INTO MEMORY AFTER THE FIXED-LENGTH ONES BECAUSE THEY // ARE APPENDED ONTO THE END OF THE OnDisk BUFFER. // NtStatus = SampValidateDsAttributes( // Context, // SAMP_VARIABLE_ATTRIBUTES); } } } else { NtStatus = SampValidateRegAttributes(Context, AttributeGroup); } } else { NtStatus = STATUS_INVALID_PARAMETER; } 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; SAMTRACE("SampUpgradeToCurrentRevision"); // // 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. The NT3.51-4.0 SAM stores attribute offsets differently from the NT5 SAM. In the earlier versions (which exclusively used the registry as the backing store), the attribute offset value (in SAMP_VARIABLE_LENGTH_ATTRIBUTE) was self-relative to the end of the attribute array. The NT5 version is self-relative from the start of the array. 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; SAMTRACE("SampObjectAttributeAddress"); ASSERT(SampVariableAttributesValid(Context)); AttributeArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE) SampVariableArrayAddress(Context); if (IsDsObject(Context)) { // DS based attribute offsets are relative to the start of the // attribute array. AttributeAddress = (PUCHAR)Context->OnDisk + (SampVariableBufferOffset(Context) + AttributeArray[AttributeIndex].Offset); } else { // Registry based attribute offsets are relative to the end of the // attribute array. 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; SAMTRACE("SampObjectAttributeLength"); 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; SAMTRACE("SampObjectAttributeQualifier"); 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; SAMTRACE("SampGetAttributeBufferReadInfo"); // // 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; SAMTRACE("SampExtendAttributeBuffer"); #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; SAMTRACE("SampReadRegistryAttribute"); // // Try to read the attribute // NtStatus = NtQueryValueKey( Key, AttributeName, //ValueName, KeyValuePartialInformation, //KeyValueInformationClass (PVOID)Buffer, BufferLength, RequiredLength ); SampDumpNtQueryValueKey(AttributeName, KeyValuePartialInformation, 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; SAMTRACE("SampSetVariableAttribute"); // // 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 // if (IsDsObject(Context)) { // DS variable-length attribute offsets are relative to the start // of the variable-length array. MoveLength = Context->OnDiskUsed - ( SampVariableBufferOffset( Context ) + AttributeArray[AttributeIndex+1].Offset ); } else { // Registry variable-length attribute offsets are relative to the // end of the variable-length array. 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 SAMTRACE("SampFreeAttributeBuffer"); 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