Xcode Signing Fails: Provisioning Profile "doesn't match" com.apple.developer.driverkit.userclient-access entitlement

Hello everyone,

I am migrating a legacy KEXT to a DriverKit (DEXT) architecture. While the DEXT itself is working correctly, I am completely blocked by a code signing issue when trying to establish the UserClient connection from our SwiftUI management app.

Project Goal & Status:

  • Our DEXT (com.accusys.Acxxx.driver) activates successfully (systemextensionsctl list confirms [activated enabled]).
  • The core functionality is working (diskutil list shows the corresponding disk device node).

The Core Problem: The userclient-access Signing Error

  • To allow the app to connect to the DEXT, the com.apple.developer.driverkit.userclient-access entitlement is required in the app's .entitlements file.

  • However, as soon as this entitlement is added, the build fails.

  • Both automatic and manual signing fail with the same error:

    `Provisioning profile ... doesn't match the entitlements file's value for the ... userclient-access entitlement.`
    
  • This build failure prevents the generation of an .app bundle, making it impossible to inspect the final entitlements with codesign.

What We've Confirmed:

  • The necessary capabilities (like DriverKit Communicates with Drivers) are visible and enabled for our App ID on the developer portal.
  • The issue persists on a clean system state and on the latest macOS Sequoia 15.7.1.

Our Research and Hypothesis:

  • We have reviewed the official documentation "Diagnosing issues with entitlements" (TN3125).
  • According to the documentation, a "doesn't match" error implies a discrepancy between the entitlements file and the provisioning profile.
  • Given that we have tried both automatic and manual profiles (after enabling the capability online), our hypothesis is that the provisioning profile generation process on Apple's backend is not correctly including the approved userclient-access entitlement into the profile file itself. The build fails because Xcode correctly detects this discrepancy.

Our Questions:

Did we misunderstand a step in the process, or is the issue not with the entitlement request at all? Alternatively, are there any other modifications we can make to successfully connect our App to the DEXT and trigger NewUserClient?

Thank you for any guidance.

Answered by DTS Engineer in 861711022

Following up with this to clear up some odds and ends:

Provisioning profile ... doesn't match the entitlements file's value for the ... userclient-access entitlement.

One thing to be aware of her is that Xcode has a "bias" in the way it presents codesign errors where it assumes the Entitlement.plist is "correct" and the profile is "wrong". However, in practice that's basically "never" the case with DriverKit entitlements and tends to lead to a lot of "flailing" trying to somehow "fix" the provisioning profile. This error ALWAYS means that "the entitlement.plist doesn't match the profile". You fix that by:

  1. Changing the Entitlement.plist to match the profile.

  2. Changing the actual profile. That means either:

  • Submitting a new request to correct any mistake (this case).

  • IF you have been granted multiple instances of the same entitlement, then you switch to manual profile generation and manual codesigning. See this forum post for more details on that flow.

However, the key here is to understand that this:

...our hypothesis is that the provisioning profile generation process on Apple's backend is not correctly including the approved userclient-access entitlement into the profile file itself.

...basically never happens. That is, the backend side of this doesn't really "fail"*, so these issue are always about feeding the "right" data into Xcode, not that the portal didn't generate what it was "told" to generate.

*While I'm sure that the portal has in fact had bugs and issues, I have never actually seen this occur and don't think that kind of failure would look anything like yours.

com.apple.developer.driverkit.userclient-access: com.accusys.Acxxx.app

Yes, this is the core problem as the bundle ID in the request value needs to be the DEXT you'll be communicating with.

The communicates-with-drivers entitlement is not working as expected, as it fails to grant the app visibility of the IOKit service.

No, unfortunately, that's not correct. It doesn't work on macOS because it's an iPad Only Entitlement:

"A Boolean value that indicates whether an iPadOS app can communicate with drivers."

Shifting to the problem here:

The IOServiceGetMatchingService("DriverKitAcxxx") call fails to find the service, and no logs from NewUserClient() are ever generated on the DEXT side.

What's makes this complicated is to talk through is that there are two different "sides" to the connection, both of which get to "veto" the connection attempt:

  1. The DEXT/Kernel connection, which "com.apple.developer.driverkit.allow-any-userclient-access" disables.

  2. The app side, particularly the app sandbox, which is what's actually failing here.

On that second point if you test with "IOServiceGetMatchingService", I think you'll find that it can't actually find "any" services (or at least, not the full set). In other words, the problem here isn't that you can't connect to "your driver", it's that your app can't really interact with IOKit. In any case, I believe there are three ways you can make this work:

  1. Disable the sandbox in the app, as the app sandbox is what's actually blocking the request.

  2. Adding the "com.apple.developer.driverkit.userclient-access" with the correct value, which is what you're currently trying to fix.

  3. I believe self adding the "right" "IOKit User Client Class Temporary Exception" value would make this work. Note that the value here is the kernel's "view" of reality, so you'd use the value "IOUserUserClient"*, NOT the class name your DEXT uses.

*IOUserUserClient is the class in the kernel that handles routing user space requests coming into the kernel "over" to your DEXT.

Note that that term "Temporary Exception" can be somewhat misleading. These entitlements where labeled as "temporary" when we introduced them because they were all cases where we didn't think the entitlement was a "good" solution to the issue and we expected to introduce new entitlements that were the "right" solution (which we largely have). However, I don't think we've actually disabled any of them. I'd probably avoid shipping code that used #3, but that's only because #2 is fairly straight forward. If I had to ship something "now" and couldn't get #2 sorted out, then I would certainly try #3 before resorting to #1.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

go to ~/Library/Developer/Xcode/UserData/Provisioning Profiles and use QuickLook to inspect the Xcode-generated .provisionprofile file for your app, to be sure that its com.apple.developer.driverkit.userclient-access claim is correct.

in the meantime, you could set com.apple.developer.driverkit.allow-any-userclient-access to true in your driver.

Hi Smith,

Thank you so much for your excellent suggestions. I've followed your advice and have some definitive results to share.

Regarding your first suggestion (Inspecting the Provisioning Profile):

You were absolutely right. I inspected the latest Xcode-managed .provisionprofile for my app (com.accusys.Acxxx.app) after a complete cleanup. The profile does not contain the correct value for the userclient-access entitlement.

Here is what the profile's Entitlements dictionary contains:

com.apple.developer.driverkit.userclient-access: com.accusys.Acxxx.app
com.apple.application-identifier: K3TDMD9Y6B.com.accusys.Acxxx.app
keychain-access-groups: K3TDMD9Y6B.*

As you can see, the value for userclient-access is incorrectly set to the app's own bundle ID, not the DEXT's (com.accusys.Acxxx.driver).

Regarding your second suggestion (Using allow-any-userclient-access): I also tried the alternative strategy you proposed, which is a setup we had already been testing. Here is the configuration:

App Entitlements (DriverKitSampleApp.entitlements):

We use the generic communicates-with-drivers entitlement.

<key>com.apple.developer.driverkit.communicates-with-drivers</key><true/>
<key>com.apple.security.app-sandbox</key><false/>
<key>com.apple.developer.system-extension.install</key><true/>

DEXT Entitlements (AcxxxDriver.entitlements): We enable allow-any-userclient-access as you suggested.

<key>com.apple.developer.driverkit</key>
<true/>
<key>com.apple.developer.driverkit.allow-any-userclient-access</key>
<true/>
<key>com.apple.developer.driverkit.family.scsicontroller</key>
<true/>

Result:

With this configuration, the app builds successfully, but it still fails at runtime. The IOServiceGetMatchingService("DriverKitAcxxx") call fails to find the service, and no logs from NewUserClient() are ever generated on the DEXT side.

Conclusion:

Your suggestions helped us confirm two critical issues on macOS Sequoia 15.7.1:

The provisioning system generates an incorrect userclient-access entitlement. The communicates-with-drivers entitlement is not working as expected, as it fails to grant the app visibility of the IOKit service.

Both paths for App-DEXT communication appear to be blocked. We have filed a Feedback report with these findings. Any other workarounds you could suggest would be greatly appreciated.

Thanks again for your help!

Hi Smith,

To add more context to my previous reply, especially regarding the userclient-access entitlement:

The main reason we switched our testing focus to the communicates-with-drivers entitlement is because the userclient-access path is completely blocked at build time for us, exactly as you suspected.

Here is a detailed breakdown of what happens when we use userclient-access:

Entitlement Configuration: We configure our DriverKitSampleApp.entitlements with the correct, specific bundle ID of our DEXT:

<key>com.apple.developer.driverkit.userclient-access</key>
<array>
    <string>com.accusys.Acxxx.driver</string>
</array>

Immediate Build Failure: As soon as we try to build the app with this entitlement, Xcode's signing process fails instantly. This happens with both "Automatic Signing" and a manually created Provisioning Profile.

The Error Message: The error is always the same:

Provisioning profile "..." doesn't match the entitlements file's value for the com.apple.developer.driverkit.userclient-access entitlement.

The Proof (from QuickLook): Following your advice, we inspected the generated provisioning profile. It confirms that the profile issued by Apple's backend incorrectly sets the value for userclient-access to the app's own bundle ID (...Acxxx.app) instead of the DEXT's (...Acxxx.driver).

That is why we are currently focusing our efforts on trying to get the alternative communicates-with-drivers path to work, which unfortunately also seems to be broken at runtime on macOS Sequoia.

Charles

are you developing for macOS or iPadOS, or both?

com.apple.developer.driverkit.communicates-with-drivers is for iPadOS. It won't do any harm to set this to true in a macOS app, but you don't need it.

When you say your driver "fails at runtime", there are various ways this can occur:

  1. it fails to load (perhaps due to an entitlement issue),
  2. your code fails to find the driver
  3. your code finds the driver, but fails to open a user client for it

It sounds like you're failing at stage 2.

Look in the system log for messages related to your driver's bundle ID. I like to plug in the device, then run

log collect --last 1m

and peruse the resulting log file in the Console app.

Use IORegistryExplorer (part of the additional tools for Xcode) to look for your driver in the IORegistry. If it isn't there, IOServiceGetMatchingService won't find it. IOServiceGetMatchingService takes a matching dictionary as a parameter, but you said you are calling

IOServiceGetMatchingService("DriverKitAcxxx")

Did you mean IOServiceNameMatching, or IOServiceGetMatchingService and you are passing in a dictionary created with CreateNameMatchingDictionary? Or something else? If you're using a matching dictionary, compare its contents carefully with the properties of your driver. Not that the name of the service is not a property called "name" (or at least, IORegistryExplorer doesn't display it as such).

I like to use IOServiceNameMatching with a unique name (i.e. in your case I'd choose "com.accusys.Acxxx.driver"). Then, in your driver's Start_Impl, before you call RegisterService, call SetName("com.accusys.Acxxx.driver"). This enables you to write a generic name for your driver's class like "driver", while having a unique name to look for the in IORegistry.

Hi Smith,

Following your suggestion, we inspected the .provisionprofile file.

We used QuickLook on the latest Xcode-managed profile for our app (com.accusys.Acxxx.app). The Entitlements dictionary inside this profile contains the following:

com.apple.developer.driverkit.userclient-access: com.accusys.Acxxx.app

It shows that the profile generated by Apple's backend incorrectly contains the App's own bundle ID for the userclient-access value, not our DEXT's bundle ID (com.accusys.Acxxx.driver).

This should be a human error on our part during the initial entitlement request.

We incorrectly entered our App's bundle ID (.app) into the UserClient Bundle IDs field on the request form, when it should have been our DEXT's bundle ID (.driver).

We are now preparing to have our Account Holder submit a new, corrected request to Apple, ensuring that the UserClient Bundle IDs field contains the correct DEXT bundle ID this time.

We are hopeful that once this new, correct request is approved, the provisioning issue will be resolved.

Best regards,

Charles

Following up with this to clear up some odds and ends:

Provisioning profile ... doesn't match the entitlements file's value for the ... userclient-access entitlement.

One thing to be aware of her is that Xcode has a "bias" in the way it presents codesign errors where it assumes the Entitlement.plist is "correct" and the profile is "wrong". However, in practice that's basically "never" the case with DriverKit entitlements and tends to lead to a lot of "flailing" trying to somehow "fix" the provisioning profile. This error ALWAYS means that "the entitlement.plist doesn't match the profile". You fix that by:

  1. Changing the Entitlement.plist to match the profile.

  2. Changing the actual profile. That means either:

  • Submitting a new request to correct any mistake (this case).

  • IF you have been granted multiple instances of the same entitlement, then you switch to manual profile generation and manual codesigning. See this forum post for more details on that flow.

However, the key here is to understand that this:

...our hypothesis is that the provisioning profile generation process on Apple's backend is not correctly including the approved userclient-access entitlement into the profile file itself.

...basically never happens. That is, the backend side of this doesn't really "fail"*, so these issue are always about feeding the "right" data into Xcode, not that the portal didn't generate what it was "told" to generate.

*While I'm sure that the portal has in fact had bugs and issues, I have never actually seen this occur and don't think that kind of failure would look anything like yours.

com.apple.developer.driverkit.userclient-access: com.accusys.Acxxx.app

Yes, this is the core problem as the bundle ID in the request value needs to be the DEXT you'll be communicating with.

The communicates-with-drivers entitlement is not working as expected, as it fails to grant the app visibility of the IOKit service.

No, unfortunately, that's not correct. It doesn't work on macOS because it's an iPad Only Entitlement:

"A Boolean value that indicates whether an iPadOS app can communicate with drivers."

Shifting to the problem here:

The IOServiceGetMatchingService("DriverKitAcxxx") call fails to find the service, and no logs from NewUserClient() are ever generated on the DEXT side.

What's makes this complicated is to talk through is that there are two different "sides" to the connection, both of which get to "veto" the connection attempt:

  1. The DEXT/Kernel connection, which "com.apple.developer.driverkit.allow-any-userclient-access" disables.

  2. The app side, particularly the app sandbox, which is what's actually failing here.

On that second point if you test with "IOServiceGetMatchingService", I think you'll find that it can't actually find "any" services (or at least, not the full set). In other words, the problem here isn't that you can't connect to "your driver", it's that your app can't really interact with IOKit. In any case, I believe there are three ways you can make this work:

  1. Disable the sandbox in the app, as the app sandbox is what's actually blocking the request.

  2. Adding the "com.apple.developer.driverkit.userclient-access" with the correct value, which is what you're currently trying to fix.

  3. I believe self adding the "right" "IOKit User Client Class Temporary Exception" value would make this work. Note that the value here is the kernel's "view" of reality, so you'd use the value "IOUserUserClient"*, NOT the class name your DEXT uses.

*IOUserUserClient is the class in the kernel that handles routing user space requests coming into the kernel "over" to your DEXT.

Note that that term "Temporary Exception" can be somewhat misleading. These entitlements where labeled as "temporary" when we introduced them because they were all cases where we didn't think the entitlement was a "good" solution to the issue and we expected to introduce new entitlements that were the "right" solution (which we largely have). However, I don't think we've actually disabled any of them. I'd probably avoid shipping code that used #3, but that's only because #2 is fairly straight forward. If I had to ship something "now" and couldn't get #2 sorted out, then I would certainly try #3 before resorting to #1.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Hi Kevin,

Thank you for the detailed response. Here is a summary of our tests based on the solutions you proposed:

Regarding the userclient-access Entitlement:

  • We discovered a human error in our initial entitlement request (we had entered the wrong Bundle ID).

  • We have submitted a new, corrected request with the proper DEXT Bundle ID and are currently awaiting its approval.

Regarding the Temporary Solutions:

To continue development while waiting, we tried your other suggestions, but are still blocked.

App Sandbox: We confirm that com.apple.security.app-sandbox is set to false in our app's entitlements. This alone did not solve the issue.

"IOKit User Client Class Temporary Exception": We implemented your third suggestion precisely. Our app's .entitlements file for this test was configured exactly as follows:

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.temporary-exception.iokit-user-client-class</key>
    <array>
        <string>IOUserUserClient</string>
    </array>
    <key>com.apple.security.app-sandbox</key>
    <false/>
    <key>com.apple.developer.system-extension.install</key>
    <true/>
    <key>com.apple.security.get-task-allow</key>
    <true/>
</dict>
</plist>

Result: Failure. Even with this entitlement configuration, the call to IOServiceGetMatchingService("DriverKitAcxxx") still fails at runtime and returns IO_OBJECT_NULL.

IOKit Registry Confirms the Service IS Active:

Although IOServiceGetMatchingService fails, the ioreg command provides definitive proof that our DEXT service is active, registered, and correctly configured.

Here is the output from ioreg -l | grep Acxxx:

+-o DriverKitAcxxx  <class IOUserSCSIParallelInterfaceController, ... registered, matched, active, ...>
  |   "IOUserClass" = "DriverKitAcxxx"
  |    "IOUserServerName" = "com.accusys.Acxxx.driver"
...
  |    "UserClientProperties" = {"IOClass"="IOUserUserClient","IOUserClass"="DriverKitAcxxxUserClient"}

This proves that the DriverKitAcxxx service exists and should be discoverable in the IOKit Registry.

Summary & Question:

We are in a situation where the IOKit Registry verifiably contains our active service, but our app (even with the temporary exception entitlement) is unable to find it via the standard IOServiceGetMatchingService API.

Given this hard evidence from ioreg, are there any other mechanisms or sandbox restrictions that could be preventing our app from "seeing" this active IOKit service?

Thank you again for your continued support.

Best Regards,

Charles

are you using

IOServiceGetMatchingService("DriverKitAcxxx")

as shorthand for

let dict = IOServiceNameMatching("DriverKitAcxxx")
        service = IOServiceGetMatchingService(kIOMainPortDefault, dict)

and did you call RegisterService from your driver code?

Accepted Answer

Given this hard evidence from ioreg, are there any other mechanisms or sandbox restrictions that could be preventing our app from "seeing" this active IOKit service?

There are more details below, but the bottom line is that I think the problem is that your "search" code is incorrect, not that we’re blocking access to the kernel. I can say that there isn't any mechanism on macOS that would "hide" a specific service, particularly not a service that wasn't "ours". Keep in mind that the tree structure of IOKit means that blocking access to an arbitrary object can have bizarre and very unpredictable consequences, particularly in the mass storage stack.

App Sandbox: We confirm that com.apple.security.app-sandbox is set to false in our app's entitlements. This alone did not solve the issue.

This does work and is actually how our sample project works. Have you tried modifying that sample to find your driver?

Result: Failure. Even with this entitlement configuration, the call to IOServiceGetMatchingService("DriverKitAcxxx") still fails at runtime and returns IO_OBJECT_NULL.

So, there are two issues here:

(A)

On the "security" front, test with "IOServiceGetMatchingServices", so you can differentiate between:

  1. Your process was specifically blocked from accessing the registry.

  2. Your process was allowed to search the registry, but didn't find anything.

If you get an error, please post back with the error code; otherwise, continue to "B".

(B)

In terms of your code, if you're specifically doing this:

IOServiceGetMatchingService("DriverKitAcxxx") 

...then I think that's wrong and will not work. IOServiceGetMatchingService looks for a specific class in the kernel, and your class ("DriverKitAcxxx") does not exist in the kernel. If you look at our user client sample code, it avoids this problem by using this as its match dictionary:

static const char* dextIdentifier = "NullDriver";
...
CFMutableDictionaryRef matchingDictionary = IOServiceNameMatching(dextIdentifier);
if (matchingDictionary == NULL)
{
	fprintf(stderr, "Failed to initialize matchingDictionary.\n");
	UserClientTeardown();
	return false;
}

...which works because the default behavior of DriverKit is to rename the in-kernel driver accordingly. As a side note here, if you're going to use name-based matching, then I'd strongly recommend following the advice given earlier:

I like to use IOServiceNameMatching with a unique name (i.e. in your case I'd choose "com.accusys.Acxxx.driver").

You can see this in your own registry snippet:

+-o DriverKitAcxxx <class IOUserSCSIParallelInterfaceController, ... registered, matched, active, ...>

However, note that your class is "IOUserSCSIParallelInterfaceController", NOT "DriverKitAcxxx".

What I would actually do here is one or more of the following:

  1. Try IOServiceNameMatching("DriverKitAcxxx").

  2. If that fails, try IOServiceMatching("IOUserSCSIParallelInterfaceController") and look at everything that returns.

  3. If THAT fails, use IORegistryCreateIterator() and just walk the entire registry until you find what you're looking for.

With all of those approaches, there's a "print" function you can steal (assuming you don't already have your own) from this forum post that will dump out the entry information, which you can then use to "tune" your matching dictionary properly.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Hi Kevin,

Thank you for the suggestions.

We have conducted a test of your third proposal regarding the IOKit User Client Class Temporary Exception.

Our test procedure was as follows:

We started from a known-good baseline commit where our DEXT loads correctly and the/dev/diskX node appears (though it reports 0 bytes, which is a separate issue we are debugging).

We made only one change: we updated our App's .entitlements file to include the temporary exception.

For absolute clarity, the exact content of the .entitlements file used for this test was:

<dict>
    <key>com.apple.security.temporary-exception.iokit-user-client-class</key>
    <array>
        <string>IOUserUserClient</string>
    </array>
    <key>com.apple.security.app-sandbox</key>
    <false/>
    <key>com.apple.developer.system-extension.install</key>
    <true/>
</dict>

After a full clean, build, and reboot, the IOServiceGetMatchingService("DriverKitAcxxx") call still fails at runtime, returning IO_OBJECT_NULL.

This result seems to indicate that the temporary exception entitlement is not sufficient to grant our app visibility of the active IOKit service on macOS.

Best,

Charles

Xcode Signing Fails: Provisioning Profile "doesn't match" com.apple.developer.driverkit.userclient-access entitlement
 
 
Q