Error code 9216 when attempting to access keychain password in LaunchAgent

574 views Asked by At

There are several other questions that discuss accessing the keychain from LaunchAgents.

One of the key ones is here where joensson mentions that you need to set a <SessionCreate> in your app plist.

I have done that, and now my application plist looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>com.ionic.python.ionic-fs-watcher.startup</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Applications/IonicFSWatcher.app/Contents/MacOS/ionic-fs-watcher</string>
        <string>--debug</string>
        <string>/Users/timothy/ionicprotected</string>
        <string>--scan</string>
    </array>
    <key>UserName</key>
    <string>timothy</string>
    <key>SessionCreate</key>
    <true />
  </dict>
</plist>

The app is a python application, which was created with pyinstaller, packaged using pkgbuild, and installed via the command line.

The application runs fine when run from the command line. If the application is run for the first time, the user gets a prompt to allow access to the keychain, the app continues from there.

When it is launched as a LaunchAgent, however, I get a return code of 9216 when attempting to access the keychain. Here is the exact command sequence I have been using to test:

# Window 1
sudo launchctl unload -w ~/Library/LaunchAgents/com.ionic.python.ionic-fs-watcher.startup.plist
sudo launchctl load -w ~/Library/LaunchAgents/com.ionic.python.ionic-fs-watcher.startup.plist
sudo launchctl debug system/com.ionic.python.ionic-fs-watcher.startup --stdout --stderr

# Window 2
sudo launchctl kickstart -k -p system/com.ionic.python.ionic-fs-watcher.startup

In the python script, I have been debugging by running a few subcommands and capturing output.

# OK
status, output = commands.getstatusoutput("security list-keychains")
logger.error("Keychain list :%s (retcode = %s)" % (
    output, status
))
# OK
status, output = commands.getstatusoutput("security find-generic-password -a 'Ionic Security' ")
logger.error("Keychain list item :%s (retcode = %s)" % (
    output, status
))
# Fails, with error code: 9216
# Prompts immediately when running from command line
status, output = commands.getstatusoutput("security find-generic-password -a 'Ionic Security' -g")
logger.error("Keychain access :%s (retcode = %s)" % (
    output, status
))

The output of that section of code looks like this:

# Correctly shows both keychains
ERROR:root:Keychain list :    "/Users/timothy/Library/Keychains/login.keychain"
    "/Library/Keychains/System.keychain" (retcode = 0)
# Correctly lists information about keychain item
ERROR:root:Keychain list item :keychain: "/Users/timothy/Library/Keychains/login.keychain"
<redacted>
# Fail
ERROR:root:Keychain access : (retcode = 9216)

These same commands work fine from the command line, with the last one (-g) resulting in a prompt to allow access to the keychain.

I have also tried opening up the entry for com.ionicsecurity.client.sdk in the KeyChain Access app, and set the "Allow all applications to access this item" radio button. After doing that, grabbing the value from the cli no longer resulted in a prompt but the app returns the same error code.

I have searched for information about error code 9216 with no results. Running the code through the security errors utility just gives

$ security error 9216
Error: 0x00002400 9216 unknown error 9216=2400

Any help on how I can get the application access to the keychain when running as a LaunchAgent would be much appreciated!

1

There are 1 answers

0
turtlemonvh On BEST ANSWER

The problem was the domain that I was using to launch the LaunchAgent. I was launching into the root system domain instead of the launching into the gui domain of the user I was setting up the LaunchAgent for. Because of this

  • The LaunchAgent did not have access to the users keychain (unless I requested the command run as the user with a session being created)
  • The LaunchAgent did not have any way to display a pop up to the user to request access to the keychain (which I am assuming is why I got the strange error code here)

Here is the mapping from the commands I was using before (not working) to what I'm using now (working). The commands assume the user for which we are installing the LaunchAgent is also the current user.

Activating the agent

# Before
sudo launchctl load -w ~/Library/LaunchAgents/com.ionic.python.ionic-fs-watcher.startup.plist

# Now
launchctl enable user/`id -u`/com.ionic.python.ionic-fs-watcher.startup
launchctl bootstrap gui/`id -u` ~/Library/LaunchAgents/com.ionic.python.ionic-fs-watcher.startup.plist

Deactivating the agent

# Before
sudo launchctl unload -w ~/Library/LaunchAgents/com.ionic.python.ionic-fs-watcher.startup.plist

# Now
launchctl bootout gui/`id -u`/com.ionic.python.ionic-fs-watcher.startup
launchctl disable user/`id -u`/com.ionic.python.ionic-fs-watcher.startup

Setting permissions for the LaunchAgent definition (plist file)

# Before
chown root:wheel ~/Library/LaunchAgents/com.ionic.python.ionic-fs-watcher.startup.plist
chmod 644 ~/Library/LaunchAgents/com.ionic.python.ionic-fs-watcher.startup.plist

# Now
chown "`id -un`":"`id -gn`" ~/Library/LaunchAgents/com.ionic.python.ionic-fs-watcher.startup.plist
chmod 644 ~/Library/LaunchAgents/com.ionic.python.ionic-fs-watcher.startup.plist

LaunchAgent plist file contents

The UserName and SessionCreate blocks are not needed.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>com.ionic.python.ionic-fs-watcher.startup</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Applications/IonicFSWatcher.app/Contents/MacOS/ionic-fs-watcher</string>
        <string>/Users/timothy/ionicprotected</string>
        <string>--scan</string>
    </array>
  </dict>