block all USB devices

Hello, I am working on app which must prevent attaching any USB devices to Mac due to security.

Unfortunately I have not found any direct way to implement such blocking:

  1. Looks like IOKit does not allow to block USB (at least in user space)
  2. ES_EVENT_TYPE_AUTH_IOKIT_OPEN (Endpoint Security) does not prevent using USB device if I send response ES_AUTH_RESULT_DENY for "AppleUSBHostDeviceUserClient"

I have found several similar problems on forum but no any solution: https://developer.apple.com/forums/thread/671193 (https://developer.apple.com/forums/thread/756573 https://developer.apple.com/forums/thread/741051

What is the easiest way to implement such blocking?

Thank you in advance!

Answered by DTS Engineer in 802131022

Hi,

What is the easiest way to implement such blocking?

Starting with the bottom line question first, the big question I'd ask here is what you're actually trying to block and why. I'm not sure there is any good way to block "everything" at the USB layer and unless you that's REALLY what you want.

Looking at a few specifics:

Looks like IOKit does not allow to block USB (at least in user space)

The issue that comes up with IOKit is that many of our drivers (including USB) "exit" the kernel at multiple points. Typically, that means some combination of:

-One more more userclients at multiple "levels" of a given driver stack. On USB, there's a user clients against the IOUSBHostDevice itself, then (typically) one or more IOUSBHostInterface user clients.

-Depending on the actual device, the "exit" out of IOKit may be through a non-IOKit interface that isn't really "visible" through IOKit. For example, mass storage volume "end" at an IOMediaBSDClient, which leads to the dev node and then DiskArbitration volume mounting.

Strictly speaking, it is possible to block USB devices through the IOKit stack. You an use either of those user clients to obtain exclusive access, at which point "no one else" will be able to communicate with the device. The problem is that the details of what's ACTUALLY required in that process depend entirely on the "rest" of the driver stack that's interacting with that device.

ES_EVENT_TYPE_AUTH_IOKIT_OPEN (Endpoint Security) does not prevent using USB device if I send response ES_AUTH_RESULT_DENY for "AppleUSBHostDeviceUserClient"

First off, as a side note, I would recommend filing a bug specifically asking that ES_EVENT_TYPE_AUTH_IOKIT_OPEN include the io_object_t reference it's targeting, then posting the bug number back here. Looking over the requests we've had here, there are actually two different requests that we've received:

  1. An API that would allow an ES client to intercept the driver load/matching process, preventing drivers from loading against a particular device/client/etc.

  2. Asking for ES_EVENT_TYPE_AUTH_IOKIT_OPEN to include it's io_object_t target.

The problem here is that while #1 is (arguably) the more useful of the two options, it's also MUCH harder to implement and more dangerous to use. I haven't found a bug specifically focused on #2, which makes it harder to advocate for.

On the specific issue here...

...does not prevent using USB device if I send response ES_AUTH_RESULT_DENY for "AppleUSBHostDeviceUserClient"

...it all depends on what you're trying to block, however, I wouldn't expect this to block all that much. The issue here is that most "interesting" USB devices actually end up "bridging" into some other layer in the kernel, and that OTHER layer is where the actual "work" happens. SO, for example:

  • Mass storage devices go into SAM (SCSI Architecture Model)/IOStorage family, which then exists the kernel as BSD dev nodes.

  • HID devices (mice, keyboards, etc.) go into the HID driver stack.

etc...

One final note here is that, in general, blocking trying to "properly" block hardware is an inherently complicated task that requires a lot of knowledge about how the system and hardware works, as well as a great deal of experimentation and testing.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Accepted Answer

Hi,

What is the easiest way to implement such blocking?

Starting with the bottom line question first, the big question I'd ask here is what you're actually trying to block and why. I'm not sure there is any good way to block "everything" at the USB layer and unless you that's REALLY what you want.

Looking at a few specifics:

Looks like IOKit does not allow to block USB (at least in user space)

The issue that comes up with IOKit is that many of our drivers (including USB) "exit" the kernel at multiple points. Typically, that means some combination of:

-One more more userclients at multiple "levels" of a given driver stack. On USB, there's a user clients against the IOUSBHostDevice itself, then (typically) one or more IOUSBHostInterface user clients.

-Depending on the actual device, the "exit" out of IOKit may be through a non-IOKit interface that isn't really "visible" through IOKit. For example, mass storage volume "end" at an IOMediaBSDClient, which leads to the dev node and then DiskArbitration volume mounting.

Strictly speaking, it is possible to block USB devices through the IOKit stack. You an use either of those user clients to obtain exclusive access, at which point "no one else" will be able to communicate with the device. The problem is that the details of what's ACTUALLY required in that process depend entirely on the "rest" of the driver stack that's interacting with that device.

ES_EVENT_TYPE_AUTH_IOKIT_OPEN (Endpoint Security) does not prevent using USB device if I send response ES_AUTH_RESULT_DENY for "AppleUSBHostDeviceUserClient"

First off, as a side note, I would recommend filing a bug specifically asking that ES_EVENT_TYPE_AUTH_IOKIT_OPEN include the io_object_t reference it's targeting, then posting the bug number back here. Looking over the requests we've had here, there are actually two different requests that we've received:

  1. An API that would allow an ES client to intercept the driver load/matching process, preventing drivers from loading against a particular device/client/etc.

  2. Asking for ES_EVENT_TYPE_AUTH_IOKIT_OPEN to include it's io_object_t target.

The problem here is that while #1 is (arguably) the more useful of the two options, it's also MUCH harder to implement and more dangerous to use. I haven't found a bug specifically focused on #2, which makes it harder to advocate for.

On the specific issue here...

...does not prevent using USB device if I send response ES_AUTH_RESULT_DENY for "AppleUSBHostDeviceUserClient"

...it all depends on what you're trying to block, however, I wouldn't expect this to block all that much. The issue here is that most "interesting" USB devices actually end up "bridging" into some other layer in the kernel, and that OTHER layer is where the actual "work" happens. SO, for example:

  • Mass storage devices go into SAM (SCSI Architecture Model)/IOStorage family, which then exists the kernel as BSD dev nodes.

  • HID devices (mice, keyboards, etc.) go into the HID driver stack.

etc...

One final note here is that, in general, blocking trying to "properly" block hardware is an inherently complicated task that requires a lot of knowledge about how the system and hardware works, as well as a great deal of experimentation and testing.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thank you a lot for response!

Starting with the bottom line question first, the big question I'd ask here is what you're actually trying to block and why. I'm not sure there is any good way to block "everything" at the USB layer and unless you that's REALLY what you want.

The customers wants to have such possibility in DLP system. Criteria to block is device properties ( e.g. productId, vendorId etc.) I found solution which is based on USBDeviceReEnumerate() and it alows to unplug the device. ( see 'https://developer.apple.com/forums/thread/741051').

However, using USBDeviceReEnumerate() leads to looping because the system tries to plug the device again and again.

  • Are there any way to avoid this looping?
  • Is it safe if application constantly calls USBDeviceReEnumerate()?

Strictly speaking, it is possible to block USB devices through the IOKit stack. You an use either of those user clients to obtain exclusive access, at which point "no one else" will be able to communicate with the device.

Thank you! It sounds very interesting. I will investigate this possibility.

Thank you a lot for response!

Starting with the bottom line question first, the big question I'd ask here is what you're actually trying to block and why. I'm not sure there is any good way to block "everything" at the USB layer and unless you that's REALLY what you want.

The customers wants to have such possibility in DLP system. Criteria to block is device properties ( e.g. productId, vendorId etc.) I found solution which is based on USBDeviceReEnumerate() and it alows to unplug the device. ( see 'https://developer.apple.com/forums/thread/741051').

That post is an excellent example of what makes this area so frustrating. That post is basically asking "I've just detected a mount request in my ES client, how do I prevent the mount". That question has an obvious answer. If you want to block I/O to a block storage device, you can do so by:

  1. Denying "mount" in your ES client.

  2. Denying "open" to the device node in your ES client.

At that point the mass storage device cannot be accessed from user space. Adding USB into that process does nothing but complicate things.

For non-mass storage devices, this VERY quickly gets EXTREMELY complicated and "messy". Case in point, that post focuses on "blocking iPhones", but iPhones DON'T actually need to connect through USB or any other bus. You can configure them to sync entirely over the network, bypassing all of this. Note that even if you block Apple's connection, that STILL doesn't block transfers from the mac to the device. Many 3rd party apps implement their own data transfer solution, typically by having the iOS app run a web server which the mac can then connect through and upload to through Safari.

Is it safe if application constantly calls USBDeviceReEnumerate()?

The depends on what you mean by "safe"? I'd expect most devices to not really care, but the USB ecosystem is vast, strange, and unpredictable.

However, using USBDeviceReEnumerate() leads to looping because the system tries to plug the device again and again. Are there any way to avoid this looping?

Sure. That's what opening the device yourself and taking exclusives access "does". Once you have exclusive access, no one else can talk to the device.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thank you for all your tips. I found the way to get exclusive access to USB Devices and this helped to avoid using device by other clients. However, the problem is that my application must be the first. Otherwise kIOReturnExclusiveAccess is returned if another client refuses IOUSBInterface closing.

Endpoint Security is a very powerful framework. However, are there any way to open enhancement request for Endpoint Security? I believe, it would be great to have something like driver filter. Such functionality will allow to develop applications, which prevents data breaches, much more easily.

Many 3rd party apps implement their own data transfer solution, typically by having the iOS app run a web server which the mac can then connect through and upload to through Safari.

Thank you for this valuable comment. I will investigate this separately.

I am very grateful for your assistance.

Thank you for all your tips. I found the way to get exclusive access to USB Devices and this helped to avoid using device by other clients. However, the problem is that my application must be the first. Otherwise kIOReturnExclusiveAccess is returned if another client refuses IOUSBInterface closing.

Yes, that's the reality.

Endpoint Security is a very powerful framework. However, are there any way to open enhancement request for Endpoint Security?

The forum post "Bug Reporting: How and Why?" details how all this works, as well as some good general advice. However, I will note that there is a bug (r.118506266) asking for ES_EVENT_TYPE_AUTH_IOKIT_OPEN to include the io_object_t of the object involved, which would greatly improve the usability of ES_EVENT_TYPE_AUTH_IOKIT_OPEN.

Having said that, I also think "ES_EVENT_TYPE_AUTH_IOKIT_OPEN" might be a better option than it "looks". The trick here is to not think about the issue in terms of blocking specific devices, but instead block (or don't block) specific processes.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thank you a lot for all this information.

block all USB devices
 
 
Q