For browser extension to communicate with a native app there must be a helper app. It is launched by the browser and the communication happens via stdin and stdout. I wrote such a helper app in Swift, it works.
I'd like to add security checks to the helper app.
- To make sure that the parent process is one of the approved browsers - I can do this with NSRunningApplication(processIdentifier: getppid())?.bundleIdentifier
- To make sure the parent process has valid signature
- To make sure that the other peer of the stdin/stdout pipes is the parent process
Do you know ways to achieve 2 and 3? Does the way I am doing 1 look correct to you?
Well, that’s kinda depressing, at least from a security standpoint )-: But I’m glad it’s not Safari that sent you down this path (-:
Anyway, it rules out XPC options, so let’s return to your initial questions.
Parent Process ID
The result from getppid
is not particularly trustworthy. There are two common situations where it’s wrong:
-
If the parent process exits, you get reparented. The rules for that are… Quinn literally dusts of his copy of Advanced Programming in the Unix Environment that the parent pid changes to 1.
-
If you’re run under the debugger, the parent pid is that of the debugger.
Keep in mind that process IDs are, in general, subject to process ID reuse attacks.
Bundle ID
Don’t use bundle IDs for anything related to security. I can quite happily create an app with a bundle ID of com.apple.Finder
!
Instead, rely on the code signature; see below for info on how to get access to that.
If you want to identify code on the Mac, use its designated requirement. I talk about this in gory detail in TN3127 Inside Code Signing: Requirements.
Code Signature
The best way to get the code signature fro a process is via its audit token. See the code here.
You don’t have an audit token )-: so you’ll have to use kSecGuestAttributePid
. That’s less than ideal due to the pid reuse vulnerability.
Other End of the Pipe
This requirement isn’t valid because, due to file descriptor sharing, the other end of a pipe might be available to multiple processes. Generally, solutions in this space yield one of two values:
-
All processes at the other end of the pipe.
-
The process that created the other end of the pipe.
I’m not aware of any good way to do this with a pipe. If the remote peer just happened to use a Unix domain socket, you can do it with LOCAL_PEERTOKEN
, or older stuff like LOCAL_PEERPID
and LOCAL_PEERCRED
.
If the remote peer really did use a pipe then I think the only option is to use libproc grovel through its file descriptors. This is not easy, and will almost certainly require privilege escalation, so it’s hard to recommend.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"