Skip to content

Instantly share code, notes, and snippets.

@jesseendahl
Last active September 14, 2015 19:03
Show Gist options
  • Save jesseendahl/72cdb8bb1941c98ccbcf to your computer and use it in GitHub Desktop.
Save jesseendahl/72cdb8bb1941c98ccbcf to your computer and use it in GitHub Desktop.
autopkgdocs.md

###Using CodeSignatureVerification in your recipe

The CodeSignatureVerification processor was added to allow both:

  1. signature verification of an package installer (.pkg).

  2. signature verification of an application bundle (.app). This option is necessary since not all software is supplied as .pkg installers. Instead software is commonly released for download 'bare' at the root of a zip archive. The processor can look inside a DMG mount, but zip's must use the Unarchiver processor first.

####Adding Application bundle (.app) verification to your recipe

For drag-drop apps downloaded as zip archives, you'd run codesign --display -r- --deep -v /path/to/.appBundle and paste the entire output of the designated key into the format below, after adding the Unarchiver processor as a previous step. Make sure that there is Sealed Resources version=2 in the output, to verify that the developer signed it with the second version of Apple's signature format, as that is what the processor checks for.

Also notice the processors placement after an EndOfCheckPhase step below, so that folks who run autopkg in 'check-only' mode won't needlessly unarchive the app and verify its contents every time the recipe runs.

<dict>
    <key>Processor</key>
    <string>EndOfCheckPhase</string>
</dict>
<dict>
    <key>Processor</key>
    <string>Unarchiver</string>
    <key>Arguments</key>
    <dict>
        <key>archive_path</key>
        <string>%pathname%</string>
        <key>destination_path</key>
        <string>%RECIPE_CACHE_DIR%/%NAME%</string>
        <key>purge_destination</key>
        <true/>
    </dict>
</dict>
<dict>
    <key>Processor</key>
    <string>CodeSignatureVerifier</string>
    <key>Arguments</key>
    <dict>
        <key>input_path</key>
        <string>%RECIPE_CACHE_DIR%/%NAME%/TextExpander.app</string>
        <key>requirement</key>
        <string>anchor apple generic and identifier "com.smileonmymac.textexpander" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or 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] = "7PKJ6G4DXL")</string>
    </dict>
</dict>

####Adding package installer (.pkg) verification to your recipe To check for a valid signature from a package, run this command: pkgutil --check-signature /path/to/pkg and fit it into this format(assuming, as in the example, it's pulled down from the vendor in a DMG, and therefore the pathname can essentially point to its mount):

<dict>   
    <key>Processor</key>   
    <string>CodeSignatureVerifier</string>   
    <key>Arguments</key>   
    <dict>   
        <key>input_path</key>   
        <string>%pathname%/Office*.*pkg</string>
        <key>expected_authority_names</key>
        <array>
            <string>Developer ID Installer: Microsoft Corporation</string>
            <string>Developer ID Certification Authority</string>
            <string>Apple Root CA</string>
        </array>
    </dict>
</dict>

###How Application bundle (.app) verification works

CodeSignatureVerifier key: requirement

Purpose: Application (.app) bundle verification

Apple binary utility used: codesign

What Autopkg does:

  1. Verifies the developer ID cert was issued by Apple.

  2. Verifies a specific developer ID by checking the value in the OU field. This prevents an attacker from tricking the user into trusting it because it's signed (but is signed by an attacker's developer cert instead the real developer's).

Detailed explanation:

Code signature verification for .app bundles uses codesign tool which verifies that the item is correctly signed and hasn't been tampered with. It does not (in its default form) evaluate trust. This means that you can easily use codesign to evaluate self-signed signatures given a proper requirement string. The CodeSignatureVerifier processor, when given an app bundle, uses the requirement string to evaluate the validity. If there's no requirement string, CodeSignatureVerifier verifies the bundle against it's own designated requirement. In most cases the requirement string should be copied by a recipe author from the apps designated requirement. Let's tear one of these requirements apart:

anchor apple generic and identifier "com.hjuutilainen.MunkiAdmin" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or 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] = "8XXWJ76X9Y")

  • anchor apple generic means that the code must be signed by Apple, including code signed using a signing certificate issued by Apple to other developers.
  • identifier "com.hjuutilainen.MunkiAdmin" means that the unique identifier string embedded in the code signature is exactly equal to "com.hjuutilainen.MunkiAdmin".
  • certificate leaf[field.1.2.840.113635.100.6.1.9] means that the leaf (the private Developer ID certificate) must have a particular field present.
  • certificate leaf[subject.OU] = "8XXWJ76X9Y" means that the leaf must have a specific subject value of "8XXWJ76X9Y" in the Organizational Unit field.

##How installer package (.pkg) verification works

CodeSignatureVerifier key: expected_authority_names

Purpose: Installer .pkg verification

Apple binary utility used: pkgutil

What Autopkg does:

  1. Checks that the package is signed by a root cert trusted by the System root store.

  2. Checks the expected_authority_names array to make sure that the specific issuers we expect were used.

Detailed explanation:

Installer package verification uses pkgutil tool which verifies that the item is correctly signed, hasn't been tampered with and also checks if it is trusted by the system. This check is done first and it must pass. Only if the package is valid and trusted, the expected_authority_names array is used and matched against the strings we got back from the validation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment