Can you tell whether code has been notarized?

Of all the changes in macOS 10.15 which we’re likely to hear about at WWDC next week, one of the most certain is the requirement for notarization of all apps distributed over the Internet but outside the App Store. It’s already upon us: as of 7 April, all new and updated kernel extensions are required to be notarized, as are apps from new developers who haven’t previously signed apps.

The recent problems in installing VirtualBox 6.0.8 in macOS 10.14.5 have raised an important question: how can we tell whether a code bundle has been notarized? If you do hit problems when trying to install and run software which should be notarized, how can you know whether it has been correctly notarized in the first place?

For regular apps, this shouldn’t be too difficult. If you’ve just downloaded them and try to open them, you should see the new variant of the ‘Gatekeeper’ dialog. If the app hasn’t picked up a quarantine flag, you can easily add one using my free tool xattred.

notarized02

You can also look inside the app: the notarization ‘ticket’ is seen as a file named CodeResources in the Contents folder. But that only applies to apps which have gone through the hardening and notarization process from new.

Developers are also encouraged to notarize older releases, in which case the ticket may not be stapled to the code bundle at all. If you’re looking at a kernel extension (as I have been when working out what the problem was with VirtualBox), you can’t run the code bundle as an app, so won’t see the modified Gatekeeper dialog either.

For apps, you can drag and drop them onto my free utility Taccy, which works whether or not the ticket has been stapled.

taccy11

But (wisely) Taccy only works with apps, not with any other form of executable code like kernel extensions. So it’s off into Terminal, armed with three examples: a regular notarized app complete with its stapled ticket, Jeff Johnson’s open source Bonjeff.app which has had retrospective notarization applied to a non-hardened runtime so has no ticket, and one of the kernel extensions from VirtualBox 6.0.8.

codesign

The standard code signing tool codesign doesn’t look to be helpful, but can be made to check that code bundles have been notarized using a command of the form
codesign --test-requirement="=notarized" --verify --verbose myapp.app
but its response doesn’t fill you confidence:
myapp.app: valid on disk
myapp.app: satisfies its Designated Requirement
myapp.app: explicit requirement satisfied

It’s the last of those three lines which is relevant here, that the explicit test requirement has been satisfied, meaning that the app being tested is notarized. We’re then left with stapler, which comes as part of the Xcode tools and isn’t part of macOS, and spctl, the security assessment tool which comes with macOS so is available to all users.

stapler

Using a command of the form
xcrun stapler validate myapp.app
is Apple’s recommended method for developers to check that notarization has been successful, and every normally-notarized app passes that check. Unfortunately, it doesn’t work when there’s no notarization ticket stapled to the bundle. Bonjeff and the kernel extension return
Bonjeff.app does not have a ticket stapled to it.
VBoxDrv.kext does not have a ticket stapled to it.

It’s always nice to be told what you already know, but in those cases stapler is clearly of no use at all.

spctl

The standard call for spctl to perform a security assessment on an app, as used by Taccy, is
spctl -a -v myapp.app
and that consistently returns reliable results on regular notarized apps complete with their stapled tickets. You can also increase the information returned by augmenting the v option to
spctl -a -vv myapp.app

This works fine for Bonjeff.app, despite its missing ticket:
Bonjeff.app: accepted
source=Notarized Developer ID
origin=Developer ID Application: Jeff Johnson (8LT69JF8NZ)

Unfortunately, pass it the code bundle for a kernel extension, and it no longer wants to help:
VBoxDrv.kext: rejected (the code is valid but does not seem to be an app)
origin=Developer ID Application: Oracle America, Inc. (VB5E2TV963)

Instead, you have to pretend to spctl that this isn’t executable code at all, but an Installer package. Use a command like
spctl -a -vv -t install mykext.kext
and you should see a report such as
VBoxDrv.kext: accepted
source=Notarized Developer ID
origin=Developer ID Application: Oracle America, Inc. (VB5E2TV963)

Further experimentation shows that using the -t install option also works fine for regular apps, so I recommend as a default command using
spctl -a -vv -t install bundlename
to check all code bundles, whether they’re apps, kexts, or whatever.

I suspect that we’ll never know for sure whether this failure occurred with Oracle or Apple, but it isn’t a good start for Apple’s plan to make notarization mandatory.

What to use?

At present, nearly a year after Apple introduced its notarization service, and just days before the first betas of 10.15 are provided to developers, tools to reliably report whether any code bundle has been notarized are messy at best. This is easier for apps, where you can use Taccy or spctl in Terminal, but stapler is of no use if an app has been notarized but the ticket isn’t stapled to its bundle. Quite why spctl should have to look at a kernel extension as an Installer package I simply can’t fathom, and that quirk isn’t documented: spctl‘s man page is dated 19 January 2012.

Let’s hope that Apple does something about this next week.

Thanks to Jeff Johnson for raising this, and for the free loan of Bonjeff.app. I am particularly grateful to Randy Saldinger for solving the riddle of how to get spctl and codesign to examine kernel extensions.