In the LightweightCodeRequirements framework, there is a LaunchCodeRequirement
object which can be used as a requirement object for a Process for example.
What I don't understand (I admit my macOS low-level knowledge is limited) is that how can this be used in a secure way that doesn't fall victim of a Time-of-Check/Time-of-Use issue.
e.g.
- I specify a LaunchCodeRequirement via Process.launchRequirement for my process, let's say
/usr/local/bin/mycommandlinetool
. - The LaunchCodeRequirement specifies my development team and a developer ID certificate.
- The process must be started in some form, before a SecCode/SecTask object can be created, rather than a SecStaticCode object (which only guarantees its validity checks to be intact as long as the file is not modified).
- But if the process was started, then I have no tools in my set to prevent it from executing its initialization code or similar. Then, by the time I'm able to check via SecCode/SecTask functions the LaunchCodeRequirement, I might have already ran malicious code - if
mycommandlinetool
was maliciously replaced.
Or does the operating system use a daemon to copy the executable specified for Process
to a secure location, then creates the SecStaticCode object, assesses the LaunchCodeRequirement and if passed, launches the executable from that trusted location (which would make sure it is immutable for replacement by malicious actors)?
I have a hard time understanding how this works under the hood - if I remember correctly these are private APIs.
You typically tackle this by applying a launch requirement to the launch itself. If you’re using Process
[1], do this by setting the launchRequirement
property on the Process
object. This is of type LaunchCodeRequirement
, which you create using the LightweightCodeRequirements file.
This should be resilient to TOC/TOU issues. When Process
runs the executable, the kernel opens the file, runs the requirement check against the file, and then launches the file. If someone substitutes a file before the open, it gets caught by the check. If they substitute it after the open, their version isn’t executed.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] This is NSTask
in Objective-C. It can do the same thing as Process
, although the mechanics are a bit different.