Hi Apple,
We are working on a general USB device management solution on macOS for enterprise security. Our goal is to enforce policy-based restrictions on USB devices, such as:
For USB storage devices: block mount, read, or write access.
For other peripherals (e.g., USB headsets or microphones, raspberry pi, etc): block usage entirely.
We know in past, kernel extension would be the way to go, but as kext has been deprecated. And DriverKit is the new advertised framework.
At first, DriverKit looked like the right direction. However, after reviewing the documentation more closely, we noticed that using DriverKit for USB requires specific entitlements:
DriverKit USB Transport – VendorID
DriverKit USB Transport – VendorID and ProductID
This raises a challenge: if our solution is meant to cover all types of USB devices, we would theoretically need entitlements for every VendorID/ProductID in existence.
My questions are:
Is DriverKit actually the right framework for this kind of general-purpose USB device control?
If not, what framework or mechanism should we be looking at for enforcing these kinds of policies?
We also developed an Endpoint Security product, but so far we haven’t found a relevant Endpoint Security event type that would allow us to achieve this.
Any guidance on the correct technical approach would be much appreciated.
Thanks in advance for your help.
We are working on a general USB device management solution on macOS for enterprise security. Our goal is to enforce policy-based restrictions on USB devices, such as: For USB storage devices: block mount, read, or write access.
For other peripherals (e.g., USB headsets or microphones, Raspberry Pi, etc.): block usage entirely. We know in the past, kernel extensions would be the way to go, but as kext has been deprecated. And DriverKit is the new advertised framework.
At first, DriverKit looked like the right direction. However, after reviewing the documentation more closely, we noticed that using DriverKit for USB requires specific entitlements: DriverKit USB Transport – VendorID DriverKit USB Transport – VendorID and ProductID
This raises a challenge: if our solution is meant to cover all types of USB devices, we would theoretically need entitlements for every VendorID/ProductID in existence.
My questions are: Is DriverKit actually the right framework for this kind of general-purpose USB device control?
No. Three issues:
(1) There are a bunch of low-level technical issues that would interfere with this. For example, DEXTs load "late" relative to KEXTs, which is going to create race conditions between your DEXT loading and other processes claiming the hardware. Deciding what to block is also harder than you think, as your DEXT doesn't have access to any kind of "storage" (so you can't have a simple "do not block" list) and determining what a device actually is at the level your blocking is hardware than it looks (that is, a "keyboard" and a "hard drive" both "look" identical at the USB interface level).
(2) You're welcome to apply, but this will not be approved:
This raises a challenge: if our solution is meant to cover all types of USB devices, we would theoretically need entitlements for every VendorID/ProductID in existence.
We actually have a "match everything" entitlement, as that’s how the "Development Only" entitlement variant works. However, one of the basic goals of the DriverKit architecture was to better constrain DEXT matching (vs IOKit) so that an arbitrary driver could claim "any" device. We let the "Development Only" entitlement variant "match anything" specifically because:
-
It's ENORMOUSLY useful to DEXT developers. For example, it's very common for development/testing boards to use the VID/PID of the hardware manufacturer (not whatever the shipping product will be) and the dev only entitlement means developers can easily use this hardware without any unnecessary.
-
The behavior and limitation of development signing make it wildly impractical to use for any kind of broad distribution. The total machine count is relatively small and the build expiration time is relatively short and non-obvious, both of which would make it a real pain to distribute software with. That's a very narrow and specialized exception. I don't expect us to ever approve that configuration for broader distribution, as DriverKit was designed around NOT working this way.
-
Even if you were able to resolve both those issues, it wouldn't actually provide a "full" solution. The problem you overlooked here is that your DEXT is also in "competition" with DEXTs (and other system components) for control of that device. In the case of DEXTs, any device-specific DEXT would supersede your DEXT because that's what the USB specification rules for driver prioritization require. In addition, the IOUSBHost framework is capable of loading "underneath" your DEXT, forcing it to unload and ceding control to the other process. There are ways you could mitigate these issues, but any mechanism that could protect against these cases can just as easily protect the interface "directly".
If not, what framework or mechanism should we be looking at for enforcing these kinds of policies?
Well, the best answer is right here:
We also developed an Endpoint Security product, but so far we haven’t found a relevant Endpoint Security event type that would allow us to achieve this.
There are basically two cases you're concerned with here:
(1) IOKit User Clients.
You can block these with ES_EVENT_TYPE_AUTH_IOKIT_OPEN, which is FABULOUSLY more useful starting in macOS 26. The new data means you can determine exactly what hardware is involved and block whatever you want.
(2) Through the file system, both as device nodes and through mounts.
The ES system already has the tools for blocking these, either by blocking the device node from opening or by blocking the mount itself. As a side note here, if you're going to be blocking mounts, then I strongly recommend implementing your initial block using DiskArb, with the ES system acting as a "backstop" if DiskArb is bypassed. This post has a more complete write-up, but the short summary is that blocking through DiskArb will reduce the load on your ES client while also providing a better overall user experience (vs. failing the mount directly through ES).
Finally, keep in mind that USB isn't the only storage bus. Thunderbolt, NVMe, and SATA are common, not to mention rarer cases like SAS or SCSI. Trying to block at the bus level means having to predict every possibility and coming up with a unique approach to block each bus, many of which can't easily be blocked. Blocking at the storage layer lets you ignore those issues and block "everything".
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware