1 /*
2  * PROJECT:     ReactOS kernel-mode tests - Filter Manager
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     FS Mini-filter wrapper to host the filter manager tests
5  * COPYRIGHT:   Copyright 2017 Thomas Faber <thomas.faber@reactos.org>
6  *              Copyright 2017 Ged Murphy <ged.murphy@reactos.org>
7  */
8 
9 #include <ntifs.h>
10 #include <ndk/ketypes.h>
11 #include <fltkernel.h>
12 
13 #define KMT_DEFINE_TEST_FUNCTIONS
14 #include <kmt_test.h>
15 
16 #define NDEBUG
17 #include <debug.h>
18 
19 #include <kmt_public.h>
20 
21 #define KMTEST_FILTER_POOL_TAG  'fTMK'
22 
23 
24 typedef struct _FILTER_DATA
25 {
26     PDRIVER_OBJECT DriverObject;
27     FLT_REGISTRATION FilterRegistration;
28     PFLT_FILTER Filter;
29     PFLT_PORT ServerPort;
30 
31 } FILTER_DATA, *PFILTER_DATA;
32 
33 
34 /* Prototypes */
35 DRIVER_INITIALIZE DriverEntry;
36 
37 /* Globals */
38 static PDRIVER_OBJECT TestDriverObject;
39 static PDEVICE_OBJECT KmtestDeviceObject;
40 static FILTER_DATA FilterData;
41 static PFLT_OPERATION_REGISTRATION Callbacks = NULL;
42 static ULONG CallbacksCount = 0;
43 static PFLT_CONTEXT_REGISTRATION Contexts = NULL;
44 
45 static PFLT_CONNECT_NOTIFY FilterConnect = NULL;
46 static PFLT_DISCONNECT_NOTIFY FilterDisconnect = NULL;
47 static PFLT_MESSAGE_NOTIFY FilterMessage = NULL;
48 static LONG MaxConnections = 0;
49 
50 NTSTATUS
51 FLTAPI
52 FilterUnload(
53     _In_ FLT_FILTER_UNLOAD_FLAGS Flags
54 );
55 
56 NTSTATUS
57 FLTAPI
58 FilterInstanceSetup(
59     _In_ PCFLT_RELATED_OBJECTS FltObjects,
60     _In_ FLT_INSTANCE_SETUP_FLAGS Flags,
61     _In_ DEVICE_TYPE VolumeDeviceType,
62     _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType
63 );
64 
65 NTSTATUS
66 FLTAPI
67 FilterQueryTeardown(
68     _In_ PCFLT_RELATED_OBJECTS FltObjects,
69     _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags
70 );
71 
72 FLT_PREOP_CALLBACK_STATUS
73 FLTAPI
74 FilterPreOperation(
75     _Inout_ PFLT_CALLBACK_DATA Data,
76     _In_ PCFLT_RELATED_OBJECTS FltObjects,
77     _Outptr_result_maybenull_ PVOID *CompletionContext
78 );
79 
80 FLT_POSTOP_CALLBACK_STATUS
81 FLTAPI
82 FilterPostOperation(
83     _Inout_ PFLT_CALLBACK_DATA Data,
84     _In_ PCFLT_RELATED_OBJECTS FltObjects,
85     _In_opt_ PVOID CompletionContext,
86     _In_ FLT_POST_OPERATION_FLAGS Flags
87 );
88 
89 
90 
91 FLT_REGISTRATION FilterRegistration =
92 {
93     sizeof(FLT_REGISTRATION),               //  Size
94     FLT_REGISTRATION_VERSION,               //  Version
95     0,                                      //  Flags
96     NULL,                                   //  ContextRegistration
97     NULL,                                   //  OperationRegistration
98     FilterUnload,                           //  FilterUnloadCallback
99     FilterInstanceSetup,                    //  InstanceSetupCallback
100     FilterQueryTeardown,                    //  InstanceQueryTeardownCallback
101     NULL,                                   //  InstanceTeardownStartCallback
102     NULL,                                   //  InstanceTeardownCompleteCallback
103     NULL,                                   //  AmFilterGenerateFileNameCallback
104     NULL,                                   //  AmFilterNormalizeNameComponentCallback
105     NULL,                                   //  NormalizeContextCleanupCallback
106 #if FLT_MGR_LONGHORN
107     NULL,                                   //  TransactionNotificationCallback
108     NULL,                                   //  AmFilterNormalizeNameComponentExCallback
109 #endif
110 };
111 
112 
113 
114 /* Filter Interface Routines ****************************/
115 
116 /**
117  * @name DriverEntry
118  *
119  * Driver entry point.
120  *
121  * @param DriverObject
122  *        Driver Object
123  * @param RegistryPath
124  *        Driver Registry Path
125  *
126  * @return Status
127  */
128 NTSTATUS
129 NTAPI
DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)130 DriverEntry(
131     IN PDRIVER_OBJECT DriverObject,
132     IN PUNICODE_STRING RegistryPath)
133 {
134     NTSTATUS Status = STATUS_SUCCESS;
135     OBJECT_ATTRIBUTES ObjectAttributes;
136     PSECURITY_DESCRIPTOR SecurityDescriptor;
137     UNICODE_STRING DeviceName;
138     WCHAR DeviceNameBuffer[128] = L"\\Device\\Kmtest-";
139     UNICODE_STRING KmtestDeviceName;
140     PFILE_OBJECT KmtestFileObject;
141     PKMT_DEVICE_EXTENSION KmtestDeviceExtension;
142     PCWSTR DeviceNameSuffix;
143     INT Flags = 0;
144     PKPRCB Prcb;
145 
146     PAGED_CODE();
147     //__debugbreak();
148     DPRINT("DriverEntry\n");
149 
150     RtlZeroMemory(&FilterData, sizeof(FILTER_DATA));
151 
152     Prcb = KeGetCurrentPrcb();
153     KmtIsCheckedBuild = (Prcb->BuildType & PRCB_BUILD_DEBUG) != 0;
154     KmtIsMultiProcessorBuild = (Prcb->BuildType & PRCB_BUILD_UNIPROCESSOR) == 0;
155     TestDriverObject = DriverObject;
156 
157     /* get the Kmtest device, so that we get a ResultBuffer pointer */
158     RtlInitUnicodeString(&KmtestDeviceName, KMTEST_DEVICE_DRIVER_PATH);
159     Status = IoGetDeviceObjectPointer(&KmtestDeviceName, FILE_ALL_ACCESS, &KmtestFileObject, &KmtestDeviceObject);
160 
161     if (!NT_SUCCESS(Status))
162     {
163         DPRINT1("Failed to get Kmtest device object pointer\n");
164         goto cleanup;
165     }
166 
167     Status = ObReferenceObjectByPointer(KmtestDeviceObject, FILE_ALL_ACCESS, NULL, KernelMode);
168 
169     if (!NT_SUCCESS(Status))
170     {
171         DPRINT1("Failed to reference Kmtest device object\n");
172         goto cleanup;
173     }
174 
175     ObDereferenceObject(KmtestFileObject);
176     KmtestFileObject = NULL;
177     KmtestDeviceExtension = KmtestDeviceObject->DeviceExtension;
178     ResultBuffer = KmtestDeviceExtension->ResultBuffer;
179     DPRINT("KmtestDeviceObject: %p\n", (PVOID)KmtestDeviceObject);
180     DPRINT("KmtestDeviceExtension: %p\n", (PVOID)KmtestDeviceExtension);
181     DPRINT("Setting ResultBuffer: %p\n", (PVOID)ResultBuffer);
182 
183 
184     /* call TestEntry */
185     RtlInitUnicodeString(&DeviceName, DeviceNameBuffer);
186     DeviceName.MaximumLength = sizeof DeviceNameBuffer;
187     TestEntry(DriverObject, RegistryPath, &DeviceNameSuffix, &Flags);
188 
189     RtlAppendUnicodeToString(&DeviceName, DeviceNameSuffix);
190 
191     /* Register with the filter manager */
192     if (!(Flags & TESTENTRY_NO_REGISTER_FILTER))
193     {
194         Status = FltRegisterFilter(DriverObject,
195                                    &FilterRegistration,
196                                    &FilterData.Filter);
197         if (!NT_SUCCESS(Status))
198         {
199             DPRINT1("Failed to register the filter driver %wZ\n", &DeviceName);
200             goto cleanup;
201         }
202     }
203 
204     if (!(Flags & TESTENTRY_NO_CREATE_COMMS_PORT))
205     {
206         /* Create a security descriptor */
207         Status = FltBuildDefaultSecurityDescriptor(&SecurityDescriptor,
208                                                    FLT_PORT_ALL_ACCESS);
209         if (!NT_SUCCESS(Status))
210         {
211             goto cleanup;
212         }
213 
214         /* Initialize the security descriptor object */
215         InitializeObjectAttributes(&ObjectAttributes,
216                                    &DeviceName,
217                                    OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
218                                    NULL,
219                                    SecurityDescriptor);
220 
221 
222         /* Create the usermode communication port */
223         Status = FltCreateCommunicationPort(FilterData.Filter,
224                                             &FilterData.ServerPort,
225                                             &ObjectAttributes,
226                                             NULL,
227                                             FilterConnect,
228                                             FilterDisconnect,
229                                             FilterMessage,
230                                             MaxConnections);
231 
232         /* Free the security descriptor */
233         FltFreeSecurityDescriptor(SecurityDescriptor);
234 
235         if (!NT_SUCCESS(Status))
236         {
237             goto cleanup;
238         }
239     }
240 
241     if (!(Flags & TESTENTRY_NO_START_FILTERING))
242     {
243         /* Start filtering the requests */
244         Status = FltStartFiltering(FilterData.Filter);
245     }
246 
247 cleanup:
248     if (!NT_SUCCESS(Status))
249     {
250         if (FilterData.ServerPort)
251         {
252             FltCloseCommunicationPort(FilterData.ServerPort);
253         }
254         if (FilterData.Filter)
255         {
256             FltUnregisterFilter(FilterData.Filter);
257         }
258     }
259 
260     return Status;
261 }
262 
263 /**
264  * @name DriverUnload
265  *
266  * Driver cleanup funtion.
267  *
268  * @param Flags
269  *        Flags describing the unload request
270  */
271 NTSTATUS
272 FLTAPI
FilterUnload(_In_ FLT_FILTER_UNLOAD_FLAGS Flags)273 FilterUnload(
274     _In_ FLT_FILTER_UNLOAD_FLAGS Flags)
275 {
276     PAGED_CODE();
277     UNREFERENCED_PARAMETER(Flags);
278     //__debugbreak();
279 
280     DPRINT("DriverUnload\n");
281 
282     TestFilterUnload(Flags);
283 
284     /* Close the port and unregister the filter */
285     if (FilterData.ServerPort)
286     {
287         FltCloseCommunicationPort(FilterData.ServerPort);
288         FilterData.ServerPort = NULL;
289     }
290     if (FilterData.Filter)
291     {
292         FltUnregisterFilter(FilterData.Filter);
293         FilterData.Filter = NULL;
294     }
295 
296     if (Callbacks)
297     {
298         ExFreePoolWithTag(Callbacks, KMTEST_FILTER_POOL_TAG);
299         Callbacks = NULL;
300         CallbacksCount = 0;
301     }
302 
303     return STATUS_SUCCESS;
304 }
305 
306 
307 /**
308  * @name FilterInstanceSetup
309  *
310  * Volume attach routine
311  *
312  * @param FltObjects
313  *        Filter Object Pointers
314  *        Pointer to an FLT_RELATED_OBJECTS structure that contains opaque pointers
315  *        for the objects related to the current operation
316  * @param Flags
317  *        Bitmask of flags that indicate why the instance is being attached
318  * @param VolumeDeviceType
319  *        Device type of the file system volume
320  * @param VolumeFilesystemType
321  *        File system type of the volume
322  *
323  * @return Status.
324  *         Return STATUS_SUCCESS to attach or STATUS_FLT_DO_NOT_ATTACH to ignore
325  */
326 NTSTATUS
327 FLTAPI
FilterInstanceSetup(_In_ PCFLT_RELATED_OBJECTS FltObjects,_In_ FLT_INSTANCE_SETUP_FLAGS Flags,_In_ DEVICE_TYPE VolumeDeviceType,_In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType)328 FilterInstanceSetup(
329     _In_ PCFLT_RELATED_OBJECTS FltObjects,
330     _In_ FLT_INSTANCE_SETUP_FLAGS Flags,
331     _In_ DEVICE_TYPE VolumeDeviceType,
332     _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType)
333 {
334 #if 0
335     UCHAR VolPropBuffer[sizeof(FLT_VOLUME_PROPERTIES) + 512];
336     PFLT_VOLUME_PROPERTIES VolumeProperties = (PFLT_VOLUME_PROPERTIES)VolPropBuffer;
337 #endif
338     PDEVICE_OBJECT DeviceObject = NULL;
339     UNICODE_STRING VolumeName;
340     ULONG ReportedSectorSize = 0;
341     ULONG SectorSize = 0;
342     NTSTATUS Status = STATUS_SUCCESS;
343 
344     PAGED_CODE();
345 
346     UNREFERENCED_PARAMETER(FltObjects);
347     UNREFERENCED_PARAMETER(Flags);
348 
349     if (!(Flags & TESTENTRY_NO_INSTANCE_SETUP))
350     {
351         RtlInitUnicodeString(&VolumeName, NULL);
352 
353 #if 0 // FltGetVolumeProperties is not yet implemented
354     /* Get the properties of this volume */
355         Status = FltGetVolumeProperties(Volume,
356                                         VolumeProperties,
357                                         sizeof(VolPropBuffer),
358                                         &LengthReturned);
359         if (NT_SUCCESS(Status))
360         {
361             FLT_ASSERT((VolumeProperties->SectorSize == 0) || (VolumeProperties->SectorSize >= MIN_SECTOR_SIZE));
362             SectorSize = max(VolumeProperties->SectorSize, MIN_SECTOR_SIZE);
363             ReportedSectorSize = VolumeProperties->SectorSize;
364         }
365         else
366         {
367             DPRINT1("Failed to get the volume properties : 0x%X", Status);
368             return Status;
369         }
370 #endif
371         /*  Get the storage device object we want a name for */
372         Status = FltGetDiskDeviceObject(FltObjects->Volume, &DeviceObject);
373         if (NT_SUCCESS(Status))
374         {
375             /* Get the dos device name */
376             Status = IoVolumeDeviceToDosName(DeviceObject, &VolumeName);
377             if (NT_SUCCESS(Status))
378             {
379                 DPRINT("VolumeDeviceType %lu, VolumeFilesystemType %lu, Real SectSize=0x%04x, Reported SectSize=0x%04x, Name=\"%wZ\"",
380                        VolumeDeviceType,
381                        VolumeFilesystemType,
382                        SectorSize,
383                        ReportedSectorSize,
384                        &VolumeName);
385 
386                 Status = TestInstanceSetup(FltObjects,
387                                            Flags,
388                                            VolumeDeviceType,
389                                            VolumeFilesystemType,
390                                            &VolumeName,
391                                            SectorSize,
392                                            ReportedSectorSize);
393 
394                 /* The buffer was allocated by the IoMgr */
395                 ExFreePool(VolumeName.Buffer);
396             }
397         }
398     }
399 
400     return Status;
401 }
402 
403 
404 /**
405  * @name FilterQueryTeardown
406  *
407  * Volume detatch routine
408  *
409  * @param FltObjects
410  *        Filter Object Pointers
411  *        Pointer to an FLT_RELATED_OBJECTS structure that contains opaque pointers
412  *        for the objects related to the current operation
413  * @param Flags
414  *        Flag that indicates why the minifilter driver instance is being torn down
415  *
416  */
417 NTSTATUS
418 FLTAPI
FilterQueryTeardown(_In_ PCFLT_RELATED_OBJECTS FltObjects,_In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags)419 FilterQueryTeardown(
420     _In_ PCFLT_RELATED_OBJECTS FltObjects,
421     _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags)
422 {
423     PAGED_CODE();
424 
425     if (!(Flags & TESTENTRY_NO_QUERY_TEARDOWN))
426     {
427         TestQueryTeardown(FltObjects, Flags);
428     }
429 
430     /* We always allow a volume to detach */
431     return STATUS_SUCCESS;
432 }
433 
434 
435 /* Public Routines **************************************/
436 
437 NTSTATUS
KmtFilterRegisterCallbacks(_In_ CONST FLT_OPERATION_REGISTRATION * OperationRegistration)438 KmtFilterRegisterCallbacks(
439     _In_ CONST FLT_OPERATION_REGISTRATION *OperationRegistration)
440 {
441     ULONG Count = 0;
442     ULONG i;
443 
444     if (Callbacks)
445     {
446         return STATUS_ALREADY_REGISTERED;
447     }
448 
449     /* Count how many IRPs being registered */
450     for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
451     {
452         if (OperationRegistration[i].MajorFunction == IRP_MJ_OPERATION_END)
453             break;
454         Count++;
455     }
456 
457     /* Allocate enough pool to hold a copy of the array */
458     Callbacks = ExAllocatePoolWithTag(NonPagedPool,
459                                       sizeof(FLT_OPERATION_REGISTRATION) * (Count + 1),
460                                       KMTEST_FILTER_POOL_TAG);
461     if (Callbacks == NULL)
462     {
463         return STATUS_INSUFFICIENT_RESOURCES;
464     }
465 
466     /* Copy the array, but using our own pre/post callbacks */
467     for (i = 0; i < Count; i++)
468     {
469         Callbacks[i].MajorFunction = OperationRegistration[i].MajorFunction;
470         Callbacks[i].Flags = OperationRegistration[i].Flags;
471         Callbacks[i].PreOperation = FilterPreOperation;
472         Callbacks[i].PostOperation = FilterPostOperation;
473     }
474 
475     /* Terminate the array */
476     Callbacks[Count].MajorFunction = IRP_MJ_OPERATION_END;
477 
478     /* Save the count of IRPs, not counting IRP_MJ_OPERATION_END last entry */
479     CallbacksCount = Count;
480 
481     return STATUS_SUCCESS;
482 }
483 
484 NTSTATUS
KmtFilterRegisterContexts(_In_ PFLT_CONTEXT_REGISTRATION ContextRegistration,_In_ PVOID Callback)485 KmtFilterRegisterContexts(
486     _In_ PFLT_CONTEXT_REGISTRATION ContextRegistration,
487     _In_ PVOID Callback)
488 {
489     UNREFERENCED_PARAMETER(ContextRegistration);
490     UNREFERENCED_PARAMETER(Callback);
491     UNREFERENCED_PARAMETER(Contexts);
492     return STATUS_NOT_IMPLEMENTED;
493 }
494 
495 NTSTATUS
KmtFilterRegisterComms(_In_ PFLT_CONNECT_NOTIFY ConnectNotifyCallback,_In_ PFLT_DISCONNECT_NOTIFY DisconnectNotifyCallback,_In_opt_ PFLT_MESSAGE_NOTIFY MessageNotifyCallback,_In_ LONG MaxClientConnections)496 KmtFilterRegisterComms(
497     _In_ PFLT_CONNECT_NOTIFY ConnectNotifyCallback,
498     _In_ PFLT_DISCONNECT_NOTIFY DisconnectNotifyCallback,
499     _In_opt_ PFLT_MESSAGE_NOTIFY MessageNotifyCallback,
500     _In_ LONG MaxClientConnections)
501 {
502     FilterConnect = ConnectNotifyCallback;
503     FilterDisconnect = DisconnectNotifyCallback;
504     FilterMessage = MessageNotifyCallback;
505     MaxConnections = MaxClientConnections;
506 
507     return STATUS_SUCCESS;
508 }
509 
510 
511 /* Private Routines ******************************************/
512 
513 FLT_PREOP_CALLBACK_STATUS
514 FLTAPI
FilterPreOperation(_Inout_ PFLT_CALLBACK_DATA Data,_In_ PCFLT_RELATED_OBJECTS FltObjects,_Outptr_result_maybenull_ PVOID * CompletionContext)515 FilterPreOperation(
516     _Inout_ PFLT_CALLBACK_DATA Data,
517     _In_ PCFLT_RELATED_OBJECTS FltObjects,
518     _Outptr_result_maybenull_ PVOID *CompletionContext)
519 {
520     FLT_PREOP_CALLBACK_STATUS Status;
521     UCHAR MajorFunction;
522     ULONG i;
523 
524     Status = FLT_PREOP_SUCCESS_NO_CALLBACK;
525     MajorFunction = Data->Iopb->MajorFunction;
526 
527     for (i = 0; i < CallbacksCount; i++)
528     {
529         if (MajorFunction == Callbacks[i].MajorFunction)
530         {
531             // Call their pre-callback
532             Status = Callbacks[i].PreOperation(Data,
533                                                FltObjects,
534                                                CompletionContext);
535         }
536     }
537 
538     return Status;
539 }
540 
541 FLT_POSTOP_CALLBACK_STATUS
542 FLTAPI
FilterPostOperation(_Inout_ PFLT_CALLBACK_DATA Data,_In_ PCFLT_RELATED_OBJECTS FltObjects,_In_opt_ PVOID CompletionContext,_In_ FLT_POST_OPERATION_FLAGS Flags)543 FilterPostOperation(
544     _Inout_ PFLT_CALLBACK_DATA Data,
545     _In_ PCFLT_RELATED_OBJECTS FltObjects,
546     _In_opt_ PVOID CompletionContext,
547     _In_ FLT_POST_OPERATION_FLAGS Flags)
548 {
549     FLT_POSTOP_CALLBACK_STATUS Status;
550     UCHAR MajorFunction;
551     ULONG i;
552 
553     Status = FLT_POSTOP_FINISHED_PROCESSING;
554     MajorFunction = Data->Iopb->MajorFunction;
555 
556     for (i = 0; i < CallbacksCount; i++)
557     {
558         if (MajorFunction == Callbacks[i].MajorFunction)
559         {
560             // Call their post-callback
561             Status = Callbacks[i].PostOperation(Data,
562                                                 FltObjects,
563                                                 CompletionContext,
564                                                 Flags);
565         }
566     }
567 
568     return Status;
569 }
570