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