summaryrefslogtreecommitdiff
path: root/UsbDk/ControlDevice.h
blob: 4811c3deb13750f0e36aa7cf5a4937eb0c389346 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
/**********************************************************************
* Copyright (c) 2013-2014  Red Hat, Inc.
*
* Developed by Daynix Computing LTD.
*
* Authors:
*     Dmitry Fleytman <dmitry@daynix.com>
*     Pavel Gurvich <pavel@daynix.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************/

#pragma once

#include "WdfDevice.h"
#include "WdfRequest.h"
#include "Alloc.h"
#include "UsbDkUtil.h"
#include "FilterDevice.h"
#include "HiderDevice.h"
#include "UsbDkDataHider.h"
#include "HideRulesRegPublic.h"

typedef struct tag_USB_DK_DEVICE_ID USB_DK_DEVICE_ID;
typedef struct tag_USB_DK_DEVICE_INFO USB_DK_DEVICE_INFO;
typedef struct tag_USB_DK_CONFIG_DESCRIPTOR_REQUEST USB_DK_CONFIG_DESCRIPTOR_REQUEST;
class CUsbDkFilterDevice;
class CWdfRequest;

typedef struct tag_USBDK_CONTROL_REQUEST_CONTEXT
{
    HANDLE CallerProcessHandle;
} USBDK_CONTROL_REQUEST_CONTEXT, *PUSBDK_CONTROL_REQUEST_CONTEXT;

WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(USBDK_CONTROL_REQUEST_CONTEXT, UsbDkControlRequestGetContext);

class CControlRequest : public CWdfRequest
{
public:
    CControlRequest(WDFREQUEST Request)
        : CWdfRequest(Request)
    {}

    PUSBDK_CONTROL_REQUEST_CONTEXT Context()
    {
        return UsbDkControlRequestGetContext(m_Request);
    }
};

class CUsbDkControlDeviceQueue : public CWdfDefaultQueue
{
public:
    CUsbDkControlDeviceQueue()
        : CWdfDefaultQueue(WdfIoQueueDispatchSequential, WdfExecutionLevelPassive)
    {}

private:
    virtual void SetCallbacks(WDF_IO_QUEUE_CONFIG &QueueConfig) override;
    static void DeviceControl(WDFQUEUE Queue,
                              WDFREQUEST Request,
                              size_t OutputBufferLength,
                              size_t InputBufferLength,
                              ULONG IoControlCode);

    static void CountDevices(CControlRequest &Request, WDFQUEUE Queue);
    static void UpdateRegistryParameters(CControlRequest &Request, WDFQUEUE Queue);
    static void EnumerateDevices(CControlRequest &Request, WDFQUEUE Queue);
    static void AddRedirect(CControlRequest &Request, WDFQUEUE Queue);
    static void GetConfigurationDescriptor(CControlRequest &Request, WDFQUEUE Queue);

    typedef NTSTATUS(CUsbDkControlDevice::*USBDevControlMethod)(const USB_DK_DEVICE_ID&);
    static void DoUSBDeviceOp(CControlRequest &Request, WDFQUEUE Queue, USBDevControlMethod Method);

    template <typename TInputObj, typename TOutputObj>
    using USBDevControlMethodWithOutput = NTSTATUS(CUsbDkControlDevice::*)(const TInputObj& Input,
                                                                           TOutputObj *Output,
                                                                           size_t* OutputBufferLen);
    template <typename TInputObj, typename TOutputObj>
    static void DoUSBDeviceOp(CControlRequest &Request,
                              WDFQUEUE Queue,
                              USBDevControlMethodWithOutput<TInputObj, TOutputObj> Method);

    static bool FetchBuffersForAddRedirectRequest(CControlRequest &WdfRequest, PUSB_DK_DEVICE_ID &DeviceId, PULONG64 &RedirectorDevice);

    CUsbDkControlDeviceQueue(const CUsbDkControlDeviceQueue&) = delete;
    CUsbDkControlDeviceQueue& operator= (const CUsbDkControlDeviceQueue&) = delete;
};

class CUsbDkHideRule : public CAllocatable < USBDK_NON_PAGED_POOL, 'RHHR' >
{
public:

    CUsbDkHideRule(bool Hide, ULONG Class, ULONG VID, ULONG PID, ULONG BCD)
        : m_Hide(Hide)
        , m_Class(Class)
        , m_VID(VID)
        , m_PID(PID)
        , m_BCD(BCD)
    {}

    bool Match(const USB_DEVICE_DESCRIPTOR &Descriptor) const
    {
        return MatchCharacteristic(m_Class, Descriptor.bDeviceClass) &&
               MatchCharacteristic(m_VID, Descriptor.idVendor)       &&
               MatchCharacteristic(m_PID, Descriptor.idProduct)      &&
               MatchCharacteristic(m_BCD, Descriptor.bcdDevice);
    }

    bool ShouldHide() const
    {
        return m_Hide;
    }

    bool ForceDecision() const
    {
        //All do-not-hide rules are terminal
        return !m_Hide;
    }

    bool operator ==(const CUsbDkHideRule &Other) const
    {
        return m_Hide == Other.m_Hide   &&
               m_Class == Other.m_Class &&
               m_VID == Other.m_VID     &&
               m_PID == Other.m_PID     &&
               m_BCD == Other.m_BCD;

    }

    void Dump(LONG traceLevel = m_defaultDumpLevel) const;

private:
    bool MatchCharacteristic(ULONG CharacteristicFilter, ULONG CharacteristicValue) const
    {
        return (CharacteristicFilter == USBDK_REG_HIDE_RULE_MATCH_ALL) ||
               (CharacteristicValue == CharacteristicFilter);
    }

    bool    m_Hide;
    ULONG   m_Class;
    ULONG   m_VID;
    ULONG   m_PID;
    ULONG   m_BCD;
    static  LONG m_defaultDumpLevel;
    DECLARE_CWDMLIST_ENTRY(CUsbDkHideRule);
};

class CUsbDkRedirection : public CAllocatable<USBDK_NON_PAGED_POOL, 'NRHR'>, public CWdmRefCountingObject
{
public:
    enum : ULONG
    {
        NO_REDIRECTOR = (ULONG) -1,
    };

    NTSTATUS Create(const USB_DK_DEVICE_ID &Id);

    bool operator==(const USB_DK_DEVICE_ID &Id) const;
    bool operator==(const CUsbDkChildDevice &Dev) const;
    bool operator==(const CUsbDkRedirection &Other) const;

    void Dump() const;

    void NotifyRedirectorCreated(CUsbDkFilterDevice *RedirectorDevice);
    void NotifyRedirectionRemoved();
    void NotifyRedirectionRemovalStarted();

    bool IsRedirected() const
    { return m_RedirectorDevice != nullptr; }

    bool IsPreparedForRemove() const
    { return m_RemovalInProgress; }

    NTSTATUS WaitForAttachment()
    { return m_RedirectionCreated.Wait(true, -SecondsTo100Nanoseconds(120)); }

    bool WaitForDetachment();

    NTSTATUS CreateRedirectorHandle(HANDLE RequestorProcess, PHANDLE ObjectHandle);

private:
    ~CUsbDkRedirection()
    {
        if (m_RedirectorDevice != nullptr)
        {
            m_RedirectorDevice->Release();
        }
    }

    virtual void OnLastReferenceGone()
    { delete this; }

    CString m_DeviceID;
    CString m_InstanceID;

    CWdmEvent m_RedirectionCreated;
    CWdmEvent m_RedirectionRemoved;
    CUsbDkFilterDevice *m_RedirectorDevice = nullptr;

    bool m_RemovalInProgress = false;

    DECLARE_CWDMLIST_ENTRY(CUsbDkRedirection);
};

class CDriverParamsRegistryPath final
{
public:
    static PCUNICODE_STRING Get()
    { return *m_Path; };

private:
    static void CreateFrom(PCUNICODE_STRING DriverRegPath);
    static void Destroy();

    class CAllocatablePath : public CString,
                             public CAllocatable < PagedPool, 'PRHR' >
    {};

    static CAllocatablePath *m_Path;

    friend NTSTATUS DriverEntry(PDRIVER_OBJECT, PUNICODE_STRING);
    friend VOID DriverUnload(IN WDFDRIVER Driver);
};

class CUsbDkControlDevice : private CWdfControlDevice, public CAllocatable<USBDK_NON_PAGED_POOL, 'DCHR'>
{
public:
    CUsbDkControlDevice() {}

    NTSTATUS Create(WDFDRIVER Driver);
    NTSTATUS Register();

    void RegisterFilter(CUsbDkFilterDevice &FilterDevice)
    { m_FilterDevices.PushBack(&FilterDevice); }
    void UnregisterFilter(CUsbDkFilterDevice &FilterDevice)
    { m_FilterDevices.Remove(&FilterDevice); }

    void RegisterHiddenDevice(CUsbDkFilterDevice &FilterDevice);
    void UnregisterHiddenDevice(CUsbDkFilterDevice &FilterDevice);

    ULONG CountDevices();
    NTSTATUS RescanRegistry()
    { return ReloadPersistentHideRules(); }

    bool EnumerateDevices(USB_DK_DEVICE_INFO *outBuff, size_t numberAllocatedDevices, size_t &numberExistingDevices);
    NTSTATUS ResetUsbDevice(const USB_DK_DEVICE_ID &DeviceId);
    NTSTATUS AddRedirect(const USB_DK_DEVICE_ID &DeviceId, HANDLE RequestorProcess, PHANDLE ObjectHandle);

    NTSTATUS AddHideRule(const USB_DK_HIDE_RULE &UsbDkRule);
    NTSTATUS AddPersistentHideRule(const USB_DK_HIDE_RULE &UsbDkRule);

    void ClearHideRules();

    NTSTATUS RemoveRedirect(const USB_DK_DEVICE_ID &DeviceId);
    NTSTATUS GetConfigurationDescriptor(const USB_DK_CONFIG_DESCRIPTOR_REQUEST &Request,
                                        PUSB_CONFIGURATION_DESCRIPTOR Descriptor,
                                        size_t *OutputBuffLen);

    bool GetDeviceDescriptor(const USB_DK_DEVICE_ID &DeviceID,
                             USB_DEVICE_DESCRIPTOR &Descriptor);

    static bool Allocate();
    static void Deallocate()
    { delete m_UsbDkControlDevice; }
    static CUsbDkControlDevice* Reference(WDFDRIVER Driver);
    static void Release()
    { m_UsbDkControlDevice->Release(); }

    template <typename TDevID>
    bool ShouldRedirect(const TDevID &Dev) const
    {
        bool DontRedirect = true;
        const_cast<RedirectionsSet*>(&m_Redirections)->ModifyOne(&Dev, [&DontRedirect](CUsbDkRedirection *Entry)
                                            { DontRedirect = Entry->IsRedirected() || Entry->IsPreparedForRemove(); });
        return !DontRedirect;
    }

    bool ShouldHide(const USB_DEVICE_DESCRIPTOR &DevDescriptor) const;

    template <typename TDevID>
    void NotifyRedirectionRemoved(const TDevID &Dev) const
    {
        const_cast<RedirectionsSet*>(&m_Redirections)->ModifyOne(&Dev, [](CUsbDkRedirection *Entry)
                                                                       { Entry->NotifyRedirectionRemoved();} );
   }

    bool NotifyRedirectorAttached(CRegText *DeviceID, CRegText *InstanceID, CUsbDkFilterDevice *RedirectorDevice);
    bool NotifyRedirectorRemovalStarted(const USB_DK_DEVICE_ID &ID);
    bool WaitForDetachment(const USB_DK_DEVICE_ID &ID);

private:
    NTSTATUS ReloadPersistentHideRules();

    CUsbDkControlDeviceQueue m_DeviceQueue;
    static CRefCountingHolder<CUsbDkControlDevice> *m_UsbDkControlDevice;

    CObjHolder<CUsbDkHiderDevice, CWdfDeviceDeleter<CUsbDkHiderDevice> > m_HiderDevice;

    CWdmList<CUsbDkFilterDevice, CLockedAccess, CNonCountingObject, CRefCountingDeleter> m_FilterDevices;

    CWdmList<CUsbDkFilterDevice, CRawAccess, CNonCountingObject, CRefCountingDeleter> m_HiddenDevices;
    CWdmSpinLock m_HiddenDevicesLock;

    typedef CWdmSet<CUsbDkRedirection, CLockedAccess, CNonCountingObject, CRefCountingDeleter> RedirectionsSet;
    RedirectionsSet m_Redirections;

    typedef CWdmSet<CUsbDkHideRule, CLockedAccess, CNonCountingObject> HideRulesSet;
    HideRulesSet m_HideRules;
    HideRulesSet m_PersistentHideRules;
    HideRulesSet m_ExtHideRules;
    HideRulesSet m_PersistentExtHideRules;

    NTSTATUS AddHideRuleToSet(const USB_DK_HIDE_RULE &UsbDkRule, HideRulesSet &Set);

    template <typename TPredicate, typename TFunctor>
    bool UsbDevicesForEachIf(TPredicate Predicate, TFunctor Functor)
    { return m_FilterDevices.ForEach([&](CUsbDkFilterDevice* Dev){ return Dev->EnumerateChildrenIf(Predicate, Functor); }); }

    template <typename TFunctor>
    bool EnumUsbDevicesByID(const USB_DK_DEVICE_ID &ID, TFunctor Functor);
    PDEVICE_OBJECT GetPDOByDeviceID(const USB_DK_DEVICE_ID &DeviceID);

    bool UsbDeviceExists(const USB_DK_DEVICE_ID &ID);

    static void ContextCleanup(_In_ WDFOBJECT DeviceObject);
    NTSTATUS AddRedirectionToSet(const USB_DK_DEVICE_ID &DeviceId, CUsbDkRedirection **NewRedirection);
    void AddRedirectRollBack(const USB_DK_DEVICE_ID &DeviceId, bool WithReset);

    NTSTATUS GetUsbDeviceConfigurationDescriptor(const USB_DK_DEVICE_ID &DeviceID,
                                                 UCHAR DescriptorIndex,
                                                 USB_CONFIGURATION_DESCRIPTOR &Descriptor,
                                                 size_t Length);

    static void IoInCallerContext(WDFDEVICE Device, WDFREQUEST Request);

    friend class CUsbDkControlDeviceInit;
};

typedef struct _USBDK_CONTROL_DEVICE_EXTENSION {

    CUsbDkControlDevice *UsbDkControl; // Store your control data here

    _USBDK_CONTROL_DEVICE_EXTENSION(const _USBDK_CONTROL_DEVICE_EXTENSION&) = delete;
    _USBDK_CONTROL_DEVICE_EXTENSION& operator= (const _USBDK_CONTROL_DEVICE_EXTENSION&) = delete;

} USBDK_CONTROL_DEVICE_EXTENSION, *PUSBDK_CONTROL_DEVICE_EXTENSION;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(USBDK_CONTROL_DEVICE_EXTENSION, UsbDkControlGetContext);