I'm working on a macOS app that I'd like to sandbox along with a login item that I'd also like to sandbox. Login items implicitly have an XPC Mach service created for them which my app is able to successfully use communicate with the sandboxed login item (because they're in the same application group).
The issue is that any non-sandboxed process can also connect to my login item's XPC Mach service, and I'd really rather that wasn't the case. I realize there's no privilege escalation going on, but this feels unnecessarily insecure. My attempts to secure the connection keep failing due to sandboxing. Is there a way to do what I'm attempting or is Apple's intention that any non-sandboxed process on the system ought to be able to successfully communicate with my login item?
If I don't sandbox my login item it's trivial for me to secure this connection.
Here's what I've tried so far:
Path based
- Retrieve the
SecCode
usingSecCodeCreateWithXPCMessage
- Retrieve the
SecStaticCode
usingSecCodeCopyStaticCode
- Retrieve the path of the static code using
SecCodeCopyPath
- Compare this path with my login item's path based on
Bundle.main.bundleURL
This fails on step 2, the SecCodeCopyStaticCode
function gets back a "UNIX error exception: 1". This kind of makes sense to me as it needs to read from the file system in order to get the static code of the running process.
Code requirements based
- Retrieve the
SecCode
usingSecCodeCreateWithXPCMessage
- Construct a
SecRequirement
including amongst other things thatcertificate leaf[subject.OU] = <my team id>
- Use
SecCodeCheckValidity
on the code instance from step #1 and the requirement from step #2
This fails on step 3, SecCodeCheckValidity
also results in a "UNIX error exception: 1". Looking at the logs generated by sandboxd
it looks like under the hood that function calls _CFBundleGetBundleVersionForURL
and fails. The violation is:
deny(1) file-read-data ~/Library/Developer/Xcode/DerivedData/LoginItemSample-ajfwjiwmyuphdbeyugmssxszdlyq/Build/Products/Debug/LoginItemSample.app
Is there perhaps some combination of SecCSFlags
values I can pass to prevent any file system access?
I'm working on a macOS app that I'd like to sandbox along with a login item that I'd also like to sandbox.
Is this because it’s a good idea in general? Or because you’re targeting the Mac App Store?
The issue is that any non-sandboxed process can also connect to my login item's XPC Mach service
Yeah, that’s a weird wrinkle.
and I'd really rather that wasn't the case.
Understandable, because…
I realize there's no privilege escalation going on
It depends on how you look at it. If you look at this from a traditional Unix privilege model, you’re absolutely correct. However, when you look at it from the per-process privilege model now supported on the Mac, this may well represent a privilege escalation. For example, the user might have granted your app a TCC privilege (in System Preferences > Security & Privacy > Privacy) and you don’t want to hand that out to anyone.
The best way to solve this problem is to have the XPC subsystem do this validation for you. See this thread. Sadly, this is not currently possible if you use NSXPCConnection
)-:
Barring that, the way I’d solve this problem is to have the client process grant access to its bundle to the login item as part of the XPC request. That’ll dynamically extended the login item’s sandbox so that it can read the code in order to validate its signature.
I believe that you can do this simply by passing a security-scoped URL in the XPC message. The login item can then call -startAccessingSecurityScopedResource
, do the validation, and then call -stopAccessingSecurityScopedResource
when it’s done.
This should be secure because it only grants the login item access to the content of the client’s bundle, which shouldn’t contain anything sensitive.
Having said that, I’ve never actually tried this [1] so lemme know how you get along (-:
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] I know that the basic strategy is sound because we use it a lot in system code. However, it’s possible that you might run into a snag caused by limitations of the public API or the App Sandbox itself.