I'm trying to use a keychain-access-group and failing

This is on macOS, not iOS. Not sure if that should make a difference?

I have a GUI app and a command line tool (that will run a daemon) that I need to share credentials between. The keys/certs will be stored using the GUI app. But, both tools need to utilize them.

        guard let accessControl = SecAccessControlCreateWithFlags(
            nil,
            kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
            [.privateKeyUsage],
            nil
        ) else {
            throw KeychainCertError.keychainError(errSecAuthFailed, "Failed to create access control for private key")
        }

        // Define Key Pair Attributes
        let privateKeyAttributes: [String: Any] = [
            kSecAttrIsPermanent as String: true,
            kSecAttrApplicationTag as String: privateLabel.data(using: .utf8)!,
            kSecAttrLabel as String: privateLabel,
//            kSecAttrAccessControl as String: accessControl,
            kSecAttrAccessGroup as String: keychainAccessGroup
        ]

With the kSecAttrAccessControl commented out, I am able to generate a private key and generate a self signed certificate that is stored on the user login keychain. If I uncomment that line, I get an error to the affect of "Keychain error (-26275): Failed to generate key pair: A required entitlement isn't present"

Also, to share the credentials, don't they need to be NOT on the user keychain for the daemon to access them?

Any ideas what I am doing wrong? I think I'm a bit over my head here with the the security, crypto kit and openssl. 😁

Written by gilburns in 775935021
This is on macOS, not iOS. Not sure if that should make a difference?

Oh yes. The keychain story on macOS is complex. See TN3137 On Mac keychain APIs and implementations. This defines terms like file-based keychain and data protection keychain. The rest of what I say won’t make sense unless you’ve read this technote.

Written by gilburns in 775935021
I have a GUI app and a command line tool (that will run a daemon) that I need to share credentials between.

This is going to be a challenge. You can’t use the data protection keychain because it’s not available to daemons [1]. However, the file-based keychain is also problematic because it can only be modified by root.

How to approach this depends on how your software is using this keychain item:

  • Is it created by the app? Or the daemon? Or could it be created by both?

  • Is it read by the app? Or the daemon? Or both?

  • You mentioned “openssl”. Is that in play here?

  • If not, what API are you using to consume this keychain item?

  • How does your app communicate with your daemon?

Oh, one last thing: I’m assuming that by “daemon” you mean a launchd daemon, that is, something started by a property list in /Library/LaunchDaemons or by SMAppService.daemon(…). If you’re using that term to mean anything else, please explain what that is.

Share and Enjoy

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

[1] Even if you could, the data protection keychain is pre-user, which means you can’t use it to share state between your daemon, running as root, and your app, running as a normal user.

In the current implementation, the GUI app will be the process that creates the secure items. The command line tool will be the process that largely consumes that secure item, but the GUI app needs access to it as well.

High level overview. The combination of the two processes is to automate macOS software updates to an MDM management system. (Intune) The GUI app is all about configuring which apps should be monitored and automatically updated for deployment in the management system. It really is just to setup all the metadata required for the automation.

The command line tool is what will then run as some scheduled interval to read the setting created by the GUI app and do the task of updating the software deployments.

This is all more or less working now when I run the two tools under my user context. I honestly have not gotten to the part where I create the launch daemon and run it in that context. The two items are not really ever interacting directly.

The GUI app can currently create a self signed key pair (openssl framework) for use to for authentication to an Entra ID Enterprise application, or consume a secret key (password) that Entra ID app generates. (Microsoft Graph API)

That is all working now. Except, I am not currently storing any of that in a secure way. The cert or secret key are currently just store in the file system unsecured. Hence my desire to move this into something secure like the keychain.

Maybe I just need to use separate credential for both items? The GUI app creates the secure items for the command line tool to read from the system keychain? (Can a daemon pull from the system keychain?) Then the GUI app uses credentials of the user at launch time to communicate with Entra and Intune? Would something like that work?

Does that answer your open questions? Any ideas or direction you could provide will be appreciated. Thanks!

Thanks for the explanation. That’s very clear.

I have one follow-up question: Does the code that runs in the background have to be a daemon? Or could you get away with an agent?

The key differences between a daemon and an agent are:

  • The daemon runs in the global execution context. That means there’s only ever a single instance, which is available all the time [1], and it runs as root.

  • Agents run in a user execution context. Instances come and go as users log in and out. Each instance runs as the user for the associated login session.

Note I go into this in much more detail in TN2083 Daemons and Agents.

If you can use an agent then life will be a lot easier. However, that’s only feasible if your background code:

  • Doesn’t need root privileges.

  • Is OK with storing separate state for each user.

  • And only needs to run while that user is logged in.

Based on my understanding of your setup I think you really do need a daemon. If that’s right, lemme know and I explain your (somewhat harder)-: options.

Share and Enjoy

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

I think I'd prefer it to be able to run on a schedule no matter if a user was logged in or not. So it would be a daemon not an agent.

What if I change the architecture and turn the daemon into a privileged helper tool and it became the only item that needed the credentials? Can it operate as both a helper tool and a daemon running on a schedule?

Then the GUI app puts in requests for things that require the Entra or Intune information via the Graph API to the helper and it passes the data back to the GUI app. Would that simplify the equation?

I'm trying to use a keychain-access-group and failing
 
 
Q