Gatekeeper refuses to start application from downloaded DMG

Hello, I have an application which uses a helper[1] to download[2] files. When files download is a DMG and user mounts the image to run the application from this DMG it doesn't pass Gatekeeper. It presents the "Application XYZ.app can't be opened.".

Same file downloaded via Safari shows a different dialog, the "XYZ.app is an app downloaded from the internet. Are you sure you want to open it?"

In the system log I see this line:

exec of /Volumes/SampleApp/SampleApp.app/Contents/MacOS/SampleApp denied since it was quarantined by Download\x20Helper and created without user consent, qtn-flags was 0x00000187

The application is running sandboxed and hardened, the main application has com.apple.security.files.downloads.read-write entitlement. Everything is signed by DeveloperID and passes all checks[3].

I tried to check the responsible process[4] of the helper. Then trivial stuff like download folder access in System Settings/Privacy & Security/Files & Folders. Everything seems to be fine.

For what it worths the value of quarantine attribute is following:

com.apple.quarantine: 0087;6723b80e;My App;

The Safari downloaded one posses:

com.apple.quarantine: 0083;6723b9fa;Safari;02162070-2561-42BE-B30B-19A0E94FE7CA

Also tried a few more ways and got to 0081 with Edge and 0082 with a sample app with similar setup. Not sure if that has any meaning.

What could I be doing wrong that Gatekeeper right away refuses to run the application from DMG instead of showing the dialog like in other cases?


  • [1] The executable is in application bundle located in Contents/Helpers/DownloadHelper.app in the main application bundle.
  • [2] Nothing fancy, curl + regular POSIX file operations
  • [3] codesign, syspolicy_check, spctl
  • [4] launchctl procinfo pid
When files download is a DMG and user mounts the image to run the application from this DMG it doesn't pass Gatekeeper.

I’m presuming that:

  • The user uses the Finder to double click the image in order to mount it.

  • The user then double clicks the app in order to launch it.

If that’s not right, let me know.

If you use the Finder to copy the app off the disk image to somewhere else — say the Applications folder or, if that already have an app of the same name, your home directory — will the app launch from there.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

If you use the Finder to copy the app off the disk image to somewhere else — say the Applications folder or, if that already have an app of the same name, your home directory — will the app launch from there.

It doesn't help. I copied it (drag & drop in Finder) from DMG to ~/Desktop and to /Applications and it shows the same dialog.

The quarantine attribute is preserved no matter what I do with the bundle. Opening the application from context menu (secondary click on the bundle in the Finder) also yields the same result.

OK.

I’d like to try another test:

  1. Using the Finder, copy the app out to Applications folder.

  2. Confirm that it still fails.

  3. Run syspolicy_check on it.

What does it report?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

It says that ticket is missing:

$ syspolicy_check distribution /Applications/VLC.app
App has failed one or more pre-distribution checks.
---------------------------------------------------------------
Notary Ticket Missing
    File: VLC.app 
    Severity: Fatal 
    Full Error: A Notarization ticket is not stapled to this application. 
    Type: Distribution Error 

When I run the stapler on it the error goes away but app still doesn't start:

$ stapler staple /Applications/VLC.app 
Processing: /Applications/VLC.app
Processing: /Applications/VLC.app
The staple and validate action worked!

$ syspolicy_check distribution /Applications/VLC.app
App passed all pre-distribution checks and is ready for distribution.

I ran also the spctl before the stapler:

$ spctl --assess -v /Applications/VLC.app
/Applications/VLC.app: File created by an AppSandbox, exec/open not allowed

I found that this is caused by the helper running in a sandbox. If the helper is in a non-sandbox environment, the downloaded dmg and installed app can be opened correctly.

Is this expected? Are there any solutions?

It says that ticket is missing:

OK. That wouldn’t be enough to cause this problem, and your stapling test confirms that.

I found that this is caused by the helper running in a sandbox.

Right. That’s where I was going next.

Is your product distributed on the Mac App Store? Or have you sandboxed it because that’s the right thing to do?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Is your product distributed on the Mac App Store? Or have you sandboxed it because that’s the right thing to do?

It's not distributed on Mac App Store (but it may eventually get there). But we do the sandboxing because it's right thing to do given the app is basically a web browser in disguise.

It’s tricky for sandboxed apps to create code. This makes sense when you view sandboxing from the perspective of the Mac App Store, because the App Store specifically prohibits apps from downloading code.

When you opt in to the App Sandbox, you can use temporary exception entitlements to bypass many of its restrictions. One of those is com.apple.security.files.user-selected.executable, which allows you to create executables that can pass Gatekeeper. Honestly, I don’t have a lot of experience with this, but I know folks who work on IDE-ish products have used it successfully.

And if that doesn’t work for you then the next best option is to un-sandbox the code that creates this disk image. My standard approach for that is to embed an XPC service in the app, because the embedded XPC service can have a custom sandbox, including no sandbox.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you Quinn,

I tried the com.apple.security.files.user-selected.executable and it didn't help.

And if that doesn’t work for you then the next best option is to un-sandbox the code that creates this disk image. My standard approach for that is to embed an XPC service in the app, because the embedded XPC service can have a custom sandbox, including no sandbox.

None of these is actually an option for us. We would be eaten alive by security folks in the company if we would disable sandbox.

I was looking into it more and dumped the kCFURLQuarantinePropertiesKey:

{
    LSQuarantineAgentName = "Download Helper";
    LSQuarantineIsOwnedByCurrentUser = 1;
    LSQuarantineTimeStamp = "2024-11-11 16:36:18 +0000";
    LSQuarantineType = LSQuarantineTypeSandboxed;
}

That LSQuarantineTypeSandboxed caught my attention and found an older post 650470 stating:

That type indicates that the process was touched by a sandboxed process that didn't have permissions to write that file.

So maybe it's a same problem like with location services, which don't (or don't used to) consult responsible process for entitlements or something. What entitlements this could be related to, the hardened runtime stuff maybe?

I tried the com.apple.security.files.user-selected.executable and it didn't help.

What exactly did you try?

Note the user-selected in that entitlement. It’s meant to be used in conjunction with the save panel. Did you try that?

Still, I don’t think this is appropriate for your use case, so this is definitely an experiment rather than something I’d recommend as a solution.

That LSQuarantineTypeSandboxed caught my attention

I don’t think that tells you much. That’s just the high-level expression of bit 1 (that is, 0x02) of the quarantine flags. It tells you that the file was created by a sandboxed app.

OTOH, bit 2 (0x04) is likely relevant to your issue. That’s the source of the “without user consent” message you referenced earlier and AFAICT it’s presence will prevent the system from running that code.

I’m not entirely sure how this flag is getting set on your executable. I tried various mechanism to get it set in a test app, and failed.

My understanding is that:

  • This flag is set on the executable you’re trying to run.

  • That executable exists on a disk image.

If so, what does the com.apple.quarantine attribute look like for:

  • The root of the disk image?

  • The .dmg file itself?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I’m not entirely sure how this flag is getting set on your executable. I tried various mechanism to get it set in a test app, and failed.

Me either, I'm trying to re-create the problem in isolated form, but the quarantine flag is different.

My understanding is that: This flag is set on the executable you’re trying to run. That executable exists on a disk image. If so, what does the com.apple.quarantine attribute look like for: The root of the disk image? The .dmg file itself? [/quote]

Just after I download the image it looks like:

 $ xattr -l /Users/luweber/Downloads/vlc-3.0.21-intel64.dmg 
com.apple.macl: 
com.apple.metadata:kMDItemWhereFroms: bplist00?_?https://[redacted]/download?id=abbd13b7-f1b7-4fd2-9e51-72e3d790ad83
                                                                     ?
com.apple.quarantine: 0087;67345866;Download Helper;

Then I mount it, but image root, nor the app bundle has a quarantine flag:

$ xattr -l /Volumes/VLC\ media\ player                     
com.apple.FinderInfo: 
$ xattr -l /Volumes/VLC\ media\ player/VLC.app             
$ xattr -l /Volumes/VLC\ media\ player/VLC.app/Contents/MacOS/VLC

Finally when I copy (drag&drop in Finder) application to /Applications:

$ xattr -l /Applications/VLC.app 
com.apple.quarantine: 0187;67345866;Download\x20Helper;
Just after I download the image it looks like:

OK. The 0x40 flag is set there, so this is clearly related to your download. You mentioned earlier than you’re doing this with “curl + regular POSIX file operations”. Is that the curl API? Or are you invoking the curl tool?

Also, how are you running Download Helper? As a child process? That is, using fork/exec*, posix_spawn, NSTask, or Swift’s Process? Using NSWorkspace? Or something else?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Gatekeeper refuses to start application from downloaded DMG
 
 
Q