We are developing a macOS system extension (more precisely a DNSProxy Network Extension) that is providing an NSXPCListener which is communicating with its host app via XPC, just as in the SimpleFirewall code example from WWDC 2019.
Both the host app and the system extension are sandboxed and share an app group, and this is a requirement as we want to be accepted into the mac app store.
As we believe it is good practice to keep potential attack surfaces small, we would like to make sure that only our host app can connect to the system extension via XPC.
There are numerous posts here on what exactly to do, most helpful is this one which inspired this question's title. The crucial step for this question is to get the connecting process' code object through a call to SecCodeCopyGuestWithAttributes by process id, which is provided in NSXPCListenerDelegate.listener(_, shouldAcceptNewConnection). From within the sandbox this returns an OSStatus of 100001, which is some general "Operation not permitted" and in the console I can see an error logged by kernel where the sandbox denies read access to the .app file of the connecting app:
Code Block error 14:12:13.946693+0200 kernel Sandbox: <bundleidentifier>(33545) deny(1) file-read-data /Users/<name>/Library/Developer/Xcode/DerivedData/<projectworkspace>/Build/Products/Debug/<appname>.app
If I disable the sandbox, or add a temporary exception entitlement to allow read access to the Xcode/DerivedData folder then I can retrieve the code object, so I conclude that indeed the sandbox blocking the read of the file is the problem.
The obvious fix to me seems to be to add a
Code Block com.apple.security.temporary-exception.files.absolute-path.read-only
entitlement for /Applications/<appname>.app/, but this a) might be frowned upon in the app review and b) seems somewhat brittle. For example, it would prevent the app from working from inside the user's application folder, and needs a separate entitlement to work from the Xcode debugger.Since making sure that your inter process communication is only open to a few trusted apps seems like a fairly standard precaution, I would like to ask whether there is a better option, or a recommended way to secure your XPC against unwanted connections from within a sandbox?
P.S. 1: This fairly recent question is very similar, however it remains without a satisfying conclusion and I hope to provide a better solution for the benefit of future investigators already inside this question.
P.S. 2: The fact that NSXPCListener is running inside a system extension rather than any other "normal" app in the same app group is likely not relevant, just note that the system extension is running as root while the host app is running as the normal user. This might for example make this bug (r. 63976204) relevant as we are targeting Catalina (macOS 10.15), but we are already using the entitlement necessary for a workaround for unrelated reasons.
P.S. 3: For the purposes of this question I am ignoring issues with using the process identifier, like the ones linked here
I think what you are trying to do (checking the signature of the incoming connection) does sound reasonable. I do have reservations about deploying an app that relies on the Container app and the Network System Extension communicating back and forth. The reason for this is because both processes run under a different user and so the lifetime of each process is vastly different. However, it does look like you are already aware of this judging by P.S.2.I would like to ask whether there is a better option, or a recommended way to secure your XPC against unwanted connections from within a sandbox?
This does provide some evidence on what may be the issue here. As you pointed out by the deny(1) file-read-data error, this does seem to indicate you are running into Sandbox issue. To try and resolve this I would try to do the following:If I disable the sandbox, or add a temporary exception entitlement to allow read access to the Xcode/DerivedData folder then I can retrieve the code object, so I conclude that indeed the sandbox blocking the read of the file is the problem.
1) Make sure that you are using the Sandbox without a temporary entitlement for a path outside of the container: temporary-exception.files.absolute-path.read-only.
2) Try building your Network System Extension and deploying it to common place such as /Applications when you are testing. This means building with either a Development Signing Identity or a Developer ID Signing Identity.
3) If you are having issues opening the XPC connection to the Mach Service make sure you have enabled the mach-lookup.global-name entitlement on the side opening the connection to the listener. Basically, this is a sanity check to ensure that you can at least stand up the connection without the Sandbox getting in the way.
Code Block <key>com.apple.security.temporary-exception.mach-lookup.global-name</key> <array> <string>com.the.marchservice.you.are.connectingto/string> </array>
Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com