Disabling newly-connected USB input devices

A seemingly innocuous USB flash drive can hide an USB keyboard controller, that once connected to a computer would be able to emulate any series of keystrokes and thus to execute any kind of command.

One way of mitigating this risk is to postulate that all legitimate USB input devices are those already present at boot time—although this may not be true for all users—and consequently to disable any new USB input device appearing after startup.

On GNU/Linux, the following Udev rule may be used to disable USB input devices:

ACTION=="add", SUBSYSTEM=="input", SUBSYSTEMS=="usb", ATTRS{authorized}=="1", \
  ENV{PARID}="$id", RUN+="/bin/sh -c 'echo 0 >/sys/bus/usb/devices/$env{PARID}/authorized'"

But this rule must not be applied at startup, otherwise no USB input devices will ever be authorized even if they are present at boot time. 1

So I keep this rule in a file named /etc/udev/rules.d/99-disable-usb-input.rules.once. Note the “.once” extension, which prevents this file from being read by Udev—it only reads files ending with “.rules”. So this rule won’t be enabled when Udev performs the initial hardware detection, and input devices present at startup will be authorized.

Now I just have to rename this file at the end of the startup sequence, and have Udev reload its rules. This will have no effect on the already detected devices, but will effectively prevent any newly-connected USB input devices from being functional. On my Slackware box, I do that with the following snippet in the /etc/rc.d/rc.local script:

if [ -f /etc/udev/rules.d/99-disable-usb-input.rules.once ]; then
  ln /etc/udev/rules.d/99-disable-usb-input.rules.once /etc/udev/rules.d/99-disable-usb-input.rules
  /sbin/udevadm control --reload
  rm /etc/udev/rules.d/99-disable-usb-input.rules

If you have some USB input devices that you want to be able to connect at any time, you may add some special rules allowing well-identified devices to bypass the disabling rules. Start by obtaining the idVendor and idProduct of your device (with udevadm info --attribute-walk --name=/dev/your_device), then modify the rules file as follows:

ACTION=="add", SUBSYSTEM=="input", SUBSYSTEMS=="usb", ATTRS{idVendor}=="xxxx", ATTRS{idProduct}=="yyyy", \

ACTION=="add", SUBSYSTEM=="input", SUBSYSTEMS=="usb", ATTRS{authorized}=="1", \
  ENV{PARID}="$id", RUN+="/bin/sh -c 'echo 0 >/sys/bus/usb/devices/$env{PARID}/authorized'"

  1. If you only have non-USB input devices, you may apply this rule at startup, and ignore the rest of this note.