I have an endpoint system extension that monitors exec system calls. It works fine, but I have to follow a very specific order when installing it.
When I (the user) click to install, I get the option to open System Settings. There, I am presented with an option to "Allow" the endpoint application.
If I:
(1) click "Allow"
and then
(2) enable full disk access
The application runs but doesn't get exec events. Console shows the error message
Failed to open service: 0xe00002d8: Caller lacks TCC authorization for Full Disk Access
Even after enabling full disk access (after allowing the extension to be installed), I do not get the exec events.
To resolve this, I have to uninstall the endpoint system extension and reinstall it.
(Note: If I first grant full disk access and then allow the endpoint system extension to be installed, everything works fine, but I suspect most users will now follow this happy path.)
Is there a way to smooth this out, so that once full disk access is granted, the endpoint system extension gets events without needing to uninstall and reinstall the endpoint agent?
I figured it out.
When my endpoint system extension encountered the initial error calling es_new_client() because the program didn't have Full Disk Access, I did not exit the program (see sample code below).
Once I added code to exit the program when an error was encountered, the operating system would restart the endpoint system extension 10 seconds later. The OS would keep starting the endpoint system extension every 10 seconds. Once the user did enable Full Disk Access, the next time the OS ran the endpoint system extension, es_new_client() succeeded and the program ran fine.
Old (bad) code:
@autoreleasepool {
dispatch_sync(myQueue, ^{
setupMonitoring();
});
}
dispatch_main();
In the code above, setupMonitoring(), which configures the es_client_t, returned a non-zero value if there was a problem (e.g., es_new_client() returned ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED), but I ignored that value.
New (working) code:
@autoreleasepool {
dispatch_sync(myQueue, ^{
if (setupMonitoring() != 0) {
exit(-1);
}
});
}
dispatch_main();