Signed Command Line Tool Rejected by spctl

I have a small command-line tool (a service) that gets compiled by Xcode, then I'm signing it during a Run Script phase. When I try to run it, it gets "Killed".

Here's the signing command:

% /usr/bin/codesign -s "Developer ID Application: ..." --keychain "/Users/.../Library/Keychains/login.keychain" --timestamp -f -o runtime --entitlements /Code/.../mytool.entitlements  /Code/.../mytool

I've got an Info.plist and entitlements for it.

% codesign -vvvv ./mytool
./mytool: valid on disk
./mytool: satisfies its Designated Requirement

% codesign -dvv ./mytool
Executable=.../mytool
Identifier=com.myorg.mytool
Format=Mach-O universal (x86_64 arm64)
CodeDirectory v=20500 size=10271 flags=0x10000(runtime) hashes=310+7 location=embedded
Signature size=8952
Authority=Developer ID Application: ...
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Timestamp=Dec 8, 2021 at 6:28:25 PM
Info.plist entries=19
TeamIdentifier=...
Runtime Version=12.0.0
Sealed Resources=none
Internal requirements count=1 size=180

When I run spctl I get:

% spctl -a -v --raw ./mytool
./mytool: rejected (the code is valid but does not seem to be an app)
<?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>assessment:authority</key>
	<dict>
		<key>assessment:authority:flags</key>
		<integer>0</integer>
		<key>assessment:authority:source</key>
		<string>obsolete resource envelope</string>
		<key>assessment:authority:weak</key>
		<true/>
	</dict>
	<key>assessment:cserror</key>
	<integer>-67002</integer>
	<key>assessment:remote</key>
	<true/>
	<key>assessment:verdict</key>
	<false/>
</dict>
</plist>

What's it mean? How can I get this tool to run signed OK? All of this stuff is highly opaque, and the documentation out of date (for example, where it says you can run spctl on /bin/ls, but it gives the same exact error output that /bin/ls isn't an app.)

Note that ultimately this tool gets distributed in a ZIP file that contains Windows and Linux executables also; there is not, and ought not be, any macOS-specific installer... I have a lot of other things to do than jumping through these hoops.

Info.plist:

<?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>CFBundleDevelopmentRegion</key>
	<string>English</string>
	<key>CFBundleExecutable</key>
	<string>mytool</string>
	<key>CFBundleIdentifier</key>
	<string>com.myorg.mytool</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>mytool</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleVersion</key>
	<string>2.0.0</string>
	<key>NSHumanReadableCopyright</key>
	<string>...</string>
</dict>
</plist>

Entitlements:

<?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.disable-library-validation</key>
	<true/>
</dict>
</plist>

First up, spctl is not a great techniques for evaluating whether something will pass Gatekeeper. The approach I recommend is the one described in Testing a Notarised Product.

Second, why are you disabling library validation? The only good reason to do that is that your program loads plug-ins from other third-party developers. Is that the case here?

The reason this matters is that library validation makes it harder to pass Gatekeeper.

Finally, did you notarise your tool. You will need to do this if you want it to run on arbitrary user systems.

See Notarizing macOS Software Before Distribution for background on notarisation. The technique it describes won’t work for a command-line tool, so you’ll need something custom. See Customizing the Notarization Workflow for hints on that front.

Oh, and my Signing a Mac Product For Distribution post has a whole bunch of hints and tips about this overall issue.


Note that ultimately this tool gets distributed in a ZIP file that contains Windows and Linux executables also; there is not, and ought not be, any macOS-specific installer.

That’s fine. The only gotcha is that the user may not be able to double click the tool in the Finder to run it in Terminal. However, if the user runs the tool directly from Terminal, or from a shell script, everything will work out just fine.

Info.plist:

You can add an information property list to a command-line tool (by using the linker’s -sectcreate option to put it in the __TEXT / __info_plist section) but that’s generally not necessary. In most cases it’s fine to ship a command-line tool with no information property list.


I think that’ll be enough for you to make progress but, if you still can’t get it working, post a status update here and I’ll take another look.

Share and Enjoy

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

Library validation was inadvertent, entitlements file pulled from something else. Wasn't notarized (Distracted by docs saying command line tools can't be notarized or stapled). This tool is always run from Terminal or as a service, never double-clicked.

I did this:

% zip -j mytool.zip .../mytool
% xcrun notarytool submit mytool.zip --keychain-profile "my profile" --wait
Successfully received submission info
  createdDate: 2021-12-09T15:20:09.725Z
  id: myid
  name: mytool.zip
  status: Accepted
% xcrun notarytool log --keychain-profile "my profile" myid
{
  "logFormatVersion": 1,
  "jobId": "myid",
  "status": "Accepted",
  "statusSummary": "Ready for distribution",
  "statusCode": 0,
  "archiveFilename": "mytool.zip",
  "uploadDate": "2021-12-09T15:20:11.851Z",
  "sha256": "9d9e6ce02881e78cb9c7a54560b18894a2b4b70716656d29573413cebba64be7",
  "ticketContents": [
    {
      "path": "mytool.zip/mytool",
      "digestAlgorithm": "SHA-256",
      "cdhash": "485e2c31245e1ba4975a05f46556c2f47497be96",
      "arch": "x86_64"
    },
    {
      "path": "mytool.zip/mytool",
      "digestAlgorithm": "SHA-256",
      "cdhash": "156a4fc6de8e02f25e39ed2999b781ca3d88ab2f",
      "arch": "arm64"
    }
  ],
  "issues": null
}

(No stapling as there is none for command line tools, AFAIK.) Now I cd to a test directory and cp .../mytool . then run mytool... Killed. Same error reporting etc as originally with no notarization. This is on my same machine that it won't even run!!! (It runs fine in the original directory that is was signed in --- in the build log there is RegisterExecutionPolicyException step, not sure exactly what controls/starts that.)

Checking the system log, there are entries for each mytool startup:

EXC_BAD_ACCESS (SIGKILL (Code Signature Invalid))
Exception Subtype: UNKNOWN_0x32 at 0x0000000104060000
Exception Codes: 0x0000000000000032, 0x0000000104060000
Exception Note:  EXC_CORPSE_NOTIFY
Termination Reason: CODESIGNING 2 

But the code signing is fine:

% codesign --verify --strict -vvvv --check-notarization ./mytool
./mytool: valid on disk
./mytool: satisfies its Designated Requirement

This stuff just drives me INSANE. Not only are there way too many tools and hoops involved, the diagnostics just aren't acceptable.

If the product is signed, notarised, and stapled correctly, everything should work. If not, you’ll need to investigate why Gatekeeper is unhappy (2), fix that, and then retest.

"Fix that" !!!!!! No! Just no! There MUST be a way for Gatekeeper/spctl to render an exact description of whether or not it will run something (apps, command line tools, ...) on arbitrary customer machines, immediately, right away, from the development machine, bypassing any caching or anything else. How many thousands of 3rd party developer days need to be lost for one Apple developer to spend a few days to update spctl to produce 100% accurate and useful messaging? For example, the messaging out of notarytool was very good! It told me which of the half-dozen hoops I had to jump through next (keychains! app-specific passwords!). It just takes too too long to do the research to figure out what to do at all, only to be confronted with an inexplicable error. $237billion a year, and developers have to guess? Apple can do better. Sorry for the rant but it is necessary.

the diagnostics just aren't acceptable.

I agree. We already have a bug on file requesting better diagnostics (r. 60626159), but you should feel free to file your own.

This tool is always run from Terminal or as a service, never double-clicked.

Cool.

It runs fine in the original directory that [it] was signed in

Hmmm, that’s interesting. Try this:

% cmp /path/to/that/original/copy /path/to/your/new/copy ; echo $?

Does that report any differences?

Checking the system log, there are entries for each mytool startup:

OK. That should generate a crash report. Please grab that and post it here. See Posting a Crash Report for advice on that.

But the code signing is fine:

Unfortunately codesign, like spctl, has only a limited view of the world and thus can’t diagnose all code signing problems )-: Still, while we’re on the subject of codesign, please run the following and post the results:

% codesign -d -vvv --entitlements :- /path/to/your/tool

Share and Enjoy

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

The cmp shows they are identical. It works in its as-compiled location only because Xcode has filed an exception for it... it wouldn't and doesn't otherwise pass Gatekeeper.

The crash report here is basically the same no matter what I've done with signing, notarizing, or nothing... I think it's just showing that external Gatekeeper code is killing the process before it actually starts. I haven't removed anything from the crash report, that's all there is.

Here's what I get from codesign:

% codesign -d -vvv --entitlements :- /Code/project/macOS/mytool
Executable=/Users/frustrated/Code/project/macOS/mytool
Identifier=com.myorg.mytool
Format=Mach-O universal (x86_64 arm64)
CodeDirectory v=20500 size=10271 flags=0x10000(runtime) hashes=310+7 location=embedded
Hash type=sha256 size=32
CandidateCDHash sha256=40b0eb4b737a4d4c2fa86558e5a07ede4c1bb907
CandidateCDHashFull sha256=40b0eb4b737a4d4c2fa86558e5a07ede4c1bb9073f041403a7ce64113f891853
Hash choices=sha256
CMSDigest=40b0eb4b737a4d4c2fa86558e5a07ede4c1bb9073f041403a7ce64113f891853
CMSDigestType=2
CDHash=40b0eb4b737a4d4c2fa86558e5a07ede4c1bb907
Signature size=8951
Authority=Developer ID Application: My Org (G00BLEDY)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Timestamp=Dec 10, 2021 at 4:51:18 PM
Info.plist entries=19
TeamIdentifier=G00BLEDY
Runtime Version=12.0.0
Sealed Resources=none
Internal requirements count=1 size=180
Warning: Specifying ':' in the path is deprecated and will not work in a future release
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>com.apple.security.automation.apple-events</key><true/></dict></plist>

Note that the embedded requirements have been fixed (needs apple-events to open things in the browser on occasion). The spctl -a -v --raw output is still the same. I've seen more transparent bricks.

The cmp shows they are identical.

Good.

It works in its as-compiled location only because Xcode has filed an exception for it... it wouldn't and doesn't otherwise pass Gatekeeper.

Is that comment based on the RegisterExecutionPolicyException build step. If so, be aware that last time I checked this was effectively a no-op. It demonstrates that Xcode is calling through to the Execution Policy framework but that currently has no impact on how system policy is enforced.

Here's what I get from codesign:

Thanks. This looks good.

Note that com.apple.security.automation.apple-events has the com.apple.security. prefix, and thus can be used by any code without being authorised by a provisioning profile. So, whatever is causing this problem it isn’t entitlements.

I haven't removed anything from the crash report, that's all there is.

Thanks for posting the whole log.

My best guess right now is that you’re being bitten by the issue discussed in Updating Mac Software. A good way to test this theory is to restart your Mac. Does that fix the problem?

Share and Enjoy

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

OK I had a nice response here but the forum timed me out after <2 hours and routed me to an error page and killed it with no recovery. How I hate that.

Synopsis: Thanks for pointing out the document and behavior. This is clearly a time-wasting gatekeeper bug. By definition, cache misses cause fetches to backing stores; gatekeeper must revalidate. The document's existence is self-incriminating. I've filed FB9807456 on it. Developers! Developers! Developers!

I've filed FB9807456 on it.

Thanks!

It’s actually super useful for me because your bug has been marked as a dup of the current lead bug tracking this issue, a bug that I hadn’t previously seen.

Share and Enjoy

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

Signed Command Line Tool Rejected by spctl
 
 
Q