Can the Endpoint Security Extension communicate with a regular app

I'm developing a system that uses an ES extension to control user file openings on Mac.

When a user tries to open a file, the ES extension can either allow or deny the user from opening it. However, the policy for allowing/denying users to open files is managed by my normal Mac app. Therefore, the ES extension needs to proactively communicate with the normal app.

Initially, I wanted to create an XPC service in my regular app, but according to the documentation, XPC services are managed by launchd and cannot be created by regular apps.

So if I want my ES extension to communicate with the regular app proactively, what IPC method can I use?

Answered by DTS Engineer in 797817022
I attempted creating a local CFMessagePort on the app side, but ES couldn't obtain the remote CFMessagePort. The CFMessagePortCreateRemote always return nil.

Right. This is because they run in different execution contexts. If you’re going to work at this level of macOS, you need to understand how execution contexts work on that system. The best [1] explanation of that is Technote 2083 Daemons and Agents. It’s super old, and a few details have changed over the years, but all the basic ideas are still accurate.

Your sysex runs in the global context. Each instance of your app runs in its own GUI login context. Connecting from your sysex to your app is nonsense because there’s no way to know which instance of your app would receive the connection. Rather, each instance of your app should connect to your sysex.

ES explicitly supports this via the the NSEndpointSecurityMachServiceName property. See the EndpointSecurity man page for details.

Note You could use CFMessagePort for your IPC, but please don’t. Rather use one of our XPC APIs. See XPC Resources for links to documentation and so on.

Keep in mind that your app is not necessarily running. You need to decide what to do if the user quits your app. In many cases the best option is to have a launchd agent running in each GUI login context. That can, if necessary, interact with the sure, or even launch your app.

Share and Enjoy

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

[1] But I’m biased, because I wrote it (-:

It sounds like your ES client is a sysex rather than a daemon. Is that correct?

It’s possible to solve handle this issue in both cases, but there’s a slightly different spin.

Share and Enjoy

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

Along with what Quinn asked, be aware that looping the user directly into the approval cycle with something like a allow/deny dialog:

When a user tries to open a file, the ES extension can either allow or deny the user from opening it. However, the policy for allowing/denying users to open files is managed by my normal Mac app. Therefore, the ES extension needs to proactively communicate with the normal app.

...won't really work or at least it won't work very well, at least not as a "general" answer. While the "on paper" deadline (<15s) can make this seem workable, there are cases where the deadline will be much shorer and most of that deadline can disappear entirely due to kernel/user space latency, ESPECIALLY under heavy system load. Our general recommendation is that you should approve/deny in <10ms, which is FAR to short to directly involve the user. I wrote more about this on a different forum post which I'd recommend taking a look at.

I think you could build a good "user" ES (as opposed to the more typically "enterprise" app), but you'd need to build around a model where the user would approve the "next" request (after you deny the first) and/or notifying the user about what's been occurring.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

There's no need to display a UI to guide the user in making a choice. We'll decide whether to allow opening a file based on its path or some other meta datas, following the policy configuration.

When ES detects that a file is about to be opened, it needs to communicate with my Mac app, and the app will use the configured policy to determine if the user can open or deny access to the file.

So, we need to find a way for ES and the Mac app to communicate effectively. I attempted creating a local CFMessagePort on the app side, but ES couldn't obtain the remote CFMessagePort. The CFMessagePortCreateRemote always return nil.

I attempted creating a local CFMessagePort on the app side, but ES couldn't obtain the remote CFMessagePort. The CFMessagePortCreateRemote always return nil.

Right. This is because they run in different execution contexts. If you’re going to work at this level of macOS, you need to understand how execution contexts work on that system. The best [1] explanation of that is Technote 2083 Daemons and Agents. It’s super old, and a few details have changed over the years, but all the basic ideas are still accurate.

Your sysex runs in the global context. Each instance of your app runs in its own GUI login context. Connecting from your sysex to your app is nonsense because there’s no way to know which instance of your app would receive the connection. Rather, each instance of your app should connect to your sysex.

ES explicitly supports this via the the NSEndpointSecurityMachServiceName property. See the EndpointSecurity man page for details.

Note You could use CFMessagePort for your IPC, but please don’t. Rather use one of our XPC APIs. See XPC Resources for links to documentation and so on.

Keep in mind that your app is not necessarily running. You need to decide what to do if the user quits your app. In many cases the best option is to have a launchd agent running in each GUI login context. That can, if necessary, interact with the sure, or even launch your app.

Share and Enjoy

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

[1] But I’m biased, because I wrote it (-:

I already have a XPC service on ES side. But according XPC, I can only send packet from APP to ES, what if I want to send packet from ES to APP?

I've noticed there is a APP called Red Canary Mac monitor based on ES. It can show the UI of the ES events. So I assume that there must have someway to send message from ES to the Mac APP.

Accepted Answer
what if I want to send packet from ES to APP?

There’s a difference between connections and requests. In XPC, you always want the ‘client’ to connect to the server. In your case, that mean the app connects to the ES sysex. However, once that connection is in place you can send requests in either direction.

There’s a trick to this, but it’s not very tricky (-: In a classic XPC setup the client sets remoteObjectInterface and the server sets exportedInterface and exportedObject. However, you can do the reverse as well. Have the client set exportedInterface and exportedObject and the server set remoteObjectInterface. That allows the server to send requests to the client.

There are some caveats here:

  • You probably want to use a different protocol. The requests that the server sends to the client are very different from the requests that the client sends to the server.

  • The server must guard itself against denial of service attacks. If the server sends unbounded requests to a client that never responds, those requests back up in the server’s address space and can cause it to run out of memory. How you do that depends on the nature of your server-to-client requests:

    • If they all take a reply, you can just refrain from sending request N+1 until you’ve received reply N.

    • If you have one-way requests, check out scheduleSendBarrierBlock(_:).

    This is particularly important on iOS [1].

Share and Enjoy

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

[1] XPC is present on iOS but it’s mostly used by the system itself. However, there is one specific place where we support XPC use by third-party code on iOS.

Wow, cool! I think I've discovered a new world! Thanks Quinn. So now I can do two way communication.

But what if the XPC receive side exited? For example, the client APP exited, and the server send a message to client according the remoteObjectProxy, but the response will never come.

But what if the XPC receive side exited?

The connection will invalidate.

Remember that there are two ways an XPC connection can fail:

  • Interruption

  • Invalidation

Interruption means that something went wrong but it’s reasonable to retry. The client would see this if the server quit, because the client’s connection references a name. If the client retries, XPC uses that name to re-establish the connection.

Invalidation means that no retry is feasible. Your server will see this if the client quits, because the connection wasn’t opened by name and thus there’s no way for the server to retry.

You can see invalidation in other cases, for example:

Share and Enjoy

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

Thank you for your detailed reply, Quinn!

Can the Endpoint Security Extension communicate with a regular app
 
 
Q