mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 10:04:49 +00:00
fe23a4aaeb
The PR #6649 which fixed an issue with orphaned KCBs leaking in memory which also pointed to unloaded registry hives, it also brought a problem. In CmpEnumerateOpenSubKeys there is a risk of getting hit by a deadlock as we enumerate the cache table to remove empty cache entries. Fundamentally CmpEnumerateOpenSubKeys locks down a KCB from cache for exclusive use in order to tear down its contents from memory but it doesn't address the fact a KCB might have already been locked in the same calling thread, leading to a recursion. This leads to random hangs when unloading a hive during system startup (tipically on a clean install). The solution here is to simply lock the whole registry when we unload a hive so that we don't have to worry the KCBs are getting tampered by anybody else. This also simplifies the code. Although locking the entire registry while other apps are doing registry related operations to other hives can cause overhead. If this turns out to be bad then we have to rethink the locking mechanism here. CORE-19539
1953 lines
59 KiB
C
1953 lines
59 KiB
C
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: ntoskrnl/config/ntapi.c
|
|
* PURPOSE: Configuration Manager - Internal Registry APIs
|
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
|
* Eric Kohl
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include "ntoskrnl.h"
|
|
#define NDEBUG
|
|
#include "debug.h"
|
|
|
|
BOOLEAN CmBootAcceptFirstTime = TRUE;
|
|
BOOLEAN CmFirstTime = TRUE;
|
|
extern ULONG InitSafeBootMode;
|
|
|
|
|
|
/* PRIVATE FUNCTIONS *********************************************************/
|
|
|
|
/*
|
|
* Adapted from ntoskrnl/include/internal/ob_x.h:ObpReleaseObjectCreateInformation()
|
|
*/
|
|
VOID
|
|
ReleaseCapturedObjectAttributes(
|
|
_In_ POBJECT_ATTRIBUTES CapturedObjectAttributes,
|
|
_In_ KPROCESSOR_MODE AccessMode)
|
|
{
|
|
/* Check if we have a security descriptor */
|
|
if (CapturedObjectAttributes->SecurityDescriptor)
|
|
{
|
|
/* Release it */
|
|
SeReleaseSecurityDescriptor(CapturedObjectAttributes->SecurityDescriptor,
|
|
AccessMode,
|
|
TRUE);
|
|
CapturedObjectAttributes->SecurityDescriptor = NULL;
|
|
}
|
|
|
|
/* Check if we have an object name */
|
|
if (CapturedObjectAttributes->ObjectName)
|
|
{
|
|
/* Release it */
|
|
ReleaseCapturedUnicodeString(CapturedObjectAttributes->ObjectName, AccessMode);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Adapted from ntoskrnl/ob/oblife.c:ObpCaptureObjectCreateInformation()
|
|
*/
|
|
NTSTATUS
|
|
ProbeAndCaptureObjectAttributes(
|
|
_Out_ POBJECT_ATTRIBUTES CapturedObjectAttributes,
|
|
_Out_ PUNICODE_STRING ObjectName,
|
|
_In_ KPROCESSOR_MODE AccessMode,
|
|
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
|
|
_In_ BOOLEAN CaptureSecurity)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
|
// PSECURITY_QUALITY_OF_SERVICE SecurityQos;
|
|
PUNICODE_STRING LocalObjectName = NULL;
|
|
|
|
/* Zero out the Capture Data */
|
|
RtlZeroMemory(CapturedObjectAttributes, sizeof(*CapturedObjectAttributes));
|
|
|
|
/* SEH everything here for protection */
|
|
_SEH2_TRY
|
|
{
|
|
/* Check if we got attributes */
|
|
if (ObjectAttributes)
|
|
{
|
|
/* Check if we're in user mode */
|
|
if (AccessMode != KernelMode)
|
|
{
|
|
/* Probe the attributes */
|
|
ProbeForRead(ObjectAttributes,
|
|
sizeof(OBJECT_ATTRIBUTES),
|
|
sizeof(ULONG));
|
|
}
|
|
|
|
/* Validate the Size and Attributes */
|
|
if ((ObjectAttributes->Length != sizeof(OBJECT_ATTRIBUTES)) ||
|
|
(ObjectAttributes->Attributes & ~OBJ_VALID_KERNEL_ATTRIBUTES)) // Understood as all the possible valid attributes
|
|
{
|
|
/* Invalid combination, fail */
|
|
_SEH2_YIELD(return STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
/* Set some Create Info and do not allow user-mode kernel handles */
|
|
CapturedObjectAttributes->Length = sizeof(OBJECT_ATTRIBUTES);
|
|
CapturedObjectAttributes->RootDirectory = ObjectAttributes->RootDirectory;
|
|
CapturedObjectAttributes->Attributes = ObpValidateAttributes(ObjectAttributes->Attributes, AccessMode);
|
|
LocalObjectName = ObjectAttributes->ObjectName;
|
|
SecurityDescriptor = ObjectAttributes->SecurityDescriptor;
|
|
// SecurityQos = ObjectAttributes->SecurityQualityOfService;
|
|
|
|
/* Check if we have a security descriptor */
|
|
if (CaptureSecurity && SecurityDescriptor)
|
|
{
|
|
/*
|
|
* Capture it.
|
|
* Note: This has an implicit memory barrier due
|
|
* to the function call, so cleanup is safe here.
|
|
*/
|
|
Status = SeCaptureSecurityDescriptor(SecurityDescriptor,
|
|
AccessMode,
|
|
NonPagedPool,
|
|
TRUE,
|
|
&CapturedObjectAttributes->
|
|
SecurityDescriptor);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Capture failed, quit */
|
|
CapturedObjectAttributes->SecurityDescriptor = NULL;
|
|
_SEH2_YIELD(return Status);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CapturedObjectAttributes->SecurityDescriptor = NULL;
|
|
}
|
|
|
|
#if 0
|
|
// We don't use the QoS!
|
|
|
|
/* Check if we have QoS */
|
|
if (SecurityQos)
|
|
{
|
|
/* Check if we came from user mode */
|
|
if (AccessMode != KernelMode)
|
|
{
|
|
/* Validate the QoS */
|
|
ProbeForRead(SecurityQos,
|
|
sizeof(SECURITY_QUALITY_OF_SERVICE),
|
|
sizeof(ULONG));
|
|
}
|
|
|
|
/* Save Info */
|
|
CapturedObjectAttributes->SecurityQualityOfService = *SecurityQos;
|
|
CapturedObjectAttributes->SecurityQos =
|
|
&CapturedObjectAttributes->SecurityQualityOfService;
|
|
}
|
|
#else
|
|
CapturedObjectAttributes->SecurityQualityOfService = NULL;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
/* We don't have a name */
|
|
LocalObjectName = NULL;
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(ExSystemExceptionFilter())
|
|
{
|
|
/* Cleanup and return the exception code */
|
|
ReleaseCapturedObjectAttributes(CapturedObjectAttributes, AccessMode);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Now check if the Object Attributes had an Object Name */
|
|
if (LocalObjectName)
|
|
{
|
|
Status = ProbeAndCaptureUnicodeString(ObjectName, AccessMode, LocalObjectName);
|
|
}
|
|
else
|
|
{
|
|
/* Clear the string */
|
|
RtlInitEmptyUnicodeString(ObjectName, NULL, 0);
|
|
|
|
/* It cannot have specified a Root Directory */
|
|
if (CapturedObjectAttributes->RootDirectory)
|
|
{
|
|
Status = STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
}
|
|
|
|
/* Set the caputured object attributes name pointer to the one the user gave to us */
|
|
CapturedObjectAttributes->ObjectName = ObjectName;
|
|
|
|
/* Cleanup if we failed */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ReleaseCapturedObjectAttributes(CapturedObjectAttributes, AccessMode);
|
|
}
|
|
|
|
/* Return status to caller */
|
|
return Status;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
CmpConvertHandleToKernelHandle(
|
|
_In_ HANDLE SourceHandle,
|
|
_In_opt_ POBJECT_TYPE ObjectType,
|
|
_In_ ACCESS_MASK DesiredAccess,
|
|
_In_ KPROCESSOR_MODE AccessMode,
|
|
_Out_ PHANDLE KernelHandle)
|
|
{
|
|
NTSTATUS Status;
|
|
PVOID Object;
|
|
|
|
*KernelHandle = NULL;
|
|
|
|
/* NULL handle is valid */
|
|
if (SourceHandle == NULL)
|
|
return STATUS_SUCCESS;
|
|
|
|
/* Get the object pointer */
|
|
Status = ObReferenceObjectByHandle(SourceHandle,
|
|
DesiredAccess,
|
|
ObjectType,
|
|
AccessMode,
|
|
&Object,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
/* Create a kernel handle from the pointer */
|
|
Status = ObOpenObjectByPointer(Object,
|
|
OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
DesiredAccess,
|
|
ObjectType,
|
|
KernelMode,
|
|
KernelHandle);
|
|
|
|
/* Dereference the object */
|
|
ObDereferenceObject(Object);
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtCreateKey(OUT PHANDLE KeyHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
|
IN ULONG TitleIndex,
|
|
IN PUNICODE_STRING Class OPTIONAL,
|
|
IN ULONG CreateOptions,
|
|
OUT PULONG Disposition OPTIONAL)
|
|
{
|
|
NTSTATUS Status;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
CM_PARSE_CONTEXT ParseContext = {0};
|
|
HANDLE Handle;
|
|
PAGED_CODE();
|
|
|
|
DPRINT("NtCreateKey(Path: %wZ, Root %x, Access: %x, CreateOptions %x)\n",
|
|
ObjectAttributes->ObjectName, ObjectAttributes->RootDirectory,
|
|
DesiredAccess, CreateOptions);
|
|
|
|
/* Ignore the WOW64 flag, it's not valid in the kernel */
|
|
DesiredAccess &= ~KEY_WOW64_RES;
|
|
|
|
/* Check for user-mode caller */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Prepare to probe parameters */
|
|
_SEH2_TRY
|
|
{
|
|
/* Check if we have a class */
|
|
if (Class)
|
|
{
|
|
/* Probe it */
|
|
ParseContext.Class = ProbeForReadUnicodeString(Class);
|
|
ProbeForRead(ParseContext.Class.Buffer,
|
|
ParseContext.Class.Length,
|
|
sizeof(WCHAR));
|
|
}
|
|
|
|
/* Probe the key handle */
|
|
ProbeForWriteHandle(KeyHandle);
|
|
*KeyHandle = NULL;
|
|
|
|
/* Probe object attributes */
|
|
ProbeForRead(ObjectAttributes,
|
|
sizeof(OBJECT_ATTRIBUTES),
|
|
sizeof(ULONG));
|
|
|
|
if (Disposition)
|
|
ProbeForWriteUlong(Disposition);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else
|
|
{
|
|
/* Save the class directly */
|
|
if (Class) ParseContext.Class = *Class;
|
|
}
|
|
|
|
/* Setup the parse context */
|
|
ParseContext.CreateOperation = TRUE;
|
|
ParseContext.CreateOptions = CreateOptions;
|
|
|
|
/* Do the create */
|
|
Status = ObOpenObjectByName(ObjectAttributes,
|
|
CmpKeyObjectType,
|
|
PreviousMode,
|
|
NULL,
|
|
DesiredAccess,
|
|
&ParseContext,
|
|
&Handle);
|
|
|
|
_SEH2_TRY
|
|
{
|
|
/* Return data to user */
|
|
if (NT_SUCCESS(Status)) *KeyHandle = Handle;
|
|
if (Disposition) *Disposition = ParseContext.Disposition;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the status */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
DPRINT("Returning handle %x, Status %x.\n", Handle, Status);
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtOpenKey(OUT PHANDLE KeyHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes)
|
|
{
|
|
CM_PARSE_CONTEXT ParseContext = {0};
|
|
HANDLE Handle;
|
|
NTSTATUS Status;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
PAGED_CODE();
|
|
DPRINT("NtOpenKey(Path: %wZ, Root %x, Access: %x)\n",
|
|
ObjectAttributes->ObjectName, ObjectAttributes->RootDirectory, DesiredAccess);
|
|
|
|
/* Ignore the WOW64 flag, it's not valid in the kernel */
|
|
DesiredAccess &= ~KEY_WOW64_RES;
|
|
|
|
/* Check for user-mode caller */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Prepare to probe parameters */
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the key handle */
|
|
ProbeForWriteHandle(KeyHandle);
|
|
*KeyHandle = NULL;
|
|
|
|
/* Probe object attributes */
|
|
ProbeForRead(ObjectAttributes,
|
|
sizeof(OBJECT_ATTRIBUTES),
|
|
sizeof(ULONG));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Just let the object manager handle this */
|
|
Status = ObOpenObjectByName(ObjectAttributes,
|
|
CmpKeyObjectType,
|
|
PreviousMode,
|
|
NULL,
|
|
DesiredAccess,
|
|
&ParseContext,
|
|
&Handle);
|
|
|
|
/* Only do this if we succeeded */
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
/* Return the handle to caller */
|
|
*KeyHandle = Handle;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the status */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
DPRINT("Returning handle %x, Status %x.\n", Handle, Status);
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtDeleteKey(IN HANDLE KeyHandle)
|
|
{
|
|
PCM_KEY_BODY KeyObject;
|
|
NTSTATUS Status;
|
|
REG_DELETE_KEY_INFORMATION DeleteKeyInfo;
|
|
REG_POST_OPERATION_INFORMATION PostOperationInfo;
|
|
PAGED_CODE();
|
|
DPRINT("NtDeleteKey(KH 0x%p)\n", KeyHandle);
|
|
|
|
/* Verify that the handle is valid and is a registry key */
|
|
Status = ObReferenceObjectByHandle(KeyHandle,
|
|
DELETE,
|
|
CmpKeyObjectType,
|
|
ExGetPreviousMode(),
|
|
(PVOID*)&KeyObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Setup the callback */
|
|
PostOperationInfo.Object = (PVOID)KeyObject;
|
|
DeleteKeyInfo.Object = (PVOID)KeyObject;
|
|
Status = CmiCallRegisteredCallbacks(RegNtPreDeleteKey, &DeleteKeyInfo);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Check if we are read-only */
|
|
if ((KeyObject->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY) ||
|
|
(KeyObject->KeyControlBlock->ParentKcb->ExtFlags & CM_KCB_READ_ONLY_KEY))
|
|
{
|
|
/* Fail */
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
else
|
|
{
|
|
/* Call the internal API */
|
|
Status = CmDeleteKey(KeyObject);
|
|
}
|
|
|
|
/* Do post callback */
|
|
PostOperationInfo.Status = Status;
|
|
CmiCallRegisteredCallbacks(RegNtPostDeleteKey, &PostOperationInfo);
|
|
}
|
|
|
|
/* Dereference and return status */
|
|
ObDereferenceObject(KeyObject);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtEnumerateKey(IN HANDLE KeyHandle,
|
|
IN ULONG Index,
|
|
IN KEY_INFORMATION_CLASS KeyInformationClass,
|
|
OUT PVOID KeyInformation,
|
|
IN ULONG Length,
|
|
OUT PULONG ResultLength)
|
|
{
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
NTSTATUS Status;
|
|
PCM_KEY_BODY KeyObject;
|
|
REG_ENUMERATE_KEY_INFORMATION EnumerateKeyInfo;
|
|
REG_POST_OPERATION_INFORMATION PostOperationInfo;
|
|
PAGED_CODE();
|
|
DPRINT("NtEnumerateKey() KH 0x%p, Index 0x%x, KIC %d, Length %lu\n",
|
|
KeyHandle, Index, KeyInformationClass, Length);
|
|
|
|
/* Reject classes we don't know about */
|
|
if ((KeyInformationClass != KeyBasicInformation) &&
|
|
(KeyInformationClass != KeyNodeInformation) &&
|
|
(KeyInformationClass != KeyFullInformation))
|
|
{
|
|
/* Fail */
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Verify that the handle is valid and is a registry key */
|
|
Status = ObReferenceObjectByHandle(KeyHandle,
|
|
KEY_ENUMERATE_SUB_KEYS,
|
|
CmpKeyObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&KeyObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForWriteUlong(ResultLength);
|
|
ProbeForWrite(KeyInformation,
|
|
Length,
|
|
sizeof(ULONG));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Dereference and return status */
|
|
ObDereferenceObject(KeyObject);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Setup the callback */
|
|
PostOperationInfo.Object = (PVOID)KeyObject;
|
|
EnumerateKeyInfo.Object = (PVOID)KeyObject;
|
|
EnumerateKeyInfo.Index = Index;
|
|
EnumerateKeyInfo.KeyInformationClass = KeyInformationClass;
|
|
EnumerateKeyInfo.Length = Length;
|
|
EnumerateKeyInfo.ResultLength = ResultLength;
|
|
|
|
/* Do the callback */
|
|
Status = CmiCallRegisteredCallbacks(RegNtPreEnumerateKey, &EnumerateKeyInfo);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Call the internal API */
|
|
Status = CmEnumerateKey(KeyObject->KeyControlBlock,
|
|
Index,
|
|
KeyInformationClass,
|
|
KeyInformation,
|
|
Length,
|
|
ResultLength);
|
|
|
|
/* Do the post callback */
|
|
PostOperationInfo.Status = Status;
|
|
CmiCallRegisteredCallbacks(RegNtPostEnumerateKey, &PostOperationInfo);
|
|
}
|
|
|
|
/* Dereference and return status */
|
|
ObDereferenceObject(KeyObject);
|
|
DPRINT("Returning status %x.\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtEnumerateValueKey(IN HANDLE KeyHandle,
|
|
IN ULONG Index,
|
|
IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
|
|
OUT PVOID KeyValueInformation,
|
|
IN ULONG Length,
|
|
OUT PULONG ResultLength)
|
|
{
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
NTSTATUS Status;
|
|
PCM_KEY_BODY KeyObject;
|
|
REG_ENUMERATE_VALUE_KEY_INFORMATION EnumerateValueKeyInfo;
|
|
REG_POST_OPERATION_INFORMATION PostOperationInfo;
|
|
|
|
PAGED_CODE();
|
|
|
|
DPRINT("NtEnumerateValueKey() KH 0x%p, Index 0x%x, KVIC %d, Length %lu\n",
|
|
KeyHandle, Index, KeyValueInformationClass, Length);
|
|
|
|
/* Reject classes we don't know about */
|
|
if ((KeyValueInformationClass != KeyValueBasicInformation) &&
|
|
(KeyValueInformationClass != KeyValueFullInformation) &&
|
|
(KeyValueInformationClass != KeyValuePartialInformation) &&
|
|
(KeyValueInformationClass != KeyValueFullInformationAlign64) &&
|
|
(KeyValueInformationClass != KeyValuePartialInformationAlign64))
|
|
{
|
|
/* Fail */
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Verify that the handle is valid and is a registry key */
|
|
Status = ObReferenceObjectByHandle(KeyHandle,
|
|
KEY_QUERY_VALUE,
|
|
CmpKeyObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&KeyObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForWriteUlong(ResultLength);
|
|
ProbeForWrite(KeyValueInformation,
|
|
Length,
|
|
sizeof(ULONG));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Dereference and return status */
|
|
ObDereferenceObject(KeyObject);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Setup the callback */
|
|
PostOperationInfo.Object = (PVOID)KeyObject;
|
|
EnumerateValueKeyInfo.Object = (PVOID)KeyObject;
|
|
EnumerateValueKeyInfo.Index = Index;
|
|
EnumerateValueKeyInfo.KeyValueInformationClass = KeyValueInformationClass;
|
|
EnumerateValueKeyInfo.KeyValueInformation = KeyValueInformation;
|
|
EnumerateValueKeyInfo.Length = Length;
|
|
EnumerateValueKeyInfo.ResultLength = ResultLength;
|
|
|
|
/* Do the callback */
|
|
Status = CmiCallRegisteredCallbacks(RegNtPreEnumerateValueKey,
|
|
&EnumerateValueKeyInfo);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Call the internal API */
|
|
Status = CmEnumerateValueKey(KeyObject->KeyControlBlock,
|
|
Index,
|
|
KeyValueInformationClass,
|
|
KeyValueInformation,
|
|
Length,
|
|
ResultLength);
|
|
|
|
/* Do the post callback */
|
|
PostOperationInfo.Status = Status;
|
|
CmiCallRegisteredCallbacks(RegNtPostEnumerateValueKey, &PostOperationInfo);
|
|
}
|
|
|
|
/* Dereference and return status */
|
|
ObDereferenceObject(KeyObject);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtQueryKey(IN HANDLE KeyHandle,
|
|
IN KEY_INFORMATION_CLASS KeyInformationClass,
|
|
OUT PVOID KeyInformation,
|
|
IN ULONG Length,
|
|
OUT PULONG ResultLength)
|
|
{
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
NTSTATUS Status;
|
|
PCM_KEY_BODY KeyObject;
|
|
REG_QUERY_KEY_INFORMATION QueryKeyInfo;
|
|
REG_POST_OPERATION_INFORMATION PostOperationInfo;
|
|
OBJECT_HANDLE_INFORMATION HandleInfo;
|
|
PAGED_CODE();
|
|
DPRINT("NtQueryKey() KH 0x%p, KIC %d, Length %lu\n",
|
|
KeyHandle, KeyInformationClass, Length);
|
|
|
|
/* Reject invalid classes */
|
|
if ((KeyInformationClass != KeyBasicInformation) &&
|
|
(KeyInformationClass != KeyNodeInformation) &&
|
|
(KeyInformationClass != KeyFullInformation) &&
|
|
(KeyInformationClass != KeyNameInformation) &&
|
|
(KeyInformationClass != KeyCachedInformation) &&
|
|
(KeyInformationClass != KeyFlagsInformation))
|
|
{
|
|
/* Fail */
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Check if just the name is required */
|
|
if (KeyInformationClass == KeyNameInformation)
|
|
{
|
|
/* Ignore access level */
|
|
Status = ObReferenceObjectByHandle(KeyHandle,
|
|
0,
|
|
CmpKeyObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&KeyObject,
|
|
&HandleInfo);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* At least a single bit of access is required */
|
|
if (!HandleInfo.GrantedAccess)
|
|
{
|
|
/* No such luck */
|
|
ObDereferenceObject(KeyObject);
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Get a reference */
|
|
Status = ObReferenceObjectByHandle(KeyHandle,
|
|
KEY_QUERY_VALUE,
|
|
CmpKeyObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&KeyObject,
|
|
NULL);
|
|
}
|
|
|
|
/* Quit on failure */
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForWriteUlong(ResultLength);
|
|
ProbeForWrite(KeyInformation,
|
|
Length,
|
|
sizeof(ULONG));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Dereference and return status */
|
|
ObDereferenceObject(KeyObject);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Setup the callback */
|
|
PostOperationInfo.Object = (PVOID)KeyObject;
|
|
QueryKeyInfo.Object = (PVOID)KeyObject;
|
|
QueryKeyInfo.KeyInformationClass = KeyInformationClass;
|
|
QueryKeyInfo.KeyInformation = KeyInformation;
|
|
QueryKeyInfo.Length = Length;
|
|
QueryKeyInfo.ResultLength = ResultLength;
|
|
|
|
/* Do the callback */
|
|
Status = CmiCallRegisteredCallbacks(RegNtPreQueryKey, &QueryKeyInfo);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Call the internal API */
|
|
Status = CmQueryKey(KeyObject->KeyControlBlock,
|
|
KeyInformationClass,
|
|
KeyInformation,
|
|
Length,
|
|
ResultLength);
|
|
|
|
/* Do the post callback */
|
|
PostOperationInfo.Status = Status;
|
|
CmiCallRegisteredCallbacks(RegNtPostQueryKey, &PostOperationInfo);
|
|
}
|
|
|
|
/* Dereference and return status */
|
|
ObDereferenceObject(KeyObject);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtQueryValueKey(IN HANDLE KeyHandle,
|
|
IN PUNICODE_STRING ValueName,
|
|
IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
|
|
OUT PVOID KeyValueInformation,
|
|
IN ULONG Length,
|
|
OUT PULONG ResultLength)
|
|
{
|
|
NTSTATUS Status;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
PCM_KEY_BODY KeyObject;
|
|
REG_QUERY_VALUE_KEY_INFORMATION QueryValueKeyInfo;
|
|
REG_POST_OPERATION_INFORMATION PostOperationInfo;
|
|
UNICODE_STRING ValueNameCopy;
|
|
|
|
PAGED_CODE();
|
|
|
|
DPRINT("NtQueryValueKey() KH 0x%p, VN '%wZ', KVIC %d, Length %lu\n",
|
|
KeyHandle, ValueName, KeyValueInformationClass, Length);
|
|
|
|
/* Reject classes we don't know about */
|
|
if ((KeyValueInformationClass != KeyValueBasicInformation) &&
|
|
(KeyValueInformationClass != KeyValueFullInformation) &&
|
|
(KeyValueInformationClass != KeyValuePartialInformation) &&
|
|
(KeyValueInformationClass != KeyValueFullInformationAlign64) &&
|
|
(KeyValueInformationClass != KeyValuePartialInformationAlign64))
|
|
{
|
|
/* Fail */
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Verify that the handle is valid and is a registry key */
|
|
Status = ObReferenceObjectByHandle(KeyHandle,
|
|
KEY_QUERY_VALUE,
|
|
CmpKeyObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&KeyObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForWriteUlong(ResultLength);
|
|
ProbeForWrite(KeyValueInformation,
|
|
Length,
|
|
sizeof(ULONG));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Dereference and return status */
|
|
ObDereferenceObject(KeyObject);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Capture the string */
|
|
Status = ProbeAndCaptureUnicodeString(&ValueNameCopy, PreviousMode, ValueName);
|
|
if (!NT_SUCCESS(Status))
|
|
goto Quit;
|
|
|
|
/* Make sure the name is aligned properly */
|
|
if (ValueNameCopy.Length & (sizeof(WCHAR) - 1))
|
|
{
|
|
/* It isn't, so we'll fail */
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Quit;
|
|
}
|
|
|
|
/* Ignore any null characters at the end */
|
|
while (ValueNameCopy.Length &&
|
|
!(ValueNameCopy.Buffer[ValueNameCopy.Length / sizeof(WCHAR) - 1]))
|
|
{
|
|
/* Skip it */
|
|
ValueNameCopy.Length -= sizeof(WCHAR);
|
|
}
|
|
|
|
/* Setup the callback */
|
|
PostOperationInfo.Object = (PVOID)KeyObject;
|
|
QueryValueKeyInfo.Object = (PVOID)KeyObject;
|
|
QueryValueKeyInfo.ValueName = &ValueNameCopy;
|
|
QueryValueKeyInfo.KeyValueInformationClass = KeyValueInformationClass;
|
|
QueryValueKeyInfo.Length = Length;
|
|
QueryValueKeyInfo.ResultLength = ResultLength;
|
|
|
|
/* Do the callback */
|
|
Status = CmiCallRegisteredCallbacks(RegNtPreQueryValueKey, &QueryValueKeyInfo);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Call the internal API */
|
|
Status = CmQueryValueKey(KeyObject->KeyControlBlock,
|
|
ValueNameCopy,
|
|
KeyValueInformationClass,
|
|
KeyValueInformation,
|
|
Length,
|
|
ResultLength);
|
|
|
|
/* Do the post callback */
|
|
PostOperationInfo.Status = Status;
|
|
CmiCallRegisteredCallbacks(RegNtPostQueryValueKey, &PostOperationInfo);
|
|
}
|
|
|
|
Quit:
|
|
if (ValueNameCopy.Buffer)
|
|
ReleaseCapturedUnicodeString(&ValueNameCopy, PreviousMode);
|
|
|
|
/* Dereference and return status */
|
|
ObDereferenceObject(KeyObject);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtSetValueKey(IN HANDLE KeyHandle,
|
|
IN PUNICODE_STRING ValueName,
|
|
IN ULONG TitleIndex,
|
|
IN ULONG Type,
|
|
IN PVOID Data,
|
|
IN ULONG DataSize)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
KPROCESSOR_MODE PreviousMode;
|
|
PCM_KEY_BODY KeyObject;
|
|
REG_SET_VALUE_KEY_INFORMATION SetValueKeyInfo;
|
|
REG_POST_OPERATION_INFORMATION PostOperationInfo;
|
|
UNICODE_STRING ValueNameCopy;
|
|
|
|
PAGED_CODE();
|
|
|
|
PreviousMode = ExGetPreviousMode();
|
|
|
|
/* Verify that the handle is valid and is a registry key */
|
|
Status = ObReferenceObjectByHandle(KeyHandle,
|
|
KEY_SET_VALUE,
|
|
CmpKeyObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&KeyObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
if (!DataSize)
|
|
Data = NULL;
|
|
|
|
/* Probe and copy the data */
|
|
if ((PreviousMode != KernelMode) && (DataSize != 0))
|
|
{
|
|
PVOID DataCopy = NULL;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForRead(Data, DataSize, 1);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Dereference and return status */
|
|
ObDereferenceObject(KeyObject);
|
|
return Status;
|
|
}
|
|
|
|
DataCopy = ExAllocatePoolWithTag(PagedPool, DataSize, TAG_CM);
|
|
if (!DataCopy)
|
|
{
|
|
/* Dereference and return status */
|
|
ObDereferenceObject(KeyObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
_SEH2_TRY
|
|
{
|
|
RtlCopyMemory(DataCopy, Data, DataSize);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Dereference and return status */
|
|
ExFreePoolWithTag(DataCopy, TAG_CM);
|
|
ObDereferenceObject(KeyObject);
|
|
return Status;
|
|
}
|
|
|
|
Data = DataCopy;
|
|
}
|
|
|
|
/* Capture the string */
|
|
Status = ProbeAndCaptureUnicodeString(&ValueNameCopy, PreviousMode, ValueName);
|
|
if (!NT_SUCCESS(Status))
|
|
goto Quit;
|
|
|
|
DPRINT("NtSetValueKey() KH 0x%p, VN '%wZ', TI %x, T %lu, DS %lu\n",
|
|
KeyHandle, &ValueNameCopy, TitleIndex, Type, DataSize);
|
|
|
|
/* Make sure the name is aligned, not too long, and the data under 4GB */
|
|
if ((ValueNameCopy.Length > 32767) ||
|
|
(ValueNameCopy.Length & (sizeof(WCHAR) - 1)) ||
|
|
(DataSize > 0x80000000))
|
|
{
|
|
/* Fail */
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Quit;
|
|
}
|
|
|
|
/* Ignore any null characters at the end */
|
|
while (ValueNameCopy.Length &&
|
|
!(ValueNameCopy.Buffer[ValueNameCopy.Length / sizeof(WCHAR) - 1]))
|
|
{
|
|
/* Skip it */
|
|
ValueNameCopy.Length -= sizeof(WCHAR);
|
|
}
|
|
|
|
/* Don't touch read-only keys */
|
|
if (KeyObject->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY)
|
|
{
|
|
/* Fail */
|
|
Status = STATUS_ACCESS_DENIED;
|
|
goto Quit;
|
|
}
|
|
|
|
/* Setup callback */
|
|
PostOperationInfo.Object = (PVOID)KeyObject;
|
|
SetValueKeyInfo.Object = (PVOID)KeyObject;
|
|
SetValueKeyInfo.ValueName = &ValueNameCopy;
|
|
SetValueKeyInfo.TitleIndex = TitleIndex;
|
|
SetValueKeyInfo.Type = Type;
|
|
SetValueKeyInfo.Data = Data;
|
|
SetValueKeyInfo.DataSize = DataSize;
|
|
|
|
/* Do the callback */
|
|
Status = CmiCallRegisteredCallbacks(RegNtPreSetValueKey, &SetValueKeyInfo);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Call the internal API */
|
|
Status = CmSetValueKey(KeyObject->KeyControlBlock,
|
|
&ValueNameCopy,
|
|
Type,
|
|
Data,
|
|
DataSize);
|
|
|
|
/* Do the post-callback */
|
|
PostOperationInfo.Status = Status;
|
|
CmiCallRegisteredCallbacks(RegNtPostSetValueKey, &PostOperationInfo);
|
|
}
|
|
|
|
Quit:
|
|
if (ValueNameCopy.Buffer)
|
|
ReleaseCapturedUnicodeString(&ValueNameCopy, PreviousMode);
|
|
|
|
if ((PreviousMode != KernelMode) && Data)
|
|
ExFreePoolWithTag(Data, TAG_CM);
|
|
|
|
/* Dereference and return status */
|
|
ObDereferenceObject(KeyObject);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtDeleteValueKey(IN HANDLE KeyHandle,
|
|
IN PUNICODE_STRING ValueName)
|
|
{
|
|
NTSTATUS Status;
|
|
PCM_KEY_BODY KeyObject;
|
|
REG_DELETE_VALUE_KEY_INFORMATION DeleteValueKeyInfo;
|
|
REG_POST_OPERATION_INFORMATION PostOperationInfo;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
UNICODE_STRING ValueNameCopy;
|
|
|
|
PAGED_CODE();
|
|
|
|
/* Verify that the handle is valid and is a registry key */
|
|
Status = ObReferenceObjectByHandle(KeyHandle,
|
|
KEY_SET_VALUE,
|
|
CmpKeyObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&KeyObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
/* Capture the string */
|
|
Status = ProbeAndCaptureUnicodeString(&ValueNameCopy, PreviousMode, ValueName);
|
|
if (!NT_SUCCESS(Status))
|
|
goto Quit;
|
|
|
|
/* Make sure the name is aligned properly */
|
|
if (ValueNameCopy.Length & (sizeof(WCHAR) - 1))
|
|
{
|
|
/* It isn't, so we'll fail */
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Quit;
|
|
}
|
|
|
|
/* Don't touch read-only keys */
|
|
if (KeyObject->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY)
|
|
{
|
|
/* Fail */
|
|
Status = STATUS_ACCESS_DENIED;
|
|
goto Quit;
|
|
}
|
|
|
|
/* Do the callback */
|
|
DeleteValueKeyInfo.Object = (PVOID)KeyObject;
|
|
DeleteValueKeyInfo.ValueName = ValueName;
|
|
Status = CmiCallRegisteredCallbacks(RegNtPreDeleteValueKey,
|
|
&DeleteValueKeyInfo);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Call the internal API */
|
|
Status = CmDeleteValueKey(KeyObject->KeyControlBlock, ValueNameCopy);
|
|
|
|
/* Do the post callback */
|
|
PostOperationInfo.Object = (PVOID)KeyObject;
|
|
PostOperationInfo.Status = Status;
|
|
CmiCallRegisteredCallbacks(RegNtPostDeleteValueKey,
|
|
&PostOperationInfo);
|
|
}
|
|
|
|
Quit:
|
|
if (ValueNameCopy.Buffer)
|
|
ReleaseCapturedUnicodeString(&ValueNameCopy, PreviousMode);
|
|
|
|
/* Dereference and return status */
|
|
ObDereferenceObject(KeyObject);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtFlushKey(IN HANDLE KeyHandle)
|
|
{
|
|
NTSTATUS Status;
|
|
PCM_KEY_BODY KeyObject;
|
|
PAGED_CODE();
|
|
|
|
/* Get the key object */
|
|
Status = ObReferenceObjectByHandle(KeyHandle,
|
|
0,
|
|
CmpKeyObjectType,
|
|
ExGetPreviousMode(),
|
|
(PVOID*)&KeyObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Lock the registry */
|
|
CmpLockRegistry();
|
|
|
|
/* Lock the KCB */
|
|
CmpAcquireKcbLockShared(KeyObject->KeyControlBlock);
|
|
|
|
/* Make sure KCB isn't deleted */
|
|
if (KeyObject->KeyControlBlock->Delete)
|
|
{
|
|
/* Fail */
|
|
Status = STATUS_KEY_DELETED;
|
|
}
|
|
else
|
|
{
|
|
/* Call the internal API */
|
|
Status = CmFlushKey(KeyObject->KeyControlBlock, FALSE);
|
|
}
|
|
|
|
/* Release the locks */
|
|
CmpReleaseKcbLock(KeyObject->KeyControlBlock);
|
|
CmpUnlockRegistry();
|
|
|
|
/* Dereference the object and return status */
|
|
ObDereferenceObject(KeyObject);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtLoadKey(IN POBJECT_ATTRIBUTES KeyObjectAttributes,
|
|
IN POBJECT_ATTRIBUTES FileObjectAttributes)
|
|
{
|
|
/* Call the newer API */
|
|
return NtLoadKeyEx(KeyObjectAttributes, FileObjectAttributes, 0, NULL);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtLoadKey2(IN POBJECT_ATTRIBUTES KeyObjectAttributes,
|
|
IN POBJECT_ATTRIBUTES FileObjectAttributes,
|
|
IN ULONG Flags)
|
|
{
|
|
/* Call the newer API */
|
|
return NtLoadKeyEx(KeyObjectAttributes, FileObjectAttributes, Flags, NULL);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtLoadKeyEx(IN POBJECT_ATTRIBUTES TargetKey,
|
|
IN POBJECT_ATTRIBUTES SourceFile,
|
|
IN ULONG Flags,
|
|
IN HANDLE TrustClassKey)
|
|
{
|
|
NTSTATUS Status;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
OBJECT_ATTRIBUTES CapturedTargetKey;
|
|
OBJECT_ATTRIBUTES CapturedSourceFile;
|
|
UNICODE_STRING TargetKeyName, SourceFileName;
|
|
HANDLE KmTargetKeyRootDir = NULL, KmSourceFileRootDir = NULL;
|
|
PCM_KEY_BODY KeyBody = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
/* Validate flags */
|
|
if (Flags & ~REG_NO_LAZY_FLUSH)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/* Validate privilege */
|
|
if (!SeSinglePrivilegeCheck(SeRestorePrivilege, PreviousMode))
|
|
{
|
|
DPRINT1("Restore Privilege missing!\n");
|
|
return STATUS_PRIVILEGE_NOT_HELD;
|
|
}
|
|
|
|
/* Block APCs */
|
|
KeEnterCriticalRegion();
|
|
|
|
/* Check for user-mode caller */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Prepare to probe parameters */
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe target key */
|
|
ProbeForRead(TargetKey,
|
|
sizeof(OBJECT_ATTRIBUTES),
|
|
sizeof(ULONG));
|
|
|
|
/* Probe source file */
|
|
ProbeForRead(SourceFile,
|
|
sizeof(OBJECT_ATTRIBUTES),
|
|
sizeof(ULONG));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(goto Quit);
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Probe and capture the target key attributes, including the security */
|
|
Status = ProbeAndCaptureObjectAttributes(&CapturedTargetKey,
|
|
&TargetKeyName,
|
|
PreviousMode,
|
|
TargetKey,
|
|
TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
goto Quit;
|
|
|
|
/*
|
|
* Probe and capture the source file attributes, but not the security.
|
|
* A proper security context is built by CmLoadKey().
|
|
*/
|
|
Status = ProbeAndCaptureObjectAttributes(&CapturedSourceFile,
|
|
&SourceFileName,
|
|
PreviousMode,
|
|
SourceFile,
|
|
FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ReleaseCapturedObjectAttributes(&CapturedTargetKey, PreviousMode);
|
|
goto Quit;
|
|
}
|
|
|
|
/* Make sure the target key root directory handle is a kernel handle */
|
|
Status = CmpConvertHandleToKernelHandle(CapturedTargetKey.RootDirectory,
|
|
CmpKeyObjectType,
|
|
KEY_READ,
|
|
PreviousMode,
|
|
&KmTargetKeyRootDir);
|
|
if (!NT_SUCCESS(Status))
|
|
goto Cleanup;
|
|
CapturedTargetKey.RootDirectory = KmTargetKeyRootDir;
|
|
CapturedTargetKey.Attributes |= OBJ_KERNEL_HANDLE;
|
|
|
|
/* Make sure the source file root directory handle is a kernel handle */
|
|
Status = CmpConvertHandleToKernelHandle(CapturedSourceFile.RootDirectory,
|
|
IoFileObjectType,
|
|
FILE_TRAVERSE,
|
|
PreviousMode,
|
|
&KmSourceFileRootDir);
|
|
if (!NT_SUCCESS(Status))
|
|
goto Cleanup;
|
|
CapturedSourceFile.RootDirectory = KmSourceFileRootDir;
|
|
CapturedSourceFile.Attributes |= OBJ_KERNEL_HANDLE;
|
|
|
|
/* Check if we have a trust class */
|
|
if (TrustClassKey)
|
|
{
|
|
/* Reference it */
|
|
Status = ObReferenceObjectByHandle(TrustClassKey,
|
|
0,
|
|
CmpKeyObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&KeyBody,
|
|
NULL);
|
|
}
|
|
|
|
/* Call the internal API */
|
|
Status = CmLoadKey(&CapturedTargetKey,
|
|
&CapturedSourceFile,
|
|
Flags,
|
|
KeyBody);
|
|
|
|
/* Dereference the trust key, if any */
|
|
if (KeyBody) ObDereferenceObject(KeyBody);
|
|
|
|
Cleanup:
|
|
/* Close the local kernel handles */
|
|
if (KmSourceFileRootDir)
|
|
ObCloseHandle(KmSourceFileRootDir, KernelMode);
|
|
if (KmTargetKeyRootDir)
|
|
ObCloseHandle(KmTargetKeyRootDir, KernelMode);
|
|
|
|
/* Release the captured object attributes */
|
|
ReleaseCapturedObjectAttributes(&CapturedSourceFile, PreviousMode);
|
|
ReleaseCapturedObjectAttributes(&CapturedTargetKey, PreviousMode);
|
|
|
|
Quit:
|
|
/* Bring back APCs */
|
|
KeLeaveCriticalRegion();
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtNotifyChangeKey(IN HANDLE KeyHandle,
|
|
IN HANDLE Event,
|
|
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
|
|
IN PVOID ApcContext OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN ULONG CompletionFilter,
|
|
IN BOOLEAN WatchTree,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
IN BOOLEAN Asynchronous)
|
|
{
|
|
/* Call the newer API */
|
|
return NtNotifyChangeMultipleKeys(KeyHandle,
|
|
0,
|
|
NULL,
|
|
Event,
|
|
ApcRoutine,
|
|
ApcContext,
|
|
IoStatusBlock,
|
|
CompletionFilter,
|
|
WatchTree,
|
|
Buffer,
|
|
Length,
|
|
Asynchronous);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtInitializeRegistry(IN USHORT Flag)
|
|
{
|
|
BOOLEAN SetupBoot;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PAGED_CODE();
|
|
|
|
/* Always do this as kernel mode */
|
|
if (KeGetPreviousMode() == UserMode)
|
|
return ZwInitializeRegistry(Flag);
|
|
|
|
/* Enough of the system has booted by now */
|
|
Ki386PerfEnd();
|
|
|
|
/* Validate flag */
|
|
if (Flag > CM_BOOT_FLAG_MAX) return STATUS_INVALID_PARAMETER;
|
|
|
|
/* Check if boot was accepted */
|
|
if ((Flag >= CM_BOOT_FLAG_ACCEPTED) && (Flag <= CM_BOOT_FLAG_MAX))
|
|
{
|
|
/* Only allow once */
|
|
if (!CmBootAcceptFirstTime) return STATUS_ACCESS_DENIED;
|
|
CmBootAcceptFirstTime = FALSE;
|
|
|
|
/* Get the control set accepted */
|
|
Flag -= CM_BOOT_FLAG_ACCEPTED;
|
|
if (Flag)
|
|
{
|
|
/* Save the last known good boot */
|
|
Status = CmpSaveBootControlSet(Flag);
|
|
|
|
/* Notify HAL */
|
|
HalEndOfBoot();
|
|
|
|
/* Enable lazy flush */
|
|
CmpHoldLazyFlush = FALSE;
|
|
CmpLazyFlush();
|
|
return Status;
|
|
}
|
|
|
|
/* Otherwise, invalid boot */
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Check if this was a setup boot */
|
|
SetupBoot = (Flag == CM_BOOT_FLAG_SETUP ? TRUE : FALSE);
|
|
|
|
/* Make sure we're only called once */
|
|
if (!CmFirstTime) return STATUS_ACCESS_DENIED;
|
|
CmFirstTime = FALSE;
|
|
|
|
/* Lock the registry exclusively */
|
|
CmpLockRegistryExclusive();
|
|
|
|
/* Initialize the hives and lazy flusher */
|
|
CmpCmdInit(SetupBoot);
|
|
|
|
/* Save version data */
|
|
CmpSetVersionData();
|
|
|
|
/* Release the registry lock */
|
|
CmpUnlockRegistry();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtCompactKeys(IN ULONG Count,
|
|
IN PHANDLE KeyArray)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtCompressKey(IN HANDLE Key)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
// FIXME: different for different windows versions!
|
|
#define PRODUCT_ACTIVATION_VERSION 7749
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtLockProductActivationKeys(IN PULONG pPrivateVer,
|
|
IN PULONG pSafeMode)
|
|
{
|
|
KPROCESSOR_MODE PreviousMode;
|
|
|
|
PreviousMode = ExGetPreviousMode();
|
|
_SEH2_TRY
|
|
{
|
|
/* Check if the caller asked for the version */
|
|
if (pPrivateVer != NULL)
|
|
{
|
|
/* For user mode, probe it */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
ProbeForWriteUlong(pPrivateVer);
|
|
}
|
|
|
|
/* Return the expected version */
|
|
*pPrivateVer = PRODUCT_ACTIVATION_VERSION;
|
|
}
|
|
|
|
/* Check if the caller asked for safe mode mode state */
|
|
if (pSafeMode != NULL)
|
|
{
|
|
/* For user mode, probe it */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
ProbeForWriteUlong(pSafeMode);
|
|
}
|
|
|
|
/* Return the safe boot mode state */
|
|
*pSafeMode = InitSafeBootMode;
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtLockRegistryKey(IN HANDLE KeyHandle)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtNotifyChangeMultipleKeys(IN HANDLE MasterKeyHandle,
|
|
IN ULONG Count,
|
|
IN POBJECT_ATTRIBUTES SlaveObjects,
|
|
IN HANDLE Event,
|
|
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
|
|
IN PVOID ApcContext OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN ULONG CompletionFilter,
|
|
IN BOOLEAN WatchTree,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
IN BOOLEAN Asynchronous)
|
|
{
|
|
UNIMPLEMENTED_ONCE;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtQueryMultipleValueKey(IN HANDLE KeyHandle,
|
|
IN OUT PKEY_VALUE_ENTRY ValueList,
|
|
IN ULONG NumberOfValues,
|
|
OUT PVOID Buffer,
|
|
IN OUT PULONG Length,
|
|
OUT PULONG ReturnLength)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtQueryOpenSubKeys(IN POBJECT_ATTRIBUTES TargetKey,
|
|
OUT PULONG HandleCount)
|
|
{
|
|
KPROCESSOR_MODE PreviousMode;
|
|
PCM_KEY_BODY KeyBody = NULL;
|
|
HANDLE KeyHandle;
|
|
NTSTATUS Status;
|
|
ULONG SubKeys;
|
|
|
|
DPRINT("NtQueryOpenSubKeys()\n");
|
|
|
|
PAGED_CODE();
|
|
|
|
/* Get the processor mode */
|
|
PreviousMode = KeGetPreviousMode();
|
|
|
|
/* Check for user-mode caller */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Prepare to probe parameters */
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe target key */
|
|
ProbeForRead(TargetKey,
|
|
sizeof(OBJECT_ATTRIBUTES),
|
|
sizeof(ULONG));
|
|
|
|
/* Probe handle count */
|
|
ProbeForWriteUlong(HandleCount);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Open a handle to the key */
|
|
Status = ObOpenObjectByName(TargetKey,
|
|
CmpKeyObjectType,
|
|
PreviousMode,
|
|
NULL,
|
|
KEY_READ,
|
|
NULL,
|
|
&KeyHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Reference the key object */
|
|
Status = ObReferenceObjectByHandle(KeyHandle,
|
|
KEY_READ,
|
|
CmpKeyObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&KeyBody,
|
|
NULL);
|
|
|
|
/* Close the handle */
|
|
NtClose(KeyHandle);
|
|
}
|
|
|
|
/* Fail, if the key object could not be referenced */
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
/* Lock the registry exclusively */
|
|
CmpLockRegistryExclusive();
|
|
|
|
/* Fail, if we did not open a hive root key */
|
|
if (KeyBody->KeyControlBlock->KeyCell !=
|
|
KeyBody->KeyControlBlock->KeyHive->BaseBlock->RootCell)
|
|
{
|
|
DPRINT("Error: Key is not a hive root key!\n");
|
|
CmpUnlockRegistry();
|
|
ObDereferenceObject(KeyBody);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Call the internal API */
|
|
SubKeys = CmpEnumerateOpenSubKeys(KeyBody->KeyControlBlock,
|
|
FALSE, FALSE);
|
|
|
|
/* Unlock the registry */
|
|
CmpUnlockRegistry();
|
|
|
|
/* Dereference the key object */
|
|
ObDereferenceObject(KeyBody);
|
|
|
|
/* Write back the result */
|
|
_SEH2_TRY
|
|
{
|
|
*HandleCount = SubKeys;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
DPRINT("Done.\n");
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtQueryOpenSubKeysEx(IN POBJECT_ATTRIBUTES TargetKey,
|
|
IN ULONG BufferLength,
|
|
IN PVOID Buffer,
|
|
IN PULONG RequiredSize)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtRenameKey(IN HANDLE KeyHandle,
|
|
IN PUNICODE_STRING ReplacementName)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtReplaceKey(IN POBJECT_ATTRIBUTES ObjectAttributes,
|
|
IN HANDLE Key,
|
|
IN POBJECT_ATTRIBUTES ReplacedObjectAttributes)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtRestoreKey(IN HANDLE KeyHandle,
|
|
IN HANDLE FileHandle,
|
|
IN ULONG RestoreFlags)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtSaveKey(IN HANDLE KeyHandle,
|
|
IN HANDLE FileHandle)
|
|
{
|
|
/* Call the extended API */
|
|
return NtSaveKeyEx(KeyHandle, FileHandle, REG_STANDARD_FORMAT);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtSaveKeyEx(IN HANDLE KeyHandle,
|
|
IN HANDLE FileHandle,
|
|
IN ULONG Flags)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE KmFileHandle = NULL;
|
|
PCM_KEY_BODY KeyObject;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
|
|
PAGED_CODE();
|
|
|
|
DPRINT("NtSaveKeyEx(0x%p, 0x%p, %lu)\n", KeyHandle, FileHandle, Flags);
|
|
|
|
/* Verify the flags */
|
|
if ((Flags != REG_STANDARD_FORMAT)
|
|
&& (Flags != REG_LATEST_FORMAT)
|
|
&& (Flags != REG_NO_COMPRESSION))
|
|
{
|
|
/* Only one of these values can be specified */
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Validate privilege */
|
|
if (!SeSinglePrivilegeCheck(SeBackupPrivilege, PreviousMode))
|
|
{
|
|
return STATUS_PRIVILEGE_NOT_HELD;
|
|
}
|
|
|
|
/* Make sure the target file handle is a kernel handle */
|
|
Status = CmpConvertHandleToKernelHandle(FileHandle,
|
|
IoFileObjectType,
|
|
FILE_WRITE_DATA,
|
|
PreviousMode,
|
|
&KmFileHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
goto Quit;
|
|
|
|
/* Verify that the handle is valid and is a registry key */
|
|
Status = ObReferenceObjectByHandle(KeyHandle,
|
|
KEY_READ,
|
|
CmpKeyObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&KeyObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
goto Quit;
|
|
|
|
/* Call the internal API */
|
|
Status = CmSaveKey(KeyObject->KeyControlBlock, KmFileHandle, Flags);
|
|
|
|
/* Dereference the registry key */
|
|
ObDereferenceObject(KeyObject);
|
|
|
|
Quit:
|
|
/* Close the local kernel handle */
|
|
if (KmFileHandle)
|
|
ObCloseHandle(KmFileHandle, KernelMode);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtSaveMergedKeys(IN HANDLE HighPrecedenceKeyHandle,
|
|
IN HANDLE LowPrecedenceKeyHandle,
|
|
IN HANDLE FileHandle)
|
|
{
|
|
NTSTATUS Status;
|
|
KPROCESSOR_MODE PreviousMode;
|
|
HANDLE KmFileHandle = NULL;
|
|
PCM_KEY_BODY HighPrecedenceKeyObject = NULL;
|
|
PCM_KEY_BODY LowPrecedenceKeyObject = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
DPRINT("NtSaveMergedKeys(0x%p, 0x%p, 0x%p)\n",
|
|
HighPrecedenceKeyHandle, LowPrecedenceKeyHandle, FileHandle);
|
|
|
|
PreviousMode = ExGetPreviousMode();
|
|
|
|
/* Validate privilege */
|
|
if (!SeSinglePrivilegeCheck(SeBackupPrivilege, PreviousMode))
|
|
{
|
|
return STATUS_PRIVILEGE_NOT_HELD;
|
|
}
|
|
|
|
/* Make sure the target file handle is a kernel handle */
|
|
Status = CmpConvertHandleToKernelHandle(FileHandle,
|
|
IoFileObjectType,
|
|
FILE_WRITE_DATA,
|
|
PreviousMode,
|
|
&KmFileHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
goto Quit;
|
|
|
|
/* Verify that the handles are valid and are registry keys */
|
|
Status = ObReferenceObjectByHandle(HighPrecedenceKeyHandle,
|
|
KEY_READ,
|
|
CmpKeyObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&HighPrecedenceKeyObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
goto Quit;
|
|
|
|
Status = ObReferenceObjectByHandle(LowPrecedenceKeyHandle,
|
|
KEY_READ,
|
|
CmpKeyObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&LowPrecedenceKeyObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
goto Quit;
|
|
|
|
/* Call the internal API */
|
|
Status = CmSaveMergedKeys(HighPrecedenceKeyObject->KeyControlBlock,
|
|
LowPrecedenceKeyObject->KeyControlBlock,
|
|
KmFileHandle);
|
|
|
|
Quit:
|
|
/* Dereference the opened key objects */
|
|
if (LowPrecedenceKeyObject)
|
|
ObDereferenceObject(LowPrecedenceKeyObject);
|
|
if (HighPrecedenceKeyObject)
|
|
ObDereferenceObject(HighPrecedenceKeyObject);
|
|
|
|
/* Close the local kernel handle */
|
|
if (KmFileHandle)
|
|
ObCloseHandle(KmFileHandle, KernelMode);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtSetInformationKey(IN HANDLE KeyHandle,
|
|
IN KEY_SET_INFORMATION_CLASS KeyInformationClass,
|
|
IN PVOID KeyInformation,
|
|
IN ULONG KeyInformationLength)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtUnloadKey(IN POBJECT_ATTRIBUTES KeyObjectAttributes)
|
|
{
|
|
return NtUnloadKey2(KeyObjectAttributes, 0);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtUnloadKey2(IN POBJECT_ATTRIBUTES TargetKey,
|
|
IN ULONG Flags)
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES CapturedTargetKey;
|
|
UNICODE_STRING ObjectName;
|
|
HANDLE KmTargetKeyRootDir = NULL;
|
|
CM_PARSE_CONTEXT ParseContext = {0};
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
PCM_KEY_BODY KeyBody = NULL;
|
|
HANDLE Handle;
|
|
|
|
PAGED_CODE();
|
|
|
|
/* Validate privilege */
|
|
if (!SeSinglePrivilegeCheck(SeRestorePrivilege, PreviousMode))
|
|
{
|
|
DPRINT1("Restore Privilege missing!\n");
|
|
return STATUS_PRIVILEGE_NOT_HELD;
|
|
}
|
|
|
|
/* Check for user-mode caller */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Prepare to probe parameters */
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe object attributes */
|
|
ProbeForRead(TargetKey,
|
|
sizeof(OBJECT_ATTRIBUTES),
|
|
sizeof(ULONG));
|
|
|
|
CapturedTargetKey = *TargetKey;
|
|
|
|
/* Probe the string */
|
|
ObjectName = ProbeForReadUnicodeString(CapturedTargetKey.ObjectName);
|
|
ProbeForRead(ObjectName.Buffer,
|
|
ObjectName.Length,
|
|
sizeof(WCHAR));
|
|
|
|
CapturedTargetKey.ObjectName = &ObjectName;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else
|
|
{
|
|
/* Save the target attributes directly */
|
|
CapturedTargetKey = *TargetKey;
|
|
}
|
|
|
|
/* Make sure the target key root directory handle is a kernel handle */
|
|
Status = CmpConvertHandleToKernelHandle(CapturedTargetKey.RootDirectory,
|
|
CmpKeyObjectType,
|
|
KEY_WRITE,
|
|
PreviousMode,
|
|
&KmTargetKeyRootDir);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
CapturedTargetKey.RootDirectory = KmTargetKeyRootDir;
|
|
CapturedTargetKey.Attributes |= OBJ_KERNEL_HANDLE;
|
|
|
|
/* Setup the parse context */
|
|
ParseContext.CreateOperation = TRUE;
|
|
ParseContext.CreateOptions = REG_OPTION_BACKUP_RESTORE;
|
|
|
|
/* Do the create */
|
|
/* Open a local handle to the key */
|
|
Status = ObOpenObjectByName(&CapturedTargetKey,
|
|
CmpKeyObjectType,
|
|
KernelMode,
|
|
NULL,
|
|
KEY_WRITE,
|
|
&ParseContext,
|
|
&Handle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Reference the key object */
|
|
Status = ObReferenceObjectByHandle(Handle,
|
|
KEY_WRITE,
|
|
CmpKeyObjectType,
|
|
KernelMode,
|
|
(PVOID*)&KeyBody,
|
|
NULL);
|
|
|
|
/* Close the handle */
|
|
ObCloseHandle(Handle, KernelMode);
|
|
}
|
|
|
|
/* Close the local kernel handle */
|
|
if (KmTargetKeyRootDir)
|
|
ObCloseHandle(KmTargetKeyRootDir, KernelMode);
|
|
|
|
/* Return if a failure was encountered */
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
/*
|
|
* Lock down the entire registry when we unload a hive.
|
|
*
|
|
* NOTE: We might block other threads of other processes that do
|
|
* operations with unrelated keys of other hives when we lock
|
|
* the registry for exclusive use by the calling thread that does
|
|
* the unloading. If this turns out to cause a major overhead we
|
|
* have to rethink the locking mechanism here (prior commit - f1d2a44).
|
|
*/
|
|
CmpLockRegistryExclusive();
|
|
ExAcquirePushLockExclusive(&CmpLoadHiveLock);
|
|
|
|
/* Check if it's being deleted already */
|
|
if (KeyBody->KeyControlBlock->Delete)
|
|
{
|
|
/* Return appropriate status */
|
|
Status = STATUS_KEY_DELETED;
|
|
goto Quit;
|
|
}
|
|
|
|
/* Check if it's a read-only key */
|
|
if (KeyBody->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY)
|
|
{
|
|
/* Return appropriate status */
|
|
Status = STATUS_ACCESS_DENIED;
|
|
goto Quit;
|
|
}
|
|
|
|
/* Call the internal API. Note that CmUnloadKey() unlocks the registry only on success. */
|
|
Status = CmUnloadKey(KeyBody->KeyControlBlock, Flags);
|
|
|
|
Quit:
|
|
/* If CmUnloadKey() failed we need to unlock registry ourselves */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Unlock the hive loading and registry locks */
|
|
ExReleasePushLockExclusive(&CmpLoadHiveLock);
|
|
CmpUnlockRegistry();
|
|
}
|
|
|
|
/* Dereference the key */
|
|
ObDereferenceObject(KeyBody);
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtUnloadKeyEx(IN POBJECT_ATTRIBUTES TargetKey,
|
|
IN HANDLE Event)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* EOF */
|