mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
482eb909fe
Probe the data before allocating a copy buffer. Otherwise NtSetValueKey returns an unexpected status code in case of too large data size. This fixes the NtSetValueKey ntdll api tests.
1989 lines
60 KiB
C
1989 lines
60 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;
|
|
ULONG ParentConv = 0, ChildConv = 0;
|
|
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;
|
|
|
|
/* Acquire the lock depending on flags */
|
|
if (Flags == REG_FORCE_UNLOAD)
|
|
{
|
|
/* Lock registry exclusively */
|
|
CmpLockRegistryExclusive();
|
|
}
|
|
else
|
|
{
|
|
/* Lock registry */
|
|
CmpLockRegistry();
|
|
|
|
/* Acquire the hive loading lock */
|
|
ExAcquirePushLockExclusive(&CmpLoadHiveLock);
|
|
|
|
/* Lock parent and child */
|
|
if (KeyBody->KeyControlBlock->ParentKcb)
|
|
ParentConv = KeyBody->KeyControlBlock->ParentKcb->ConvKey;
|
|
else
|
|
ParentConv = KeyBody->KeyControlBlock->ConvKey;
|
|
|
|
ChildConv = KeyBody->KeyControlBlock->ConvKey;
|
|
|
|
CmpAcquireTwoKcbLocksExclusiveByKey(ChildConv, ParentConv);
|
|
}
|
|
|
|
/* 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);
|
|
|
|
/* Check if we failed, but really need to succeed */
|
|
if ((Status == STATUS_CANNOT_DELETE) && (Flags == REG_FORCE_UNLOAD))
|
|
{
|
|
/* TODO: We should perform another attempt here */
|
|
_SEH2_TRY
|
|
{
|
|
DPRINT1("NtUnloadKey2(%wZ): We want to force-unload the hive but couldn't unload it: Retrying is UNIMPLEMENTED!\n", TargetKey->ObjectName);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* If CmUnloadKey() failed we need to unlock registry ourselves */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (Flags != REG_FORCE_UNLOAD)
|
|
{
|
|
/* Release the KCB locks */
|
|
CmpReleaseTwoKcbLockByKey(ChildConv, ParentConv);
|
|
|
|
/* Release the hive loading lock */
|
|
ExReleasePushLockExclusive(&CmpLoadHiveLock);
|
|
}
|
|
|
|
/* Unlock the registry */
|
|
CmpUnlockRegistry();
|
|
}
|
|
|
|
Quit:
|
|
/* 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 */
|