This morning I had some time to research this further on my own.
I wrote a simple command line app that reads and writes to Keychain random strings. I sandboxed it to represent better what I am doing in my other application.
From my user, it worked perfectly. It read and wrote keys to my login keychain just fine.
But from root, it did not work. It failed with the same The authorization was denied. (-60005) error.
I looked back into Console logs and found this:
Sandbox: KeychainTest(1166) deny(1) authorization-right-obtain system.keychain.create.loginkc
There were also logs from authd similarly saying that sandbox denied authorization.
From what I can understand, it looks like it tries to obtain rights to create a login keychain (create.loginkc)? This is just my guess. I'm probably wrong.
It looked to me that it was trying to access something completely different, so just for a sanity check, I decided to try to explicitly open and use the System Keychain.
This worked perfectly.
For some reason, SecItemAdd is no longer trying to access the System Keychain. To fix my issue, all I had to do, was add this code:
var keychain: SecKeychain?
let openStatus = SecKeychainOpen("/Library/Keychains/System.keychain", &keychain)
guard openStatus == errSecSuccess else {
throw KeychainError.underlyingSecurityError(openStatus)
}
// This is an example. I handle this like the SecAccess object
request[kSecUseKeychain] = keychain as Any
Previously I didn't have to do this. I didn't have to explicitly open the System Keychain to use it. I have no idea what changed, but this put a major wrench in my work.
I understand that the file-based keychains are "deprecated" but to change how SecItem handles them on the fly like this is a bit annoying.
As a note, wrapping a Daemon in an app bundle helps it access the System Keychain. If I didn't do that, I would need to add the com.apple.security.temporary-exception.files.absolute-path.read-write entitlement for the /Library/Keychains/ directory since Sandbox would deny temporary file creation there.
TLDR
Open the System Keychain explicitly, do not let SecItem choose what to use, even if it worked previously.
Post
Replies
Boosts
Views
Activity
Hi!
Thanks for the excellent insight on what SMAppService does!
All it cares about is whether or not the target exists. The fact that most daemon's haven't been insides app bundles is ENTIRELY a matter historical accident/choice, NOT because of any requirement, benefit, or even formal "choice".
This is true. I've put my Daemon in an app bundle out of choice. I technically do not need to do that, but I found that it will make a few things easier.
I just wanted to be extra clear about that, since it looks like this isn't that common or recommended.
The sandbox side of this is even more confusing, since launchd.plist's can be other tools on the system or even shell script. You can have a daemon that's sandbox'd, but nothing in launchd requires (or even cares) about that, as the sandbox is well outside of launchd's area of responsibility.
The reason why I have my Daemon sandboxed is this:
If I try to register a daemon that is not sandboxed (irrelevant if it is in an app bundle or not) from a sandboxed application I get this error:
Error Domain=SMAppServiceErrorDomain Code=1 "Operation not permitted" UserInfo={NSLocalizedFailureReason=Operation not permitted}
After looking at Console, I also see this:
-[BTMManager registerLaunchItemWithAuditToken:type:relativeURL:configuration:uid:]_block_invoke: error: sandbox required
So I think this isn't a SMAppService requirement, but BackgroundTaskManagment's.
...since it's difficult for a process to create a keychain entry that it can't read. Is your daemon creating an entry that it's unable to retrieve? If so, what's the error it's getting and what API is it getting the error from?
When trying to use the System Keychina from the Daemon, I can see this log:
2024-07-23 16:57:01.577 A Daemon[97140:bb0991] (Security) SecItemCopyMatching
2024-07-23 16:57:01.577 A Daemon[97140:bb0991] (Security) SecItemCopyMatching_ios
2024-07-23 16:57:01.577 Df Daemon[97140:bb0991] [com.apple.securityd:secitemratelimit] Not internal release, disabling SIRL
2024-07-23 16:57:01.577 Df Daemon[97140:bb0991] [com.apple.securityd:keychain] Enabling System Keychain Always due to platform
2024-07-23 16:57:01.577 Df Daemon[97140:bb0991] [com.apple.xpc:connection] [0x6000025c8000] activating connection: mach=true listener=false peer=false name=com.apple.securityd.xpc
2024-07-23 16:57:01.577 Df Daemon[97140:bb0991] [com.apple.xpc:connection] [0x6000025c8000] failed to do a bootstrap look-up: xpc_error=[3: No such process]
2024-07-23 16:57:01.577 Df Daemon[97140:bb0991] [com.apple.xpc:connection] [0x6000025c8000] invalidated after a failed init
2024-07-23 16:57:01.577 Df Daemon[97140:bb0991] [com.apple.securityd:SecCritical] Failed to talk to secd after 4 attempts.
2024-07-23 16:57:01.577 Df Daemon[97140:bb0991] [com.apple.xpc:connection] [0x6000025cc0f0] activating connection: mach=false listener=false peer=false name=com.apple.security.XPCKeychainSandboxCheck
2024-07-23 16:57:01.608 Df Daemon[97140:bb0991] [com.apple.xpc:connection] [0x6000025cc0f0] invalidated after the last release of the connection object
2024-07-23 16:57:01.608 A Daemon[97140:bb0991] (CoreFoundation) Loading Preferences From System CFPrefsD
2024-07-23 16:57:01.611 Df Daemon[97140:bb0991] [com.apple.securityd:mds] Recording an MDS plugin: /System/Library/Security/ldapdl.bundle {87191ca6-0fc9-11d4-849a-000502b52122}
2024-07-23 16:57:01.611 Df Daemon[97140:bb0991] [com.apple.xpc:connection] [0x6000025f8000] activating connection: mach=true listener=false peer=false name=com.apple.analyticsd
2024-07-23 16:57:01.612 Df Daemon[97140:bb0991] [com.apple.securityd:mds] Recording an MDS plugin: /System/Library/Frameworks/Security.framework {87191ca0-0fc9-11d4-849a-000502b52122}
2024-07-23 16:57:01.615 Df Daemon[97140:bb0991] [Daemon:general] Password ref missing, setting new to: vhncUYQr
2024-07-23 16:57:01.615 A Daemon[97140:bb0991] (Security) SecItemAdd
2024-07-23 16:57:01.615 Df Daemon[97140:bb0991] [com.apple.securityd:security_exception] MacOS error: -25307
2024-07-23 16:57:01.615 Df Daemon[97140:bb0991] [com.apple.securityd:security_exception] MacOS error: -25307
2024-07-23 16:57:01.615 Df Daemon[97140:bb0991] [com.apple.xpc:connection] [0x6000025e44b0] activating connection: mach=false listener=false peer=false name=com.apple.authd
2024-07-23 16:57:01.620 Df Daemon[97140:bb0991] [com.apple.securityd:security_exception] MacOS error: -25307
2024-07-23 16:57:01.620 Df Daemon[97140:bb0991] [com.apple.securityd:security_exception] MacOS error: -25307
2024-07-23 16:57:01.624 Df Daemon[97140:bb0991] [com.apple.securityd:security_exception] MacOS error: -60005
2024-07-23 16:57:01.628 E Daemon[97140:bb0991] [Daemon:general] Keychain error: The authorization was denied.
The last log contains the message corresponding to the OSStatus I got back from SecItemAdd.
Adding a bit more context to this log, I first try to get the persistent reference for a constant service and account using the following code:
let request = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: account,
kSecMatchLimit: kSecMatchLimitOne,
kSecReturnPersistentRef: true,
] as [String: Any]
var result: CFTypeRef?
let status = SecItemCopyMatching(request as CFDictionary, &result)
Which returns errSecItemNotFound, so I assume the "Password ref is missing".
Then I try to add the key again with the code I attached in my first message.
It looks like I can read the System Keychain and see that there is no key with the requested service and account, but I am unable to write to it anything.
So, the data protection keychain isn’t an option for you.
Yes, I do know that I can't use the Data Protection Keychain, that's why I explicitly set the kSecUseDataProtectionKeychain to false because of that.
My problem here is that my Daemon can't access the System Keychain.
Let me be more clear, here is my setup:
UI --XPC--> Daemon <--> System Keychain
/\
\/
System Extension
I have the main application talking with my Daemon via XPC, which then it tries to use the System Keychain. The one thing that might be unusual, is that my Daemon is in an App-like structure and is sandboxed (mostly because of SMAppService).
As I already mentioned, I wrote a test Daemon to test out the System Keychain, and the code I posted worked for me then. It now does not work.