I am currently in a rather problematic context while developing my custom login application. I leave the original loginwindow:login mechanism intact, and present a non-privileged GUI auth plugin. (So it is not based on SFAuthorizationPluginView).
The problem to solve
I'll be as specific as possible, because the following certainly seems a very security-intensive task. The problem I'm trying to solve is storing a secret item in Keychain that is relevant only for the currently logging in user. This looks like an issue because:
The "login" keychain item for said user is unavailable, as I am outside of the per-user security context based on Daemons and Agents.
The "system" keychain item is available if I connect to a launchd daemon, but if I understand correctly, this is a file-based keychain item.
Option 2 looks like a definite go-to from the two, but
I have several issues with the (seemingly) only available option
1 - The keychain basically loses its purpose if I use the "system" Keychain, because anyone with admin credentials can open and read the item, not just the currently logging in user. So I would have to implement manual per-user encryption INSIDE the system keychain. I (maybe falsely) view the keychain as the equivalent of a password-based key derivation function, and in this example, I would have to implement a double encrpytion:
[System Keychain]
---(encryption)--->
[keychain item]
---(my own encryption, maybe something based on PBKDF2 + AES-GCM, which is partly CommonCrypto, so I really want to avoid it)--->
[secret]
2 - The system keychain is a file-based keychain as far as I know, thus it cannot be protected by the Secure Enclave, which means based on documentation that:
Keeping a private key in a keychain is a great way to secure it. The key data is encrypted on disk and accessible only to your app or the apps you authorize. However, to use the key, you must briefly copy a plain-text version of it into system memory. While this presents a reasonably small attack surface, there’s still the chance that if your app is compromised, the key could also become compromised. As an added layer of protection, you can protect a private key using the Secure Enclave.
3 - Can a file-based keychain still be trusted? Does it use the same encryption algorithms as the data protection keychain? Based on documentation for Keychain,
Keychain items are encrypted using two different AES-256-GCM keys: a table key (metadata) and a per-row key (secret key).
But since this certain piece of documentation seems to not detail normally the difference between file-based and data protection keychains, still it says (right at the top!) that "In macOS (including a Mac with Apple silicon), Data Protection is not used directly to enforce these guarantees.", I am really unsure if the above encryption details are used for file-based keychains. I do not want to use some old, unsecure encryption mechanism if I can avoid it, and yet, file-based keychains seem to be the only option for me. I really want to make sure file-based keychain indeed uses an algorithm that is (currently) considered secure.
4 - Which brings me to another issue: I seem to have non-conditional access to the user's password via context values. I can freely query it with GetContextValue passing kAuthorizationEnvironmentPassword. This does not seem secure, its availability although looks related to another context key, kAuthorizationEnvironmentShared. Is this really set by Apple's own mechanism? And is this okay? What stops me from creating a malicious auth plug-in and just constantly reading this value from every login session? Obviously users need to install plugins themselves, but any benign-looking plugin can exploit this. And if this is true, I have better things to worry about than a momentary plain-text exposure of my key without the use of secure enclave.
5 - Although this above feature(? bug?) makes it easy to hash the password, send it (hopefully securely - client side sets .priviliged flag on the connection and daemon side implements Code Signing Requirement check) via XPC to the launchd daemon, and then encrypt the secret based on that. _Is this something that's okay?
As a primary attempt,
I thought about switching my userid temporarily (which I can do from a launch daemon by pthread_setugid_np), creating a custom file-based keychain item with SecKeychainCreate in the user's Application Support directory, protecting it with their password that I can obtain by above means, and then save the secret unencrypted there, because this way I (seemingly...) have some advantages over the system keychain:
A - My Keychain file is only accessible once the user's home directory is mounted, meaning that HomeDirMechanism has already run for the current user. This seems to separate my file from other users of the machine.
B - My Keychain item is not protected by any admin's credential anymore, instead, it is only unlockable by the current user's password. This seems like an option where I can avoid the above-described double encryption since it provides no gain to encrypt something by a user's password myself if it's already in a file-based keychain protected by the user's password (if the used encryption is indeed aes-256-gcm).
C - I no longer have to give (in my opinion) very unsecure access to my Keychain Item to the security agent, in order to avoid a credentials popup, but instead, I can use the (sadly deprecated) SecKeychainUnlock/SecKeychainOpen APIs to retrieve the item from the keychain. So the custom keychain is not openly accessible by any process hiding behind the securityagent/authorizationhost.
The main question is definitely this above attempt and whether it's secure or not
This is obviously a very long post, with lots of questions, but I believe it's about a very important concept that's probably overlooked by more important OS features. I am mostly just looking for confirmation/disproof.
Authorization Plugins are definitely old school, but they are a crucial part of the OS and they deserve the greatest security measures, yet, they seem to have lacking support for them (this is mostly a reference to data protection keychain being unavailable).