setCodeSigningRequirement seems not to work in new Service Management API setup.

I have developed a sample app following the example found Updating your app package installer to use the new Service Management API and referring this discussion on XPC Security.

The app is working fine, I have used Swift NSXPCConnection in favour of xpc_connection_create_mach_service used in the example. (I am running app directly from Xcode)

I am trying to set up security requirements for the client connection using setCodeSigningRequirement on the connection instance.

But it fails for even basic requirement connection.setCodeSigningRequirement("anchor apple").

Error is as follows.

cannot open file at line 46986 of [554764a6e7]
os_unix.c:46986: (0) open(/private/var/db/DetachedSignatures) - Undefined error: 0
xpc_support_check_token: anchor apple error: Error Domain=NSOSStatusErrorDomain Code=-67050 "(null)" status: -67050

I have used codesign -d --verbose=4 /path/to/executable to check the attributes I do get them in the terminal.

Other way round, I have tried XPC service provider sending back process id (pid) with each request, and I am probing this id to get attributes using this code which gives all the details.

func inspectCodeSignature(ofPIDString pidString: String) {

    guard let pid = pid_t(pidString) else {
        print("Invalid PID string: \(pidString)")
        return
    }

    let attributes = [kSecGuestAttributePid: pid] as CFDictionary
    var codeRef: SecCode?

    let status = SecCodeCopyGuestWithAttributes(nil, attributes, [], &codeRef)
    guard status == errSecSuccess, let code = codeRef else {
        print("Failed to get SecCode for PID \(pid) (status: \(status))")
        return
    }

    var staticCode: SecStaticCode?
    let staticStatus = SecCodeCopyStaticCode(code, [], &staticCode)
    guard staticStatus == errSecSuccess, let staticCodeRef = staticCode else {
        print("Failed to get SecStaticCode (status: \(staticStatus))")
        return
    }

    var infoDict: CFDictionary?
    if SecCodeCopySigningInformation(staticCodeRef, SecCSFlags(rawValue: kSecCSSigningInformation), &infoDict) == errSecSuccess,
       let info = infoDict as? [String: Any] {
        print("🔍 Code Signing Info for PID \(pid):")
        print("• Identifier: \(info["identifier"] ?? "N/A")")
        print("• Team ID: \(info["teamid"] ?? "N/A")")

        if let entitlements = info["entitlements-dict"] as? [String: Any] {
            print("• Entitlements:")
            for (key, value) in entitlements {
                print("    - \(key): \(value)")
            }
        }
    } else {
        print("Failed to retrieve signing information.")
    }

    
    var requirement: SecRequirement?
    if SecRequirementCreateWithString("anchor apple" as CFString, [], &requirement) == errSecSuccess,
       let req = requirement {
        let result = SecStaticCodeCheckValidity(staticCodeRef, [], req)
        if result == errSecSuccess {
            print("Signature is trusted (anchor apple)")
        } else {
            print("Signature is NOT trusted by Apple (failed anchor check)")
        }
    }
    
    var infoDict1: CFDictionary?
    let signingStatus = SecCodeCopySigningInformation(staticCodeRef, SecCSFlags(rawValue: kSecCSSigningInformation), &infoDict1)

    guard signingStatus == errSecSuccess, let info = infoDict1 as? [String: Any] else {
        print("Failed to retrieve signing information.")
        return
    }

    print("🔍 Signing Info for PID \(pid):")
    for (key, value) in info.sorted(by: { $0.key < $1.key }) {
        print("• \(key): \(value)")
    }
}

If connection.setCodeSigningRequirement does not works I plan to use above logic as backup.

Q: Please advise is there some setting required to be enabled or I have to sign code with some flags enabled.

Note: My app is not running in a Sandbox or Hardened Runtime, which I want.

Forgot to mention, I am using almost the same setup from Security Authorisation Plugin, I have XPC client inside Authorisation Plugin which will be talking to XPS Service inside a normal/global setup daemon.

In this discussion setCodeSigningRequirement and Security Agent Plugins, it is mentioned there is some bug and feedback was raised, however clicking on it there is no information, I am not sure how to figure out how things will play in Security Authorisation plugin.

Let’s start with some random notes:

My app is not running in a Sandbox or Hardened Runtime, which I want.

You’ll need to enable the hardened runtime. It’s a requirement for notarisation.

it is mentioned there is some bug and feedback was raised, however clicking on it there is no information

In general, you can’t see the content of bugs filed by other developers. I talk about this whole issue in some depth in my Bug Reporting: How and Why? post.

Now returning to your main issue, you wrote:

I am running app directly from Xcode

Running which app? The client? An XPC server? Or both?

App-to-app communication is tricky with XPC, especially when it comes to Xcode because the way it runs your app can be misleading. See XPC and App-to-App Communication DevForums for all the details.

To test this properly you need an XPC server and a client as separate processes. The server should be either a launchd daemon or agent [1] that’s actually launched by launchd in response to demand on the named XPC endpoint.

All your code will need to be signed, because code signing requirements only make sense in the context of signed code.

It’s fine to start with a generic requirement like anchor apple generic. When it comes to testing this properly, you’ll want to apply a team constraint. That’s not a problem for the OK case but to test the NG case you’ll need a second team. I usually do this sort of thing using my Personal Team, that is, the free team you get from any Apple Account.

If you run into problems, it’d be good if you created a test project to demo the issue and posted a link to it here.

Share and Enjoy

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

[1] You can probably get away with XPC service but, seeing as your ultimate goal is to make this a launchd job, it’s best to start there.

Running which app? The client? An XPC server? Or both?

The app from Updating your app package installer to use the new Service Management API provides a way to package your app (main app + XPC Client) along with the launch daemon inside it, it is not global.

So when I am running the main app it launches and I call following.

static let ServicePlist = "com.company.usecase.daemon.plist"

class func register() {
        let service = SMAppService.daemon(plistName: ServicePlist)

        do {
            try service.register()
            print("Successfully registered \(service)")
        } catch {
            print("Unable to register \(error)")
            exit(1)
        }
    }

This registers the launch daemon in the system. The plist and launch daemon binary is packaged inside the app's Contents folder. The plist has entry where to find launch daemon binary inside the app. Once the service is up, I have a button on click of it I simply get a response from the service.

I run the app from Xcode, the app shows up, on init in background I register the service, I click on a button and it triggers the service.

Give me some time I'll prepare a sample and share here.

Thanks for the response, it cleared some doubts, for more I'll share code soon.

I have prepared the sample, the launch daemon is packaged inside the app. The app requires launch daemon's plist and binary to be located inside the app and specified location, which is controlled by Build Settings.

I tried this example and my own project with Hardened Runtime and it does not works. The original example that I took from Apple Developer's website also did not include it and hence I did not opt for it too earlier.

I commented the code that calls setCodeSigningRequirement. I have also commented SwiftUI preview as it will eager load daemon during preview. Currently I am calling register service in constructor hence the issue, I do it differently in project.

Debugging launch daemon at times is difficult main need being to understand why it is not starting, but launch daemon inside the app is much more challenging, if it fails to start for some reason, digging logs in console.app is hard. Just a view point.

When you launch the app there is single button on click of it you should get back response from XPC provider what is its pid.

The demo app is uploaded here.

Hi,

Any update on the example I shared last.

Yeah, sorry, it’s on my list of things to come back to, but things have been super busy (*cough* WWDC).

Share and Enjoy

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

ok, no issues, I'll await your response.

Aim of Installer (that I want to achieve using mechanism discussed above):

  1. Copy files packaged within app to root protected directory by launch daemon.
  2. Execute command line to install auth plugin and take backup of current setup, for rollback. (via installer launch daemon)
  3. Execute command line to install launch daemon for(paired) auth plugin (via installer launch daemon)
  4. Execute command line to store secret in system keychain (via installer launch daemon)

First time app installs auth plugin and other artefacts, on next launch helps manage them. (installation + management)

I mention this as you noted above I am not using Hardened Runtime, if I do app's launch daemon fail to work. If hardened runtime is not used it will not get Notarised, I am in a fix here.

Also call to setCodeSigningRequirement is in question.

Accepted Answer

Blat! Sometimes I can’t see the wood for the trees )-:

I downloaded your project today, installed it, re-enabled the setCodeSigningRequirement(_:) call, and then reproduced the problem. Cool.

The error code logged, -67050, is errSecCSReqFailed, aka:

% security error -67050
Error: 0xFFFEFA16 -67050 code failed to satisfy specified code requirement(s)

This is very specific. The code doesn’t satisfy the requirement. So after a bit of faffing around I used codesign to actually test the requirement on that code:

% codesign --verify -vvv -R "=anchor apple" AppleDTSLaunchDaemon1.app/Contents/Resources/DTSDaemon
…
test-requirement: code failed to satisfy specified code requirement(s)

Well, that explains why XPC is complaining! But this should work, because the code is properly signed:

% codesign -d -vvv AppleDTSLaunchDaemon1.app/Contents/Resources/DTSDaemon                        
…
Authority=Apple Development: Quinn Quinn (7XFU7D52S4)
…

And then it struck me. anchor apple is the wrong requirement. It checks whether the code is signed by Apple as Apple code. You want anchor apple generic:

% codesign --verify -vvv -R "=anchor apple generic" AppleDTSLaunchDaemon1.app/Contents/Resources/DTSDaemon
…
AppleDTSLaunchDaemon1.app/Contents/Resources/DTSDaemon: explicit requirement satisfied

And after tweaking your setCodeSigningRequirement(_:) call to use that, things worked as expected.

I suggest you review TN3127 Inside Code Signing: Requirements and the docs that it links to.


Some closing notes:

  • Setting a requirement on the client side of the connection is a bit strange. You normally do this on the server side to reject unwanted clients.

  • On the client side set the .privileged option when you create the NSXPCConnection. It restricts the lookup to the global namespace, so code running in the user’s namespace can impersonate your daemon.

  • And once you set that, there’s no real need to add a requirement on the client side because, if an attacker can replace your daemon in the global namespace, the user has bigger security problems.

  • OTOH, it shouldn’t cause any problems, so it’s fine to keep it if you want extra checking.

Share and Enjoy

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

Thanks, for the reply and insight, it should help.

However, please provide guidance on notarisation, with hardened runtime enabled, XPC stops working. (The example I have shared with you in this post can be used to test this.)

Any suggestion on this topic?

In case you want me to open another thread, please advice, I'll do so.

I did find this discussion along the same lines here but I fail to get any solution was derived, or may be I still fail to get this.

To get it running with Hardened Runtime.

with hardened runtime enabled, XPC stops working.

I’m not aware of any interaction between the hardened runtime and XPC. The App Sandbox and XPC sure, but the hardened runtime, nope, none of its security enhancements related to XPC at all.

“XPC stops working” is kinda vague. What are the specific symptoms?

Share and Enjoy

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

I just tried the example I shared with you with Hardened Runtime enabled, it seems to be working fine.

When I said XPC stops working, I meant the service fails to launch and reply, sorry I don't have error details right now. At present I cannot reproduce it with the sample I shared. I was getting it with the project I was working on.

I am surprised with the result, I clearly remember calls failing, may be I did really something stupid and assumed it does not works.

I'll revisit the project and check again, in case I am able to reproduce the issue, I'll start a new thread.

Thanks for your efforts in helping with the setCodeSigningRequirement issue, really appreciate.

setCodeSigningRequirement seems not to work in new Service Management API setup.
 
 
Q