3542 lines
102 KiB
C
3542 lines
102 KiB
C
/*++
|
||
|
||
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 <samsrvp.h>
|
||
#include <dsutilp.h>
|
||
#include <mappings.h>
|
||
#include <objids.h>
|
||
|
||
// 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);
|
||
}
|