In the macOS 14.0 SDK, environment and library constraints were introduced, which made defense against common attack vectors relatively simple (especially with the LightWeightCodeRequirements framework added in 14.4).
Now, the application I'm working on must support macOS 13.0 too, so I was looking into alternatives that do work for those operating systems as well.
What I found myself is that the SecCode/SecStaticCode APIs in the Security Framework do offer very similar fashion checks as the LightWeightCodeRequirements framework does:
- SecCodeCopySigningInformation can return values like signing identifier, team identifier, code requirement string and so on.
- SecStaticCodeCreateWithPath can return a SecStaticCode object to an executable/app bundle on the file system.
Let's say, I would want to protect myself against launchd executable swap.
- From macOS 14.0 onward, I would use a Spawn Constraint for this, directly in the launchd.plist file.
- Before macOS 14.0, I would create a SecStaticCode object for the executable path found in the launchd.plist, and then examine its SecCodeCopySigningInformation dictionary. If the expectations are met, only then would I execute the launchd.plist-defined executable or connect to it via XPC.
Are these two equivalent? If not, what are the differences?
I’m not a big for of SMJobSubmit
. I understand why folks use it, but I’m unconvinced that it’s better than SMJobBless
. Most apps that update themselves want to be able to do this repeatedly, and SMJobBless
facilitates that.
App bundle protection is kinda key. I only see two ways to solve this problem properly:
-
SMJobBless
, which makes a copy and then checks the copy -
Relying on app data protection
Everything else suffers from at TOC/TOU, at least.
However, I agree that there are two levels of threat here:
-
Replacing the executable at any time
-
Replacing the executable at just the right time to hit the TOC/TOU window
The latter is much harder to pull off in practice.
Which brings us back to your original question. If you want to check that your helper tool is the code that you think it is — while acknowledging that your check is subject to TOC/TOU attacks on systems without app bundle protection or where app bundle protection is not working for some reason — then the code signing API is fine. I wouldn’t manually check the code’s attributes. Rather, craft an appropriate requirement — usually the existing DR on the tool is the right option here — and use SecCodeCheckValidityWithErrors
to check that requirement.
I talk more these concepts in more depth in TN3127 Inside Code Signing: Requirements.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"