vUSB Daemon

Copyright 2015 by Assured Information Security, Inc. Created by Jean-Edouard Lejosne <lejosnej@ainfosec.com>. This work is licensed under the Creative Commons Attribution 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/.

Description

vusb-daemon handles USB device passthrough.
It listens to dbus and udev for user, toolstack and device events.
Then, according to the database-stored policy, it uses the vusb dom0 kernel module to trigger USB passthrough action.

Udev

Client

vusb-daemon runs a udev monitor to get notified of device activity.
It also uses udev to get more information about new devices.

DBus

Client

vusb-daemon talks to:

  • xenmgr to get platform and VM information
  • input server to know what VM currently has focus
  • dbd to read/write the policy

Server

vusb-daemon exposes device information and methods on dbus, to interact with the UI and the toolstack.
It is possible to test it using xec -s com.citrix.xenclient.usbdaemon. NOTE: This call is blocked by SELinux policy, to call this run setenforce 0 to enter SELinux permissive mode.

Policy

Storage

The policy is stored in the main database, under /usb-rules.
It can be dumped using db-ls /usb-rules.
vusb-daemon uses db.c to access it. db.c has been written in such a way that it should be re-usable by other projects, like a command line policy manager.

Format

Each rule is written under /usb-rules/. Low number == high priority.
Rules are made of:

  • a "description":
    Free text to describe the rule
  • a "command"
    • "always": always assign the device (or all if unspecified) to the vm (or none if unspecified), implies "allow" for that vm. Prevents re-assignment to any other VM.
    • "default": same as "always", without preventing re-assignment.
    • "allow": allow the device (or all if unspecified) to be passed to the vm (or all if unspecified)
    • "deny": deny the device (or all if unspecified) to be passed to the vm (or all if unspecified)
  • a "device"
    See below for sub-nodes
  • a "vm"
    For now, the only valid sub-node is "uuid", to specify the UUID of the VM

A device is a list of sub-nodes, chosen from:

Shortcuts

  • "keyboard" = "1"/"0" (the device has to be / not be a keyboard)
  • "mouse" = "1"/"0" (for mice and trackpads)
  • "game_controller" = "1"/"0"
  • "mass_storage" = "1"/"0" (includes optical drives)
  • "optical" = "1"/"0" (cd / dvd / blu ray drive)
  • "vendor_id" (Vendor ID, as parseable to a 'strtol')
  • "device_id" (Device ID, as parseable to a 'strtol')

sysfs Nodes

  • "sysattr" (Parent node for the set of key-value pairs to match against /sys attribute nodes)

Example: To deny all USB 3.0 devices:

 "usb-rules": {
  "1001": {
    "description": "Deny USB 3.0 to all VMs",
    "command": "deny",
    "device": {
      "sysattr": {
        "version" = "3.00"
      }
    }
  }
  ...
}

udev Properties

  • "property" (Parent node for the set of key-value pairs to match against udev properties)

Example: To allow all Toshiba devices by manufacturer name (as opposed to vendor ID):

"usb-rules": {
  "1001": {
    "description": "Allow Toshiba to all VMs",
    "command": "deny",
    "device": {
      "property": {
        "ID_VENDOR" = "Toshiba"
      }
    }
  }
  ...
} 

Behavior

  • Rules are parsed in ("priority") order, from low to high
  • First match "wins", the rest of the policy is ignored, default to deny if no rule matches the device
  • The order is critical
  • Multiple attributes inside a device/vm block must all match (they are AND-ed together).

Default policy

Note the actual default internal policy is "deny" everything. This is the external default policy in json, taken from /config/db:

"usb-rules": {
  "9900": {
    "description": "Deny keyboard passthrough to all VMs",
    "command": "deny",
    "device": {
      "keyboard": "1"
    }
  },
  "9999": {
    "description": "Allow everything else",
    "command": "allow"
  }
} 

Rule 9900 blocks keyboard passthrough and rule 9999 allows everything, effectively switching the default behavior from "deny" to "allow".

Other policy bits

Some legacy flags are still supported by the vusb-daemon, even it they should probably be moved to the policy in the database.

  • Those VM flags are handled:
    • If the VM flag "usb-enabled" is set to false, no device will be plugged to the VM.
    • If "usb-auto-passthrough" is set to false, devices won't be auto-assigned to the VM when it's focused.
  • This VM flags don't have any effect anymore:
    • "usb-grab-devices", which can be expressed by an "always" policy rule with no device and the desired VM

The previous vusb-daemon used to check if VMs had tools installed before allowing plugging devices to them. That requirement doesn't make much sense so it got removed. Now one can install only the USB drivers in a VM and still get USB passthrough.
If this ends up begin a requirement, something like a "tools-installed" VM property could be added to the policy.

Modification

Automatic

When a device is set to be "Always assigned" to a VM, vusb-daemon creates an "always" rule, and puts it at the beginning of the policy, using the highest available priority number below 1001. This means that those rules will have a high priority, which is assumed is what the user wants...
For that reason, manually added rules should have a priority higher than 1000.

Manual

Rules can be manually added using db-write (recommended) or by editing /config/db(in that case, don't forget to run killall -HUP dbd after).
As mentioned above, using priority numbers above 1000 is highly recommended.