calling posix_spawn from a network System Extension

I'm attempting to create an application that uses a System Extension / Network Extension to implement a PacketTunnelProvider.

After creating and configuring the packet device, I want to spawn a child process to do the actual reading and writing of network packets. I want to do this because the child is written in Go (it uses wireguard-go and my company's Go-SDK).

When I call posix_spawn from within the System Extension, I get "Operation not permitted" as the error, and sandboxd drops a log with

Violation:       deny(1) process-exec* /private/var/root/Library/Containers/<my system extension>/Data/Documents/<my-child-binary>

Is it possible to execute other processes from within the System Extension sandbox? Do the binaries have to be stored in a particular place, and if so, where?

I attempted to build with the App Sandbox removed from the System Extension capabilities, and this seemed to fail before even executing my Network Extension code, so I'm guessing System Extensions are required to be sandboxed, but it would be nice to have that confirmed.

Answered by DTS Engineer in 801048022

First off, following up on myself...

I'm not sure if this is something I'd rely on or not, but I've pinged the network extension team for their thoughts...

The answer from the network extension team is basically, yes, this will work on macOS. It isn't something they specifically designed for or recommend, but the need to support preexisting VPN solutions meant that they used a much broader sandbox configuration on macOS than they do on iOS. You shouldn't assume this will work in all extension points, but it does work in this case for the reasons below.

I'm sorry, but again, are you sure that's the case, or is this another instance of what you "expect" to be?

Call it a complicated mix of all three. More specifically:

-Extension point hosts are in full control of their extensions sandbox. If you're curious, you can actually see the definition in the Info.plist of any framework which defines an extension point. Look for a dictionary called "NSExtensionSDK" and you'll find key "NSExtensionSandboxProfile". You can look at the Quicklook framework as a more "complete" example.

-Some extension points (including NetworkExtension) don't define the key on macOS. That means they end up falling back to the "default" sandbox... which is currently "application.sb".

With all that context, let me jump back to your original message here:

When I call posix_spawn from within the System Extension, I get "Operation not permitted" as the error, and sandboxd drops a log with

  1. Are you actually testing with the network extension "installed" (meaning, inside "/Applications/") or are you running it from "inside" Xcode's directories?

  2. Is the tool you're trying to run embedded in your extensions bundle or is it located "somewhere" else?

I'd expect the embedded case to "just work, but if it's inside your extension and it's still failing, then I think you're hitting a weird edge case caused by the the interactions between who launched you (the hosting process), the user context ("root") you're running in, and the sandbox system.

However, that won't be an issue when the app is actually installed in "/Applications/". I didn't go into this in my previous message, but the reason this works:

I've been continuing to experiment, and I've found I can exec binaries located in /Library/, so I don't believe this information is accurate.

...is that the default sandbox allows read access to a number of system wide directories, which includes "/Applications/".

In my testing, I'm able to exec files in /var/root/Downloads if I set Downloads to read/write in the signing capabilities for my System Extension, and unable to exec if I don't set this permission.

Yes, that comes from application.sb. However, just to be clear, I would not use "/var/root" (or the root user's home directory) as a data storage location. Two somewhat contradictory issues:

  1. It basically exists because the system/OpenDirectory "expects" users to have home directories and it was easier to provide a home directory than to try and provide a special case solution for "root". To the extent it's "useful", it should be used for root's "user" (for example, shell configuration) data, not system wide data.

  2. It is a true "home directory", which means there isn't any guarantee that it's actually in "/var/root". I'm sure it's not common but it is possible to relocate that directory, creating a whole new layer of complication.

I'd really like someone from the network extension team to weigh in, if possible.

My answer above came directly from them.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Is it possible to execute other processes from within the System Extension sandbox?

No, at least not through posix_spawn (or fork/exec). It's possible you might be allowed to launch an XPCService, but I suspect that will fail as well.

Do the binaries have to be stored in a particular place, and if so, where?

No, that won't matter.

Some background context here:

I attempted to build with the App Sandbox removed from the System Extension capabilities, and this seemed to fail before even executing my Network Extension code, so I'm guessing System Extensions are required to be sandboxed, but it would be nice to have that confirmed.

They are, but this also works very differently than a standard "sandboxed app". The issue here is that the "owner" of an extension point is responsible for determining what your extension can/cannot do, NOT the app extension itself. Critically, your app extension cannot "expand" it's own capabilities beyond what the extension point "owner" (in this caes, then network extension system) defined. I haven't looked at this in depth, but I suspect most of our extension points block direct process creation, both to limit security/resource issues and to keep the behavior consistent across our platforms (most of which already restrict process creation).

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Is it possible to execute other processes from within the System Extension sandbox?

No, at least not through posix_spawn (or fork/exec). It's possible you might be allowed to launch an XPCService, but I suspect that will fail as well.

I've been continuing to experiment, and I've found I can exec binaries located in /Library/, so I don't believe this information is accurate.

I ended up spelunking into /System/Library/Sandbox/application.sb to try to understand what is and is not allowed while sandboxed because I could not find any official documentation. There appear to be several paths that allow exec.

this also works very differently than a standard "sandboxed app". The issue here is that the "owner" of an extension point is responsible for determining what your extension can/cannot do, NOT the app extension itself. Critically, your app extension cannot "expand" it's own capabilities beyond what the extension point "owner" (in this caes, then network extension system) defined.

Is there any documentation that describes how the extension point sandboxing works, i.e. which profile is used?

Is it possible to execute other processes from within the System Extension sandbox?

I've been continuing to experiment, and I've found I can exec binaries located in /Library/, so I don't believe this information is accurate.

OK. To clarify my previous answer, that answer was about what I'd "expect" to work in the general case, not exactly how every extension point handles their own implementation. The actual behavior is determined by the hosting process. Those sandboxes are more restrictive than the standard app profile, but the exact behavior will vary.

I'm not sure if this is something I'd rely on or not, but I've pinged the network extension team for their thoughts...

I ended up spelunking into /System/Library/Sandbox/application.sb to try to understand what is and is not allowed while sandboxed because I could not find any official documentation. There appear to be several paths that allow exec.

Well, yes. "application.sb" is the "base" sandbox used by all applications, so there are going to be PLENTY of execution paths it allows. That's definitely not the sandbox profile app extension hosts would typically use.

Is there any documentation that describes how the extension point sandboxing works, i.e. which profile is used?

Not in the general case, no. Several of our extension points are explicit about how their sandbox is restricted, but that's generally because they're significantly more restrictive than any standard process ("NEFilterDataProvider", for example). However, "neagent" does have a man page, which says:

"neagent is part of the Network Extension framework. It is responsible for hosting Network Extension plugins, including NEProvider app extensions."

It has it's own relatively small profile, derived from system.sb. However, I would highlight the comment included at the top of the "core" sandbox profiles

;;;;;; WARNING: The sandbox rules in this file currently constitute
;;;;;; Apple System Private Interface and are subject to change at any time and
;;;;;; without notice. The contents of this file are also auto-generated and
;;;;;; not user editable; it may be overwritten at any time.

The fact that the sandbox doesn't block something doesn't mean it's behavior you should rely on.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Well, yes. "application.sb" is the "base" sandbox used by all applications, so there are going to be PLENTY of execution paths it allows. That's definitely not the sandbox profile app extension hosts would typically use.

They are, but this also works very differently than a standard "sandboxed app". The issue here is that the "owner" of an extension point is responsible for determining what your extension can/cannot do, NOT the app extension itself. Critically, your app extension cannot "expand" it's own capabilities beyond what the extension point "owner" (in this caes, then network extension system) defined.

I'm sorry, but again, are you sure that's the case, or is this another instance of what you "expect" to be?

In my testing, I'm able to exec files in /var/root/Downloads if I set Downloads to read/write in the signing capabilities for my System Extension, and unable to exec if I don't set this permission.

In applications.sb I see

(when (or (entitlement "com.apple.security.files.downloads.read-only")
          (entitlement "com.apple.security.files.downloads.read-write"))
      (allow process-exec (home-subpath "/Downloads")))

but nothing like this appears in system.sb or com.apple.neagent.sb.

I'd really like someone from the network extension team to weigh in, if possible.

Accepted Answer

First off, following up on myself...

I'm not sure if this is something I'd rely on or not, but I've pinged the network extension team for their thoughts...

The answer from the network extension team is basically, yes, this will work on macOS. It isn't something they specifically designed for or recommend, but the need to support preexisting VPN solutions meant that they used a much broader sandbox configuration on macOS than they do on iOS. You shouldn't assume this will work in all extension points, but it does work in this case for the reasons below.

I'm sorry, but again, are you sure that's the case, or is this another instance of what you "expect" to be?

Call it a complicated mix of all three. More specifically:

-Extension point hosts are in full control of their extensions sandbox. If you're curious, you can actually see the definition in the Info.plist of any framework which defines an extension point. Look for a dictionary called "NSExtensionSDK" and you'll find key "NSExtensionSandboxProfile". You can look at the Quicklook framework as a more "complete" example.

-Some extension points (including NetworkExtension) don't define the key on macOS. That means they end up falling back to the "default" sandbox... which is currently "application.sb".

With all that context, let me jump back to your original message here:

When I call posix_spawn from within the System Extension, I get "Operation not permitted" as the error, and sandboxd drops a log with

  1. Are you actually testing with the network extension "installed" (meaning, inside "/Applications/") or are you running it from "inside" Xcode's directories?

  2. Is the tool you're trying to run embedded in your extensions bundle or is it located "somewhere" else?

I'd expect the embedded case to "just work, but if it's inside your extension and it's still failing, then I think you're hitting a weird edge case caused by the the interactions between who launched you (the hosting process), the user context ("root") you're running in, and the sandbox system.

However, that won't be an issue when the app is actually installed in "/Applications/". I didn't go into this in my previous message, but the reason this works:

I've been continuing to experiment, and I've found I can exec binaries located in /Library/, so I don't believe this information is accurate.

...is that the default sandbox allows read access to a number of system wide directories, which includes "/Applications/".

In my testing, I'm able to exec files in /var/root/Downloads if I set Downloads to read/write in the signing capabilities for my System Extension, and unable to exec if I don't set this permission.

Yes, that comes from application.sb. However, just to be clear, I would not use "/var/root" (or the root user's home directory) as a data storage location. Two somewhat contradictory issues:

  1. It basically exists because the system/OpenDirectory "expects" users to have home directories and it was easier to provide a home directory than to try and provide a special case solution for "root". To the extent it's "useful", it should be used for root's "user" (for example, shell configuration) data, not system wide data.

  2. It is a true "home directory", which means there isn't any guarantee that it's actually in "/var/root". I'm sure it's not common but it is possible to relocate that directory, creating a whole new layer of complication.

I'd really like someone from the network extension team to weigh in, if possible.

My answer above came directly from them.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

calling posix_spawn from a network System Extension
 
 
Q