[Spice-devel,6/6] usbclerk: add device filter support

Submitted by Arnon Gilboa on July 23, 2012, 9:23 a.m.

Details

Message ID 1343035388-15090-6-git-send-email-agilboa@redhat.com
State New
Headers show

Not browsing as part of any series.

Commit Message

Arnon Gilboa July 23, 2012, 9:23 a.m.
read from HKLM\Software\USBClerk\filter_rules
same format as in client
---
 usbclerk.cpp    |   69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 usbclerk.vcproj |   40 +++++++++++++++++++++++++++++++
 2 files changed, 109 insertions(+), 0 deletions(-)

Patch hide | download patch | download mbox

diff --git a/usbclerk.cpp b/usbclerk.cpp
index 09f1e66..2913998 100644
--- a/usbclerk.cpp
+++ b/usbclerk.cpp
@@ -5,6 +5,7 @@ 
 #include <string.h>
 #include <tchar.h>
 #include "usbclerk.h"
+#include "usbredirfilter.h"
 #include "libwdi.h"
 #include "vdlog.h"
 
@@ -22,6 +23,8 @@ 
 #define USB_DRIVER_INSTALL_RETRIES  10
 #define USB_DRIVER_INSTALL_INTERVAL 2000
 #define MAX_DEVICE_PROP_LEN         256
+#define MAX_DEVICE_HCID_LEN         1024
+#define MAX_DEVICE_FILTER_LEN       1024
 
 class USBClerk {
 public:
@@ -40,6 +43,7 @@  private:
     bool remove_dev(HDEVINFO devs, PSP_DEVINFO_DATA dev_info);
     bool rescan();
     bool get_dev_info(HDEVINFO devs, int vid, int pid, SP_DEVINFO_DATA *dev_info);
+    bool dev_filter_check(int vid, int pid);
     static DWORD WINAPI control_handler(DWORD control, DWORD event_type,
                                         LPVOID event_data, LPVOID context);
     static VOID WINAPI main(DWORD argc, TCHAR * argv[]);
@@ -48,6 +52,8 @@  private:
     static USBClerk* _singleton;
     SERVICE_STATUS _status;
     SERVICE_STATUS_HANDLE _status_handle;
+    struct usbredirfilter_rule *filter_rules;
+    int filter_count;
     char _wdi_path[MAX_PATH];
     HANDLE _pipe;
     bool _running;
@@ -265,8 +271,11 @@  bool USBClerk::execute()
     SECURITY_DESCRIPTOR* sec_desr;
     USBClerkReply reply = {{USB_CLERK_MAGIC, USB_CLERK_VERSION,
         USB_CLERK_REPLY, sizeof(USBClerkReply)}};
+    CHAR filter_str[MAX_DEVICE_FILTER_LEN];
     CHAR buffer[USB_CLERK_PIPE_BUF_SIZE];
     DWORD bytes;
+    HKEY hkey;
+    LONG ret;
 
 #if 0
     /* Hack for wdi logging */
@@ -288,6 +297,20 @@  bool USBClerk::execute()
         vd_printf("CreatePipe() failed: %u", GetLastError());
         return false;
     }
+    ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\USBClerk", 0, KEY_READ, &hkey);
+    if (ret == ERROR_SUCCESS) {
+        DWORD size = sizeof(filter_str);
+        ret = RegQueryValueExA(hkey, "filter_rules", NULL, NULL, (LPBYTE)filter_str, &size);
+        if (ret == ERROR_SUCCESS) {
+            ret = usbredirfilter_string_to_rules(filter_str, ",", "|",
+                                                 &filter_rules, &filter_count);
+            if (ret != 0) {
+                vd_printf("Failed parsing filter rules: %s %d", filter_str, ret);
+            }
+        }
+        RegCloseKey(hkey);
+    }
+    vd_printf("filter_count: %d", filter_count);
     while (_running) {
         if (!ConnectNamedPipe(_pipe, NULL) && GetLastError() != ERROR_PIPE_CONNECTED) {
             vd_printf("ConnectNamedPipe() failed: %u", GetLastError());
@@ -308,6 +331,7 @@  bool USBClerk::execute()
 disconnect:
         DisconnectNamedPipe(_pipe);
     }
+    free(filter_rules);
     CloseHandle(_pipe);
     return true;
 }
@@ -326,6 +350,10 @@  bool USBClerk::dispatch_message(CHAR *buffer, DWORD bytes, USBClerkReply *reply)
         return false;
     }
     dev = (USBClerkDriverOp *)buffer;
+    if (!dev_filter_check(dev->vid, dev->pid)) {
+        reply->status = FALSE;
+        return true;
+    }
     switch (hdr->type) {
     case USB_CLERK_DRIVER_INSTALL:
         vd_printf("Installing winusb driver for %04x:%04x", dev->vid, dev->pid);
@@ -504,6 +532,47 @@  bool USBClerk::get_dev_info(HDEVINFO devs, int vid, int pid, SP_DEVINFO_DATA *de
     return false;
 }
 
+bool USBClerk::dev_filter_check(int vid, int pid)
+{
+    HDEVINFO devs;
+    SP_DEVINFO_DATA dev_info;
+    TCHAR compat_ids[MAX_DEVICE_HCID_LEN];
+    uint8_t dev_cls, dev_subcls, dev_proto;
+    bool ret = false;
+
+    if (!filter_rules) {
+        return true;
+    }
+    devs = SetupDiGetClassDevs(NULL, L"USB", NULL, DIGCF_ALLCLASSES);
+    if (devs == INVALID_HANDLE_VALUE) {
+        vd_printf("SetupDiGetClassDevsEx failed: %u", GetLastError());
+        return false;
+    }
+    if (!get_dev_info(devs, vid, pid, &dev_info)) {
+        vd_printf("Cannot find device info %04x:%04x", vid, pid);
+        goto cleanup;
+    }
+    if (!SetupDiGetDeviceRegistryProperty(devs, &dev_info, SPDRP_COMPATIBLEIDS, NULL,
+            (PBYTE)compat_ids, sizeof(compat_ids), NULL)) {
+        vd_printf("Cannot get compatible id %04x:%04x", vid, pid);
+        goto cleanup;
+    }
+    if (swscanf_s(compat_ids, L"USB\\Class_%02hx&SubClass_%02hx&Prot_%02hx",
+            &dev_cls, &dev_subcls, &dev_proto) != 3) {
+        vd_printf("Cannot parse compatible id %S", compat_ids);
+        goto cleanup;
+    }
+    if (usbredirfilter_check(filter_rules, filter_count, dev_cls, dev_subcls, dev_proto,
+            NULL, NULL, NULL, 0, vid, pid, 0, 0) == 0) {
+        ret = true;
+    } else {
+        vd_printf("Device filter failed %04x:%04x", vid, pid);
+    }
+cleanup:
+    SetupDiDestroyDeviceInfoList(devs);
+    return ret;
+}
+
 int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
 {
     bool success = false;
diff --git a/usbclerk.vcproj b/usbclerk.vcproj
index b692737..dc59ad5 100644
--- a/usbclerk.vcproj
+++ b/usbclerk.vcproj
@@ -347,6 +347,10 @@ 
 				>
 			</File>
 			<File
+				RelativePath=".\usbredirfilter.h"
+				>
+			</File>
+			<File
 				RelativePath=".\vdlog.h"
 				>
 			</File>
@@ -371,6 +375,42 @@ 
 				>
 			</File>
 			<File
+				RelativePath=".\usbredirfilter.c"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						CompileAs="2"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|x64"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						CompileAs="2"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						CompileAs="2"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|x64"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						CompileAs="2"
+					/>
+				</FileConfiguration>
+			</File>
+			<File
 				RelativePath=".\vdlog.cpp"
 				>
 			</File>

Comments

Hi Arnon,

Two comments below.

On 07/23/2012 12:23 PM, Arnon Gilboa wrote:
> read from HKLM\Software\USBClerk\filter_rules
> same format as in client
> ---
>   usbclerk.cpp    |   69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   usbclerk.vcproj |   40 +++++++++++++++++++++++++++++++
>   2 files changed, 109 insertions(+), 0 deletions(-)
>
>
>   class USBClerk {
>   public:
> @@ -40,6 +43,7 @@ private:
>       bool remove_dev(HDEVINFO devs, PSP_DEVINFO_DATA dev_info);
>       bool rescan();
>       bool get_dev_info(HDEVINFO devs, int vid, int pid, SP_DEVINFO_DATA *dev_info);
> +    bool dev_filter_check(int vid, int pid);
>       static DWORD WINAPI control_handler(DWORD control, DWORD event_type,
>                                           LPVOID event_data, LPVOID context);
>       static VOID WINAPI main(DWORD argc, TCHAR * argv[]);
> @@ -48,6 +52,8 @@ private:
>       static USBClerk* _singleton;
>       SERVICE_STATUS _status;
>       SERVICE_STATUS_HANDLE _status_handle;
> +    struct usbredirfilter_rule *filter_rules;
> +    int filter_count;
>       char _wdi_path[MAX_PATH];
>       HANDLE _pipe;
>       bool _running;
> @@ -265,8 +271,11 @@ bool USBClerk::execute()
>       SECURITY_DESCRIPTOR* sec_desr;
>       USBClerkReply reply = {{USB_CLERK_MAGIC, USB_CLERK_VERSION,
>           USB_CLERK_REPLY, sizeof(USBClerkReply)}};
> +    CHAR filter_str[MAX_DEVICE_FILTER_LEN];
>       CHAR buffer[USB_CLERK_PIPE_BUF_SIZE];
>       DWORD bytes;
> +    HKEY hkey;
> +    LONG ret;
>
>   #if 0
>       /* Hack for wdi logging */
> @@ -288,6 +297,20 @@ bool USBClerk::execute()
>           vd_printf("CreatePipe() failed: %u", GetLastError());
>           return false;
>       }

filter_rules should be initialized to NULL and filter_count to 0.
In case they are not initialized by usbredirfilter_string_to_rules (e.g. if
that registry entry does not exist).

> +    ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\USBClerk", 0, KEY_READ,&hkey);
> +    if (ret == ERROR_SUCCESS) {
> +        DWORD size = sizeof(filter_str);
> +        ret = RegQueryValueExA(hkey, "filter_rules", NULL, NULL, (LPBYTE)filter_str,&size);
> +        if (ret == ERROR_SUCCESS) {
> +            ret = usbredirfilter_string_to_rules(filter_str, ",", "|",
> +&filter_rules,&filter_count);
> +            if (ret != 0) {
> +                vd_printf("Failed parsing filter rules: %s %d", filter_str, ret);
> +            }
> +        }
> +        RegCloseKey(hkey);
> +    }
> +    vd_printf("filter_count: %d", filter_count);
>       while (_running) {
>           if (!ConnectNamedPipe(_pipe, NULL)&&  GetLastError() != ERROR_PIPE_CONNECTED) {
>               vd_printf("ConnectNamedPipe() failed: %u", GetLastError());
> @@ -308,6 +331,7 @@ bool USBClerk::execute()
>   disconnect:
>           DisconnectNamedPipe(_pipe);
>       }
> +    free(filter_rules);
>       CloseHandle(_pipe);
>       return true;
>   }
> @@ -326,6 +350,10 @@ bool USBClerk::dispatch_message(CHAR *buffer, DWORD bytes, USBClerkReply *reply)
>           return false;
>       }
>       dev = (USBClerkDriverOp *)buffer;
> +    if (!dev_filter_check(dev->vid, dev->pid)) {
> +        reply->status = FALSE;
> +        return true;
> +    }
>       switch (hdr->type) {
>       case USB_CLERK_DRIVER_INSTALL:
>           vd_printf("Installing winusb driver for %04x:%04x", dev->vid, dev->pid);
> @@ -504,6 +532,47 @@ bool USBClerk::get_dev_info(HDEVINFO devs, int vid, int pid, SP_DEVINFO_DATA *de
>       return false;
>   }
>
> +bool USBClerk::dev_filter_check(int vid, int pid)
> +{
> +    HDEVINFO devs;
> +    SP_DEVINFO_DATA dev_info;
> +    TCHAR compat_ids[MAX_DEVICE_HCID_LEN];
> +    uint8_t dev_cls, dev_subcls, dev_proto;
> +    bool ret = false;
> +
> +    if (!filter_rules) {
> +        return true;
> +    }
> +    devs = SetupDiGetClassDevs(NULL, L"USB", NULL, DIGCF_ALLCLASSES);
> +    if (devs == INVALID_HANDLE_VALUE) {
> +        vd_printf("SetupDiGetClassDevsEx failed: %u", GetLastError());
> +        return false;
> +    }
> +    if (!get_dev_info(devs, vid, pid,&dev_info)) {
> +        vd_printf("Cannot find device info %04x:%04x", vid, pid);
> +        goto cleanup;
> +    }
> +    if (!SetupDiGetDeviceRegistryProperty(devs,&dev_info, SPDRP_COMPATIBLEIDS, NULL,
> +            (PBYTE)compat_ids, sizeof(compat_ids), NULL)) {
> +        vd_printf("Cannot get compatible id %04x:%04x", vid, pid);
> +        goto cleanup;
> +    }
> +    if (swscanf_s(compat_ids, L"USB\\Class_%02hx&SubClass_%02hx&Prot_%02hx",
> +&dev_cls,&dev_subcls,&dev_proto) != 3) {
> +        vd_printf("Cannot parse compatible id %S", compat_ids);
> +        goto cleanup;
> +    }
> +    if (usbredirfilter_check(filter_rules, filter_count, dev_cls, dev_subcls, dev_proto,
> +            NULL, NULL, NULL, 0, vid, pid, 0, 0) == 0) {
> +        ret = true;
> +    } else {
> +        vd_printf("Device filter failed %04x:%04x", vid, pid);
> +    }
> +cleanup:
> +    SetupDiDestroyDeviceInfoList(devs);
> +    return ret;
> +}
> +
>   int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
>   {
>       bool success = false;

> diff --git a/usbclerk.vcproj b/usbclerk.vcproj

This can be moved to the previous commit, if you like.

> index b692737..dc59ad5 100644
> --- a/usbclerk.vcproj
> +++ b/usbclerk.vcproj
> @@ -347,6 +347,10 @@
>   				>
>   			</File>
>   			<File
> +				RelativePath=".\usbredirfilter.h"
> +				>
> +			</File>
> +			<File
>   				RelativePath=".\vdlog.h"
>   				>
>   			</File>
> @@ -371,6 +375,42 @@
>   				>
>   			</File>
>   			<File
> +				RelativePath=".\usbredirfilter.c"
> +				>
> +				<FileConfiguration
> +					Name="Debug|Win32"
> +					>
> +					<Tool
> +						Name="VCCLCompilerTool"
> +						CompileAs="2"
> +					/>
> +				</FileConfiguration>
> +				<FileConfiguration
> +					Name="Debug|x64"
> +					>
> +					<Tool
> +						Name="VCCLCompilerTool"
> +						CompileAs="2"
> +					/>
> +				</FileConfiguration>
> +				<FileConfiguration
> +					Name="Release|Win32"
> +					>
> +					<Tool
> +						Name="VCCLCompilerTool"
> +						CompileAs="2"
> +					/>
> +				</FileConfiguration>
> +				<FileConfiguration
> +					Name="Release|x64"
> +					>
> +					<Tool
> +						Name="VCCLCompilerTool"
> +						CompileAs="2"
> +					/>
> +				</FileConfiguration>
> +			</File>
> +			<File
>   				RelativePath=".\vdlog.cpp"
>   				>
>   			</File>
Uri Lublin wrote:
>
> filter_rules should be initialized to NULL and filter_count to 0.
> In case they are not initialized by usbredirfilter_string_to_rules 
> (e.g. if
> that registry entry does not exist).
>
sure, already fixed, name was also with no _prefix...
>
>> diff --git a/usbclerk.vcproj b/usbclerk.vcproj
>
> This can be moved to the previous commit, if you like.
right, will make it be easier to revert when mingw'ed