Posts

Post not yet marked as solved
1 Replies
753 Views
I am building a Transparent Proxy Network Extension for Developer ID publication (i.e. not Mac App Store). When testing, I ran the NE as an Application Extension (appex -- entitlement app-proxy-provider), and that ran as the active user in an App Sandbox. Part of my extension reads the system root certificate store (both admin and system domains) from Keychain using the Security framework, and this all worked perfectly fine. However, since my app is non-MAS, the extension must be made into a System Extension (entitlement app-proxy-provider-systemextension). This means my extension is now running as root and unsandboxed, which is definitely not ideal. So I re-enabled App Sandbox on the extension, and surprisingly it actually works. (I've been unable to get an executable running as root to use an App Sandbox in the past, e.g. through sandbox-exec or otherwise.) The problem is that the root sandbox seems more restrictive than the regular user sandbox. Namely, unlike the appex's sandbox, it cannot seem to access Keychain at all. Trying to read the admin and system certificate store fails. For example, using the function SecTrustSettingsCopyCertificates on the system domain returns the error No Trust Settings were found. Additionally, these logs are written to the system Console when I called the function. error kernel Sandbox: [my bundle id](1478) deny(1) file-write-data /private/var/db/mds/system/mds.lock default [my bundle id] MacOS error: -25337 default [my bundle id] CSSM Exception: 3 unknown error 3=3 default [my bundle id] CSSM Exception: -2147414013 CSSMERR_DL_MDS_ERROR default [my bundle id] CSSM Exception: -2147414013 CSSMERR_DL_MDS_ERROR So it looks like the Sysex running in an App Sandbox as root prevents access to MDS (CSSM Module Directory Service, perhaps? Or is it Spotlight's Metadata Service?), which I guess breaks the Keychain lookups. Other functions to access the Keychain, such as SecItemCopyMatching, also fail with the same sandbox errors logged to console. Surprisingly, a solution that seems to solve the problem is to use a temporary-exception to allow mds.lock. <key>com.apple.security.temporary-exception.files.absolute-path.read-write</key> <array> <string>/private/var/db/mds/system/mds.lock</string> </array> However, this obviously seems quite sketchy, especially since I'm not even sure what mds is. So, ultimately, my question is: is there any non-sketchy way to create a Network Extension for non-Mac App Store publication which can run in a secure context (e.g. not root, or root with App Sandbox) and still have access Keychain? Is my temporary-exception solution actually okay for production use? My extension does not need root permissions, and I took great care to ensure that my userspace proxy daemon was running as unprivileged as possible back with Network Kernel Extensions. It seems silly that now the "more secure" NKE replacement involves requiring NEs to run as root.
Posted Last updated
.
Post marked as solved
2 Replies
1k Views
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 - https://developer.apple.com/forums/thread/129596 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: Error Domain=NSOSStatusErrorDomain Code=-25291 "failed to generate asymmetric keypair" (errKCNotAvailable / errSecNotAvailable:&#9;/ 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 - https://support.apple.com/guide/security/keychain-data-protection-overview-secb0694df1a/web 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.
Posted Last updated
.