Protecting XPC service when called from Authorisation Plugin

I have Authorisation Plugin which talks using XPC to my Launch Daemon to perform privileged actions.

I want to protect my XPC service narrowing it to be called from known trusted clients.

Now since I want authorisation plugin code which is from apple to call my service, I cannot use my own team id or app group here.

I am currently banking on following properties of client connection.

  1. Apple Team ID : EQHXZ8M8AV
  2. Bundle ID starting with com.apple.
  3. Client signature verified By Apple.

This is what I have come up with.

func isClientTrusted(connection: NSXPCConnection) -> Bool {
    
    let clientPID = connection.processIdentifier
    logInfo("🔍 Checking XPC Client - PID: \(clientPID)")
    
    var secCode: SecCode?
    var secStaticCode: SecStaticCode?

    let attributes = [kSecGuestAttributePid: clientPID] as NSDictionary
    let status = SecCodeCopyGuestWithAttributes(nil, attributes, [], &secCode)

    guard status == errSecSuccess, let code = secCode else {
        logInfo("Failed to get SecCode for PID \(clientPID)")
        return false
    }

    let staticStatus = SecCodeCopyStaticCode(code, [], &secStaticCode)
    guard staticStatus == errSecSuccess, let staticCode = secStaticCode else {
        logInfo("Failed to get SecStaticCode")
        return false
    }

    var signingInfo: CFDictionary?
    let signingStatus = SecCodeCopySigningInformation(staticCode, SecCSFlags(rawValue: kSecCSSigningInformation), &signingInfo)

    guard signingStatus == errSecSuccess, let info = signingInfo as? [String: Any] else {
        logInfo("Failed to retrieve signing info")
        return false
    }

    // Extract and Verify Team ID
    if let teamID = info["teamid"] as? String {
        logInfo("XPC Client Team ID: \(teamID)")

        if teamID != "EQHXZ8M8AV" { // Apple's official Team ID
            logInfo("Client is NOT signed by Apple")
            return false
        }
    } else {
        logInfo("Failed to retrieve Team ID")
        return false
    }

    // Verify Bundle ID Starts with "com.apple."
    if let bundleID = info["identifier"] as? String {
        logInfo("XPC Client Bundle ID: \(bundleID)")

        if !bundleID.hasPrefix("com.apple.") {
            logInfo("Client is NOT an Apple system process")
            return false
        }
    } else {
        logInfo("Failed to retrieve Bundle Identifier")
        return false
    }

    // Verify Apple Code Signature Trust
    var trustRequirement: SecRequirement?
    let trustStatus = SecRequirementCreateWithString("anchor apple" as CFString, [], &trustRequirement)

    guard trustStatus == errSecSuccess, let trust = trustRequirement else {
        logInfo("Failed to create trust requirement")
        return false
    }

    let verifyStatus = SecStaticCodeCheckValidity(staticCode, [], trust)

    if verifyStatus != errSecSuccess {
        logInfo("Client's signature is NOT trusted by Apple")
        return false
    }

    logInfo("Client is fully verified as Apple-trusted")
    return true
}

Q: Just wanted community feedback, is this correct approach?

Accepted Answer

There isn’t an ideal way to do this.

The canonical mechanism to protect your XPC endpoint is to apply a code-signing requirement, as explained in Validating Signature Of XPC Process.

IMPORTANT See TN3127 Inside Code Signing: Requirements for an in-depth discussion of code-signing requirements.

This mechanism doesn’t work in your case because you can’t control the process that loads your authorisation plug-in. It can be loaded by a variety of different system processes. Moreover, that set of processes has changed in the past and it’s not hard to imagine it changing again in the future.

So, you have a security/reliability trade-off to make:

  • If you apply a tight requirement then it’s possible for it to fail in the future if Apple changes the identity of the program that loads your plug-in.

  • If you apply a loose requirement then you might end up allowing connections from contexts where you really shouldn’t be.

If I were in your shoes I’d probably thread this needle as follows:

  • Apply an anchor apple requirement to the listener, ensuring that only Apple code can connect to it.

  • Also check that the effectiveUserIdentifier is 0.

If you’re being attacked by Apple code running as root, you have bigger problems (-:

Share and Enjoy

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

Thanks for the response, I'll look into it as advised.

Protecting XPC service when called from Authorisation Plugin
 
 
Q