Mounting an external disk from a sandboxed app

I’m attempting to make an app that uses Disk Arbitration to intercept a disk mount (by creating and returning a dissenter in the appropriate callback) and then mount the disk with certain options (specifically, read-only, nobrowse, or both, depending on user options). For example:

DADiskMountWithArguments(disk, nil, DADiskMountOptions(kDADiskMountOptionDefault), nil, nil, kReadOnly)

…where kReadOnly is a pointer to an array only containing a “rdonly” CFString.

While DADiskMountWithArguments seems to be usable in a sandboxed app for disk images, it doesn’t work when the disk is an external disk (e.g. connected via USB). I see lines like this in Console.app when this happens:

Sandbox denied authorizing right 'system.volume.external.mount' by client '/path/to/exe' [17934] (engine 580)

I’ve identified two workarounds that allow this to work in a sandbox, but both have their own problems:

  1. If a LaunchDaemon (even a sandboxed one, which is required for registration with SMAppService.daemon from the sandboxed app) does the call to DADiskMountWithArguments, it will succeed. But App Store policies don’t allow escalation to root.
  2. If I use the undocumented entitlement com.apple.security.temporary-exception.sbpl with a value of (allow authorization-right-obtain (right-name "system.volume.external.mount")), the mount works without escalation to root. But I understand that App Review is likely to reject the use of this entitlement, and that this entitlement isn't supported to begin with.

Specifically, these are the behaviors I see on macOS Sequoia 15.3.1 and Xcode 16.2.

Since I would like to try to publish this app on the App Store, neither of these seem like acceptable solutions.

I don’t see why this should be restricted if the sandboxed app is not declaring a special path (i.e. the path in DADiskMountWithArguments is set to nil) and still does not have access to the mounted filesystem - am I missing something/is there a way to accomplish this?

While DADiskMountWithArguments seems to be usable in a sandboxed app for disk images, it doesn’t work when the disk is an external disk (e.g. connected via USB). I see lines like this in Console.app when this happens:

As a minor aside, the issue isn't purely the "sandbox" a such. DiskArb actually constructs authorization rights for all the various mount types and then authorizes those rights before performing each operation. If you're curious you can find the code for this here.

External disks fail for sandbox'ed app because the sandbox doesn't allow this kind of authorization extension, but you could actually configure the system to restrict this fairly arbitrarily.

...seems to be usable in a sandboxed app for disk images...

The exception here is actually that the authorization system doesn't actually restrict "system.volume.virtual", so AuthorizationCopyRights always succeeds.

I’ve identified two workarounds that allow this to work in a sandbox, but both have their own problems:

Both of these are policy decisions App Review will need to make, however, I will say that it's possible that #1 might be allowed (and less likely that #2 will).

In terms of other options, you can also do this:

mount the disk with certain options (specifically, read-only, nobrowse, or both, depending on user options).

Through the "diskutil" command line tool. I can't say I'm optimistic that it will work, however, it's probably worth at least trying.

I don’t see why this should be restricted if the sandboxed app is not declaring a special path (i.e. the path in DADiskMountWithArguments is set to nil) and still does not have access to the mounted filesystem

It's certainly worth filing an enhancement request asking for the sandbox to handle this better.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thanks for the insights! I'm surprised to see that the relevant code is open-source. I'm also surprised, but pleased, to hear that my first workaround isn't guaranteed to be hopeless; I'll give it a try and see what App Review thinks.

the "diskutil" command line tool

Unfortunately, this didn't seem to get around the issue. I tried

let process = Process()
process.executableURL = URL(filePath: "/usr/sbin/diskutil")
process.arguments = ["mount", "readOnly", diskBSDName]
try? process.run()

which had the expected behavior when App Sandbox was disabled but did not work when App Sandbox was enabled.

It's certainly worth filing an enhancement request asking for the sandbox to handle this better.

I filed FB16728800 and attached a sample project to it.

Mounting an external disk from a sandboxed app
 
 
Q