MacOS codesignature invalid after adding entitlements

I'm signing a small command line TCP listener for Mac M1 which usually signs and notarizes correctly using the following command:

codesign --sign $IDENTITY --options runtime --timestamp server/executable

In order to enrich the features of our listener we may need to load JVM library from Oracle, in order to be allowed to load a third party dyamic library I've introduced an entitlements plist file called macos-entlist.plist as follows:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.cs.allow-jit</key>
    <false/>
    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
    <false/>
    <key>com.apple.security.cs.disable-executable-page-protection</key>
    <false/>
    <key>com.apple.security.cs.allow-dyld-environment-variables</key>
    <true/>
    <key>com.apple.security.cs.disable-library-validation</key>
    <true/>
    <key>com.apple.security.get-task-allow</key>
    <false/>
</dict>
</plist>

And changed my codesign command as follows:

codesign --sign $IDENTITY --entitlements macos-entlist.plist --options runtime --timestamp server/executable

After adding the entitlements feature to my codesigning and notarization logic my ZIP file still passes the notarization phase but when I try to run executable I get an error message saying that executable cannot be opened because the developer cannot be verified.

The issue happens regardless the contents of macos-entlist.plist (eg: even setting all the values to false I still get the error) it seems that that the mere introduction of the parameter --entitlements macos-entlist.plist causes the executable to not be valid anymore despite the fact that the notarization phase succeeds.

Curiously other command line executable files in my ZIP signed with the same codesign command are still running fine.

Do you have any suggestions on how I can correctly introduce entitlements?

Replies

Your entitlements are disabling library validation, which is a common cause of Gatekeeper problems. If you remove com.apple.security.cs.disable-library-validation from your entitlements and run through the whole process again, does it pass Gatekeeper?

It’ll probably fail to run, because you do need to disable library validation in order to load that third-party library, but this should confirm that disabling library validation is the root cause of this issue.

<key>com.apple.security.cs.allow-jit</key>
<false/>
… 
<key>com.apple.security.get-task-allow</key>
<false/>

Oh, just as an aside, these are the default values and, per the discussed on the Hardened Runtime page, it’s a bad idea to set entitlements to their default values.

Share and Enjoy

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

Dear Mr.Quinn,

thank you for your reply.

In fact you are right, I've changed my entitlements file to:

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>com.apple.security.cs.allow-dyld-environment-variables</key>
        <true/>
</dict>
</plist>

And now the executable is signed and starts correctly. My problem here is that I really need to enable com.apple.security.cs.disable-library-validation in order to load the third party dynamic library from Oracle.

Is there a way to understand why I can't enable com.apple.security.cs.disable-library-validation? Am I missing something?

Is there a way to understand why I can't enable [library validation]?

Sure. The above was just a diagnostic test, and it’s done it’s job. Now we need to fix the real problem.

Gatekeeper problems that go away when you stop disabling library validation are almost always caused by dangling load commands. See Resolving Gatekeeper Problems Caused by Dangling Load Command Paths for all the gory details.

Share and Enjoy

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

Dear Mr.Quinn,

thank you very much for your prompt reply.

I've tried to follow Resolving Gatekeeper Problems Caused by Dangling Load Command Paths as you suggested but I'm unable to find any helpful message in the system logs to help me determine if there are any dangling paths. I admit I'm not very familiar with the MacOS interfaces as I normally work with terminals so I may not be able to find the message myself.

Anyways in order to pass the signing and notarization phase I've changed all the relative library dependencies to @rpath, here is the otool -L output of our executable:

% otool -L executable
executable:
        @rpath/libexecore.dylib (compatibility version 0.0.0, current version 0.0.0)
        /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 904.4.0)
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1291.0.0)

% otool -l executable | grep -B 1 -A 2 LC_RPATH
Load command 16
          cmd LC_RPATH
      cmdsize 32
         path @loader_path (offset 12)

I'm wondering if that @rpath/libexecore.dylib is correct, for sure without com.apple.security.cs.disable-library-validation everything is loaded correctly.

As the code in our libexecore.dylib includes an elevated number of dlopen() operations (which may be run optionally) I wonder if these may be a problem as well.

Is there a simpler way to understand why my executable is rejected with com.apple.security.cs.disable-library-validation?

Hmmmm, I’d like to take a step back here. Is this “JVM library from Oracle” something that you ship as part of your product? Or something that the user installs by themselves?

Also, is it installed at a fixed location? Or does the user supply location, for example, by selecting it using the open panel or add a file path to a prefs file?

Share and Enjoy

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

Dear Mr.Quinn,

thank you very much for your help here.

Here is a brief description of our configuration.

The JVM library is shipped with the Java SDK by Oracle which we don't distribute in our bundle. Java SDK can be installed as dmg format or extracted from .tar.gz format in an arbitrary location.

Unfortunately given the fact that it can be installed in arbitrary locations the user configures its Java SDK location trough a text configuration file loaded by our executable. Our executable will load the text configuration file and tries to load the JVM library as specified in the configuration through a dlopen() call.

NOTE: FYI as mentioned in my previous message our code contains several other dlopen() calls to open addon libraries that may be developed by our developers, by third party developers or even from our users. In my testing case I don't explicitly load libraries but the dlopen() calls are compiled an may be optionally called depending on the configuration file.

Again, thank you very much for your help.

So, the fact that you need to load a library signed by another developer mean that you must disable library validation. There’s no debate about that.

By disabling library validation you open yourself up to dangling load command problems. However, those are less of a worry than I originally thought because you’re opening the library using dlopen. The dangling load command check done by Gatekeeper is a static check. It doesn’t, indeed it can’t, check the path you pass to dlopen. So your overall goal should be feasible.

As to what’s causing your Gatekeeper issue, I don’t think I’ll be able to work that out given the amount of time I have to spend here on DevForums. I’m going to recommend that you open a DTS tech support incident so that I can look at your issue in depth.

Share and Enjoy

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