Clarification on RPC (clnt_call()) Usage in ES and Network System Extensions

Dear Apple Developer Community,,

I understand that RPC is not the recommended IPC mechanism for communication between an Endpoint Security (ES) Extension or a Network System Extension and a daemon. However, I would like to clarify whether Apple currently allows the use of RPC (clnt_call()) as an IPC method for these extensions to communicate event details to a daemon.

Given that ES Extensions operate in a sandboxed environment, they may lack the necessary permissions to create network sockets (e.g., clnt_call() over TCP) on a properly signed macOS system with SIP enabled (macOS Sequoia).

Looking for clarification on the following points:

  1. Whether RPC (clnt_call()) is currently supported as an IPC mechanism for ES Extensions or Network System Extensions?

  2. If supported, does Apple have any plans to deprecate RPC-based IPC (such as clnt_call()) in the near future for these extensions?

I would appreciate any insights or references to official documentation on this topic.

Answered by DTS Engineer in 824682022

I understand that RPC is not the recommended IPC mechanism for communication between an Endpoint Security (ES) Extension or a Network System Extension and a daemon.

First off, it's important to be clear about what our recommendations here really mean. Our "standard" IPC recommendation is XPC. That's because it's a good API with very high performance. Many applications (including ESE's) use sockets (generally Unix, sometimes IP), typically because it's either part of an existing implementation (often cross platform) or because the technical details mean that there isn't a good way to get an XPC connection. I consider both of those APIs to be very "reasonable" solution and could happily argue for or against either one of them, depending on the context and use case. We do (quite strongly) recommend against trying to use mach ports "directly" and that's because XPC provides the same capability and experience has shown that the mach API is nearly impossible to use properly.

However, the issue ESE's have isn't API choice it's architecture and design. An ESE is so tightly integrated with the system that it has a nearly unbounded ability to disrupt and damage the overall system experience. Further complicating things, the critical auth paths must be processed very quickly (<100ms), as any kind of significant delays are what create the kinds of misbehavior ESE's are famous for creating. You can read some of what I've written about these issues here, here, and here.

That leads to here:

Given that ES Extensions operate in a sandboxed environment, they may lack the necessary permissions to create network sockets (e.g., clnt_call() over TCP) on a properly signed macOS system with SIP enabled (macOS Sequoia).

The ESE sandbox is not particularly "tight" and, more to the point, you can use an ESE in a standard daemon (not just an extension), at which point there isn't any sandbox at all. They aren't restricted from accessing the network, however, they do have a very broad execution window, so you do need be careful about assuming what may or may not be available. For example, an ESE could be running for a relatively long period of time before any network become available.

Moving to the specific question here:

However, I would like to clarify whether Apple currently allows the use of RPC (clnt_call()) as an IPC method for these extensions to communicate event details to a daemon.

A few different points here:

  • Across our entire platform (not just ESE), Apple's "support" for this kind of POSIX API has always been somewhat limited. It's an API that exists on our platform and it (presumably) does work, but it's certainly not a "core" API that's widely used by the system or that we've ever encouraged developers to rely on. How well it works in any specific context is really determined by the details of that context and the APIs implementation, not any specific design intention on our part.

  • At a purely technical level, I don't know any reason why an ESE couldn't use those APIs. As far as the lower level system is called, this is basically just another network API and the ESE has access to the network. The only way to be sure that it DOES in fact work would be to test it, but my expectation is that it probably does work.

  • Any conversation about specific IPC APIs in an ESE makes me very nervous. Much of the appeal IPC APIs provide is the ability to interact with remote endpoints as if they were local API and that illusion is VERY dangerous in an ESE. The performance requirements are simply to tight for IPC to be directly involved in any auth call, but that's exactly the kind of design trap many ESE implementations fall into.

Whether RPC (clnt_call()) is currently supported as an IPC mechanism for ES Extensions

See above.

or Network System Extensions?

Network Extensions are completely unrelated to ESE and the actual behavior almost certainly varies between network extension points. My guess is that it probably does work most of the time, but I also wouldn't be surprised if the unusual network environment a NSE operates in ends up breaking it.

If supported, does Apple have any plans to deprecate RPC-based IPC (such as clnt_call()) in the near future for these extensions?

Future plans aren't something we're ever going to talk about. Having said that, I think it's important to understand that the main risk with this kind of API isn't that we'll formally deprecated or remove it, but that an unrelated will break it by "accident" and that it's non-critical nature means that it take awhile for use to resolve the issue.

I would appreciate any insights or references to official documentation on this topic.

The big thing I'd emphasize here is that you need to be VERY careful about how your ESE interacts with ANY "external" resource, regardless of IPC API. More specifically:

  • You're ESE should be able to process ALL auth requests without requiring ANY external "resource" at all.

  • Any access/manipulation of external resources needs to be done "outside" of auth path.

  • Data sharing between the auth path and the external resource "system" needs to be handled carefully to ensure that it doesn't create a bottleneck that stalls the auth system.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Accepted Answer

I understand that RPC is not the recommended IPC mechanism for communication between an Endpoint Security (ES) Extension or a Network System Extension and a daemon.

First off, it's important to be clear about what our recommendations here really mean. Our "standard" IPC recommendation is XPC. That's because it's a good API with very high performance. Many applications (including ESE's) use sockets (generally Unix, sometimes IP), typically because it's either part of an existing implementation (often cross platform) or because the technical details mean that there isn't a good way to get an XPC connection. I consider both of those APIs to be very "reasonable" solution and could happily argue for or against either one of them, depending on the context and use case. We do (quite strongly) recommend against trying to use mach ports "directly" and that's because XPC provides the same capability and experience has shown that the mach API is nearly impossible to use properly.

However, the issue ESE's have isn't API choice it's architecture and design. An ESE is so tightly integrated with the system that it has a nearly unbounded ability to disrupt and damage the overall system experience. Further complicating things, the critical auth paths must be processed very quickly (<100ms), as any kind of significant delays are what create the kinds of misbehavior ESE's are famous for creating. You can read some of what I've written about these issues here, here, and here.

That leads to here:

Given that ES Extensions operate in a sandboxed environment, they may lack the necessary permissions to create network sockets (e.g., clnt_call() over TCP) on a properly signed macOS system with SIP enabled (macOS Sequoia).

The ESE sandbox is not particularly "tight" and, more to the point, you can use an ESE in a standard daemon (not just an extension), at which point there isn't any sandbox at all. They aren't restricted from accessing the network, however, they do have a very broad execution window, so you do need be careful about assuming what may or may not be available. For example, an ESE could be running for a relatively long period of time before any network become available.

Moving to the specific question here:

However, I would like to clarify whether Apple currently allows the use of RPC (clnt_call()) as an IPC method for these extensions to communicate event details to a daemon.

A few different points here:

  • Across our entire platform (not just ESE), Apple's "support" for this kind of POSIX API has always been somewhat limited. It's an API that exists on our platform and it (presumably) does work, but it's certainly not a "core" API that's widely used by the system or that we've ever encouraged developers to rely on. How well it works in any specific context is really determined by the details of that context and the APIs implementation, not any specific design intention on our part.

  • At a purely technical level, I don't know any reason why an ESE couldn't use those APIs. As far as the lower level system is called, this is basically just another network API and the ESE has access to the network. The only way to be sure that it DOES in fact work would be to test it, but my expectation is that it probably does work.

  • Any conversation about specific IPC APIs in an ESE makes me very nervous. Much of the appeal IPC APIs provide is the ability to interact with remote endpoints as if they were local API and that illusion is VERY dangerous in an ESE. The performance requirements are simply to tight for IPC to be directly involved in any auth call, but that's exactly the kind of design trap many ESE implementations fall into.

Whether RPC (clnt_call()) is currently supported as an IPC mechanism for ES Extensions

See above.

or Network System Extensions?

Network Extensions are completely unrelated to ESE and the actual behavior almost certainly varies between network extension points. My guess is that it probably does work most of the time, but I also wouldn't be surprised if the unusual network environment a NSE operates in ends up breaking it.

If supported, does Apple have any plans to deprecate RPC-based IPC (such as clnt_call()) in the near future for these extensions?

Future plans aren't something we're ever going to talk about. Having said that, I think it's important to understand that the main risk with this kind of API isn't that we'll formally deprecated or remove it, but that an unrelated will break it by "accident" and that it's non-critical nature means that it take awhile for use to resolve the issue.

I would appreciate any insights or references to official documentation on this topic.

The big thing I'd emphasize here is that you need to be VERY careful about how your ESE interacts with ANY "external" resource, regardless of IPC API. More specifically:

  • You're ESE should be able to process ALL auth requests without requiring ANY external "resource" at all.

  • Any access/manipulation of external resources needs to be done "outside" of auth path.

  • Data sharing between the auth path and the external resource "system" needs to be handled carefully to ensure that it doesn't create a bottleneck that stalls the auth system.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thank you for your detailed response, truly appreciate your time.

Clarification on RPC (clnt_call()) Usage in ES and Network System Extensions
 
 
Q