Check activation status of an [Endpoint Security] system extension.

Hello,

I've been working with system extensions on macOS Catalina / Big Sur (Endpoint Security extensions to be precise) and it seems that there is no 'right' way to check whether a system extension has already been approved by the user or not. You can of course use an activation OSSystemExtensionRequest and determine through the OSSystemExtensionRequestDelegate whether the user needs to approve it (a 'requestNeedsUserApproval:' message is passed) or if the extension has been loaded into the system (a 'request:didFinishWithResult:' message is passed). That's great but the major drawback is that making such a request when the extension is not loaded also starts the process of loading the system extension: The user is shown a system popup window and the 'Allow' request shows up in System Preferences > Security & Privacy.

I'm looking for a non-intrusive way of checking the load status of the system extension.
I think being able to do this is very useful. Say, for instance, you have an app with optional features, one of which requires the activation of a system extension. If we could check the load status of that system extension, we could display to the user a proper UI that can either suggest that it could be activated or show to the user that it is already approved and working.

There are some ways to achieve this that I've thought about, but they don't seem the proper way of doing things:
1) Trying to parse the output of 'systemextensionsctl list'. It seems problematic since an extension can appear multiple times in here (based on succesive activations / deactivations) and also since the output isn't particularly documented.
2) Opening an NSXPCListener from inside the system extension and determining the activation status of the extension by whether or not a process can connect to this.
3) Attempting to look for a TeamId.com.mybundle.identifier process in the output of a 'launchctl' command, such as 'sudo launchctl list TeamId.com.mybundle.identifier 2>&1 | grep PID'. I've been using this method and it seems consistent for now.

Is there a recommended way of achieving this?
Another scenario:
  1. OSSystemExtensionManager.shared.submitRequest(OSSystemExtensionRequest.activationRequest(...))

  2. User sees system modal asking to go to the settings and enable extension, but presses OK and does nothing.

  3. Next time app tries to submit new activation request one more time, delegate receives request( , actionForReplacingExtension, withExtension) and returns .cancel because versions are the same.

  4. Delegates request(_ request: OSSystemExtensionRequest, didFailWithError error: Error) is called with error OSSystemExtensionError.requestCanceled.

Nowhere in this sequence app had a chance to know that user still hasn't accepted installation of the extension.

Nowhere in this sequence app had a chance to know that user still
hasn't accepted installation of the extension.

In situations like this, where the API isn’t giving you the info you need to create a decent user experience, I encourage you to file an enhancement request describing your predicament.

Please post your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
I've submitted a suggestion with a similar description as this post through Feedback Assistant on the 11th of December 2020. The suggestion number is FB8936914. It has not received any response as of now.
I also submitted a report in Feedback assistant regarding this issue: FB8978342.

At the moment the only workaround I see to make app a little more user friendly is to always return .replace in request(request: , actionForReplacingExtension, withExtension), because in this case if user has not yet allowed installation of extension, system modal will appear one more time asking user to do it. Of course this means that after user accepts it, app will flood extensions with installation requests all the time it tries to use its extension.
jaroslavqwerty, I don't think the application would flood the system with popups if it would always return .replace. The system popup will only appear if the system extension last requested on the system is of a different version to the one you are requesting now. The delegate method wouldn't be called anyway if the versions are the same, as mentioned in the method documentation.

I always return .replace in my extension. In my experience, it seems that nothing happens to the currently loaded extension if you make more activationRequests after the first one.
Another reason you might want to always answer with .replace is that an application can uninstall the corresponding system extension loaded on the system only if it has the same version as the system extension embedded in the application. So choosing not to replace the extension might mean you end up without any way of uninstalling the given system extension (as far as I can tell).

If you are seeing a different behaviour as described in this comment, please make sure that developer mode is turned off (run systemextensionsctl developer off in a terminal)
Check activation status of an [Endpoint Security] system extension.
 
 
Q