14

On a given USB port, I only want to accept USB Mass Storage capabilities, and nothing else. No HID devices, no Bluetooth adapters, no RS232 converters, nothing. Is there a way to do this e.g. using udev? I'm aware that I can write a custom udev rule for including a driver for a given device, or a given port, but can I somehow exclude all other drivers? I'm trying to only allow one class of devices, namely USB Mass Storage; there are a myriad different device models in this class, and I don't know which are going to connect to the port (clients will bring their own, there is no way for me to affect this).

Threats from reprogrammed USB firmware are only going to get worse in the foreseeable future. I'm trying to mitigate them for this use case: I have a computer with internally connected USB peripherals (network card, specialized peripherals, keyboard) and one public-facing USB port, which is only supposed to be used for file transfer. So, I can't blacklist the other USB modules altogether; but I'd like to "sanitize" that particular port, so that plugging in a different device type would do nothing.

The case is physically locked, so that only this one specific USB port is accessible from outside, and meddling with the case or splicing into the keyboard cable should be suspicious enough to trigger physical security response; moreover, I don't expect most of the users to be actively malicious, but I expect the number of unwitting carriers of re-flashed USB drives to increase (as it was with floppy boot sector infections of yore). As far as security goes, it does not really matter whether the user brings the "weaponized" USB disk with malicious intent, or just doesn't know that it's "infected".

I am aware that perfect security is infeasible here, and allowing user to interact with the system in any way is risky - but alas, I need to balance security against usability: the computer needs to be client-usable. Also, I'm not trying to defend against a targetted, determined attacker with this; rather, I'm using this as one of mitigation techniques, so that the system is not low-hanging-fruit.

13
  • 2
    Um. I'm just learning stuff, so not sure if this may help , but see if this is relevant: "linux-usb.org/FAQ.html" Specifically , scroll to "Why do I only see one device from my multipurpose storage device" question, and there's line " If you do not want to do this for all SCSI devices then you can tell the kernel to scan for a specific device using; echo >/proc/scsi/scsi "scsi add-single-device 0 0 0 1" " Oct 6, 2014 at 8:46
  • 1
    You only want a specific port to be locked to USB Mass Storage?
    – Kaz Wolfe
    Oct 6, 2014 at 9:41
  • 3
    "With physical access, the battle for security is lost." ~Every Security.SE user ever.
    – Kaz Wolfe
    Oct 6, 2014 at 10:17
  • 1
    @Piskvor I'm not sure but you might have better luck finding an answer at security.stackexchange.com or one of the other more advanced / professional-oriented sites.
    – Brian Z
    Oct 6, 2014 at 12:32
  • 2
    Maybe this can help: irongeek.com/… , especially the 3.2 Locking down Linux using UDEV section.
    – alci
    Oct 6, 2014 at 12:32

1 Answer 1

17
+250

It seems to work for me in Ubuntu 14.04 with 2 flash keys & android phone as storage and usb network adapter & webcam as other type. (I couldn't test placing a usb hub)

  1. Check USB port (which is a parent device for the plugged device)

    $ udevadm info --name=/dev/sdc --attribute-walk
    
      looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0':
        KERNELS=="2-1.2:1.0"
        SUBSYSTEMS=="usb"
        DRIVERS=="usb-storage"
        ATTRS{bInterfaceClass}=="08"
        ATTRS{bInterfaceSubClass}=="06"
        ATTRS{bInterfaceProtocol}=="50"
        ATTRS{bNumEndpoints}=="02"
        ATTRS{supports_autosuspend}=="1"
        ATTRS{bAlternateSetting}==" 0"
        ATTRS{bInterfaceNumber}=="00"
    
  2. Create udev rule, matching usb port kernel name without usb-storage driver

    /etc/udev/rules.d/90-remove-non-storage.rules

    Allow any device that have storage as 1st interface (Composite devices allowed)

    KERNELS=="2-1.2:1.0", DRIVERS!="usb-storage", RUN+="/bin/sh -c 'echo 1 > /sys/bus/usb/drivers/hub/2-1\:1.0/port2/device/remove'"
    

    Block any device that have a non storage interface (Composite devices denied)

    Actually, Phone gets mounted as modem to /dev/ttyACM0 as KERNELS=="2-1.2:1.1". This will not let phones (composite devices) to be mount only simple storage devices will.

    KERNELS=="2-1.2:1.[0-9]*", DRIVERS!="usb-storage", RUN+="/bin/sh -c 'echo 1 > /sys/bus/usb/drivers/hub/2-1\:1.0/port2/device/remove'"
    

    Block only interfaces that are not storage (Composite devices allowed as storage only)

    After some search about a way to disable only non allowed interfaces. Driver unbinding seems to work. My phone could only be used as storage, It does not create /dev/ttyACM0.

    KERNELS=="2-1.2:1.[0-9]*", DRIVERS!="usb-storage", RUN+="/bin/sh -c 'echo -n %k >/sys%p/driver/unbind'"
    
  3. Reload udev rules

    udevadm control --reload-rules
    

References:

11
  • 1
    This is a good approach, but why name the script 90-? Udev rules are processed in lexical order, so the lower number script gets executed first. Would you not want to run your remove script before anything else happens with the devices that you want to block?
    – user643011
    Dec 9, 2022 at 20:52
  • 1
    Can you think of any race conditions where the plugged-in device could exploit a bug in the kernel before it gets removed by the udev script? What exactly happens from the point you plug in a device until the script runs? Could you add more sources to your answer?
    – user643011
    Dec 9, 2022 at 20:56
  • 1
    @user643011 Yes, a lower number seems more convenient for this case, I just used to work late scripts. Udev is slow in that sense and may miss event while booting. Instead of blacklist, it may be best for security thinking of whitelist. Every thing blocked till allowed by udev. Seems not easy but could be tested in a virtual box, see this post: unix.stackexchange.com/a/412227/12209
    – user.dz
    Dec 9, 2022 at 22:05
  • 1
    One of the comments in the issue that you linked brought me to a project that apparently builds on the safe USB authorization approach in the kernel und comes with an easier to use configuration language than using udev rules directly: usbguard.github.io
    – user643011
    Dec 9, 2022 at 22:42
  • 1
    One more note: I tried your udev rules above, and it worked well. It reliably blocks keyboards, mouses, webcams, NICs, etc. and even USB hubs. But I had to add two udev rules per physical USB port because USB1.1/2/3.0 devices require different kernel-strings on the same physical port. E.g. one physical port required me to add both KERNELS=="1-4:1.[0-9]*" ..... /hub/1-0\:1.0/usb1-port4/device/remove'" and KERNELS=="2-5:1.[0-9]*" ..... /hub/2-0\:1.0/usb2-port5/device/remove'"
    – user643011
    Dec 10, 2022 at 8:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .