19 Replies
      Latest reply on May 28, 2020 2:50 PM by Kentzo
      jasosimm Level 1 Level 1 (0 points)

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

        • Re: XPC restricted to processes with the same code signing?
          eskimo Apple Staff Apple Staff (13,915 points)

          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 goạls, 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!”
          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
          let myEmail = "eskimo" + "1" + "@apple.com"

          1 of 1 people found this helpful
            • Re: XPC restricted to processes with the same code signing?
              jasosimm Level 1 Level 1 (0 points)

              Thank you for the in depth answer. Very helpful.

              • Re: XPC restricted to processes with the same code signing?
                Coder256 Level 1 Level 1 (0 points)

                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?

              • Re: XPC restricted to processes with the same code signing?
                eskimo Apple Staff Apple Staff (13,915 points)

                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"

                • Re: XPC restricted to processes with the same code signing?
                  Kentzo Level 1 Level 1 (0 points)

                  @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)

                    • Re: XPC restricted to processes with the same code signing?
                      eskimo Apple Staff Apple Staff (13,915 points)

                      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"

                        • Re: XPC restricted to processes with the same code signing?
                          Kentzo Level 1 Level 1 (0 points)

                          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.

                            • Re: XPC restricted to processes with the same code signing?
                              eskimo Apple Staff Apple Staff (13,915 points)

                              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)

                                • Re: XPC restricted to processes with the same code signing?
                                  Kentzo Level 1 Level 1 (0 points)

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

                                    • Re: XPC restricted to processes with the same code signing?
                                      eskimo Apple Staff Apple Staff (13,915 points)

                                      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"

                                        • Re: XPC restricted to processes with the same code signing?
                                          Kentzo Level 1 Level 1 (0 points)

                                          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?

                                            • Re: XPC restricted to processes with the same code signing?
                                              eskimo Apple Staff Apple Staff (13,915 points)

                                              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"

                                                • Re: XPC restricted to processes with the same code signing?
                                                  Kentzo Level 1 Level 1 (0 points)

                                                  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?

                                                    • Re: XPC restricted to processes with the same code signing?
                                                      eskimo Apple Staff Apple Staff (13,915 points)

                                                      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"

                                                        • Re: XPC restricted to processes with the same code signing?
                                                          Kentzo Level 1 Level 1 (0 points)

                                                          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?

                                              • Re: XPC restricted to processes with the same code signing?
                                                Kentzo Level 1 Level 1 (0 points)

                                                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!