3239 lines
105 KiB
C
3239 lines
105 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1997-1999 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
wmi.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Device driver interface for WMI
|
||
|
|
||
|
Author:
|
||
|
|
||
|
AlanWar
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
Kernel mode
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "wmikmp.h"
|
||
|
#ifndef MEMPHIS
|
||
|
#include "evntrace.h"
|
||
|
#include "tracep.h"
|
||
|
#endif
|
||
|
|
||
|
NTSTATUS
|
||
|
WmipOpenCloseCleanup(
|
||
|
IN PDEVICE_OBJECT DeviceObject,
|
||
|
IN PIRP Irp
|
||
|
);
|
||
|
NTSTATUS
|
||
|
WmipIoControl(
|
||
|
IN PDEVICE_OBJECT DeviceObject,
|
||
|
IN PIRP Irp
|
||
|
);
|
||
|
|
||
|
NTSTATUS WmipObjectToPDO(
|
||
|
PFILE_OBJECT FileObject,
|
||
|
PDEVICE_OBJECT DeviceObject,
|
||
|
PDEVICE_OBJECT *PDO
|
||
|
);
|
||
|
|
||
|
BOOLEAN
|
||
|
WmipFastIoDeviceControl(
|
||
|
IN PFILE_OBJECT FileObject,
|
||
|
IN BOOLEAN Wait,
|
||
|
IN PVOID InputBuffer OPTIONAL,
|
||
|
IN ULONG InputBufferLength,
|
||
|
OUT PVOID OutputBuffer OPTIONAL,
|
||
|
IN ULONG OutputBufferLength,
|
||
|
IN ULONG IoControlCode,
|
||
|
OUT PIO_STATUS_BLOCK IoStatus,
|
||
|
IN struct _DEVICE_OBJECT *DeviceObject
|
||
|
);
|
||
|
|
||
|
NTSTATUS
|
||
|
DriverEntry(
|
||
|
IN PDRIVER_OBJECT DriverObject,
|
||
|
IN PUNICODE_STRING RegistryPath
|
||
|
);
|
||
|
|
||
|
|
||
|
NTSTATUS WmipProbeWnodeAllData(
|
||
|
PWNODE_ALL_DATA Wnode,
|
||
|
ULONG InBufferLen,
|
||
|
ULONG OutBufferLen
|
||
|
);
|
||
|
|
||
|
NTSTATUS WmipProbeWnodeSingleInstance(
|
||
|
PWNODE_SINGLE_INSTANCE Wnode,
|
||
|
ULONG InBufferLen,
|
||
|
ULONG OutBufferLen,
|
||
|
BOOLEAN OutBound
|
||
|
);
|
||
|
|
||
|
NTSTATUS WmipProbeWnodeSingleItem(
|
||
|
PWNODE_SINGLE_ITEM Wnode,
|
||
|
ULONG InBufferLen
|
||
|
);
|
||
|
|
||
|
|
||
|
NTSTATUS WmipProbeWnodeMethodItem(
|
||
|
PWNODE_METHOD_ITEM Wnode,
|
||
|
ULONG InBufferLen,
|
||
|
ULONG OutBufferLen
|
||
|
);
|
||
|
|
||
|
NTSTATUS WmipProbeWnodeWorker(
|
||
|
PWNODE_HEADER WnodeHeader,
|
||
|
ULONG MinWnodeSize,
|
||
|
ULONG InstanceNameOffset,
|
||
|
ULONG DataBlockOffset,
|
||
|
ULONG DataBlockSize,
|
||
|
ULONG InBufferLen,
|
||
|
ULONG OutBufferLen,
|
||
|
BOOLEAN CheckOutBound,
|
||
|
BOOLEAN CheckInBound
|
||
|
);
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
WmipSystemControl(
|
||
|
IN PDEVICE_OBJECT DeviceObject,
|
||
|
IN PIRP Irp
|
||
|
);
|
||
|
|
||
|
NTSTATUS WmipSendWmiIrp(
|
||
|
UCHAR MinorFunction,
|
||
|
ULONG ProviderId,
|
||
|
PVOID DataPath,
|
||
|
ULONG BufferLength,
|
||
|
PVOID Buffer,
|
||
|
PIO_STATUS_BLOCK Iosb
|
||
|
);
|
||
|
|
||
|
NTSTATUS WmipProbeWmiRegRequest(
|
||
|
IN PWMIREGREQUEST Buffer,
|
||
|
IN ULONG InBufferLen,
|
||
|
IN ULONG OutBufferLen
|
||
|
);
|
||
|
|
||
|
#ifdef ALLOC_PRAGMA
|
||
|
#pragma alloc_text(INIT,WmipDriverEntry)
|
||
|
#pragma alloc_text(INIT,DriverEntry)
|
||
|
|
||
|
#pragma alloc_text(PAGE,WmipOpenCloseCleanup)
|
||
|
#pragma alloc_text(PAGE,WmipIoControl)
|
||
|
#pragma alloc_text(PAGE,WmipForwardWmiIrp)
|
||
|
#pragma alloc_text(PAGE,WmipObjectToPDO)
|
||
|
#pragma alloc_text(PAGE,WmipTranslateFileHandle)
|
||
|
#pragma alloc_text(PAGE,WmipProbeWnodeAllData)
|
||
|
#pragma alloc_text(PAGE,WmipProbeWnodeSingleInstance)
|
||
|
#pragma alloc_text(PAGE,WmipProbeWnodeSingleItem)
|
||
|
#pragma alloc_text(PAGE,WmipProbeWnodeMethodItem)
|
||
|
#pragma alloc_text(PAGE,WmipProbeWnodeWorker)
|
||
|
#pragma alloc_text(PAGE,WmipProbeWmiOpenGuidBlock)
|
||
|
#pragma alloc_text(PAGE,WmipProbeAndCaptureGuidObjectAttributes)
|
||
|
#pragma alloc_text(PAGE,WmipUpdateDeviceStackSize)
|
||
|
#pragma alloc_text(PAGE,WmipSystemControl)
|
||
|
#pragma alloc_text(PAGE,WmipGetDevicePDO)
|
||
|
#pragma alloc_text(PAGE,WmipSendWmiIrp)
|
||
|
#pragma alloc_text(PAGE,WmipProbeWmiRegRequest)
|
||
|
|
||
|
#ifndef MEMPHIS
|
||
|
#pragma alloc_text(PAGE,WmipFastIoDeviceControl)
|
||
|
#endif
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|
||
|
PDEVICE_OBJECT WmipServiceDeviceObject;
|
||
|
|
||
|
//
|
||
|
// This specifies the maximum size that an event can be
|
||
|
ULONG WmipMaxKmWnodeEventSize = DEFAULTMAXKMWNODEEVENTSIZE;
|
||
|
|
||
|
|
||
|
|
||
|
#ifdef ALLOC_DATA_PRAGMA
|
||
|
#pragma data_seg("PAGEDATA")
|
||
|
#endif
|
||
|
|
||
|
#if defined(_AMD64_) || defined(_IA64_) || defined(i386)
|
||
|
PVOID WmipDockUndockNotificationEntry;
|
||
|
#endif
|
||
|
|
||
|
KMUTEX WmipSMMutex;
|
||
|
KMUTEX WmipTLMutex;
|
||
|
|
||
|
//
|
||
|
// This maintains the registry path for the wmi device
|
||
|
UNICODE_STRING WmipRegistryPath;
|
||
|
|
||
|
#ifndef MEMPHIS
|
||
|
FAST_IO_DISPATCH WmipFastIoDispatch;
|
||
|
#endif
|
||
|
|
||
|
#ifdef ALLOC_DATA_PRAGMA
|
||
|
#pragma const_seg("PAGECONST")
|
||
|
#endif
|
||
|
|
||
|
NTSTATUS
|
||
|
DriverEntry(
|
||
|
IN PDRIVER_OBJECT DriverObject,
|
||
|
IN PUNICODE_STRING RegistryPath
|
||
|
)
|
||
|
{
|
||
|
UNREFERENCED_PARAMETER(DriverObject);
|
||
|
UNREFERENCED_PARAMETER(RegistryPath);
|
||
|
// Never called
|
||
|
return(STATUS_SUCCESS);
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
WmipDriverEntry(
|
||
|
IN PDRIVER_OBJECT DriverObject,
|
||
|
IN PUNICODE_STRING RegistryPath
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This is the callback function when we call IoCreateDriver to create a
|
||
|
WMI Driver Object. In this function, we need to remember the
|
||
|
DriverObject, create a device object and then create a Win32 visible
|
||
|
symbolic link name so that the WMI user mode component can access us.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
DriverObject - Pointer to the driver object created by the system.
|
||
|
|
||
|
RegistryPath - is NULL.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
STATUS_SUCCESS
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
UNICODE_STRING DeviceName;
|
||
|
UNICODE_STRING ServiceSymbolicLinkName;
|
||
|
#ifndef MEMPHIS
|
||
|
PFAST_IO_DISPATCH fastIoDispatch;
|
||
|
#endif
|
||
|
ANSI_STRING AnsiString;
|
||
|
|
||
|
PAGED_CODE();
|
||
|
UNREFERENCED_PARAMETER(RegistryPath);
|
||
|
|
||
|
//
|
||
|
// First thing to do is make sure our critical section has been initalized
|
||
|
//
|
||
|
KeInitializeMutex(&WmipSMMutex, 0);
|
||
|
KeInitializeMutex(&WmipTLMutex, 0);
|
||
|
|
||
|
//
|
||
|
// Initialize internal WMI data structrurs
|
||
|
//
|
||
|
WmipInitializeRegistration(0);
|
||
|
WmipInitializeNotifications();
|
||
|
Status = WmipInitializeDataStructs();
|
||
|
if (! NT_SUCCESS(Status))
|
||
|
{
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Since Io does not pass a registry path for this device we need to make
|
||
|
// up one
|
||
|
RtlInitAnsiString(&AnsiString,
|
||
|
"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\WMI");
|
||
|
Status = RtlAnsiStringToUnicodeString(&WmipRegistryPath,
|
||
|
&AnsiString,
|
||
|
TRUE);
|
||
|
#ifndef MEMPHIS
|
||
|
Status = WmipInitializeSecurity();
|
||
|
if (!NT_SUCCESS(Status))
|
||
|
{
|
||
|
return(Status);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Create the service device object and symbolic link
|
||
|
//
|
||
|
RtlInitUnicodeString( &DeviceName, WMIServiceDeviceObjectName );
|
||
|
Status = IoCreateDevice(
|
||
|
DriverObject,
|
||
|
0,
|
||
|
&DeviceName,
|
||
|
FILE_DEVICE_UNKNOWN,
|
||
|
#ifdef MEMPHIS
|
||
|
0,
|
||
|
#else
|
||
|
FILE_DEVICE_SECURE_OPEN, // No standard device characteristics
|
||
|
#endif
|
||
|
FALSE, // This isn't an exclusive device
|
||
|
&WmipServiceDeviceObject
|
||
|
);
|
||
|
|
||
|
if (! NT_SUCCESS(Status))
|
||
|
{
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
RtlInitUnicodeString( &ServiceSymbolicLinkName,
|
||
|
WMIServiceSymbolicLinkName );
|
||
|
Status = IoCreateSymbolicLink( &ServiceSymbolicLinkName,
|
||
|
&DeviceName );
|
||
|
if (! NT_SUCCESS(Status))
|
||
|
{
|
||
|
IoDeleteDevice( WmipServiceDeviceObject );
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Establish an initial irp stack size
|
||
|
WmipServiceDeviceObject->StackSize = WmiDeviceStackSize;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Create dispatch entrypoints
|
||
|
//
|
||
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = WmipOpenCloseCleanup;
|
||
|
DriverObject->MajorFunction[IRP_MJ_CLOSE] = WmipOpenCloseCleanup;
|
||
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = WmipIoControl;
|
||
|
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = WmipOpenCloseCleanup;
|
||
|
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = WmipSystemControl;
|
||
|
DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = WmipShutdown;
|
||
|
|
||
|
//
|
||
|
// Register for notification of docking events
|
||
|
#if defined(_AMD64_) || defined(_IA64_) || defined(i386)
|
||
|
IoRegisterPlugPlayNotification(
|
||
|
EventCategoryHardwareProfileChange,
|
||
|
0,
|
||
|
NULL,
|
||
|
DriverObject,
|
||
|
WmipDockUndockEventCallback,
|
||
|
NULL,
|
||
|
&WmipDockUndockNotificationEntry);
|
||
|
#endif
|
||
|
//
|
||
|
// We reset this flag to let the IO manager know that the device
|
||
|
// is ready to receive requests. We only do this for the kernel
|
||
|
// dll since the IO manager does it when WMI loads as a normal
|
||
|
// device.
|
||
|
WmipServiceDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
||
|
|
||
|
#ifndef MEMPHIS
|
||
|
IoWMIRegistrationControl(WmipServiceDeviceObject,
|
||
|
WMIREG_ACTION_REGISTER);
|
||
|
#endif
|
||
|
|
||
|
#ifndef MEMPHIS
|
||
|
fastIoDispatch = &WmipFastIoDispatch;
|
||
|
RtlZeroMemory(fastIoDispatch, sizeof(FAST_IO_DISPATCH));
|
||
|
fastIoDispatch->SizeOfFastIoDispatch = sizeof(FAST_IO_DISPATCH);
|
||
|
fastIoDispatch->FastIoDeviceControl = WmipFastIoDeviceControl;
|
||
|
DriverObject->FastIoDispatch = fastIoDispatch;
|
||
|
RtlZeroMemory(&WmipRefCount[0], MAXLOGGERS*sizeof(ULONG));
|
||
|
RtlZeroMemory(&WmipLoggerContext[0], MAXLOGGERS*sizeof(PWMI_LOGGER_CONTEXT));
|
||
|
WmipStartGlobalLogger(); // Try and see if we need to start this
|
||
|
IoRegisterShutdownNotification(WmipServiceDeviceObject);
|
||
|
#endif // MEMPHIS
|
||
|
|
||
|
SharedUserData->TraceLogging = 0; //Initialize the Heap and Crisec Coll tracing status off
|
||
|
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
WmipOpenCloseCleanup(
|
||
|
IN PDEVICE_OBJECT DeviceObject,
|
||
|
IN PIRP Irp
|
||
|
)
|
||
|
{
|
||
|
PAGED_CODE();
|
||
|
UNREFERENCED_PARAMETER(DeviceObject);
|
||
|
|
||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
Irp->IoStatus.Information = 0;
|
||
|
|
||
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
void WmipUpdateDeviceStackSize(
|
||
|
CCHAR NewStackSize
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine will update the stack size that is specified in the WMI
|
||
|
device's device object. This needs to be protected since it can be updated
|
||
|
when a device registers and whenever an irp is forwarded to a device.
|
||
|
WMI needs to maintain a stack size one greater than the stack size of the
|
||
|
largest device stack to which it forwards irps to. Consider a bottom
|
||
|
driver that registers with WMI and has a stack size of 1. If 2 device
|
||
|
attach on top of it then WMI will forward to the topmost in the stack
|
||
|
which would need a stack size of 3, so the original WMI irp (ie the one
|
||
|
created by the IOCTL to the WMI device) would need a stack size of 4.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
NewStackSize is the new stack size needed
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NT status ccode
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PAGED_CODE();
|
||
|
|
||
|
WmipEnterSMCritSection();
|
||
|
if (WmipServiceDeviceObject->StackSize < NewStackSize)
|
||
|
{
|
||
|
WmipServiceDeviceObject->StackSize = NewStackSize;
|
||
|
}
|
||
|
WmipLeaveSMCritSection();
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
WmipIoControl(
|
||
|
IN PDEVICE_OBJECT DeviceObject,
|
||
|
IN PIRP Irp
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
|
ULONG InBufferLen = irpStack->Parameters.DeviceIoControl.InputBufferLength;
|
||
|
ULONG OutBufferLen = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
||
|
PVOID Buffer = Irp->AssociatedIrp.SystemBuffer;
|
||
|
PWNODE_HEADER Wnode = (PWNODE_HEADER)Buffer;
|
||
|
ULONG SizeNeeded;
|
||
|
ULONG Ioctl;
|
||
|
|
||
|
PAGED_CODE();
|
||
|
|
||
|
UNREFERENCED_PARAMETER(DeviceObject);
|
||
|
|
||
|
Ioctl = irpStack->Parameters.DeviceIoControl.IoControlCode;
|
||
|
|
||
|
switch (Ioctl)
|
||
|
{
|
||
|
#ifndef MEMPHIS
|
||
|
case IOCTL_WMI_OPEN_GUID:
|
||
|
case IOCTL_WMI_OPEN_GUID_FOR_QUERYSET:
|
||
|
case IOCTL_WMI_OPEN_GUID_FOR_EVENTS:
|
||
|
{
|
||
|
WMIOPENGUIDBLOCK CapturedGuidBlock;
|
||
|
OBJECT_ATTRIBUTES CapturedObjectAttributes;
|
||
|
UNICODE_STRING CapturedGuidString;
|
||
|
WCHAR CapturedGuidBuffer[WmiGuidObjectNameLength + 1];
|
||
|
PWMIOPENGUIDBLOCK InGuidBlock;
|
||
|
HANDLE Handle;
|
||
|
ULONG DesiredAccess;
|
||
|
|
||
|
InGuidBlock = (PWMIOPENGUIDBLOCK)Buffer;
|
||
|
|
||
|
Status = WmipProbeWmiOpenGuidBlock(&CapturedObjectAttributes,
|
||
|
&CapturedGuidString,
|
||
|
CapturedGuidBuffer,
|
||
|
&DesiredAccess,
|
||
|
InGuidBlock,
|
||
|
InBufferLen,
|
||
|
OutBufferLen);
|
||
|
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
Status = WmipOpenBlock(Ioctl,
|
||
|
UserMode,
|
||
|
&CapturedObjectAttributes,
|
||
|
DesiredAccess,
|
||
|
&Handle);
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
#if defined(_WIN64)
|
||
|
if (IoIs32bitProcess(NULL))
|
||
|
{
|
||
|
((PWMIOPENGUIDBLOCK32)InGuidBlock)->Handle.Handle32 = PtrToUlong(Handle);
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
InGuidBlock->Handle.Handle = Handle;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
case IOCTL_WMI_QUERY_ALL_DATA:
|
||
|
{
|
||
|
if (OutBufferLen < sizeof(WNODE_ALL_DATA))
|
||
|
{
|
||
|
//
|
||
|
// WMI will not send any request whose output buffer is not
|
||
|
// at least the size of a WNODE_TOO_SMALL.
|
||
|
Status = STATUS_BUFFER_TOO_SMALL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
Status = WmipProbeWnodeAllData((PWNODE_ALL_DATA)Wnode,
|
||
|
InBufferLen,
|
||
|
OutBufferLen);
|
||
|
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
Status = WmipQueryAllData(NULL,
|
||
|
Irp,
|
||
|
UserMode,
|
||
|
(PWNODE_ALL_DATA)Wnode,
|
||
|
OutBufferLen,
|
||
|
&OutBufferLen);
|
||
|
|
||
|
} else {
|
||
|
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Invalid IOCTL_WMI_QUERY_ALL_DATA Wnode\n"));
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_QAD_MULTIPLE:
|
||
|
{
|
||
|
PWMIQADMULTIPLE QadMultiple;
|
||
|
|
||
|
if ((InBufferLen >= sizeof(WMIQADMULTIPLE)) &&
|
||
|
(OutBufferLen >= sizeof(WNODE_TOO_SMALL)))
|
||
|
{
|
||
|
QadMultiple = (PWMIQADMULTIPLE)Buffer;
|
||
|
if ((QadMultiple->HandleCount < QUERYMULIPLEHANDLELIMIT) &&
|
||
|
(InBufferLen >= (FIELD_OFFSET(WMIQADMULTIPLE, Handles) +
|
||
|
(QadMultiple->HandleCount * sizeof(HANDLE3264)))))
|
||
|
{
|
||
|
Status = WmipQueryAllDataMultiple(0,
|
||
|
NULL,
|
||
|
Irp,
|
||
|
UserMode,
|
||
|
Buffer,
|
||
|
OutBufferLen,
|
||
|
QadMultiple,
|
||
|
&OutBufferLen);
|
||
|
} else {
|
||
|
Status = STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
} else {
|
||
|
Status = STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
case IOCTL_WMI_QUERY_SINGLE_INSTANCE:
|
||
|
{
|
||
|
if (OutBufferLen < sizeof(WNODE_TOO_SMALL))
|
||
|
{
|
||
|
//
|
||
|
// WMI will not send any request whose output buffer is not
|
||
|
// at least the size of a WNODE_TOO_SMALL.
|
||
|
Status = STATUS_BUFFER_TOO_SMALL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
Status = WmipProbeWnodeSingleInstance((PWNODE_SINGLE_INSTANCE)Wnode,
|
||
|
InBufferLen,
|
||
|
OutBufferLen,
|
||
|
TRUE);
|
||
|
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
Status = WmipQuerySetExecuteSI(NULL,
|
||
|
Irp,
|
||
|
UserMode,
|
||
|
IRP_MN_QUERY_SINGLE_INSTANCE,
|
||
|
Wnode,
|
||
|
OutBufferLen,
|
||
|
&OutBufferLen);
|
||
|
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
WmipAssert(Irp->IoStatus.Information <= OutBufferLen);
|
||
|
}
|
||
|
} else {
|
||
|
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Invalid IOCTL_WMI_SINGLE_INSTANCE Wnode\n"));
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_QSI_MULTIPLE:
|
||
|
{
|
||
|
PWMIQSIMULTIPLE QsiMultiple;
|
||
|
|
||
|
if ((InBufferLen >= sizeof(WMIQSIMULTIPLE)) &&
|
||
|
(OutBufferLen >= sizeof(WNODE_TOO_SMALL)))
|
||
|
{
|
||
|
QsiMultiple = (PWMIQSIMULTIPLE)Buffer;
|
||
|
|
||
|
if ((QsiMultiple->QueryCount < QUERYMULIPLEHANDLELIMIT) &&
|
||
|
(InBufferLen >= (FIELD_OFFSET(WMIQSIMULTIPLE, QsiInfo) +
|
||
|
(QsiMultiple->QueryCount * sizeof(WMIQSIINFO)))))
|
||
|
{
|
||
|
Status = WmipQuerySingleMultiple(Irp,
|
||
|
UserMode,
|
||
|
Buffer,
|
||
|
OutBufferLen,
|
||
|
QsiMultiple,
|
||
|
QsiMultiple->QueryCount,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
&OutBufferLen);
|
||
|
|
||
|
} else {
|
||
|
Status = STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
Status = STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_SET_SINGLE_INSTANCE:
|
||
|
{
|
||
|
Status = WmipProbeWnodeSingleInstance((PWNODE_SINGLE_INSTANCE)Wnode,
|
||
|
InBufferLen,
|
||
|
OutBufferLen,
|
||
|
FALSE);
|
||
|
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
Status = WmipQuerySetExecuteSI(NULL,
|
||
|
Irp,
|
||
|
UserMode,
|
||
|
IRP_MN_CHANGE_SINGLE_INSTANCE,
|
||
|
Wnode,
|
||
|
InBufferLen,
|
||
|
&OutBufferLen);
|
||
|
|
||
|
OutBufferLen = 0;
|
||
|
} else {
|
||
|
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Invalid IOCTL_WMI_SET_SINGLE_INSTANCE Wnode\n"));
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
case IOCTL_WMI_SET_SINGLE_ITEM:
|
||
|
{
|
||
|
Status = WmipProbeWnodeSingleItem((PWNODE_SINGLE_ITEM)Wnode,
|
||
|
InBufferLen);
|
||
|
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
Status = WmipQuerySetExecuteSI(NULL,
|
||
|
Irp,
|
||
|
UserMode,
|
||
|
IRP_MN_CHANGE_SINGLE_ITEM,
|
||
|
Wnode,
|
||
|
InBufferLen,
|
||
|
&OutBufferLen);
|
||
|
|
||
|
OutBufferLen = 0;
|
||
|
} else {
|
||
|
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Invalid IOCTL_WMI_SET_SINGLE_ITEM Wnode\n"));
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_EXECUTE_METHOD:
|
||
|
{
|
||
|
//
|
||
|
// The buffer passed is the InputWnode directly followed by the
|
||
|
// method wnode. This is so that the driver can fill in the
|
||
|
// output WNODE directly on top of the input wnode.
|
||
|
PWNODE_METHOD_ITEM MethodWnode = (PWNODE_METHOD_ITEM)Wnode;
|
||
|
|
||
|
Status = WmipProbeWnodeMethodItem(MethodWnode,
|
||
|
InBufferLen,
|
||
|
OutBufferLen);
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
Status = WmipQuerySetExecuteSI(NULL,
|
||
|
Irp,
|
||
|
UserMode,
|
||
|
IRP_MN_EXECUTE_METHOD,
|
||
|
Wnode,
|
||
|
OutBufferLen,
|
||
|
&OutBufferLen);
|
||
|
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
WmipAssert(Irp->IoStatus.Information <= OutBufferLen);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_TRANSLATE_FILE_HANDLE:
|
||
|
{
|
||
|
if (InBufferLen != FIELD_OFFSET(WMIFHTOINSTANCENAME,
|
||
|
InstanceNames))
|
||
|
{
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
} else {
|
||
|
Status = WmipTranslateFileHandle((PWMIFHTOINSTANCENAME)Buffer,
|
||
|
&OutBufferLen,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_GET_VERSION:
|
||
|
{
|
||
|
if (OutBufferLen < sizeof(WMIVERSIONINFO))
|
||
|
{
|
||
|
Status = STATUS_BUFFER_TOO_SMALL;
|
||
|
} else {
|
||
|
((PWMIVERSIONINFO)Buffer)->Version = WMI_CURRENT_VERSION;
|
||
|
OutBufferLen = sizeof(WMIVERSIONINFO);
|
||
|
Status = STATUS_SUCCESS;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
case IOCTL_WMI_ENUMERATE_GUIDS_AND_PROPERTIES:
|
||
|
case IOCTL_WMI_ENUMERATE_GUIDS:
|
||
|
{
|
||
|
if (OutBufferLen < FIELD_OFFSET(WMIGUIDLISTINFO, GuidList))
|
||
|
{
|
||
|
Status = STATUS_BUFFER_TOO_SMALL;
|
||
|
} else {
|
||
|
Status = WmipEnumerateGuids(Ioctl,
|
||
|
(PWMIGUIDLISTINFO)Buffer,
|
||
|
OutBufferLen,
|
||
|
&OutBufferLen);
|
||
|
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_QUERY_GUID_INFO:
|
||
|
{
|
||
|
if (OutBufferLen < sizeof(WMIQUERYGUIDINFO))
|
||
|
{
|
||
|
Status = STATUS_BUFFER_TOO_SMALL;
|
||
|
} else {
|
||
|
Status = WmipQueryGuidInfo((PWMIQUERYGUIDINFO)Buffer);
|
||
|
OutBufferLen = sizeof(WMIQUERYGUIDINFO);
|
||
|
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_ENUMERATE_MOF_RESOURCES:
|
||
|
{
|
||
|
if (OutBufferLen >= sizeof(WMIMOFLIST))
|
||
|
{
|
||
|
Status = WmipEnumerateMofResources((PWMIMOFLIST)Buffer,
|
||
|
OutBufferLen,
|
||
|
&OutBufferLen);
|
||
|
} else {
|
||
|
Status = STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_RECEIVE_NOTIFICATIONS:
|
||
|
{
|
||
|
PWMIRECEIVENOTIFICATION ReceiveNotification;
|
||
|
ULONG CountExpected;
|
||
|
|
||
|
if ((InBufferLen >= sizeof(WMIRECEIVENOTIFICATION)) &&
|
||
|
(OutBufferLen >= sizeof(WNODE_TOO_SMALL)))
|
||
|
{
|
||
|
ReceiveNotification = (PWMIRECEIVENOTIFICATION)Buffer;
|
||
|
|
||
|
CountExpected = (InBufferLen -
|
||
|
FIELD_OFFSET(WMIRECEIVENOTIFICATION, Handles)) /
|
||
|
sizeof(HANDLE3264);
|
||
|
|
||
|
if (ReceiveNotification->HandleCount <= CountExpected)
|
||
|
{
|
||
|
Status = WmipReceiveNotifications(ReceiveNotification,
|
||
|
&OutBufferLen,
|
||
|
Irp);
|
||
|
} else {
|
||
|
//
|
||
|
// Input buffer not large enough which is an error
|
||
|
//
|
||
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
|
}
|
||
|
} else {
|
||
|
//
|
||
|
// Input and or output buffers not large enough
|
||
|
// which is an error
|
||
|
//
|
||
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_MARK_HANDLE_AS_CLOSED:
|
||
|
{
|
||
|
PWMIMARKASCLOSED MarkAsClosed;
|
||
|
|
||
|
if (InBufferLen >= sizeof(WMIMARKASCLOSED))
|
||
|
{
|
||
|
MarkAsClosed = (PWMIMARKASCLOSED)Buffer;
|
||
|
Status = WmipMarkHandleAsClosed(MarkAsClosed->Handle.Handle);
|
||
|
OutBufferLen = 0;
|
||
|
} else {
|
||
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_NOTIFY_LANGUAGE_CHANGE:
|
||
|
{
|
||
|
LPGUID LanguageGuid;
|
||
|
PWMILANGUAGECHANGE LanguageChange;
|
||
|
|
||
|
if (InBufferLen == sizeof(WMILANGUAGECHANGE))
|
||
|
{
|
||
|
LanguageChange = (PWMILANGUAGECHANGE)Buffer;
|
||
|
if (LanguageChange->Flags & WMILANGUAGECHANGE_FLAG_ADDED)
|
||
|
{
|
||
|
LanguageGuid = &GUID_MOF_RESOURCE_ADDED_NOTIFICATION;
|
||
|
} else if (LanguageChange->Flags & WMILANGUAGECHANGE_FLAG_REMOVED) {
|
||
|
LanguageGuid = &GUID_MOF_RESOURCE_REMOVED_NOTIFICATION;
|
||
|
} else {
|
||
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
WmipGenerateMofResourceNotification(LanguageChange->Language,
|
||
|
L"",
|
||
|
LanguageGuid,
|
||
|
MOFEVENT_ACTION_LANGUAGE_CHANGE);
|
||
|
|
||
|
OutBufferLen = 0;
|
||
|
Status = STATUS_SUCCESS;
|
||
|
} else {
|
||
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
#ifndef MEMPHIS
|
||
|
// Event trace logging IOCTLS
|
||
|
|
||
|
case IOCTL_WMI_UNREGISTER_GUIDS:
|
||
|
{
|
||
|
if ((InBufferLen == sizeof(WMIUNREGGUIDS)) &&
|
||
|
(OutBufferLen == sizeof(WMIUNREGGUIDS)))
|
||
|
{
|
||
|
Status = WmipUnregisterGuids((PWMIUNREGGUIDS)Buffer);
|
||
|
} else {
|
||
|
Status = STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_REGISTER_GUIDS:
|
||
|
{
|
||
|
//
|
||
|
// Register guids for user mode provider
|
||
|
//
|
||
|
Status = WmipProbeWmiRegRequest(
|
||
|
Buffer,
|
||
|
InBufferLen,
|
||
|
OutBufferLen
|
||
|
);
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
HANDLE RequestHandle;
|
||
|
PWMIREGREQUEST WmiRegRequest;
|
||
|
PWMIREGINFOW WmiRegInfo;
|
||
|
ULONG WmiRegInfoSize;
|
||
|
ULONG GuidCount;
|
||
|
ULONG TraceGuidMapSize;
|
||
|
PWMIREGRESULTS WmiRegResults;
|
||
|
PWMIREGINFOW WmiRegInfoThunk = NULL;
|
||
|
|
||
|
WmiRegRequest = (PWMIREGREQUEST)Buffer;
|
||
|
WmiRegInfo = (PWMIREGINFOW)OffsetToPtr(Buffer, sizeof(WMIREGREQUEST));
|
||
|
WmiRegInfoSize = InBufferLen - sizeof(WMIREGREQUEST);
|
||
|
GuidCount = WmiRegRequest->GuidCount;
|
||
|
TraceGuidMapSize = GuidCount * sizeof(TRACEGUIDMAP);
|
||
|
WmiRegResults = (PWMIREGRESULTS)OffsetToPtr(Buffer, TraceGuidMapSize);
|
||
|
|
||
|
//
|
||
|
// For WOW64, WMIREGINFOW and WMIREGGUIDW structures both need
|
||
|
// to be thunked here because of padding and ULONG_PTR in them.
|
||
|
//
|
||
|
#if defined(_WIN64)
|
||
|
if (IoIs32bitProcess(NULL))
|
||
|
{
|
||
|
ULONG SizeNeeded, SizeToCopy, i;
|
||
|
PWMIREGINFOW WmiRegInfo32;
|
||
|
PWMIREGGUIDW WmiRegGuid;
|
||
|
PUCHAR pSource, pTarget;
|
||
|
ULONG ImageNameLength = 0;
|
||
|
ULONG ResourceNameLength = 0;
|
||
|
ULONG Offset = 0;
|
||
|
//
|
||
|
// Find the GuidCount and allocate storage here.
|
||
|
//
|
||
|
|
||
|
if (WmiRegInfo->RegistryPath > 0)
|
||
|
{
|
||
|
pSource = OffsetToPtr(WmiRegInfo, WmiRegInfo->RegistryPath);
|
||
|
ImageNameLength = *( (PUSHORT) pSource) + sizeof(USHORT);
|
||
|
}
|
||
|
|
||
|
if (WmiRegInfo->MofResourceName > 0)
|
||
|
{
|
||
|
pSource = OffsetToPtr(WmiRegInfo, WmiRegInfo->MofResourceName);
|
||
|
ResourceNameLength = *((PUSHORT)pSource) + sizeof(USHORT);
|
||
|
}
|
||
|
|
||
|
SizeNeeded = sizeof(WMIREGINFOW) +
|
||
|
GuidCount * sizeof(WMIREGGUIDW) +
|
||
|
ImageNameLength + ResourceNameLength;
|
||
|
|
||
|
SizeNeeded = (SizeNeeded + 7) & ~7;
|
||
|
|
||
|
WmiRegInfoThunk = (PWMIREGINFOW) WmipAlloc(SizeNeeded);
|
||
|
|
||
|
if (WmiRegInfoThunk == NULL)
|
||
|
{
|
||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
OutBufferLen = 0;
|
||
|
break;
|
||
|
}
|
||
|
RtlZeroMemory(WmiRegInfoThunk, SizeNeeded);
|
||
|
pTarget = (PUCHAR)WmiRegInfoThunk;
|
||
|
pSource = (PUCHAR)WmiRegInfo;
|
||
|
SizeToCopy = WmiRegRequest->WmiRegInfo32Size;
|
||
|
RtlCopyMemory(pTarget, pSource, SizeToCopy);
|
||
|
|
||
|
pTarget += FIELD_OFFSET(WMIREGINFOW, WmiRegGuid);
|
||
|
pSource += SizeToCopy;
|
||
|
SizeToCopy = WmiRegRequest->WmiRegGuid32Size;
|
||
|
Offset = FIELD_OFFSET(WMIREGINFOW, WmiRegGuid);
|
||
|
|
||
|
for (i=0; i < GuidCount; i++)
|
||
|
{
|
||
|
RtlCopyMemory(pTarget, pSource, SizeToCopy);
|
||
|
|
||
|
//
|
||
|
// The InstanceCount checks are done here because the
|
||
|
// source may not be aligned.
|
||
|
//
|
||
|
WmiRegGuid = (PWMIREGGUIDW) pTarget;
|
||
|
if ( (WmiRegGuid->InstanceCount > 0) ||
|
||
|
(WmiRegGuid->InstanceNameList > 0) )
|
||
|
{
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
pTarget += sizeof(WMIREGGUIDW);
|
||
|
pSource += SizeToCopy;
|
||
|
Offset += sizeof(WMIREGGUIDW);
|
||
|
}
|
||
|
|
||
|
if (ImageNameLength > 0)
|
||
|
{
|
||
|
pSource = OffsetToPtr(WmiRegInfo, WmiRegInfo->RegistryPath);
|
||
|
RtlCopyMemory(pTarget, pSource, ImageNameLength);
|
||
|
pTarget += ImageNameLength;
|
||
|
WmiRegInfoThunk->RegistryPath = Offset;
|
||
|
Offset += ImageNameLength;
|
||
|
}
|
||
|
|
||
|
if (ResourceNameLength > 0)
|
||
|
{
|
||
|
pSource = OffsetToPtr(WmiRegInfo, WmiRegInfo->MofResourceName);
|
||
|
RtlCopyMemory(pTarget, pSource, ResourceNameLength);
|
||
|
pTarget += ResourceNameLength;
|
||
|
WmiRegInfoThunk->MofResourceName = Offset;
|
||
|
Offset += ResourceNameLength;
|
||
|
}
|
||
|
|
||
|
WmiRegInfo = WmiRegInfoThunk;
|
||
|
WmiRegInfoSize = SizeNeeded;
|
||
|
WmiRegInfo->BufferSize = SizeNeeded;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
Status = WmipRegisterUMGuids(WmiRegRequest->ObjectAttributes,
|
||
|
WmiRegRequest->Cookie,
|
||
|
WmiRegInfo,
|
||
|
WmiRegInfoSize,
|
||
|
(PTRACEGUIDMAP)Buffer,
|
||
|
GuidCount,
|
||
|
&RequestHandle,
|
||
|
&WmiRegResults->LoggerContext);
|
||
|
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
#if defined(_WIN64)
|
||
|
if (IoIs32bitProcess(NULL))
|
||
|
{
|
||
|
WmiRegResults->RequestHandle.Handle64 = 0;
|
||
|
WmiRegResults->RequestHandle.Handle32 = PtrToUlong(RequestHandle);
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
WmiRegResults->RequestHandle.Handle = RequestHandle;
|
||
|
}
|
||
|
OutBufferLen = sizeof(WMIREGRESULTS) + TraceGuidMapSize;
|
||
|
}
|
||
|
|
||
|
if (WmiRegInfoThunk != NULL)
|
||
|
{
|
||
|
WmipFree(WmiRegInfoThunk);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_CREATE_UM_LOGGER:
|
||
|
{
|
||
|
//
|
||
|
// Create User mode logger
|
||
|
//
|
||
|
PWNODE_HEADER Wnode;
|
||
|
ULONG MinLength;
|
||
|
|
||
|
#if defined(_WIN64)
|
||
|
if (IoIs32bitProcess(NULL))
|
||
|
{
|
||
|
ULONG SizeNeeded;
|
||
|
PUCHAR src, dest;
|
||
|
PWMICREATEUMLOGGER32 WmiCreateUmLogger32 = (PWMICREATEUMLOGGER32)Buffer;
|
||
|
PWMICREATEUMLOGGER WmiCreateUmLoggerThunk;
|
||
|
|
||
|
MinLength = sizeof(WMICREATEUMLOGGER32) + sizeof(WNODE_HEADER);
|
||
|
if (InBufferLen < MinLength) {
|
||
|
Status = STATUS_INVALID_PARAMETER;
|
||
|
OutBufferLen = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
Wnode = (PWNODE_HEADER)((PUCHAR)WmiCreateUmLogger32 + sizeof(WMICREATEUMLOGGER32));
|
||
|
|
||
|
if (Wnode->BufferSize > (InBufferLen-sizeof(WMICREATEUMLOGGER32)) ) {
|
||
|
Status = STATUS_INVALID_PARAMETER;
|
||
|
OutBufferLen = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
SizeNeeded = InBufferLen + sizeof(WMICREATEUMLOGGER) - sizeof(WMICREATEUMLOGGER32);
|
||
|
|
||
|
SizeNeeded = (SizeNeeded + 7) & ~7;
|
||
|
|
||
|
WmiCreateUmLoggerThunk = (PWMICREATEUMLOGGER) WmipAlloc(SizeNeeded);
|
||
|
|
||
|
if (WmiCreateUmLoggerThunk == NULL)
|
||
|
{
|
||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
OutBufferLen = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
RtlZeroMemory(WmiCreateUmLoggerThunk, SizeNeeded);
|
||
|
WmiCreateUmLoggerThunk->ObjectAttributes =
|
||
|
UlongToPtr(WmiCreateUmLogger32->ObjectAttributes);
|
||
|
WmiCreateUmLoggerThunk->ControlGuid = WmiCreateUmLogger32->ControlGuid;
|
||
|
|
||
|
dest = (PUCHAR)WmiCreateUmLoggerThunk + sizeof(WMICREATEUMLOGGER);
|
||
|
src = (PUCHAR)WmiCreateUmLogger32 + sizeof(WMICREATEUMLOGGER32);
|
||
|
|
||
|
RtlCopyMemory(dest, src, Wnode->BufferSize);
|
||
|
|
||
|
Status = WmipCreateUMLogger(WmiCreateUmLoggerThunk);
|
||
|
WmiCreateUmLogger32->ReplyHandle.Handle64 = 0;
|
||
|
WmiCreateUmLogger32->ReplyHandle.Handle32 = PtrToUlong(WmiCreateUmLoggerThunk->ReplyHandle.Handle);
|
||
|
WmiCreateUmLogger32->ReplyCount = WmiCreateUmLoggerThunk->ReplyCount;
|
||
|
|
||
|
WmipFree(WmiCreateUmLoggerThunk);
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
MinLength = sizeof(WMICREATEUMLOGGER) + sizeof(WNODE_HEADER);
|
||
|
if (InBufferLen < MinLength) {
|
||
|
Status = STATUS_INVALID_PARAMETER;
|
||
|
OutBufferLen = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
Wnode = (PWNODE_HEADER) ((PUCHAR)Buffer + sizeof(WMICREATEUMLOGGER));
|
||
|
|
||
|
if (Wnode->BufferSize > (InBufferLen-sizeof(WMICREATEUMLOGGER)) ) {
|
||
|
Status = STATUS_INVALID_PARAMETER;
|
||
|
OutBufferLen = 0;
|
||
|
break;
|
||
|
}
|
||
|
Status = WmipCreateUMLogger((PWMICREATEUMLOGGER)Buffer);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_MB_REPLY:
|
||
|
{
|
||
|
//
|
||
|
// MB Reply message
|
||
|
//
|
||
|
PUCHAR Message;
|
||
|
ULONG MessageSize;
|
||
|
PWMIMBREPLY WmiMBReply;
|
||
|
|
||
|
if (InBufferLen >= FIELD_OFFSET(WMIMBREPLY, Message))
|
||
|
{
|
||
|
WmiMBReply = (PWMIMBREPLY)Buffer;
|
||
|
Message = (PUCHAR)Buffer + FIELD_OFFSET(WMIMBREPLY, Message);
|
||
|
MessageSize = InBufferLen - FIELD_OFFSET(WMIMBREPLY, Message);
|
||
|
|
||
|
Status = WmipMBReply(WmiMBReply->Handle.Handle,
|
||
|
WmiMBReply->ReplyIndex,
|
||
|
Message,
|
||
|
MessageSize);
|
||
|
} else {
|
||
|
Status = STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
OutBufferLen = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
case IOCTL_WMI_ENABLE_DISABLE_TRACELOG:
|
||
|
{
|
||
|
PWMITRACEENABLEDISABLEINFO TraceEnableInfo;
|
||
|
|
||
|
OutBufferLen = 0;
|
||
|
if (InBufferLen == sizeof(WMITRACEENABLEDISABLEINFO))
|
||
|
{
|
||
|
TraceEnableInfo = (PWMITRACEENABLEDISABLEINFO)Buffer;
|
||
|
Status = WmipEnableDisableTrace(Ioctl,
|
||
|
TraceEnableInfo);
|
||
|
} else {
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
case IOCTL_WMI_START_LOGGER:
|
||
|
{
|
||
|
PWMI_LOGGER_INFORMATION LoggerInfo;
|
||
|
ULONG LoggerBuf, LogFileBuf;
|
||
|
|
||
|
if ((InBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ||
|
||
|
(OutBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ) {
|
||
|
OutBufferLen = 0;
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( !(Wnode->Flags & WNODE_FLAG_TRACED_GUID) ) {
|
||
|
OutBufferLen = 0;
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
LoggerInfo = (PWMI_LOGGER_INFORMATION) Wnode;
|
||
|
#ifdef _WIN64
|
||
|
if (IoIs32bitProcess(Irp)) {
|
||
|
LoggerBuf = ( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer;
|
||
|
LoggerInfo->LoggerName.Buffer = UlongToPtr(LoggerBuf);
|
||
|
LogFileBuf = ( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer;
|
||
|
LoggerInfo->LogFileName.Buffer = UlongToPtr(LogFileBuf);
|
||
|
}
|
||
|
#endif
|
||
|
Status = WmipStartLogger( LoggerInfo );
|
||
|
OutBufferLen = sizeof (WMI_LOGGER_INFORMATION);
|
||
|
#ifdef _WIN64
|
||
|
if (IoIs32bitProcess(Irp)) {
|
||
|
( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer = LoggerBuf;
|
||
|
( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer = LogFileBuf;
|
||
|
}
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_STOP_LOGGER:
|
||
|
{
|
||
|
PWMI_LOGGER_INFORMATION LoggerInfo;
|
||
|
ULONG LoggerBuf, LogFileBuf;
|
||
|
|
||
|
if ((InBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ||
|
||
|
(OutBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ) {
|
||
|
OutBufferLen = 0;
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( !(Wnode->Flags & WNODE_FLAG_TRACED_GUID) ) {
|
||
|
OutBufferLen = 0;
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
LoggerInfo = (PWMI_LOGGER_INFORMATION) Wnode;
|
||
|
#ifdef _WIN64
|
||
|
if (IoIs32bitProcess(Irp)) {
|
||
|
LoggerBuf = ( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer;
|
||
|
LoggerInfo->LoggerName.Buffer = UlongToPtr(LoggerBuf);
|
||
|
LogFileBuf = ( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer;
|
||
|
LoggerInfo->LogFileName.Buffer = UlongToPtr(LogFileBuf);
|
||
|
}
|
||
|
#endif
|
||
|
Status = WmiStopTrace( LoggerInfo );
|
||
|
OutBufferLen = sizeof (WMI_LOGGER_INFORMATION);
|
||
|
#ifdef _WIN64
|
||
|
if (IoIs32bitProcess(Irp)) {
|
||
|
( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer = LoggerBuf;
|
||
|
( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer = LogFileBuf;
|
||
|
}
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_QUERY_LOGGER:
|
||
|
{
|
||
|
PWMI_LOGGER_INFORMATION LoggerInfo;
|
||
|
ULONG LoggerBuf, LogFileBuf;
|
||
|
|
||
|
if ((InBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ||
|
||
|
(OutBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ) {
|
||
|
OutBufferLen = 0;
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( !(Wnode->Flags & WNODE_FLAG_TRACED_GUID) ) {
|
||
|
OutBufferLen = 0;
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
LoggerInfo = (PWMI_LOGGER_INFORMATION) Wnode;
|
||
|
#ifdef _WIN64
|
||
|
if (IoIs32bitProcess(Irp)) {
|
||
|
LoggerBuf = ( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer;
|
||
|
LoggerInfo->LoggerName.Buffer = UlongToPtr(LoggerBuf);
|
||
|
LogFileBuf = ( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer;
|
||
|
LoggerInfo->LogFileName.Buffer = UlongToPtr(LogFileBuf);
|
||
|
}
|
||
|
#endif
|
||
|
Status = WmipQueryLogger( LoggerInfo, NULL );
|
||
|
OutBufferLen = sizeof (WMI_LOGGER_INFORMATION);
|
||
|
#ifdef _WIN64
|
||
|
if (IoIs32bitProcess(Irp)) {
|
||
|
( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer = LoggerBuf;
|
||
|
( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer = LogFileBuf;
|
||
|
}
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_UPDATE_LOGGER:
|
||
|
{
|
||
|
PWMI_LOGGER_INFORMATION LoggerInfo;
|
||
|
ULONG LoggerBuf, LogFileBuf;
|
||
|
|
||
|
if ((InBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ||
|
||
|
(OutBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ) {
|
||
|
OutBufferLen = 0;
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( !(Wnode->Flags & WNODE_FLAG_TRACED_GUID) ) {
|
||
|
OutBufferLen = 0;
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
LoggerInfo = (PWMI_LOGGER_INFORMATION) Wnode;
|
||
|
#ifdef _WIN64
|
||
|
if (IoIs32bitProcess(Irp)) {
|
||
|
LoggerBuf = ( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer;
|
||
|
LoggerInfo->LoggerName.Buffer = UlongToPtr(LoggerBuf);
|
||
|
LogFileBuf = ( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer;
|
||
|
LoggerInfo->LogFileName.Buffer = UlongToPtr(LogFileBuf);
|
||
|
}
|
||
|
#endif
|
||
|
Status = WmiUpdateTrace( LoggerInfo );
|
||
|
OutBufferLen = sizeof (WMI_LOGGER_INFORMATION);
|
||
|
#ifdef _WIN64
|
||
|
if (IoIs32bitProcess(Irp)) {
|
||
|
( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer = LoggerBuf;
|
||
|
( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer = LogFileBuf;
|
||
|
}
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_FLUSH_LOGGER:
|
||
|
{
|
||
|
PWMI_LOGGER_INFORMATION LoggerInfo;
|
||
|
ULONG LoggerBuf, LogFileBuf;
|
||
|
|
||
|
if ((InBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ||
|
||
|
(OutBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ) {
|
||
|
OutBufferLen = 0;
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( !(Wnode->Flags & WNODE_FLAG_TRACED_GUID) ) {
|
||
|
OutBufferLen = 0;
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
LoggerInfo = (PWMI_LOGGER_INFORMATION) Wnode;
|
||
|
#ifdef _WIN64
|
||
|
if (IoIs32bitProcess(Irp)) {
|
||
|
LoggerBuf = ( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer;
|
||
|
LoggerInfo->LoggerName.Buffer = UlongToPtr(LoggerBuf);
|
||
|
LogFileBuf = ( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer;
|
||
|
LoggerInfo->LogFileName.Buffer = UlongToPtr(LogFileBuf);
|
||
|
}
|
||
|
#endif
|
||
|
Status = WmiFlushTrace( LoggerInfo );
|
||
|
OutBufferLen = sizeof (WMI_LOGGER_INFORMATION);
|
||
|
#ifdef _WIN64
|
||
|
if (IoIs32bitProcess(Irp)) {
|
||
|
( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer = LoggerBuf;
|
||
|
( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer = LogFileBuf;
|
||
|
}
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_TRACE_EVENT:
|
||
|
{ // NOTE: This relies on WmiTraceEvent to probe the buffer!
|
||
|
OutBufferLen = 0;
|
||
|
if ( InBufferLen < sizeof(WNODE_HEADER) ) {
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
Status = WmiTraceEvent(
|
||
|
(PWNODE_HEADER)
|
||
|
irpStack->Parameters.DeviceIoControl.Type3InputBuffer,
|
||
|
KeGetPreviousMode()
|
||
|
);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_TRACE_MESSAGE:
|
||
|
{ // NOTE: This relies on WmiTraceUserMessage to probe the buffer!
|
||
|
OutBufferLen = 0;
|
||
|
if ( InBufferLen < sizeof(MESSAGE_TRACE_USER) ) {
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
}
|
||
|
Status = WmiTraceUserMessage(
|
||
|
(PMESSAGE_TRACE_USER)
|
||
|
irpStack->Parameters.DeviceIoControl.Type3InputBuffer,
|
||
|
InBufferLen
|
||
|
);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_SET_MARK:
|
||
|
{
|
||
|
OutBufferLen = 0;
|
||
|
if ( InBufferLen <= FIELD_OFFSET(WMI_SET_MARK_INFORMATION, Mark)) {
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
Status = WmiSetMark( (PVOID) Wnode, InBufferLen );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IOCTL_WMI_CLOCK_TYPE:
|
||
|
{
|
||
|
if ((InBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ||
|
||
|
(OutBufferLen < sizeof(WMI_LOGGER_INFORMATION))) {
|
||
|
OutBufferLen = 0;
|
||
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
WmipValidateClockType((PWMI_LOGGER_INFORMATION) Wnode);
|
||
|
|
||
|
Status = STATUS_SUCCESS;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
#ifdef NTPERF
|
||
|
case IOCTL_WMI_SWITCH_BUFFER:
|
||
|
{
|
||
|
if ((InBufferLen < sizeof(PWMI_SWITCH_PERFMEM_BUFFER_INFORMATION)) ||
|
||
|
(OutBufferLen < sizeof(PWMI_SWITCH_PERFMEM_BUFFER_INFORMATION)) ) {
|
||
|
OutBufferLen = 0;
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
Status = WmipSwitchPerfmemBuffer((PWMI_SWITCH_PERFMEM_BUFFER_INFORMATION) Wnode );
|
||
|
OutBufferLen = sizeof (PVOID);
|
||
|
break;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
#endif // if not MEMPHIS
|
||
|
case IOCTL_WMI_NTDLL_LOGGERINFO:
|
||
|
{
|
||
|
|
||
|
if ((InBufferLen < sizeof(WMINTDLLLOGGERINFO)) ||
|
||
|
(OutBufferLen < sizeof(WMINTDLLLOGGERINFO))) {
|
||
|
OutBufferLen = 0;
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
Status = WmipNtDllLoggerInfo((PWMINTDLLLOGGERINFO)Buffer);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Unsupported IOCTL %x\n",
|
||
|
irpStack->Parameters.DeviceIoControl.IoControlCode));
|
||
|
|
||
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (Status != STATUS_PENDING)
|
||
|
{
|
||
|
Irp->IoStatus.Status = Status;
|
||
|
Irp->IoStatus.Information = NT_SUCCESS(Status) ? OutBufferLen : 0;
|
||
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
||
|
}
|
||
|
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
WmipSystemControl(
|
||
|
IN PDEVICE_OBJECT DeviceObject,
|
||
|
IN PIRP Irp
|
||
|
)
|
||
|
{
|
||
|
PAGED_CODE();
|
||
|
|
||
|
return(IoWMISystemControl((PWMILIB_INFO)&WmipWmiLibInfo,
|
||
|
DeviceObject,
|
||
|
Irp));
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS WmipWmiIrpCompletion(
|
||
|
IN PDEVICE_OBJECT DeviceObject,
|
||
|
IN PIRP Irp,
|
||
|
IN PVOID Context
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
WMI forwarded IRP completion routine. Set an event and return
|
||
|
STATUS_MORE_PROCESSING_REQUIRED. WmipForwardWmiIrp will wait on this
|
||
|
event and then re-complete the irp after cleaning up.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
DeviceObject is the device object of the WMI driver
|
||
|
Irp is the WMI irp that was just completed
|
||
|
Context is a PKEVENT that WmipForwardWmiIrp will wait on
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NT status code
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PIRPCOMPCTX IrpCompCtx;
|
||
|
PREGENTRY RegEntry;
|
||
|
PKEVENT Event;
|
||
|
|
||
|
UNREFERENCED_PARAMETER(DeviceObject);
|
||
|
UNREFERENCED_PARAMETER(Irp);
|
||
|
|
||
|
IrpCompCtx = (PIRPCOMPCTX)Context;
|
||
|
RegEntry = IrpCompCtx->RegEntry;
|
||
|
Event = &IrpCompCtx->Event;
|
||
|
|
||
|
WmipDecrementIrpCount(RegEntry);
|
||
|
|
||
|
KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
|
||
|
|
||
|
return(STATUS_MORE_PROCESSING_REQUIRED);
|
||
|
}
|
||
|
|
||
|
NTSTATUS WmipGetDevicePDO(
|
||
|
PDEVICE_OBJECT DeviceObject,
|
||
|
PDEVICE_OBJECT *PDO
|
||
|
)
|
||
|
{
|
||
|
PIRP Irp;
|
||
|
PIO_STACK_LOCATION IrpSp;
|
||
|
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
PDEVICE_RELATIONS DeviceRelations;
|
||
|
NTSTATUS Status;
|
||
|
KEVENT Event;
|
||
|
|
||
|
PAGED_CODE();
|
||
|
|
||
|
*PDO = NULL;
|
||
|
KeInitializeEvent( &Event,
|
||
|
NotificationEvent,
|
||
|
FALSE );
|
||
|
|
||
|
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
|
||
|
DeviceObject,
|
||
|
NULL,
|
||
|
0,
|
||
|
NULL,
|
||
|
&Event,
|
||
|
&IoStatusBlock );
|
||
|
|
||
|
if (Irp == NULL)
|
||
|
{
|
||
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
||
|
}
|
||
|
|
||
|
IrpSp = IoGetNextIrpStackLocation( Irp );
|
||
|
IrpSp->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
|
||
|
IrpSp->Parameters.QueryDeviceRelations.Type = TargetDeviceRelation;
|
||
|
|
||
|
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
||
|
|
||
|
Status = IoCallDriver( DeviceObject, Irp );
|
||
|
|
||
|
if (Status == STATUS_PENDING)
|
||
|
{
|
||
|
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL );
|
||
|
Status = IoStatusBlock.Status;
|
||
|
|
||
|
}
|
||
|
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
|
||
|
ASSERT(DeviceRelations);
|
||
|
ASSERT(DeviceRelations->Count == 1);
|
||
|
*PDO = DeviceRelations->Objects[0];
|
||
|
ExFreePool(DeviceRelations);
|
||
|
}
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
NTSTATUS WmipObjectToPDO(
|
||
|
PFILE_OBJECT FileObject,
|
||
|
PDEVICE_OBJECT DeviceObject,
|
||
|
PDEVICE_OBJECT *PDO
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine will determine the PDO which is the target of a file handle.
|
||
|
The mechananism is to build a IRP_MJ_PNP irp with IRP_MN_QUERY_RELATIONS
|
||
|
and query for TargetDeviceRelation. This irp is supposed to be passed down
|
||
|
a device stack until it hits the PDO which will fill in its device object
|
||
|
and return. Note that some drivers may not support this.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
FileObject is the file object for device that is being queried
|
||
|
|
||
|
DeviceObject is the device object that is being queried
|
||
|
|
||
|
*PDO returns with the PDO that is targeted by the file object. When
|
||
|
the caller has finished using the PDO it must ObDereferenceObject it.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NT status code
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
PAGED_CODE();
|
||
|
|
||
|
|
||
|
if (DeviceObject == NULL)
|
||
|
{
|
||
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
||
|
}
|
||
|
|
||
|
if (DeviceObject != NULL)
|
||
|
{
|
||
|
Status = WmipGetDevicePDO(DeviceObject, PDO);
|
||
|
} else {
|
||
|
Status = STATUS_NO_SUCH_DEVICE;
|
||
|
}
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS WmipForwardWmiIrp(
|
||
|
PIRP Irp,
|
||
|
UCHAR MinorFunction,
|
||
|
ULONG ProviderId,
|
||
|
PVOID DataPath,
|
||
|
ULONG BufferLength,
|
||
|
PVOID Buffer
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
If the provider is a driver then this routine will allocate a new irp
|
||
|
with the correct stack size and send it to the driver. If the provider
|
||
|
is a callback then it is called directly.
|
||
|
|
||
|
It is assumed that the caller has performed any security checks required
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Irp is the IOCTL irp that initiated the request
|
||
|
MinorFunction specifies the minor function code of the WMI Irp
|
||
|
WmiRegistrationId is the id passed by the user mode code. This routine
|
||
|
will look it up to determine the device object pointer.
|
||
|
DataPath is the value for the DataPath parameter of the WMI irp
|
||
|
BufferLength is the value for the BufferLength parameter of the WMI irp
|
||
|
Buffer is the value for the Buffer parameter of the WMI irp
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NT status code
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PREGENTRY RegEntry;
|
||
|
NTSTATUS Status;
|
||
|
PIO_STACK_LOCATION irpStack;
|
||
|
PDEVICE_OBJECT TargetDeviceObject;
|
||
|
CCHAR DeviceStackSize;
|
||
|
IRPCOMPCTX IrpCompCtx;
|
||
|
PWNODE_HEADER Wnode = (PWNODE_HEADER)Buffer;
|
||
|
BOOLEAN IsPnPIdRequest;
|
||
|
PDEVICE_OBJECT DeviceObject;
|
||
|
|
||
|
PAGED_CODE();
|
||
|
|
||
|
|
||
|
WmipAssert( (MinorFunction >= IRP_MN_QUERY_ALL_DATA) &&
|
||
|
(MinorFunction <= IRP_MN_REGINFO_EX) );
|
||
|
|
||
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
|
|
||
|
//
|
||
|
// For non-file handle based requests we get the registration entry
|
||
|
// to validate the target and check for a callback
|
||
|
|
||
|
RegEntry = WmipFindRegEntryByProviderId(ProviderId, TRUE);
|
||
|
|
||
|
if (RegEntry != NULL)
|
||
|
{
|
||
|
if (RegEntry->Flags & REGENTRY_FLAG_NOT_ACCEPTING_IRPS)
|
||
|
{
|
||
|
WmipUnreferenceRegEntry(RegEntry);
|
||
|
WmipDecrementIrpCount(RegEntry);
|
||
|
|
||
|
if ((MinorFunction == IRP_MN_QUERY_SINGLE_INSTANCE) ||
|
||
|
(MinorFunction == IRP_MN_CHANGE_SINGLE_INSTANCE))
|
||
|
{
|
||
|
Status = STATUS_WMI_INSTANCE_NOT_FOUND;
|
||
|
} else {
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
DeviceObject = RegEntry->DeviceObject;
|
||
|
|
||
|
#ifndef MEMPHIS
|
||
|
if (RegEntry->Flags & REGENTRY_FLAG_CALLBACK)
|
||
|
{
|
||
|
ULONG Size = 0;
|
||
|
//
|
||
|
// This guy registered as a callback so do the callback and go.
|
||
|
Status = (*RegEntry->WmiEntry)(MinorFunction,
|
||
|
DataPath,
|
||
|
BufferLength,
|
||
|
Buffer,
|
||
|
RegEntry->WmiEntry,
|
||
|
&Size
|
||
|
);
|
||
|
Irp->IoStatus.Status = Status;
|
||
|
Irp->IoStatus.Information = Size;
|
||
|
|
||
|
WmipUnreferenceRegEntry(RegEntry);
|
||
|
WmipDecrementIrpCount(RegEntry);
|
||
|
|
||
|
return(Status);
|
||
|
}
|
||
|
#endif
|
||
|
} else {
|
||
|
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Invalid device object passed from user mode %x\n",
|
||
|
ProviderId));
|
||
|
if ((MinorFunction == IRP_MN_QUERY_SINGLE_INSTANCE) ||
|
||
|
(MinorFunction == IRP_MN_CHANGE_SINGLE_INSTANCE))
|
||
|
{
|
||
|
Status = STATUS_WMI_INSTANCE_NOT_FOUND;
|
||
|
} else {
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Determine if this is a query for the device pnp id guid
|
||
|
IsPnPIdRequest = ((MinorFunction != IRP_MN_REGINFO) &&
|
||
|
(MinorFunction != IRP_MN_REGINFO_EX)) &&
|
||
|
((IsEqualGUID(&Wnode->Guid, &WmipDataProviderPnpidGuid)) ||
|
||
|
(IsEqualGUID(&Wnode->Guid, &WmipDataProviderPnPIdInstanceNamesGuid)));
|
||
|
if (IsPnPIdRequest && (RegEntry->PDO != NULL))
|
||
|
{
|
||
|
//
|
||
|
// Its the PnPId request and WMI is handling it on behalf of the
|
||
|
// device then switch the device object to our own
|
||
|
DeviceObject = WmipServiceDeviceObject;
|
||
|
IsPnPIdRequest = FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get the top of the device stack for our targer WMI device. Note that
|
||
|
// IoGetAttachedDeviceReference also takes an object reference
|
||
|
// which we get rid of after the the irp is completed by the
|
||
|
// data provider driver.
|
||
|
TargetDeviceObject = IoGetAttachedDeviceReference(DeviceObject);
|
||
|
DeviceStackSize = TargetDeviceObject->StackSize + 1;
|
||
|
|
||
|
//
|
||
|
// Check that there are enough stack locations in our irp so that we
|
||
|
// can forward it to the top of the device stack. We must also check
|
||
|
// if our target device is the WMI data or service device otherwise
|
||
|
// the number of stack locations for it will keep increementing until
|
||
|
// the machine crashes
|
||
|
if ((DeviceStackSize <= WmipServiceDeviceObject->StackSize) ||
|
||
|
(TargetDeviceObject == WmipServiceDeviceObject))
|
||
|
{
|
||
|
//
|
||
|
// There are enough stack locations in the WMI irp to forward
|
||
|
// Remember some context information in our irp stack and use
|
||
|
// it as our completion context value
|
||
|
|
||
|
KeInitializeEvent( &IrpCompCtx.Event,
|
||
|
SynchronizationEvent,
|
||
|
FALSE );
|
||
|
|
||
|
IrpCompCtx.RegEntry = RegEntry;
|
||
|
|
||
|
IoSetCompletionRoutine(Irp,
|
||
|
WmipWmiIrpCompletion,
|
||
|
(PVOID)&IrpCompCtx,
|
||
|
TRUE,
|
||
|
TRUE,
|
||
|
TRUE);
|
||
|
|
||
|
//
|
||
|
// Setup next irp stack location with WMI irp info
|
||
|
irpStack = IoGetNextIrpStackLocation(Irp);
|
||
|
irpStack->MajorFunction = IRP_MJ_SYSTEM_CONTROL;
|
||
|
irpStack->MinorFunction = MinorFunction;
|
||
|
irpStack->Parameters.WMI.ProviderId = (ULONG_PTR)DeviceObject;
|
||
|
irpStack->Parameters.WMI.DataPath = DataPath;
|
||
|
irpStack->Parameters.WMI.BufferSize = BufferLength;
|
||
|
irpStack->Parameters.WMI.Buffer = Buffer;
|
||
|
|
||
|
//
|
||
|
// Initialize irp status to STATUS_NOT_SUPPORTED so that we can
|
||
|
// detect the case where no data provider responded to the irp
|
||
|
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
||
|
|
||
|
IoMarkIrpPending(Irp);
|
||
|
Status = IoCallDriver(TargetDeviceObject, Irp);
|
||
|
|
||
|
if (Status == STATUS_PENDING) {
|
||
|
KeWaitForSingleObject( &IrpCompCtx.Event,
|
||
|
Executive,
|
||
|
KernelMode,
|
||
|
FALSE,
|
||
|
(PLARGE_INTEGER) NULL );
|
||
|
Status = Irp->IoStatus.Status;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check if the status code is still STATUS_NOT_SUPPORTED. If this is
|
||
|
// the case then most likely no data provider responded to the irp.
|
||
|
// So we want to change the status code to something more relevant
|
||
|
// to WMI like STATUS_WMI_GUID_NOT_FOUND
|
||
|
if (Status == STATUS_NOT_SUPPORTED)
|
||
|
{
|
||
|
Status = STATUS_WMI_GUID_NOT_FOUND;
|
||
|
Irp->IoStatus.Status = STATUS_WMI_GUID_NOT_FOUND;
|
||
|
}
|
||
|
|
||
|
#if DBG
|
||
|
if (((MinorFunction == IRP_MN_REGINFO) || (MinorFunction == IRP_MN_REGINFO_EX)) &&
|
||
|
(NT_SUCCESS(Status)) &&
|
||
|
(Irp->IoStatus.Information == 0))
|
||
|
{
|
||
|
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: %p completed IRP_MN_REGINFO with size 0 (%p, %x)\n",
|
||
|
DeviceObject, Buffer, BufferLength));
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// If this was a registration request then we need to see if there are
|
||
|
// any PDOs that need to be translated into static instance names.
|
||
|
if (((MinorFunction == IRP_MN_REGINFO) ||
|
||
|
(MinorFunction == IRP_MN_REGINFO_EX)) &&
|
||
|
(NT_SUCCESS(Status)) &&
|
||
|
(Irp->IoStatus.Information > FIELD_OFFSET(WMIREGINFOW,
|
||
|
WmiRegGuid)))
|
||
|
{
|
||
|
WmipTranslatePDOInstanceNames(Irp,
|
||
|
MinorFunction,
|
||
|
BufferLength,
|
||
|
RegEntry);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Dereference regentry which was taken when forwarding the irp
|
||
|
WmipUnreferenceRegEntry(RegEntry);
|
||
|
} else {
|
||
|
//
|
||
|
// There are not enough stack locations to forward this irp.
|
||
|
// We bump the stack count for the WMI device and return
|
||
|
// an error asking to try the irp again.
|
||
|
WmipUnreferenceRegEntry(RegEntry);
|
||
|
WmipDecrementIrpCount(RegEntry);
|
||
|
|
||
|
WmipUpdateDeviceStackSize(DeviceStackSize);
|
||
|
Status = STATUS_WMI_TRY_AGAIN;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Dereference the target device which was the top of the stack to
|
||
|
// which we forwarded the irp.
|
||
|
ObDereferenceObject(TargetDeviceObject);
|
||
|
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
NTSTATUS WmipSendWmiIrp(
|
||
|
UCHAR MinorFunction,
|
||
|
ULONG ProviderId,
|
||
|
PVOID DataPath,
|
||
|
ULONG BufferLength,
|
||
|
PVOID Buffer,
|
||
|
PIO_STATUS_BLOCK Iosb
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine will allocate a new irp and then forward it on as a WMI
|
||
|
irp appropriately. The routine handles the case where the stack size
|
||
|
is too small and will retry the irp.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
See WmipForwardWmiIrp
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NT status code
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PIRP Irp;
|
||
|
PIO_STACK_LOCATION IrpStack;
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
PAGED_CODE();
|
||
|
|
||
|
Irp = NULL;
|
||
|
do
|
||
|
{
|
||
|
Irp = IoAllocateIrp((CCHAR)(WmipServiceDeviceObject->StackSize+1),
|
||
|
FALSE);
|
||
|
|
||
|
if (Irp == NULL)
|
||
|
{
|
||
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
||
|
}
|
||
|
|
||
|
IoSetNextIrpStackLocation(Irp);
|
||
|
IrpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
|
IrpStack->DeviceObject = WmipServiceDeviceObject;
|
||
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
||
|
Irp->AssociatedIrp.SystemBuffer = Buffer;
|
||
|
|
||
|
Status = WmipForwardWmiIrp(
|
||
|
Irp,
|
||
|
MinorFunction,
|
||
|
ProviderId,
|
||
|
DataPath,
|
||
|
BufferLength,
|
||
|
Buffer);
|
||
|
|
||
|
*Iosb = Irp->IoStatus;
|
||
|
|
||
|
IoFreeIrp(Irp);
|
||
|
} while (Status == STATUS_WMI_TRY_AGAIN);
|
||
|
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS WmipTranslateFileHandle(
|
||
|
IN OUT PWMIFHTOINSTANCENAME FhToInstanceName,
|
||
|
IN OUT PULONG OutBufferLen,
|
||
|
IN HANDLE FileHandle,
|
||
|
IN PDEVICE_OBJECT DeviceObject,
|
||
|
IN PWMIGUIDOBJECT GuidObject,
|
||
|
OUT PUNICODE_STRING InstanceNameString
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine will translate a file handle or device object into the
|
||
|
device instance name for the target PDO of the device object
|
||
|
pointed to by the file handle.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
FhToInstanceName passes in the file handle and returns the device
|
||
|
instance name.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NT status code
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PDEVICE_OBJECT PDO;
|
||
|
UNICODE_STRING DeviceInstanceName;
|
||
|
PFILE_OBJECT FileObject;
|
||
|
NTSTATUS Status;
|
||
|
ULONG SizeNeeded;
|
||
|
PWCHAR InstanceName;
|
||
|
ULONG Length;
|
||
|
PWCHAR HandleName;
|
||
|
ULONG HandleNameLen;
|
||
|
PWCHAR BaseName;
|
||
|
ULONG BaseNameLen;
|
||
|
PBGUIDENTRY GuidEntry;
|
||
|
PLIST_ENTRY InstanceSetList;
|
||
|
PBINSTANCESET InstanceSet;
|
||
|
ULONG BaseIndex;
|
||
|
|
||
|
PAGED_CODE();
|
||
|
|
||
|
|
||
|
if (FhToInstanceName != NULL)
|
||
|
{
|
||
|
WmipAssert(FileHandle == NULL);
|
||
|
WmipAssert(GuidObject == NULL);
|
||
|
WmipAssert(InstanceNameString == NULL);
|
||
|
WmipAssert(DeviceObject == NULL);
|
||
|
FileHandle = FhToInstanceName->FileHandle.Handle;
|
||
|
if (FileHandle == NULL)
|
||
|
{
|
||
|
return(STATUS_INVALID_HANDLE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (FileHandle != NULL)
|
||
|
{
|
||
|
//
|
||
|
// Make reference to the file object so it doesn't go away
|
||
|
//
|
||
|
Status = ObReferenceObjectByHandle(FileHandle,
|
||
|
0,
|
||
|
IoFileObjectType,
|
||
|
KernelMode,
|
||
|
&FileObject,
|
||
|
NULL);
|
||
|
} else {
|
||
|
//
|
||
|
// Make regerence to the device object so it doesn't go away
|
||
|
//
|
||
|
Status = ObReferenceObjectByPointer(DeviceObject,
|
||
|
FILE_ALL_ACCESS,
|
||
|
NULL,
|
||
|
KernelMode);
|
||
|
}
|
||
|
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
Status = WmipObjectToPDO(FileObject,
|
||
|
DeviceObject,
|
||
|
&PDO);
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
//
|
||
|
// Map file object to PDO
|
||
|
Status = WmipPDOToDeviceInstanceName(PDO,
|
||
|
&DeviceInstanceName);
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
//
|
||
|
// Now see if we can find an instance name
|
||
|
//
|
||
|
HandleName = DeviceInstanceName.Buffer;
|
||
|
HandleNameLen = DeviceInstanceName.Length / sizeof(WCHAR);
|
||
|
if (FhToInstanceName != NULL)
|
||
|
{
|
||
|
Status = ObReferenceObjectByHandle(FhToInstanceName->KernelHandle.Handle,
|
||
|
WMIGUID_QUERY,
|
||
|
WmipGuidObjectType,
|
||
|
UserMode,
|
||
|
&GuidObject,
|
||
|
NULL);
|
||
|
} else {
|
||
|
Status = ObReferenceObjectByPointer(GuidObject,
|
||
|
WMIGUID_QUERY,
|
||
|
WmipGuidObjectType,
|
||
|
KernelMode);
|
||
|
}
|
||
|
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
Status = STATUS_WMI_INSTANCE_NOT_FOUND;
|
||
|
GuidEntry = GuidObject->GuidEntry;
|
||
|
|
||
|
WmipEnterSMCritSection();
|
||
|
if (GuidEntry->ISCount > 0)
|
||
|
{
|
||
|
InstanceSetList = GuidEntry->ISHead.Flink;
|
||
|
while ((InstanceSetList != &GuidEntry->ISHead) &&
|
||
|
! NT_SUCCESS(Status))
|
||
|
{
|
||
|
InstanceSet = CONTAINING_RECORD(InstanceSetList,
|
||
|
INSTANCESET,
|
||
|
GuidISList);
|
||
|
if (InstanceSet->Flags & IS_INSTANCE_BASENAME)
|
||
|
{
|
||
|
BaseName = InstanceSet->IsBaseName->BaseName;
|
||
|
BaseNameLen = wcslen(BaseName);
|
||
|
|
||
|
//
|
||
|
// If the instance set has a base name
|
||
|
// and the beginning of it matches the
|
||
|
// PnPId and it has only an _ after it
|
||
|
// then we have got a match
|
||
|
//
|
||
|
if ((_wcsnicmp(BaseName,
|
||
|
HandleName,
|
||
|
HandleNameLen) == 0) &&
|
||
|
(BaseNameLen == (HandleNameLen+1)) &&
|
||
|
(BaseName[BaseNameLen-1] == L'_'))
|
||
|
{
|
||
|
BaseIndex = InstanceSet->IsBaseName->BaseIndex;
|
||
|
Status = STATUS_SUCCESS;
|
||
|
}
|
||
|
}
|
||
|
InstanceSetList = InstanceSetList->Flink;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
WmipLeaveSMCritSection();
|
||
|
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
if (FhToInstanceName != NULL)
|
||
|
{
|
||
|
FhToInstanceName->BaseIndex = BaseIndex;
|
||
|
SizeNeeded = DeviceInstanceName.Length + 2 * sizeof(WCHAR) +
|
||
|
FIELD_OFFSET(WMIFHTOINSTANCENAME,
|
||
|
InstanceNames);
|
||
|
if (*OutBufferLen >= SizeNeeded)
|
||
|
{
|
||
|
InstanceName = &FhToInstanceName->InstanceNames[0];
|
||
|
Length = DeviceInstanceName.Length;
|
||
|
|
||
|
FhToInstanceName->InstanceNameLength = (USHORT)(Length + 2 * sizeof(WCHAR));
|
||
|
RtlCopyMemory(InstanceName,
|
||
|
DeviceInstanceName.Buffer,
|
||
|
DeviceInstanceName.Length);
|
||
|
|
||
|
//
|
||
|
// Double NUL terminate string
|
||
|
//
|
||
|
Length /= 2;
|
||
|
InstanceName[Length++] = UNICODE_NULL;
|
||
|
InstanceName[Length] = UNICODE_NULL;
|
||
|
|
||
|
*OutBufferLen = SizeNeeded;
|
||
|
} else if (*OutBufferLen >= sizeof(ULONG)) {
|
||
|
FhToInstanceName->SizeNeeded = SizeNeeded;
|
||
|
*OutBufferLen = sizeof(ULONG);
|
||
|
} else {
|
||
|
Status = STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
} else {
|
||
|
InstanceName = ExAllocatePoolWithTag(PagedPool,
|
||
|
DeviceInstanceName.Length + 32,
|
||
|
WmipInstanceNameTag);
|
||
|
if (InstanceName != NULL)
|
||
|
{
|
||
|
Length = swprintf(InstanceName,
|
||
|
L"%ws_%d",
|
||
|
DeviceInstanceName.Buffer,
|
||
|
BaseIndex);
|
||
|
InstanceNameString->Buffer = InstanceName;
|
||
|
InstanceNameString->Length = (USHORT)Length * sizeof(WCHAR);
|
||
|
InstanceNameString->MaximumLength = (USHORT)Length * sizeof(WCHAR);
|
||
|
} else {
|
||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ObDereferenceObject(GuidObject);
|
||
|
}
|
||
|
RtlFreeUnicodeString(&DeviceInstanceName);
|
||
|
}
|
||
|
ObDereferenceObject(PDO);
|
||
|
}
|
||
|
|
||
|
if (FileHandle != NULL)
|
||
|
{
|
||
|
ObDereferenceObject(FileObject);
|
||
|
} else {
|
||
|
ObDereferenceObject(DeviceObject);
|
||
|
}
|
||
|
}
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
#ifndef MEMPHIS
|
||
|
BOOLEAN
|
||
|
WmipFastIoDeviceControl(
|
||
|
IN PFILE_OBJECT FileObject,
|
||
|
IN BOOLEAN Wait,
|
||
|
IN PVOID InputBuffer OPTIONAL,
|
||
|
IN ULONG InputBufferLength,
|
||
|
OUT PVOID OutputBuffer OPTIONAL,
|
||
|
IN ULONG OutputBufferLength,
|
||
|
IN ULONG IoControlCode,
|
||
|
OUT PIO_STATUS_BLOCK IoStatus,
|
||
|
IN struct _DEVICE_OBJECT *DeviceObject
|
||
|
)
|
||
|
{
|
||
|
PAGED_CODE();
|
||
|
|
||
|
UNREFERENCED_PARAMETER(FileObject);
|
||
|
UNREFERENCED_PARAMETER(Wait);
|
||
|
UNREFERENCED_PARAMETER(OutputBuffer);
|
||
|
UNREFERENCED_PARAMETER(OutputBufferLength);
|
||
|
UNREFERENCED_PARAMETER(DeviceObject);
|
||
|
|
||
|
if (IoControlCode == IOCTL_WMI_TRACE_EVENT) {
|
||
|
if (InputBufferLength < sizeof(EVENT_TRACE_HEADER))
|
||
|
return FALSE;
|
||
|
|
||
|
IoStatus->Status = WmiTraceEvent( InputBuffer, KeGetPreviousMode() );
|
||
|
return TRUE;
|
||
|
} else if (IoControlCode == IOCTL_WMI_TRACE_MESSAGE) {
|
||
|
if (InputBufferLength < sizeof(MESSAGE_TRACE_USER))
|
||
|
return FALSE;
|
||
|
|
||
|
IoStatus->Status = WmiTraceUserMessage( InputBuffer, InputBufferLength );
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
NTSTATUS WmipProbeWnodeWorker(
|
||
|
PWNODE_HEADER WnodeHeader,
|
||
|
ULONG MinWnodeSize,
|
||
|
ULONG InstanceNameOffset,
|
||
|
ULONG DataBlockOffset,
|
||
|
ULONG DataBlockSize,
|
||
|
ULONG InBufferLen,
|
||
|
ULONG OutBufferLen,
|
||
|
BOOLEAN CheckOutBound,
|
||
|
BOOLEAN CheckInBound
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Probe the incoming Wnode to ensure that any offsets in the
|
||
|
header point to memory that is valid within the buffer. Also validate
|
||
|
that the Wnode is properly formed.
|
||
|
|
||
|
This routine assumes that the input and output buffers has been
|
||
|
probed enough to determine that it is at least as large as
|
||
|
MinWnodeSize and MinWnodeSize must be at least as large as
|
||
|
sizeof(WNODE_HEADER)
|
||
|
|
||
|
WNODE Rules:
|
||
|
|
||
|
9. For outbound data WnodeDataBlockOffset != 0
|
||
|
5. For inbound Wnode->DataBlockOffset must be 0 (implying no data) or
|
||
|
Wnode->DataBlockOffset must be <= incoming buffer size and >=
|
||
|
sizeof(WNODE_SINGLE_INSTANCE), that is
|
||
|
the data block must start in the incoming buffer, but after the
|
||
|
WNODE_SINGLE_INSTANCE header.
|
||
|
6. Wnode and Wnode->DataBlockOffset must be aligned on an 8 byte boundry.
|
||
|
7. For inbound data (SetSingleInstance) (Wnode->DataBlockOffset +
|
||
|
Wnode->DataBlockSize) < incoming buffer length. That is the entire
|
||
|
data block must fit within the incoming buffer.
|
||
|
8. For outbound data (QuerySingleInstance) Wnode->DataBlockOffset
|
||
|
must be <= outgoing buffer length. That is the start of the outgoing
|
||
|
data block must fit within the outgoing data buffer. Note that it is
|
||
|
the provider's responsibility to determine if there will be enough
|
||
|
space in the outgoing buffer to write the returned data.
|
||
|
|
||
|
10. Wnode->OffsetInstanceNames must be aligned on a 2 byte boundry
|
||
|
11. Wnode->OffsetInstanceNames must be <= (incoming buffer size) +
|
||
|
sizeof(USHORT), that is it must start within the incoming buffer and
|
||
|
the USHORT that specifies the length must be within the incoming
|
||
|
buffer.
|
||
|
12. The entire instance name string must fit with the incoming buffer
|
||
|
13. For outbound data (QuerySingleInstance) the entire instance name
|
||
|
must start and fit within the output buffer.
|
||
|
14. Wnode->DataBlockOffset must be placed after any instance name and
|
||
|
not overlap the instance name.
|
||
|
|
||
|
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
WnodeHeader - pointer to WNODE to be probed
|
||
|
|
||
|
InBufferLen - Size of the incoming buffer
|
||
|
|
||
|
OutBufferLen - Size of the outgoing buffer
|
||
|
|
||
|
MinWnodeSize - minimum size that the WNODE can be
|
||
|
|
||
|
InstanceNameOffset - Offset within WNODE to instance name
|
||
|
|
||
|
DataBlockOffset - Offset within WNODE to data block
|
||
|
|
||
|
DataBlockSize - Size of data block
|
||
|
|
||
|
CheckOutBound - If TRUE, WNODE needs to be validated for provider to
|
||
|
return data.
|
||
|
|
||
|
CheckInBound - If TRUE WNODE needs to be validated for provider to
|
||
|
receive data
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NT status code
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
PWCHAR InstanceNamePtr;
|
||
|
|
||
|
PAGED_CODE();
|
||
|
|
||
|
if (InstanceNameOffset != 0)
|
||
|
{
|
||
|
#if DBG
|
||
|
//
|
||
|
// Validate instance name begins beyond WNODE header
|
||
|
if (InstanceNameOffset < MinWnodeSize)
|
||
|
{
|
||
|
return(STATUS_UNSUCCESSFUL);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Validate InstanceName is aligned properly. This is left
|
||
|
// in the free build since alphas may have alignment requiremnts
|
||
|
// in handling USHORTs and WCHARs
|
||
|
|
||
|
//
|
||
|
// Validate that USHORT holding instance name length is within
|
||
|
// WNODE
|
||
|
if (( ! WmipIsAligned(InstanceNameOffset, 2)) ||
|
||
|
(InstanceNameOffset > InBufferLen - sizeof(USHORT)) )
|
||
|
{
|
||
|
return(STATUS_UNSUCCESSFUL);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Validate Dynamic Instance Name text is fully within
|
||
|
// input buffer and output buffer for outbound WNODEs
|
||
|
InstanceNamePtr = (PWCHAR)OffsetToPtr(WnodeHeader,
|
||
|
InstanceNameOffset);
|
||
|
InstanceNameOffset += sizeof(USHORT) + *InstanceNamePtr;
|
||
|
if ( (InstanceNameOffset > InBufferLen) ||
|
||
|
( (CheckOutBound) && (InstanceNameOffset > OutBufferLen)) )
|
||
|
{
|
||
|
return(STATUS_UNSUCCESSFUL);
|
||
|
}
|
||
|
#if DBG
|
||
|
//
|
||
|
// If data block is specified then it must be placed after the
|
||
|
// end of the instance name
|
||
|
if ((DataBlockOffset != 0) &&
|
||
|
(DataBlockOffset < InstanceNameOffset))
|
||
|
{
|
||
|
return(STATUS_UNSUCCESSFUL);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#if DBG
|
||
|
//
|
||
|
// Ensure data block offset is placed after the WNODE header
|
||
|
// header
|
||
|
if ((DataBlockOffset != 0) &&
|
||
|
(DataBlockOffset < MinWnodeSize))
|
||
|
{
|
||
|
return(STATUS_UNSUCCESSFUL);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Ensure data block is aligned properly
|
||
|
if (! WmipIsAligned(DataBlockOffset, 8))
|
||
|
{
|
||
|
return(STATUS_UNSUCCESSFUL);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// For incoming WNODE, make sure the data block
|
||
|
// does not extend beyond the input buffer.
|
||
|
if ((CheckInBound) &&
|
||
|
(DataBlockOffset != 0) &&
|
||
|
( (DataBlockSize > InBufferLen) ||
|
||
|
(DataBlockOffset > InBufferLen - DataBlockSize) ) )
|
||
|
{
|
||
|
return(STATUS_UNSUCCESSFUL);
|
||
|
}
|
||
|
|
||
|
if (CheckOutBound)
|
||
|
{
|
||
|
//
|
||
|
// For outgoing WNODE make sure there is
|
||
|
// enough room to write the WNODE header
|
||
|
|
||
|
//
|
||
|
// For outgoing WNODE make sure the data block
|
||
|
// offset is within the bounds of the output buffer
|
||
|
if ( (OutBufferLen < MinWnodeSize) ||
|
||
|
(DataBlockOffset > OutBufferLen) )
|
||
|
{
|
||
|
return(STATUS_UNSUCCESSFUL);
|
||
|
}
|
||
|
|
||
|
#if DBG
|
||
|
//
|
||
|
// Make sure that the data block offset is specified so provider
|
||
|
// can know where to write data
|
||
|
if (DataBlockOffset == 0)
|
||
|
{
|
||
|
return(STATUS_UNSUCCESSFUL);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
return(Status);
|
||
|
}
|
||
|
NTSTATUS WmipProbeWnodeAllData(
|
||
|
PWNODE_ALL_DATA Wnode,
|
||
|
ULONG InBufferLen,
|
||
|
ULONG OutBufferLen
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Probe the incoming WNODE_ALL_DATA to ensure that any offsets in the
|
||
|
header point to memory that is valid within the buffer. Also validate
|
||
|
that the WNODE_ALL_DATA is properly formed.
|
||
|
|
||
|
This routine MUST succeed before any fields in the WNODE_ALL_DATA can be
|
||
|
used by any kernel components when passed in from user mode. Note that
|
||
|
we can trust that the input and output buffer are properly sized since
|
||
|
the WMI IOCTLs are METHOD_BUFFERED and the IO manager does that for us.
|
||
|
|
||
|
|
||
|
WNODE_ALL_DATA_RULES:
|
||
|
|
||
|
1. Wnode is aligned on a 8 byte boundry
|
||
|
2. The incoming buffer must be at least as large as sizeof(WNODE_HEADER)
|
||
|
3. The outgoing buffer must be at least as large as sizeof(WNODE_ALL_DATA)
|
||
|
5. WnodeHeader->BufferSize must equal incoming bufffer size
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Wnode - WNODE_ALL_DATA to be validated
|
||
|
|
||
|
InBufferLen - Size of the incoming buffer
|
||
|
|
||
|
OutBufferLen - Size of the outgoing buffer
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NT status code
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
PWNODE_HEADER WnodeHeader = (PWNODE_HEADER)Wnode;
|
||
|
|
||
|
PAGED_CODE();
|
||
|
|
||
|
#if DBG
|
||
|
//
|
||
|
// Make sure WNODE is on a 8 byte boundry
|
||
|
// if (! WmipIsAligned((PUCHAR)Wnode, 8))
|
||
|
// {
|
||
|
// return(STATUS_UNSUCCESSFUL);
|
||
|
// }
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Make sure that enough of the WNODE_ALL_DATA was passed so that we
|
||
|
// can look at it and the drivers can fill it in
|
||
|
|
||
|
if ((InBufferLen < sizeof(WNODE_HEADER)) ||
|
||
|
(OutBufferLen < sizeof(WNODE_ALL_DATA)))
|
||
|
{
|
||
|
return(STATUS_UNSUCCESSFUL);
|
||
|
}
|
||
|
|
||
|
Status = WmipValidateWnodeHeader(WnodeHeader,
|
||
|
InBufferLen,
|
||
|
sizeof(WNODE_HEADER),
|
||
|
WNODE_FLAG_ALL_DATA,
|
||
|
0xffffff7e);
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
NTSTATUS WmipProbeWnodeSingleInstance(
|
||
|
PWNODE_SINGLE_INSTANCE Wnode,
|
||
|
ULONG InBufferLen,
|
||
|
ULONG OutBufferLen,
|
||
|
BOOLEAN OutBound
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Probe the incoming WNODE_SINGLE_INSTANCE to ensure that any offsets in the
|
||
|
header point to memory that is valid within the buffer. Also validate
|
||
|
that the WNODE_SINGLE_INSTANCE is properly formed.
|
||
|
|
||
|
This routine MUST succeed before any fields in the WNODE_SINGLE_INSTANCE
|
||
|
can be used by any kernel components when passed in from user mode.
|
||
|
Note that we can trust that the input and output buffer are properly
|
||
|
sized since the WMI IOCTLs are METHOD_BUFFERED and the IO manager does
|
||
|
that for us.
|
||
|
|
||
|
WNODE_SINGLE_INSTANCE Rules:
|
||
|
|
||
|
1. The incoming buffer must be at least as large as
|
||
|
sizeof(WNODE_SINGLE_INSTANCE)
|
||
|
2. The outgoing buffer must be at least as large as
|
||
|
sizeof(WNODE_SINGLE_INSTANCE)
|
||
|
3. WnodeHeader->ProviderId must be non null, Actual value validated when
|
||
|
irp is forwarded.
|
||
|
4. WnodeHeader->BufferSize must equal incoming bufffer size
|
||
|
5. Wnode->DataBlockOffset must be 0 (implying no data) or
|
||
|
Wnode->DataBlockOffset must be <= incoming buffer size and >=
|
||
|
sizeof(WNODE_SINGLE_INSTANCE), that is
|
||
|
the data block must start in the incoming buffer, but after the
|
||
|
WNODE_SINGLE_INSTANCE header.
|
||
|
6. Wnode and Wnode->DataBlockOffset must be aligned on an 8 byte boundry.
|
||
|
7. For inbound data (SetSingleInstance) (Wnode->DataBlockOffset +
|
||
|
Wnode->DataBlockSize) <= incoming buffer length. That is the entire
|
||
|
data block must fit within the incoming buffer.
|
||
|
8. For outbound data (QuerySingleInstance) Wnode->DataBlockOffset
|
||
|
must be <= outgoing buffer length. That is the start of the outgoing
|
||
|
data block must fit within the outgoing data buffer. Note that it is
|
||
|
the provider's responsibility to determine if there will be enough
|
||
|
space in the outgoing buffer to write the returned data.
|
||
|
9. For outbound data (QuerySingleInstance) WnodeDataBlockOffset != 0
|
||
|
|
||
|
10. Wnode->OffsetInstanceNames must be aligned on a 2 byte boundry
|
||
|
11. Wnode->OffsetInstanceNames + sizeof(USHORT) must be <= incoming
|
||
|
buffer size, that is it must start within the incoming buffer and
|
||
|
the USHORT that specifies the length must be within the incoming
|
||
|
buffer.
|
||
|
12. The entire instance name string must fit with the incoming buffer
|
||
|
13. For outbound data (QuerySingleInstance) the entire instance name
|
||
|
must start and fit within the output buffer.
|
||
|
14. Wnode->DataBlockOffset must be placed after any instance name and
|
||
|
not overlap the instance name.
|
||
|
|
||
|
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Wnode - WNODE_SINGLE_INSTANCE to be validated
|
||
|
|
||
|
InBufferLen - Size of the incoming buffer
|
||
|
|
||
|
OutBufferLen - Size of the outgoing buffer
|
||
|
|
||
|
OutBound - If FALSE, WNODE_SINGLE_INSTANCE has inbound data that must be
|
||
|
validated to be within the input buffer. If FALSE,
|
||
|
WNODE_SINGLE_INSTANCE is expected to be filled with data
|
||
|
by the driver so insure that data buffer is validated to
|
||
|
be within the output buffer.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NT status code
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PWNODE_HEADER WnodeHeader = (PWNODE_HEADER)Wnode;
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
PAGED_CODE();
|
||
|
|
||
|
//
|
||
|
// Make sure WNODE is on a 8 byte boundry
|
||
|
#if DBG
|
||
|
// if (! WmipIsAligned((PUCHAR)Wnode, 8))
|
||
|
// {
|
||
|
// return(STATUS_UNSUCCESSFUL);
|
||
|
// }
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Make sure that enough of the WNODE_SINGLE_INSTANCE was passed
|
||
|
// so that we can look at it
|
||
|
if ((InBufferLen < FIELD_OFFSET(WNODE_SINGLE_INSTANCE, VariableData)) ||
|
||
|
( (OutBound) && (OutBufferLen < FIELD_OFFSET(WNODE_SINGLE_INSTANCE,
|
||
|
VariableData))))
|
||
|
{
|
||
|
return(STATUS_UNSUCCESSFUL);
|
||
|
}
|
||
|
|
||
|
|
||
|
Status = WmipProbeWnodeWorker(WnodeHeader,
|
||
|
FIELD_OFFSET(WNODE_SINGLE_INSTANCE,
|
||
|
VariableData),
|
||
|
Wnode->OffsetInstanceName,
|
||
|
Wnode->DataBlockOffset,
|
||
|
Wnode->SizeDataBlock,
|
||
|
InBufferLen,
|
||
|
OutBufferLen,
|
||
|
OutBound,
|
||
|
(BOOLEAN)(! OutBound));
|
||
|
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
Status = WmipValidateWnodeHeader(WnodeHeader,
|
||
|
InBufferLen,
|
||
|
FIELD_OFFSET(WNODE_SINGLE_INSTANCE,
|
||
|
VariableData),
|
||
|
WNODE_FLAG_SINGLE_INSTANCE,
|
||
|
0xffffff7d);
|
||
|
}
|
||
|
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
NTSTATUS WmipProbeWnodeSingleItem(
|
||
|
PWNODE_SINGLE_ITEM Wnode,
|
||
|
ULONG InBufferLen
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Probe the incoming WNODE_SINGLE_ITEM to ensure that any offsets in the
|
||
|
header point to memory that is valid within the buffer. Also validate
|
||
|
that the WNODE_SINGLE_ITEM is properly formed.
|
||
|
|
||
|
This routine MUST succeed before any fields in the WNODE_SINGLE_INSTANCE
|
||
|
can be used by any kernel components when passed in from user mode.
|
||
|
Note that we can trust that the input and output buffer are properly
|
||
|
sized since the WMI IOCTLs are METHOD_BUFFERED and the IO manager does
|
||
|
that for us.
|
||
|
|
||
|
WNODE_SINGLE_ITEM rules:
|
||
|
|
||
|
1. The incoming buffer must be at least as large as
|
||
|
sizeof(WNODE_SINGLE_ITEM)
|
||
|
2. The outgoing buffer must be at least as large as
|
||
|
sizeof(WNODE_SINGLE_ITEM)
|
||
|
3. WnodeHeader->ProviderId must be non null, Actual value validated when
|
||
|
irp is forwarded.
|
||
|
4. WnodeHeader->BufferSize must equal incoming bufffer size
|
||
|
5. Wnode->DataBlockOffset must be 0 (implying no data) or
|
||
|
Wnode->DataBlockOffset must be <= incoming buffer size and >=
|
||
|
sizeof(WNODE_SINGLE_ITEM), that is
|
||
|
the data block must start in the incoming buffer, but after the
|
||
|
WNODE_SINGLE_ITEM header.
|
||
|
6. Wnode and Wnode->DataBlockOffset must be aligned on an 8 byte boundry.
|
||
|
7. (Wnode->DataBlockOffset + Wnode->SizeDataItem) <
|
||
|
incoming buffer length. That is the entire
|
||
|
data block must fit within the incoming buffer.
|
||
|
8. Wnode->DataItemId must not be 0
|
||
|
|
||
|
9. Wnode->OffsetInstanceNames must be aligned on a 2 byte boundry
|
||
|
10. Wnode->OffsetInstanceNames must be <= (incoming buffer size) +
|
||
|
sizeof(USHORT), that is it must start within the incoming buffer and
|
||
|
the USHORT that specifies the length must be within the incoming
|
||
|
buffer.
|
||
|
11. The entire instance name string must fit with the incoming buffer
|
||
|
12. Wnode->DataBlockOffset must be placed after any instance name and
|
||
|
not overlap the instance name.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Wnode - WNODE_SINGLE_ITEM to be validated
|
||
|
|
||
|
InBufferLen - Size of the incoming buffer
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NT status code
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PWNODE_HEADER WnodeHeader = (PWNODE_HEADER)Wnode;
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
PAGED_CODE();
|
||
|
|
||
|
//
|
||
|
// Make sure WNODE is on a 8 byte boundry
|
||
|
#if DBG
|
||
|
// if (! WmipIsAligned((PUCHAR)Wnode, 8))
|
||
|
// {
|
||
|
// return(STATUS_UNSUCCESSFUL);
|
||
|
// }
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Make sure that enough of the WNODE_SINGLE_ITEM was passed
|
||
|
// so that we can look at it
|
||
|
if (InBufferLen < FIELD_OFFSET(WNODE_SINGLE_ITEM, VariableData))
|
||
|
{
|
||
|
return(STATUS_UNSUCCESSFUL);
|
||
|
}
|
||
|
|
||
|
#if DBG
|
||
|
// if (Wnode->ItemId == 0)
|
||
|
// {
|
||
|
// return(STATUS_UNSUCCESSFUL);
|
||
|
// }
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// We don't use sizeof(WNODE_SINGLE_ITEM), but rather use the offset
|
||
|
// to the variable data since in the case of WNODE_SINGLE_ITEM they are
|
||
|
// different. The C compiler will round up the packing to 8 bytes so
|
||
|
// the former is 48 and the later is 44.
|
||
|
Status = WmipProbeWnodeWorker(WnodeHeader,
|
||
|
(ULONG)((ULONG_PTR)(&((PWNODE_SINGLE_ITEM)0)->VariableData)),
|
||
|
Wnode->OffsetInstanceName,
|
||
|
Wnode->DataBlockOffset,
|
||
|
Wnode->SizeDataItem,
|
||
|
InBufferLen,
|
||
|
0,
|
||
|
FALSE,
|
||
|
TRUE);
|
||
|
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
Status = WmipValidateWnodeHeader(WnodeHeader,
|
||
|
InBufferLen,
|
||
|
FIELD_OFFSET(WNODE_SINGLE_ITEM,
|
||
|
VariableData),
|
||
|
WNODE_FLAG_SINGLE_ITEM,
|
||
|
0xffffff7b);
|
||
|
}
|
||
|
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS WmipProbeWnodeMethodItem(
|
||
|
PWNODE_METHOD_ITEM Wnode,
|
||
|
ULONG InBufferLen,
|
||
|
ULONG OutBufferLen
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Probe the incoming WNODE_METHOD_ITEM to ensure that any offsets in the
|
||
|
header point to memory that is valid within the buffer. Also validate
|
||
|
that the WNODE_METHOD_ITEM is properly formed.
|
||
|
|
||
|
This routine MUST succeed before any fields in the WNODE_METHOD_INSTANCE
|
||
|
can be used by any kernel components when passed in from user mode.
|
||
|
Note that we can trust that the input and output buffer are properly
|
||
|
sized since the WMI IOCTLs are METHOD_BUFFERED and the IO manager does
|
||
|
that for us.
|
||
|
|
||
|
WNODE_METHOD_ITEM Rules:
|
||
|
|
||
|
1. The incoming buffer must be at least as large as
|
||
|
sizeof(WNODE_METHOD_ITEM)
|
||
|
2. The outgoing buffer must be at least as large as
|
||
|
sizeof(WNODE_METHOD_ITEM)
|
||
|
3. WnodeHeader->ProviderId must be non null, Actual value validated when
|
||
|
irp is forwarded and Wnode->MethodId must not be 0
|
||
|
4. WnodeHeader->BufferSize must equal incoming bufffer size
|
||
|
5. Wnode->DataBlockOffset must be 0 (implying no data) or
|
||
|
Wnode->DataBlockOffset must be <= incoming buffer size and >=
|
||
|
sizeof(WNODE_METHOD_ITEM), that is
|
||
|
the data block must start in the incoming buffer, but after the
|
||
|
WNODE_METHOD_ITEM header.
|
||
|
6. Wnode and Wnode->DataBlockOffset must be aligned on an 8 byte boundry.
|
||
|
7. For inbound data (Wnode->DataBlockOffset +
|
||
|
Wnode->DataBlockSize) < incoming buffer length. That is the entire
|
||
|
data block must fit within the incoming buffer.
|
||
|
8. For outbound data Wnode->DataBlockOffset
|
||
|
must be <= outgoing buffer length. That is the start of the outgoing
|
||
|
data block must fit within the outgoing data buffer. Note that it is
|
||
|
the provider's responsibility to determine if there will be enough
|
||
|
space in the outgoing buffer to write the returned data.
|
||
|
9. WnodeDataBlockOffset != 0
|
||
|
|
||
|
10. Wnode->OffsetInstanceNames must be aligned on a 2 byte boundry
|
||
|
11. Wnode->OffsetInstanceNames must be <= (incoming buffer size) +
|
||
|
sizeof(USHORT), that is it must start within the incoming buffer and
|
||
|
the USHORT that specifies the length must be within the incoming
|
||
|
buffer.
|
||
|
12. The entire instance name string must fit with the incoming buffer
|
||
|
13. For outbound data the entire instance name
|
||
|
must start and fit within the output buffer.
|
||
|
14. Wnode->DataBlockOffset must be placed after any instance name and
|
||
|
not overlap the instance name.
|
||
|
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Wnode - WNODE_METHOD_ITEM to be validated
|
||
|
|
||
|
InBufferLen - Size of the incoming buffer
|
||
|
|
||
|
OutBufferLen - Size of the Output buffer
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NT status code
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PWNODE_HEADER WnodeHeader = (PWNODE_HEADER)Wnode;
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
PAGED_CODE();
|
||
|
|
||
|
//
|
||
|
// Make sure WNODE is on a 8 byte boundry
|
||
|
#if DBG
|
||
|
// if (! WmipIsAligned((PUCHAR)Wnode, 8))
|
||
|
// {
|
||
|
// return(STATUS_UNSUCCESSFUL);
|
||
|
// }
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Make sure that enough of the WNODE_METHOD_ITEM was passed
|
||
|
// so that we can look at it
|
||
|
if (InBufferLen < FIELD_OFFSET(WNODE_METHOD_ITEM, VariableData))
|
||
|
{
|
||
|
return(STATUS_UNSUCCESSFUL);
|
||
|
}
|
||
|
|
||
|
#if DBG
|
||
|
if (Wnode->MethodId == 0)
|
||
|
{
|
||
|
return(STATUS_UNSUCCESSFUL);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
Status = WmipProbeWnodeWorker(WnodeHeader,
|
||
|
(ULONG)((ULONG_PTR)(&((PWNODE_METHOD_ITEM)0)->VariableData)),
|
||
|
Wnode->OffsetInstanceName,
|
||
|
Wnode->DataBlockOffset,
|
||
|
Wnode->SizeDataBlock,
|
||
|
InBufferLen,
|
||
|
OutBufferLen,
|
||
|
TRUE,
|
||
|
TRUE);
|
||
|
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
Status = WmipValidateWnodeHeader(WnodeHeader,
|
||
|
InBufferLen,
|
||
|
FIELD_OFFSET(WNODE_METHOD_ITEM,
|
||
|
VariableData),
|
||
|
WNODE_FLAG_METHOD_ITEM,
|
||
|
0xffff7f7f);
|
||
|
}
|
||
|
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
NTSTATUS WmipProbeAndCaptureGuidObjectAttributes(
|
||
|
POBJECT_ATTRIBUTES CapturedObjectAttributes,
|
||
|
PUNICODE_STRING CapturedGuidString,
|
||
|
PWCHAR CapturedGuidBuffer,
|
||
|
POBJECT_ATTRIBUTES ObjectAttributes
|
||
|
)
|
||
|
{
|
||
|
PAGED_CODE();
|
||
|
|
||
|
#if defined(_WIN64)
|
||
|
if (IoIs32bitProcess(NULL))
|
||
|
{
|
||
|
POBJECT_ATTRIBUTES32 ObjectAttributes32;
|
||
|
PUNICODE_STRING32 GuidString32;
|
||
|
|
||
|
//
|
||
|
// Probe the embedded object attributes and string name
|
||
|
//
|
||
|
ObjectAttributes32 = (POBJECT_ATTRIBUTES32)ObjectAttributes;
|
||
|
|
||
|
try
|
||
|
{
|
||
|
//
|
||
|
// Probe, capture and validate the OBJECT_ATTRIBUTES
|
||
|
//
|
||
|
ProbeForRead( ObjectAttributes32,
|
||
|
sizeof(OBJECT_ATTRIBUTES32),
|
||
|
sizeof(ULONG) );
|
||
|
|
||
|
CapturedObjectAttributes->Length = sizeof(OBJECT_ATTRIBUTES);
|
||
|
CapturedObjectAttributes->RootDirectory = UlongToPtr(ObjectAttributes32->RootDirectory);
|
||
|
CapturedObjectAttributes->Attributes = ObjectAttributes32->Attributes;
|
||
|
CapturedObjectAttributes->SecurityDescriptor = UlongToPtr(ObjectAttributes32->SecurityDescriptor);
|
||
|
CapturedObjectAttributes->SecurityQualityOfService = UlongToPtr(ObjectAttributes32->SecurityQualityOfService);
|
||
|
|
||
|
//
|
||
|
// Now probe and validate the guid nane string passed
|
||
|
//
|
||
|
GuidString32 = UlongToPtr(ObjectAttributes32->ObjectName);
|
||
|
ProbeForRead(GuidString32,
|
||
|
sizeof(UNICODE_STRING32),
|
||
|
sizeof(ULONG));
|
||
|
|
||
|
CapturedGuidString->Length = GuidString32->Length;
|
||
|
CapturedGuidString->MaximumLength = GuidString32->MaximumLength;
|
||
|
CapturedGuidString->Buffer = UlongToPtr(GuidString32->Buffer);
|
||
|
|
||
|
if (CapturedGuidString->Length != (WmiGuidObjectNameLength * sizeof(WCHAR)))
|
||
|
{
|
||
|
return(STATUS_INVALID_PARAMETER);
|
||
|
}
|
||
|
|
||
|
ProbeForRead(CapturedGuidString->Buffer,
|
||
|
CapturedGuidString->Length,
|
||
|
sizeof(UCHAR));
|
||
|
|
||
|
RtlCopyMemory(CapturedGuidBuffer,
|
||
|
CapturedGuidString->Buffer,
|
||
|
WmiGuidObjectNameLength * sizeof(WCHAR));
|
||
|
|
||
|
CapturedGuidBuffer[WmiGuidObjectNameLength] = UNICODE_NULL;
|
||
|
CapturedGuidString->Buffer = CapturedGuidBuffer;
|
||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
return(GetExceptionCode());
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
PUNICODE_STRING GuidString;
|
||
|
|
||
|
//
|
||
|
// Probe the embedded object attributes and string name
|
||
|
//
|
||
|
try
|
||
|
{
|
||
|
//
|
||
|
// Probe, capture and validate the OBJECT_ATTRIBUTES
|
||
|
//
|
||
|
*CapturedObjectAttributes = ProbeAndReadStructure( ObjectAttributes,
|
||
|
OBJECT_ATTRIBUTES);
|
||
|
|
||
|
//
|
||
|
// Now probe and validate the guid nane string passed
|
||
|
//
|
||
|
GuidString = CapturedObjectAttributes->ObjectName;
|
||
|
*CapturedGuidString = ProbeAndReadUnicodeString(GuidString);
|
||
|
|
||
|
if (CapturedGuidString->Length != (WmiGuidObjectNameLength * sizeof(WCHAR)))
|
||
|
{
|
||
|
return(STATUS_INVALID_PARAMETER);
|
||
|
}
|
||
|
|
||
|
ProbeForRead(CapturedGuidString->Buffer,
|
||
|
CapturedGuidString->Length,
|
||
|
sizeof(UCHAR));
|
||
|
|
||
|
RtlCopyMemory(CapturedGuidBuffer,
|
||
|
CapturedGuidString->Buffer,
|
||
|
WmiGuidObjectNameLength * sizeof(WCHAR));
|
||
|
|
||
|
CapturedGuidBuffer[WmiGuidObjectNameLength] = UNICODE_NULL;
|
||
|
CapturedGuidString->Buffer = CapturedGuidBuffer;
|
||
|
|
||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
return(GetExceptionCode());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CapturedObjectAttributes->ObjectName = CapturedGuidString;
|
||
|
|
||
|
return(STATUS_SUCCESS);
|
||
|
}
|
||
|
|
||
|
NTSTATUS WmipProbeWmiOpenGuidBlock(
|
||
|
POBJECT_ATTRIBUTES CapturedObjectAttributes,
|
||
|
PUNICODE_STRING CapturedGuidString,
|
||
|
PWCHAR CapturedGuidBuffer,
|
||
|
PULONG DesiredAccess,
|
||
|
PWMIOPENGUIDBLOCK InGuidBlock,
|
||
|
ULONG InBufferLen,
|
||
|
ULONG OutBufferLen
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
POBJECT_ATTRIBUTES ObjectAttributes;
|
||
|
|
||
|
PAGED_CODE();
|
||
|
|
||
|
#if defined(_WIN64)
|
||
|
if (IoIs32bitProcess(NULL))
|
||
|
{
|
||
|
PWMIOPENGUIDBLOCK32 InGuidBlock32;
|
||
|
|
||
|
if ((InBufferLen != sizeof(WMIOPENGUIDBLOCK32)) ||
|
||
|
(OutBufferLen != sizeof(WMIOPENGUIDBLOCK32)))
|
||
|
{
|
||
|
return(STATUS_UNSUCCESSFUL);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Probe the embedded object attributes and string name
|
||
|
//
|
||
|
InGuidBlock32 = (PWMIOPENGUIDBLOCK32)InGuidBlock;
|
||
|
ObjectAttributes = ULongToPtr(InGuidBlock32->ObjectAttributes);
|
||
|
*DesiredAccess = InGuidBlock32->DesiredAccess;
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
//
|
||
|
// Ensure the input and output buffer sizes are correct
|
||
|
//
|
||
|
if ((InBufferLen != sizeof(WMIOPENGUIDBLOCK)) ||
|
||
|
(OutBufferLen != sizeof(WMIOPENGUIDBLOCK)))
|
||
|
{
|
||
|
return(STATUS_UNSUCCESSFUL);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Probe the embedded object attributes and string name
|
||
|
//
|
||
|
ObjectAttributes = InGuidBlock->ObjectAttributes;
|
||
|
*DesiredAccess = InGuidBlock->DesiredAccess;
|
||
|
}
|
||
|
|
||
|
Status = WmipProbeAndCaptureGuidObjectAttributes(CapturedObjectAttributes,
|
||
|
CapturedGuidString,
|
||
|
CapturedGuidBuffer,
|
||
|
ObjectAttributes);
|
||
|
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
if ((CapturedObjectAttributes->RootDirectory != NULL) ||
|
||
|
(CapturedObjectAttributes->Attributes != 0) ||
|
||
|
(CapturedObjectAttributes->SecurityDescriptor != NULL) ||
|
||
|
(CapturedObjectAttributes->SecurityQualityOfService != NULL))
|
||
|
{
|
||
|
Status = STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
NTSTATUS WmipProbeWmiRegRequest(
|
||
|
PWMIREGREQUEST Buffer,
|
||
|
ULONG InBufferLen,
|
||
|
ULONG OutBufferLen
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Probe the incoming WMIREGREQUEST to ensure that any offsets in the
|
||
|
header point to memory that is valid within the buffer. Also validate
|
||
|
that the WMIREGINFO is properly formed.
|
||
|
|
||
|
This routine MUST succeed before any fields in the WMI_REG_INFO
|
||
|
can be used by any kernel components when passed in from user mode.
|
||
|
Note that we can trust that the input and output buffer are properly
|
||
|
sized since the WMI IOCTLs are METHOD_BUFFERED and the IO manager does
|
||
|
that for us.
|
||
|
|
||
|
WMIREGREQUEST Rules:
|
||
|
|
||
|
1. The incoming buffer must be at least as large as
|
||
|
sizeof(WMIREGREQUEST) + sizeof(WMIREGINFOW)
|
||
|
2. The outgoing buffer must be at least as large as
|
||
|
sizeof(WMIREGRESULTS)
|
||
|
3. WmiRegInfo->BufferSize must be less than incoming Buffer size minus
|
||
|
sizeof(WMIREGREQUEST)
|
||
|
4. GuidCount must be at least 1 and less than MAXWMIGUIDCOUNT
|
||
|
5. WmiRegInfo->BufferSize must be greater than equal to
|
||
|
sizeof(WMIREGINFOW) + WmiRegInfo->GuidCount * sizeof(WMIREGGUIDW)
|
||
|
5. WmiRegInfo->RegistryPath offset must be within the incoming buffer
|
||
|
6. WmiRegInfo->MofResourcePath offset must be within the incomoing buffer
|
||
|
7. RegistryPath and MofResourceName strings are counted unicode strings.
|
||
|
Their length must be within the incoming buffer
|
||
|
8. For WOW64, RefInfo32Size and RegGuid32Size passed in must be non-zero and
|
||
|
cannot be larger than their 64 bit counter part.
|
||
|
9. Since we decipher the counted strings at a buffer offset, the offset
|
||
|
must be aligned to 2 bytes (for USHORT).
|
||
|
10. Trace Registrations do not use InstanceNames within REGGUIDW.
|
||
|
Therefore InstanceCount and InstanceNameList fields must be zero.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Buffer - WMIREGREQUEST to be validated
|
||
|
|
||
|
InBufferLen - Size of the incoming buffer
|
||
|
|
||
|
OutBufferLen - Size of the Output buffer
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NT status code
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
HANDLE RequestHandle;
|
||
|
ULONG WmiRegInfoSize;
|
||
|
PWMIREGINFOW WmiRegInfo;
|
||
|
PWMIREGRESULTS WmiRegResults;
|
||
|
PWMIREGREQUEST WmiRegRequest;
|
||
|
PWMIREGINFOW WmiRegInfoThunk = NULL;
|
||
|
PWMIREGGUIDW WmiRegGuid;
|
||
|
ULONG GuidCount;
|
||
|
ULONG SizeNeeded;
|
||
|
ULONG ImageNameLength=0;
|
||
|
ULONG ResourceNameLength=0;
|
||
|
PUCHAR pSource;
|
||
|
ULONG i;
|
||
|
|
||
|
PAGED_CODE();
|
||
|
|
||
|
//
|
||
|
// Incoming Buffer must be atleast the sizeof WMIREGREQUEST + WMIREGINFO
|
||
|
//
|
||
|
|
||
|
if (InBufferLen >= (sizeof(WMIREGREQUEST) + sizeof(WMIREGINFO)))
|
||
|
{
|
||
|
WmiRegRequest = (PWMIREGREQUEST)Buffer;
|
||
|
WmiRegInfo = (PWMIREGINFOW) OffsetToPtr (Buffer, sizeof(WMIREGREQUEST));
|
||
|
WmiRegInfoSize = WmiRegInfo->BufferSize;
|
||
|
|
||
|
GuidCount = WmiRegRequest->GuidCount;
|
||
|
// We are rejecting any MOF notifications.
|
||
|
WmiRegInfo->RegistryPath = 0;
|
||
|
WmiRegInfo->MofResourceName = 0;
|
||
|
//
|
||
|
// BufferSize specified must be within the size of incoming Buffer.
|
||
|
//
|
||
|
|
||
|
if (WmiRegInfoSize <= (InBufferLen - sizeof(WMIREGREQUEST)) )
|
||
|
{
|
||
|
if ((GuidCount == 0) || (GuidCount > WMIMAXREGGUIDCOUNT))
|
||
|
{
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
//
|
||
|
// Make sure the RegistryPath and MofResourceName offsets are
|
||
|
// within the REGINFO buffer.
|
||
|
//
|
||
|
|
||
|
if ( (WmiRegInfo->RegistryPath >= WmiRegInfoSize) ||
|
||
|
(WmiRegInfo->MofResourceName >= WmiRegInfoSize) ) {
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Validate the Counted Strings.
|
||
|
//
|
||
|
|
||
|
if (WmiRegInfo->RegistryPath > 0)
|
||
|
{
|
||
|
//
|
||
|
// String Offsets need to be aligned to 2 Bytes
|
||
|
//
|
||
|
if (( WmiRegInfo->RegistryPath & 1) != 0)
|
||
|
{
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
ImageNameLength = *((PUSHORT)OffsetToPtr(WmiRegInfo, WmiRegInfo->RegistryPath) );
|
||
|
ImageNameLength += sizeof(USHORT);
|
||
|
|
||
|
if ((WmiRegInfo->RegistryPath + ImageNameLength ) > WmiRegInfoSize)
|
||
|
{
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (WmiRegInfo->MofResourceName > 0)
|
||
|
{
|
||
|
if ((WmiRegInfo->MofResourceName & 1) != 0)
|
||
|
{
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
|
||
|
ResourceNameLength = *((PUSHORT)OffsetToPtr(WmiRegInfo, WmiRegInfo->MofResourceName));
|
||
|
ResourceNameLength += sizeof(USHORT);
|
||
|
|
||
|
if ( (WmiRegInfo->MofResourceName + ResourceNameLength) > WmiRegInfoSize)
|
||
|
{
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
}
|
||
|
// Note: If the ImagePath and MofResource trample over each other but stayed
|
||
|
// within BufferSize , we will not catch it.
|
||
|
|
||
|
#if defined(_WIN64)
|
||
|
if (IoIs32bitProcess(NULL))
|
||
|
{
|
||
|
//
|
||
|
// Check to make sure the 32 bit structure sizes passed in
|
||
|
// by the caller is comparable to the 64-bit counterparts
|
||
|
//
|
||
|
|
||
|
if ((WmiRegRequest->WmiRegInfo32Size == 0) ||
|
||
|
(WmiRegRequest->WmiRegInfo32Size > sizeof(WMIREGINFOW)) )
|
||
|
{
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
|
||
|
if ((WmiRegRequest->WmiRegGuid32Size == 0) ||
|
||
|
(WmiRegRequest->WmiRegGuid32Size > sizeof(WMIREGGUIDW)) )
|
||
|
{
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// InstanceCount and InstanceNameList in
|
||
|
// WMIREGGUIDW structure must be zero. This check is
|
||
|
// done after thunking gor WOW64.
|
||
|
//
|
||
|
|
||
|
|
||
|
SizeNeeded = WmiRegRequest->WmiRegInfo32Size +
|
||
|
GuidCount * WmiRegRequest->WmiRegGuid32Size +
|
||
|
ImageNameLength +
|
||
|
ResourceNameLength;
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
|
||
|
//
|
||
|
// Check to see if the InstanceCount and InstanceNameList in
|
||
|
// WMIREGGUIDW structure is zero
|
||
|
//
|
||
|
pSource = OffsetToPtr(WmiRegInfo, sizeof(WMIREGINFOW) );
|
||
|
for (i=0; i < GuidCount; i++) {
|
||
|
WmiRegGuid = (PWMIREGGUIDW) pSource;
|
||
|
if ( (WmiRegGuid->InstanceCount > 0) ||
|
||
|
(WmiRegGuid->InstanceNameList > 0) )
|
||
|
{
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
pSource += sizeof(WMIREGGUIDW);
|
||
|
}
|
||
|
|
||
|
SizeNeeded = sizeof(WMIREGINFOW) +
|
||
|
GuidCount * sizeof(WMIREGGUIDW) +
|
||
|
ImageNameLength +
|
||
|
ResourceNameLength;
|
||
|
}
|
||
|
|
||
|
if (SizeNeeded > WmiRegInfoSize) {
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
//
|
||
|
// Now validate the OutBuffer size needed
|
||
|
//
|
||
|
|
||
|
SizeNeeded = sizeof(TRACEGUIDMAP) * GuidCount + sizeof(WMIREGRESULTS);
|
||
|
if (SizeNeeded > OutBufferLen)
|
||
|
{
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
//
|
||
|
// All tests passed. Return SUCCESS
|
||
|
//
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
}
|
||
|
return STATUS_UNSUCCESSFUL;
|
||
|
}
|
||
|
|