Can one restrict XPC communications to only occur between processes
that share the same code signing identity?
Yes. Well, you probably don’t want the same core signing identity (in general, each separate executable should have its own identity) but rather you want to evaluate code signing requirements for the client, and you can definitely do that. There are two basic tools for this:
-
Various properties on the XPC connection (like NSXPCConnection’s auditSessionIdentifier
, processIdentifier
, effectiveUserIdentifier
, and effectiveGroupIdentifier
properties, and the equivalent C functions to get these values from an xpc_connection_t
)
-
Various security APIs, most notably the code signing API (<Security/SecCode.h>
)
The exact way in which you combine these is up to you, but here’s an example of how you might restrict access to your service to just your app:
-
When a client makes a connection to your service, get the process ID of the client from that connection. If you’re using NSXPCConnection, you should do this in your -listener:shouldAcceptNewConnection:
delegate callback. If you’re using the lower-level XPC C API, you’d should do this in the event handler for your listener connection.
-
Use that process ID to create a code object for that client (SecCodeCopyGuestWithAttributes
with kSecGuestAttributePid
).
-
Create a requirement object that describes the clients you’d want to allow to connect (typically using SecRequirementCreateWithData
but also see the note below).
-
Check that the code object from step 2 meets the requirement from step 3 (SecCodeCheckValidity
or SecCodeCheckValidityWithErrors
).
There’s a couple of potential security gotchas here. I’ll discuss those below, but first I want to make a few ancillary points:
-
In step 3, you can prepare the requirement object up front, so you don’t need to create it anew for each connection.
-
Again in step 3, you can create your requirement object using either SecRequirementCreateWithData
or SecRequirementCreateWithString
. If you use the former, you can pre-compile the requirement data using csreq
.
- It’s seems pretty obvious that the system should provide better support for XPC service access control. In fact, it does provide better support for this, it’s just that this support is all private. I encourage you to file an enhancement request describing your high-level goals, what you had to do to achieve them, and how you’d like to see the system improved to make things easier. Please post your bug number, just for the record.
The first potential security gotcha relates to the security of the client app itself. If your client app loads any code (like plug-ins), that code will have the same access as the app’s main code. This could completely undermine the security of the system as a whole.
The best solution to this problem is to turn on library validation for your app. This is a code signing feature that ensures that your app can only load ‘blessed’ code. I’m not an expert in library validation but you can get started by looking at the library
option described in the codesign
man page.
The second issue relates to the process ID. The OS’s process ID space is relatively small, which means that process IDs are commonly reused. Thus, it’s a bad idea to use a process ID in security-related work.
There is a recommended alternative to process IDs, namely audit tokens (audit_token_t
), but you can’t use this because a critical piece of public API is missing. While you can do step 2 with an audit token (using kSecGuestAttributeAudit
), there’s no public API to get an audit token from an XPC connection (step 1)-:
If you’d like to switch over to audit tokens then, again, I recommend you file an enhancement request for this facility to be made public.
Fortunately, process ID wrapping problems aren’t a real threat in this context because, if you create an XPC connection per process, you can do your checking based on the process ID of that process. If the process dies, the connection goes away and you’ll end up rechecking the process ID on the new connection.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"