USBGuard / usbguard

USBGuard is a software framework for implementing USB device authorization policies (what kind of USB devices are authorized) as well as method of use policies (how a USB device may interact with the system)
https://usbguard.github.io/
GNU General Public License v2.0
1.1k stars 133 forks source link

How do usbguard rules work ? #558

Closed lateo-net closed 1 year ago

lateo-net commented 1 year ago

I'm trying to get a machine to allow usb mass storage, but reject some combinations (usb+unwanted stuff) and other various stuff. There's obviously something that i'm missing since i can't get the wanted behaviour.

Some questions I couldn't find an answer to after a few different tries :

Current config and ruleset :

usbguard-daemon.conf

RuleFile=/etc/usbguard/rules.conf
ImplicitPolicyTarget=block
PresentDevicePolicy=allow
PresentControllerPolicy=keep
InsertedDevicePolicy=apply-policy
RestoreControllerDeviceState=false
[...] IPC & audit stuff

rules.conf

# Rules generated on boot using generate-policy :
allow id [...] name "EHCI Host Controller" [...]
allow id [...] name "xHCI Host Controller" [...]
allow id [...] name "USB Keyboard" [...]
allow id [...] name "USB Optical Mouse" [...]
[...]
# Static / generic ruleset :
allow with-interface all-of { 08:*:* } # allow storage
allow with-interface all-of { 03:*:* 0b:*:* } # allow yubikey
reject with-interface all-of { 08:*:* 02:*:* }  # deny storage+ com/cdc
reject with-interface all-of { 08:*:* 0a:*:* }  # deny storage+ cdc data
reject with-interface all-of { 08:*:* e0:*:* }  # deny storage+ wireless
reject with-interface one-of { 02:*:* 07:*:* 0a:*:* 0d:*:* 0e:*:* e0:*:* ef:*:* fe:*:* }  # various unwanted stuff

Result :

What am I doing wrong with this conf ?

hartwork commented 1 year ago

@lateo-net have you checked https://usbguard.github.io/documentation/rule-language ?

lateo-net commented 1 year ago

@hartwork The "generic ruleset" was build with the help of the rule language page you mentioned. The page looks like a starting point to me, and doesn't answer any of my questions :(

hartwork commented 1 year ago

@lateo-net before I go on I should note that I have not hand-built a ruleset myself before and that I'm a contributor here, not one of the original authors.

I believe the rule language page https://usbguard.github.io/documentation/rule-language does answer your first two questions on order and processing to some extent:

When an USB device is inserted into the system, the daemon scans the existing rules sequentially and when a matching rule is found, it either authorizes (allows), deauthorizes (blocks) or removes (rejects) the device, based on the rule target. If no matching rule is found, the decision is based on an implicit default target. This implicit default is to block the device until a decision is made by the user.

From that I would expect that more specific rules need to go before more general rules, e.g. I'd expect that allowing all storage must come after rejecting some specific storage, which seems to be your scenario.

What I would personally do for debugging is to stop the system-wide usbguard-daemon and run e.g. sudo timeout 10s usbguard-daemon -k [..] in the foreground and with auto-kill so that it goes away in case you blocked to many devices, e.g. your keyboard used for typing.

Personally, what I do is I whitelist a device on first use a la sudo usbguard allow -p 44 and hence never had to write rule files manually.

I hope any of that helped.

lateo-net commented 1 year ago

@hartwork

About the quote, if it means :

Then considering all "allow" are before any "reject" in my ruleset, then all usb mass storage devices should be allowed, but they're all denied.

If allowing must come after rejecting, then it doesn't match the example on the rule-language page : first allowing ALL usb storage before denying SOME usb storage+something)

Or once again I'm missing something.

Note : I'm working on a live system, just have to reboot and voilà, access is back if I messed something ;-) That's why I'm injecting the "machine specific" rules at boot.

lateo-net commented 1 year ago

Note : the ruleset became as it is because no rules would block my badusb test device

USB codes of the badusb device used to test the ruleset : { 02:02:00 0a:00:00 08:06:50 03:00:00 01:01:00 01:03:00 }

That' a lot, but whatever I have tried : when mass storage is allowed, so will be the badusb device ; when I find a ruleset that blocks the badusb device, then all mass storage devices are also blocked :(

hartwork commented 1 year ago

Hi @lateo-net, this is getting interesting. To be clear, I'm not trying to "prove you wrong" about anything, rather I'm trying to figure things out together, however sane or broken status quo turns out to be.

I'm playing with version 1.1.2 here btw. You?

I played with a ruleset base upon sudo usbguard generate-policy here now, removed the entry on the mouse, and started playing with mouse rules and observe results. That way I always have my keyboard active: That one reboot a minute ago is meant to be the last.

So the base is this, for reproducibility:

sudo /etc/init.d/usbguard stop  # or a systemd equivalent
cd "$(mktemp -d)"
sudo usbguard generate-policy > rules.conf
echo "RuleFile=${PWD}/rules.conf" > usbguard-daemon.conf
sudo usbguard-daemon -d -k -P -c ./usbguard-daemon.conf -l ./usbguard-daemon.log

These four additions to rules.conf make me believe that matching is indeed stopped after the first match, no matter how generic or specific the rule is:

allow with-interface 03:01:02
block with-interface 03:*:*

allowed 03:01:02 mouse in practice, first match won

block with-interface 03:*:*
allow with-interface 03:01:02

blocks 03:01:02 mouse in practice, first match won

allow with-interface 03:*:*
block with-interface 03:01:02

allowed 03:01:02 mouse in practice, first match won

block with-interface 03:01:02
allow with-interface 03:*:*

blocks 03:01:02 mouse in practice, first match won

I think the rest likely comes down to the [operator] { set } part. The docs have equals { 08:*:* } for "nothing but a storage interface" but your version has all-of { 08:*:* } which is "anything as long as it also has a storage interface" which in contrast happily matches more than pure storage.

I'm not yet sure why all storage devices are blocked for you despite allow with-interface all-of { 08:*:* } though that rule seems dangerous. Which rule is blocking things then in practice, any idea?

muelli commented 1 year ago

That' a lot, but whatever I have tried : when mass storage is allowed, so will be the badusb device ; when I find a ruleset that blocks the badusb device, then all mass storage devices are also blocked :(

note that usbguard can only allow the full device rather than certain interfaces. https://github.com/USBGuard/usbguard/issues/369

lateo-net commented 1 year ago

@hartwork

USBGuard version : 1.1.0(-3 @fedora36) as a daemon (systemd unit) I'm also using usbguard-notifier (0.0.6-7@fedora36) for basic usefull UI feedback. Will probably have an update soon since fedora37 is soon to be out, and it shouldn't be too bothersome to upgrade my livesystem.

Regarding all-of or equals. You're right, my bad, i somehow messed up between Device specification and Conditions parts of the Rule Language page :

Device specification

operator is one of:

    all-of: The device attribute set must contain all of the specified values for the rule to match.
    one-of: The device attribute set must contain at least one of the specified values for the rule to match.
    none-of: The device attribute set must not contain any of the specified values for the rule to match.
    equals: The device attribute set must contain exactly the same set of values for the rule to match.
    equals-ordered: The device attribute set must contain exactly the same set of values in the same order for the rule to match.

Conditions

Interpretation of the set operator:

    all-of: Evaluate to true if all of the specified conditions evaluated to true.
    one-of: Evaluate to true if one of the specified conditions evaluated to true.
    none-of: Evaluate to true if none of the specified conditions evaluated to true.
    equals: Same as all-of.
    equals-ordered: Same as all-of.

I will have to take a look at that tomorrow !

The docs have equals { 08:: } for "nothing but a storage interface" but your version has all-of { 08:: } which is "anything as long as it also has a storage interface" which in contrast happily matches more than pure storage.

I think you're on it. ^^


btw, if your daemon.conf is using ImplicitPolicyTarget=block like mine, it is sometimes usefull to use reject instruction rather than the same block instruction in your rules.conf so you can see which one is actually applied. In some cases I was dumbfounded to discover that the default block was applied whilst I was expecting a reject to be applied ^^'

lateo-net commented 1 year ago

@muelli

That' a lot, but whatever I have tried : when mass storage is allowed, so will be the badusb device ; when I find a ruleset that blocks the badusb device, then all mass storage devices are also blocked :(

I probably didn't make myself clear in that phrase. I'm playing with a badusb device and legit usb keys, an additional keyboard... I couldn't kick out the badusb device and not kick out legit usb storage. :(

note that usbguard can only allow the full device rather than certain interfaces. #369

I'm perfectly ok with this behaviour ! Suspicious stuff at one level should be regarded as suspicious as a whole and kicked out.

lateo-net commented 1 year ago

Ok, I had 3 issues :

Thanks guys !


Let's add a config that seems to work as intended (^^') to the post :

# usbguard-daemon.conf : ImplicitPolicyTarget=block
# ruleset :
allow with-interface equals { 08:*:* } # allow storage
allow with-interface equals { 03:00:* } # allow keyboards & simple yubikeys
allow with-interface all-of { 03:00:* 0b:*:* } # allow yubikeys (the ones with data slots)
reject with-interface one-of { 02:*:* 07:*:* 0a:*:* 0d:*:* 0e:*:* e0:*:* ef:*:* fe:*:* }  # no storage nor keyboard with any of these additional functionalities.