2021-01-11
usbkill
the OpenBSD way
usbkill
is a kill-switch that shuts down your computer on any USB change. It does this by watching the output of lsusb
and when there is a change it runs shutdown -h now
.
Simple, right? One thing to note is that lsusb
is not included in OpenBSD’s base packages, but hotplugd(8)
is.
hotplugd
So what is hotplugd
, and how can it be useful? The man page does a good job of explaining it. Simply put: when any device is attached to or detached from your machine hotplugd
will execute a script.
To see how simple it is to write a hotplug script we can start by simply logging device attach events. First, start by enabling and starting hotplugd
(as root):
# rcctl enable hotplugd
# rcctl start hotplugd
hotplugd(ok)
Next we will write the script that is executed on a device attach event:
# mkdir -p /etc/hotplug
# $EDITOR /etc/hotplug/attach
In this file we will log the device class and name:
#!/bin/sh
DEVCLASS=$1
DEVNAME=$2
# log attach event
logger -t hotplug "$DEVCLASS:$DEVNAME attached"
To see this in action in another terminal type
$ tail -f /var/log/messages
Then connect a USB device to your machine and you should see messages tagged ‘hotplug’. This is what I get when I plug in my Logitech USB adapter for my wireless mouse (which I usually don’t use):
Jan 11 11:48:32 x1 hotplug: 0:uhid3 attached
Jan 11 11:48:32 x1 hotplug: 5:wsmouse2 attached
Jan 11 11:48:32 x1 hotplug: 0:ums0 attached
Jan 11 11:48:32 x1 hotplug: 0:uhid1 attached
Jan 11 11:48:32 x1 hotplug: 5:wskbd1 attached
Jan 11 11:48:32 x1 hotplug: 0:uhid0 attached
Jan 11 11:48:32 x1 hotplug: 0:uhid4 attached
Jan 11 11:48:32 x1 hotplug: 0:uhid2 attached
Jan 11 11:48:32 x1 hotplug: 0:uhidev1 attached
Jan 11 11:48:32 x1 hotplug: 0:uhidev2 attached
Jan 11 11:48:32 x1 hotplug: 0:ukbd0 attached
Jan 11 11:48:32 x1 hotplug: 0:uhidev0 attached
Jan 11 11:48:32 x1 hotplug: 0:uhid5 attached
Jan 11 11:48:32 x1 hotplug: 0:uhid6 attached
Site note: What is happening here? Why have so many events popped up after inserting one USB device? My theory is that because it’s a Unifying Receiver, it allows more than one Logitech HID to be connected to a single receiver, so it must register a bunch of HIDs to the kernel.
The ‘kill’ part
Now that we’ve seen how hotplugd
works, we can work on the kill part of usbkill
.
We want to shutdown the machine on a device attach or detach event, but we also want to be able to arm and disarm the kill part, since I might need to plug something in once in a while. Make the /etc/hotplug/attach
script as so:
#!/bin/sh
arm_file=/tmp/arm_usbkill
DEVCLASS="$1"
DEVNAME="$2"
logger -t "${0##*/}" "${DEVCLASS}:${DEVNAME} attached"
if [ -f "$arm_file" ]; then
shutdown -p now
else
logger -t "${0##*/}" "unarmed - aborted shutdown"
fi
If the /tmp/arm_usbkill
file exists then the machine will shutdown, if it doesn’t exist it will just add a log entry to syslog.
hotplug
also supports running a different script, /etc/hotplug/detach
on detach events. Since both scripts are exactly the same I’ve symlinked the attach script to the location of the detach script.
# ln -s /etc/hotplug/attach /etc/hotplug/detach
The ${0##*/}
logger tag gets substituted to the filename of the script being executed (in this case it is either attach
or detach
).
One thing to note is that this setup will execute the shutdown command on any device change event. This includes USBs but also includes network devices, disk drives, and serial line interfaces1. If that is a problem, you can implement a device class-based whitelist by checking $DEVCLASS
:
#!/bin/sh
# class whitelist (space-separated)
class_whitelist="0 2 3 5"
# arm file
arm_file=/tmp/arm_usbkill
DEVCLASS="$1"
DEVNAME="$2"
whitelisted() {
for class in $class_whitelist; do
if [ "$class" = "$1" ]; then
return 0
fi
done
return 1
}
logger -t "${0##*/}" "${DEVCLASS}:${DEVNAME} attached"
if [ -f "$arm_file" ]; then
if whitelisted "$DEVCLASS"; then
logger -t "${0##*/}" "whitelisted - aborted shutdown"
exit 0
fi
shutdown -p now
else
logger -t "${0##*/}" "unarmed - aborted shutdown"
fi
But this is not really useful to me since I use a laptop and I’m not attaching and detaching network devices or disk drives.
One disadvantage of this setup is the inability to whitelist devices based on their USB IDs like you can with usbkill
. This could be acheived through a much more complicated script using usbdevs
to get USB IDs, but as of yet I have no need for that.
Arming and disarming
To arm and disarm this script all you have to do is create or remove the $arm_file
respectively. Here are some shell functions to add to your shell rc to make it easier:
# arm/disarm usbkill
arm() {
touch /tmp/arm_usbkill && printf 'armed\\n' || printf 'error arming\\n'
}
disarm() {
rm -f /tmp/arm_usbkill && printf 'disarmed\\n' || printf 'error disarming\\n'
}
The script can be armed or disarmed by any regular user which may or may not be preferable to you (and isn’t the case for usbkill
, which requires root to run).
Originally I had added the uchg
flag to the file to lock it. But this proved to be counterproductive since it wasn’t cleared from /tmp
on reboot and when hotplugd
would first start up, it would immediately shutdown the machine again. That was fun to fix (not).
Why?
So why do all this? Why not just pkg_add usbutils
, download + run usbkill
and be done?
Well for starters that isn’t as fun. The main reason is that all this works in a default OpenBSD install - no additional packages needed. Why install a program to solve a problem when the default tools and services can solve the problem just as (if not more) elegantly?
SEE ALSO
- OpenBSD
hotplug(4)
pseudo-device man page: https://man.openbsd.org/hotplug . - OpenBSD includes the
usbdevs(8)
tool in base that lists connected USB devices and their IDs: https://man.openbsd.org/usbdevs 2. - OpenWRT has it’s own similarly named hotplug.d service: https://openwrt.org/docs/guide-user/base-system/hotplug. But it’s slightly different to OpenBSD’s.
- A couple of years ago I wrote a hotplug script that instead runs
xlock(1)
on all running X displays after a HID is attached. In theory this would to thwart HID/badusb attacks, while not being as aggressive as shutting down your machine. Might be worth looking into if you like the sound of that.
- As per the
hotplugd(8)
man page [return] - In the future I might write a script to act more similarly to
usbkill
, usingusbdevs
to poll for device changes instead oflsusb
. [return]