/*++ Copyright (c) 1990 Microsoft Corporation Module Name: dsutil.c Abstract: This file contains helper routines for accessing and manipulating data based on the DS backing store. Included, are routines for converting be- tween the SAM data format (BLOBs) and the DS data format (ATTRBLOCKs). NOTE: The routines in this file have direct knowledge of the SAM fixed- length attribute and variable-length attribute structures, as well as the DS ATTRBLOCK structure. Any changes to these structures, including: -addition/deletion of a structure member -data type/size change of a structure member -reordering of the data members -renaming of the data members will break these routines. SAM attributes are accessed via byte buffer offsets and lengths, rather than by identifier or by explicit structure data members. Because of this, changes to the structure layout will lead to failures in SAM operation. Several of the routines have been written assuming that the order of the attributes passed in via an ATTRBLOCK are exactly the order in which SAM understands its own buffer layout. If the attributes are passed into the routines (that take ATTRBLOCKs) out of order, the data in the SAM buffers will be invalid. Author: Chris Mayhall (ChrisMay) 09-May-1996 Environment: User Mode - Win32 Revision History: ChrisMay 09-May-1996 Created initial file, DS ATTRBLOCK-SAM buffer conversion routines for variable-length attributes. ChrisMay 14-May-1996 DS ATTRBLOCK-SAM buffer conversion routines for fixed-length attri- butes. ChrisMay 22-May-1996 Added DWORD_ALIGN macro to align data on DWORD boundaries. Fixed alignment problems on MIPS in SampExtractAttributeFromDsAttr routine. ChrisMay 30-May-1996 Added routines to convert SAM combined-buffer attributes to/from DS ATTRBLOCKs. Revised fixed-length routines to do explicit structure member assignment instead of attempting to compute structure offsets. ChrisMay 18-Jun-1996 Updated fixed-attribute tables to reflect recent changes in mappings.c and mappings.h, and DS schema. Added code to coerce the data sizes of USHORT and BOOLEAN to DS integer data type (4 bytes) so that the DS modify entry routines don't AV. Correctly set attribute type for the variable-length attributes. ChrisMay 25-Jun-1996 Added RtlZeroMemory calls where they were missing. ColinBr 18-Jul-1996 Fixed array overwrite and assigned type to variable length attributes when combining fixed and variable length attrs into one. --*/ #include #include #include #include // Private debugging display routine is enabled when DSUTIL_DBG_PRINTF = 1. #define DSUTIL_DBG_PRINTF 0 #if (DSUTIL_DBG_PRINTF == 1) #define DebugPrint printf #else #define DebugPrint #endif // DWORD_ALIGN is used to adjust pointer offsets up to the next DWORD boundary // during the construction of SAM blob buffers. #define DWORD_ALIGN(value) (((DWORD)(value) + 3) & ~3) // Because it is apparently difficult for the DS to support NT data types of // USHORT, UCHAR, and BOOLEAN (and which are used by SAM), these crappy data // types have been defined for the SampFixedAttributeInfo table so that four- // byte quantities are used. These four-byte quantities correspond to the DS // "integer" data type (for how long?) which is used for storing certain SAM // attributes. Note that it is important to zero out any memory allocated w/ // these data sizes, since only the lower couple of bytes actually contain // data. Enjoy...and refer to the DS schema(.hlp file) for the ultimate word // on the currently used DS data types. #define DS_USHORT ULONG #define DS_UCHAR ULONG #define DS_BOOLEAN ULONG // This type-information table is used by the routines that convert between // SAM fixed-length buffers and DS ATTRBLOCKs. The table contains information // about the data type and size (but may contain any suitable information that // is needed in the future) of the fixed-length attributes. NOTE: the layout // of this table corresponds to the data members of the fixed-length struct- // ures (in samsrvp.h), hence, any changes to those structures must be re- // flected in the type-information table. SAMP_FIXED_ATTRIBUTE_TYPE_INFO SampFixedAttributeInfo[SAMP_OBJECT_TYPES_MAX][SAMP_FIXED_ATTRIBUTES_MAX] = { // The initialization values of this table must strictly match the set // and order of the data members in the SAM fixed-attribute structures, // contained in samsrvp.h. // The routines that manipulate this table assume that the fixed-length // attributes, unlike the variable-length counterparts, are single valued // attributes (i.e. are not multi-valued attributes). // The first column of each element in the table is a type identifier, as // defined in mappings.c. This is used to map the SAM data type into the // equivalent DS data type. The second column of each table element is the // actual (C-defined) size of the element and is used throughout the data // conversion routines in this file in order to allocate memory or set // offset information correctly. // SampServerObjectType { {SAMP_FIXED_SERVER_REVISION_LEVEL, sizeof(ULONG)} }, // SampDomainObjectType { {SAMP_FIXED_DOMAIN_REVISION_LEVEL, sizeof(ULONG)}, {SAMP_FIXED_DOMAIN_UNUSED1, sizeof(ULONG)}, {SAMP_FIXED_DOMAIN_CREATION_TIME, sizeof(LARGE_INTEGER)}, {SAMP_FIXED_DOMAIN_MODIFIED_COUNT, sizeof(LARGE_INTEGER)}, {SAMP_FIXED_DOMAIN_MAX_PASSWORD_AGE, sizeof(LARGE_INTEGER)}, {SAMP_FIXED_DOMAIN_MIN_PASSWORD_AGE, sizeof(LARGE_INTEGER)}, {SAMP_FIXED_DOMAIN_FORCE_LOGOFF, sizeof(LARGE_INTEGER)}, {SAMP_FIXED_DOMAIN_LOCKOUT_DURATION, sizeof(LARGE_INTEGER)}, {SAMP_FIXED_DOMAIN_LOCKOUT_OBSERVATION_WINDOW, sizeof(LARGE_INTEGER)}, {SAMP_FIXED_DOMAIN_MODCOUNT_LAST_PROMOTION, sizeof(LARGE_INTEGER)}, {SAMP_FIXED_DOMAIN_NEXT_RID, sizeof(ULONG)}, {SAMP_FIXED_DOMAIN_PWD_PROPERTIES, sizeof(ULONG)}, {SAMP_FIXED_DOMAIN_MIN_PASSWORD_LENGTH, sizeof(DS_USHORT)}, {SAMP_FIXED_DOMAIN_PASSWORD_HISTORY_LENGTH, sizeof(DS_USHORT)}, {SAMP_FIXED_DOMAIN_LOCKOUT_THRESHOLD, sizeof(DS_USHORT)}, {SAMP_FIXED_DOMAIN_SERVER_STATE, sizeof(DOMAIN_SERVER_ENABLE_STATE)}, {SAMP_FIXED_DOMAIN_SERVER_ROLE, sizeof(DOMAIN_SERVER_ROLE)}, {SAMP_FIXED_DOMAIN_UAS_COMPAT_REQUIRED, sizeof(DS_BOOLEAN)} }, // SampGroupObjectType { {SAMP_FIXED_GROUP_REVISION_LEVEL, sizeof(ULONG)}, {SAMP_FIXED_GROUP_RID, sizeof(ULONG)}, {SAMP_FIXED_GROUP_ATTRIBUTES, sizeof(ULONG)}, {SAMP_FIXED_GROUP_UNUSED1, sizeof(ULONG)}, {SAMP_FIXED_GROUP_ADMIN_COUNT, sizeof(DS_UCHAR)}, {SAMP_FIXED_GROUP_OPERATOR_COUNT, sizeof(DS_UCHAR)} }, // SampAliasObjectType { {SAMP_FIXED_ALIAS_RID, sizeof(ULONG)} }, // SampUserObjectType { {SAMP_FIXED_USER_REVISION_LEVEL, sizeof(ULONG)}, {SAMP_FIXED_USER_UNUSED1, sizeof(ULONG)}, {SAMP_FIXED_USER_LAST_LOGON, sizeof(LARGE_INTEGER)}, {SAMP_FIXED_USER_LAST_LOGOFF, sizeof(LARGE_INTEGER)}, {SAMP_FIXED_USER_PWD_LAST_SET, sizeof(LARGE_INTEGER)}, {SAMP_FIXED_USER_ACCOUNT_EXPIRES, sizeof(LARGE_INTEGER)}, {SAMP_FIXED_USER_LAST_BAD_PASSWORD_TIME, sizeof(LARGE_INTEGER)}, {SAMP_FIXED_USER_USERID, sizeof(ULONG)}, {SAMP_FIXED_USER_PRIMARY_GROUP_ID, sizeof(ULONG)}, {SAMP_FIXED_USER_ACCOUNT_CONTROL, sizeof(ULONG)}, {SAMP_FIXED_USER_COUNTRY_CODE, sizeof(DS_USHORT)}, {SAMP_FIXED_USER_CODEPAGE, sizeof(DS_USHORT)}, {SAMP_FIXED_USER_BAD_PWD_COUNT, sizeof(DS_USHORT)}, {SAMP_FIXED_USER_LOGON_COUNT, sizeof(DS_USHORT)}, {SAMP_FIXED_USER_ADMIN_COUNT, sizeof(DS_USHORT)}, {SAMP_FIXED_USER_UNUSED2, sizeof(DS_USHORT)}, {SAMP_FIXED_USER_OPERATOR_COUNT, sizeof(DS_USHORT)} } }; SAMP_VAR_ATTRIBUTE_TYPE_INFO SampVarAttributeInfo[SAMP_OBJECT_TYPES_MAX][SAMP_VAR_ATTRIBUTES_MAX] = { // The initialization values of this table must strictly match the set // and order of the data members in the SAM variable-attributes, defined // in samsrvp.h. Size is not defined here, because SAM variable-length // attributes store attribute length explicity. Refer to mappings.c and // mappings.h for the definitions used for the data types in this table. // SampServerObjectType { {SAMP_SERVER_SECURITY_DESCRIPTOR} }, // SampDomainObjectType { {SAMP_DOMAIN_SECURITY_DESCRIPTOR}, {SAMP_DOMAIN_SID}, {SAMP_DOMAIN_OEM_INFORMATION}, {SAMP_DOMAIN_REPLICA} }, // SampGroupObjectType { {SAMP_GROUP_SECURITY_DESCRIPTOR}, {SAMP_GROUP_NAME}, {SAMP_GROUP_ADMIN_COMMENT}, {SAMP_GROUP_MEMBERS} }, // SampAliasObjectType { {SAMP_ALIAS_SECURITY_DESCRIPTOR}, {SAMP_ALIAS_NAME}, {SAMP_ALIAS_ADMIN_COMMENT}, {SAMP_ALIAS_MEMBERS} }, // SampUserObjectType { {SAMP_USER_SECURITY_DESCRIPTOR}, {SAMP_USER_ACCOUNT_NAME}, {SAMP_USER_FULL_NAME}, {SAMP_USER_ADMIN_COMMENT}, {SAMP_USER_USER_COMMENT}, {SAMP_USER_PARAMETERS}, {SAMP_USER_HOME_DIRECTORY}, {SAMP_USER_HOME_DIRECTORY_DRIVE}, {SAMP_USER_SCRIPT_PATH}, {SAMP_USER_PROFILE_PATH}, {SAMP_USER_WORKSTATIONS}, {SAMP_USER_LOGON_HOURS}, {SAMP_USER_GROUPS}, {SAMP_USER_DBCS_PWD}, {SAMP_USER_UNICODE_PWD}, {SAMP_USER_NT_PWD_HISTORY}, {SAMP_USER_LM_PWD_HISTORY} } }; // // MISCELLANEOUS HELPER ROUTINES // NTSTATUS SampFreeSamAttributes( IN PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamAttributes ) /*++ Routine Description: (Under development) Arguments: Return Value: --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; SAMTRACE("SampFreeSamAttributes"); return(NtStatus); } NTSTATUS SampReallocateBuffer( IN ULONG OldLength, IN ULONG NewLength, IN OUT PVOID *Buffer ) /*++ Routine Description: This routine resizes an in-memory buffer. The routine can either grow or shrink the buffer based on specified lengths. Data is preserved from old to new buffers, truncating if the new buffer is shorter than the actual data length. The newly allocated buffer is returned as an out parameter, the passed in buffer is released for the caller. Arguments: OldLength - Length of the buffer passed into the routine. NewLength - Length of the re-allocated buffer. Buffer - Pointer, incoming buffer to resize, outgoing new buffer. Return Value: STATUS_SUCCESS - Buffer header block allocated and initialized. Other codes indicating the nature of the failure. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; PVOID BufferTmp = NULL; SAMTRACE("SampReallocateBuffer"); if ((NULL != Buffer) && (NULL != *Buffer) && (0 < OldLength) && (0 < NewLength)) { // Allocate a new buffer and set the temporary variable. Note that // the routine does not destroy the old buffer if there is any kind // of failure along the way. BufferTmp = RtlAllocateHeap(RtlProcessHeap(), 0, NewLength); if (NULL != BufferTmp) { RtlZeroMemory(BufferTmp, NewLength); // Copy the original buffer into the new one, truncating data if // the new buffer is shorter than the original data size. if (OldLength < NewLength) { RtlCopyMemory(BufferTmp, *Buffer, OldLength); } else { RtlCopyMemory(BufferTmp, *Buffer, NewLength); } // If all has worked, delete the old buffer and set the outgoing // buffer pointer. RtlFreeHeap(RtlProcessHeap(), 0, *Buffer); *Buffer = BufferTmp; NtStatus = STATUS_SUCCESS; } else { NtStatus = STATUS_NO_MEMORY; } } else { NtStatus = STATUS_INVALID_PARAMETER; } return(NtStatus); } // // ATTRBLOCK-TO-VARIABLE LENGTH CONVERSION ROUTINES // NTSTATUS SampInitializeVarLengthAttributeBuffer( IN ULONG AttributeCount, OUT PULONG BufferLength, OUT PSAMP_VARIABLE_LENGTH_ATTRIBUTE *SamAttributes ) /*++ Routine Description: This routine sets up the SAM attribute buffer that is the destination for attributes read from the DS backing store. The buffer contains a header, followed by variable-length attributes (SAMP_VARIABLE_LENGTH_ATTRIBUTE). This routine allocates memory for the buffer header and zeros it out. Arguments: AttributeCount - Number of variable-length attributes. BufferLength - Pointer, buffer size allocated by this routine. SamAttributes - Pointer, returned buffer. Return Value: STATUS_SUCCESS - Buffer header block allocated and initialized. Other codes indicating the nature of the failure. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; ULONG Length = 0; SAMTRACE("SampInitializeVarLengthAttributeBuffer"); if (0 < AttributeCount) { // Calculate the space needed for the attribute-offset array. If the // attribute count is zero, skip the allocation and return an error. Length = AttributeCount * sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE); if (NULL != SamAttributes) { *SamAttributes = RtlAllocateHeap(RtlProcessHeap(), 0, Length); if (NULL != *SamAttributes) { // Initialize the block and return the updated buffer offset, // which now points to the last byte of the header block. RtlZeroMemory(*SamAttributes, Length); if (NULL != BufferLength) { *BufferLength = Length; NtStatus = STATUS_SUCCESS; } } else { NtStatus = STATUS_NO_MEMORY; } } } return(NtStatus); } NTSTATUS SampExtractAttributeFromDsAttr( IN PDSATTR Attribute, OUT PULONG MultiValuedCount, OUT PULONG Length, OUT PVOID *Buffer ) /*++ Routine Description: This routine determines whether or not the current attribute is single- valued or multi-valued and returns a buffer containing the value(s) of the attribute. If the attribute is multi-valued, the values are appended in the buffer. Arguments: Attribute - Pointer, incoming DS attribute structure. MultiValuedCount - Pointer, returned count of the number of values found for this attribute. Length - Pointer, returned buffer length. Buffer - Pointer, returned buffer containing one or more values. Return Value: STATUS_SUCCESS - Buffer header block allocated and initialized. Other codes indicating the nature of the failure. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; ULONG ValueCount = 0; PDSATTRVALBLOCK ValueBlock; PDSATTRVAL Values = NULL; ULONG ValueIndex = 0; ULONG TotalLength = 0; ULONG Offset = 0; SAMTRACE("SampExtractAttributeFromDsAttr"); // Get the count of attributes and a pointer to the attribute. Note that // it is possible to have multi-valued attributes, in which case they are // appended onto the end of the return buffer. if (NULL != Attribute) { // DSATTR structure contains: attrTyp, AttrVal ValueBlock = &(Attribute->AttrVal); // DSATTRVALBLOCK structure contains: valCount, pAVal ValueCount = ValueBlock->valCount; Values = ValueBlock->pAVal; if ((0 < ValueCount) && (NULL != Values)) { // Multi-valued attribute processing; first determine the total // buffer length that will be needed. for (ValueIndex = 0; ValueIndex < ValueCount; ValueIndex++) { // Determine total length needed for this attribute. Because // the value lengths may not be DWORD size, pad up to the // next DWORD size. TotalLength += DWORD_ALIGN(Values[ValueIndex].valLen); } } if ((0 < TotalLength) && (NULL != Buffer)) { // Allocate the buffer for the attributes. *Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, TotalLength); if (NULL != *Buffer) { RtlZeroMemory(*Buffer, TotalLength); for (ValueIndex = 0; ValueIndex < ValueCount; ValueIndex++) { // DSATTRVAL structure contains: valLen, pVal. Append // subsequent values onto the end of the buffer, up- // dating the end-of-buffer offset each time. RtlCopyMemory((*(BYTE **)Buffer + Offset), (PBYTE)(Values[ValueIndex].pVal), Values[ValueIndex].valLen); // Adjust the offset up to the next DWORD boundary. Offset += DWORD_ALIGN(Values[ValueIndex].valLen); } if ((NULL != MultiValuedCount) && (NULL != Length)) { // Finished, update return values. *MultiValuedCount = ValueCount; *Length = TotalLength; NtStatus = STATUS_SUCCESS; } } else { NtStatus = STATUS_NO_MEMORY; } } } return(NtStatus); } NTSTATUS SampVerifyVarLengthAttribute( IN INT ObjectType, IN ULONG AttrIndex, IN ULONG MultiValuedCount, IN ULONG AttributeLength ) /*++ Routine Description: This routine is under construction. Arguments: Return Value: STATUS_SUCCESS - Buffer header block allocated and initialized. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; SAMTRACE("SampVerifyVarLengthAttribute"); // BUG: Define a table of variable-length attribute information. switch(ObjectType) { // For each SAM object type, verify that attributes that are supposed to // be single valued, have a MultiValueCount of 1 (multi-valued attributes // can have a count greater-than or equal to 1). case SampServerObjectType: if (1 == MultiValuedCount) { NtStatus = STATUS_SUCCESS; } break; case SampDomainObjectType: if (1 == MultiValuedCount) { NtStatus = STATUS_SUCCESS; } break; case SampGroupObjectType: // Multi-valued attribute if ((SAMP_GROUP_MEMBERS != AttrIndex)) { if (1 == MultiValuedCount) { NtStatus = STATUS_SUCCESS; } } break; case SampAliasObjectType: // Multi-valued attribute if ((SAMP_ALIAS_MEMBERS != AttrIndex)) { if (1 == MultiValuedCount) { NtStatus = STATUS_SUCCESS; } } break; case SampUserObjectType: // Multi-valued attributes if ((SAMP_ALIAS_MEMBERS != AttrIndex) && (SAMP_USER_LOGON_HOURS != AttrIndex)) { if (1 == MultiValuedCount) { NtStatus = STATUS_SUCCESS; } } break; default: break; } NtStatus = STATUS_SUCCESS; return(NtStatus); } NTSTATUS SampAppendVarLengthAttributeToBuffer( IN ULONG AttrIndex, IN PVOID NewAttribute, IN ULONG MultiValuedCount, IN ULONG AttributeLength, IN OUT PULONG BufferLength, IN OUT PSAMP_VARIABLE_LENGTH_ATTRIBUTE *SamAttributes ) /*++ Routine Description: This routine appends the current attribute onto the end of the attribute buffer, and updates the SAMP_VARIABLE_LENGTH_DATA structures in the head- er of the buffer with new offset, length, and qualifier information. Arguments: AttrIndex - Index into the array of variable-length offsets. NewAttribute - Pointer, the new attribute to be appended to the buffer. MultiValuedCount - Number of values for the attribute. AttributeLength - Number of bytes of the attribute. BufferLength - Pointer, incoming contains the current length of the buf- fer; outgoing contains the updated length after appending the latest attribute. SamAttributes - Pointer, SAMP_VARIABLE_LENGTH_ATTRIBUTE buffer. Return Value: STATUS_SUCCESS - Buffer header block allocated and initialized. Other codes indicating the nature of the failure. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; ULONG NewLength = 0; SAMTRACE("SampAppendVarLengthAttributeToBuffer"); if ((NULL != BufferLength) && (0 < AttributeLength)) { // Compute the required buffer length needed to append the attribute. NewLength = *BufferLength + AttributeLength; if (0 < NewLength) { // Adjust buffer size for the attribute. NtStatus = SampReallocateBuffer(*BufferLength, NewLength, SamAttributes); } if (NT_SUCCESS(NtStatus)) { // Append the attribute onto the return buffer. RtlCopyMemory((((PBYTE)(*SamAttributes)) + *BufferLength), NewAttribute, AttributeLength); // Update the variable-length header information for the latest // attribute. (*SamAttributes + AttrIndex)->Offset = *BufferLength; (*SamAttributes + AttrIndex)->Length = AttributeLength; // BUG: Assuming that Qualifier is used for multi-value count? (*SamAttributes + AttrIndex)->Qualifier = MultiValuedCount; // Pass back the updated buffer length. *BufferLength = NewLength; DebugPrint("BufferLength = %lu\n", *BufferLength); DebugPrint("NewLength = %lu\n", NewLength); DebugPrint("SamAttributes Offset = %lu\n", (*SamAttributes + AttrIndex)->Offset); DebugPrint("SamAttributes Length = %lu\n", (*SamAttributes + AttrIndex)->Length); DebugPrint("SamAttributes Qualifier = %lu\n", (*SamAttributes + AttrIndex)->Qualifier); } } return(NtStatus); } NTSTATUS SampConvertAttrBlockToVarLengthAttributes( IN INT ObjectType, IN PDSATTRBLOCK DsAttributes, OUT PSAMP_VARIABLE_LENGTH_ATTRIBUTE *SamAttributes, OUT PULONG TotalLength ) /*++ Routine Description: This routine extracts the DS attributes from a DS READRES structure and builds a SAMP_VARIABLE_LENGTH_BUFFER with them. This routine allocates the necessary memory block for the SAM variable-length attribute buffer. This routine assumes that the attributes passed in via the READRES struc- ture are in the correct order (as known to SAM). Arguments: ObjectType - SAM object type identifier (this parameter is currently un- used, but will likely be used to set the maximum number of attributes for any given conversion). DsAttributes - Pointer, DS attribute list. SamAttributes - Pointer, returned SAM variable-length attribute buffer. Return Value: STATUS_SUCCESS - The object has been successfully accessed. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; ULONG AttributeCount = 0; PDSATTR Attributes = NULL; ULONG BufferLength = 0; ULONG AttrIndex = 0; ULONG AttributeLength = 0; ULONG MultiValuedCount = 0; PVOID Attribute = NULL; SAMTRACE("SampConvertAttrBlockToVarLengthAttributes"); if ((NULL != DsAttributes) && (NULL != SamAttributes)) { // Get the attribute count and a pointer to the attributes. // DSATTRBLOCK contains: attrCount, pAttr // BUG: ObjectType can be used to get the compile-time attr count. // Obtaining the attribute count from DsAttributes may be erroron- // eous, hence ObjectType could be used instead to set the count to // the constants that define the maximum number of attributes. AttributeCount = DsAttributes->attrCount; Attributes = DsAttributes->pAttr; if ((0 < AttributeCount) && (NULL != Attributes)) { // Set up the variable-length attribute buffer header based on the // number of attributes. Allocate and initialize the SamAttributes // buffer. Update BufferLength to reflect the new size. NtStatus = SampInitializeVarLengthAttributeBuffer( AttributeCount, &BufferLength, SamAttributes); } } else { NtStatus = STATUS_INVALID_PARAMETER; } if (NT_SUCCESS(NtStatus)) { // For each attribute, get its value (or values in the case of multi- // valued attributes). for (AttrIndex = 0; AttrIndex < AttributeCount; AttrIndex++) { // A given attribute may be multi-valued, in which case multiple // values are simply concatenated together. MultiValuedCount will // contain the number of values for the attribute. NtStatus = SampExtractAttributeFromDsAttr( &(Attributes[AttrIndex]), &MultiValuedCount, &AttributeLength, &Attribute); // Verify that the DS has returned SAM attributes correctly. Check // such things as attribute length, single vs. multi-value status. NtStatus = SampVerifyVarLengthAttribute(ObjectType, AttrIndex, MultiValuedCount, AttributeLength); if (NT_SUCCESS(NtStatus)) { // Append the current attribute onto the end of the SAM vari- // able length attribute buffer and update the offset array. // AttrIndex is not only the loop counter, but is also the // index into the proper element of the variable-length attr- // ibute array. NOTE: This routine assumes that the order in // which the elements were returned in the READRES buffer is // in fact the correct order of the SAM attributes as defined // in samsrvp.h NtStatus = SampAppendVarLengthAttributeToBuffer( AttrIndex, Attribute, MultiValuedCount, AttributeLength, &BufferLength, SamAttributes); } if (!NT_SUCCESS(NtStatus)) { // Detect failure of either routine and break for return. Let // the caller release the memory that is returned. break; } } if (NULL != TotalLength) { *TotalLength = BufferLength; } else { NtStatus = STATUS_INVALID_PARAMETER; } } return(NtStatus); } // // VARIABLE LENGTH-TO-ATTRBLOCK CONVERSION ROUTINES // BOOLEAN SampIsQualifierTheCount( IN INT ObjectType, IN ULONG AttrIndex ) { BOOLEAN IsCount = FALSE; SAMTRACE("SampIsQualifierTheCount"); switch(ObjectType) { case SampServerObjectType: IsCount = FALSE; break; case SampDomainObjectType: IsCount = FALSE; break; case SampGroupObjectType: // Multi-valued attribute if ((SAMP_GROUP_MEMBERS == AttrIndex)) { IsCount = TRUE; } break; case SampAliasObjectType: // Multi-valued attribute if ((SAMP_ALIAS_MEMBERS == AttrIndex)) { IsCount = TRUE; } break; case SampUserObjectType: // Multi-valued attributes if ((SAMP_ALIAS_MEMBERS == AttrIndex) || (SAMP_USER_LOGON_HOURS == AttrIndex)) { IsCount = TRUE; } break; default: // Error break; } return(IsCount); } NTSTATUS SampConvertAndAppendAttribute( IN INT ObjectType, IN ULONG AttrIndex, IN PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamAttributes, OUT PDSATTR Attributes ) /*++ Routine Description: This routine does the work of converting a variable-length attribute from a SAM buffer into a DS attribute. A DSATTR structure is constructed and passed back from this routine to the caller. Arguments: ObjectType - SAM object type identifier (this parameter is currently un- used, but will likely be used to set the maximum number of attributes for any given conversion). AttrIndex - Index into the array of the variable-length attribute inform- ation and the DS attribute (i.e. the current attribute). SamAttributes - Pointer, returned SAM variable-length attribute buffer. Attributes - Pointer, the returned DS attribute structure. Return Value: STATUS_SUCCESS - The object has been successfully accessed. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; ULONG Offset = SamAttributes[AttrIndex].Offset; ULONG Length = SamAttributes[AttrIndex].Length; ULONG MultiValuedCount = SamAttributes[AttrIndex].Qualifier; ULONG Index = 0; PDSATTRVAL Attribute = NULL; PBYTE Value = NULL; SAMTRACE("SampConvertAndAppendAttribute"); // Set the attribute type to the equivalent DS data type. Attributes[AttrIndex].attrTyp = SampVarAttributeInfo[ObjectType][AttrIndex].Type; if (TRUE == SampIsQualifierTheCount(ObjectType, AttrIndex)) { // Qualifier contains the attribute's multi-value count. Attributes[AttrIndex].AttrVal.valCount = MultiValuedCount; } else { // BUG: Lost "Qualifier" information (e.g. REVISION) during conversion. // Qualifier contains something other than the count, so set valCount // to 1 because this is a single-valued attribute. Attributes[AttrIndex].AttrVal.valCount = 1; MultiValuedCount = 1; } // Allocate memory for the attribute (array if multi-valued). Attribute = RtlAllocateHeap(RtlProcessHeap(), 0, (MultiValuedCount * sizeof(DSATTRVAL))); if (NULL != Attribute) { RtlZeroMemory(Attribute, (MultiValuedCount * sizeof(DSATTRVAL))); // Begin construction of the DSATTR structure by setting the pointer // the to the attribute. Attributes[AttrIndex].AttrVal.pAVal = Attribute; // SAM does not store per-value length information for multi-valued // attributes, instead the total length of all of the values of a // single attribute is stored. // Length is the number of bytes in the overall attribute. If the // attribute is multi-valued, then this length is the total length // of all of the attribute values. The per-value allocation is equal // to the Length divided by the number of values (because all values // of all multi-valued attributes are a fixed size (i.e. ULONG or // LARGE_INTEGER). // Test to make sure that total length is an integral multiple of the // number of values--a sanity check. if (0 == (Length % MultiValuedCount)) { Length = (Length / MultiValuedCount); } else { // The length is erroneous, so artificially reset to zero in order // to terminate things. Length = 0; } for (Index = 0; Index < MultiValuedCount; Index++) { // Allocate memory for the attribute data. Value = RtlAllocateHeap(RtlProcessHeap(), 0, Length); if (NULL != Value) { RtlZeroMemory(Value, Length); // For each value, in the attribute, store its length and // copy the value into the destination buffer. Attribute[Index].valLen = Length; Attribute[Index].pVal = Value; // Note: SamAttributes is passed in as PSAMP_VARIABLE_LENTGH- // ATTRIBUTE, hence is explicitly cast to a byte pointer to // do the byte-offset arithmetic correctly for RtlCopyMemory. RtlCopyMemory(Value, (((PBYTE)SamAttributes) + Offset), Length); // Adjust the SAM-buffer offset to point at the next value in // the multi-valued attribute. Offset += Length; NtStatus = STATUS_SUCCESS; } else { NtStatus = STATUS_NO_MEMORY; break; } } } else { NtStatus = STATUS_NO_MEMORY; } return(NtStatus); } NTSTATUS SampConvertVarLengthAttributesToAttrBlock( IN INT ObjectType, IN PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamAttributes, OUT PDSATTRBLOCK *DsAttributes ) /*++ Routine Description: This routine determines the SAM object type so that the attribute count can be set, and then performs the attribute conversion. This routine al- locates the top-level DS structure and then calls a helper routine to fill in the rest of the data. Arguments: ObjectType - SAM object type identifier (this parameter is currently un- used, but will likely be used to set the maximum number of attributes for any given conversion). SamAttributes - Pointer, returned SAM variable-length attribute buffer. DsAttributes - Pointer, the returned DS attribute structure. Return Value: STATUS_SUCCESS - The object has been successfully accessed. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; ULONG AttributeCount = 0; PDSATTR Attributes = NULL; PVOID Attribute = NULL; ULONG AttrIndex = 0; ULONG Length = 0; ULONG Qualifier = 0; SAMTRACE("SampConvertVarLengthAttributesToAttrBlock"); if (NULL != DsAttributes) { // Allocate the top-level structure. *DsAttributes = RtlAllocateHeap(RtlProcessHeap(), 0, sizeof(DSATTRBLOCK)); if ((NULL != SamAttributes) && (NULL != *DsAttributes)) { RtlZeroMemory(*DsAttributes, sizeof(DSATTRBLOCK)); // Determine the object type, and hence set the corresponding // attribute count. switch(ObjectType) { case SampServerObjectType: AttributeCount = SAMP_SERVER_VARIABLE_ATTRIBUTES; break; case SampDomainObjectType: AttributeCount = SAMP_DOMAIN_VARIABLE_ATTRIBUTES; break; case SampGroupObjectType: AttributeCount = SAMP_GROUP_VARIABLE_ATTRIBUTES; break; case SampAliasObjectType: AttributeCount = SAMP_ALIAS_VARIABLE_ATTRIBUTES; break; case SampUserObjectType: AttributeCount = SAMP_USER_VARIABLE_ATTRIBUTES; break; default: AttributeCount = 0; break; } DebugPrint("AttributeCount = %lu\n", AttributeCount); // Allocate the array of DS attribute-information structs. Attributes = RtlAllocateHeap(RtlProcessHeap(), 0, (AttributeCount * sizeof(DSATTR))); if (NULL != Attributes) { RtlZeroMemory(Attributes, (AttributeCount * sizeof(DSATTR))); (*DsAttributes)->attrCount = AttributeCount; (*DsAttributes)->pAttr = Attributes; // Walk through the array of attributes, converting each // SAM variable-length attribute to a DS attribute. Refer to // the DS header files (core.h, drs.h) for definitions of // these structures. for (AttrIndex = 0; AttrIndex < AttributeCount; AttrIndex++) { NtStatus = SampConvertAndAppendAttribute(ObjectType, AttrIndex, SamAttributes, Attributes); if (!NT_SUCCESS(NtStatus)) { break; } DebugPrint("attrCount = %lu\n", (*DsAttributes)->attrCount); DebugPrint("attrTyp = %lu\n", (*DsAttributes)->pAttr[AttrIndex].attrTyp); DebugPrint("valCount = %lu\n", (*DsAttributes)->pAttr[AttrIndex].AttrVal.valCount); DebugPrint("valLen = %lu\n", (*DsAttributes)->pAttr[AttrIndex].AttrVal.pAVal->valLen); } } } } return(NtStatus); } // // ATTRBLOCK-TO-FIXED LENGTH CONVERSION ROUTINES // NTSTATUS SampExtractFixedLengthAttributeFromDsAttr( IN PDSATTR Attribute, OUT PULONG MultiValuedCount, OUT PULONG Length, OUT PVOID *Buffer ) /*++ Routine Description: This routine determines whether or not the current attribute is single- valued or multi-valued and returns a buffer containing the value(s) of the attribute. If the attribute is multi-valued, the values are appended in the buffer. Arguments: Attribute - Pointer, incoming DS attribute structure. MultiValuedCount - Pointer, returned count of the number of values found for this attribute. Length - Pointer, returned buffer length. Buffer - Pointer, returned buffer containing one or more values. Return Value: STATUS_SUCCESS - Buffer header block allocated and initialized. Other codes indicating the nature of the failure. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; ULONG ValueCount = 0; PDSATTRVALBLOCK ValueBlock; PDSATTRVAL Values = NULL; ULONG ValueIndex = 0; ULONG TotalLength = 0; ULONG Offset = 0; SAMTRACE("SampExtractFixedLengthAttributeFromDsAttr"); // Get the count of attributes and a pointer to the attribute. Note that // it is possible to have multi-valued attributes, in which case they are // appended onto the end of the return buffer. if (NULL != Attribute) { // DSATTR structure contains: attrTyp, AttrVal ValueBlock = &(Attribute->AttrVal); // DSATTRVALBLOCK structure contains: valCount, pAVal if (NULL != ValueBlock) { ValueCount = ValueBlock->valCount; Values = ValueBlock->pAVal; } if ((0 < ValueCount) && (NULL != Values)) { // Multi-valued attribute processing; first determine the total // buffer length that will be needed. // BUG: Use SampFixedAttrInfo for length instead? // BUG: Fixed Attributes are only single valued, remove loop. for (ValueIndex = 0; ValueIndex < ValueCount; ValueIndex++) { // Determine total length needed for this attribute. TotalLength += Values[ValueIndex].valLen; } } if ((0 < TotalLength) && (NULL != Buffer)) { // Allocate the buffer for the attributes. *Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, TotalLength); if (NULL != *Buffer) { RtlZeroMemory(*Buffer, TotalLength); for (ValueIndex = 0; ValueIndex < ValueCount; ValueIndex++) { // DSATTRVAL structure contains: valLen, pVal. Append // subsequent values onto the end of the buffer, up- // dating the end-of-buffer offset each time. RtlCopyMemory((*(BYTE **)Buffer + Offset), (PBYTE)(Values[ValueIndex].pVal), Values[ValueIndex].valLen); Offset += Values[ValueIndex].valLen; } if ((NULL != MultiValuedCount) && (NULL != Length)) { // Finished, update return values. *MultiValuedCount = ValueCount; *Length = TotalLength; NtStatus = STATUS_SUCCESS; } } else { NtStatus = STATUS_NO_MEMORY; } } } return(NtStatus); } NTSTATUS SampVerifyFixedLengthAttribute( IN INT ObjectType, IN ULONG AttrIndex, IN ULONG MultiValuedCount, IN ULONG AttributeLength ) /*++ Routine Description: This routine verifies that the length of a given (fixed-length) attribute obtained from the attribute information in a DSATTRBLOCK is in fact the correct length. This check is necessary because the underlying data store and various internal DS layers remap the SAM data types to their internal data types, which may be a different size (e.g. BOOLEAN is mapped to INT). Validation of the lenght is accomplished by comparing the passed-in length to the a prior known lengths stored in the SampFixedAttributeInfo table. NOTE: Currently, this routine simply checks for equality, returning an error if the two lengths are not equal. This test may need to "special case" certain attributes as the database schema is finalized and more is known about the underlying data types. Arguments: ObjectType - SAM Object identifier (server, domain, etc.) index AttrIndex - Index into the array of fixed-length attribute length inform- ation. MultiValuedCount - Number of values in a multi-valued attribute. AttributeLength - Attribute length (byte count) to be verified. Return Value: STATUS_SUCCESS - The object has been successfully accessed. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; SAMTRACE("SampVerifyFixedLengthAttribute"); // Verify that the attribute length is correct. The AttributeLength is // already rounded up to a DWORD boundary, so do the same for the attri- // bute information length. if (AttributeLength == (SampFixedAttributeInfo[ObjectType][AttrIndex].Length)) { if (1 == MultiValuedCount) { // Verify that the fixed-length attribute is single-valued. NtStatus = STATUS_SUCCESS; } } else { DebugPrint("AttributeLength = %lu Length = %lu\n", AttributeLength, SampFixedAttributeInfo[ObjectType][AttrIndex].Length); } return(NtStatus); } NTSTATUS SampAppendFixedLengthAttributeToBuffer( IN INT ObjectType, IN ULONG AttrIndex, IN PVOID NewAttribute, IN OUT PVOID SamAttributes ) /*++ Routine Description: This routine builds a SAM fixed-length attribute buffer from a correspond- ing DS attribute by copying the data into the SAM fixed-length structure. Note that pointer-casts during structure member assignment are not only needed due to the fact that NewAttribute is a PVOID, but also because the DS uses different data types than does SAM for certain data types (e.g. SAM USHORT is stored as a four-byte integer in the DS). Refer to the Samp- FixedAttributeInfo table for details. The data truncation is benign in all cases. Arguments: ObjectType - SAM object type (server, domain, etc.). AttrIndex - Index of the attribute to set. This value corresponds to the elements of the various fixed-length attributes (see samsrvp.h). NewAttribute - The incoming attribute, extracted from the DS data. SamAttributes - Pointer, updated SAM attribute buffer. Return Value: STATUS_SUCCESS - The object has been successfully accessed. --*/ { NTSTATUS NtStatus = STATUS_SUCCESS; PSAMP_V1_FIXED_LENGTH_SERVER ServerAttrs = NULL; PSAMP_V1_0A_FIXED_LENGTH_DOMAIN DomainAttrs = NULL; PSAMP_V1_0A_FIXED_LENGTH_GROUP GroupAttrs = NULL; PSAMP_V1_FIXED_LENGTH_ALIAS AliasAttrs = NULL; PSAMP_V1_0A_FIXED_LENGTH_USER UserAttrs = NULL; SAMTRACE("SampAppendFixedLengthAttributeToBuffer"); if ((NULL != NewAttribute) && (NULL != SamAttributes)) { // BUG: Define constants for the fixed attributes cases. // Determine the object type, and then the attribute for that object // to copy into the target SAM fixed-length structure. switch(ObjectType) { case SampServerObjectType: ServerAttrs = SamAttributes; switch(AttrIndex) { case 0: ServerAttrs->RevisionLevel = *(PULONG)NewAttribute; break; default: NtStatus = STATUS_INTERNAL_ERROR; break; } break; case SampDomainObjectType: DomainAttrs = SamAttributes; switch(AttrIndex) { case 0: DomainAttrs->Revision = *(PULONG)NewAttribute; break; case 1: DomainAttrs->Unused1 = *(PULONG)NewAttribute; break; case 2: DomainAttrs->CreationTime = *(PLARGE_INTEGER)NewAttribute; break; case 3: DomainAttrs->ModifiedCount = *(PLARGE_INTEGER)NewAttribute; break; case 4: DomainAttrs->MaxPasswordAge = *(PLARGE_INTEGER)NewAttribute; break; case 5: DomainAttrs->MinPasswordAge = *(PLARGE_INTEGER)NewAttribute; break; case 6: DomainAttrs->ForceLogoff = *(PLARGE_INTEGER)NewAttribute; break; case 7: DomainAttrs->LockoutDuration = *(PLARGE_INTEGER)NewAttribute; break; case 8: DomainAttrs->LockoutObservationWindow = *(PLARGE_INTEGER)NewAttribute; break; case 9: DomainAttrs->ModifiedCountAtLastPromotion = *(PLARGE_INTEGER)NewAttribute; break; case 10: DomainAttrs->NextRid = *(PULONG)NewAttribute; break; case 11: DomainAttrs->PasswordProperties = *(PULONG)NewAttribute; break; case 12: DomainAttrs->MinPasswordLength = *(PUSHORT)NewAttribute; break; case 13: DomainAttrs->PasswordHistoryLength = *(PUSHORT)NewAttribute; break; case 14: DomainAttrs->LockoutThreshold = *(PUSHORT)NewAttribute; break; case 15: DomainAttrs->ServerState = *(PULONG)NewAttribute; break; case 16: DomainAttrs->ServerRole = *(PULONG)NewAttribute; break; case 17: DomainAttrs->UasCompatibilityRequired = *(PBOOLEAN)NewAttribute; break; default: NtStatus = STATUS_INTERNAL_ERROR; break; } break; case SampGroupObjectType: GroupAttrs = SamAttributes; switch(AttrIndex) { case 0: GroupAttrs->Revision = *(PULONG)NewAttribute; break; case 1: GroupAttrs->RelativeId = *(PULONG)NewAttribute; break; case 2: GroupAttrs->Attributes = *(PULONG)NewAttribute; break; case 3: GroupAttrs->Unused1 = *(PULONG)NewAttribute; break; case 4: GroupAttrs->AdminCount = *(PUCHAR)NewAttribute; break; case 5: GroupAttrs->OperatorCount = *(PUCHAR)NewAttribute; break; default: break; } break; case SampAliasObjectType: AliasAttrs = SamAttributes; switch(AttrIndex) { case 0: AliasAttrs->RelativeId = *(PULONG)NewAttribute; break; default: NtStatus = STATUS_INTERNAL_ERROR; break; } break; case SampUserObjectType: UserAttrs = SamAttributes; switch(AttrIndex) { case 0: UserAttrs->Revision = *(PULONG)NewAttribute; break; case 1: UserAttrs->Unused1 = *(PULONG)NewAttribute; break; case 2: UserAttrs->LastLogon = *(PLARGE_INTEGER)NewAttribute; break; case 3: UserAttrs->LastLogoff = *(PLARGE_INTEGER)NewAttribute; break; case 4: UserAttrs->PasswordLastSet = *(PLARGE_INTEGER)NewAttribute; break; case 5: UserAttrs->AccountExpires = *(PLARGE_INTEGER)NewAttribute; break; case 6: UserAttrs->LastBadPasswordTime = *(PLARGE_INTEGER)NewAttribute; break; case 7: UserAttrs->UserId = *(PULONG)NewAttribute; break; case 8: UserAttrs->PrimaryGroupId = *(PULONG)NewAttribute; break; case 9: UserAttrs->UserAccountControl = *(PULONG)NewAttribute; break; case 10: UserAttrs->CountryCode = *(PUSHORT)NewAttribute; break; case 11: UserAttrs->CodePage = *(PUSHORT)NewAttribute; break; case 12: UserAttrs->BadPasswordCount = *(PUSHORT)NewAttribute; break; case 13: UserAttrs->LogonCount = *(PUSHORT)NewAttribute; break; case 14: UserAttrs->AdminCount = *(PUSHORT)NewAttribute; break; case 15: UserAttrs->Unused2 = *(PUSHORT)NewAttribute; break; case 16: UserAttrs->OperatorCount = *(PUSHORT)NewAttribute; break; default: NtStatus = STATUS_INTERNAL_ERROR; break; } break; default: NtStatus = STATUS_INTERNAL_ERROR; break; } } else { NtStatus = STATUS_INVALID_PARAMETER; } return(NtStatus); } NTSTATUS SampConvertAttrBlockToFixedLengthAttributes( IN INT ObjectType, IN PDSATTRBLOCK DsAttributes, OUT PVOID *SamAttributes, OUT PULONG TotalLength ) /*++ Routine Description: This routine converts a DS ATTRBLOCK into a SAM fixed-length buffer. The SAM buffer that is passed back from the routine can be either treated as a blob or can be cast to one of the SAM fixed-length attribute types for convenience. Arguments: ObjectType - Identifies which SAM object type, and hence, which attribute set to work with. DsAttributes - Pointer, incoming DS ATTRBLOCK containing fixed-length attributes. SamAttributes - Pointer, updated SAM attribute buffer. TotalLength - Pointer, length of the SAM fixed attribute data returned. Return Value: STATUS_SUCCESS - The object has been successfully accessed. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; ULONG AttributeCount = 0; PDSATTR Attributes = NULL; ULONG Length = 0; ULONG BufferLength = 0; ULONG AttributeLength = 0; ULONG AttrIndex = 0; ULONG MultiValuedCount = 0; PVOID Attribute = NULL; SAMTRACE("SampConvertAttrBlockToFixedLengthAttributes"); if ((NULL != DsAttributes) && (NULL != SamAttributes)) { AttributeCount = DsAttributes->attrCount; Attributes = DsAttributes->pAttr; // Using the SAM object type identifer, set the length of the buffer // to be allocated based on the fixed-length data structure. switch(ObjectType) { case SampServerObjectType: Length = sizeof(SAMP_V1_FIXED_LENGTH_SERVER); break; case SampDomainObjectType: Length = sizeof(SAMP_V1_0A_FIXED_LENGTH_DOMAIN); break; case SampGroupObjectType: Length = sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP); break; case SampAliasObjectType: Length = sizeof(SAMP_V1_FIXED_LENGTH_ALIAS); break; case SampUserObjectType: Length = sizeof(SAMP_V1_0A_FIXED_LENGTH_USER); break; default: Length = 0; break; } // Allocate space for the fixed-length attributes. *SamAttributes = RtlAllocateHeap(RtlProcessHeap(), 0, Length); if ((NULL != *SamAttributes) && (NULL != Attributes)) { RtlZeroMemory(*SamAttributes, Length); // Walk the DSATTRBLOCK, pulling out the attributes and returning // each one in the Attribute out parameter. // BUG: Verify that the attribute count is correct. for (AttrIndex = 0; AttrIndex < AttributeCount; AttrIndex++) { NtStatus = SampExtractFixedLengthAttributeFromDsAttr( &(Attributes[AttrIndex]), &MultiValuedCount, &AttributeLength, &Attribute); // Always call the verification routine, regardless of an // error in the extraction routine. NtStatus = SampVerifyFixedLengthAttribute(ObjectType, AttrIndex, MultiValuedCount, AttributeLength); // Append the attribute onto the end of the SAM buffer (i.e. // fill in the members of the fixed-length data structure). // NOTE: This routine assumes that the order of the attributes // returned in the DSATTRBLOCK are correct (i.e. correspond // to the order of the members in the given SAM fixed-length // structure). It also assumes that SAM fixed-length attri- // butes are always single-valued attributes. if (NT_SUCCESS(NtStatus) && (NULL != Attribute)) { NtStatus = SampAppendFixedLengthAttributeToBuffer( ObjectType, AttrIndex, Attribute, *SamAttributes); } else { NtStatus = STATUS_INTERNAL_ERROR; break; } } if (NULL != TotalLength) { *TotalLength = Length; } else { NtStatus = STATUS_INVALID_PARAMETER; } } } return(NtStatus); } // // FIXED LENGTH-TO-ATTRBLOCK CONVERSION ROUTINES // NTSTATUS SampConvertFixedLengthAttributes( IN INT ObjectType, IN PVOID SamAttributes, IN ULONG AttributeCount, OUT PDSATTR Attributes ) /*++ Routine Description: This routine does the work of converting a given SAM fixed-length attri- bute type (i.e. contains all of the fixed-length attributes pertinent to the specified ObjectType) into a DSATTR array. Related DS attribute infor- mation, such as attribute length and type, are also set by this routine. Arguments: ObjectType - Identifies which SAM object type, and hence, which attribute set to work with. SamAttributes - Pointer, updated SAM attribute buffer. AttributeCount - Number of attributes to convert into DSATTRs. Attributes - Pointer, outgoing DSATTR, containing fixed-length attributes. Return Value: STATUS_SUCCESS - The object has been successfully accessed. --*/ { NTSTATUS NtStatus = STATUS_SUCCESS; ULONG Offset = 0; ULONG Length = 0; ULONG Index = 0; PDSATTRVAL Attribute = NULL; PBYTE Value = NULL; ULONG AttrIndex = 0; PSAMP_V1_FIXED_LENGTH_SERVER ServerAttrs = NULL; PSAMP_V1_0A_FIXED_LENGTH_DOMAIN DomainAttrs = NULL; PSAMP_V1_0A_FIXED_LENGTH_GROUP GroupAttrs = NULL; PSAMP_V1_FIXED_LENGTH_ALIAS AliasAttrs = NULL; PSAMP_V1_0A_FIXED_LENGTH_USER UserAttrs = NULL; SAMTRACE("SampConvertFixedLengthAttributes"); for (AttrIndex = 0; AttrIndex < AttributeCount; AttrIndex++) { // BUG: Assuming that all fixed-length attributes are single-valued. // Set the multi-value count to 1 for the fixed-length attribute, and // set its type identifier. Attributes[AttrIndex].AttrVal.valCount = 1; Attributes[AttrIndex].attrTyp = SampFixedAttributeInfo[ObjectType][AttrIndex].Type; // First, allocate a block for the individual DSATTRVAL. Attribute = RtlAllocateHeap(RtlProcessHeap(), 0, sizeof(DSATTRVAL)); if (NULL != Attribute) { RtlZeroMemory(Attribute, sizeof(DSATTRVAL)); Attributes[AttrIndex].AttrVal.pAVal = Attribute; Length = SampFixedAttributeInfo[ObjectType][AttrIndex].Length; // Second, allocate a block for the actual value, and make the // DSATTRVAL point to it. Value = RtlAllocateHeap(RtlProcessHeap(), 0, Length); RtlZeroMemory(Value, Length); if (NULL != Value) { Attribute->pVal = Value; Attribute->valLen = Length; // Then copy the data into the target DS attribute. switch(ObjectType) { case SampServerObjectType: ServerAttrs = SamAttributes; switch(AttrIndex) { case 0: RtlCopyMemory(Value, &(ServerAttrs->RevisionLevel), Length); break; default: NtStatus = STATUS_INTERNAL_ERROR; break; } break; case SampDomainObjectType: DomainAttrs = SamAttributes; switch(AttrIndex) { case 0: RtlCopyMemory(Value, &(DomainAttrs->Revision), Length); break; case 1: RtlCopyMemory(Value, &(DomainAttrs->Unused1), Length); break; case 2: RtlCopyMemory(Value, &(DomainAttrs->CreationTime), Length); break; case 3: RtlCopyMemory(Value, &(DomainAttrs->ModifiedCount), Length); break; case 4: RtlCopyMemory(Value, &(DomainAttrs->MaxPasswordAge), Length); break; case 5: RtlCopyMemory(Value, &(DomainAttrs->MinPasswordAge), Length); break; case 6: RtlCopyMemory(Value, &(DomainAttrs->ForceLogoff), Length); break; case 7: RtlCopyMemory(Value, &(DomainAttrs->LockoutDuration), Length); break; case 8: RtlCopyMemory(Value, &(DomainAttrs->LockoutObservationWindow), Length); break; case 9: RtlCopyMemory(Value, &(DomainAttrs->ModifiedCountAtLastPromotion), Length); break; case 10: RtlCopyMemory(Value, &(DomainAttrs->NextRid), Length); break; case 11: RtlCopyMemory(Value, &(DomainAttrs->PasswordProperties), Length); break; case 12: RtlCopyMemory(Value, &(DomainAttrs->MinPasswordLength), Length); break; case 13: RtlCopyMemory(Value, &(DomainAttrs->PasswordHistoryLength), Length); break; case 14: RtlCopyMemory(Value, &(DomainAttrs->LockoutThreshold), Length); break; case 15: RtlCopyMemory(Value, &(DomainAttrs->ServerState), Length); break; case 16: RtlCopyMemory(Value, &(DomainAttrs->ServerRole), Length); break; case 17: RtlCopyMemory(Value, &(DomainAttrs->UasCompatibilityRequired), Length); break; default: NtStatus = STATUS_INTERNAL_ERROR; break; } break; case SampGroupObjectType: GroupAttrs = SamAttributes; switch(AttrIndex) { case 0: RtlCopyMemory(Value, &(GroupAttrs->Revision), Length); break; case 1: RtlCopyMemory(Value, &(GroupAttrs->RelativeId), Length); break; case 2: RtlCopyMemory(Value, &(GroupAttrs->Attributes), Length); break; case 3: RtlCopyMemory(Value, &(GroupAttrs->Unused1), Length); break; case 4: RtlCopyMemory(Value, &(GroupAttrs->AdminCount), Length); break; case 5: RtlCopyMemory(Value, &(GroupAttrs->OperatorCount), Length); break; default: NtStatus = STATUS_INTERNAL_ERROR; break; } break; case SampAliasObjectType: AliasAttrs = SamAttributes; switch(AttrIndex) { case 0: RtlCopyMemory(Value, &(AliasAttrs->RelativeId), Length); break; default: NtStatus = STATUS_INTERNAL_ERROR; break; } break; case SampUserObjectType: UserAttrs = SamAttributes; switch(AttrIndex) { case 0: RtlCopyMemory(Value, &(UserAttrs->Revision), Length); break; case 1: RtlCopyMemory(Value, &(UserAttrs->Unused1), Length); break; case 2: RtlCopyMemory(Value, &(UserAttrs->LastLogon), Length); break; case 3: RtlCopyMemory(Value, &(UserAttrs->LastLogoff), Length); break; case 4: RtlCopyMemory(Value, &(UserAttrs->PasswordLastSet), Length); break; case 5: RtlCopyMemory(Value, &(UserAttrs->AccountExpires), Length); break; case 6: RtlCopyMemory(Value, &(UserAttrs->LastBadPasswordTime), Length); break; case 7: RtlCopyMemory(Value, &(UserAttrs->UserId), Length); break; case 8: RtlCopyMemory(Value, &(UserAttrs->PrimaryGroupId), Length); break; case 9: RtlCopyMemory(Value, &(UserAttrs->UserAccountControl), Length); break; case 10: RtlCopyMemory(Value, &(UserAttrs->CountryCode), Length); break; case 11: RtlCopyMemory(Value, &(UserAttrs->CodePage), Length); break; case 12: RtlCopyMemory(Value, &(UserAttrs->BadPasswordCount), Length); break; case 13: RtlCopyMemory(Value, &(UserAttrs->LogonCount), Length); break; case 14: RtlCopyMemory(Value, &(UserAttrs->AdminCount), Length); break; case 15: RtlCopyMemory(Value, &(UserAttrs->Unused2), Length); break; case 16: RtlCopyMemory(Value, &(UserAttrs->OperatorCount), Length); break; default: NtStatus = STATUS_INTERNAL_ERROR; break; } break; default: NtStatus = STATUS_INTERNAL_ERROR; break; } } else { NtStatus = STATUS_NO_MEMORY; break; } } else { NtStatus = STATUS_NO_MEMORY; break; } } return(NtStatus); } NTSTATUS SampConvertFixedLengthAttributesToAttrBlock( IN INT ObjectType, IN PVOID SamAttributes, OUT PDSATTRBLOCK *DsAttributes ) /*++ Routine Description: This routine is the top-level routine for converting a SAM fixed-length attribute into a DSATTRBLOCK. Based on the SAM object type, the attribute count is set, and subsequently used to allocate memory for the DS attri- butes. Arguments: ObjectType - Identifies which SAM object type, and hence, which attribute set to work with. SamAttributes - Pointer, updated SAM attribute buffer. DsAttributes - Pointer, outgoing DSATTRBLOCK, containing fixed-length attributes. Return Value: STATUS_SUCCESS - The object has been successfully accessed. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; ULONG AttributeCount = 0; ULONG Length = 0; PDSATTR Attributes = NULL; SAMTRACE("SampConvertFixedLengthAttributesToAttrBlock"); if (NULL != DsAttributes) { // Allocate the top-level DS structure, DSATTRBLOCK. *DsAttributes = RtlAllocateHeap(RtlProcessHeap(), 0, sizeof(DSATTRBLOCK)); // From the SAM object type, set the attribute count. if ((NULL != SamAttributes) && (NULL != *DsAttributes)) { RtlZeroMemory(*DsAttributes, sizeof(DSATTRBLOCK)); switch(ObjectType) { case SampServerObjectType: AttributeCount = SAMP_SERVER_FIXED_ATTR_COUNT; break; case SampDomainObjectType: AttributeCount = SAMP_DOMAIN_FIXED_ATTR_COUNT; break; case SampGroupObjectType: AttributeCount = SAMP_GROUP_FIXED_ATTR_COUNT; break; case SampAliasObjectType: AttributeCount = SAMP_ALIAS_FIXED_ATTR_COUNT; break; case SampUserObjectType: AttributeCount = SAMP_USER_FIXED_ATTR_COUNT; break; default: break; } // Allocate a block for the DSATTR array, then convert the SAM // fixed-length attributes into the DSATTRBLOCK. Length = AttributeCount * sizeof(DSATTR); Attributes = RtlAllocateHeap(RtlProcessHeap(), 0, Length); if (NULL != Attributes) { RtlZeroMemory(Attributes, Length); (*DsAttributes)->attrCount = AttributeCount; (*DsAttributes)->pAttr = Attributes; NtStatus = SampConvertFixedLengthAttributes(ObjectType, SamAttributes, AttributeCount, Attributes); } } } return(NtStatus); } // // ATTRBLOCK-TO-COMBINED BUFFER CONVERSION ROUTINES // NTSTATUS SampWalkAttrBlock( IN ULONG FixedLengthAttributeCount, IN ULONG VarLengthAttributeCount, IN PDSATTRBLOCK DsAttributes, OUT PDSATTRBLOCK *FixedLengthAttributes, OUT PDSATTRBLOCK *VarLengthAttributes ) /*++ Routine Description: This routine scans the DSATTRBLOCK containing the fixed and variable- length attributes, identifying where each starts. Two new DSATTRBLOCK are allocated, one that points to the fixed-length data, while the second points at the variable-length data. Arguments: FixedLengthAttributeCount - Number of fixed-length attributes for this object. VarLengthAttributeCount - Number of variable-length attributes for this object. DsAttributes - Pointer, incoming DSATTRBLOCK, containing all of the attributes. FixedLengthAttributes - Pointer, returned pointer to the first fixed- length attribute. VarLengthAttributes - Pointer, returned pointer to the first variable- length attribute. Return Value: STATUS_SUCCESS - The object has been successfully accessed. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; ULONG AttributeCount = FixedLengthAttributeCount + VarLengthAttributeCount; if ((0 < FixedLengthAttributeCount) && (0 < VarLengthAttributeCount) && (NULL != DsAttributes)) { ASSERT(DsAttributes->attrCount == AttributeCount); if ((NULL != FixedLengthAttributes) && (NULL != VarLengthAttributes)) { // Allocate a new DSATTRBLOCK structure that will point to the // first N DSATTR elements, representing the fixed-length attri- // butes for this SAM object. *FixedLengthAttributes = RtlAllocateHeap(RtlProcessHeap(), 0, sizeof(DSATTRBLOCK)); if (NULL != *FixedLengthAttributes) { RtlZeroMemory(*FixedLengthAttributes, sizeof(DSATTRBLOCK)); // Set the pointer, and attribute count to the number of fixed // length attributes. if (NULL != DsAttributes->pAttr) { (*FixedLengthAttributes)->pAttr = DsAttributes->pAttr; (*FixedLengthAttributes)->attrCount = FixedLengthAttributeCount; // Now, allocate a second DSATTRBLOCK that will point // to the variable-length attributes. *VarLengthAttributes = RtlAllocateHeap(RtlProcessHeap(), 0, sizeof(DSATTRBLOCK)); if (NULL != *VarLengthAttributes) { RtlZeroMemory(*VarLengthAttributes, sizeof(DSATTRBLOCK)); // The remaining M DSATTR elements represent the var- // iable length attributes. Set the pointer, and the // attribute count to the number of variable attrs. (*VarLengthAttributes)->pAttr = DsAttributes->pAttr + FixedLengthAttributeCount; (*VarLengthAttributes)->attrCount = VarLengthAttributeCount; NtStatus = STATUS_SUCCESS; } else { NtStatus = STATUS_NO_MEMORY; } } else { NtStatus = STATUS_INTERNAL_ERROR; } } else { NtStatus = STATUS_NO_MEMORY; } } else { NtStatus = STATUS_INVALID_PARAMETER; } } else { NtStatus = STATUS_INVALID_PARAMETER; } return(NtStatus); } NTSTATUS SampLocateAttributesInAttrBlock( IN INT ObjectType, IN PDSATTRBLOCK DsAttributes, OUT PDSATTRBLOCK *FixedLengthAttributes, OUT PDSATTRBLOCK *VarLengthAttributes ) /*++ Routine Description: This routine determines the number of attributes based on object type, then calls a worker routine to obtain pointers to the fixed-length and variable-length portions of the DSATTRBLOCK. Arguments: ObjectType - Identifies which SAM object type, and hence, which attribute set to work with. DsAttributes - Pointer, incoming DSATTRBLOCK. FixedLengthAttributes - Pointer, returned pointer to the fixed data. VarLengthAttributes - Pointer, returned pointer to the variable data. Return Value: STATUS_SUCCESS - The object has been successfully accessed. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; ULONG FixedLengthAttributeCount = 0; ULONG VarLengthAttributeCount = 0; ULONG AttributeCount = 0; SAMTRACE("SampLocateAttributesInAttrBlock"); // Set the fixed-length, variable-length attribute counts based upon // the object type. switch(ObjectType) { case SampServerObjectType: FixedLengthAttributeCount = SAMP_SERVER_FIXED_ATTR_COUNT; VarLengthAttributeCount = SAMP_SERVER_VARIABLE_ATTRIBUTES; break; case SampDomainObjectType: FixedLengthAttributeCount = SAMP_DOMAIN_FIXED_ATTR_COUNT; VarLengthAttributeCount = SAMP_DOMAIN_VARIABLE_ATTRIBUTES; break; case SampGroupObjectType: FixedLengthAttributeCount = SAMP_GROUP_FIXED_ATTR_COUNT; VarLengthAttributeCount = SAMP_GROUP_VARIABLE_ATTRIBUTES; break; case SampAliasObjectType: FixedLengthAttributeCount = SAMP_ALIAS_FIXED_ATTR_COUNT; VarLengthAttributeCount = SAMP_ALIAS_VARIABLE_ATTRIBUTES; break; case SampUserObjectType: FixedLengthAttributeCount = SAMP_USER_FIXED_ATTR_COUNT; VarLengthAttributeCount = SAMP_USER_VARIABLE_ATTRIBUTES; break; default: break; } AttributeCount = FixedLengthAttributeCount + VarLengthAttributeCount; if (0 < AttributeCount) { NtStatus = SampWalkAttrBlock(FixedLengthAttributeCount, VarLengthAttributeCount, DsAttributes, FixedLengthAttributes, VarLengthAttributes); } return(NtStatus); } NTSTATUS SampCombineSamAttributes( IN PVOID SamFixedLengthAttributes, IN ULONG FixedLength, IN PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamVarLengthAttributes, IN ULONG VarLength, OUT PVOID *SamAttributes ) /*++ Routine Description: This routine combines the SAM fixed and variable-length buffers into a single SAM combined-attribute buffer. Arguments: SamFixedLengthAttributes - Pointer, fixed attributes. FixedLength - Number of bytes. SamVarLengthAttributes - Pointer, variable attributes. VarLength - Number of bytes. SamAttributes - Pointer, returned combined-attribute buffer. Return Value: STATUS_SUCCESS - The object has been successfully accessed. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; ULONG CombinedLength = 0; SAMTRACE("SampCombineSamAttributes"); if ((0 < FixedLength) && (0 < VarLength)) { // Adjust the length so that the appended variable attributes start // on a DWORD boundary. FixedLength = DWORD_ALIGN(FixedLength); CombinedLength = FixedLength + VarLength; if (NULL != SamAttributes) { // Allocate a new buffer for the combined attributes. *SamAttributes = RtlAllocateHeap(RtlProcessHeap(), 0, CombinedLength); if (NULL != *SamAttributes) { RtlZeroMemory(*SamAttributes, CombinedLength); if ((NULL != SamFixedLengthAttributes) && (NULL != SamVarLengthAttributes)) { // BUG: Check return value from RtlCopyMemory. // Copy the fixed-length attributes first... RtlCopyMemory(*SamAttributes, SamFixedLengthAttributes, FixedLength); RtlFreeHeap(RtlProcessHeap(), 0, SamFixedLengthAttributes); // then the variable ones. RtlCopyMemory(((PBYTE)(*SamAttributes)) + FixedLength, SamVarLengthAttributes, VarLength); RtlFreeHeap(RtlProcessHeap(), 0, SamVarLengthAttributes); // BUG: Need to set Object->VariableArrayOffset, etc. NtStatus = STATUS_SUCCESS; } else { NtStatus = STATUS_INVALID_PARAMETER; } } else { NtStatus = STATUS_NO_MEMORY; } } else { NtStatus = STATUS_INVALID_PARAMETER; } } else { NtStatus = STATUS_INVALID_PARAMETER; } return(NtStatus); } NTSTATUS SampConvertAttrBlockToCombinedAttributes( IN INT ObjectType, IN PDSATTRBLOCK DsAttributes, OUT PVOID *SamAttributes, OUT PULONG FixedLength, OUT PULONG VariableLength ) /*++ Routine Description: This routine produces a SAM combined-attribute buffer from a DSATTRBLOCK. It is assumed that the order of the attributes in the DSATTRBLOCK is cor- rect. This means that the fixed-length attributes come first, followed by the variable-length attributes. The attributes within each of these parts must also be correctly ordered, as per samsrvp.h definitions. Arguments: ObjectType - Identifies which SAM object type, and hence, which attribute set to work with. DsAttributes - Pointer, incoming DSATTRBLOCK. SamAttributes - Pointer, returned SAM combined-attribute buffer. FixedLength - Pointer, returned byte count of the fixed-length portion. VariableLength - Pointer, returned byte count of the variable-length portion. Return Value: STATUS_SUCCESS - The object has been successfully accessed. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; PDSATTRBLOCK DsFixedLengthAttributes = NULL; PDSATTRBLOCK DsVarLengthAttributes = NULL; PVOID SamFixedLengthAttributes = NULL; PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamVarLengthAttributes = NULL; ULONG TotalFixedLength = 0; ULONG TotalVarLength = 0; SAMTRACE("SampConvertAttrBlockToCombinedAttributes"); // This routine assumes that the incoming attributes, in DsAttributes, // are correctly ordered. This means that the fixed-length attributes // come first, followed by the variable-length attributes. Within the // fixed or variable sections, the individual attributes must also be // arranged in the same order in which SAM expects the attributes to // appear. Essentially, the order of the attributes must match the order // in which they are arranged in the output SAM buffer (see samsrvp.h // for details). if (NULL != DsAttributes) { // The incoming DSATTRBLOCK is pointing to a set of both fixed-length // and variable-length attributes. SampLocateAttributesInAttrBlock // will return a new DSATTRBLOCK pointer for the fixed-length attri- // butes and a new DSATTRBLOCK pointer for the variable-length attri- // butes, so that these can be subsequently passed onto the conversion // routines. NtStatus = SampLocateAttributesInAttrBlock(ObjectType, DsAttributes, &DsFixedLengthAttributes, &DsVarLengthAttributes); if (NT_SUCCESS(NtStatus) && (NULL != DsFixedLengthAttributes)) { // First, convert the fixed-length attributes... NtStatus = SampConvertAttrBlockToFixedLengthAttributes( ObjectType, DsFixedLengthAttributes, &SamFixedLengthAttributes, &TotalFixedLength); if (NT_SUCCESS(NtStatus) && (NULL != DsVarLengthAttributes)) { // then convert the variable-length attributes. NtStatus = SampConvertAttrBlockToVarLengthAttributes( ObjectType, DsVarLengthAttributes, &SamVarLengthAttributes, &TotalVarLength); if (NT_SUCCESS(NtStatus) && (NULL != SamFixedLengthAttributes) && (NULL != SamVarLengthAttributes)) { // Finally, concatenate the the variable-length attribute // buffer onto the end of the fixed-length buffer, passing // the result back as the SAM combined-attribute buffer. if ((0 < TotalFixedLength) && (0 < TotalVarLength) && (NULL != SamAttributes)) { NtStatus = SampCombineSamAttributes( SamFixedLengthAttributes, TotalFixedLength, SamVarLengthAttributes, TotalVarLength, SamAttributes); ASSERT(NULL != SamAttributes); if (NT_SUCCESS(NtStatus)) { if ((NULL != FixedLength) && (NULL != VariableLength)) { *FixedLength = TotalFixedLength; *VariableLength = TotalVarLength; } else { NtStatus = STATUS_INVALID_PARAMETER; } } else { NtStatus = STATUS_INTERNAL_ERROR; } } else { NtStatus = STATUS_INTERNAL_ERROR; } } else { NtStatus = STATUS_INTERNAL_ERROR; } } else { NtStatus = STATUS_INTERNAL_ERROR; } } else { NtStatus = STATUS_INTERNAL_ERROR; } } else { NtStatus = STATUS_INVALID_PARAMETER; } return(NtStatus); } // // COMBINED BUFFER-TO-ATTRBLOCK CONVERSION ROUTINES // NTSTATUS SampLocateAttributesInSamBuffer( IN INT ObjectType, IN PVOID SamAttributes, IN ULONG FixedLength, IN ULONG VariableLength, OUT PVOID *FixedLengthAttributes, OUT PSAMP_VARIABLE_LENGTH_ATTRIBUTE *VarLengthAttributes ) /*++ Routine Description: This routine finds the start of the fixed-length and variable-length attributes, returning a pointer to each. Arguments: ObjectType - Identifies which SAM object type, and hence, which attribute set to work with. SamAttributes - Pointer, SAM attribute buffer. FixedLength - Number of bytes of fixed-length attributes. VariableLength - Number of bytes of variable-length attributes. FixedLengthAttributes - Pointer, returned pointer to the fixed data. VarLengthAttributes - Pointer, returned pointer to the variable data. Return Value: STATUS_SUCCESS - The object has been successfully accessed. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; SAMTRACE("SampLocateAttributesInSamBuffer"); // BUG: ObjectType and VariableLength are not used in this routine. // These parameters could be used in the future for validation checks. if ((NULL != SamAttributes) && (NULL != FixedLengthAttributes)) { // The fixed-length attributes are in the first part of the overall // buffer. *FixedLengthAttributes = SamAttributes; if (NULL != VarLengthAttributes) { // The variable-length attributes come after the fixed ones. *VarLengthAttributes = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE)(((PBYTE)SamAttributes) + FixedLength); NtStatus = STATUS_SUCCESS; } } return(NtStatus); } NTSTATUS SampCreateDsAttributes( IN INT ObjectType, IN PDSATTRBLOCK DsFixedLengthAttributes, IN ULONG FixedLengthAttributeCount, IN PDSATTRBLOCK DsVarLengthAttributes, IN ULONG VarLengthAttributeCount, OUT PDSATTRBLOCK *DsAttributes ) /*++ Routine Description: This routine does the work of combining two DSATTRBLOCKs into a single DSATTRBLOCK by "concatenating" them together. The routine allocates a new top-level DSATTR array, and then fixes up the pointers to the real attributes, finally releasing the old DSATTR array. Arguments: AttributeCount - Total number of attributes, fixed and variable. DsFixedLengthAttributes - Pointer, the DSATTRBLOCK containing the fixed- length attributes. DsVarLengthAttributes - Pointer, the DSATTRBLOCK containing the variable- length attributes. DsAttributes - Pointer, the outgoing DSATTRBLOCK containing both sets of attributes. Return Value: STATUS_SUCCESS - The object has been successfully accessed. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; PDSATTR Attributes = NULL; PDSATTR FixedAttributes = NULL; PDSATTR VarAttributes = NULL; ULONG AttrIndex = 0; ULONG AttrIndexTmp = 0; ULONG AttributeCount = FixedLengthAttributeCount + VarLengthAttributeCount; if (NULL != DsAttributes) { // Allocate a new top-level DSATTRBLOCK for DsAttributes. *DsAttributes = RtlAllocateHeap(RtlProcessHeap(), 0, sizeof(DSATTRBLOCK)); if (NULL != *DsAttributes) { RtlZeroMemory(*DsAttributes, sizeof(DSATTRBLOCK)); // Allocate the DSATTR array for the attributes. Attributes = RtlAllocateHeap(RtlProcessHeap(), 0, (AttributeCount * sizeof(DSATTR))); if (NULL != Attributes) { RtlZeroMemory(Attributes, (AttributeCount * sizeof(DSATTR))); // Set the return DsAttributes members. (*DsAttributes)->attrCount = AttributeCount; (*DsAttributes)->pAttr = Attributes; if ((NULL != DsFixedLengthAttributes) && (NULL != DsVarLengthAttributes)) { FixedAttributes = DsFixedLengthAttributes->pAttr; VarAttributes = DsVarLengthAttributes->pAttr; if ((NULL != FixedAttributes) && (NULL != VarAttributes)) { // Reset the attribute pointers so that DsAttributes // points to the fixed-length attributes and counts. for (AttrIndex = 0; AttrIndex < FixedLengthAttributeCount; AttrIndex++) { Attributes[AttrIndex].attrTyp = SampFixedAttributeInfo[ObjectType][AttrIndex].Type; Attributes[AttrIndex].AttrVal.valCount = FixedAttributes[AttrIndex].AttrVal.valCount; Attributes[AttrIndex].AttrVal.pAVal = FixedAttributes[AttrIndex].AttrVal.pAVal; } // Save the current attribute index so that the // variable-length attributes can be appended next. AttrIndexTmp = AttrIndex; // Now fix up the variable-length attribute pointers. for (AttrIndex = 0; AttrIndex < VarLengthAttributeCount; AttrIndex++) { Attributes[AttrIndex + AttrIndexTmp].attrTyp = VarAttributes[AttrIndex].attrTyp; Attributes[AttrIndex + AttrIndexTmp].AttrVal.valCount = VarAttributes[AttrIndex].AttrVal.valCount; Attributes[AttrIndex + AttrIndexTmp].AttrVal.pAVal = VarAttributes[AttrIndex].AttrVal.pAVal; } ASSERT(AttrIndex == (AttributeCount-1)); NtStatus = STATUS_SUCCESS; } } // BUG: Need to free FixedAttributes, VarAttributes arrays. } else { NtStatus = STATUS_NO_MEMORY; } } else { NtStatus = STATUS_NO_MEMORY; } } else { NtStatus = STATUS_INVALID_PARAMETER; } return(NtStatus); } NTSTATUS SampCombineDsAttributes( IN INT ObjectType, IN PDSATTRBLOCK DsFixedLengthAttributes, IN PDSATTRBLOCK DsVarLengthAttributes, OUT PDSATTRBLOCK *DsAttributes ) /*++ Routine Description: This routine does the work of combining two DSATTRBLOCKs into a single DSATTRBLOCK by "concatenating" them together. The routine allocates a new top-level DSATTR array, and then fixes up the pointers to the real attributes, finally releasing the old DSATTR array. Arguments: ObjectType - Identifies which SAM object type, and hence, which attribute set to work with. DsFixedLengthAttributes - Pointer, the DSATTRBLOCK containing the fixed- length attributes. DsVarLengthAttributes - Pointer, the DSATTRBLOCK containing the variable- length attributes. DsAttributes - Pointer, the outgoing DSATTRBLOCK containing both sets of attributes. Return Value: STATUS_SUCCESS - The object has been successfully accessed. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; ULONG FixedLengthAttributeCount = 0; ULONG VarLengthAttributeCount = 0; ULONG AttributeCount = 0; SAMTRACE("SampCombineDsAttributes"); // Set the fixed-length, variable-length attribute counts based upon // the object type. switch(ObjectType) { case SampServerObjectType: FixedLengthAttributeCount = SAMP_SERVER_FIXED_ATTR_COUNT; VarLengthAttributeCount = SAMP_SERVER_VARIABLE_ATTRIBUTES; break; case SampDomainObjectType: FixedLengthAttributeCount = SAMP_DOMAIN_FIXED_ATTR_COUNT; VarLengthAttributeCount = SAMP_DOMAIN_VARIABLE_ATTRIBUTES; break; case SampGroupObjectType: FixedLengthAttributeCount = SAMP_GROUP_FIXED_ATTR_COUNT; VarLengthAttributeCount = SAMP_GROUP_VARIABLE_ATTRIBUTES; break; case SampAliasObjectType: FixedLengthAttributeCount = SAMP_ALIAS_FIXED_ATTR_COUNT; VarLengthAttributeCount = SAMP_ALIAS_VARIABLE_ATTRIBUTES; break; case SampUserObjectType: FixedLengthAttributeCount = SAMP_USER_FIXED_ATTR_COUNT; VarLengthAttributeCount = SAMP_USER_VARIABLE_ATTRIBUTES; break; default: // Error case, NtStatus, counts, etc. already set. break; } AttributeCount = FixedLengthAttributeCount + VarLengthAttributeCount; if (0 < AttributeCount) { NtStatus = SampCreateDsAttributes(ObjectType, DsFixedLengthAttributes, FixedLengthAttributeCount, DsVarLengthAttributes, VarLengthAttributeCount, DsAttributes); } return(NtStatus); } NTSTATUS SampConvertCombinedAttributesToAttrBlock( IN INT ObjectType, IN PVOID SamAttributes, IN ULONG FixedLength, IN ULONG VariableLength, OUT PDSATTRBLOCK *DsAttributes ) /*++ Routine Description: This routine converts a SAM combined-attribute buffer into a DSATTRBLOCK containing all of the attributes. A SAM combined buffer contains fixed- length attributes, followed by variable-length attributes (see attr.c for the layout). The resultant DSATTRBLOCK contains the SAM attributes in exactly the order in which they appeared in the input SAM buffer. Arguments: ObjectType - Identifies which SAM object type, and hence, which attribute set to work with. SamAttributes - Pointer, input SAM combined attribute buffer. FixedLength - Number of bytes of the buffer containing the fixed-length attributes. VariableLength - Number of bytes of the buffer containing the variable- length attributes. DsAttributes - Pointer, the returned DSATTRBLOCK containing the SAM attri- butes. Return Value: STATUS_SUCCESS - The object has been successfully accessed. --*/ { NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; PVOID SamFixedLengthAttributes = NULL; PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamVarLengthAttributes = NULL; PDSATTRBLOCK DsFixedLengthAttributes = NULL; PDSATTRBLOCK DsVarLengthAttributes = NULL; SAMTRACE("SampConvertCombinedAttributesToAttrBlock"); if ((NULL != SamAttributes) && (0 < FixedLength) && (0 < VariableLength)) { // Begin by obtaining a two pointers: a pointer to the fixed-length // attributes and a pointer to the variable-length attributes within // the SAM buffer. NtStatus = SampLocateAttributesInSamBuffer(ObjectType, SamAttributes, FixedLength, VariableLength, &SamFixedLengthAttributes, &SamVarLengthAttributes); if (NT_SUCCESS(NtStatus) && (NULL != SamFixedLengthAttributes) && (NULL != SamVarLengthAttributes)) { // First, convert the fixed-length attributes into a DSATTRBLOCK. NtStatus = SampConvertFixedLengthAttributesToAttrBlock( ObjectType, SamFixedLengthAttributes, &DsFixedLengthAttributes); if (NT_SUCCESS(NtStatus) && (NULL != DsFixedLengthAttributes)) { // Then convert the variable-length attributes. NtStatus = SampConvertVarLengthAttributesToAttrBlock( ObjectType, SamVarLengthAttributes, &DsVarLengthAttributes); if (NT_SUCCESS(NtStatus) && (NULL != DsVarLengthAttributes)) { if (NULL != DsAttributes) { // Finally, combine the two DSATTRBLOCKs into a single // DSATTRBLOCK, containing all of the attributes. NtStatus = SampCombineDsAttributes( ObjectType, DsFixedLengthAttributes, DsVarLengthAttributes, DsAttributes); ASSERT(NULL != DsAttributes); } else { NtStatus = STATUS_INVALID_PARAMETER; } } else { NtStatus = STATUS_INTERNAL_ERROR; } } else { NtStatus = STATUS_INTERNAL_ERROR; } } else { NtStatus = STATUS_INTERNAL_ERROR; } } else { NtStatus = STATUS_INVALID_PARAMETER; } return(NtStatus); }