Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

802 lines
19 KiB

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
reglayer.c
Abstract:
Implemntation of LSA/Registry interface and support routines
Author:
Mac McLain (MacM) Jan 17, 1997
Environment:
User Mode
Revision History:
--*/
#include <lsapch2.h>
#include <dbp.h>
NTSTATUS
LsapRegReadObjectSD(
IN LSAPR_HANDLE ObjectHandle,
OUT PSECURITY_DESCRIPTOR *ppSD
)
/*++
Routine Description:
This function will ready the security descriptor from the specified object
Arguments:
ObjectHandle - Object to read the SD from
ppSD -- Where the allocated security descriptor is returned. Allocated via
LsapAllocateLsaHeap.
Return Value:
Pointer to allocated memory on success or NULL on failure
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG SecurityDescriptorLength = 0;
Status = LsapDbReadAttributeObject(
ObjectHandle,
&LsapDbNames[SecDesc],
NULL,
&SecurityDescriptorLength
);
if ( NT_SUCCESS(Status ) ) {
//
// Allocate a buffer from the Lsa Heap for the existing object's SD.
//
*ppSD = LsapAllocateLsaHeap( SecurityDescriptorLength );
if ( *ppSD == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
//
// Read the SD. It is the value of the SecDesc subkey.
//
Status = LsapDbReadAttributeObject(
ObjectHandle,
&LsapDbNames[SecDesc],
*ppSD,
&SecurityDescriptorLength
);
if ( !NT_SUCCESS( Status ) ) {
LsapFreeLsaHeap( *ppSD );
*ppSD = NULL;
}
}
}
return( Status );
}
NTSTATUS
LsapRegGetPhysicalObjectName(
IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation,
IN PUNICODE_STRING LogicalNameU,
OUT OPTIONAL PUNICODE_STRING PhysicalNameU
)
/*++
Routine Description:
This function returns the Physical Name of an object
given an object information buffer. Memory will be allocated for
the Unicode String Buffers that will receive the name(s).
The Physical Name of an object is the full path of the object relative
to the root ot the Database. It is computed by concatenating the Physical
Name of the Container Object (if any), the Classifying Directory
corresponding to the object type id, and the Logical Name of the
object.
<Physical Name of Object> =
[<Physical Name of Container Object> "\"]
[<Classifying Directory> "\"] <Logical Name of Object>
If there is no Container Object (as in the case of the Policy object)
the <Physical Name of Container Object> and following \ are omitted.
If there is no Classifying Directory (as in the case of the Policy object)
the <Classifying Directory> and following \ are omitted. If neither
Container Object not Classifying Directory exist, the Logical and Physical
names coincide.
Note that memory is allocated by this routine for the output
Unicode string buffer(s). When the output Unicode String(s) are no
longer needed, the memory must be freed by call(s) to
RtlFreeUnicodeString().
Arguments:
ObjectInformation - Pointer to object information containing as a minimum
the object's Logical Name, Container Object's handle and object type
id.
LogicalNameU - Optional pointer to Unicode String structure which will
receive the Logical Name of the object. A buffer will be allocated
by this routine for the name text. This memory must be freed when no
longer needed by calling RtlFreeUnicodeString() wiht a pointer such
as LogicalNameU to the Unicode String structure.
PhysicalNameU - Optional pointer to Unicode String structure which will
receive the Physical Name of the object. A buffer will be allocated by
this routine for the name text. This memory must be freed when no
longer needed by calling RtlFreeUnicodeString() with a pointer such as
PhysicalNameU to the Unicode String structure.
Return Value:
NTSTATUS - Standard Nt Result Code
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources to
allocate the name string buffer for the Physical Name or
Logical Name.
--*/
{
NTSTATUS Status;
PUNICODE_STRING ContainerPhysicalNameU = NULL;
PUNICODE_STRING ClassifyingDirU = NULL;
UNICODE_STRING IntermediatePath1U;
PUNICODE_STRING JoinedPath1U = &IntermediatePath1U;
LSAP_DB_OBJECT_TYPE_ID ObjectTypeId = ObjectInformation->ObjectTypeId;
POBJECT_ATTRIBUTES ObjectAttributes = &ObjectInformation->ObjectAttributes;
//
// Initialize
//
RtlInitUnicodeString( &IntermediatePath1U, NULL );
//
// The Physical Name of the object is requested. Construct this
// in stages. First, get the Container Object Physical Name from
// the handle stored inside ObjectAttributes.
//
if (ObjectAttributes->RootDirectory != NULL) {
ContainerPhysicalNameU =
&(((LSAP_DB_HANDLE)
ObjectAttributes->RootDirectory)->PhysicalNameU);
}
//
// Next, get the Classifying Directory name appropriate to the
// object type.
//
if (LsapDbContDirs[ObjectTypeId].Length != 0) {
ClassifyingDirU = &LsapDbContDirs[ObjectTypeId];
}
//
// Now join the Physical Name of the Container Object and Classifying
// Directory together. If there is no Container Object and no
// Classifying Directory, just set the result to NULL.
//
if (ContainerPhysicalNameU == NULL && ClassifyingDirU == NULL) {
JoinedPath1U = NULL;
} else {
Status = LsapDbJoinSubPaths(
ContainerPhysicalNameU,
ClassifyingDirU,
JoinedPath1U
);
if (!NT_SUCCESS(Status)) {
goto GetNamesError;
}
}
//
// Now join the Physical Name of the Containing Object, Classifying
// Directory and Logical Name of the object together. Note that
// JoinedPath1U may be NULL, but LogicalNameU is never NULL.
//
Status = LsapDbJoinSubPaths(
JoinedPath1U,
LogicalNameU,
PhysicalNameU
);
if (JoinedPath1U != NULL) {
RtlFreeUnicodeString( JoinedPath1U );
JoinedPath1U = NULL; // so we don't try to free it again
}
if (!NT_SUCCESS(Status)) {
goto GetNamesError;
}
goto GetNamesFinish;
GetNamesError:
//
// If necessary, free any string buffer allocated to JoinedPath1U
//
if (JoinedPath1U != NULL) {
RtlFreeUnicodeString( JoinedPath1U );
}
GetNamesFinish:
return( Status );
}
NTSTATUS
LsapRegOpenObject(
IN LSAP_DB_HANDLE ObjectHandle,
IN ULONG OpenMode,
OUT PVOID *pvKey
)
/*++
Routine Description:
Opens the object in the LSA registry database
Arguments:
ObjectHandle - Internal LSA object handle
OpenMode - How to open the object
pvKey - Where the key is returned
Return Value:
NTSTATUS - Standard Nt Result Code
--*/
{
NTSTATUS Status;
OBJECT_ATTRIBUTES OpenKeyObjectAttributes;
//
// Setup Object Attributes structure for opening the Registry key of
// the object. Specify as path the Physical Name of the object, this
// being the path of the object's Registry Key relative to the
// LSA Database root key.
//
InitializeObjectAttributes(
&OpenKeyObjectAttributes,
&(ObjectHandle->PhysicalNameU),
OBJ_CASE_INSENSITIVE,
LsapDbState.DbRootRegKeyHandle,
NULL
);
//
// Now attempt to open the object's Registry Key. Store the Registry
// Key handle in the object's handle.
//
Status = RtlpNtOpenKey(
(PHANDLE) pvKey,
KEY_READ | KEY_WRITE,
&OpenKeyObjectAttributes,
0L
);
return( Status );
}
NTSTATUS
LsapRegOpenTransaction(
)
/*++
Routine Description:
This function starts a transaction within the LSA Database.
WARNING: The Lsa Database must be in the locked state when this function
is called.
Arguments:
None.
Return Value:
NTSTATUS - Standard Nt Result Code
Result codes are those returned from the Registry Transaction
Package.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
LsapDsDebugOut(( DEB_FTRACE, "LsapRegOpenTransaction\n" ));
LsapDbLockAcquire( &LsapDbState.RegistryLock );
ASSERT( LsapDbState.RegistryTransactionOpen == FALSE );
Status = RtlStartRXact(LsapDbState.RXactContext);
ASSERT( NT_SUCCESS( Status ) || Status == STATUS_NO_MEMORY );
if ( NT_SUCCESS( Status ) ) {
LsapDbState.RegistryTransactionOpen = TRUE;
LsapDbState.RegistryModificationCount = 0;
} else {
LsapDbLockRelease( &LsapDbState.RegistryLock );
}
LsapDsDebugOut(( DEB_FTRACE, "LsapRegOpenTransaction: 0x%lx\n", Status ));
return Status;
}
NTSTATUS
LsapRegApplyTransaction(
)
/*++
Routine Description:
This function applies a transaction within the LSA Database.
WARNING: The Lsa Database must be in the locked state when this function
is called.
Arguments:
ObjectHandle - Handle to an LSA object. This is expected to have
already been validated.
Options - Specifies optional actions to be taken. The following
options are recognized, other options relevant to calling routines
are ignored.
LSAP_DB_OMIT_REPLICATOR_NOTIFICATION - Omit notification to
Replicator.
Return Value:
NTSTATUS - Standard Nt Result Code
Result codes are those returned from the Registry Transaction
Package.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
LsapDsDebugOut(( DEB_FTRACE, "LsapRegApplyTransaction\n" ));
//
ASSERT( LsapDbState.RegistryTransactionOpen == TRUE );
//
// Apply the Registry Transaction.
//
if ( LsapDbState.RegistryModificationCount > 0 ) {
Status = RtlApplyRXact(LsapDbState.RXactContext);
} else {
Status = RtlAbortRXact(LsapDbState.RXactContext);
}
if ( NT_SUCCESS( Status ) ) {
LsapDbState.RegistryTransactionOpen = FALSE;
LsapDbState.RegistryModificationCount = 0;
LsapDbLockRelease( &LsapDbState.RegistryLock );
}
LsapDsDebugOut(( DEB_FTRACE, "LsapRegApplyTransaction: 0x%lx\n", Status ));
return( Status );
}
NTSTATUS
LsapRegAbortTransaction(
)
/*++
Routine Description:
This function aborts a transaction within the LSA Database.
WARNING: The Lsa Database must be in the locked state when this function
is called.
Arguments:
None.
Return Value:
NTSTATUS - Standard Nt Result Code
Result codes are those returned from the Registry Transaction
Package.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
LsapDsDebugOut(( DEB_FTRACE, "LsapRegAbortTransaction\n" ));
ASSERT( LsapDbState.RegistryTransactionOpen == TRUE );
Status = RtlAbortRXact(LsapDbState.RXactContext);
if ( NT_SUCCESS( Status ) ) {
LsapDbState.RegistryTransactionOpen = FALSE;
LsapDbState.RegistryModificationCount = 0;
}
ASSERT( NT_SUCCESS( Status ) );
LsapDbLockRelease( &LsapDbState.RegistryLock );
LsapDsDebugOut(( DEB_FTRACE, "LsapRegAbortTransaction: 0x%lx\n", Status ));
return( Status );
}
NTSTATUS
LsapRegCreateObject(
IN PUNICODE_STRING ObjectPath,
IN LSAP_DB_OBJECT_TYPE_ID ObjectType
)
{
NTSTATUS Status;
LsapDsDebugOut(( DEB_FTRACE, "LsapRegCreateObject\n" ));
Status = RtlAddActionToRXact(
LsapDbState.RXactContext,
RtlRXactOperationSetValue,
ObjectPath,
ObjectType,
NULL, // No Key Value needed
0L
);
if ( NT_SUCCESS( Status ) ) {
LsapDbState.RegistryModificationCount++;
}
LsapDsDebugOut(( DEB_FTRACE, "LsapRegCreateObjectL 0x%lx\n", Status ));
return( Status );
}
NTSTATUS
LsapRegDeleteObject(
IN PUNICODE_STRING ObjectPath
)
{
NTSTATUS Status;
LsapDsDebugOut(( DEB_FTRACE, "LsapRegDeleteObject\n" ));
Status = RtlAddActionToRXact(
LsapDbState.RXactContext,
RtlRXactOperationDelete,
ObjectPath,
0L,
NULL,
0
);
if ( NT_SUCCESS( Status ) ) {
LsapDbState.RegistryModificationCount++;
}
LsapDsDebugOut(( DEB_FTRACE, "LsapRegDeleteObject: 0x%lx\n", Status ));
return( Status );
}
NTSTATUS
LsapRegWriteAttribute(
IN PUNICODE_STRING AttributePath,
IN PVOID pvAttribute,
IN ULONG AttributeLength
)
{
NTSTATUS Status;
LsapDsDebugOut(( DEB_FTRACE, "LsapRegWriteAttribute\n" ));
Status = RtlAddActionToRXact(
LsapDbState.RXactContext,
RtlRXactOperationSetValue,
AttributePath,
0L,
pvAttribute,
AttributeLength
);
if ( NT_SUCCESS( Status ) ) {
LsapDbState.RegistryModificationCount++;
}
LsapDsDebugOut(( DEB_FTRACE, "LsapRegWriteAttribute: 0x%lx\n", Status ));
return( Status );
}
NTSTATUS
LsapRegDeleteAttribute(
IN PUNICODE_STRING AttributePath,
IN BOOLEAN DeleteSecurely,
IN ULONG AttributeLength
)
/*++
Routine Description:
Deletes a registry attribute
Arguments:
AttributePath full pathname of attribute to delete
DeleteSecurely fill value with zero prior to deletion?
AttributeLength number of bytes to fill with zero (must be equal to
actual length of the attribute for secure deletion to
work); ignored if DeleteSecurely is FALSE
Returns:
STATUS_SUCCESS if happy
STATUS_ error code otherwise
--*/
{
NTSTATUS Status;
PBYTE Buffer = NULL;
LsapDsDebugOut(( DEB_FTRACE, "LsapRegDeleteAttribute\n" ));
if ( DeleteSecurely &&
AttributeLength > 0 ) {
Buffer = ( PBYTE )LsapAllocateLsaHeap( AttributeLength );
if ( Buffer == NULL ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// NOTE: LsapAllocateLsaHeap returns memory that is zero-filled
// but even if it didn't, filling the secret value with
// random junk is just as good
//
Status = LsapRegWriteAttribute(
AttributePath,
Buffer,
AttributeLength
);
if ( !NT_SUCCESS( Status )) {
LsapFreeLsaHeap( Buffer );
return Status;
}
}
Status = RtlAddActionToRXact(
LsapDbState.RXactContext,
RtlRXactOperationDelete,
AttributePath,
0L,
NULL,
0
);
if ( NT_SUCCESS( Status ) ) {
LsapDbState.RegistryModificationCount++;
}
LsapDsDebugOut(( DEB_FTRACE, "LsapRegDeleteAttribute: 0x%lx\n", Status ));
LsapFreeLsaHeap( Buffer );
return( Status );
}
NTSTATUS
LsapRegReadAttribute(
IN LSAPR_HANDLE ObjectHandle,
IN PUNICODE_STRING AttributeName,
IN OPTIONAL PVOID AttributeValue,
IN OUT PULONG AttributeValueLength
)
{
//
// The LSA Database is implemented as a subtree of the Configuration
// Registry. In this implementation, Lsa Database objects correspond
// to Registry keys and "attributes" and their "values" correspond to
// Registry "subkeys" and "values" of the Registry key representing the
// object.
//
NTSTATUS Status, SecondaryStatus;
ULONG SubKeyValueActualLength;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE SubKeyHandle = NULL;
LSAP_DB_HANDLE InternalHandle = (LSAP_DB_HANDLE) ObjectHandle;
//
// Reading an attribute of an object is simpler than writing one,
// because the Registry Transaction package is not used. Since an
// attribute is stored as the value of a subkey of the object's
// Registry Key, we can simply call the Registry API RtlpNtReadKey
// specifying the relative name of the subkey and the parent key's
// handle.
//
// Prior to opening the subkey in the Registry, setup ObjectAttributes
// containing the SubKey name and the Registry Handle for the LSA Database
// Root.
//
InitializeObjectAttributes(
&ObjectAttributes,
AttributeName,
OBJ_CASE_INSENSITIVE,
InternalHandle->KeyHandle,
NULL
);
//
// Open the subkey
//
Status = RtlpNtOpenKey(
&SubKeyHandle,
KEY_READ,
&ObjectAttributes,
0L
);
if (!NT_SUCCESS(Status)) {
SubKeyHandle = NULL; //For error processing
return(Status);
}
//
// Now query the size of the buffer required to read the subkey's
// value.
//
SubKeyValueActualLength = *AttributeValueLength;
//
// If a NULL buffer parameter has been supplied or the size of the
// buffer given is 0, this is just a size query.
//
if (!ARGUMENT_PRESENT(AttributeValue) || *AttributeValueLength == 0) {
Status = RtlpNtQueryValueKey(
SubKeyHandle,
NULL,
NULL,
&SubKeyValueActualLength,
NULL
);
if ((Status == STATUS_BUFFER_OVERFLOW) || NT_SUCCESS(Status)) {
*AttributeValueLength = SubKeyValueActualLength;
Status = STATUS_SUCCESS;
goto ReadAttError;
} else {
goto ReadAttError;
}
}
//
// Supplied buffer is large enough to hold the SubKey's value.
// Query the value.
//
Status = RtlpNtQueryValueKey(
SubKeyHandle,
NULL,
AttributeValue,
&SubKeyValueActualLength,
NULL
);
if( (Status == STATUS_BUFFER_OVERFLOW) || NT_SUCCESS(Status) ) {
//
// Return the length of the Sub Key.
//
*AttributeValueLength = SubKeyValueActualLength;
}
ReadAttFinish:
//
// If necessary, close the Sub Key
//
if (SubKeyHandle != NULL) {
SecondaryStatus = NtClose( SubKeyHandle );
}
return(Status);
ReadAttError:
goto ReadAttFinish;
}