TCC configuration (endpoint security extension) failing via MDM on Ventura

Hello there.

We have an endpoint security service that consists of a command-line tool and a client app that bundles a network extension (the command-line tool runs as a daemon via Launch Services and communicates with the extension via XPC). It works when installed manually under all OS versions, and under MacOS 12.x (Monterey) and earlier when provisioned via MDM.

However, beginning with some version of 13.x (Ventura), MDM provisioning is insufficient. The daemon is unable to connect to the extension via XPC.

Under "Full Disk Access" in System Pref^H^H^H^HSettings, an entry for our component appears but the switch is off. Turning the switch on manually at this point does not change the situation; the daemon apparently remains unable to talk to the extension.

It seems as though some additional entitlement or declaration is now needed in the MDM mobileconfig to make things work under 13.x and above, but after trying a multitude of combinations, I'm at a loss. Any hints?

Replies

the command-line tool runs as a daemon via Launch Services

You mean “via launchd”, right?

We have an endpoint security service that consists of a [daemon] and a client app that bundles a network extension

So where does your Endpoint Security client code live?

Share and Enjoy

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

You mean “via launchd”, right?

Whoops! Yes. The daemon is added using launchctl.

So where does your Endpoint Security client code live?

In the network extension (which also contains NEFilterDataProvider and NEFilterPacketProvider implementations). Its entitlements include:

  • com.apple.developer.endpoint-security.client = true
  • com.apple.developer.networking.networkextension = ["content-filter-provider-systemextension"]

I should note that the architecture is slightly strange in that each time the daemon is launched it first [re-]downloads the container app from a remote server, installs it in /Applications (possibly overwriting the prior version if there was one), and then invokes it to solicit the user interactively to grant the permissions (if not already in effect).

Nonetheless it looks like the extension is launching properly; the Console.app shows a continuous spew of its log messages indicating its event ring buffer is full (due to not being serviced by the daemon).

In the network extension

Oh, interesting.

I still have questions though. There must be at least three components in play here:

  • Your daemon

  • Your sysex, which includes both NE and ES subsystems

  • Your sysex’s container app

Right?

I should note that the architecture is slightly strange in that each time the daemon is launched it first [re-]downloads the container app from a remote server, installs it in /Applications … and then invokes it to solicit the user interactively to grant the permissions …

Well, it’s not like that could cause problems, eh?

If you (temporarily, just for testing) disable that part of your product, does TCC behave itself?

Share and Enjoy

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

(Pardon my delay getting following up; I was away for a couple of weeks.)

There must be at least three components in play here:

  • Your daemon
  • Your sysex, which includes both NE and ES subsystems
  • Your sysex’s container app

Right?

That's correct.

If you (temporarily, just for testing) disable that part of your product, does TCC behave itself?

Indeed, when I rebuild all the components on my development machine with that bit patched out, it does!

However, when I then do same with the original configuration, it actually also seems to work.

You'd be forgiven for assuming this is what was doing heretofore, but in fact I'd been testing with the existing production builds. I'm sort of working at arm's length on this, because the production build pipeline is under someone else's control, and don't personally have the Developer ID cert for this org in order to be able to simulate a complete release build locally.

Could the deployment signing or provisioning be playing a role here?

don't personally have the Developer ID cert for this org

Yeah, that’s a hassle, but it’s actually the right thing to do IMO. See The Care and Feeding of Developer ID.

Could the deployment signing or provisioning be playing a role here?

Yes.

It’s common to run into TCC problems when you mix Apple Development and Developer ID signing. My general advice is:

  • Use Apple Development signing for all the stuff on your day-to-day development machine.

  • Use a dedicate machine — typically I use a VM for this — for testing the final user experience with Developer ID signing.

I talk about this, with a different end goal, in my Testing a Notarised Product post.

Share and Enjoy

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

Thanks.

I've procured a Developer ID-signed release build that elides the step of replacing the container app (instead I first invoke the latter to activate the extension prior to starting the daemon)—and this works.

Interestingly however, when I then tried to re-prove the original scenario by cp -a a locally-stashed copy of the container app into /Applications and re-activating the extension without disturbing the running daemon, it continued to work. (I expected this procedure to be tantamount to what the daemon was doing initially, and that once I replaced the container app the XPC would fail. It did not.)

Therefore my hypothesis of container app replacement being the issue seems to be incorrect, or at least incomplete.

On a related note, this same behaviour appears to now also affect Monterey, as of the 12.6.8 update released this week. (Prior, this MDM installation worked on Monterey but not Ventura.) Can you speak to whether the recent security updates might affect this area?