2020-09-30 17:12:29 +02:00

6021 lines
162 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
attr.c
Abstract:
This file contains services that manipulate SAM object attributes.
WARNING: Terminology can sometimes be confusing. SAM objects have
attributes (e.g., users have LogonHours, FullName, AcctName,
et cetera). These attributes are stored in the registry
in registry-key-attributes. There is NOT a one-to-one
correllation between object-attributes and registry-key-
attributes. For example, all the fixed-length attributes
of an object are stored in a single registry-key-attribute
(whose name is pointed to by SampFixedAttributeName).
Author:
Jim Kelly (JimK) 26-June-1992
Environment:
User Mode - Win32
Revision History:
ChrisMay 04-Jun-96
Added routines for DS data manipulation.
ChrisMay 10-Jun-96
Rewrote SampStoreObjectAttributes to branch to either the registry
or DS backing store, based on the value of Context->ObjectFlags. Note
that when a context object is created, this member is set to indicate
registry storage by default.
ChrisMay 18-Jun-96
Set FlushVariable flag correctly in SampStoreDsObjectAttributes. Add-
ed routines to validate DS data by making SampValidateAttributes a
wrapper for SampValidateRegAttributes and SampValidateDsAttributes.
Moved SAMP_FIXED/VARIABLE_ATTRIBUTES into dsutilp.h.
ChrisMay 25-Jun-96
Added code to SampValidateDsAttributes to update the SAM context
OnDisk member if the attributes are invalid. Added code to handle
initial case when OnDisk is NULL (new context).
ChrisMay 26-Jun-96
Added code to update the buffer lengths and offsets in the SAMP_-
OBJECT and SAMP_OBJECT_INFORMATION structures after the attribute
buffer (Context.OnDisk) has been updated during SampDsValidateAttri-
butes.
ChrisMay 28-Jun-96
Finished separating the attribute accessor macros to handle both
the registry and DS versions of the attribute buffers.
ChrisMay 02-Jul-96
Corrected attribute-address computation in SampObjectAttributeAddress
for DS attributes. Corrected attribute-offset computation in Samp-
VariableAttributeOffset for DS attributes.
ChrisMay 19-Jul-96
Corrected buffer-length computation in SampDsUpdateContextFixed-
Attributes.
--*/
/*
Each SAM object-type has an Object-type descriptor. This is in a
data structure called SAMP_OBJECT_INFORMATION. This structure
contains information that applies to all instances of that object
type. This includes things like a mask of write operations for
the object type, and a name for the object type to be used in
auditing.
Each instance of an open SAM object has another data structure
used to identify it (called SAMP_OBJECT). The header of this
structure contains information that is common to all object-types
and is there to allow unified object manipulation. This includes
things like the handle to the object's registry key.
There are fields in each of these structures that are there to
allow generic object-attribute support routines to operate. In
SAMP_OBJECT, there is a pointer to a block of allocated memory
housing a copy of the object's attributes as they are stored on-disk.
These attributes are arbitrarily divided into two groups: fixed-length
and variable-length.
One of the fields in SAMP_OBJECT_INFORMATION indicates whether the
fixed-length and variable-length attributes for that object-type
are stored together in a single registry-key-attribute or separately
in two registry-key-attributes.
The registry api for querying and setting registry-key attributes are
rather peculiar in that they require the I/O buffer to include a
description of the data. Even the simplest data structure for reading
attribute values (KEY_VALUE_PARTIAL_INFORMATION) includes 3 ULONGs
before the actual data (TitleIndex, value Type, data length,
and then, finally, the data). To efficiently perform registry i/o,
the in-memory copy of the on-disk object attributes includes room
for this information preceeding the fixed and variable-length attribute
sections of the data.
NOTE: For object classes that store fixed and variable-length
data together, only the KEY_VALUE_PARTIAL_INFORMATION
structure preceeding the fixed-length attributes is used.
The one preceeding the variable-length attributes is
#ifdef'd out.
The structures related to object-attributes look like:
On-Disk Image
+-------------+ SAMP_OBJECT_INFORMATION
+-->|KEY_VALUE_ | +-----------------------+
SAMP_OBJECT | |PARTIAL_ | | |
+-----------+ | |INFORMATION | | (header) |
| | | |-------------| | |
| (header) | | | Fixed-Length|<-----+ | |
| | | | Attributes | | |-----------------------|
|-----------| | | | +-------|-< FixedAttrsOffset |
| OnDisk >-|--+ |-------------+ |-----------------------|
|-----------| |KEY_VALUE_ |<-------------|-< VariableBuffOffset |
| OnDisk | |PARTIAL_ | |-----------------------|
| Control | |INFORMATION | +-------|-< VariableArrayOffset |
| Flags | |(Optional) | | |-----------------------|
|-----------| |-------------| | +----|-< VariableDataOffset |
| | | Variable- |<-----+ | |-----------------------|
| type- | | Length | | |VariableAttributeCount |
| specific | | Attributes | | |-----------------------|
| body | | Array | | |FixedStoredSeparately |
| | |-------------| | |-----------------------|
+-----------+ | Variable- |<--------+ | |
| Length | | |
| Attributes | | o |
| Data | | o |
| | +-----------------------+
| |
+-------------+
The KEY_VALUE_PARTIAL_INFORMATION preceeding the VariableLengthAttributes
array is marked optional because it is only present if fixed-length and
variable-length attribute information is stored separately. In this case,
the VariableBufferOffset field in the SAMP_OBJECT_INFORMATION structure
is set to be zero.
*/
///////////////////////////////////////////////////////////////////////////////
// //
// Includes //
// //
///////////////////////////////////////////////////////////////////////////////
#include <samsrvp.h>
#include <lmcons.h>
#include <nturtl.h>
#include <dsutilp.h>
#include <dslayer.h>
//
// This value indicates the minumum size block of memory to allocate
// when retrieving object attributes from disk.
#define SAMP_MINIMUM_ATTRIBUTE_ALLOC (1000)
//
// This value is used when growing the size of the buffer containing
// object attributes. It represents the amount of free space that
// should be left (approximately) for future growth in the buffer.
//
#define SAMP_MINIMUM_ATTRIBUTE_PAD (200)
//
// The following line enables attribute debugging code
//
//#define SAM_DEBUG_ATTRIBUTES
//#ifdef SAM_DEBUG_ATTRIBUTES
//Boolean that allows us to turn off debugging output
//BOOLEAN SampDebugAttributes = FALSE;
//#endif
// Private debugging display routine is enabled when ATTR_DBG_PRINTF = 1.
#define ATTR_DBG_PRINTF 0
#if (ATTR_DBG_PRINTF == 1)
#define DebugPrint printf
#else
#define DebugPrint
#endif
///////////////////////////////////////////////////////////////////////////////
// //
// private macros //
// //
///////////////////////////////////////////////////////////////////////////////
//
// Macro to round up a ULONG value to be dword aligned
// (i.e., be a multipleof 4).
// Note, this does not handle the case where the Ulong is greater
// than 0xfffffffc.
//
#define SampDwordAlignUlong( v ) (((v)+3) & 0xfffffffc)
//
// Make sure an object type and corresponding variable-length attribute
// index are legitimate.
//
#define SampValidateAttributeIndex( c, i ) { \
ASSERT( ((c)->ObjectType < SampUnknownObjectType) ); \
ASSERT(((i) < SampObjectInformation[(c)->ObjectType].VariableAttributeCount) ); \
}
//
// Test to see if an object's fixed or variable-length attributes
// are in memory.
//
#define SampFixedAttributesValid( c ) ((c)->FixedValid)
#define SampVariableAttributesValid( c ) ((c)->VariableValid)
//
// Get the number of variable-length attributes defined for the
// specified object
//
#define SampVariableAttributeCount( c ) \
(SampObjectInformation[(c)->ObjectType].VariableAttributeCount)
//
// Get the offset of the beginning of the attribute buffers
//
#define SampRegFixedBufferOffset( c ) \
( \
SampObjectInformation[(c)->ObjectType].FixedAttributesOffset \
)
#define SampDsFixedBufferOffset( c ) \
( \
SampObjectInformation[(c)->ObjectType].FixedDsAttributesOffset \
)
#define SampFixedBufferOffset( c ) \
( \
(IsDsObject(c)) ? \
SampDsFixedBufferOffset(c) : SampRegFixedBufferOffset(c) \
)
#define SampRegVariableBufferOffset( c ) \
( \
SampObjectInformation[(c)->ObjectType].VariableBufferOffset \
)
#define SampDsVariableBufferOffset( c ) \
( \
SampObjectInformation[(c)->ObjectType].VariableDsBufferOffset \
)
#define SampVariableBufferOffset( c ) \
( \
(IsDsObject(c)) ? \
SampDsVariableBufferOffset(c) : SampRegVariableBufferOffset(c) \
)
//
// Get the offset of the beginning of the variable data i/o buffer.
// If the fixed and variable-length attributes are stored separately,
// then this will be the lower half of the buffer.
// Otherwise, there is only one buffer, so it is the entire allocated buffer.
//
#define SampRegFixedBufferAddress( c ) \
( \
((PUCHAR)((c)->OnDisk)) + SampFixedBufferOffset( c ) \
)
#define SampDsFixedBufferAddress( c ) \
( \
((PUCHAR)((c)->OnDisk)) + SampDsFixedBufferOffset( c ) \
)
#define SampFixedBufferAddress( c ) \
( \
(IsDsObject(c)) ? \
SampDsFixedBufferAddress(c): SampRegFixedBufferAddress(c) \
)
#define SampRegVariableBufferAddress( c ) \
( \
((PUCHAR)((c)->OnDisk)) + SampVariableBufferOffset( c ) \
)
#define SampDsVariableBufferAddress( c ) \
( \
((PUCHAR)((c)->OnDisk)) + SampDsVariableBufferOffset( c ) \
)
#define SampVariableBufferAddress( c ) \
( \
(IsDsObject(c)) ? \
SampDsVariableBufferAddress(c) : SampRegVariableBufferAddress(c)\
)
//
// Get the offset of the beginning of the variable-length
// attributes discriptors array. This address is dword-aligned.
//
#define SampRegVariableArrayOffset( c ) \
( \
SampObjectInformation[(c)->ObjectType].VariableArrayOffset \
)
#define SampDsVariableArrayOffset( c ) \
( \
SampObjectInformation[(c)->ObjectType].VariableDsArrayOffset \
)
#define SampVariableArrayOffset( c ) \
( \
(IsDsObject(c)) ? \
SampDsVariableArrayOffset(c) : SampRegVariableArrayOffset(c) \
)
//
// Calculate the address of the beginning of the variable-length
// attributes array.
//
#define SampRegVariableArrayAddress( c ) \
( \
(PSAMP_VARIABLE_LENGTH_ATTRIBUTE)((PUCHAR)((c)->OnDisk) + \
SampVariableArrayOffset( c ) ) \
)
#define SampDsVariableArrayAddress( c ) \
( \
(PSAMP_VARIABLE_LENGTH_ATTRIBUTE)((PUCHAR)((c)->OnDisk) + \
SampDsVariableArrayOffset( c ) ) \
)
#define SampVariableArrayAddress( c ) \
( \
(IsDsObject(c)) ? \
SampDsVariableArrayAddress(c) : SampRegVariableArrayAddress(c) \
)
//
// Get the offset of the beginning of the variable-length
// attributes data.
//
#define SampRegVariableDataOffset( c ) \
( \
SampObjectInformation[(c)->ObjectType].VariableDataOffset \
)
#define SampDsVariableDataOffset( c ) \
( \
SampObjectInformation[(c)->ObjectType].VariableDsDataOffset \
)
#define SampVariableDataOffset( c ) \
( \
(IsDsObject(c)) ? \
SampDsVariableDataOffset(c) : SampRegVariableDataOffset(c) \
)
//
// Get the length of the on-disk buffer for holding the variable-length
// attribute array and data. If the fixed and variable-length attributes
// are stored separately, then this will be the lower half of the buffer.
// Otherwise, there is only one buffer, so it is the entire allocated buffer.
//
#define SampRegFixedBufferLength( c ) \
( \
SampObjectInformation[(c)->ObjectType].FixedLengthSize \
)
#define SampDsFixedBufferLength( c ) \
( \
SampObjectInformation[(c)->ObjectType].FixedDsLengthSize \
)
#define SampFixedBufferLength( c ) \
( \
(IsDsObject(c)) ? \
SampDsFixedBufferLength(c) : SampRegFixedBufferLength(c) \
)
#define SampRegVariableBufferLength( c ) \
( \
(c)->OnDiskAllocated - SampVariableBufferOffset( c ) \
)
#define SampDsVariableBufferLength( c ) \
( \
(c)->OnDiskAllocated - SampDsVariableBufferOffset( c ) \
)
#define SampVariableBufferLength( c ) \
( \
(IsDsObject(c)) ? \
SampDsVariableBufferLength(c) : SampRegVariableBufferLength(c) \
)
//
// Return the address of a Qualifier field within the variable-length
// attribute descriptor array.
//
#define SampRegVariableQualifier( c, i ) \
( \
SampVariableArrayAddress( c ) + \
(sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE) * i) \
+ FIELD_OFFSET(SAMP_VARIABLE_LENGTH_ATTRIBUTE, Qualifier) \
)
#define SampDsVariableQualifier( c, i ) \
( \
SampDsVariableArrayAddress( c ) + \
(sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE) * i) \
+ FIELD_OFFSET(SAMP_VARIABLE_LENGTH_ATTRIBUTE, Qualifier) \
)
#define SampVariableQualifier( c, i ) \
( \
(IsDsObject(c)) ? \
SampDsVariableQualifier(c, i) : SampRegVariableQualifier(c, i) \
)
//
// Return the address of the first byte of free space
// in an object's attribute data buffer.
// This will be dword aligned.
//
#define SampRegFirstFreeVariableAddress( c ) \
(PUCHAR)(((PUCHAR)((c)->OnDisk)) + (c)->OnDiskUsed)
#define SampDsFirstFreeVariableAddress( c ) \
(PUCHAR)(((PUCHAR)((c)->OnDisk)) + (c)->OnDiskUsed)
#define SampFirstFreeVariableAddress( c ) \
( \
(IsDsObject(c)) ? \
SampDsFirstFreeVariableAddress(c) : \
SampRegFirstFreeVariableAddress(c) \
)
//
// Get the number of bytes needed to store the entire variable-length
// attribute information on disk.
//
#define SampRegVariableBufferUsedLength( c ) \
( \
(PUCHAR)SampFirstFreeVariableAddress(c) - \
(PUCHAR)SampVariableArrayAddress(c) \
)
#define SampDsVariableBufferUsedLength( c ) \
( \
(PUCHAR)SampDsFirstFreeVariableAddress(c) - \
(PUCHAR)SampDsVariableArrayAddress(c) \
)
#define SampVariableBufferUsedLength( c ) \
( \
(IsDsObject(c)) ? \
SampDsVariableBufferUsedLength(c): \
SampRegVariableBufferUsedLength(c) \
)
///////////////////////////////////////////////////////////////////////////////
// //
// private service prototypes //
// //
///////////////////////////////////////////////////////////////////////////////
NTSTATUS
SampValidateAttributes(
IN PSAMP_OBJECT Context,
IN ULONG AttributeGroup
);
PUCHAR
SampObjectAttributeAddress(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex
);
ULONG
SampObjectAttributeLength(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex
);
PULONG
SampObjectAttributeQualifier(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex
);
NTSTATUS
SampGetAttributeBufferReadInfo(
IN PSAMP_OBJECT Context,
IN ULONG AttributeGroup,
OUT PUCHAR *Buffer,
OUT PULONG BufferLength,
OUT PUNICODE_STRING *KeyAttributeName
);
NTSTATUS
SampExtendAttributeBuffer(
IN PSAMP_OBJECT Context,
IN ULONG NewSize
);
NTSTATUS
SampReadRegistryAttribute(
IN HANDLE Key,
IN PUCHAR Buffer,
IN ULONG BufferLength,
IN PUNICODE_STRING AttributeName,
OUT PULONG RequiredLength
);
NTSTATUS
SampSetVariableAttribute(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex,
IN ULONG Qualifier,
IN PUCHAR Buffer,
IN ULONG Length
);
NTSTATUS
SampUpgradeToCurrentRevision(
IN PSAMP_OBJECT Context,
IN ULONG AttributeGroup,
IN PUCHAR Buffer,
IN ULONG LengthOfDataRead,
IN PULONG TotalRequiredLength
);
#ifdef SAM_DEBUG_ATTRIBUTES
VOID
SampDumpAttributes(
IN PSAMP_OBJECT Context
);
VOID
SampDumpData(
IN PVOID Buffer,
IN ULONG Length
);
#endif
///////////////////////////////////////////////////////////////////////////////
// //
// Public Routines //
// //
///////////////////////////////////////////////////////////////////////////////
VOID
SampInitDsObjectInfoAttributes(
)
/*++
Routine Description:
This routine initializes the offset and length information fields of the
SAM_OBJECT_INFORMATION structure for DS attributes. This structure con-
tains offset and length information for two sets of attributes:
-Those attributes stored in the registry (workstation account info)
-Those attributes stored in the DS (domain or DC account info)
The former set are initialized by SampInitObjectInfoAttriubtes, while this
routine inializes the latter set of information. Regardless of whether the
attributes are persistently stored in the registry or the DS, their in-
memory representation always uses the SAM fixed-length and variable-length
data buffers.
Note that DS data buffers do not contain the KEY_VALUE_PARTIAL_INFORMATION
data because this is registry-specific, hence unnecessary for the DS-based
attributes.
Parameters:
None.
Return Values:
None.
--*/
{
PSAMP_OBJECT_INFORMATION Object;
SAMTRACE("SampInitDsObjectInfoAttributes");
//
// SERVER object attribute information
//
Object = &SampObjectInformation[SampServerObjectType];
// Object->FixedStoredSeparately = SAMP_SERVER_STORED_SEPARATELY;
Object->FixedDsAttributesOffset = 0;
Object->FixedDsLengthSize = sizeof(SAMP_V1_FIXED_LENGTH_SERVER);
#if SAMP_SERVER_STORED_SEPARATELY
Object->VariableDsBufferOffset =
Object->FixedDsAttributesOffset +
SampDwordAlignUlong(Object->FixedDsLengthSize);
Object->VariableDsArrayOffset =
Object->VariableDsBufferOffset + 0;
#else
Object->VariableDsBufferOffset = 0;
Object->VariableDsArrayOffset =
Object->FixedDsAttributesOffset +
SampDwordAlignUlong(Object->FixedDsLengthSize);
#endif //SAMP_SERVER_STORED_SEPARATELY
// Object->VariableAttributeCount = SAMP_SERVER_VARIABLE_ATTRIBUTES;
Object->VariableDsDataOffset =
SampDwordAlignUlong( Object->VariableDsArrayOffset +
(SAMP_SERVER_VARIABLE_ATTRIBUTES *
sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE))
);
//
// DOMAIN object attribute information
//
Object = &SampObjectInformation[SampDomainObjectType];
// Object->FixedStoredSeparately = SAMP_DOMAIN_STORED_SEPARATELY;
Object->FixedDsAttributesOffset = 0;
Object->FixedDsLengthSize = sizeof(SAMP_V1_0A_FIXED_LENGTH_DOMAIN);
#if SAMP_DOMAIN_STORED_SEPARATELY
Object->VariableDsBufferOffset =
Object->FixedDsAttributesOffset +
SampDwordAlignUlong(Object->FixedDsLengthSize);
Object->VariableDsArrayOffset =
Object->VariableDsBufferOffset + 0;
#else
Object->VariableDsBufferOffset = 0;
Object->VariableDsArrayOffset =
Object->FixedDsAttributesOffset +
SampDwordAlignUlong(Object->FixedDsLengthSize);
#endif //SAMP_DOMAIN_STORED_SEPARATELY
// Object->VariableAttributeCount = SAMP_DOMAIN_VARIABLE_ATTRIBUTES;
Object->VariableDsDataOffset =
SampDwordAlignUlong( Object->VariableDsArrayOffset +
(SAMP_DOMAIN_VARIABLE_ATTRIBUTES *
sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE))
);
//
// USER object attribute information
//
Object = &SampObjectInformation[SampUserObjectType];
// Object->FixedStoredSeparately = SAMP_USER_STORED_SEPARATELY;
Object->FixedDsAttributesOffset = 0;
Object->FixedDsLengthSize = sizeof(SAMP_V1_0A_FIXED_LENGTH_USER);
#if SAMP_USER_STORED_SEPARATELY
Object->VariableDsBufferOffset =
Object->FixedDsAttributesOffset +
SampDwordAlignUlong(Object->FixedDsLengthSize);
Object->VariableDsArrayOffset =
Object->VariableDsBufferOffset + 0;
#else
Object->VariableDsBufferOffset = 0;
Object->VariableDsArrayOffset =
Object->FixedDsAttributesOffset +
SampDwordAlignUlong(Object->FixedDsLengthSize);
#endif //SAMP_USER_STORED_SEPARATELY
// Object->VariableAttributeCount = SAMP_USER_VARIABLE_ATTRIBUTES;
Object->VariableDsDataOffset =
SampDwordAlignUlong( Object->VariableDsArrayOffset +
(SAMP_USER_VARIABLE_ATTRIBUTES *
sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE))
);
//
// GROUP object attribute information
//
Object = &SampObjectInformation[SampGroupObjectType];
// Object->FixedStoredSeparately = SAMP_GROUP_STORED_SEPARATELY;
Object->FixedDsAttributesOffset = 0;
Object->FixedDsLengthSize = sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP);
#if SAMP_GROUP_STORED_SEPARATELY
Object->VariableDsBufferOffset =
Object->FixedDsAttributesOffset +
SampDwordAlignUlong(Object->FixedDsLengthSize);
Object->VariableDsArrayOffset =
Object->VariableDsBufferOffset + 0;
#else
Object->VariableDsBufferOffset = 0;
Object->VariableDsArrayOffset =
Object->FixedDsAttributesOffset +
SampDwordAlignUlong(Object->FixedDsLengthSize);
#endif //SAMP_GROUP_STORED_SEPARATELY
// Object->VariableAttributeCount = SAMP_GROUP_VARIABLE_ATTRIBUTES;
Object->VariableDsDataOffset =
SampDwordAlignUlong( Object->VariableDsArrayOffset +
(SAMP_GROUP_VARIABLE_ATTRIBUTES *
sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE))
);
//
// ALIAS object attribute information
//
Object = &SampObjectInformation[SampAliasObjectType];
// Object->FixedStoredSeparately = SAMP_ALIAS_STORED_SEPARATELY;
Object->FixedDsAttributesOffset = 0;
Object->FixedDsLengthSize = sizeof(SAMP_V1_FIXED_LENGTH_ALIAS);
#if SAMP_ALIAS_STORED_SEPARATELY
Object->VariableDsBufferOffset =
Object->FixedDsAttributesOffset +
SampDwordAlignUlong(Object->FixedDsLengthSize);
Object->VariableDsArrayOffset =
Object->VariableDsBufferOffset + 0;
#else
Object->VariableDsBufferOffset = 0;
Object->VariableDsArrayOffset =
Object->FixedDsAttributesOffset +
SampDwordAlignUlong(Object->FixedDsLengthSize);
#endif //SAMP_ALIAS_STORED_SEPARATELY
// Object->VariableAttributeCount = SAMP_ALIAS_VARIABLE_ATTRIBUTES;
Object->VariableDsDataOffset =
SampDwordAlignUlong( Object->VariableDsArrayOffset +
(SAMP_ALIAS_VARIABLE_ATTRIBUTES *
sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE))
);
return;
}
VOID
SampInitObjectInfoAttributes(
)
/*++
This API initializes the attribute field information
of the various object information structures.
Attribute information includes:
FixedStoredSeparately (BOOLEAN)
FixedAttributeOffset (ULONG)
VariableBufferOffset (ULONG)
VariableArrayOffset (ULONG)
VariableDataOffset (ULONG)
FixedLengthSize (ULONG)
VariableAttributeCount (ULONG)
Parameters:
None.
Return Values:
None.
--*/
{
//
// Define the size of the header that is in front of our data when
// we read it back out of the registry.
//
#define KEY_VALUE_HEADER_SIZE (SampDwordAlignUlong( \
FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)))
PSAMP_OBJECT_INFORMATION Object;
SAMTRACE("SampInitObjectInfoAttributes");
//
// SERVER object attribute information
//
Object = &SampObjectInformation[SampServerObjectType];
Object->FixedStoredSeparately = SAMP_SERVER_STORED_SEPARATELY;
Object->FixedAttributesOffset = KEY_VALUE_HEADER_SIZE;
Object->FixedLengthSize = sizeof(SAMP_V1_FIXED_LENGTH_SERVER);
#if SAMP_SERVER_STORED_SEPARATELY
Object->VariableBufferOffset =
Object->FixedAttributesOffset +
SampDwordAlignUlong(Object->FixedLengthSize);
Object->VariableArrayOffset =
Object->VariableBufferOffset + KEY_VALUE_HEADER_SIZE;
#else
Object->VariableBufferOffset = 0;
Object->VariableArrayOffset =
Object->FixedAttributesOffset +
SampDwordAlignUlong(Object->FixedLengthSize);
#endif //SAMP_SERVER_STORED_SEPARATELY
Object->VariableAttributeCount = SAMP_SERVER_VARIABLE_ATTRIBUTES;
Object->VariableDataOffset =
SampDwordAlignUlong( Object->VariableArrayOffset +
(SAMP_SERVER_VARIABLE_ATTRIBUTES *
sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE))
);
//
// DOMAIN object attribute information
//
Object = &SampObjectInformation[SampDomainObjectType];
Object->FixedStoredSeparately = SAMP_DOMAIN_STORED_SEPARATELY;
Object->FixedAttributesOffset = KEY_VALUE_HEADER_SIZE;
Object->FixedLengthSize = sizeof(SAMP_V1_0A_FIXED_LENGTH_DOMAIN);
#if SAMP_DOMAIN_STORED_SEPARATELY
Object->VariableBufferOffset =
Object->FixedAttributesOffset +
SampDwordAlignUlong(Object->FixedLengthSize);
Object->VariableArrayOffset =
Object->VariableBufferOffset + KEY_VALUE_HEADER_SIZE;
#else
Object->VariableBufferOffset = 0;
Object->VariableArrayOffset =
Object->FixedAttributesOffset +
SampDwordAlignUlong(Object->FixedLengthSize);
#endif //SAMP_DOMAIN_STORED_SEPARATELY
Object->VariableAttributeCount = SAMP_DOMAIN_VARIABLE_ATTRIBUTES;
Object->VariableDataOffset =
SampDwordAlignUlong( Object->VariableArrayOffset +
(SAMP_DOMAIN_VARIABLE_ATTRIBUTES *
sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE))
);
//
// USER object attribute information
//
Object = &SampObjectInformation[SampUserObjectType];
Object->FixedStoredSeparately = SAMP_USER_STORED_SEPARATELY;
Object->FixedAttributesOffset = KEY_VALUE_HEADER_SIZE;
Object->FixedLengthSize = sizeof(SAMP_V1_0A_FIXED_LENGTH_USER);
#if SAMP_USER_STORED_SEPARATELY
Object->VariableBufferOffset =
Object->FixedAttributesOffset +
SampDwordAlignUlong(Object->FixedLengthSize);
Object->VariableArrayOffset =
Object->VariableBufferOffset + KEY_VALUE_HEADER_SIZE;
#else
Object->VariableBufferOffset = 0;
Object->VariableArrayOffset =
Object->FixedAttributesOffset +
SampDwordAlignUlong(Object->FixedLengthSize);
#endif //SAMP_USER_STORED_SEPARATELY
Object->VariableAttributeCount = SAMP_USER_VARIABLE_ATTRIBUTES;
Object->VariableDataOffset =
SampDwordAlignUlong( Object->VariableArrayOffset +
(SAMP_USER_VARIABLE_ATTRIBUTES *
sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE))
);
//
// GROUP object attribute information
//
Object = &SampObjectInformation[SampGroupObjectType];
Object->FixedStoredSeparately = SAMP_GROUP_STORED_SEPARATELY;
Object->FixedAttributesOffset = KEY_VALUE_HEADER_SIZE;
Object->FixedLengthSize = sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP);
#if SAMP_GROUP_STORED_SEPARATELY
Object->VariableBufferOffset =
Object->FixedAttributesOffset +
SampDwordAlignUlong(Object->FixedLengthSize);
Object->VariableArrayOffset =
Object->VariableBufferOffset + KEY_VALUE_HEADER_SIZE;
#else
Object->VariableBufferOffset = 0;
Object->VariableArrayOffset =
Object->FixedAttributesOffset +
SampDwordAlignUlong(Object->FixedLengthSize);
#endif //SAMP_GROUP_STORED_SEPARATELY
Object->VariableAttributeCount = SAMP_GROUP_VARIABLE_ATTRIBUTES;
Object->VariableDataOffset =
SampDwordAlignUlong( Object->VariableArrayOffset +
(SAMP_GROUP_VARIABLE_ATTRIBUTES *
sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE))
);
//
// ALIAS object attribute information
//
Object = &SampObjectInformation[SampAliasObjectType];
Object->FixedStoredSeparately = SAMP_ALIAS_STORED_SEPARATELY;
Object->FixedAttributesOffset = KEY_VALUE_HEADER_SIZE;
Object->FixedLengthSize = sizeof(SAMP_V1_FIXED_LENGTH_ALIAS);
#if SAMP_ALIAS_STORED_SEPARATELY
Object->VariableBufferOffset =
Object->FixedAttributesOffset +
SampDwordAlignUlong(Object->FixedLengthSize);
Object->VariableArrayOffset =
Object->VariableBufferOffset + KEY_VALUE_HEADER_SIZE;
#else
Object->VariableBufferOffset = 0;
Object->VariableArrayOffset =
Object->FixedAttributesOffset +
SampDwordAlignUlong(Object->FixedLengthSize);
#endif //SAMP_ALIAS_STORED_SEPARATELY
Object->VariableAttributeCount = SAMP_ALIAS_VARIABLE_ATTRIBUTES;
Object->VariableDataOffset =
SampDwordAlignUlong( Object->VariableArrayOffset +
(SAMP_ALIAS_VARIABLE_ATTRIBUTES *
sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE))
);
// Initialize the DS-specific buffer offsets and lengths.
SampInitDsObjectInfoAttributes();
return;
}
NTSTATUS
SampStoreDsObjectAttributes(
IN PSAMP_OBJECT Context
)
/*++
Routine Description:
This routine does the work of writing the SAM attributes out to the DS
backing store. Determination is made as to whether the fixed, or vari-
able, or both sets of attributes are dirty and valid. If so, then they
are updated in the backing store. The SAM attributes are first converted
into a DSATTRBLOCK so that they can be written to storage. The dirty
flags are updated accordingly.
Arguments:
Context - Pointer, the object's SAM context.
Return Value:
STATUS_SUCCESS - storage was updated without a problem, otherwise an
error code is returned.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
BOOLEAN FlushFixed = FALSE;
BOOLEAN FlushVariable = FALSE;
INT ObjectType = Context->ObjectType;
ULONG Flags = 0;
PDSNAME DsObjectName = Context->ObjectNameInDs;
PDSATTRBLOCK AttributeBlock = NULL;
SAMTRACE("SampStoreDsObjectAttributes");
// Determine which attributes (fixed or variable) need to be written to
// storage.
if (Context->FixedValid && Context->FixedDirty)
{
FlushFixed = TRUE;
}
if (Context->VariableValid && Context->VariableDirty)
{
FlushVariable = TRUE;
}
if (NULL != (Context->OnDisk))
{
// Determine whether the attributes are stored separately or combined.
if (TRUE == SampObjectInformation[ObjectType].FixedStoredSeparately)
{
if (TRUE == FlushFixed)
{
// Get a pointer to the start of the fixed-length attributes
// (note that this should always be the same address as the
// Context->OnDisk address for DS-based attributes) and con-
// them into a DSATTRBLOCK.
NtStatus = SampConvertFixedLengthAttributesToAttrBlock(
ObjectType,
SampDsFixedBufferAddress(Context),
&AttributeBlock);
if (NT_SUCCESS(NtStatus))
{
if (NULL != AttributeBlock)
{
// Write the fixed-length attributes to storage.
NtStatus = SampDsSetAttributes(DsObjectName,
Flags,
ObjectType,
AttributeBlock);
if (NT_SUCCESS(NtStatus))
{
Context->FixedDirty = FALSE;
}
}
else
{
NtStatus = STATUS_INTERNAL_ERROR;
}
}
}
if (NT_SUCCESS(NtStatus) && (TRUE == FlushVariable))
{
// Get a pointer to the variable-length attributes and convert
// them into a DSATTRBLOCK.
NtStatus = SampConvertVarLengthAttributesToAttrBlock(
ObjectType,
SampDsVariableArrayAddress(Context),
&AttributeBlock);
if (NT_SUCCESS(NtStatus))
{
if (NULL != AttributeBlock)
{
NtStatus = SampDsSetAttributes(DsObjectName,
Flags,
ObjectType,
AttributeBlock);
if (NT_SUCCESS(NtStatus))
{
Context->VariableDirty = FALSE;
}
}
else
{
NtStatus = STATUS_INTERNAL_ERROR;
}
}
}
}
else
{
// The attributes are combined, check the flush status.
if ((TRUE == FlushFixed) || (TRUE == FlushVariable))
{
// Get a pointer to the combined (i.e. fixed and variable-
// length) attributes and convert them into a DSATTRBLOCK.
// BUG: Where is OnDiskAllocated, OnDiskUsed, etc. set?
NtStatus = SampConvertCombinedAttributesToAttrBlock(
ObjectType,
Context->OnDisk,
SampDsFixedBufferLength(Context),
SampDsVariableBufferLength(Context),
&AttributeBlock);
if (NT_SUCCESS(NtStatus))
{
if (NULL != AttributeBlock)
{
NtStatus = SampDsSetAttributes(DsObjectName,
Flags,
ObjectType,
AttributeBlock);
if (NT_SUCCESS(NtStatus))
{
Context->FixedDirty = FALSE;
Context->VariableDirty = FALSE;
}
}
else
{
NtStatus = STATUS_INTERNAL_ERROR;
}
}
}
}
}
return(NtStatus);
}
NTSTATUS
SampStoreRegObjectAttributes(
IN PSAMP_OBJECT Context,
IN BOOLEAN UseKeyHandle
)
/*++
This API is used to store an object's attributes onto
backing store.
The object attributes are not flushed to disk with this
routine. They are just added to the RXACT.
Parameters:
Context - Pointer to an object context block.
UseKeyHandle - If TRUE, the RootKey in the context block is passed
to the transaction code - this assumes that the key
will still be open when the transaction is committed.
If FALSE, the RootKey will be ignored and the transaction code will
open a key for itself.
Return Values:
STATUS_SUCCESS - The service completed successfully.
Other status values as may be returned by the RXACT services.
--*/
{
NTSTATUS
NtStatus;
BOOLEAN
FlushFixed = FALSE,
FlushVariable = FALSE;
HANDLE
RootKey;
SAMTRACE("SampStoreRegObjectAttributes");
//
// See if anything is dirty and needs to be stored
//
if (Context->FixedValid && Context->FixedDirty) {
FlushFixed = TRUE;
}
if (Context->VariableValid && Context->VariableDirty) {
FlushVariable = TRUE;
}
if (!(FlushFixed || FlushVariable)) {
return(STATUS_SUCCESS);
}
//
// Calculate the RootKey to pass to the transaction code
//
if (UseKeyHandle) {
RootKey = Context->RootKey;
} else {
RootKey = INVALID_HANDLE_VALUE;
}
//
// We keep an open domain context that is used to modify the change
// count whenever a change is made. But if this is a domain change
// here, then that change will overwrite this one. Check for that
// case, and copy this fixed data to the open domain context. Note
// that the open domain's variable data never gets changed.
//
if ( ( Context->ObjectType == SampDomainObjectType ) &&
( Context != SampDefinedDomains[Context->DomainIndex].Context ) ) {
PSAMP_OBJECT DefinedContext;
//
// Get a pointer to the corresponding open defined domain.
// No changes should have been made to its data.
//
DefinedContext = SampDefinedDomains[Context->DomainIndex].Context;
ASSERT( DefinedContext->FixedValid == TRUE );
ASSERT( DefinedContext->FixedDirty == FALSE );
#if DBG
if ( DefinedContext->VariableValid ) {
ASSERT( DefinedContext->VariableDirty == FALSE );
}
#endif
DefinedContext->VariableDirty = FALSE;
//
// Copy our fixed data over the defined domain's fixed data.
// Note that we're assuming that the fixed and variable data are
// stored separately.
//
ASSERT(SampObjectInformation[SampDomainObjectType].FixedStoredSeparately);
RtlCopyMemory(
SampFixedBufferAddress( DefinedContext ),
SampFixedBufferAddress( Context ),
SampFixedBufferLength( Context )
);
//
// No need to flush this context's fixed data, since the commit
// code will flush the same stuff (plus an altered modified count).
//
FlushFixed = FALSE;
Context->FixedDirty = FALSE;
}
//
// One or more of the attributes needs to be stored.
//
if (!SampObjectInformation[Context->ObjectType].FixedStoredSeparately) {
//
// fixed and variable-length attributes stored together.
// Note - strip off the partial key info struct from the start
//
SampDumpRXact(SampRXactContext,
RtlRXactOperationSetValue,
&(Context->RootName),
RootKey,
&SampCombinedAttributeName,
REG_BINARY,
SampFixedBufferAddress(Context),
Context->OnDiskUsed - SampFixedBufferOffset(Context),
FixedBufferAddressFlag);
NtStatus = RtlAddAttributeActionToRXact(
SampRXactContext,
RtlRXactOperationSetValue,
&(Context->RootName),
RootKey,
&SampCombinedAttributeName,
REG_BINARY,
SampFixedBufferAddress(Context),
Context->OnDiskUsed - SampFixedBufferOffset(Context)
);
#if SAMP_DIAGNOSTICS
if (!NT_SUCCESS(NtStatus)) {
SampDiagPrint( DISPLAY_STORAGE_FAIL,
("SAM: Failed to add action to RXact (0x%lx)\n",
NtStatus) );
IF_SAMP_GLOBAL( BREAK_ON_STORAGE_FAIL ) {
ASSERT(NT_SUCCESS(NtStatus));
}
}
#endif //SAMP_DIAGNOSTICS
if ( NT_SUCCESS( NtStatus ) ) {
Context->FixedDirty = FALSE;
Context->VariableDirty = FALSE;
}
} else {
//
// fixed and variable-length attributes stored separately.
// Only update the one(s) we need to.
//
NtStatus = STATUS_SUCCESS;
if (FlushFixed) {
SampDumpRXact(SampRXactContext,
RtlRXactOperationSetValue,
&(Context->RootName),
RootKey,
&SampFixedAttributeName,
REG_BINARY,
SampFixedBufferAddress(Context),
SampVariableBufferOffset(Context) - SampFixedBufferOffset(Context),
FixedBufferAddressFlag);
NtStatus = RtlAddAttributeActionToRXact(
SampRXactContext,
RtlRXactOperationSetValue,
&(Context->RootName),
RootKey,
&SampFixedAttributeName,
REG_BINARY,
SampFixedBufferAddress(Context),
SampVariableBufferOffset(Context) - SampFixedBufferOffset(Context)
);
#if SAMP_DIAGNOSTICS
if (!NT_SUCCESS(NtStatus)) {
SampDiagPrint( DISPLAY_STORAGE_FAIL,
("SAM: Failed to add action to RXact (0x%lx)\n",
NtStatus) );
IF_SAMP_GLOBAL( BREAK_ON_STORAGE_FAIL ) {
ASSERT(NT_SUCCESS(NtStatus));
}
}
#endif //SAMP_DIAGNOSTICS
if ( NT_SUCCESS( NtStatus ) ) {
Context->FixedDirty = FALSE;
}
}
if (NT_SUCCESS(NtStatus) && FlushVariable) {
SampDumpRXact(SampRXactContext,
RtlRXactOperationSetValue,
&(Context->RootName),
RootKey,
&SampVariableAttributeName,
REG_BINARY,
(PUCHAR)SampVariableArrayAddress(Context),
SampVariableBufferUsedLength(Context),
VARIABLE_LENGTH_ATTRIBUTE_FLAG);
NtStatus = RtlAddAttributeActionToRXact(
SampRXactContext,
RtlRXactOperationSetValue,
&(Context->RootName),
RootKey,
&SampVariableAttributeName,
REG_BINARY,
SampVariableArrayAddress( Context ),
SampVariableBufferUsedLength(Context)
);
#if SAMP_DIAGNOSTICS
if (!NT_SUCCESS(NtStatus)) {
SampDiagPrint( DISPLAY_STORAGE_FAIL,
("SAM: Failed to add action to RXact (0x%lx)\n",
NtStatus) );
IF_SAMP_GLOBAL( BREAK_ON_STORAGE_FAIL ) {
ASSERT(NT_SUCCESS(NtStatus));
}
}
#endif //SAMP_DIAGNOSTICS
if ( NT_SUCCESS( NtStatus ) ) {
Context->VariableDirty = FALSE;
}
}
}
return(NtStatus);
}
NTSTATUS
SampStoreObjectAttributes(
IN PSAMP_OBJECT Context,
IN BOOLEAN UseKeyHandle
)
/*++
Routine Description:
This routine determines from the object context whether to update object
attributes residing in the registry or in the DS backing store, and then
calls the appropriate routine to do the work.
Arguments:
Context - Pointer, the object's SAM context.
UseKeyHandle - Flag indicating that the registry key handle should be
used (if this is a registry update--it is not used in DS updates).
Return Value:
STATUS_SUCCESS - storage was updated without a problem, otherwise an
error code is returned.
--*/
{
NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
SAMTRACE("SampStoreObjectAttributes");
if (NULL != Context)
{
if (IsDsObject(Context))
{
NtStatus = SampStoreDsObjectAttributes(Context);
}
else
{
NtStatus = SampStoreRegObjectAttributes(Context, UseKeyHandle);
}
}
else
{
NtStatus = STATUS_INVALID_PARAMETER;
}
return(NtStatus);
}
NTSTATUS
SampDeleteAttributeKeys(
IN PSAMP_OBJECT Context
)
/*++
This API is used to delete the attribute keys that are created in the
registry underneath a SAM object.
Parameters:
Context - Pointer to an object context block.
Return Values:
STATUS_SUCCESS - The service completed successfully.
Error status may be returned by registry calls.
--*/
{
UNICODE_STRING
KeyName;
NTSTATUS
NtStatus;
SAMTRACE("SampDeleteAttributeKeys");
if (SampObjectInformation[Context->ObjectType].FixedStoredSeparately) {
//
// Must delete both fixed and variable attribute keys.
//
NtStatus = SampBuildAccountSubKeyName(
SampUserObjectType,
&KeyName,
Context->TypeBody.User.Rid,
&SampFixedAttributeName
);
if (NT_SUCCESS(NtStatus)) {
NtStatus = RtlAddActionToRXact(
SampRXactContext,
RtlRXactOperationDelete,
&KeyName,
0,
NULL,
0
);
SampFreeUnicodeString( &KeyName );
NtStatus = SampBuildAccountSubKeyName(
SampUserObjectType,
&KeyName,
Context->TypeBody.User.Rid,
&SampVariableAttributeName
);
if (NT_SUCCESS(NtStatus)) {
NtStatus = RtlAddActionToRXact(
SampRXactContext,
RtlRXactOperationDelete,
&KeyName,
0,
NULL,
0
);
SampFreeUnicodeString( &KeyName );
}
}
} else {
//
// Must delete the combined attribute key.
//
NtStatus = SampBuildAccountSubKeyName(
SampUserObjectType,
&KeyName,
Context->TypeBody.User.Rid,
&SampCombinedAttributeName
);
if (NT_SUCCESS(NtStatus)) {
NtStatus = RtlAddActionToRXact(
SampRXactContext,
RtlRXactOperationDelete,
&KeyName,
0,
NULL,
0
);
SampFreeUnicodeString( &KeyName );
}
}
return( NtStatus );
}
NTSTATUS
SampGetFixedAttributes(
IN PSAMP_OBJECT Context,
IN BOOLEAN MakeCopy,
OUT PVOID *FixedData
)
/*++
This API is used to get a pointer to the fixed-length attributes.
Parameters:
Context - Pointer to an object context block.
FixedData - Receives a pointer to the fixed-length data.
Return Values:
STATUS_SUCCESS - The service completed successfully.
STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not
be allocated.
--*/
{
NTSTATUS
NtStatus;
SAMTRACE("SampGetFixedAttributes");
//
// Make the data valid
//
NtStatus = SampValidateAttributes( Context, SAMP_FIXED_ATTRIBUTES );
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Return a pointer to the fixed-length attributes.
//
if (MakeCopy == FALSE) {
*FixedData = (PVOID)SampFixedBufferAddress( Context );
return(STATUS_SUCCESS);
}
//
// Need to make a copy of the fixed data
//
*FixedData = (PVOID)MIDL_user_allocate( SampFixedBufferLength( Context ) );
if ((*FixedData) == NULL) {
return(STATUS_NO_MEMORY);
}
RtlCopyMemory( *FixedData,
SampFixedBufferAddress( Context ),
SampFixedBufferLength( Context ) );
return(STATUS_SUCCESS);
}
NTSTATUS
SampSetFixedAttributes(
IN PSAMP_OBJECT Context,
IN PVOID FixedData
)
/*++
This API is used to replace the fixed-length data attribute.
Parameters:
Context - Pointer to an object context block.
Return Values:
STATUS_SUCCESS - The service completed successfully.
--*/
{
NTSTATUS
NtStatus;
SAMTRACE("SampSetFixedAttributes");
//
// Make the fixed-length data valid
//
NtStatus = SampValidateAttributes( Context, SAMP_FIXED_ATTRIBUTES );
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
if ( FixedData != SampFixedBufferAddress( Context ) ) {
//
// The caller had a copy of the data, so we must copy the changes
// over our data buffer.
//
RtlCopyMemory( SampFixedBufferAddress( Context ),
FixedData,
SampFixedBufferLength( Context ) );
}
//
// Mark the buffer dirty now and it will get flushed when the
// changes are committed.
//
Context->FixedDirty = TRUE;
return( NtStatus );
}
NTSTATUS
SampGetUnicodeStringAttribute(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex,
IN BOOLEAN MakeCopy,
OUT PUNICODE_STRING UnicodeAttribute
)
/*++
This API is used to get a copy of a UNICODE_STRING attribute or a
pointer to the attribute. If a pointer to the attribute is sought,
care must be taken to ensure the pointer is not used after it becomes
invalid. Actions that may cause an attribute pointer to become invalid
include setting an attribute value or dereferencing and then referencing the
object again.
If MakeCopy is FALSE, indicating the string is to be referenced rather
than copied, then only the body of the string is referenced. The lengths
and pointer are set in the provided argument.
Parameters:
Context - Pointer to an object context block.
AttributeIndex - Indicates the index (into the variable length attribute
array) of the attribute to be retrieved as a UNICODE_STRING.
MakeCopy - If TRUE, indicates that a copy of the attribute is to be made.
If FALSE, indicates a pointer to the attribute is desired without
making a copy. WARNING, if this is FALSE, the pointer is only
valid while the in-memory copy of the attribute remains in place.
Addition or replacement of any variable length attribute may
cause the attribute to be moved, and previously returned pointers
invalidated.
UnicodeAttribute - Receives a pointer to the UNICODE_STRING. If
MakeCopy was specified as TRUE, then this pointer points to a block
of memory allocated with MIDL_user_allocate() which the caller is
responsible for freeing (using MIDL_user_free()).
Return Values:
STATUS_SUCCESS - The service completed successfully.
STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not
be allocated.
--*/
{
NTSTATUS NtStatus;
ULONG Length;
SAMTRACE("SampGetUnicodeStringAttribute");
//
// Make sure the requested attribute exists for the specified
// object type.
//
SampValidateAttributeIndex( Context, AttributeIndex );
//
// Make the data valid
//
NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES );
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Get the length of the attribute
//
Length = SampObjectAttributeLength( Context, AttributeIndex );
ASSERT(Length <= 0xFFFF);
UnicodeAttribute->MaximumLength = (USHORT)Length;
UnicodeAttribute->Length = (USHORT)Length;
//
// If we are not to allocate memory, then just return a pointer
// to the attribute.
//
if (MakeCopy == FALSE) {
UnicodeAttribute->Buffer =
(PWSTR)SampObjectAttributeAddress( Context, AttributeIndex );
return(STATUS_SUCCESS);
}
//
// Need to make a copy of the attribute
//
// NOTE: We should test for zero length here and return a NULL pointer
// in that case, but this change would require verification of all of the
// callers of this routine, so I'm leaving it as is.
//
UnicodeAttribute->Buffer = (PSID)MIDL_user_allocate( Length );
if ((UnicodeAttribute->Buffer) == NULL) {
return(STATUS_NO_MEMORY);
}
RtlCopyMemory(
UnicodeAttribute->Buffer,
SampObjectAttributeAddress( Context, AttributeIndex ),
Length
);
return(STATUS_SUCCESS);
}
NTSTATUS
SampSetUnicodeStringAttribute(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex,
IN PUNICODE_STRING Attribute
)
/*++
This API is used to replace a UNICODE_STRING attribute in an
object's variable length attributes.
Parameters:
Context - Pointer to an object context block.
AttributeIndex - Indicates the index (into the variable length attribute
array) of the attribute to be set as a UNICODE_STRING.
Attribute - Points to the new UNICODE_STRING value.
Return Values:
STATUS_SUCCESS - The service completed successfully.
STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not
be allocated.
--*/
{
NTSTATUS
NtStatus;
SAMTRACE("SampSetUnicodeStringAttribute");
//
// Make sure the requested attribute exists for the specified
// object type.
//
SampValidateAttributeIndex( Context, AttributeIndex );
//
// Make the variable-length data valid
//
NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES );
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Set the new attribute value...
//
NtStatus = SampSetVariableAttribute(
Context,
AttributeIndex,
0, // Qualifier not used
(PUCHAR)Attribute->Buffer,
Attribute->Length
);
return(NtStatus);
}
NTSTATUS
SampGetSidAttribute(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex,
IN BOOLEAN MakeCopy,
OUT PSID *Sid
)
/*++
This API is used to get a copy of a SID attribute or a pointer to
the attribute. If a pointer to the attribute is sought, care must
be taken to ensure the pointer is not used after it becomes invalid.
Actions that may cause an attribute pointer to become invalid include
setting an attribute value or dereferencing and then referencing the
object again.
Parameters:
Context - Pointer to an object context block.
AttributeIndex - Indicates the index (into the variable length attribute
array) of the attribute to be retrieved as a SID.
MakeCopy - If TRUE, indicates that a copy of the SID is to be made.
If FALSE, indicates a pointer to the SID is desired without
making a copy. WARNING, if this is FALSE, the pointer is only
valid while the in-memory copy of the SID remains in place.
Addition or replacement of any variable length attribute may
cause the SID to be moved, and previously returned pointers
invalidated.
Sid - Receives a pointer to the SID. If MakeCopy was specified, then
this pointer points to a block of memory allocated with
MIDL_user_allocate() which the caller is responsible for freeing
(using MIDL_user_free()).
Return Values:
STATUS_SUCCESS - The service completed successfully.
STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not
be allocated.
--*/
{
NTSTATUS
NtStatus;
ULONG
Length;
PSID
SidAttribute;
SAMTRACE("SampGetSidAttribute");
//
// Make sure the requested attribute exists for the specified
// object type.
//
SampValidateAttributeIndex( Context, AttributeIndex );
//
// Make the variable-length data valid
//
NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES );
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Get the address of the attribute in question
//
SidAttribute = (PSID)SampObjectAttributeAddress( Context, AttributeIndex );
//
// If we are not to allocate memory, then just return a pointer
// to the attribute.
//
if (MakeCopy == FALSE) {
(*Sid) = SidAttribute;
return(STATUS_SUCCESS);
}
//
// Need to make a copy of the SID
//
Length = SampObjectAttributeLength( Context, AttributeIndex );
ASSERT(Length == RtlLengthSid( SidAttribute ) );
(*Sid) = (PSID)MIDL_user_allocate( Length );
if ((*Sid) == NULL) {
return(STATUS_NO_MEMORY);
}
NtStatus = RtlCopySid( Length, (*Sid), SidAttribute );
ASSERT(NT_SUCCESS(NtStatus));
return(NtStatus);
}
NTSTATUS
SampSetSidAttribute(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex,
IN PSID Attribute
)
/*++
This API is used to replace a SID attribute in an object's variable
length attributes.
Parameters:
Context - Pointer to an object context block.
AttributeIndex - Indicates the index (into the variable length attribute
array) of the attribute to be set as a SID.
Attribute - Points to the new SID value.
Length - The length of the new attribute value (in bytes).
Return Values:
STATUS_SUCCESS - The service completed successfully.
STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not
be allocated.
--*/
{
NTSTATUS
NtStatus;
SAMTRACE("SampSetSidAttribute");
//
// Validate the passed SID
//
ASSERT(RtlValidSid(Attribute));
//
// Make sure the requested attribute exists for the specified
// object type.
//
SampValidateAttributeIndex( Context, AttributeIndex );
//
// Make the variable-length data valid
//
NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES );
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Set the new attribute value...
//
NtStatus = SampSetVariableAttribute(
Context,
AttributeIndex,
0, // Qualifier not used
(PUCHAR)Attribute,
RtlLengthSid(Attribute)
);
return(NtStatus);
}
NTSTATUS
SampGetAccessAttribute(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex,
IN BOOLEAN MakeCopy,
OUT PULONG Revision,
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor
)
/*++
This API is used to get a copy of the object access information.
This includes the security descriptor and revision level of the
object.
Parameters:
Context - Pointer to an object context block.
AttributeIndex - Indicates the index (into the variable length attribute
array) of the attribute to be retrieved.
MakeCopy - If TRUE, indicates that a copy of the attribute is to be made.
If FALSE, indicates a pointer to the attribute is desired without
making a copy. WARNING, if this is FALSE, the pointer is only
valid while the in-memory copy of the attribute remains in place.
Addition or replacement of any variable length attribute may
cause the attribute to be moved, and previously returned pointers
invalidated.
Revision - Receives the revision level from the access information.
SecurityDescriptor - Receives a pointer to the attribute. If MakeCopy
was specified as TRUE, then this pointer points to a block of memory
allocated with MIDL_user_allocate() which the caller is responsible
for freeing (using MIDL_user_free()).
Return Values:
STATUS_SUCCESS - The service completed successfully.
STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not
be allocated.
--*/
{
NTSTATUS
NtStatus;
ULONG
Length;
PVOID
RawAttribute;
SAMTRACE("SampGetAccessAttribute");
//
// Make sure the requested attribute exists for the specified
// object type.
//
SampValidateAttributeIndex( Context, AttributeIndex );
//
// Make the data valid
//
NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES );
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Get the revision level from the qualifier field of the variable
// array entry.
//
(*Revision) = *(SampObjectAttributeQualifier( Context, AttributeIndex ));
//
// Get the address of the attribute in question
//
RawAttribute = (PVOID)SampObjectAttributeAddress( Context, AttributeIndex );
//
// If we are not to allocate memory, then just return a pointer
// to the attribute.
//
if (MakeCopy == FALSE) {
(*SecurityDescriptor) = (PSECURITY_DESCRIPTOR)RawAttribute;
return(STATUS_SUCCESS);
}
//
// Need to make a copy of the attribute
//
Length = SampObjectAttributeLength( Context, AttributeIndex );
(*SecurityDescriptor) = (PSECURITY_DESCRIPTOR)MIDL_user_allocate( Length );
if ((*SecurityDescriptor) == NULL) {
return(STATUS_NO_MEMORY);
}
RtlCopyMemory( (*SecurityDescriptor), RawAttribute, Length );
return(STATUS_SUCCESS);
}
NTSTATUS
SampSetAccessAttribute(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex,
IN PSECURITY_DESCRIPTOR Attribute,
IN ULONG Length
)
/*++
This API is used to replace a SECURITY_DESCRIPTOR attribute in
an object's variable length attributes.
Parameters:
Context - Pointer to an object context block.
AttributeIndex - Indicates the index (into the variable length attribute
array) of the attribute to be set as a SECURITY_DESCRIPTOR.
Attribute - Points to the new SECURITY_DESCRIPTOR value.
Length - The length of the new attribute value (in bytes).
Return Values:
STATUS_SUCCESS - The service completed successfully.
STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not
be allocated.
--*/
{
NTSTATUS
NtStatus;
SAMTRACE("SampSetAccessAttribute");
//
// Make sure the requested attribute exists for the specified
// object type.
//
SampValidateAttributeIndex( Context, AttributeIndex );
//
// Make the variable-length data valid
//
NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES );
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Set the new attribute value...
//
NtStatus = SampSetVariableAttribute(
Context,
AttributeIndex,
SAMP_REVISION,
(PUCHAR)Attribute,
Length
);
return(NtStatus);
}
NTSTATUS
SampGetUlongArrayAttribute(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex,
IN BOOLEAN MakeCopy,
OUT PULONG *UlongArray,
OUT PULONG UsedCount,
OUT PULONG LengthCount
)
/*++
This API is used to get a copy of an array of ULONGs attribute or
a pointer to the attribute. If a pointer to the attribute is sought,
care must be taken to ensure the pointer is not used after it becomes
invalid. Actions that may cause an attribute pointer to become invalid
include setting an attribute value or dereferencing and then referencing
the object again.
Parameters:
Context - Pointer to an object context block.
AttributeIndex - Indicates the index (into the variable length attribute
array) of the attribute to be retrieved as a ULONG array.
MakeCopy - If TRUE, indicates that a copy of the attribute is to be made.
If FALSE, indicates a pointer to the attribute is desired without
making a copy. WARNING, if this is FALSE, the pointer is only
valid while the in-memory copy of the attribute remains in place.
Addition or replacement of any variable length attribute may
cause the attribute to be moved, and previously returned pointers
invalidated.
UlongArray - Receives a pointer to the array of ULONGS. If
MakeCopy was specified as TRUE, then this pointer points to a block
of memory allocated with MIDL_user_allocate() which the caller is
responsible for freeing (using MIDL_user_free()).
UsedCount - Receives the number of elements used in the array.
LengthCount - Receives the total number of elements in the array (some
at the end may be unused). If this value is zero, then
UlongArray will be returned as NULL.
Return Values:
STATUS_SUCCESS - The service completed successfully.
STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not
be allocated.
--*/
{
NTSTATUS
NtStatus;
ULONG
Length;
SAMTRACE("SampGetUlongArrayAttribute");
//
// Make sure the requested attribute exists for the specified
// object type.
//
SampValidateAttributeIndex( Context, AttributeIndex );
//
// Make the data valid
//
NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES );
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Get the count of array elements.
// If this is zero, then return will a null buffer pointer.
//
(*UsedCount) = *(SampObjectAttributeQualifier( Context, AttributeIndex));
//
// Get the length of the attribute
//
Length = SampObjectAttributeLength( Context, AttributeIndex );
(*LengthCount) = Length / sizeof(ULONG);
ASSERT( (*UsedCount) <= (*LengthCount) );
if ((*LengthCount) == 0) {
(*UlongArray) = NULL;
return(STATUS_SUCCESS);
}
//
// If we are not to allocate memory, then just return a pointer
// to the attribute.
//
if (MakeCopy == FALSE) {
(*UlongArray) =
(PULONG)SampObjectAttributeAddress( Context, AttributeIndex );
return(STATUS_SUCCESS);
}
//
// Need to make a copy of the attribute
//
(*UlongArray) = (PULONG)MIDL_user_allocate( Length );
if ((*UlongArray) == NULL) {
return(STATUS_NO_MEMORY);
}
RtlCopyMemory( (*UlongArray),
SampObjectAttributeAddress( Context, AttributeIndex ),
Length );
return(STATUS_SUCCESS);
}
NTSTATUS
SampSetUlongArrayAttribute(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex,
IN PULONG Attribute,
IN ULONG UsedCount,
IN ULONG LengthCount
)
/*++
This API is used to replace a ULONG array attribute in an
object's variable length attributes.
Parameters:
Context - Pointer to an object context block.
AttributeIndex - Indicates the index (into the variable length attribute
array) of the attribute to be set.
Attribute - Points to the new ULONG array value.
UsedCount - The number of used elements in the array.
LengthCount - the total number of elements in the array (some at the
end may be unused).
Return Values:
STATUS_SUCCESS - The service completed successfully.
STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not
be allocated.
--*/
{
NTSTATUS
NtStatus;
SAMTRACE("SampSetUlongArrayAttribute");
ASSERT( LengthCount >= UsedCount );
//
// Make sure the requested attribute exists for the specified
// object type.
//
SampValidateAttributeIndex( Context, AttributeIndex );
//
// Make the variable-length data valid
//
NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES );
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Set the new attribute value...
//
NtStatus = SampSetVariableAttribute(
Context,
AttributeIndex,
UsedCount, // Qualifier contains used element count
(PUCHAR)Attribute,
(LengthCount * sizeof(ULONG))
);
return(NtStatus);
}
NTSTATUS
SampGetLargeIntArrayAttribute(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex,
IN BOOLEAN MakeCopy,
OUT PLARGE_INTEGER *LargeIntArray,
OUT PULONG Count
)
/*++
This API is used to get a copy of an array of LARGE_INTEGERs attribute or
a pointer to the attribute. If a pointer to the attribute is sought,
care must be taken to ensure the pointer is not used after it becomes
invalid. Actions that may cause an attribute pointer to become invalid
include setting an attribute value or dereferencing and then referencing
the object again.
Parameters:
Context - Pointer to an object context block.
AttributeIndex - Indicates the index (into the variable length attribute
array) of the attribute to be retrieved.
MakeCopy - If TRUE, indicates that a copy of the attribute is to be made.
If FALSE, indicates a pointer to the attribute is desired without
making a copy. WARNING, if this is FALSE, the pointer is only
valid while the in-memory copy of the attribute remains in place.
Addition or replacement of any variable length attribute may
cause the attribute to be moved, and previously returned pointers
invalidated.
LargeIntArray - Receives a pointer to the array of ULONGS. If
MakeCopy was specified as TRUE, then this pointer points to a block
of memory allocated with MIDL_user_allocate() which the caller is
responsible for freeing (using MIDL_user_free()).
Count - Receives the number of elements in the array. If this value
is zero, then LargeIntArray will be returned as NULL.
Return Values:
STATUS_SUCCESS - The service completed successfully.
STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not
be allocated.
--*/
{
NTSTATUS
NtStatus;
ULONG
Length;
SAMTRACE("SampGetLargeIntArrayAttribute");
//
// Make sure the requested attribute exists for the specified
// object type.
//
SampValidateAttributeIndex( Context, AttributeIndex );
//
// Make the data valid
//
NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES );
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Get the count of array elements.
// If this is zero, then return will a null buffer pointer.
//
(*Count) = *(SampObjectAttributeQualifier( Context, AttributeIndex));
if ((*Count) == 0) {
(*LargeIntArray) = NULL;
return(STATUS_SUCCESS);
}
//
// Get the length of the attribute
//
Length = SampObjectAttributeLength( Context, AttributeIndex );
ASSERT((*Count) == (Length / sizeof(LARGE_INTEGER)) );
//
// If we are not to allocate memory, then just return a pointer
// to the attribute.
//
if (MakeCopy == FALSE) {
(*LargeIntArray) =
(PLARGE_INTEGER)SampObjectAttributeAddress( Context, AttributeIndex );
return(STATUS_SUCCESS);
}
//
// Need to make a copy of the attribute
//
(*LargeIntArray) = (PLARGE_INTEGER)MIDL_user_allocate( Length );
if ((*LargeIntArray) == NULL) {
return(STATUS_NO_MEMORY);
}
RtlCopyMemory( (*LargeIntArray),
SampObjectAttributeAddress( Context, AttributeIndex ),
Length );
return(STATUS_SUCCESS);
}
NTSTATUS
SampSetLargeIntArrayAttribute(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex,
IN PLARGE_INTEGER Attribute,
IN ULONG Count
)
/*++
This API is used to replace a LARGE_INTEGER array attribute in an
object's variable length attributes.
Parameters:
Context - Pointer to an object context block.
AttributeIndex - Indicates the index (into the variable length attribute
array) of the attribute to be set.
Attribute - Points to the new LARGE_INTEGER array value.
Count - The number of elements in the array.
Return Values:
STATUS_SUCCESS - The service completed successfully.
STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not
be allocated.
--*/
{
NTSTATUS
NtStatus;
SAMTRACE("SampSetLargeIntArrayAttribute");
//
// Make sure the requested attribute exists for the specified
// object type.
//
SampValidateAttributeIndex( Context, AttributeIndex );
//
// Make the variable-length data valid
//
NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES );
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Set the new attribute value...
//
NtStatus = SampSetVariableAttribute(
Context,
AttributeIndex,
Count, // Qualifier contains element count
(PUCHAR)Attribute,
(Count * sizeof(LARGE_INTEGER))
);
return(NtStatus);
}
NTSTATUS
SampGetSidArrayAttribute(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex,
IN BOOLEAN MakeCopy,
OUT PSID *SidArray,
OUT PULONG Length,
OUT PULONG Count
)
/*++
This API is used to get a copy of an array of SIDs attribute or
a pointer to the attribute. If a pointer to the attribute is sought,
care must be taken to ensure the pointer is not used after it becomes
invalid. Actions that may cause an attribute pointer to become invalid
include setting an attribute value or dereferencing and then referencing
the object again.
NOTE: This routine does not define the structure of a SID array,
so this effectively is a GetRawDataAttribute routine.
Parameters:
Context - Pointer to an object context block.
AttributeIndex - Indicates the index (into the variable length attribute
array) of the attribute to be retrieved.
MakeCopy - If TRUE, indicates that a copy of the attribute is to be made.
If FALSE, indicates a pointer to the attribute is desired without
making a copy. WARNING, if this is FALSE, the pointer is only
valid while the in-memory copy of the attribute remains in place.
Addition or replacement of any variable length attribute may
cause the attribute to be moved, and previously returned pointers
invalidated.
SidArray - Receives a pointer to the array of SIDs. If
MakeCopy was specified as TRUE, then this pointer points to a block
of memory allocated with MIDL_user_allocate() which the caller is
responsible for freeing (using MIDL_user_free()).
Count - Receives the number of elements in the array. If this value
is zero, then SidArray will be returned as NULL.
Return Values:
STATUS_SUCCESS - The service completed successfully.
STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not
be allocated.
--*/
{
NTSTATUS
NtStatus;
SAMTRACE("SampGetSidArrayAttribute");
//
// Make sure the requested attribute exists for the specified
// object type.
//
SampValidateAttributeIndex( Context, AttributeIndex );
//
// Make the data valid
//
NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES );
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Get the count of array elements.
// If this is zero, then return will a null buffer pointer.
//
(*Count) = *(SampObjectAttributeQualifier( Context, AttributeIndex));
if ((*Count) == 0) {
(*SidArray) = NULL;
(*Length) = 0;
return(STATUS_SUCCESS);
}
//
// Get the length of the attribute
//
(*Length) = SampObjectAttributeLength( Context, AttributeIndex );
//
// If we are not to allocate memory, then just return a pointer
// to the attribute.
//
if (MakeCopy == FALSE) {
(*SidArray) =
(PSID)SampObjectAttributeAddress( Context, AttributeIndex );
return(STATUS_SUCCESS);
}
//
// Need to make a copy of the attribute
//
(*SidArray) = (PSID)MIDL_user_allocate( (*Length) );
if ((*SidArray) == NULL) {
return(STATUS_NO_MEMORY);
}
RtlCopyMemory( (*SidArray),
SampObjectAttributeAddress( Context, AttributeIndex ),
(*Length) );
return(STATUS_SUCCESS);
}
NTSTATUS
SampSetSidArrayAttribute(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex,
IN PSID Attribute,
IN ULONG Length,
IN ULONG Count
)
/*++
This API is used to replace a SID array attribute in an
object's variable length attributes.
Parameters:
Context - Pointer to an object context block.
AttributeIndex - Indicates the index (into the variable length attribute
array) of the attribute to be set.
Attribute - Points to the new SID array value.
Length - Number of byte in the attribute buffer.
Count - Number of SIDs in the array.
Return Values:
STATUS_SUCCESS - The service completed successfully.
STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not
be allocated.
--*/
{
NTSTATUS
NtStatus;
SAMTRACE("SampSetSidArrayAttribute");
//
// Make sure the requested attribute exists for the specified
// object type.
//
SampValidateAttributeIndex( Context, AttributeIndex );
//
// Make the variable-length data valid
//
NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES );
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Set the new attribute value...
//
NtStatus = SampSetVariableAttribute(
Context,
AttributeIndex,
Count, // Qualifier contains element count
(PUCHAR)Attribute,
Length
);
return(NtStatus);
}
NTSTATUS
SampGetLogonHoursAttribute(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex,
IN BOOLEAN MakeCopy,
OUT PLOGON_HOURS LogonHours
)
/*++
This API is used to get a copy of a logon hours attribute or
a pointer to the attribute. If a pointer to the attribute is sought,
care must be taken to ensure the pointer is not used after it becomes
invalid. Actions that may cause an attribute pointer to become invalid
include setting an attribute value or dereferencing and then referencing
the object again.
Parameters:
Context - Pointer to an object context block.
AttributeIndex - Indicates the index (into the variable length attribute
array) of the attribute to be retrieved.
MakeCopy - If TRUE, indicates that a copy of the attribute is to be made.
If FALSE, indicates a pointer to the attribute is desired without
making a copy. WARNING, if this is FALSE, the pointer is only
valid while the in-memory copy of the attribute remains in place.
Addition or replacement of any variable length attribute may
cause the attribute to be moved, and previously returned pointers
invalidated.
LogonHours - Receives the logon hours information. If MakeCopy is TRUE
then the bitmap pointed to from within this structure will be a copy
of the attribute and must be deallocated uing MIDL_user_free().
Otherwise, this same field will point to the bitmap in the on-disk
buffer.
Return Values:
STATUS_SUCCESS - The service completed successfully.
STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not
be allocated.
--*/
{
NTSTATUS
NtStatus;
ULONG
Length,
Units;
SAMTRACE("SampGetLogonHoursAttribute");
//
// Make sure the requested attribute exists for the specified
// object type.
//
SampValidateAttributeIndex( Context, AttributeIndex );
//
// Make the data valid
//
NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES );
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Get the time units.
// If this is zero, then return will a null buffer pointer.
//
Units = *(SampObjectAttributeQualifier( Context, AttributeIndex));
ASSERT(Units <= 0xFFFF);
LogonHours->UnitsPerWeek = (USHORT)Units;
if (Units == 0) {
LogonHours->LogonHours = NULL;
return(STATUS_SUCCESS);
}
//
// If we are not to allocate memory, then just return a pointer
// to the attribute.
//
if (MakeCopy == FALSE) {
LogonHours->LogonHours =
(PUCHAR)SampObjectAttributeAddress( Context, AttributeIndex );
return(STATUS_SUCCESS);
}
//
// Get the length of the attribute
//
Length = SampObjectAttributeLength( Context, AttributeIndex );
ASSERT(Length <= 0xFFFF);
//
// Need to make a copy of the attribute
//
LogonHours->LogonHours =
(PUCHAR)MIDL_user_allocate( Length );
if (LogonHours->LogonHours == NULL) {
return(STATUS_NO_MEMORY);
}
RtlCopyMemory( LogonHours->LogonHours,
SampObjectAttributeAddress( Context, AttributeIndex ),
Length );
return(STATUS_SUCCESS);
}
NTSTATUS
SampSetLogonHoursAttribute(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex,
IN PLOGON_HOURS Attribute
)
/*++
This API is used to replace a LOGON_HOURS attribute in an
object's variable length attributes.
UnitsPerWeek are stored in the Qualifier field of the attribute.
Parameters:
Context - Pointer to an object context block.
AttributeIndex - Indicates the index (into the variable length attribute
array) of the attribute to be set.
Attribute - Points to the new LOGON_HOURS value.
Return Values:
STATUS_SUCCESS - The service completed successfully.
STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not
be allocated.
--*/
{
NTSTATUS NtStatus;
PUCHAR LogonHours;
ULONG Length;
USHORT UnitsPerWeek;
SAMTRACE("SampSetLogonHoursAttribute");
//
// Make sure the requested attribute exists for the specified
// object type.
//
SampValidateAttributeIndex( Context, AttributeIndex );
//
// Make the variable-length data valid
//
NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES );
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Grab the UnitsPerWeek value for the logon_hours structure.
// We use this to calculate the length of the data.
//
if ( Attribute == NULL ) {
UnitsPerWeek = 0;
LogonHours = NULL;
} else {
UnitsPerWeek = Attribute->UnitsPerWeek;
LogonHours = Attribute->LogonHours;
}
//
// Validate the data - make sure that if the units per week are non-zero
// then the logon hours buffer is non-NULL.
//
if ( (UnitsPerWeek != 0) && (LogonHours == NULL) ) {
return(STATUS_INVALID_PARAMETER);
}
//
// Calculate length of logon_hours structure
//
Length = (ULONG)((UnitsPerWeek + 7) / 8);
//
// Set the new attribute value...
//
NtStatus = SampSetVariableAttribute(
Context,
AttributeIndex,
(ULONG)UnitsPerWeek, // Qualifier contains units per week
LogonHours,
Length
);
return(NtStatus);
}
///////////////////////////////////////////////////////////////////////////////
// //
// private routines //
// //
///////////////////////////////////////////////////////////////////////////////
BOOLEAN
SampDsIsAlreadyValidData(
IN PSAMP_OBJECT Context,
IN ULONG AttributeGroup
)
/*++
Description:
This routine determines whether or not the attributes are in memory or
not (i.e. valid or not). OnDisk should only be NULL for a newly created
context that has just been initialized, otherwise it is non-NULL.
Parameters:
Context - Pointer to an object context block.
AttributeGroup - Flag, either SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE-
ATTRIBUTES.
Return Values:
This routine returns a flag, TRUE if the attributes are in memory, FALSE
otherwise.
--*/
{
BOOLEAN Flag = FALSE;
SAMTRACE("SampDsIsAlreadyValidData");
if (NULL != Context->OnDisk)
{
if (AttributeGroup == SAMP_FIXED_ATTRIBUTES)
{
if (SampFixedAttributesValid(Context))
{
Flag = TRUE;
}
}
else
{
ASSERT(AttributeGroup == SAMP_VARIABLE_ATTRIBUTES);
if (SampVariableAttributesValid(Context))
{
Flag = TRUE;
}
}
}
return(Flag);
}
NTSTATUS
SampDsPrepareAttrBlock(
IN ULONG FixedCount,
IN PSAMP_FIXED_ATTRIBUTE_TYPE_INFO FixedAttrIds[],
IN ULONG VarCount,
IN PSAMP_VAR_ATTRIBUTE_TYPE_INFO VarAttrIds[],
OUT PDSATTRBLOCK AttrBlock
)
/*++
Description:
This routine sets up a DSATTRBLOCK in preparation to read the DS. An
attribute block is created containaing the attribute identifiers for
the attributes to read.
Parameters:
FixedCount - Number of fixed-length attributes.
FixedAttrIds - Array of attribute IDs of the fixed-length attributes.
VarCount - Number of variable-length attributes.
VarAttrIds - Array of attribute IDs of the fixed-length attributes.
AttrBlock - Pointer, the generated attribute block with IDs.
Return Values:
STATUS_SUCCESS if successful, error otherwise.
--*/
{
NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
ULONG i = 0;
ULONG AttrCount = FixedCount + VarCount;
PDSATTR Attributes = NULL;
SAMTRACE("SampDsPrepareAttrBlock");
Attributes = RtlAllocateHeap(RtlProcessHeap(),
0,
AttrCount * sizeof(DSATTR));
if (NULL != Attributes)
{
RtlZeroMemory(Attributes, (AttrCount * sizeof(DSATTR)));
// This loop is set up to handle the case where both fixed-length
// and variable-length attribute buffers are passed in (which may
// be needed for combined attribute conversion).
for (i = 0; i < AttrCount; i++)
{
if (i < FixedCount)
{
// Set the fixed-length attribute type/id.
Attributes[i].attrTyp = FixedAttrIds[i].Type;
}
else
{
ASSERT(0 <= (i - FixedCount));
// Set the variable-length attribute type/id.
Attributes[i].attrTyp = VarAttrIds[i - FixedCount].Type;
}
// The read operation does not require setting up valCount or
// pAVal.
Attributes[i].AttrVal.valCount = 0;
Attributes[i].AttrVal.pAVal = NULL;
}
// Hook up the attributes to the top-level attrblock and return.
AttrBlock->attrCount = AttrCount;
AttrBlock->pAttr = Attributes;
NtStatus = STATUS_SUCCESS;
}
else
{
NtStatus = STATUS_NO_MEMORY;
}
return(NtStatus);
}
NTSTATUS
SampDsMakeAttrBlock(
IN INT ObjectType,
IN ULONG AttributeGroup,
OUT PDSATTRBLOCK AttrBlock
)
/*++
Description:
This routine determines the object type and sets the count of fixed-
length and variable-length attributes for the object. SampDsPrepare-
AttrBlock to set up the DSATTRBLOCK.
Parameters:
ObjectType - SAM object ID.
AttributeGroup - Flag, either SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE-
ATTRIBUTES.
AttrBlock - Pointer, generated attribute block.
Return Values:
STATUS_SUCCESS if no errors.
--*/
{
NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
ULONG FixedLenAttrCount = 0;
ULONG VarLenAttrCount = 0;
SAMTRACE("SampDsMakeAttrBlock");
switch(ObjectType)
{
case SampServerObjectType:
FixedLenAttrCount = SAMP_SERVER_FIXED_ATTR_COUNT;
VarLenAttrCount = SAMP_SERVER_VARIABLE_ATTRIBUTES;
break;
case SampDomainObjectType:
FixedLenAttrCount = SAMP_DOMAIN_FIXED_ATTR_COUNT;
VarLenAttrCount = SAMP_DOMAIN_VARIABLE_ATTRIBUTES;
break;
case SampGroupObjectType:
FixedLenAttrCount = SAMP_GROUP_FIXED_ATTR_COUNT;
VarLenAttrCount = SAMP_GROUP_VARIABLE_ATTRIBUTES;
break;
case SampAliasObjectType:
FixedLenAttrCount = SAMP_ALIAS_FIXED_ATTR_COUNT;
VarLenAttrCount = SAMP_ALIAS_VARIABLE_ATTRIBUTES;
break;
case SampUserObjectType:
FixedLenAttrCount = SAMP_USER_FIXED_ATTR_COUNT;
VarLenAttrCount = SAMP_USER_VARIABLE_ATTRIBUTES;
break;
default:
ASSERT(FALSE && "Invalid SampObjectType");
break;
}
if ((0 < FixedLenAttrCount) && (0 < VarLenAttrCount))
{
if (SAMP_FIXED_ATTRIBUTES == AttributeGroup)
{
NtStatus = SampDsPrepareAttrBlock(
FixedLenAttrCount,
&SampFixedAttributeInfo[ObjectType][0],
0,
NULL,
AttrBlock);
}
else
{
NtStatus = SampDsPrepareAttrBlock(
0,
NULL,
VarLenAttrCount,
&SampVarAttributeInfo[ObjectType][0],
AttrBlock);
}
}
return(NtStatus);
}
NTSTATUS
SampDsConvertReadAttrBlock(
IN INT ObjectType,
IN ULONG AttributeGroup,
IN PDSATTRBLOCK AttrBlock,
OUT PVOID *SamAttributes,
OUT PULONG FixedLength,
OUT PULONG VariableLength
)
/*++
Description:
This routine converts an attribute block (DSATTRBLOCK) into a SAM attri-
bute buffer. This is used to convert the resultant attributes from a DS
read into the SAM attribute format.
Parameters:
ObjectType - SAM object ID.
AttributeGroup - Flag, either SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE-
ATTRIBUTES.
AttrBlock - Pointer, input attribute block.
SamAttributes - Pointer, returned SAM attributes.
FixedLength - Pointer, byte count of the fixed-length attributes size.
VariableLength - Pointer, byte count of the variable-length attributes
size.
Return Values:
STATUS_SUCCESS if no error.
--*/
{
NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
SAMTRACE("SampDsConvertReadData");
// Initialize the returned lengths and buffer.
*FixedLength = 0;
*VariableLength = 0;
*SamAttributes = NULL;
if (SAMP_FIXED_ATTRIBUTES == AttributeGroup)
{
NtStatus = SampConvertAttrBlockToFixedLengthAttributes(
ObjectType,
AttrBlock,
SamAttributes,
FixedLength);
}
else
{
NtStatus = SampConvertAttrBlockToVarLengthAttributes(
ObjectType,
AttrBlock,
(SAMP_VARIABLE_LENGTH_ATTRIBUTE**)SamAttributes,
VariableLength);
}
return(NtStatus);
}
NTSTATUS
SampDsUpdateContextFixedAttributes(
IN PSAMP_OBJECT Context,
IN ULONG FixedLength,
IN PVOID SamAttributes,
IN BOOLEAN FirstTimeInitialization
)
/*++
Description:
This routine updates the SAM context fixed-length attributes if the size
of the attributes have changed. For fixed-length attributes, this only
occurs when a revision of the fixed-length data structures has caused a
size change in the structures.
Parameters:
Context - Pointer to an object context block.
FixedLength - Byte count of the fixed-length attributes.
SamAttributes - Pointer, the SAM fixed-length attributes.
FirstTimeInitialization - Flag, the first time that the attributes are
set, the SAM global buffer lengths, addresses, and offsets do not
all accurately reflect the actual memory allocated, so skip certain
calculations when this flag is set.
Return Values:
STATUS_SUCCESS if no error.
--*/
{
NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
ULONG NewLength = 0;
PBYTE Buffer = NULL;
PBYTE VariableData = NULL;
ULONG VariableLength = 0;
SAMTRACE("SampDsUpdateContextFixedAttributes");
// The first time through, the variable-buffer length will be zero, so
// the new length is only the fixed length. Note that OnDiskAllocated is
// also zero (a global) so don't attempt to use the variable-buffer
// offset in the calculation.
if (FirstTimeInitialization)
{
NewLength = FixedLength;
}
else
{
ASSERT(0 < Context->OnDiskAllocated);
NewLength = FixedLength + SampDsVariableBufferLength(Context);
DebugPrint("OnDiskAllocated = %lu\n", Context->OnDiskAllocated);
DebugPrint("VarBufOffset = %lu\n", SampDsVariableBufferOffset(Context));
DebugPrint("VarBufLenth = %lu\n", SampDsVariableBufferLength(Context));
}
Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, NewLength);
DebugPrint("FixedLength = %lu\n", FixedLength);
DebugPrint("NewLength = %lu\n", NewLength);
DebugPrint("Buffer Addr = 0x%lx\n", Buffer);
if (NULL != Buffer)
{
// Zero the new buffer and copy the fixed-length attributes into it.
RtlZeroMemory(Buffer, NewLength);
RtlCopyMemory(Buffer, SamAttributes, FixedLength);
if (NULL != Context->OnDisk)
{
// Save the current address and length of the variable data, if
// it exists. The first time through, the variable data is NULL.
// Release the old buffer.
VariableData = SampDsVariableBufferAddress(Context);
VariableLength = SampDsVariableBufferLength(Context);
RtlCopyMemory(Buffer + FixedLength, VariableData, VariableLength);
// Free the old OnDisk buffer.
RtlFreeHeap(RtlProcessHeap(), 0, Context->OnDisk);
}
// Reset the context attribute buffer to the new buffer.
Context->OnDisk = Buffer;
// Release the memory for the input buffer
RtlFreeHeap(RtlProcessHeap(), 0, SamAttributes);
NtStatus = STATUS_SUCCESS;
}
else
{
NtStatus = STATUS_NO_MEMORY;
}
return(NtStatus);
}
NTSTATUS
SampDsUpdateContextVariableAttributes(
IN PSAMP_OBJECT Context,
IN ULONG VariableLength,
IN PVOID SamAttributes
)
/*++
Description:
This routine updates the SAM context variable-length attributes if the
size of the attributes has changed. Unlike the fixed-length attributes,
this will occur frequently due to fact that these attributes are vari-
able length.
Parameters:
Context - Pointer to an object context block.
FixedLength - Byte count of the fixed-length attributes.
SamAttributes - Pointer, the SAM fixed-length attributes.
Return Values:
STATUS_SUCCESS if no error.
--*/
{
NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
ULONG NewLength = 0;
PBYTE Buffer = NULL;
PBYTE FixedData = NULL;
ULONG FixedLength = 0;
SAMTRACE("SampDsUpdateContextVariableAttributes");
// Get the current fixed-buffer length, add the new variable length, and
// allocate the new buffer.
NewLength = SampDsFixedBufferLength(Context) + VariableLength;
Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, NewLength);
if (NULL != Buffer)
{
RtlZeroMemory(Buffer, NewLength);
if (NULL != Context->OnDisk)
{
// Get the fixed-length buffer address and length...
FixedData = SampDsFixedBufferAddress(Context);
FixedLength = SampDsFixedBufferLength(Context);
if ((NULL != FixedData) && (0 < FixedLength))
{
// Copy the fixed data into the new buffer and append the
// variable-length data.
RtlCopyMemory(Buffer, FixedData, FixedLength);
RtlCopyMemory(Buffer + FixedLength,
SamAttributes,
VariableLength);
// Release the old attribute buffer and reset OnDisk to
// point at the new buffer.
RtlFreeHeap(RtlProcessHeap(), 0, Context->OnDisk);
Context->OnDisk = Buffer;
// Release the temporary attribute buffer.
RtlFreeHeap(RtlProcessHeap(), 0, SamAttributes);
NtStatus = STATUS_SUCCESS;
}
}
// Context->OnDisk should never be NULL in this routine.
ASSERT(NULL != Context->OnDisk);
}
else
{
NtStatus = STATUS_NO_MEMORY;
}
return(NtStatus);
}
NTSTATUS
SampUpdateOffsets(
IN PSAMP_OBJECT Context,
IN BOOLEAN FirstTimeInitialization
)
/*++
Description:
This routine updates the buffer (OnDisk) offset and length information
that is stored in the object information (SAMP_OBJECT_INFORMATION) and
in the instance information (SAMP_OBJECT), after the attributes have
been successfully updated.
Parameters:
Context - Pointer to an object context block.
AttributeGroup - Flag, either SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE-
ATTRIBUTES.
Return Values:
STATUS_SUCCESS if no error.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
INT ObjectType = Context->ObjectType;
ULONG FixedDataLength = 0;
ULONG VariableArrayLength = 0;
ULONG VariableDataOffset = 0;
ULONG VariableDataLength = 0;
ULONG TotalBufferLength = 0;
PSAMP_VARIABLE_LENGTH_ATTRIBUTE VariableArray = NULL;
ULONG i = 0;
ULONG AttributeCount = 0;
// Determine the SAM object type and compute the length of the fixed-
// length attributes and the length of the variable-length attribute
// array, which will be used as buffer offsets.
SAMTRACE("SampUpdateOffsets");
switch(ObjectType)
{
case SampServerObjectType:
FixedDataLength = sizeof(SAMP_V1_FIXED_LENGTH_SERVER);
AttributeCount = SAMP_SERVER_VARIABLE_ATTRIBUTES;
break;
case SampDomainObjectType:
FixedDataLength = sizeof(SAMP_V1_0A_FIXED_LENGTH_DOMAIN);
AttributeCount = SAMP_DOMAIN_VARIABLE_ATTRIBUTES;
break;
case SampGroupObjectType:
FixedDataLength = sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP);
AttributeCount = SAMP_GROUP_VARIABLE_ATTRIBUTES;
break;
case SampAliasObjectType:
FixedDataLength = sizeof(SAMP_V1_FIXED_LENGTH_ALIAS);
AttributeCount = SAMP_ALIAS_VARIABLE_ATTRIBUTES;
break;
case SampUserObjectType:
FixedDataLength = sizeof(SAMP_V1_0A_FIXED_LENGTH_USER);
AttributeCount = SAMP_USER_VARIABLE_ATTRIBUTES;
break;
default:
// Invalid object type specified.
NtStatus = STATUS_INTERNAL_ERROR;
break;
}
VariableArrayLength = (AttributeCount *
sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE));
if (NT_SUCCESS(NtStatus))
{
// First, update the object information offsets and lengths.
SampObjectInformation[ObjectType].FixedDsAttributesOffset =
0;
SampObjectInformation[ObjectType].FixedDsLengthSize =
FixedDataLength;
SampObjectInformation[ObjectType].VariableDsBufferOffset =
FixedDataLength;
SampObjectInformation[ObjectType].VariableDsArrayOffset =
FixedDataLength;
SampObjectInformation[ObjectType].VariableDsDataOffset =
FixedDataLength + VariableArrayLength;
if (FALSE == FirstTimeInitialization)
{
// Get a pointer to the array of variable-length information and
// total up the lengths of these attributes.
VariableArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE)
((PBYTE)(Context->OnDisk) + FixedDataLength);
for (i = 0; i < AttributeCount; i++)
{
VariableDataLength = VariableDataLength + VariableArray[i].Length;
}
}
else
{
// The first time through, the attribute buffer only contains
// the fixed-length attributes, so make sure the lengths for
// the variable attributes are zero.
VariableArrayLength = 0;
VariableDataLength = 0;
}
TotalBufferLength = FixedDataLength +
VariableArrayLength +
VariableDataLength;
}
// Finally, update the instance information of the object's context.
Context->OnDiskAllocated = TotalBufferLength;
Context->OnDiskUsed = TotalBufferLength;
// The DS routines do not allocate extra space at the end of the SAM
// OnDisk buffer, hence OnDiskFree is always zero.
// BUG: Should allocate extra OnDisk buffer free space for growth.
Context->OnDiskFree = 0;
DebugPrint("OnDiskAllocated = %lu\n", Context->OnDiskAllocated);
DebugPrint("OnDiskUsed = %lu\n", Context->OnDiskUsed);
DebugPrint("OnDiskFree = %lu\n", Context->OnDiskFree);
return(NtStatus);
}
NTSTATUS
SampDsUpdateContextAttributes(
IN PSAMP_OBJECT Context,
IN ULONG AttributeGroup,
IN PVOID SamAttributes,
IN ULONG FixedLength,
IN ULONG VariableLength
)
/*++
Description:
This routine updates an object's attribute buffer (OnDisk) for the given
context. If the new buffer size is the same as the old size, as in the
case of modifications, then a simple memory copy is performed, otherwise
helper routines are called to resize the buffer and copy the data. A sub-
sequent helper routine is called to update the context buffer lengths
and offsets.
Parameters:
Context - Pointer to an object context block.
Return Values:
STATUS_SUCCESS
--*/
{
NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
INT ObjectType = Context->ObjectType;
PBYTE FixedLengthData = NULL;
PBYTE VariableLengthData = NULL;
BOOLEAN FirstTimeInitialization = FALSE;
SAMTRACE("SampDsUpdateContextAttributes");
// BUG: OnDisk may only be set to fixed or variable attributes.
// The current version of SAM contains a hack that resets all attributes
// whenever the variable-length ones are asked for. This allows both the
// fixed and variable attributes to be set even if FixedStoredSeparately
// is TRUE. This routine does not maintain that behavior, which may create
// problems for SAM. If so, the caller of this routine will need to call
// it twice, once for fixed and once for variable attributes in the cases
// where FixedStoredSeparately is TRUE.
if (SAMP_FIXED_ATTRIBUTES == AttributeGroup)
{
// Update the fixed-length attributes. The first time through, OnDisk
// will be NULL.
if ((NULL != Context->OnDisk) &&
(SampDsFixedBufferLength(Context) == FixedLength))
{
// The fixed-length data is the same size (i.e. not
// doing an upgrade), so copy the new attributes and
// release the buffer.
FixedLengthData = SampDsFixedBufferAddress(Context);
RtlCopyMemory(FixedLengthData,
(PBYTE)SamAttributes,
FixedLength);
RtlFreeHeap(RtlProcessHeap(), 0, SamAttributes);
NtStatus = STATUS_SUCCESS;
}
else
{
if (NULL == Context->OnDisk)
{
// If OnDisk is NULL, then this is the first time that the
// attribute buffer of the context has been set.
FirstTimeInitialization = TRUE;
}
// The new attributes are not the same size as the old ones.
NtStatus = SampDsUpdateContextFixedAttributes(Context,
FixedLength,
SamAttributes,
FirstTimeInitialization);
// Fixed-attribute size changes when the context attributes
// have been set for the first time (i.e. changed from zero to
// actual sizes), or when the fixed-length structures are
// changed in an upgrade scenario.
if (NT_SUCCESS(NtStatus))
{
NtStatus = SampUpdateOffsets(Context, FirstTimeInitialization);
}
}
if (NT_SUCCESS(NtStatus))
{
Context->FixedValid = TRUE;
}
else
{
Context->FixedValid = FALSE;
}
}
else
{
// Update the variable-length attributes. In the event that the
// attributes are stored separately, OnDisk will be NULL the first
// time through (for variable-length attributes).
if ((NULL != Context->OnDisk) &&
(SampDsVariableBufferLength(Context) == VariableLength))
{
// The variable-length data is the same size, so copy the new
// attributes and release the buffer.
VariableLengthData = SampDsVariableBufferAddress(Context);
RtlCopyMemory(VariableLengthData,
(PBYTE)SamAttributes,
VariableLength);
RtlFreeHeap(RtlProcessHeap(), 0, SamAttributes);
NtStatus = STATUS_SUCCESS;
}
else
{
if (NULL == Context->OnDisk)
{
FirstTimeInitialization = TRUE;
}
// The new attributes are not the same size as the old
// ones.
NtStatus = SampDsUpdateContextVariableAttributes(
Context,
VariableLength,
SamAttributes);
// Variable-attribute size changes when the context attributes
// have been set for the first time (i.e. changed from zero to
// actual sizes), or when the variable-length data has changed
// size.
if (NT_SUCCESS(NtStatus))
{
NtStatus = SampUpdateOffsets(Context, FirstTimeInitialization);
}
}
if (NT_SUCCESS(NtStatus))
{
Context->VariableValid = TRUE;
}
else
{
Context->VariableValid = FALSE;
}
}
return(NtStatus);
}
NTSTATUS
SampValidateDsAttributes(
IN PSAMP_OBJECT Context,
IN ULONG AttributeGroup
)
/*++
Description:
Ensure specified attributes are in-memory. If they are not, then read
them from the DS backing store. This routine fetches all of the stored
attributes for a given SAM object. To read a single attribute, or a
subset of attributes, SampDsRead should be used to selectively fetch
attributes. Context->OnDisk is updated with the new attributes.
Parameters:
Context - Pointer to an object context block.
AttributeGroup - identifies which kind of attributes are being validated
(SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE_ATTRIBUTES).
Return Values:
STATUS_SUCCESS - The attributes are in-memory.
STATUS_NO_MEMORY - Memory could not be allocated to retrieve the
attributes.
Other values as may be returned by registry API trying to retrieve
the attributes from backing store.
This routine returns a flag, TRUE if the attributes are in memory, FALSE
otherwise.
--*/
{
NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
ULONG Flags = 0;
INT ObjectType = -1;
DSATTRBLOCK ReadAttrBlock;
DSATTRBLOCK ResultAttrBlock;
ULONG FixedLength = 0;
ULONG VariableLength = 0;
PVOID SamAttributes = NULL;
SAMTRACE("SampValidateDsAttributes");
// The data might already be in memory, so check it out.
if (FALSE == SampDsIsAlreadyValidData(Context, AttributeGroup))
{
if (NULL != Context->ObjectNameInDs)
{
ObjectType = Context->ObjectType;
RtlZeroMemory(&ReadAttrBlock, sizeof(DSATTRBLOCK));
RtlZeroMemory(&ResultAttrBlock, sizeof(DSATTRBLOCK));
// Construct the input ATTRBLOCK used to specify which attributes
// should be read from the DS.
NtStatus = SampDsMakeAttrBlock(ObjectType,
AttributeGroup,
&ReadAttrBlock);
if ((NT_SUCCESS(NtStatus)) && (NULL != ReadAttrBlock.pAttr))
{
// Read the attributes from the DS, flags is currently unused.
NtStatus = SampDsRead(Context->ObjectNameInDs,
Flags,
ObjectType,
&ReadAttrBlock,
&ResultAttrBlock);
if ((NT_SUCCESS(NtStatus)) && (NULL != ResultAttrBlock.pAttr))
{
// Convert the ATTRBLOCK into the appropriate SAM attri-
// butes, returning them in the SamAttributes buffer. Note
// that the returned lengths are as follows:
//
// FixedLength - The byte count of the returned fixed-
// length buffer
//
// VariableLength - The byte count of the returned var-
// able-length buffer.
NtStatus = SampDsConvertReadAttrBlock(ObjectType,
AttributeGroup,
&ResultAttrBlock,
&SamAttributes,
&FixedLength,
&VariableLength);
if ((NT_SUCCESS(NtStatus)) && (NULL != SamAttributes))
{
NtStatus = SampDsUpdateContextAttributes(
Context,
AttributeGroup,
SamAttributes,
FixedLength,
VariableLength);
}
}
}
}
}
else
{
NtStatus = STATUS_SUCCESS;
}
return(NtStatus);
}
NTSTATUS
SampValidateRegAttributes(
IN PSAMP_OBJECT Context,
IN ULONG AttributeGroup
)
/*++
Ensure specified attributes are in-memory.
If they are not, then read them from backing store.
Parameters:
Context - Pointer to an object context block.
AttributeGroup - identifies which kind of attributes are being
validated (SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE_ATTRIBUTES).
Return Values:
STATUS_SUCCESS - The attributes are in-memory.
STATUS_NO_MEMORY - Memory could not be allocated to retrieve the
attributes.
Other values as may be returned by registry API trying to retrieve
the attributes from backing store.
--*/
{
NTSTATUS
NtStatus;
ULONG
RequiredLength,
TotalRequiredLength,
BufferLength;
PUCHAR
Buffer;
PUNICODE_STRING
KeyAttributeName;
BOOLEAN
CreatedObject = FALSE;
SAMTRACE("SampValidateRegAttributes");
//
// The data might already be in memory.
//
if (AttributeGroup == SAMP_FIXED_ATTRIBUTES) {
if (SampFixedAttributesValid( Context )) {
ASSERT(Context->OnDisk != NULL);
return(STATUS_SUCCESS);
}
} else {
ASSERT( AttributeGroup == SAMP_VARIABLE_ATTRIBUTES );
if (SampVariableAttributesValid( Context )) {
ASSERT(Context->OnDisk != NULL);
return(STATUS_SUCCESS);
}
}
//
// Retrieve it from the registry, or allocate it if new.
//
NtStatus = SampGetAttributeBufferReadInfo(
Context,
AttributeGroup,
&Buffer,
&BufferLength,
&KeyAttributeName
);
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
if ( Context->RootKey != INVALID_HANDLE_VALUE ) {
//
// Account exists on disk, so read in the attributes.
//
NtStatus = SampReadRegistryAttribute( Context->RootKey,
Buffer,
BufferLength,
KeyAttributeName,
&RequiredLength
);
RequiredLength = SampDwordAlignUlong(RequiredLength);
if ( ( SampObjectInformation[Context->ObjectType].FixedStoredSeparately ) &&
( AttributeGroup == SAMP_VARIABLE_ATTRIBUTES ) ) {
//
// RequiredLength was returned to us as the length of the
// variable attributes on the disk. However, we're going
// to be using it to determine the total buffer size as well
// as to set how much of the buffer is in use, so we must add
// the size of the fixed stuff that preceeds the variable
// buffer.
//
TotalRequiredLength = RequiredLength +
SampVariableBufferOffset( Context );
} else {
//
// Either the attribute groups are read together, or we're
// reading in the fixed attribute group. Either way, we
// already have the total size we need.
//
TotalRequiredLength = RequiredLength;
}
if ((NtStatus == STATUS_BUFFER_TOO_SMALL) ||
( NtStatus == STATUS_BUFFER_OVERFLOW ) ) {
NtStatus = SampExtendAttributeBuffer( Context, TotalRequiredLength );
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
NtStatus = SampGetAttributeBufferReadInfo(
Context,
AttributeGroup,
&Buffer,
&BufferLength,
&KeyAttributeName
);
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
NtStatus = SampReadRegistryAttribute( Context->RootKey,
Buffer,
BufferLength,
KeyAttributeName,
&RequiredLength
);
}
} else {
//
// We're creating a new object.
//
// Initialize the requiredlength to the amount of the buffer
// we have used when we created the empty attributes. This will
// be the value stored in OnDiskUsed.
//
// Note OnDiskUsed is only used by operations on the variable
// length attributes.
//
TotalRequiredLength = SampVariableDataOffset(Context);
ASSERT(TotalRequiredLength <= Context->OnDiskAllocated);
CreatedObject = TRUE;
}
//
// if we read something, indicate that the corresponding buffer
// (and maybe both) are now valid.
//
// Also set the used and free information for the buffer if necessary.
//
if (NT_SUCCESS(NtStatus)) {
if (SampObjectInformation[Context->ObjectType].FixedStoredSeparately) {
//
// only one attribute group was read in
//
if (AttributeGroup == SAMP_FIXED_ATTRIBUTES) {
Context->FixedValid = TRUE;
Context->FixedDirty = FALSE;
} else {
ASSERT(AttributeGroup == SAMP_VARIABLE_ATTRIBUTES);
Context->VariableValid = TRUE;
Context->VariableDirty = FALSE;
Context->OnDiskUsed = SampDwordAlignUlong(TotalRequiredLength);
Context->OnDiskFree = Context->OnDiskAllocated -
Context->OnDiskUsed;
}
} else {
//
// Both attribute groups read in.
//
Context->FixedValid = TRUE;
Context->FixedDirty = FALSE;
Context->VariableValid = TRUE;
Context->VariableDirty = FALSE;
Context->OnDiskUsed = SampDwordAlignUlong(TotalRequiredLength);
Context->OnDiskFree = Context->OnDiskAllocated -
Context->OnDiskUsed;
}
}
if (NT_SUCCESS(NtStatus) && !CreatedObject) {
//
// make any adjustments necessary to bring the data
// just read in up to current revision format.
//
NtStatus = SampUpgradeToCurrentRevision(
Context,
AttributeGroup,
Buffer,
RequiredLength,
&TotalRequiredLength
);
}
#ifdef SAM_DEBUG_ATTRIBUTES
if (SampDebugAttributes) {
DbgPrint("SampValidateAttributes - initialized the context :\n\n");
SampDumpAttributes(Context);
}
#endif
return(NtStatus);
}
NTSTATUS
SampValidateAttributes(
IN PSAMP_OBJECT Context,
IN ULONG AttributeGroup
)
/*++
Routine Description:
This routine determines from the object context whether to validate object
attributes residing in the registry or in the DS backing store, and then
calls the appropriate routine to do the work.
Arguments:
Context - Pointer, the object's SAM context.
AttributeGroup - identifies which kind of attributes are being validated
(SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE_ATTRIBUTES).
Return Value:
STATUS_SUCCESS - attributes were checked and read from storage if neces-
sary without a problem, otherwise an error code is returned.
--*/
{
NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
SAMTRACE("SampValidateAttributes");
if (NULL != Context)
{
if (IsDsObject(Context))
{
if (NULL != Context->OnDisk)
{
// The SAM object attributes have been set atleast once, so
// update them if needed.
NtStatus = SampValidateDsAttributes(Context, AttributeGroup);
}
else
{
// The SAM object attributes have never been set because this
// is a new context. First set the fixed-length attributes and
// then the variable-length ones.
ASSERT(SAMP_FIXED_ATTRIBUTES == AttributeGroup);
// If the OnDisk buffer is NULL, make sure that the fixed-
// length attributes are loaded first.
NtStatus = SampValidateDsAttributes(
Context,
SAMP_FIXED_ATTRIBUTES);
if (NT_SUCCESS(NtStatus))
{
// BUG: Not setting up var-length attrs during init time.
// The NT3.5 - NT4.0 SAM attempts to set both the fixed-
// length and variable-length attributes if the OnDisk
// buffer is NULL (i.e. this is the first time that the
// buffer is set). This may not be necessary in NT5 SAM,
// so explicitly loading the variable attributes now is
// skipped. They are lazily loaded into memory whenever
// any variable-length attribute is asked for (see the
// implementation of SampValidateAttributes). THIS MEANS
// THAT THE VARIABLE-LENGTH ATTRIBUTES CAN ONLY BE LOADED
// INTO MEMORY AFTER THE FIXED-LENGTH ONES BECAUSE THEY
// ARE APPENDED ONTO THE END OF THE OnDisk BUFFER.
// NtStatus = SampValidateDsAttributes(
// Context,
// SAMP_VARIABLE_ATTRIBUTES);
}
}
}
else
{
NtStatus = SampValidateRegAttributes(Context, AttributeGroup);
}
}
else
{
NtStatus = STATUS_INVALID_PARAMETER;
}
return(NtStatus);
}
NTSTATUS
SampUpgradeToCurrentRevision(
IN PSAMP_OBJECT Context,
IN ULONG AttributeGroup,
IN OUT PUCHAR Buffer,
IN ULONG LengthOfDataRead,
IN OUT PULONG TotalRequiredLength
)
/*++
Make any changes necessary bring attributes just read in
from disk up to the current revision level format.
When we upgrade our attribute format, we don't bother changing
all data on disk. We take a lazy update approach, and only change
the data as it is changed for other operations. This means that
data we read from disk may be from revision 1. When this is
detected, the data is copied into a current revision structure,
and a pointer to that buffer is returned.
NOTE: For future reference, GROUP and ALIAS objects have
a revision level stored as a "Qualifier" value associated
with the security descriptor attribute. The SERVER object
stores the revision level in its fixed length attributes.
Parameters:
Context - Pointer to an object context block.
AttributeGroup - identifies which kind of attributes are being
validated (SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE_ATTRIBUTES).
Buffer - Pointer to the buffer containing the attributes.
LengthOfDataRead - This is an important value. It must be the value
returned from the registry on the read operation. This tells us
exactly how many bytes of data were retrieved from disk.
TotalRequiredLength - Will be left unchanged if no update was
was required. If an updated was made, this will be adjusted
to reflect the new length of the data.
Return Values:
None.
--*/
{
LARGE_INTEGER
ZeroModifiedCount = {0,0};
PULONG
Pointer;
NTSTATUS
NtStatus = STATUS_SUCCESS;
SAMTRACE("SampUpgradeToCurrentRevision");
//
// Note that Buffer points inside a buffer that is
// hung off the Context block. We don't need to re-allocate
// a new attributes buffer because we are only changing
// fixed-length attributes in this release (and the variable
// length attributes were placed beyond the end of the new
// format fixed-length data).
//
// The approach we take is to copy the current fixed-length
// contents into a temporary buffer, and then copy them back
// into the attribute buffer. This can be done with stack
// variables.
//
//
// Switch on the type of objects that have gone through revision
// changes.
//
switch (Context->ObjectType) {
case SampDomainObjectType:
//
// Domain FIXED_LENGTH attributes have had the following
// revisions:
//
// Revision 0x00010001 - NT1.0 (Revision NOT stored in )
// (record. )
// (Must ascertain revision )
// (by record length. )
//
// Revision 0x00010002 - NT1.0a (Revision is first ULONG )
// (in record. )
if (LengthOfDataRead ==
(sizeof(SAMP_V1_0_FIXED_LENGTH_DOMAIN) +
FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)) ) {
PSAMP_V1_0A_FIXED_LENGTH_DOMAIN
V1aFixed;
SAMP_V1_0_FIXED_LENGTH_DOMAIN
V1Fixed, *OldV1Fixed;
//
// Update from revision 0x00010001
//
// First, copy the current buffer contents into a temporary
// buffer.
//
OldV1Fixed = (PSAMP_V1_0_FIXED_LENGTH_DOMAIN)(Buffer +
FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data));
RtlMoveMemory(&V1Fixed, OldV1Fixed, sizeof(SAMP_V1_0_FIXED_LENGTH_DOMAIN));
//
// Now copy it back in the new format
//
V1aFixed = (PSAMP_V1_0A_FIXED_LENGTH_DOMAIN)OldV1Fixed;
V1aFixed->CreationTime = V1Fixed.CreationTime;
V1aFixed->ModifiedCount = V1Fixed.ModifiedCount;
V1aFixed->MaxPasswordAge = V1Fixed.MaxPasswordAge;
V1aFixed->MinPasswordAge = V1Fixed.MinPasswordAge;
V1aFixed->ForceLogoff = V1Fixed.ForceLogoff;
V1aFixed->NextRid = V1Fixed.NextRid;
V1aFixed->PasswordProperties = V1Fixed.PasswordProperties;
V1aFixed->MinPasswordLength = V1Fixed.MinPasswordLength;
V1aFixed->PasswordHistoryLength = V1Fixed.PasswordHistoryLength;
V1aFixed->ServerState = V1Fixed.ServerState;
V1aFixed->ServerRole = V1Fixed.ServerRole;
V1aFixed->UasCompatibilityRequired = V1Fixed.UasCompatibilityRequired;
//
// And initialize fields new for this revision
//
V1aFixed->Revision = SAMP_REVISION;
V1aFixed->LockoutDuration.LowPart = 0xCF1DCC00; // 30 minutes - low part
V1aFixed->LockoutDuration.HighPart = 0XFFFFFFFB; // 30 minutes - high part
V1aFixed->LockoutObservationWindow.LowPart = 0xCF1DCC00; // 30 minutes - low part
V1aFixed->LockoutObservationWindow.HighPart = 0XFFFFFFFB; // 30 minutes - high part
V1aFixed->LockoutThreshold = 0; // Disabled
if (V1aFixed->ServerRole == DomainServerRolePrimary) {
V1aFixed->ModifiedCountAtLastPromotion = V1Fixed.ModifiedCount;
} else {
V1aFixed->ModifiedCountAtLastPromotion = ZeroModifiedCount;
}
}
break; //out of switch
case SampUserObjectType:
//
// User FIXED_LENGTH attributes have had the following
// revisions:
//
// Revision 0x00010001 - NT1.0 (Revision NOT stored in )
// (record. )
// (Must ascertain revision )
// (by record length. )
//
// Revision 0x00010002 - NT1.0a (Revision is first ULONG )
// (in record. )
// Revision 0x00010002a - NT3.5 (Revision is first ULONG )
// (in record, still )
// (0x00010002. Must )
// (ascertain revison by )
// (by record length )
if (LengthOfDataRead ==
(sizeof(SAMP_V1_FIXED_LENGTH_USER) +
FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)) ) {
PSAMP_V1_0A_FIXED_LENGTH_USER
V1aFixed;
SAMP_V1_FIXED_LENGTH_USER
V1Fixed, *OldV1Fixed;
//
// Update from revision 0x00010001
//
// First, copy the current buffer contents into a temporary
// buffer.
//
OldV1Fixed = (PSAMP_V1_FIXED_LENGTH_USER)(Buffer +
FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data));
RtlMoveMemory(&V1Fixed, OldV1Fixed, sizeof(SAMP_V1_FIXED_LENGTH_USER));
//
// Now copy it back in the new format
//
V1aFixed = (PSAMP_V1_0A_FIXED_LENGTH_USER)OldV1Fixed;
V1aFixed->LastLogon = V1Fixed.LastLogon;
V1aFixed->LastLogoff = V1Fixed.LastLogoff;
V1aFixed->PasswordLastSet = V1Fixed.PasswordLastSet;
V1aFixed->AccountExpires = V1Fixed.AccountExpires;
V1aFixed->UserId = V1Fixed.UserId;
V1aFixed->PrimaryGroupId = V1Fixed.PrimaryGroupId;
V1aFixed->UserAccountControl = V1Fixed.UserAccountControl;
V1aFixed->CountryCode = V1Fixed.CountryCode;
V1aFixed->CodePage = V1Fixed.CodePage;
V1aFixed->BadPasswordCount = V1Fixed.BadPasswordCount;
V1aFixed->LogonCount = V1Fixed.LogonCount;
V1aFixed->AdminCount = V1Fixed.AdminCount;
//
// And initialize fields new for this revision
//
V1aFixed->Revision = SAMP_REVISION;
V1aFixed->LastBadPasswordTime = SampHasNeverTime;
V1aFixed->OperatorCount = 0;
V1aFixed->Unused2 = 0;
} else if ((LengthOfDataRead ==
(sizeof(SAMP_V1_0_FIXED_LENGTH_USER) +
FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)) ) &&
(AttributeGroup == SAMP_FIXED_ATTRIBUTES)) {
PSAMP_V1_0A_FIXED_LENGTH_USER
V1aFixed;
//
// Update from revision 0x00010002
//
// Just set the added field at the end to 0.
//
V1aFixed = (PSAMP_V1_0A_FIXED_LENGTH_USER)(Buffer +
FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data));
V1aFixed->OperatorCount = 0;
V1aFixed->Unused2 = 0;
}
break; //out of switch
case SampGroupObjectType:
//
// Group FIXED_LENGTH attributes have had the following
// revisions:
//
// Revision 0x00010001 - NT1.0 (Revision NOT stored in )
// (record. )
// (Must ascertain revision )
// (by first few ULONGs. )
//
// Revision 0x00010002 - NT1.0a (Revision is first ULONG )
// (in record. )
Pointer = (PULONG) (Buffer + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data));
//
// The old fixed length group had a RID in the first ULONG and
// an attributes field in the second. The attributes are in the
// first and last nibble of the field. Currently, the RID is in
// the second ULONG. Since all RIDs are more than one nibble,
// a rid will always have something set in the middle six nibbles.
//
if ( ( Pointer[0] != SAMP_REVISION ) &&
( ( Pointer[1] & 0x0ffffff0 ) == 0 ) ) {
PSAMP_V1_0A_FIXED_LENGTH_GROUP
V1aFixed;
SAMP_V1_FIXED_LENGTH_GROUP
V1Fixed, *OldV1Fixed;
ULONG TotalLengthRequired;
//
// Calculate the length required for the new group information.
// It is the size of the old group plus enough space for the
// new fields in the new fixed attributes.
//
TotalLengthRequired = SampDwordAlignUlong(
LengthOfDataRead +
sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP) -
sizeof(SAMP_V1_FIXED_LENGTH_GROUP)
);
NtStatus = SampExtendAttributeBuffer(
Context,
TotalLengthRequired
);
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Get the new buffer pointer
//
Buffer = Context->OnDisk;
//
// Move the variable information up to make space for the
// fixed information
//
RtlMoveMemory(
Buffer + SampFixedBufferOffset( Context ) + sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP),
Buffer + SampFixedBufferOffset( Context) + sizeof(SAMP_V1_FIXED_LENGTH_GROUP),
LengthOfDataRead - SampFixedBufferOffset( Context) - sizeof(SAMP_V1_FIXED_LENGTH_GROUP)
);
//
// Update from revision 0x00010001
//
// First, copy the current buffer contents into a temporary
// buffer.
//
OldV1Fixed = (PSAMP_V1_FIXED_LENGTH_GROUP)(Buffer +
FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data));
RtlCopyMemory(&V1Fixed, OldV1Fixed, sizeof(SAMP_V1_FIXED_LENGTH_GROUP));
//
// Now copy it back in the new format
//
V1aFixed = (PSAMP_V1_0A_FIXED_LENGTH_GROUP)OldV1Fixed;
V1aFixed->Revision = SAMP_REVISION;
V1aFixed->Unused1 = 0;
V1aFixed->RelativeId = V1Fixed.RelativeId;
V1aFixed->Attributes = V1Fixed.Attributes;
V1aFixed->AdminCount = (V1Fixed.AdminGroup) ? TRUE : FALSE;
V1aFixed->OperatorCount = 0;
//
// Update the indicator of how long the on disk structure
// is.
//
Context->OnDiskUsed += (sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP) - sizeof(SAMP_V1_FIXED_LENGTH_GROUP));
Context->OnDiskFree = Context->OnDiskAllocated - Context->OnDiskUsed;
}
break;
default:
//
// The rest of the object types have not changed format
// and so need not be updated.
//
break; //out of switch
}
return(NtStatus);
}
PUCHAR
SampObjectAttributeAddress(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex
)
/*++
Retrieve the address of a variable-length attribute. The attributes are
assumed to already be in-memory. The NT3.51-4.0 SAM stores attribute
offsets differently from the NT5 SAM. In the earlier versions (which
exclusively used the registry as the backing store), the attribute offset
value (in SAMP_VARIABLE_LENGTH_ATTRIBUTE) was self-relative to the end
of the attribute array. The NT5 version is self-relative from the start
of the array.
Parameters:
Context - Pointer to an object context block.
AttributeIndex - Indicates the index (into the variable length attribute
array) of the attribute to be retrieved.
Return Values:
STATUS_SUCCESS - The attributes are in-memory.
STATUS_NO_MEMORY - Memory could not be allocated to retrieve the
attributes.
Other values as may be returned by registry API trying to retrieve
the attributes from backing store.
--*/
{
PSAMP_VARIABLE_LENGTH_ATTRIBUTE AttributeArray;
PUCHAR AttributeAddress;
SAMTRACE("SampObjectAttributeAddress");
ASSERT(SampVariableAttributesValid(Context));
AttributeArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE)
SampVariableArrayAddress(Context);
if (IsDsObject(Context))
{
// DS based attribute offsets are relative to the start of the
// attribute array.
AttributeAddress = (PUCHAR)Context->OnDisk +
(SampVariableBufferOffset(Context) +
AttributeArray[AttributeIndex].Offset);
}
else
{
// Registry based attribute offsets are relative to the end of the
// attribute array.
AttributeAddress = (PUCHAR)Context->OnDisk +
(SampVariableDataOffset(Context) +
AttributeArray[AttributeIndex].Offset);
}
return(AttributeAddress);
}
ULONG
SampObjectAttributeLength(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex
)
/*++
Retrieve the length of a variable-length attribute.
The attributes are assumed to already be in-memory.
Parameters:
Context - Pointer to an object context block.
AttributeIndex - Indicates the index (into the variable length attribute
array) of the attribute whose length is to be retrieved.
Return Values:
The length of the attribute (in bytes).
--*/
{
PSAMP_VARIABLE_LENGTH_ATTRIBUTE
AttributeArray;
SAMTRACE("SampObjectAttributeLength");
ASSERT( SampVariableAttributesValid( Context ) );
AttributeArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE)
SampVariableArrayAddress( Context );
return( AttributeArray[AttributeIndex].Length );
}
PULONG
SampObjectAttributeQualifier(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex
)
/*++
Retrieve the address of the qualifier field of a variable-length
attribute.
The attributes are assumed to already be in-memory.
Parameters:
Context - Pointer to an object context block.
AttributeIndex - Indicates the index (into the variable length attribute
array) of the attribute whose qualifier address is to be returned.
Return Values:
The address of the specifed attribute's qualifier field.
--*/
{
PSAMP_VARIABLE_LENGTH_ATTRIBUTE
AttributeArray;
SAMTRACE("SampObjectAttributeQualifier");
ASSERT( SampVariableAttributesValid( Context ) );
AttributeArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE)
SampVariableArrayAddress( Context );
return( &(AttributeArray[AttributeIndex].Qualifier) );
}
NTSTATUS
SampGetAttributeBufferReadInfo(
IN PSAMP_OBJECT Context,
IN ULONG AttributeGroup,
OUT PUCHAR *Buffer,
OUT PULONG BufferLength,
OUT PUNICODE_STRING *KeyAttributeName
)
/*++
Get attribute buffer information needed to read data from
backing store.
If there is currently no attribute buffer, then allocate one.
Parameters:
Context - Pointer to an object context block.
AttributeGroup - Indicates which attribute grouping you are
interested in. This is only interesting if the fixed and
variable-length attributes are stored separately.
Buffer - Receives a pointer to the beginning of the appropriate
buffer (fixed or variable). This will be dword aligned.
If the attributes are stored together, this will point
to the beginning of the fixed-length attributes.
BufferLength - Receives the number of bytes in the buffer.
KeyAttributeName - Receives a pointer to the unicode name of the
attribute to read the attributes from.
Return Values:
STATUS_SUCCESS - The attributes have been read.
STATUS_NO_MEMORY - Memory could not be allocated to receive the
data from disk.
Other values as may be returned reading from disk.
--*/
{
NTSTATUS
NtStatus = STATUS_SUCCESS;
SAMTRACE("SampGetAttributeBufferReadInfo");
//
// If the context block currently has no buffer info, then
// "extend" (create) it so we can return buffer information.
//
if (Context->OnDiskAllocated == 0) {
NtStatus = SampExtendAttributeBuffer(
Context,
SAMP_MINIMUM_ATTRIBUTE_ALLOC
);
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
}
//
// Get the buffer address and length
//
if (SampObjectInformation[Context->ObjectType].FixedStoredSeparately) {
//
// stored separately. Address and length is dependent upon
// what is being asked for. Source registry attribute name
// is also.
//
if (AttributeGroup == SAMP_FIXED_ATTRIBUTES) {
(*Buffer) = Context->OnDisk;
(*BufferLength) = SampVariableBufferOffset( Context );
(*KeyAttributeName) = &SampFixedAttributeName;
} else {
(*Buffer) = SampVariableBufferAddress( Context );
(*BufferLength) = SampVariableBufferLength( Context );
(*KeyAttributeName) = &SampVariableAttributeName;
}
} else {
//
// Attributes stored together - doesn't matter which is being
// asked for.
//
(*Buffer) = Context->OnDisk;
(*BufferLength) = Context->OnDiskAllocated;
(*KeyAttributeName) = &SampCombinedAttributeName;
}
return(NtStatus);
}
NTSTATUS
SampExtendAttributeBuffer(
IN PSAMP_OBJECT Context,
IN ULONG NewSize
)
/*++
This routine extends (or creates) an attribute buffer by allocating
a larger one. It then copies the existing buffer's contents into
the new buffer, if there is an existing buffer.
If a new buffer can not be allocated, then the context block is
returned with the old buffer intact.
If this call succeeds, the buffer will be at least as large as
that asked for (and perhaps larger).
Parameters:
Context - Pointer to an object context block.
NewSize - The number of bytes to allocate for the new buffer.
This value can not be less than the number of bytes currently
in use.
Return Values:
STATUS_SUCCESS - The attributes are in-memory.
STATUS_NO_MEMORY - Memory could not be allocated to retrieve the
attributes.
Other values as may be returned by registry API trying to retrieve
the attributes from backing store.
--*/
{
PUCHAR
OldBuffer;
ULONG
AllocationSize;
SAMTRACE("SampExtendAttributeBuffer");
#if DBG
if ( Context->VariableValid ) {
ASSERT(NewSize >= Context->OnDiskUsed);
}
#endif
//
// Is an allocation necessary?
//
if (NewSize <= Context->OnDiskAllocated) {
return(STATUS_SUCCESS);
}
OldBuffer = Context->OnDisk;
//
// Pad the extend to allow for future edits efficiently.
//
AllocationSize = SampDwordAlignUlong(NewSize + SAMP_MINIMUM_ATTRIBUTE_PAD);
Context->OnDisk = RtlAllocateHeap(
RtlProcessHeap(), 0,
AllocationSize
);
if (Context->OnDisk == NULL) {
Context->OnDisk = OldBuffer;
return(STATUS_NO_MEMORY);
}
//
// Set the new allocated size
Context->OnDiskAllocated = AllocationSize;
//
// If there was no buffer originally, then zero the new buffer, mark
// it as being invalid, and return.
//
if (OldBuffer == NULL) {
RtlZeroMemory( (PVOID)Context->OnDisk, AllocationSize );
Context->FixedDirty = TRUE;
Context->VariableDirty = TRUE;
Context->FixedValid = FALSE;
Context->VariableValid = FALSE;
return(STATUS_SUCCESS);
}
//
// Set the free size. Note that this information is only set if
// the variable data is valid.
// Used size remains the same.
//
if (Context->VariableValid == TRUE) {
Context->OnDiskFree = AllocationSize - Context->OnDiskUsed;
ASSERT(Context->OnDiskUsed == SampDwordAlignUlong(Context->OnDiskUsed));
}
//
// There was an old buffer (or else we would have exited earlier).
// If any data in it was valid, copy it to the new buffer. Free the
// old buffer.
//
if ( Context->FixedValid ) {
RtlCopyMemory(
Context->OnDisk,
OldBuffer,
SampFixedBufferLength( Context ) + SampFixedBufferOffset( Context )
);
}
//
// Note: in thise case we may copy the fixed data twice, since if the
// variable data is not stored separately then SampVariableBufferOffset
// is zero.
//
if ( Context->VariableValid ) {
RtlCopyMemory(
SampVariableBufferAddress( Context ),
OldBuffer + SampVariableBufferOffset( Context ),
Context->OnDiskUsed - SampVariableBufferOffset( Context )
);
}
RtlFreeHeap( RtlProcessHeap(), 0, OldBuffer );
return(STATUS_SUCCESS);
}
NTSTATUS
SampReadRegistryAttribute(
IN HANDLE Key,
IN PUCHAR Buffer,
IN ULONG BufferLength,
IN PUNICODE_STRING AttributeName,
OUT PULONG RequiredLength
)
/*++
Retrieve the address of a variable-length attribute.
The attributes are assumed to already be in-memory.
Parameters:
Key - Handle to the key whose attribute is to be read.
Buffer - Pointer to the buffer to receive the information.
BufferLength - Length of the buffer receiving the information.
AttributeName - The name of the attribute.
Return Values:
STATUS_SUCCESS - Successful completion.
STATUS_BUFFER_TOO_SMALL - The data could not be read because the
buffer was too small.
Other values as may be returned by registry API trying to retrieve
the attribute from backing store.
--*/
{
NTSTATUS
NtStatus;
SAMTRACE("SampReadRegistryAttribute");
//
// Try to read the attribute
//
NtStatus = NtQueryValueKey( Key,
AttributeName, //ValueName,
KeyValuePartialInformation, //KeyValueInformationClass
(PVOID)Buffer,
BufferLength,
RequiredLength
);
SampDumpNtQueryValueKey(AttributeName,
KeyValuePartialInformation,
Buffer,
BufferLength,
RequiredLength);
return(NtStatus);
}
NTSTATUS
SampSetVariableAttribute(
IN PSAMP_OBJECT Context,
IN ULONG AttributeIndex,
IN ULONG Qualifier,
IN PUCHAR Buffer,
IN ULONG Length
)
/*++
This API is used to set a new attribute value. The new attribute
value may be longer, shorter, or the same size as the current
attribute. The data in the attribute buffer will be shifted to
make room for a larger attribute value or to fill in room left by
a smaller attribute value.
PERFORMANCE CONCERN: If you have a lot of attributes to set, it
is worthwhile to start with the smallest indexed attribute
and work up to the largest indexed attribute.
Parameters:
Context - Pointer to an object context block.
AttributeIndex - Indicates the index (into the variable length attribute
array) of the attribute to be set. Typically, all attributes beyond
this one will have their data shifted.
Buffer - The address of the buffer containing the new attribute value.
May be NULL if Length is zero.
Length - The length (in bytes) of the new attribute value.
Return Values:
STATUS_SUCCESS - The service completed successfully.
STATUS_NO_MEMORY - Memory to expand the attribute buffer could not
be allocated.
--*/
{
NTSTATUS
NtStatus;
ULONG
OriginalAttributeLength,
AdditionalSpaceNeeded,
NewBufferLength,
MaximumAttributeIndex,
MoveLength,
i;
LONG
OffsetDelta;
PSAMP_VARIABLE_LENGTH_ATTRIBUTE
AttributeArray;
PUCHAR
NewStart,
OriginalStart;
SAMTRACE("SampSetVariableAttribute");
//
// Make sure the requested attribute exists for the specified
// object type.
//
SampValidateAttributeIndex( Context, AttributeIndex );
//
// Make the data valid
//
NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES );
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Allocate a new buffer if necessary
//
OriginalAttributeLength = SampObjectAttributeLength(Context, AttributeIndex);
if (OriginalAttributeLength < Length) {
AdditionalSpaceNeeded = Length - OriginalAttributeLength;
if (Context->OnDiskFree < AdditionalSpaceNeeded) {
NewBufferLength = Context->OnDiskUsed + AdditionalSpaceNeeded;
ASSERT(NewBufferLength > Context->OnDiskAllocated);
NtStatus = SampExtendAttributeBuffer( Context, NewBufferLength );
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
}
}
//
// Get the address of the attribute array.
//
AttributeArray = SampVariableArrayAddress( Context );
//
// Now shift following attribute values
//
OffsetDelta = (LONG)(SampDwordAlignUlong(Length) -
SampDwordAlignUlong(OriginalAttributeLength));
MaximumAttributeIndex = SampVariableAttributeCount( Context );
if ((OffsetDelta != 0) && (AttributeIndex+1 < MaximumAttributeIndex)) {
//
// Shift all attributes above this one up or down by the OffsetDelta
//
if (IsDsObject(Context))
{
// DS variable-length attribute offsets are relative to the start
// of the variable-length array.
MoveLength = Context->OnDiskUsed -
( SampVariableBufferOffset( Context ) +
AttributeArray[AttributeIndex+1].Offset );
}
else
{
// Registry variable-length attribute offsets are relative to the
// end of the variable-length array.
MoveLength = Context->OnDiskUsed -
( SampVariableDataOffset( Context ) +
AttributeArray[AttributeIndex+1].Offset );
}
//
// Shift the data (if there is any)
//
if (MoveLength != 0) {
OriginalStart = SampObjectAttributeAddress( Context, AttributeIndex+1);
NewStart = (PUCHAR)(OriginalStart + OffsetDelta);
RtlMoveMemory( NewStart, OriginalStart, MoveLength );
}
//
// Adjust the offset pointers
//
for ( i=AttributeIndex+1; i<MaximumAttributeIndex; i++) {
AttributeArray[i].Offset =
(ULONG)(OffsetDelta + (LONG)(AttributeArray[i].Offset));
}
}
//
// Now set the length and qualifier, and copy in the new attribute value
// (if it is non-zero length)
//
AttributeArray[AttributeIndex].Length = Length;
AttributeArray[AttributeIndex].Qualifier = Qualifier;
if (Length != 0) {
RtlCopyMemory( SampObjectAttributeAddress( Context, AttributeIndex ),
Buffer,
Length
);
}
//
// Adjust the Used and Free values
//
Context->OnDiskUsed += OffsetDelta;
Context->OnDiskFree -= OffsetDelta;
ASSERT(Context->OnDiskFree == Context->OnDiskAllocated - Context->OnDiskUsed);
//
// Mark the variable attributes dirty
//
Context->VariableDirty = TRUE;
#ifdef SAM_DEBUG_ATTRIBUTES
if (SampDebugAttributes) {
DbgPrint("SampSetVariableAttribute %d to length %#x, qualifier %#x:\n", AttributeIndex, Length, Qualifier);
SampDumpAttributes(Context);
}
#endif
return(STATUS_SUCCESS);
}
VOID
SampFreeAttributeBuffer(
IN PSAMP_OBJECT Context
)
/*++
Free the buffer used to keep in-memory copies of the on-disk
object attributes.
Parameters:
Context - Pointer to the object context whose buffer is to
be freed.
Return Values:
None.
--*/
{
#if DBG
if ( Context->FixedValid ) { ASSERT(Context->FixedDirty == FALSE); }
if ( Context->VariableValid) { ASSERT(Context->VariableDirty == FALSE); }
ASSERT(Context->OnDisk != NULL);
ASSERT(Context->OnDiskAllocated != 0);
#endif
SAMTRACE("SampFreeAttributeBuffer");
RtlFreeHeap( RtlProcessHeap(), 0, Context->OnDisk );
Context->OnDisk = NULL;
Context->OnDiskAllocated = 0;
//
// Mark all attributes as invalid
//
Context->FixedValid = FALSE;
Context->VariableValid = FALSE;
return;
}
#ifdef SAM_DEBUG_ATTRIBUTES
VOID
SampDumpAttributes(
IN PSAMP_OBJECT Context
)
/*++
This is a debug-only API to dump out the attributes for a context
to the kernel debugger.
Parameters:
Context - Pointer to an object context block.
Return Values:
None.
--*/
{
ULONG Index;
PSAMP_OBJECT_INFORMATION ObjectTypeInfo = &SampObjectInformation[Context->ObjectType];
PSAMP_VARIABLE_LENGTH_ATTRIBUTE AttributeArray;
AttributeArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE)
SampVariableArrayAddress( Context );
DbgPrint("Dumping context 0x%x\n", Context);
DbgPrint("\n");
DbgPrint("TYPE INFO\n");
DbgPrint("Object type name = %wZ\n", &ObjectTypeInfo->ObjectTypeName);
DbgPrint("Fixed stored separately = %s\n", ObjectTypeInfo->FixedStoredSeparately ? "TRUE" : "FALSE");
DbgPrint("Fixed attributes offset = %#x\n", ObjectTypeInfo->FixedAttributesOffset);
DbgPrint("Fixed attributes size = %#x\n", ObjectTypeInfo->FixedLengthSize);
DbgPrint("Variable buffer offset = %#x\n", ObjectTypeInfo->VariableBufferOffset);
DbgPrint("Variable array offset = %#x\n", ObjectTypeInfo->VariableArrayOffset);
DbgPrint("Variable data offset = %#x\n", ObjectTypeInfo->VariableDataOffset);
DbgPrint("Variable attribute count = %d\n", ObjectTypeInfo->VariableAttributeCount);
DbgPrint("\n");
DbgPrint("INSTANCE INFO\n");
DbgPrint("RootName = %wZ\n", &Context->RootName);
DbgPrint("Fixed Valid = %s\n", Context->FixedValid ? "TRUE" : "FALSE");
DbgPrint("Variable Valid = %s\n", Context->VariableValid ? "TRUE" : "FALSE");
DbgPrint("Fixed Dirty = %s\n", Context->FixedDirty ? "TRUE" : "FALSE");
DbgPrint("Variable Dirty = %s\n", Context->VariableDirty ? "TRUE" : "FALSE");
DbgPrint("OnDiskAllocated = %#x\n", Context->OnDiskAllocated);
DbgPrint("OnDiskUsed = %#x\n", Context->OnDiskUsed);
DbgPrint("OnDiskFree = %#x\n", Context->OnDiskFree);
DbgPrint("\n");
if ( Context->VariableValid ) {
for (Index = 0; Index < ObjectTypeInfo->VariableAttributeCount; Index ++) {
DbgPrint("Attr %d: Qualifier = %#6x, Offset = %#6x, Length = %#6x\n",
Index,
AttributeArray[Index].Qualifier,
AttributeArray[Index].Offset,
AttributeArray[Index].Length
);
SampDumpData(SampObjectAttributeAddress(Context, Index),
SampObjectAttributeLength(Context, Index));
}
}
DbgPrint("\n\n");
}
VOID
SampDumpData(
IN PVOID Buffer,
IN ULONG Length
)
/*++
This is a debug-only API to dump out a buffer in hex
Parameters:
Buffer - Pointer to data
Length - number of bytes in data
Return Values:
None.
--*/
{
ULONG Index;
for (Index = 0; Index < Length; Index ++) {
ULONG Value = (ULONG)(((PBYTE)Buffer)[Index]);
if ((Index % 16) == 0) {
DbgPrint("\n ");
}
DbgPrint("%02x ", Value & 0xff);
}
if (Length > 0) {
DbgPrint("\n\n");
}
}
#endif