XPC restricted to processes with the same code signing?

Can one restrict XPC communications to only occur between processes that share the same code signing identity?

Answered by DTS Engineer in 677610022

This question come up again in a different thread and I’ve post some important new updates on that thread.

Share and Enjoy

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

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:

  1. 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.

  2. Use that process ID to create a code object for that client (SecCodeCopyGuestWithAttributes with kSecGuestAttributePid).

  3. Create a requirement object that describes the clients you’d want to allow to connect (typically using SecRequirementCreateWithData but also see the note below).

  4. 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"

Thank you for the in depth answer. Very helpful.

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.

 I would rather not rely on the connection being invalidated just because the client process exits. Especially given that Apple has started switching from PIDs to audit tokens, which is demonstrated by WebKit bug 170616, and more importantly its corresponding changeset 215132, both of which were created by an Apple employee. In fact, it appears that they have started validation using audit tokens in more than just WebKit - even SystemAdministration.framework checks the audit token (and it's already obsolete!). My point is that Apple already internally does the checking you describe, but has still SPI'd the audit token in both the C and Objective-C XPC interfaces (

xpc_connection_get_audit_token
and
NSXPCConnection.auditToken
respectively).

 It is not clear why they do this; the audit token contains only useful but not sensitive information: the sender's audit UID, effective/real UID/GID, PID, audit session ID, and PID version, used for when the PID wraps around (reference:

/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/bsm/libbsm.h
, the online versions are out of date). Most of that information could be inferred anyway based on the PID and effective UID/GID already provided.

 There's really no reason to conceal the audit token. Its only purpose since its creation is for security verification, and that was back in 2004 before GateKeeper, and before even File Quarantine (reference:

man au_token
). As its name suggests, it is meant for security auditing and is not a security risk if exposed. There is no reason why this additional protection should be restricted from developers, because the only difference it would make is that their apps would have the option of increased security.


 How do you file an enhancement request in the new bug reporter?

@eskimo @Coder256 How does the audit token relate to the audit session identifier? NSXPCConnection.auditSessionIdentifier has been available since macOS 10.8. Can we use that to authenticate the sender? If so, how?

[Hmmm, I seem to have missed your post last year, possibly due to the Apple’s winter shutdown. Oh well, better never than late.]

I would rather not rely on the connection being invalidated just because the client process exits.

I absolutely agree with you there.

How do you file an enhancement request in the new bug reporter?

You should be as simple as setting the Classification to Enhancement Request.

Did you get a bug filed about this? If so, what was the bug number?

Share and Enjoy

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

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

How does the audit token relate to the audit session identifier?

This audit session identifiers identies the login session, which isn’t really helpful in this context.

Share and Enjoy

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

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

I’m posting a link to The story behind CVE-2019-13013, primarily for the benefit of Future Quinn™.

Share and Enjoy

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

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

@eskimo, what is your opinion regarding code validation technique suggested in https://speakerdeck.com/vashchenko/job-s-bless-us-privileged-operations-on-macos?slide=54 (from Objective by the Sea v3.0)

what is your opinion regarding [this] code validation technique

I’m not going to review other folks’ work here on DevForums. However, if you have a specific question then I’d be happy to answer it.

Share and Enjoy

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

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

In 2017 you said that verifying using PID is fine, but there were a number of CVEs reported since then.


Do you still hold by:


> 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.

Do you still hold by

No. I recently had cause to look into this in depth as part of a DTS tech support incident. The news is, alas, mixed. There are two potential issues with XPC authentication:

  • Message vs connection — As a general rule we’d prefer that folks authenticate each message rather than the connection as a whole. Right now, alas, there’s simply no public support for that )-:

  • Process ID vs audit token — The obvious problem with using the process ID is process ID reuse. The theory here is that an attacker could force a process ID wrap and then impersonate a recently quit XPC client.

    You can avoid this problem by using an audit token but, alas, there is no public API to get an audit token from an XPC connection (r. 27605210).

As to the significance of the process ID wrap attack, my opinion in that this is a concern in other contexts but it’s not a fatal problem in the context of XPC. The critical point here is that XPC connections are owned by a process so, if the process terminates, the connection goes with it. If the XPC service ties the authentication to the connection and throws that away when the connection is invalidated, there’s nothing to exploit.

While the above is generally true, the XPC team pointed out that there is a possible vulnerability with the first message for a given connection. This suggests two paths forward:

  • There are unsupported ways to get an audit token from a connection — given the links you posted earlier, I’m sure you’re aware of them already — and you can use those to create your code object (

    SecCode
    ) via using
    kSecGuestAttributeAudit
    .
  • You can use the public API to get the process ID from the connection (

    processIdentifier
    or
    xpc_connection_get_pid
    ) and create your code object from that using
    kSecGuestAttributePid
    . In this case, design your IPC protocol to not accept any security-critical requests as the first message on the connection.

IMPORTANT DTS does not support the first path as it can engender both binary compatibility and App Review problems

Share and Enjoy

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

let myEmail = "eskimo" + "1" + "@apple.com"
(s. 732651775)

> the XPC team pointed out that there is a possible vulnerability with the first message for a given connection


Do you suggest that OS verifies validity of an xpc connection of each message only after the preceding message got processed by the user?


If that's the case, it appears that while only the second and further messages should be allowed to perform security-sensitive tasks, all messages need to be verified. Otherwise the attack seems to be possible assuming that malicious process is warped after Nth message connection is vetted by the OS but before it's verified by the user.

Do you suggest that OS verifies validity of an xpc connection of each message only after the preceding message got processed by the user?

I’m not sure how the user comes into this?

Share and Enjoy

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

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

When you wrote:


> the XPC team pointed out that there is a possible vulnerability with the first message for a given connection.


did you mean an attack that happens due to a time window between the OS vertting an XPC message and user's code seeing it? I.e. when by the time user's code attempts to verify its XPC peer by PID, it is some other process, not the one that originally sent the message.


If my understanding is correct, then it's an important implementation detail. Otherwise how else can you prevent user's code in the XPC service from receiving multiple messages from the malicious process?

did you mean an attack that happens due to a time window

No, this is nothing to do with time windows. Rather, it’s related to the way that XPC manages connections. Once the first message has been processed, the connection is established such that my “if the process terminates, the connection goes with it” logic kicks in.

Share and Enjoy

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

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

Could you elaborate what is implied by


> has been processed


Is it processed by the time user's callback is entered or exited?


> Once the first message has been processed, the connection is established such that my “if the process terminates, the connection goes with it” logic kicks in.


Isn't it possible for an originating process to die and wrap while XPC service is still within user's callback for the 2nd message?

Isn't it possible for an originating process to die and wrap while XPC service is still within user's callback for the 2nd message?

Absolutely. But, by the time of the second message, the XPC connection is fully established, so there’s no way for a replacement process to send a message down that same connection.

Share and Enjoy

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

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

While the OS ensures that the second message can only be sent by the same process that sent the first message, it alone is not enough for user's code in the XPC service.


Thus it seems important for user's code in the XPC service to:

1. When handling the first message capture process's identity (in terms of requirements, version, code signature etc) and avoid performing any priviliged actions. In other words let the first message be a handshake.

2. When handling all consequent messages use the identity captured in [1] for permissions check before performing any priviliged actions


At least it appears to be enough if permissions checks are run against code signature and backed-in data (such as requirements) which is also reflected by the signature.


Is my understanding more-or-less in line with what you have suggested above?

Quinn,


My apologies by I seem to miss something obvious here. In this reply you suggested two paths:


1. Use a private API to acquire the audit token of an XPC connection

2. Use PID but rely on that 2nd and forth messages are guaranteed to come from the same connection


While [1] is quite clear I'm completly puzzled by [2].


Which of the following do you suggest:


1. Run peer validation pased on code object created from a PID associated with the connection upon receiving the first message, then, if successful, mark that connection authenticated and perform priviliged work starting with the 2nd message

2. Same as [1] but re-validate the peer for each message

3. Something else


---


The scenario I have in mind is:


1. Malicious process starts, sends 2 messages

2. The system does whaterver it does, processes the messages and enques them for delivery to user's code

3. Malicious process through other means (e.g. timing?) figures out that its messages were enqued for delivery and initiates wrapping

4. Legitimate process takes its places

5. Enqueued messages finally reach user's code. User acquires PID, gets code object of the Legitimate process and responds to the messages. Since both message are already enqueued user has no chance (aside from using private API to get auditToken) to recognize malicious intent.


To avoid the attack, as I understand, the OS must not bulk process messages. Instead it should process them one by one, only picking the next message after user's code is done with the previous one.


In the scenario above it would force the peer to be around up until the moment the 2nd message was passed to user's code. Thus preventing the wrap.


Please guide me!

This question come up again in a different thread and I’ve post some important new updates on that thread.

Share and Enjoy

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

After spending a while trying to figure the best approach for this problem, I eventually settled on using the XPC C API which allows for securing the connection using the SecCodeCreateWithXPCMessage function on macOS 11 and later, and falling back to using a private function on older versions. But I didn't want to have to directly use the C API all throughout my codebase so I created the SecureXPC framework in Swift. Hopefully this is helpful to some people here. Feedback is most welcome on the Github page either as issues or discussions!

XPC restricted to processes with the same code signing?
 
 
Q