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.
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
.privilegedoption when you create theNSXPCConnection. 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"