Securing XPC Daemon Communication from Authorization Plugin

I'm working on securing communication between an Authorization Plugin and an XPC daemon, and I’d appreciate some guidance on best practices and troubleshooting.

The current design which, I’ve implemented a custom Authorization Plugin for step-up authentication, which is loaded by Authorization Services at the loginwindow (inside SecurityAgent). This plugin acts as an XPC client and connects to a custom XPC daemon.

Setup Details

1. XPC Daemon

  • Runs as root (LaunchDaemon)
  • Not sandboxed (my understanding is that root daemons typically don’t run sandboxed—please correct me if this is wrong)
  • Mach service: com.roboInc.AuthXpcDaemon
  • Bundle identifier: com.roboInc.OfflineAuthXpcDaemon

2. Authorization Plugin

  • Bundle identifier: com.roboInc.AuthPlugin
  • Loaded by SecurityAgent during login

3. Code Signing Both plugin and daemon are signed using a development certificate

What I’m Trying to Achieve

I want to secure the XPC communication so that:

  • The daemon only accepts connections from trusted clients
  • The plugin only connects to the legitimate daemon
  • Communication is protected against unauthorized access

The Issue I'm facing

I attempted to validate code signatures using:

  • SecRequirementCreateWithString
  • SecCodeCopyGuestWithAttributes
  • SecCodeCheckValidity

However, validation consistently fails with:

-67050 (errSecCSReqFailed)

Could you please help here

  1. What is the recommended way to securely authenticate an Authorization Plugin (running inside SecurityAgent) to a privileged XPC daemon?

  2. Since the plugin runs inside SecurityAgent, how can the daemon reliably distinguish my plugin from other plugins?

  3. What is the correct approach to building a SecRequirement in this scenario?

Any guidance, examples, or pointers would be greatly appreciated. Thanks in advance!

Answered by DTS Engineer in 880794022
my understanding is that root daemons typically don’t run sandboxed

Correct. While it is possible to enable the App Sandbox on a daemon, folks don’t normally do that.

The plugin only connects to the legitimate daemon

The canonical way to do that is by setting the privileged flag. I have a link to an explanation of that in XPC Resources.

You could also validate the peer’s signature. Again, I have a link to info about that in XPC Resources

IMPORTANT This works in this direction because you control the daemon’s main executable.

The daemon only accepts connections from trusted clients

There isn’t a good way to achieve this goal, presuming that this set of trusted clients includes an authorisation plug-in. The issue is that authorisation plug-ins are in-memory plug-ins, so you don’t control the main executable of the process in which the plug-in runs. Rather, it’s loaded by a process running a system executable (well, actually a small set of system executables). That presents two problems:

  • The exact identity of those executables isn’t considered API. It has changed in the past and it may well change again in the future. So you can either apply a very loose constraint (any Apple code) or apply a strict constraint and run the risk of things failing in the future.
  • The same executables are used to load all authorisation, so your daemon can’t distinguish between your plug-in and any other authorisation plug-in.
However, validation consistently fails with … errSecCSReqFailed

It’s hard to say what’s going on there without seeing the specific requirement you’re using. In general:

  • See TN3127 Inside Code Signing: Requirements for lots of background about code signing requirements.
  • As you bring this up, syntax check your requirement using csreq. See the csreq man page.
  • And then test it using codesign. TN3127 has examples of this.

Share and Enjoy

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

my understanding is that root daemons typically don’t run sandboxed

Correct. While it is possible to enable the App Sandbox on a daemon, folks don’t normally do that.

The plugin only connects to the legitimate daemon

The canonical way to do that is by setting the privileged flag. I have a link to an explanation of that in XPC Resources.

You could also validate the peer’s signature. Again, I have a link to info about that in XPC Resources

IMPORTANT This works in this direction because you control the daemon’s main executable.

The daemon only accepts connections from trusted clients

There isn’t a good way to achieve this goal, presuming that this set of trusted clients includes an authorisation plug-in. The issue is that authorisation plug-ins are in-memory plug-ins, so you don’t control the main executable of the process in which the plug-in runs. Rather, it’s loaded by a process running a system executable (well, actually a small set of system executables). That presents two problems:

  • The exact identity of those executables isn’t considered API. It has changed in the past and it may well change again in the future. So you can either apply a very loose constraint (any Apple code) or apply a strict constraint and run the risk of things failing in the future.
  • The same executables are used to load all authorisation, so your daemon can’t distinguish between your plug-in and any other authorisation plug-in.
However, validation consistently fails with … errSecCSReqFailed

It’s hard to say what’s going on there without seeing the specific requirement you’re using. In general:

  • See TN3127 Inside Code Signing: Requirements for lots of background about code signing requirements.
  • As you bring this up, syntax check your requirement using csreq. See the csreq man page.
  • And then test it using codesign. TN3127 has examples of this.

Share and Enjoy

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

Securing XPC Daemon Communication from Authorization Plugin
 
 
Q