SystemPolicyAllFiles code signing requirement

Hello. I have an enterprise application that requires specific privileges to execute correctly on MacOS. One of these privileges is SystemPolicyAllFiles (aka Full Disk Access), as we use the endpoint security framework.

When we distribute our application, we generate:

  • A signed, notarized pkg consisting of our application binaries.
  • An MDM-compatible .mobileconfig, which contains the SystemPolicyAllFiles setting.

We expect our users to install both to get the application to function correctly.

However, we have three environments we deploy to: Internal (local development on a developer's workstation), "development" (where features are integrated prior to release) and "production" (what our customers get).

For local, our developers create an Apple account and use a Mac Development certificate for signing. They also generate their own embedded.provisionprofile and drop that into their local installation config. For development/production, we use our Developer ID certificate and Developer Installer certificate, with an endpoint security embedded.provisionprofile bound to those.

However, when we generate a .mobileconfig, we need to include a CodeRequirement (CR) for SystemPolicyAllFiles. I've been retrieving this using codesign -dr - ... (i.e., the designated requirement aka DR). However, the designated requirement is very specific to the certificate, which is problematic specifically for local development, where each developer has their own Mac Development certificate.

Here's what the relevant section of our generated mobileconfig looks like right now:

        <dict>
          <key>SystemPolicyAllFiles</key>
          <array>
            <dict>
              <key>Allowed</key>
              <true/>
              <key>CodeRequirement</key>
              <string>identifier "com.example.app and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = <TEAMID></string>
              <key>Comment</key>
              <string>app</string>
              <key>Identifier</key>
              <string>com.exmple.app</string>
              <key>IdentifierType</key>
              <string>bundleID</string>
              <key>StaticCode</key>
              <false/>
            </dict>
          </array>
        </dict>

That's in a format that works for our Developer ID cert, but the DR for the Mac Development certificate looks like:

identifier "com.example.app" and anchor apple generic and certificate leaf[subject.CN] = "Mac Developer: John Doe (12ABC34567)" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */

Question: Is it possible to relax the code requirement so that it is generic enough to cover all Mac Developer certificates and Developer ID certificates we use? If not, is there a way to have one code requirement for our Mac Developer certificates and a separate CR for our Developer ID certificate?

My use case is deploying a static "local" .mobileconfig using our internal company MDM (Apple Business Essentials) to all developer workstations so we don't have to have each developer manually configure their system for the software to run.

Thanks!

D

Accepted Answer

I’m not exactly an expert on MDM stuff, but my understanding is that the CodeRequirement property is a requirement. It doesn’t have to be the designated requirement of the code in question. Thus, you can create a profile with this property set to a custom requirement, one that’ll accept a development-signed app built by any of your team members.

For more background on this, see TN3127 Inside Code Signing: Requirements.

Consider this:

% codesign -d -r - Test777163
Executable=/Users/quinn/Library/Developer/Xcode/DerivedData/Test777163-cihuekycmkocddfnmmrztacqdito/Build/Products/Debug/Test777163
designated => identifier Test777163 and anchor apple generic and certificate leaf[subject.CN] = "Apple Development: Quinn Quinn (7XFU7D52S4)" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */
% cat custom.txt 
identifier Test777163 
    and anchor apple generic 
    and certificate leaf[subject.OU] = "SKMME9E2Y8" 
    and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */
% codesign -v -vvv -R custom.txt Test777163
Test777163: valid on disk
Test777163: satisfies its Designated Requirement
Test777163: explicit requirement satisfied

I’ve changed the requirement to use a check the subject.OU field of the leaf, the Team ID, rather than the subject.CN field, the certificate name. I then explicitly checked that the code satisfies that requirement. Yay!

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Yeah, my biggest concern is that the OS has some requirements on the CR (specific fields being checked) even though the code requirement language seems generic. Sounds like that isn't the case.

I've made a requirement that looks like this now, and it seems to work for both cases (note: there seems to be a weird bug in the forum that pushes code fence markdown to the end of the message):

identifier "com.example.app" 
   and anchor apple generic
   and certificate leaf[subject.OU] = ##TEAMID##

However, I'm clearly not checking on the cert OIDs which you had, though I am checking on the apple generic anchor.

Security wise, I assume the apple generic anchor is good enough (unless there's some other way to get an apple-signed certificate with our developer team ID in the OU that i'm not aware of). Else I think i'd have to use an OR condition in the CR that tests for the existence of the development cert OID(s) OR the developer ID cert OID(s).

As always, thank you so much for your help. D

there seems to be a weird bug in the forum that pushes code fence markdown to the end of the message

Yep. This was introduced by our update earlier in the week. We’re working on a fix (i. 096647815).

Security wise, I assume the apple generic anchor is good enough

I wouldn’t rely on that. Rather, I recommend that you check for development certificates specifically. Apple issues a lot of certificates, for a lot of different reasons, and it’s better to err on the side of security here.

Similarly, I recommend that you only deploy this technique internally. Your customers should be use the DR of the Developer ID product.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Alright, a bit more pain but I agree with the approach of erring on the side of security. However, that creates a new challenge in that we want to run the local development AND release versions of the app (we want to develop and drink our own champagne i.e., use our product in tandem), and they both need the same permissions.

My sense is that we should probably use a local prefix on the app bundle ID (and create a new app in our Apple Developer account), such as local.com.example.app. Then, use the "generalized" developer certificate CR for the policy we apply for the local.com.example.app. The production version (for both our staging/prod envs) would then just drop the local. prefix and use the DR since both envs use our Developer ID cert.

Does that make sense?

Thanks again for the help,

D

Also, is it safe to assume we can reuse the same DR between builds of the production version of our app (assuming the developer ID cert never changes)?

Does that make sense?

Sure.

But at some point these things stop being technical questions and instead become a reflection of your policy.

is it safe to assume we can reuse the same DR between builds of the production version of our app

Yes. That is, in fact, the whole reason for a DR, in that it’s a cryptographically sound way for the code to identify itself, such that the system knows that version N+1 of your app is the ‘same code’ as version N.

If your build system has a validation mechanism, it would make sense to add a validation step that confirms that the built product has the expected DR.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Sure.

But at some point these things stop being technical questions and instead become a reflection of your policy.

I agree/understand regarding the policy. I framed the question oddly, but I was really asking if that policy made sense (i.e., was there some other approach to do what I'm saying or is there anything unforseen that I'd encounter). I've already implemented it though and it seems to work out fine, so we'll stick with it.

Yes. That is, in fact, the whole reason for a DR, in that it’s a cryptographically sound way for the code to identify itself, such that the system knows that version N+1 of your app is the ‘same code’ as version N.

Ok, that's great. My concern was that what constitutes a DR (as emitted by codesign) could change in the future, and that "same code" meant the exact code the DR was computed for at the time it was run. This is obviously not the case since it is only reliant on certificate OIDs and such (so I'd assume if the signing certificate changes that would be the only thing that invokes a change in the DR). However, I wasn't sure if that was just the current policy from Apple and that in the future, there would be some type of hash added or something. Sounds like that's not the case, so that makes things quite a bit easier.

Thank you again!

D

We’re working on a fix (i. 096647815).

And that just landed. Yay!

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

SystemPolicyAllFiles code signing requirement
 
 
Q