I've been trying to use Keychain from a Daemon for some time now. In the end, I managed to have the System Keychain work for my application and I moved to work on other parts.
I finally went back to dealing with Keychain, but the code I wrote before stopped working. Even the application I wrote to test things out stopped working for me, and now it gives the The authorization was denied.
error.
To give more perspective into what I am doing, I am running a Sandboxed Launch Daemon wrapped in an App-like structure. I register it from my main app via SMAppService API. I also have a System Extension.
My test app was structured in the same way and I used the following code to put a new key into the System Keychain and get its reference:
var err: Unmanaged<CFError>?
let access = SecAccessCreateWithOwnerAndACL(getuid(), getgid(), UInt32(kSecUseOnlyUID | kSecHonorRoot), nil, &err)
if let err = err {
log.error("Failed to create SecAccess: \(err.takeUnretainedValue().localizedDescription)")
}
let request = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: account,
kSecValueData: passwordData,
kSecAttrAccess: access as Any,
kSecAttrSynchronizable: false,
kSecUseDataProtectionKeychain: false,
kSecReturnPersistentRef: true,
] as [String: Any]
var result: CFTypeRef?
let status = SecItemAdd(request as CFDictionary, &result)
The goal of this was to share some secrets with a System Extension.
The code above worked for me some time ago and I was able to use the System Keychain from my sandboxed daemon.
Am I missing something again? Did something change in the meantime? Or did I do something last time that I haven't noticed?
Should I cut my losses and avoid Keychain since Apple will not support it anyway?
OK, some factoids…
Your daemon cannot use the data protection keychain. Maybe some day, but not right now (as of macOS 15, which is currently in beta).
Your daemon may or may not be sandboxed.
To run sandboxed your daemon must be have a Info.plist
with a bundle ID. You can either:
-
Embed that in the
__TEXT
/__info_plist
section, typically using the Create Info.plist Section in Binary build setting. -
Put the daemon in an app-like wrapper, as described in Signing a daemon with a restricted entitlement
I generally favour the latter because it offers more flexibility. However, the former is required if you’re installing the daemon using the legacy SMJobBless
API.
If you’re using SMAppService
to install your daemon and the containing app is sandboxed then the daemon must be sandboxed [1].
In general, the App Sandbox prevents you from writing to the System keychain (unless you’re an NE sysex). You can get around this using a temporary exception entitlement (com.apple.security.temporary-exception.files.absolute-path.read-write
). See this thread for more context.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] This is a new security restriction added in macOS 14.2.