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?