System-wide keychain item sharing in a Launch Daemon

My goal is to install a private key in the Keychain on macOS securely in such a way that only my app and its Network Extension can access the same key from any user on the machine. This is not an App Store app; it is installed via an Installer and additionally uses a privileged Launch Daemon XPC service to perform privileged operations during the app's lifetime.

Obviously, the Network Extension is not capable of installing such a key due to its sandbox. Currently, my app adds the key to the System Keychain via the privileged helper and uses Keychain ACLs (through SecTrustedApplicationCreateFromPath) to permit only the extension and the main app to access the key. This works fine.

Unfortunately, SecTrustedApplicationCreateFromPath is deprecated as of macOS 10.15, and so I'm trying to use its replacement, Keychain Sharing/kSecAttrAccessGroup.

I have followed this tutorial to allow my Launch Daemon to use Provisioning Profiles so that I can enable Keychain Sharing for the daemon. This works okay, as far as I can tell.

However, when I attempt to use kSecUseDataProtectionKeychain, a prerequisite to using kSecAttrAccessGroup, to generate the key (with SecKeyCreateRandomKey), I get this error:

Code Block
Error Domain=NSOSStatusErrorDomain Code=-25291 "failed to generate asymmetric keypair" (errKCNotAvailable / errSecNotAvailable: / No trust results are available.) UserInfo={NSDescription=failed to generate asymmetric keypair}

The same code works fine when running as a regular user application, so I assume this is an issue with using keychain protection from a non-user environment. I have not been able to find any solution to this error.

To make matters worse, I have tested regular applications using kSecUseDataProtectionKeychain+kSecAttrAccessGroup and although they work perfectly within a single user account, they do not function across user accounts, even if I run them as root and explicitly use the System Keychain with SecKeychainSetPreferenceDomain(.system). Therefore, I assume keychain protection on macOS is a feature of the login keychain, which also explains why it does not work in a launch daemon.

Am I missing something? Or is what I need impossible using only non-deprecated Keychain features? I can probably resort to storing the key in a regular file which only root/my XPC service can read and having it serve the key to my app and its extension, but I'd prefer to use Keychain if possible.

Thank you.

Accepted Reply

The problem with using the iOS-style keychain is that you have various components running as different users and each user gets their own iOS-style keychain. So you can share items between, say, an app and an appex, but you won’t be able to share items between an app and a daemon.

If you already have a launchd daemon, why not have the other code use XPC to request that the daemon do keychain operations on their behalf?

Share and Enjoy

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

Replies

The problem with using the iOS-style keychain is that you have various components running as different users and each user gets their own iOS-style keychain. So you can share items between, say, an app and an appex, but you won’t be able to share items between an app and a daemon.

If you already have a launchd daemon, why not have the other code use XPC to request that the daemon do keychain operations on their behalf?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
Okay, the iOS-style keychain being per-user is what I thought might be happening. Good to know, so I don't keep trying to make that work. I was able to get my Network Extension to get the key from Keychain through the XPC service instead. Thanks!

Note to anyone else: To allow a Network Extension, or any sandboxed app, to access a privileged XPC service, it needs the com.apple.security.temporary-exception.mach-lookup.global-name temporary entitlement. Documentation for this entitlement here. This entitlement may make it "tricky" to get an app into the Mac App Store, but for self-published ("Developer ID") apps its okay. (Oh also, since its somewhat relevant, info on securing a privileged XPC service here.)