XPC vs socket-based IPC access control & security in user (and kernel) space

Hi Eskimo (and all interested readers).

I am working on the design of a macOS application which will consist on a variety of components:

  • App: a user-space application with UI (user space, logged-in user context)
  • Manager: a launchdDaemon that maintains the global state (user space, global context --> root)
  • Services: a variety of "plugins" controlled by the Manager. Depending of the functionality, each plugin may be itself a launch agent, lunchd daemon or a kernel extension.


Aside for the underlying stability aspects of the multi-process design, the aim of the architecture is that of assigning the correct priviledges to each component as well as modularisation for ease of update over time.

The usual drawback of having multiple processes having to communicate with each other is that of securing the inter process communication, where for securing I mostly mean the aspect of access control or identifying the peer / process at the other end of the communication channel (I am not worried about sniffing the comms data and hence no need for encryption).


I have been researching on pros and cons of XPC versus the traditional socket based IPC as well as reading through related posts here where the aspects of access control in XPC has been discussed: https://forums.developer.apple.com/message/212532#212532.


As far as I can see, the main advantage of XPC is its ease of use and clarity, at the expense of cross-platform portability and to some extent its repercussion on how agnostic the individual components can be. The latter consideration arised from the fact that a XPCConnection usually requires the bundle identifier of the launch daemon / mac service the client wants to connect to :

NSXPCConnection(machServiceName: "com.mydomain.myservice", options: .Privileged).

The authentication of a client trying to connect to a listener, that is making sure that the code signing is of the peer is consistent to expectations, is left to the developer and would normally involve using the code signing API base upon the pid that can be retrieved from the XPCConnection.


A model based on pure IPC using Unix Domain sockets may instead look like this:

  • The Manager, acting as the server, always listens on a Unix socket at a hardcoded path.
  • Any of the clients of the system, be that the App or any of the plugins, always connect to the socket above in order to estabilish the communicaiton channel
  • When a new client connects, the Manager calls getsockopt to retrieve the client's crendentials (pid, gid etc..).
  • As for the XPC case, the pid is used to validate the signature of the client using the code signing API.


Here my questions:

1) Have I missed something in terms of XPC built-in access control features or is it really the same support of the underlying IPC technology, and hence the choice between the two comms does not have security repercussion?


2) Also, is the private API SecTaskValidateForRequirement (combined with the also private audit token XPCConnection's property) the only way to make sure that the peer is signed with as specific Developer ID (as opposed to being signed with a 'legit' developer id) and therefore ensuring that only "my" clients can connect to the XPC listener in my server? I know that this exact logic is used by Apple for patching the security vulnerability found in WriteConfig's XPC ,but the API doesn't seem to be available to third party developer's to secure their own daemon's XPC services despite the enhancement requests. Again, not for @Eskimo to comment about enhencement requests but something to keep in mind when assessing whether the future availibilty of such API's would give XPC a security advantage over socket-based IPC.


3) When it comes to IPC peer identification in kernel space, here Eskimo suggets a solution based upon a user space helper to perform code signing verification of clients trying to connect to the kernel.

I struggle to see how the kExt would identify the user space Helper used to perform code signature verification in the first place.

In other words: assuming that I have a kext that should only be configured / controller by the Manager daemon via a kernel control socket, and that the same manager will also act as the user space Helper to validate the code signing of other components attempting to talk to my kext: how would the kext reliably validate the Manager in the first place?

Accepted Reply

As far as I can see, the main advantage of XPC is its ease of use and clarity, at the expense of cross-platform portability and to some extent its repercussion on how agnostic the individual components can be.

I think cross platform portability is a bit of a red herring here. The design you’ve outlined is tightly bound to macOS’s launchd architecture, meaning that XPC is going to be the least of your portability problems. If you want to build something portable, you’ll need to create a much higher-level abstraction layer.

In the XPC ‘pros’ column:

  • XPC is a lot faster than UNIX domains sockets

  • XPC has a nice, high-level API, NSXPCConnection

  • XPC makes a lot of common operations (descriptor passing, shared memory passing, and so on) much easier

  • XPC makes a small number of operations possible (passing IOSurfaces, for example)

However, if you want to use UNIX domains sockets that’s just fine. Regardless of how else the system evolves, standard APIs like this will continue to be supported.

1) Have I missed something in terms of XPC built-in access control features or is it really the same support of the underlying IPC technology, and hence the choice between the two comms does not have security repercussion?

XPC (and launchd) have a lot of other built-in access control mechanisms but, alas, these are not public API )-: Feel free to file enhancement requests for the things that would make your life easier. Please post any bug numbers, just for the record.

2) Also, is the private API SecTaskValidateForRequirement (combined with the also private audit token XPCConnection's property) the only way to make sure that the peer is signed with as specific Developer ID (as opposed to being signed with a 'legit' developer id) and therefore ensuring that only "my" clients can connect to the XPC listener in my server?

No. Given a PID you can create a SecCode object and check whatever requirement from that. The post you reference discusses most of that. The only bit missing is how you craft a code signing requirement that checks for your Developer ID. Here’s an example of such a requirement:

anchor apple generic and
certificate 1[field.1.2.840.113635.100.6.2.6] and
certificate leaf[field.1.2.840.113635.100.6.1.13] and
certificate leaf[subject.OU] = SKMME9E2Y8

The first three clauses check for a Developer ID certificate issued by Apple and the last clause checks for a specific Team ID.

WARNING I copied the above from an informal email I sent to a friend recently. If you’re going to secure your product by this check, you must audit the requirement you use to make sure it works as you expect. You can find the necessary info in:

Notably, the latter will explain all the magic numbers in my example.

I struggle to see how the kExt would identify the user space Helper used to perform code signature verification in the first place.

The KEXT could just check that the code signing helper is running as root. If an attacker has root on your system, you can’t really protect against that (Apple can, and does, protect against that via System Integrity Protection, but there’s no way for you to take advantage of that infrastructure).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

As far as I can see, the main advantage of XPC is its ease of use and clarity, at the expense of cross-platform portability and to some extent its repercussion on how agnostic the individual components can be.

I think cross platform portability is a bit of a red herring here. The design you’ve outlined is tightly bound to macOS’s launchd architecture, meaning that XPC is going to be the least of your portability problems. If you want to build something portable, you’ll need to create a much higher-level abstraction layer.

In the XPC ‘pros’ column:

  • XPC is a lot faster than UNIX domains sockets

  • XPC has a nice, high-level API, NSXPCConnection

  • XPC makes a lot of common operations (descriptor passing, shared memory passing, and so on) much easier

  • XPC makes a small number of operations possible (passing IOSurfaces, for example)

However, if you want to use UNIX domains sockets that’s just fine. Regardless of how else the system evolves, standard APIs like this will continue to be supported.

1) Have I missed something in terms of XPC built-in access control features or is it really the same support of the underlying IPC technology, and hence the choice between the two comms does not have security repercussion?

XPC (and launchd) have a lot of other built-in access control mechanisms but, alas, these are not public API )-: Feel free to file enhancement requests for the things that would make your life easier. Please post any bug numbers, just for the record.

2) Also, is the private API SecTaskValidateForRequirement (combined with the also private audit token XPCConnection's property) the only way to make sure that the peer is signed with as specific Developer ID (as opposed to being signed with a 'legit' developer id) and therefore ensuring that only "my" clients can connect to the XPC listener in my server?

No. Given a PID you can create a SecCode object and check whatever requirement from that. The post you reference discusses most of that. The only bit missing is how you craft a code signing requirement that checks for your Developer ID. Here’s an example of such a requirement:

anchor apple generic and
certificate 1[field.1.2.840.113635.100.6.2.6] and
certificate leaf[field.1.2.840.113635.100.6.1.13] and
certificate leaf[subject.OU] = SKMME9E2Y8

The first three clauses check for a Developer ID certificate issued by Apple and the last clause checks for a specific Team ID.

WARNING I copied the above from an informal email I sent to a friend recently. If you’re going to secure your product by this check, you must audit the requirement you use to make sure it works as you expect. You can find the necessary info in:

Notably, the latter will explain all the magic numbers in my example.

I struggle to see how the kExt would identify the user space Helper used to perform code signature verification in the first place.

The KEXT could just check that the code signing helper is running as root. If an attacker has root on your system, you can’t really protect against that (Apple can, and does, protect against that via System Integrity Protection, but there’s no way for you to take advantage of that infrastructure).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

> I think cross platform portability is a bit of a red herring here. The design you’ve outlined is tightly bound to macOS’s launchd architecture, meaning that XPC is going to be the least of your portability problems. If you want to build something portable, you’ll need to create a much higher-level abstraction layer. <

Absolutely, I have omitted that for synthesis purposes but the description I gave above is the macOS translation of a higher level architecture: on Windows the processes will run as native Windows services (or however the experts of the platform will decide that they need to). I am trying to isolate elements of that architecture that we may share as a C/C++ as a cross-platform solution, and as IPC could theoretically one of those I am trying to figure out if it's worth going native for essential reasons such as security.

> XPC (and launchd) have a lot of other built-in access control mechanisms but, alas, these are not public API )-: Feel free to file enhancement requests for the things that would make your life easier. Please post any bug numbers, just for the record. <

Yep, will do so as soon as I have a clear picture of what I am talking about and then come back to update this post :).

> The KEXT could just check that the code signing helper is running as root <

I guess this is the aspect where I was hoping for something more than "trust all root clients", considering that user space does allow you to protect against unwanted root communication via the aforementioned code signing validation. On one side, once something runs as root on your system there are other ways for it to tamper with your solution (for instance, by unloading the kext). At the same time those drastic "attacks" are easier to spot and defend against using watchdogs and additional anti-tamper mechanisms...whilst an unverified root controller that configures your kext to do nothing it is trickier to defend against. But hey, it is what is is so I'll take it into account.


Finally, thanks for the exhaustive explanation and especially in pointing me out towards the supported way to validate a specific code signature: although using the process id may not be ideal, it probably good enough for the moment. .


Edited: just a quick note to feedback that I've successfully used your code above to validate the XPC's peer identity. For the benefit of future readers, there is a small typo in the last requirement as the TeamID requires double quotes:


#define MyCodeSigningRequirement "anchor apple generic and "\
                                 "certificate 1[field.1.2.840.113635.100.6.2.6] and "\
                                  "certificate leaf[field.1.2.840.113635.100.6.1.13] and "\
                                  "certificate leaf[subject.OU]=\"MY_TEAM_ID\""