Code Signing Auxiliary Executables

Author: Samuel Williams When: Wednesday, 17 October 2012

I recently updated one of my projects Library Inspector for OS X 10.8. After a month of waiting, it was finally rejected (!#@$%!@) due to problems with the code signing entitlements. The Apple Reviewer was very vague about the problem which caused additional frustrations (%!@#$%) but after searching I found a very helpful application RB App Checker Lite which can check the certificates, entitlements and other details of your application likely to cause problems.

I actally emailed Rainer Brockerhoff since I figured he'd have a lot of experience (and I gave him a free copy of Library Inspector to sweeten the deal). He told me that for NSTask based task invocation I'd need to encode the entitlements directly into the binaries, which I wasn't doing.

To make things even more complex, the binaries I am including in the application bundle are actually copied from Xcode (nm, otool, c++filt, etc) and class-dump (a fantastic tool by the way!). So, I am ripping out the existing Apple signatures and adding my own. This is because you can't submit other people's code signed binaries to the Mac App Store.

Anyway, here is what I ended up with:

Application Entitlements

This is the main entitlements file for Library Inspector.

<?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.app-sandbox</key>
	<true/>
	<key>com.apple.security.files.user-selected.read-write</key>
	<true/>
</dict>
</plist>

Auxiliary Executable Entitlements

This is the entitlements used for the auxiliary binaries.

<?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.app-sandbox</key>
	<true/>
	<key>com.apple.security.inherit</key>
	<true/>
</dict>
</plist>

Of particular importance is the com.apple.security.inherit property. When Library Inspector, itself running in a sandbox due to com.apple.security.app-sandbox invokes tasks using NSTask, it will itself run in a sandbox and inherit the permissions of Library Inspector so it can read any relevant input files (normally provided as arguments).

Code Signing Script

This Ruby script is used as a build phase within Library Inspector and signs the auxiliary executables if a signature was specified. For your own application you'd need to update the list of tools and the identifier base.

Dir.chdir ENV['BUILT_PRODUCTS_DIR']

# These tools are copied directly to the built products directory in a previous copy files build phase:
tools = ['class-dump', 'otool', 'nm', 'c++filt']
cert = ENV['CODE_SIGN_IDENTITY']

# The base identifier for the tools:
identifier_base = 'nz.co.oriontransfer.Library-Inspector.tools'

# The path to the auxiliary executable entitlements file:
entitlements = File.join(ENV['PROJECT_DIR'], 'Auxiliary Executable.entitlements')

tools.each do |tool|
	full_path = File.join(ENV['EXECUTABLE_FOLDER_PATH'], tool)
	
	# The full (new) identifier for the executable:
	identifier = identifier_base + '.' + tool
	
	# Use the codesign tool to embed a new signature and entitlements:
	system('codesign', '--entitlements', entitlements, '-i', identifier, '--force', '-s', cert, full_path)
end

Now I just have to submit the application and wait for a month for Apple to review it... Fingers crossed that everything is fine this time through the system.

Addendum, March 2013: Quicklook Plugins

I found that there are some issues when the above approach is used with Quicklook Plugins. I found that my external processes were crashing with the following type of error:

Process:         nm [45806]
Path:            /Applications/Developer/Library Inspector.app/Contents/Library/QuickLook/Library Inspector Quick Look.qlgenerator/Contents/MacOS/nm
Identifier:      nm
Version:         ???
Code Type:       X86-64 (Native)
Parent Process:  sh [45805]
User ID:         502

Date/Time:       2013-03-09 19:10:04.506 +1300
OS Version:      Mac OS X 10.8.2 (12C60)
Report Version:  10

Crashed Thread:  0  Dispatch queue: com.apple.main-thread

Exception Type:  EXC_BAD_INSTRUCTION (SIGILL)
Exception Codes: 0x0000000000000001, 0x0000000000000000

Application Specific Information:
XPC domain creation failed: Process is not in an inherited sandbox.

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libxpc.dylib                  	0x00007fff8678424a _xpc_runtime_init_once + 1077
1   libdispatch.dylib             	0x00007fff8cb2f0b6 _dispatch_client_callout + 8
2   libdispatch.dylib             	0x00007fff8cb2f041 dispatch_once_f + 50
3   libxpc.dylib                  	0x00007fff8677b919 _xpc_runtime_set_domain + 190
4   libxpc.dylib                  	0x00007fff8677ae90 _libxpc_initializer + 430
5   libSystem.B.dylib             	0x00007fff8c78bb2d libSystem_initializer + 172
6   dyld                          	0x00007fff61f38378 ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&) + 236
7   dyld                          	0x00007fff61f38762 ImageLoaderMachO::doInitialization(ImageLoader::LinkContext const&) + 46
8   dyld                          	0x00007fff61f3506e ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&) + 380
9   dyld                          	0x00007fff61f34fc4 ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&) + 210
10  dyld                          	0x00007fff61f34eba ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 54
11  dyld                          	0x00007fff61f26fc0 dyld::initializeMainExecutable() + 207
12  dyld                          	0x00007fff61f2ab04 dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 3060
13  dyld                          	0x00007fff61f26397 dyldbootstrap::start(macho_header const*, int, char const**, long, macho_header const*, unsigned long*) + 761
14  dyld                          	0x00007fff61f2605e _dyld_start + 54

<...snip...>

I narrowed it down to the entitlements. It turns out that the Quicklook Plugin Host doesn't seem to run in a typical sandbox (as far as I can tell), and thus trying to use the inherit entitlement doesn't work. Simply removing the entitlements file arguments ('--entitlements', entitlements) from the above code signing script fixes the issue. This works because the executable no longer tries to inherit sandbox permissions from the parent, but I'm not exactly sure why this is an issue. It seems like this is only affects Mac OS X 10.8.2+ but I haven't had a lot of experience with this problem yet.

Comments

Thanks for posting this! I just had an app update rejected because I don’t have entitlements declared for some auxiliary command line apps bundled with the main app. Now I know how to fix them.

Thank you for this detailed post! It really cleared things up for me. (Also, now Apple actually scans your app immediately after you submit and emails you if you have unsigned binaries, so I was able to catch this very quickly.)

Leave a comment

Please note, comments must be formatted using Markdown. Links can be enclosed in angle brackets, e.g. <www.codeotaku.com>.