]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_codesigning/lib/StaticCode.cpp
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / StaticCode.cpp
index b7fb0d7719a18753972cac3f3ef969ae27dcd8b5..85abf01ac3867e0c71584636d49d4f6a773b0161 100644 (file)
 #include "StaticCode.h"
 #include "Code.h"
 #include "reqmaker.h"
 #include "StaticCode.h"
 #include "Code.h"
 #include "reqmaker.h"
+#if TARGET_OS_OSX
 #include "drmaker.h"
 #include "drmaker.h"
+#include "notarization.h"
+#endif
 #include "reqdumper.h"
 #include "reqparser.h"
 #include "sigblob.h"
 #include "resources.h"
 #include "detachedrep.h"
 #include "reqdumper.h"
 #include "reqparser.h"
 #include "sigblob.h"
 #include "resources.h"
 #include "detachedrep.h"
+#include "signerutils.h"
+#if TARGET_OS_OSX
 #include "csdatabase.h"
 #include "csdatabase.h"
+#endif
 #include "dirscanner.h"
 #include <CoreFoundation/CFURLAccess.h>
 #include <Security/SecPolicyPriv.h>
 #include <Security/SecTrustPriv.h>
 #include <Security/SecCertificatePriv.h>
 #include "dirscanner.h"
 #include <CoreFoundation/CFURLAccess.h>
 #include <Security/SecPolicyPriv.h>
 #include <Security/SecTrustPriv.h>
 #include <Security/SecCertificatePriv.h>
+#if TARGET_OS_OSX
 #include <Security/CMSPrivate.h>
 #include <Security/CMSPrivate.h>
+#endif
+#import <Security/SecCMS.h>
 #include <Security/SecCmsContentInfo.h>
 #include <Security/SecCmsSignerInfo.h>
 #include <Security/SecCmsSignedData.h>
 #include <Security/SecCmsContentInfo.h>
 #include <Security/SecCmsSignerInfo.h>
 #include <Security/SecCmsSignedData.h>
+#if TARGET_OS_OSX
 #include <Security/cssmapplePriv.h>
 #include <Security/cssmapplePriv.h>
+#endif
 #include <security_utilities/unix++.h>
 #include <security_utilities/cfmunge.h>
 #include <security_utilities/unix++.h>
 #include <security_utilities/cfmunge.h>
+#include <security_utilities/casts.h>
 #include <Security/CMSDecoder.h>
 #include <security_utilities/logging.h>
 #include <dirent.h>
 #include <Security/CMSDecoder.h>
 #include <security_utilities/logging.h>
 #include <dirent.h>
@@ -53,6 +65,9 @@
 #include <sstream>
 #include <IOKit/storage/IOStorageDeviceCharacteristics.h>
 #include <dispatch/private.h>
 #include <sstream>
 #include <IOKit/storage/IOStorageDeviceCharacteristics.h>
 #include <dispatch/private.h>
+#include <os/assumes.h>
+#include <regex.h>
+#import <utilities/entitlements.h>
 
 
 namespace Security {
 
 
 namespace Security {
@@ -87,26 +102,33 @@ static inline OSStatus errorForSlot(CodeDirectory::SpecialSlot slot)
 //
 // Construct a SecStaticCode object given a disk representation object
 //
 //
 // Construct a SecStaticCode object given a disk representation object
 //
-SecStaticCode::SecStaticCode(DiskRep *rep)
-       : mRep(rep),
+SecStaticCode::SecStaticCode(DiskRep *rep, uint32_t flags)
+       : mCheckfix30814861builder1(NULL),
+         mRep(rep),
          mValidated(false), mExecutableValidated(false), mResourcesValidated(false), mResourcesValidContext(NULL),
          mValidated(false), mExecutableValidated(false), mResourcesValidated(false), mResourcesValidContext(NULL),
-         mProgressQueue("com.apple.security.validation-progress", false, QOS_CLASS_DEFAULT),
+         mProgressQueue("com.apple.security.validation-progress", false, QOS_CLASS_UNSPECIFIED),
          mOuterScope(NULL), mResourceScope(NULL),
          mOuterScope(NULL), mResourceScope(NULL),
-         mDesignatedReq(NULL), mGotResourceBase(false), mMonitor(NULL), mLimitedAsync(NULL), mEvalDetails(NULL)
+         mDesignatedReq(NULL), mGotResourceBase(false), mMonitor(NULL), mLimitedAsync(NULL),
+         mFlags(flags), mNotarizationChecked(false), mStaplingChecked(false), mNotarizationDate(NAN)
+    , mTrustedSigningCertChain(false)
+
 {
        CODESIGN_STATIC_CREATE(this, rep);
 {
        CODESIGN_STATIC_CREATE(this, rep);
+#if TARGET_OS_OSX
        checkForSystemSignature();
        checkForSystemSignature();
+#endif
 }
 
 
 //
 // Clean up a SecStaticCode object
 //
 }
 
 
 //
 // Clean up a SecStaticCode object
 //
-SecStaticCode::~SecStaticCode() throw()
+SecStaticCode::~SecStaticCode() _NOEXCEPT
 try {
        ::free(const_cast<Requirement *>(mDesignatedReq));
        delete mResourcesValidContext;
        delete mLimitedAsync;
 try {
        ::free(const_cast<Requirement *>(mDesignatedReq));
        delete mResourcesValidContext;
        delete mLimitedAsync;
+       delete mCheckfix30814861builder1;
 } catch (...) {
        return;
 }
 } catch (...) {
        return;
 }
@@ -159,7 +181,7 @@ CFTypeRef SecStaticCode::reportEvent(CFStringRef stage, CFDictionaryRef info)
 void SecStaticCode::prepareProgress(unsigned int workload)
 {
        dispatch_sync(mProgressQueue, ^{
 void SecStaticCode::prepareProgress(unsigned int workload)
 {
        dispatch_sync(mProgressQueue, ^{
-               mCancelPending = false;                 // not cancelled
+               mCancelPending = false;                 // not canceled
        });
        if (mValidationFlags & kSecCSReportProgress) {
                mCurrentWork = 0;                               // nothing done yet
        });
        if (mValidationFlags & kSecCSReportProgress) {
                mCurrentWork = 0;                               // nothing done yet
@@ -246,6 +268,7 @@ void SecStaticCode::detachedSignature(CFDataRef sigData)
 //
 void SecStaticCode::checkForSystemSignature()
 {
 //
 void SecStaticCode::checkForSystemSignature()
 {
+#if TARGET_OS_OSX
        if (!this->isSigned()) {
                SignatureDatabase db;
                if (db.isOpen())
        if (!this->isSigned()) {
                SignatureDatabase db;
                if (db.isOpen())
@@ -257,6 +280,9 @@ void SecStaticCode::checkForSystemSignature()
                        } catch (...) {
                        }
        }
                        } catch (...) {
                        }
        }
+#else
+    MacOSError::throwMe(errSecUnimplemented);
+#endif
 }
 
 
 }
 
 
@@ -317,6 +343,7 @@ void SecStaticCode::resetValidity()
                mResourcesValidContext = NULL;
        }
        mDir = NULL;
                mResourcesValidContext = NULL;
        }
        mDir = NULL;
+       mCodeDirectories.clear();
        mSignature = NULL;
        for (unsigned n = 0; n < cdSlotCount; n++)
                mCache[n] = NULL;
        mSignature = NULL;
        for (unsigned n = 0; n < cdSlotCount; n++)
                mCache[n] = NULL;
@@ -328,11 +355,15 @@ void SecStaticCode::resetValidity()
        mGotResourceBase = false;
        mTrust = NULL;
        mCertChain = NULL;
        mGotResourceBase = false;
        mTrust = NULL;
        mCertChain = NULL;
-       mEvalDetails = NULL;
+       mNotarizationChecked = false;
+       mStaplingChecked = false;
+       mNotarizationDate = NAN;
        mRep->flush();
 
        mRep->flush();
 
+#if TARGET_OS_OSX
        // we may just have updated the system database, so check again
        checkForSystemSignature();
        // we may just have updated the system database, so check again
        checkForSystemSignature();
+#endif
 }
 
 
 }
 
 
@@ -354,7 +385,7 @@ CFDataRef SecStaticCode::component(CodeDirectory::SpecialSlot slot, OSStatus fai
                                        return NULL;
 
                                if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), // ... and it's no good
                                        return NULL;
 
                                if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), // ... and it's no good
-                                               CFDataGetLength(data), -slot))
+                                               CFDataGetLength(data), -slot, false))
                                        MacOSError::throwMe(errorForSlot(slot)); // ... then bail
                        }
                        cache = data;   // it's okay, cache it
                                        MacOSError::throwMe(errorForSlot(slot)); // ... then bail
                        }
                        cache = data;   // it's okay, cache it
@@ -369,6 +400,41 @@ CFDataRef SecStaticCode::component(CodeDirectory::SpecialSlot slot, OSStatus fai
 }
 
 
 }
 
 
+//
+// Get the CodeDirectories.
+// Throws (if check==true) or returns NULL (check==false) if there are none.
+// Always throws if the CodeDirectories exist but are invalid.
+// NEVER validates against the signature.
+//
+const SecStaticCode::CodeDirectoryMap *
+SecStaticCode::codeDirectories(bool check /* = true */) const
+{
+       if (mCodeDirectories.empty()) {
+               try {
+                       loadCodeDirectories(mCodeDirectories);
+               } catch (...) {
+                       if (check)
+                               throw;
+                       // We wanted a NON-checked peek and failed to safely decode the existing CodeDirectories.
+                       // Pretend this is unsigned, but make sure we didn't somehow cache an invalid CodeDirectory.
+                       if (!mCodeDirectories.empty()) {
+                               assert(false);
+                               Syslog::warning("code signing internal problem: mCodeDirectories set despite exception exit");
+                               MacOSError::throwMe(errSecCSInternalError);
+                       }
+               }
+       } else {
+               return &mCodeDirectories;
+       }
+       if (!mCodeDirectories.empty()) {
+               return &mCodeDirectories;
+       }
+       if (check) {
+               MacOSError::throwMe(errSecCSUnsigned);
+       }
+       return NULL;
+}
+
 //
 // Get the CodeDirectory.
 // Throws (if check==true) or returns NULL (check==false) if there is none.
 //
 // Get the CodeDirectory.
 // Throws (if check==true) or returns NULL (check==false) if there is none.
@@ -380,11 +446,10 @@ const CodeDirectory *SecStaticCode::codeDirectory(bool check /* = true */) const
        if (!mDir) {
                // pick our favorite CodeDirectory from the choices we've got
                try {
        if (!mDir) {
                // pick our favorite CodeDirectory from the choices we've got
                try {
-                       CodeDirectoryMap candidates;
-                       if (loadCodeDirectories(candidates)) {
+                       CodeDirectoryMap const *candidates = codeDirectories(check);
+                       if (candidates != NULL) {
                                CodeDirectory::HashAlgorithm type = CodeDirectory::bestHashOf(mHashAlgorithms);
                                CodeDirectory::HashAlgorithm type = CodeDirectory::bestHashOf(mHashAlgorithms);
-                               mDir = candidates[type];                                                                // and the winner is...
-                               candidates.swap(mCodeDirectories);
+                               mDir = candidates->at(type);    // and the winner is...
                        }
                } catch (...) {
                        if (check)
                        }
                } catch (...) {
                        if (check)
@@ -480,6 +545,26 @@ CFArrayRef SecStaticCode::cdHashes()
        return mCDHashes;
 }
 
        return mCDHashes;
 }
 
+//
+// Get a dictionary of untruncated cdhashes for all digest types in this signature.
+//
+CFDictionaryRef SecStaticCode::cdHashesFull()
+{
+       if (!mCDHashFullDict) {
+               CFRef<CFMutableDictionaryRef> cdDict = makeCFMutableDictionary();
+               for (auto const &it : mCodeDirectories) {
+                       CodeDirectory::HashAlgorithm alg = it.first;
+                       const CodeDirectory *cd = (const CodeDirectory *)CFDataGetBytePtr(it.second);
+                       CFRef<CFDataRef> hash = cd->cdhash(false);
+                       if (hash) {
+                               CFDictionaryAddValue(cdDict, CFTempNumber(alg), hash);
+                       }
+               }
+               mCDHashFullDict = cdDict.get();
+       }
+       return mCDHashFullDict;
+}
+
 
 //
 // Return the CMS signature blob; NULL if none found.
 
 //
 // Return the CMS signature blob; NULL if none found.
@@ -529,6 +614,10 @@ void SecStaticCode::validateDirectory()
                        throw;
                }
        assert(validated());
                        throw;
                }
        assert(validated());
+    // XXX: Embedded doesn't have CSSMERR_TP_CERT_EXPIRED so we can't throw it
+    // XXX: This should be implemented for embedded once we implement
+    // XXX: verifySignature and see how we're going to handle expired certs
+#if TARGET_OS_OSX
        if (mValidationResult == errSecSuccess) {
                if (mValidationExpired)
                        if ((mValidationFlags & kSecCSConsiderExpiration)
        if (mValidationResult == errSecSuccess) {
                if (mValidationExpired)
                        if ((mValidationFlags & kSecCSConsiderExpiration)
@@ -536,6 +625,7 @@ void SecStaticCode::validateDirectory()
                                MacOSError::throwMe(CSSMERR_TP_CERT_EXPIRED);
        } else
                MacOSError::throwMe(mValidationResult);
                                MacOSError::throwMe(CSSMERR_TP_CERT_EXPIRED);
        } else
                MacOSError::throwMe(mValidationResult);
+#endif
 }
 
 
 }
 
 
@@ -574,7 +664,7 @@ void SecStaticCode::validateTopDirectory()
                        if (component(slot))
                                foundVector.push_back(slot);
                int alternateCount = int(mCodeDirectories.size() - 1);          // one will go into cdCodeDirectorySlot
                        if (component(slot))
                                foundVector.push_back(slot);
                int alternateCount = int(mCodeDirectories.size() - 1);          // one will go into cdCodeDirectorySlot
-               for (unsigned n = 0; n < alternateCount; n++)
+               for (int n = 0; n < alternateCount; n++)
                        foundVector.push_back(cdAlternateCodeDirectorySlots + n);
                foundVector.push_back(cdSignatureSlot);         // mandatory (may be empty)
                
                        foundVector.push_back(cdAlternateCodeDirectorySlots + n);
                foundVector.push_back(cdSignatureSlot);         // mandatory (may be empty)
                
@@ -604,6 +694,58 @@ CFAbsoluteTime SecStaticCode::signingTimestamp()
        return mSigningTimestamp;
 }
 
        return mSigningTimestamp;
 }
 
+#if TARGET_OS_OSX
+#define kSecSHA256HashSize 32
+// subject:/C=US/ST=California/L=San Jose/O=Adobe Systems Incorporated/OU=Information Systems/OU=Digital ID Class 3 - Microsoft Software Validation v2/CN=Adobe Systems Incorporated
+// issuer :/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Code Signing 2010 CA
+// Not Before: Dec 15 00:00:00 2010 GMT
+// Not After : Dec 14 23:59:59 2012 GMT
+static const unsigned char ASI_CS_12[] = {
+       0x77,0x82,0x9C,0x64,0x33,0x45,0x2E,0x4A,0xD3,0xA8,0xE4,0x6F,0x00,0x6C,0x27,0xEA,
+       0xFB,0xD3,0xF2,0x6D,0x50,0xF3,0x6F,0xE0,0xE9,0x6D,0x06,0x59,0x19,0xB5,0x46,0xFF
+};
+
+bool SecStaticCode::checkfix41082220(OSStatus cssmTrustResult)
+{
+       // only applicable to revoked results
+       if (cssmTrustResult != CSSMERR_TP_CERT_REVOKED) {
+               return false;
+       }
+
+       // only this leaf certificate
+       if (CFArrayGetCount(mCertChain) == 0) {
+               return false;
+       }
+       CFRef<CFDataRef> leafHash(SecCertificateCopySHA256Digest((SecCertificateRef)CFArrayGetValueAtIndex(mCertChain, 0)));
+       if (memcmp(ASI_CS_12, CFDataGetBytePtr(leafHash), kSecSHA256HashSize) != 0) {
+               return false;
+       }
+
+       // detached dmg signature
+       if (!isDetached() || format() != std::string("disk image")) {
+               return false;
+       }
+
+       // sha-1 signed
+       if (hashAlgorithms().size() != 1 || hashAlgorithm() != kSecCodeSignatureHashSHA1) {
+               return false;
+       }
+
+       // not a privileged binary - no TeamID and no entitlements
+       if (component(cdEntitlementSlot) || teamID()) {
+               return false;
+       }
+
+       // no flags and old version
+       if (codeDirectory()->version != 0x20100 || codeDirectory()->flags != 0) {
+               return false;
+       }
+
+       Security::Syslog::warning("CodeSigning: Check-fix enabled for dmg '%s' with identifier '%s' signed with revoked certificates",
+                                                         mainExecutablePath().c_str(), identifier().c_str());
+       return true;
+}
+#endif // TARGET_OS_OSX
 
 //
 // Verify the CMS signature.
 
 //
 // Verify the CMS signature.
@@ -622,184 +764,296 @@ bool SecStaticCode::verifySignature()
        }
 
        DTRACK(CODESIGN_EVAL_STATIC_SIGNATURE, this, (char*)this->mainExecutablePath().c_str());
        }
 
        DTRACK(CODESIGN_EVAL_STATIC_SIGNATURE, this, (char*)this->mainExecutablePath().c_str());
-
-       // decode CMS and extract SecTrust for verification
-       CFRef<CMSDecoderRef> cms;
-       MacOSError::check(CMSDecoderCreate(&cms.aref())); // create decoder
-       CFDataRef sig = this->signature();
-       MacOSError::check(CMSDecoderUpdateMessage(cms, CFDataGetBytePtr(sig), CFDataGetLength(sig)));
-       this->codeDirectory();  // load CodeDirectory (sets mDir)
-       MacOSError::check(CMSDecoderSetDetachedContent(cms, mBaseDir));
-       MacOSError::check(CMSDecoderFinalizeMessage(cms));
-       MacOSError::check(CMSDecoderSetSearchKeychain(cms, cfEmptyArray()));
-       CFRef<CFArrayRef> vf_policies(verificationPolicies());
-       CFRef<CFArrayRef> ts_policies(SecPolicyCreateAppleTimeStampingAndRevocationPolicies(vf_policies));
-
-       CMSSignerStatus status;
-       MacOSError::check(CMSDecoderCopySignerStatus(cms, 0, vf_policies,
-                               false, &status, &mTrust.aref(), NULL));
-
-       if (status != kCMSSignerValid) {
-               const char *reason;
-               switch (status) {
-                       case kCMSSignerUnsigned: reason="kCMSSignerUnsigned"; break;
-                       case kCMSSignerNeedsDetachedContent: reason="kCMSSignerNeedsDetachedContent"; break;
-                       case kCMSSignerInvalidSignature: reason="kCMSSignerInvalidSignature"; break;
-                       case kCMSSignerInvalidCert: reason="kCMSSignerInvalidCert"; break;
-                       case kCMSSignerInvalidIndex: reason="kCMSSignerInvalidIndex"; break;
-                       default: reason="unknown"; break;
+#if TARGET_OS_OSX
+       if (!(mValidationFlags & kSecCSApplyEmbeddedPolicy)) {
+               // decode CMS and extract SecTrust for verification
+               CFRef<CMSDecoderRef> cms;
+               MacOSError::check(CMSDecoderCreate(&cms.aref())); // create decoder
+               CFDataRef sig = this->signature();
+               MacOSError::check(CMSDecoderUpdateMessage(cms, CFDataGetBytePtr(sig), CFDataGetLength(sig)));
+               this->codeDirectory();  // load CodeDirectory (sets mDir)
+               MacOSError::check(CMSDecoderSetDetachedContent(cms, mBaseDir));
+               MacOSError::check(CMSDecoderFinalizeMessage(cms));
+               MacOSError::check(CMSDecoderSetSearchKeychain(cms, cfEmptyArray()));
+               CFRef<CFArrayRef> vf_policies(createVerificationPolicies());
+               CFRef<CFArrayRef> ts_policies(createTimeStampingAndRevocationPolicies());
+
+               CMSSignerStatus status;
+               MacOSError::check(CMSDecoderCopySignerStatus(cms, 0, vf_policies,
+                                       false, &status, &mTrust.aref(), NULL));
+
+               if (status != kCMSSignerValid) {
+                       const char *reason;
+                       switch (status) {
+                               case kCMSSignerUnsigned: reason="kCMSSignerUnsigned"; break;
+                               case kCMSSignerNeedsDetachedContent: reason="kCMSSignerNeedsDetachedContent"; break;
+                               case kCMSSignerInvalidSignature: reason="kCMSSignerInvalidSignature"; break;
+                               case kCMSSignerInvalidCert: reason="kCMSSignerInvalidCert"; break;
+                               case kCMSSignerInvalidIndex: reason="kCMSSignerInvalidIndex"; break;
+                               default: reason="unknown"; break;
+                       }
+                       Security::Syslog::error("CMSDecoderCopySignerStatus failed with %s error (%d)",
+                                                                       reason, (int)status);
+                       MacOSError::throwMe(errSecCSSignatureFailed);
                }
                }
-               Security::Syslog::error("CMSDecoderCopySignerStatus failed with %s error (%d)",
-                                                               reason, (int)status);
-               MacOSError::throwMe(errSecCSSignatureFailed);
-       }
 
 
-       // retrieve auxiliary data bag and verify against current state
-       CFRef<CFDataRef> hashBag;
-       switch (OSStatus rc = CMSDecoderCopySignerAppleCodesigningHashAgility(cms, 0, &hashBag.aref())) {
-       case noErr:
-               if (hashBag) {
-                       CFRef<CFDictionaryRef> hashDict = makeCFDictionaryFrom(hashBag);
-                       CFArrayRef cdList = CFArrayRef(CFDictionaryGetValue(hashDict, CFSTR("cdhashes")));
-                       CFArrayRef myCdList = this->cdHashes();
-                       if (cdList == NULL || !CFEqual(cdList, myCdList))
-                               MacOSError::throwMe(errSecCSSignatureFailed);
+               // retrieve auxiliary v1 data bag and verify against current state
+               CFRef<CFDataRef> hashAgilityV1;
+               switch (OSStatus rc = CMSDecoderCopySignerAppleCodesigningHashAgility(cms, 0, &hashAgilityV1.aref())) {
+               case noErr:
+                       if (hashAgilityV1) {
+                               CFRef<CFDictionaryRef> hashDict = makeCFDictionaryFrom(hashAgilityV1);
+                               CFArrayRef cdList = CFArrayRef(CFDictionaryGetValue(hashDict, CFSTR("cdhashes")));
+                               CFArrayRef myCdList = this->cdHashes();
+
+                               /* Note that this is not very "agile": There's no way to calculate the exact
+                                * list for comparison if it contains hash algorithms we don't know yet... */
+                               if (cdList == NULL || !CFEqual(cdList, myCdList))
+                                       MacOSError::throwMe(errSecCSSignatureFailed);
+                       }
+                       break;
+               case -1:        /* CMS used to return this for "no attribute found", so tolerate it. Now returning noErr/NULL */
+                       break;
+               default:
+                       MacOSError::throwMe(rc);
                }
                }
-               break;
-       case -1:        /* CMS used to return this for "no attribute found", so tolerate it. Now returning noErr/NULL */
-               break;
-       default:
-               MacOSError::throwMe(rc);
-       }
 
 
-       // internal signing time (as specified by the signer; optional)
-       mSigningTime = 0;       // "not present" marker (nobody could code sign on Jan 1, 2001 :-)
-       switch (OSStatus rc = CMSDecoderCopySignerSigningTime(cms, 0, &mSigningTime)) {
-       case errSecSuccess:
-       case errSecSigningTimeMissing:
-               break;
-       default:
-               Security::Syslog::error("Could not get signing time (error %d)", (int)rc);
-               MacOSError::throwMe(rc);
-       }
+               // retrieve auxiliary v2 data bag and verify against current state
+               CFRef<CFDictionaryRef> hashAgilityV2;
+               switch (OSStatus rc = CMSDecoderCopySignerAppleCodesigningHashAgilityV2(cms, 0, &hashAgilityV2.aref())) {
+                       case noErr:
+                               if (hashAgilityV2) {
+                                       /* Require number of code directoris and entries in the hash agility
+                                        * dict to be the same size (no stripping out code directories).
+                                        */
+                                       if (CFDictionaryGetCount(hashAgilityV2) != mCodeDirectories.size()) {
+                                               MacOSError::throwMe(errSecCSSignatureFailed);
+                                       }
 
 
-       // certified signing time (as specified by a TSA; optional)
-       mSigningTimestamp = 0;
-       switch (OSStatus rc = CMSDecoderCopySignerTimestampWithPolicy(cms, ts_policies, 0, &mSigningTimestamp)) {
-       case errSecSuccess:
-       case errSecTimestampMissing:
-               break;
-       default:
-               Security::Syslog::error("Could not get timestamp (error %d)", (int)rc);
-               MacOSError::throwMe(rc);
-       }
+                                       /* Require every cdhash of every code directory whose hash
+                                        * algorithm we know to be in the agility dictionary.
+                                        *
+                                        * We check untruncated cdhashes here because we can.
+                                        */
+                                       bool foundOurs = false;
+                                       for (auto& entry : mCodeDirectories) {
+                                               SECOidTag tag = CodeDirectorySet::SECOidTagForAlgorithm(entry.first);
+
+                                               if (tag == SEC_OID_UNKNOWN) {
+                                                       // Unknown hash algorithm, ignore.
+                                                       continue;
+                                               }
+
+                                               CFRef<CFNumberRef> key = makeCFNumber(int(tag));
+                                               CFRef<CFDataRef> entryCdhash;
+                                               entryCdhash = (CFDataRef)CFDictionaryGetValue(hashAgilityV2, (void*)key.get());
+
+                                               CodeDirectory const *cd = (CodeDirectory const*)CFDataGetBytePtr(entry.second);
+                                               CFRef<CFDataRef> ourCdhash = cd->cdhash(false); // Untruncated cdhash!
+                                               if (!CFEqual(entryCdhash, ourCdhash)) {
+                                                       MacOSError::throwMe(errSecCSSignatureFailed);
+                                               }
+
+                                               if (entry.first == this->hashAlgorithm()) {
+                                                       foundOurs = true;
+                                               }
+                                       }
 
 
-       // set up the environment for SecTrust
-    if (mValidationFlags & kSecCSNoNetworkAccess) {
-        MacOSError::check(SecTrustSetNetworkFetchAllowed(mTrust,false)); // no network?
-    }
-    MacOSError::check(SecTrustSetKeychainsAllowed(mTrust, false));
+                                       /* Require the cdhash of our chosen code directory to be in the dictionary.
+                                        * In theory, the dictionary could be full of unsupported cdhashes, but we
+                                        * really want ours, which is bound to be supported, to be covered.
+                                        */
+                                       if (!foundOurs) {
+                                               MacOSError::throwMe(errSecCSSignatureFailed);
+                                       }
+                               }
+                               break;
+                       case -1:        /* CMS used to return this for "no attribute found", so tolerate it. Now returning noErr/NULL */
+                               break;
+                       default:
+                               MacOSError::throwMe(rc);
+               }
 
 
-       CSSM_APPLE_TP_ACTION_DATA actionData = {
-               CSSM_APPLE_TP_ACTION_VERSION,   // version of data structure
-               0       // action flags
-       };
+               // internal signing time (as specified by the signer; optional)
+               mSigningTime = 0;       // "not present" marker (nobody could code sign on Jan 1, 2001 :-)
+               switch (OSStatus rc = CMSDecoderCopySignerSigningTime(cms, 0, &mSigningTime)) {
+               case errSecSuccess:
+               case errSecSigningTimeMissing:
+                       break;
+               default:
+                       Security::Syslog::error("Could not get signing time (error %d)", (int)rc);
+                       MacOSError::throwMe(rc);
+               }
 
 
-       if (!(mValidationFlags & kSecCSCheckTrustedAnchors)) {
-               /* no need to evaluate anchor trust when building cert chain */
-               MacOSError::check(SecTrustSetAnchorCertificates(mTrust, cfEmptyArray())); // no anchors
-               actionData.ActionFlags |= CSSM_TP_ACTION_IMPLICIT_ANCHORS;      // action flags
-       }
+               // certified signing time (as specified by a TSA; optional)
+               mSigningTimestamp = 0;
+               switch (OSStatus rc = CMSDecoderCopySignerTimestampWithPolicy(cms, ts_policies, 0, &mSigningTimestamp)) {
+               case errSecSuccess:
+               case errSecTimestampMissing:
+                       break;
+               default:
+                       Security::Syslog::error("Could not get timestamp (error %d)", (int)rc);
+                       MacOSError::throwMe(rc);
+               }
+
+               // set up the environment for SecTrust
+               if (mValidationFlags & kSecCSNoNetworkAccess) {
+                       MacOSError::check(SecTrustSetNetworkFetchAllowed(mTrust,false)); // no network?
+               }
+               MacOSError::check(SecTrustSetKeychainsAllowed(mTrust, false));
 
 
-       for (;;) {      // at most twice
-               MacOSError::check(SecTrustSetParameters(mTrust,
-                       CSSM_TP_ACTION_DEFAULT, CFTempData(&actionData, sizeof(actionData))));
+               CSSM_APPLE_TP_ACTION_DATA actionData = {
+                       CSSM_APPLE_TP_ACTION_VERSION,   // version of data structure
+                       0       // action flags
+               };
 
 
-               // evaluate trust and extract results
-               SecTrustResultType trustResult;
-               MacOSError::check(SecTrustEvaluate(mTrust, &trustResult));
-               MacOSError::check(SecTrustGetResult(mTrust, &trustResult, &mCertChain.aref(), &mEvalDetails));
-
-               // if this is an Apple developer cert....
-               if (teamID() && SecStaticCode::isAppleDeveloperCert(mCertChain)) {
-                       CFRef<CFStringRef> teamIDFromCert;
-                       if (CFArrayGetCount(mCertChain) > 0) {
-                               /* Note that SecCertificateCopySubjectComponent sets the out parameter to NULL if there is no field present */
-                               MacOSError::check(SecCertificateCopySubjectComponent((SecCertificateRef)CFArrayGetValueAtIndex(mCertChain, Requirement::leafCert),
-                                                                                                                                        &CSSMOID_OrganizationalUnitName,
-                                                                                                                                        &teamIDFromCert.aref()));
-
-                               if (teamIDFromCert) {
-                                       CFRef<CFStringRef> teamIDFromCD = CFStringCreateWithCString(NULL, teamID(), kCFStringEncodingUTF8);
-                                       if (!teamIDFromCD) {
-                                               Security::Syslog::error("Could not get team identifier (%s)", teamID());
-                                               MacOSError::throwMe(errSecCSInvalidTeamIdentifier);
+               if (!(mValidationFlags & kSecCSCheckTrustedAnchors)) {
+                       /* no need to evaluate anchor trust when building cert chain */
+                       MacOSError::check(SecTrustSetAnchorCertificates(mTrust, cfEmptyArray())); // no anchors
+                       actionData.ActionFlags |= CSSM_TP_ACTION_IMPLICIT_ANCHORS;      // action flags
+               }
+
+               for (;;) {      // at most twice
+                       MacOSError::check(SecTrustSetParameters(mTrust,
+                               CSSM_TP_ACTION_DEFAULT, CFTempData(&actionData, sizeof(actionData))));
+
+                       // evaluate trust and extract results
+                       SecTrustResultType trustResult;
+                       MacOSError::check(SecTrustEvaluate(mTrust, &trustResult));
+                       mCertChain.take(copyCertChain(mTrust));
+
+                       // if this is an Apple developer cert....
+                       if (teamID() && SecStaticCode::isAppleDeveloperCert(mCertChain)) {
+                               CFRef<CFStringRef> teamIDFromCert;
+                               if (CFArrayGetCount(mCertChain) > 0) {
+                                       SecCertificateRef leaf = (SecCertificateRef)CFArrayGetValueAtIndex(mCertChain, Requirement::leafCert);
+                                       CFArrayRef organizationalUnits = SecCertificateCopyOrganizationalUnit(leaf);
+                                       if (organizationalUnits) {
+                                               teamIDFromCert.take((CFStringRef)CFRetain(CFArrayGetValueAtIndex(organizationalUnits, 0)));
+                                               CFRelease(organizationalUnits);
+                                       } else {
+                                               teamIDFromCert = NULL;
                                        }
 
                                        }
 
-                                       if (CFStringCompare(teamIDFromCert, teamIDFromCD, 0) != kCFCompareEqualTo) {
-                                               Security::Syslog::error("Team identifier in the signing certificate (%s) does not match the team identifier (%s) in the code directory",
-                                                                                               cfString(teamIDFromCert).c_str(), teamID());
-                                               MacOSError::throwMe(errSecCSBadTeamIdentifier);
+                                       if (teamIDFromCert) {
+                                               CFRef<CFStringRef> teamIDFromCD = CFStringCreateWithCString(NULL, teamID(), kCFStringEncodingUTF8);
+                                               if (!teamIDFromCD) {
+                                                       Security::Syslog::error("Could not get team identifier (%s)", teamID());
+                                                       MacOSError::throwMe(errSecCSInvalidTeamIdentifier);
+                                               }
+
+                                               if (CFStringCompare(teamIDFromCert, teamIDFromCD, 0) != kCFCompareEqualTo) {
+                                                       Security::Syslog::error("Team identifier in the signing certificate (%s) does not match the team identifier (%s) in the code directory",
+                                                                                                       cfString(teamIDFromCert).c_str(), teamID());
+                                                       MacOSError::throwMe(errSecCSBadTeamIdentifier);
+                                               }
                                        }
                                }
                        }
                                        }
                                }
                        }
-               }
 
 
-               CODESIGN_EVAL_STATIC_SIGNATURE_RESULT(this, trustResult, mCertChain ? (int)CFArrayGetCount(mCertChain) : 0);
-               switch (trustResult) {
-               case kSecTrustResultProceed:
-               case kSecTrustResultUnspecified:
-                       break;                          // success
-               case kSecTrustResultDeny:
-                       MacOSError::throwMe(CSSMERR_APPLETP_TRUST_SETTING_DENY);        // user reject
-               case kSecTrustResultInvalid:
-                       assert(false);          // should never happen
-                       MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED);
-               default:
-                       {
-                               OSStatus result;
-                               MacOSError::check(SecTrustGetCssmResultCode(mTrust, &result));
-                               // if we have a valid timestamp, CMS validates against (that) signing time and all is well.
-                               // If we don't have one, may validate against *now*, and must be able to tolerate expiration.
-                               if (mSigningTimestamp == 0) { // no timestamp available
-                                       if (((result == CSSMERR_TP_CERT_EXPIRED) || (result == CSSMERR_TP_CERT_NOT_VALID_YET))
-                                                       && !(actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED)) {
-                                               CODESIGN_EVAL_STATIC_SIGNATURE_EXPIRED(this);
-                                               actionData.ActionFlags |= CSSM_TP_ACTION_ALLOW_EXPIRED; // (this also allows postdated certs)
-                                               continue;               // retry validation while tolerating expiration
+                       CODESIGN_EVAL_STATIC_SIGNATURE_RESULT(this, trustResult, mCertChain ? (int)CFArrayGetCount(mCertChain) : 0);
+                       switch (trustResult) {
+                       case kSecTrustResultProceed:
+                       case kSecTrustResultUnspecified:
+                               break;                          // success
+                       case kSecTrustResultDeny:
+                               MacOSError::throwMe(CSSMERR_APPLETP_TRUST_SETTING_DENY);        // user reject
+                       case kSecTrustResultInvalid:
+                               assert(false);          // should never happen
+                               MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED);
+                       default:
+                               {
+                                       OSStatus result;
+                                       MacOSError::check(SecTrustGetCssmResultCode(mTrust, &result));
+                                       // if we have a valid timestamp, CMS validates against (that) signing time and all is well.
+                                       // If we don't have one, may validate against *now*, and must be able to tolerate expiration.
+                                       if (mSigningTimestamp == 0) { // no timestamp available
+                                               if (((result == CSSMERR_TP_CERT_EXPIRED) || (result == CSSMERR_TP_CERT_NOT_VALID_YET))
+                                                               && !(actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED)) {
+                                                       CODESIGN_EVAL_STATIC_SIGNATURE_EXPIRED(this);
+                                                       actionData.ActionFlags |= CSSM_TP_ACTION_ALLOW_EXPIRED; // (this also allows postdated certs)
+                                                       continue;               // retry validation while tolerating expiration
+                                               }
+                                       }
+                                       if (checkfix41082220(result)) {
+                                               break; // success
                                        }
                                        }
+                                       Security::Syslog::error("SecStaticCode: verification failed (trust result %d, error %d)", trustResult, (int)result);
+                                       MacOSError::throwMe(result);
                                }
                                }
-                               Security::Syslog::error("SecStaticCode: verification failed (trust result %d, error %d)", trustResult, (int)result);
-                               MacOSError::throwMe(result);
                        }
                        }
-               }
 
 
-               if (mSigningTimestamp) {
-                       CFIndex rootix = CFArrayGetCount(mCertChain);
-                       if (SecCertificateRef mainRoot = SecCertificateRef(CFArrayGetValueAtIndex(mCertChain, rootix-1)))
-                               if (isAppleCA(mainRoot)) {
-                                       // impose policy: if the signature itself draws to Apple, then so must the timestamp signature
-                                       CFRef<CFArrayRef> tsCerts;
-                                       OSStatus result = CMSDecoderCopySignerTimestampCertificates(cms, 0, &tsCerts.aref());
-                                       if (result) {
-                                               Security::Syslog::error("SecStaticCode: could not get timestamp certificates (error %d)", (int)result);
-                                               MacOSError::check(result);
-                                       }
-                                       CFIndex tsn = CFArrayGetCount(tsCerts);
-                                       bool good = tsn > 0 && isAppleCA(SecCertificateRef(CFArrayGetValueAtIndex(tsCerts, tsn-1)));
-                                       if (!good) {
-                                               result = CSSMERR_TP_NOT_TRUSTED;
-                                               Security::Syslog::error("SecStaticCode: timestamp policy verification failed (error %d)", (int)result);
-                                               MacOSError::throwMe(result);
+                       if (mSigningTimestamp) {
+                               CFIndex rootix = CFArrayGetCount(mCertChain);
+                               if (SecCertificateRef mainRoot = SecCertificateRef(CFArrayGetValueAtIndex(mCertChain, rootix-1)))
+                                       if (isAppleCA(mainRoot)) {
+                                               // impose policy: if the signature itself draws to Apple, then so must the timestamp signature
+                                               CFRef<CFArrayRef> tsCerts;
+                                               OSStatus result = CMSDecoderCopySignerTimestampCertificates(cms, 0, &tsCerts.aref());
+                                               if (result) {
+                                                       Security::Syslog::error("SecStaticCode: could not get timestamp certificates (error %d)", (int)result);
+                                                       MacOSError::check(result);
+                                               }
+                                               CFIndex tsn = CFArrayGetCount(tsCerts);
+                                               bool good = tsn > 0 && isAppleCA(SecCertificateRef(CFArrayGetValueAtIndex(tsCerts, tsn-1)));
+                                               if (!good) {
+                                                       result = CSSMERR_TP_NOT_TRUSTED;
+                                                       Security::Syslog::error("SecStaticCode: timestamp policy verification failed (error %d)", (int)result);
+                                                       MacOSError::throwMe(result);
+                                               }
                                        }
                                        }
-                               }
+                       }
+
+                       return actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED;
+               }
+
+       } else
+#endif
+       {
+               // Do some pre-verification initialization
+               CFDataRef sig = this->signature();
+               this->codeDirectory();  // load CodeDirectory (sets mDir)
+               mSigningTime = 0;       // "not present" marker (nobody could code sign on Jan 1, 2001 :-)
+
+               CFRef<CFDictionaryRef> attrs;
+               CFRef<CFArrayRef> vf_policies(createVerificationPolicies());
+
+               // Verify the CMS signature against mBaseDir (SHA1)
+               MacOSError::check(SecCMSVerifyCopyDataAndAttributes(sig, mBaseDir, vf_policies, &mTrust.aref(), NULL, &attrs.aref()));
+
+               // Copy the signing time
+               mSigningTime = SecTrustGetVerifyTime(mTrust);
+
+               // Validate the cert chain
+               SecTrustResultType trustResult;
+               MacOSError::check(SecTrustEvaluate(mTrust, &trustResult));
+
+               // retrieve auxiliary data bag and verify against current state
+               CFRef<CFDataRef> hashBag;
+               hashBag = CFDataRef(CFDictionaryGetValue(attrs, kSecCMSHashAgility));
+               if (hashBag) {
+                       CFRef<CFDictionaryRef> hashDict = makeCFDictionaryFrom(hashBag);
+                       CFArrayRef cdList = CFArrayRef(CFDictionaryGetValue(hashDict, CFSTR("cdhashes")));
+                       CFArrayRef myCdList = this->cdHashes();
+                       if (cdList == NULL || !CFEqual(cdList, myCdList))
+                               MacOSError::throwMe(errSecCSSignatureFailed);
                }
 
                }
 
-               return actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED;
+               /*
+                * Populate mCertChain with the certs.  If we failed validation, the
+                * signer's cert will be checked installed provisioning profiles as an
+                * alternative to verification against the policy for store-signed binaries
+                */
+               mCertChain.take(copyCertChain(mTrust));
+
+               // Did we implicitly trust the signer?
+               mTrustedSigningCertChain = (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed);
+
+               return false; // XXX: Not checking for expired certs
        }
 }
 
        }
 }
 
-
+#if TARGET_OS_OSX
 //
 // Return the TP policy used for signature verification.
 // This may be a simple SecPolicyRef or a CFArray of policies.
 //
 // Return the TP policy used for signature verification.
 // This may be a simple SecPolicyRef or a CFArray of policies.
@@ -810,12 +1064,23 @@ static SecPolicyRef makeRevocationPolicy(CFOptionFlags flags)
        CFRef<SecPolicyRef> policy(SecPolicyCreateRevocation(flags));
        return policy.yield();
 }
        CFRef<SecPolicyRef> policy(SecPolicyCreateRevocation(flags));
        return policy.yield();
 }
+#endif
 
 
-CFArrayRef SecStaticCode::verificationPolicies()
+CFArrayRef SecStaticCode::createVerificationPolicies()
 {
 {
+       if (mValidationFlags & kSecCSUseSoftwareSigningCert) {
+               CFRef<SecPolicyRef> ssRef = SecPolicyCreateAppleSoftwareSigning();
+               return makeCFArray(1, ssRef.get());
+       }
+#if TARGET_OS_OSX
+       if (mValidationFlags & kSecCSApplyEmbeddedPolicy) {
+               CFRef<SecPolicyRef> iOSRef = SecPolicyCreateiPhoneApplicationSigning();
+               return makeCFArray(1, iOSRef.get());
+       }
+
        CFRef<SecPolicyRef> core;
        MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3,
        CFRef<SecPolicyRef> core;
        MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3,
-                       &CSSMOID_APPLE_TP_CODE_SIGNING, &core.aref()));
+                                                                       &CSSMOID_APPLE_TP_CODE_SIGNING, &core.aref()));
        if (mValidationFlags & kSecCSNoNetworkAccess) {
                // Skips all revocation since they require network connectivity
                // therefore annihilates kSecCSEnforceRevocationChecks if present
        if (mValidationFlags & kSecCSNoNetworkAccess) {
                // Skips all revocation since they require network connectivity
                // therefore annihilates kSecCSEnforceRevocationChecks if present
@@ -823,12 +1088,63 @@ CFArrayRef SecStaticCode::verificationPolicies()
                return makeCFArray(2, core.get(), no_revoc.get());
        }
        else if (mValidationFlags & kSecCSEnforceRevocationChecks) {
                return makeCFArray(2, core.get(), no_revoc.get());
        }
        else if (mValidationFlags & kSecCSEnforceRevocationChecks) {
-        // Add CRL and OCSP policies
+               // Add CRL and OCSP policies
                CFRef<SecPolicyRef> revoc = makeRevocationPolicy(kSecRevocationUseAnyAvailableMethod);
                return makeCFArray(2, core.get(), revoc.get());
        } else {
                return makeCFArray(1, core.get());
        }
                CFRef<SecPolicyRef> revoc = makeRevocationPolicy(kSecRevocationUseAnyAvailableMethod);
                return makeCFArray(2, core.get(), revoc.get());
        } else {
                return makeCFArray(1, core.get());
        }
+#elif TARGET_OS_TV
+       CFRef<SecPolicyRef> tvOSRef = SecPolicyCreateAppleTVOSApplicationSigning();
+       return makeCFArray(1, tvOSRef.get());
+#else
+       CFRef<SecPolicyRef> iOSRef = SecPolicyCreateiPhoneApplicationSigning();
+       return makeCFArray(1, iOSRef.get());
+#endif
+
+}
+
+CFArrayRef SecStaticCode::createTimeStampingAndRevocationPolicies()
+{
+       CFRef<SecPolicyRef> tsPolicy = SecPolicyCreateAppleTimeStamping();
+#if TARGET_OS_OSX
+       if (mValidationFlags & kSecCSNoNetworkAccess) {
+               // Skips all revocation since they require network connectivity
+               // therefore annihilates kSecCSEnforceRevocationChecks if present
+               CFRef<SecPolicyRef> no_revoc = makeRevocationPolicy(kSecRevocationNetworkAccessDisabled);
+               return makeCFArray(2, tsPolicy.get(), no_revoc.get());
+       }
+       else if (mValidationFlags & kSecCSEnforceRevocationChecks) {
+               // Add CRL and OCSP policies
+               CFRef<SecPolicyRef> revoc = makeRevocationPolicy(kSecRevocationUseAnyAvailableMethod);
+               return makeCFArray(2, tsPolicy.get(), revoc.get());
+       }
+       else {
+               return makeCFArray(1, tsPolicy.get());
+       }
+#else
+       return makeCFArray(1, tsPolicy.get());
+#endif
+
+}
+
+CFArrayRef SecStaticCode::copyCertChain(SecTrustRef trust)
+{
+       SecCertificateRef leafCert = SecTrustGetCertificateAtIndex(trust, 0);
+       if (leafCert != NULL) {
+               CFIndex count = SecTrustGetCertificateCount(trust);
+
+               CFMutableArrayRef certs = CFArrayCreateMutable(kCFAllocatorDefault, count,
+                                                                                                          &kCFTypeArrayCallBacks);
+
+               CFArrayAppendValue(certs, leafCert);
+               for (CFIndex i = 1; i < count; ++i) {
+                       CFArrayAppendValue(certs, SecTrustGetCertificateAtIndex(trust, i));
+               }
+
+               return certs;
+       }
+       return NULL;
 }
 
 
 }
 
 
@@ -846,7 +1162,7 @@ void SecStaticCode::validateComponent(CodeDirectory::SpecialSlot slot, OSStatus
                if (codeDirectory()->slotIsPresent(-slot)) // was supposed to be there...
                                MacOSError::throwMe(fail);      // ... and is missing
        } else {
                if (codeDirectory()->slotIsPresent(-slot)) // was supposed to be there...
                                MacOSError::throwMe(fail);      // ... and is missing
        } else {
-               if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), CFDataGetLength(data), -slot))
+               if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), CFDataGetLength(data), -slot, false))
                        MacOSError::throwMe(fail);
        }
 }
                        MacOSError::throwMe(fail);
        }
 }
@@ -881,7 +1197,8 @@ void SecStaticCode::validateExecutable()
                                __block bool good = true;
                                CodeDirectory::multipleHashFileData(fd, thisPage, hashAlgorithms(), ^(CodeDirectory::HashAlgorithm type, Security::DynamicHash *hasher) {
                                        const CodeDirectory* cd = (const CodeDirectory*)CFDataGetBytePtr(mCodeDirectories[type]);
                                __block bool good = true;
                                CodeDirectory::multipleHashFileData(fd, thisPage, hashAlgorithms(), ^(CodeDirectory::HashAlgorithm type, Security::DynamicHash *hasher) {
                                        const CodeDirectory* cd = (const CodeDirectory*)CFDataGetBytePtr(mCodeDirectories[type]);
-                                       if (!hasher->verify((*cd)[slot]))
+                                       if (!hasher->verify(cd->getSlot(slot,
+                                                                                                       mValidationFlags & kSecCSValidatePEH)))
                                                good = false;
                                });
                                if (!good) {
                                                good = false;
                                });
                                if (!good) {
@@ -910,7 +1227,6 @@ void SecStaticCode::validateExecutable()
                MacOSError::throwMe(mExecutableValidResult);
 }
 
                MacOSError::throwMe(mExecutableValidResult);
 }
 
-
 //
 // Perform static validation of sealed resources and nested code.
 //
 //
 // Perform static validation of sealed resources and nested code.
 //
@@ -938,8 +1254,21 @@ void SecStaticCode::validateResources(SecCSFlags flags)
        }
 
        if (doit) {
        }
 
        if (doit) {
+               string root = cfStringRelease(copyCanonicalPath());
+               bool itemIsOnRootFS = isOnRootFilesystem(root.c_str());
+               bool skipRootVolumeExceptions = (mValidationFlags & kSecCSSkipRootVolumeExceptions);
+               bool useRootFSPolicy = itemIsOnRootFS && !skipRootVolumeExceptions;
+
+               bool itemMightUseXattrFiles = pathFileSystemUsesXattrFiles(root.c_str());
+               bool skipXattrFiles = itemMightUseXattrFiles && (mValidationFlags & kSecCSSkipXattrFiles);
+
+               secinfo("staticCode", "performing resource validation for %s (%d, %d, %d, %d, %d)", root.c_str(),
+                               itemIsOnRootFS, skipRootVolumeExceptions, useRootFSPolicy, itemMightUseXattrFiles, skipXattrFiles);
+
                if (mLimitedAsync == NULL) {
                if (mLimitedAsync == NULL) {
-                       mLimitedAsync = new LimitedAsync(diskRep()->fd().mediumType() == kIOPropertyMediumTypeSolidStateKey);
+                       bool runMultiThreaded = ((flags & kSecCSSingleThreaded) == kSecCSSingleThreaded) ? false :
+                                       (diskRep()->fd().mediumType() == kIOPropertyMediumTypeSolidStateKey);
+                       mLimitedAsync = new LimitedAsync(runMultiThreaded);
                }
 
                try {
                }
 
                try {
@@ -958,13 +1287,15 @@ void SecStaticCode::validateResources(SecCSFlags flags)
 
                        // check for weak resource rules
                        bool strict = flags & kSecCSStrictValidate;
 
                        // check for weak resource rules
                        bool strict = flags & kSecCSStrictValidate;
-                       if (strict) {
-                               if (hasWeakResourceRules(rules, version, mAllowOmissions))
-                                       if (mTolerateErrors.find(errSecCSWeakResourceRules) == mTolerateErrors.end())
-                                               MacOSError::throwMe(errSecCSWeakResourceRules);
-                               if (version == 1)
-                                       if (mTolerateErrors.find(errSecCSWeakResourceEnvelope) == mTolerateErrors.end())
-                                               MacOSError::throwMe(errSecCSWeakResourceEnvelope);
+                       if (!useRootFSPolicy) {
+                               if (strict) {
+                                       if (hasWeakResourceRules(rules, version, mAllowOmissions))
+                                               if (mTolerateErrors.find(errSecCSWeakResourceRules) == mTolerateErrors.end())
+                                                       MacOSError::throwMe(errSecCSWeakResourceRules);
+                                       if (version == 1)
+                                               if (mTolerateErrors.find(errSecCSWeakResourceEnvelope) == mTolerateErrors.end())
+                                                       MacOSError::throwMe(errSecCSWeakResourceEnvelope);
+                               }
                        }
 
                        Dispatch::Group group;
                        }
 
                        Dispatch::Group group;
@@ -982,7 +1313,26 @@ void SecStaticCode::validateResources(SecCSFlags flags)
                                bool isSymlink = (ent->fts_info == FTS_SL);
 
                                void (^validate)() = ^{
                                bool isSymlink = (ent->fts_info == FTS_SL);
 
                                void (^validate)() = ^{
-                                       validateResource(files, relpath, isSymlink, *mResourcesValidContext, flags, version);
+                                       bool needsValidation = true;
+
+                                       if (skipXattrFiles && pathIsValidXattrFile(cfString(resourceBase()) + "/" + relpath, "staticCode")) {
+                                               secinfo("staticCode", "resource validation on xattr file skipped: %s", relpath.c_str());
+                                               needsValidation = false;
+                                       }
+
+                                       if (useRootFSPolicy) {
+                                               CFRef<CFURLRef> itemURL = makeCFURL(relpath, false, resourceBase());
+                                               string itemPath = cfString(itemURL);
+                                               if (isOnRootFilesystem(itemPath.c_str())) {
+                                                       secinfo("staticCode", "resource validation on root volume skipped: %s", itemPath.c_str());
+                                                       needsValidation = false;
+                                               }
+                                       }
+
+                                       if (needsValidation) {
+                                               secinfo("staticCode", "performing resource validation on item: %s", relpath.c_str());
+                                               validateResource(files, relpath, isSymlink, *mResourcesValidContext, flags, version);
+                                       }
                                        reportProgress();
                                };
 
                                        reportProgress();
                                };
 
@@ -990,10 +1340,15 @@ void SecStaticCode::validateResources(SecCSFlags flags)
                        });
                        group.wait();   // wait until all async resources have been validated as well
 
                        });
                        group.wait();   // wait until all async resources have been validated as well
 
-                       unsigned leftovers = unsigned(CFDictionaryGetCount(resourceMap));
-                       if (leftovers > 0) {
-                               secinfo("staticCode", "%d sealed resource(s) not found in code", int(leftovers));
-                               CFDictionaryApplyFunction(resourceMap, SecStaticCode::checkOptionalResource, mResourcesValidContext);
+                       if (useRootFSPolicy) {
+                               // It's ok to allow leftovers on the root filesystem for now.
+                       } else {
+                               // Look through the leftovers and make sure they're all properly optional resources.
+                               unsigned leftovers = unsigned(CFDictionaryGetCount(resourceMap));
+                               if (leftovers > 0) {
+                                       secinfo("staticCode", "%d sealed resource(s) not found in code", int(leftovers));
+                                       CFDictionaryApplyFunction(resourceMap, SecStaticCode::checkOptionalResource, mResourcesValidContext);
+                               }
                        }
 
                        // now check for any errors found in the reporting context
                        }
 
                        // now check for any errors found in the reporting context
@@ -1164,7 +1519,7 @@ CFDataRef SecStaticCode::copyComponent(CodeDirectory::SpecialSlot slot, CFDataRe
        const CodeDirectory* cd = this->codeDirectory();
        if (CFCopyRef<CFDataRef> component = this->component(slot)) {
                if (hash) {
        const CodeDirectory* cd = this->codeDirectory();
        if (CFCopyRef<CFDataRef> component = this->component(slot)) {
                if (hash) {
-                       const void *slotHash = (*cd)[slot];
+                       const void *slotHash = cd->getSlot(slot, false);
                        if (cd->hashSize != CFDataGetLength(hash) || 0 != memcmp(slotHash, CFDataGetBytePtr(hash), cd->hashSize)) {
                                Syslog::notice("copyComponent hash mismatch slot %d length %d", slot, int(CFDataGetLength(hash)));
                                return NULL;    // mismatch
                        if (cd->hashSize != CFDataGetLength(hash) || 0 != memcmp(slotHash, CFDataGetBytePtr(hash), cd->hashSize)) {
                                Syslog::notice("copyComponent hash mismatch slot %d length %d", slot, int(CFDataGetLength(hash)));
                                return NULL;    // mismatch
@@ -1220,6 +1575,77 @@ CFDictionaryRef SecStaticCode::diskRepInformation()
        return mRep->diskRepInformation();
 }
 
        return mRep->diskRepInformation();
 }
 
+bool SecStaticCode::checkfix30814861(string path, bool addition) {
+       // <rdar://problem/30814861> v2 resource rules don't match v1 resource rules
+
+       //// Condition 1: Is the app an iOS app that was built with an SDK lower than 9.0?
+
+       // We started signing correctly in 2014, 9.0 was first seeded mid-2016.
+
+       CFRef<CFDictionaryRef> inf = diskRepInformation();
+       try {
+               CFDictionary info(diskRepInformation(), errSecCSNotSupported);
+               uint32_t platform =
+                       cfNumber(info.get<CFNumberRef>(kSecCodeInfoDiskRepVersionPlatform, errSecCSNotSupported), 0);
+               uint32_t sdkVersion =
+                       cfNumber(info.get<CFNumberRef>(kSecCodeInfoDiskRepVersionSDK, errSecCSNotSupported), 0);
+
+               if (platform != PLATFORM_IOS || sdkVersion >= 0x00090000) {
+                       return false;
+               }
+       } catch (const MacOSError &error) {
+               return false;
+       }
+
+       //// Condition 2: Is it a .sinf/.supf/.supp file at the right location?
+
+       static regex_t pathre_sinf;
+       static regex_t pathre_supp_supf;
+       static dispatch_once_t once;
+
+       dispatch_once(&once, ^{
+               os_assert_zero(regcomp(&pathre_sinf,
+                                                          "^(Frameworks/[^/]+\\.framework/|PlugIns/[^/]+\\.appex/|())SC_Info/[^/]+\\.sinf$",
+                                                          REG_EXTENDED | REG_NOSUB));
+               os_assert_zero(regcomp(&pathre_supp_supf,
+                                                          "^(Frameworks/[^/]+\\.framework/|PlugIns/[^/]+\\.appex/|())SC_Info/[^/]+\\.(supf|supp)$",
+                                                          REG_EXTENDED | REG_NOSUB));
+       });
+
+       // .sinf is added, .supf/.supp are modified.
+       const regex_t &pathre = addition ? pathre_sinf : pathre_supp_supf;
+
+       const int result = regexec(&pathre, path.c_str(), 0, NULL, 0);
+
+       if (result == REG_NOMATCH) {
+               return false;
+       } else if (result != 0) {
+               // Huh?
+               secerror("unexpected regexec result %d for path '%s'", result, path.c_str());
+               return false;
+       }
+
+       //// Condition 3: Do the v1 rules actually exclude the file?
+
+       dispatch_once(&mCheckfix30814861builder1_once, ^{
+               // Create the v1 resource builder lazily.
+               CFDictionaryRef rules1 = cfget<CFDictionaryRef>(resourceDictionary(), "rules");
+               const string base = cfString(resourceBase());
+
+               mCheckfix30814861builder1 = new ResourceBuilder(base, base, rules1, false, mTolerateErrors);
+       });
+
+       ResourceBuilder::Rule const * const matchingRule = mCheckfix30814861builder1->findRule(path);
+
+       if (matchingRule == NULL || !(matchingRule->flags & ResourceBuilder::omitted)) {
+               return false;
+       }
+
+       //// All matched, this file is a check-fixed sinf/supf/supp.
+
+       return true;
+
+}
 
 void SecStaticCode::validateResource(CFDictionaryRef files, string path, bool isSymlink, ValidationContext &ctx, SecCSFlags flags, uint32_t version)
 {
 
 void SecStaticCode::validateResource(CFDictionaryRef files, string path, bool isSymlink, ValidationContext &ctx, SecCSFlags flags, uint32_t version)
 {
@@ -1255,8 +1681,13 @@ void SecStaticCode::validateResource(CFDictionaryRef files, string path, bool is
                                        if (!hasher->verify(rseal.hash(type)))
                                                good = false;
                                });
                                        if (!hasher->verify(rseal.hash(type)))
                                                good = false;
                                });
-                               if (!good)
-                                       ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // altered
+                               if (!good) {
+                                       if (version == 2 && checkfix30814861(path, false)) {
+                                               secinfo("validateResource", "%s check-fixed (altered).", path.c_str());
+                                       } else {
+                                               ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // altered
+                                       }
+                               }
                        } else {
                                if (!seal.optional())
                                        ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing, fullpath); // was sealed but is now missing
                        } else {
                                if (!seal.optional())
                                        ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing, fullpath); // was sealed but is now missing
@@ -1272,7 +1703,11 @@ void SecStaticCode::validateResource(CFDictionaryRef files, string path, bool is
                if (::readlink(cfString(fullpath).c_str(), target, sizeof(target)) > 0)
                        return;
        }
                if (::readlink(cfString(fullpath).c_str(), target, sizeof(target)) > 0)
                        return;
        }
-       ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAdded, CFTempURL(path, false, resourceBase()));
+       if (version == 2 && checkfix30814861(path, true)) {
+               secinfo("validateResource", "%s check-fixed (added).", path.c_str());
+       } else {
+               ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAdded, CFTempURL(path, false, resourceBase()));
+       }
 }
 
 void SecStaticCode::validatePlainMemoryResource(string path, CFDataRef fileData, SecCSFlags flags)
 }
 
 void SecStaticCode::validatePlainMemoryResource(string path, CFDataRef fileData, SecCSFlags flags)
@@ -1357,11 +1792,11 @@ void SecStaticCode::validateNestedCode(CFURLRef path, const ResourceSeal &seal,
                        flags |= kSecCSBasicValidateOnly | kSecCSQuickCheck;
                SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(cfString(path)));
                code->initializeFromParent(*this);
                        flags |= kSecCSBasicValidateOnly | kSecCSQuickCheck;
                SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(cfString(path)));
                code->initializeFromParent(*this);
-               code->staticValidate(flags & ~kSecCSRestrictToAppLike, SecRequirement::required(req));
+               code->staticValidate(flags & (~kSecCSRestrictToAppLike), SecRequirement::required(req));
 
                if (isFramework && (flags & kSecCSStrictValidate))
                        try {
 
                if (isFramework && (flags & kSecCSStrictValidate))
                        try {
-                               validateOtherVersions(path, flags, req, code);
+                               validateOtherVersions(path, flags & (~kSecCSRestrictToAppLike), req, code);
                        } catch (const CSError &err) {
                                MacOSError::throwMe(errSecCSBadFrameworkVersion);
                        } catch (const MacOSError &err) {
                        } catch (const CSError &err) {
                                MacOSError::throwMe(errSecCSBadFrameworkVersion);
                        } catch (const MacOSError &err) {
@@ -1511,15 +1946,28 @@ const Requirement *SecStaticCode::defaultDesignatedRequirement()
                }
                return maker.make();
        } else {
                }
                return maker.make();
        } else {
+#if TARGET_OS_OSX
                // full signature: Gin up full context and let DRMaker do its thing
                validateDirectory();            // need the cert chain
                // full signature: Gin up full context and let DRMaker do its thing
                validateDirectory();            // need the cert chain
+               CFRef<CFDateRef> secureTimestamp;
+               if (CFAbsoluteTime time = this->signingTimestamp()) {
+                       secureTimestamp.take(CFDateCreate(NULL, time));
+               }
                Requirement::Context context(this->certificates(),
                        this->infoDictionary(),
                        this->entitlements(),
                        this->identifier(),
                Requirement::Context context(this->certificates(),
                        this->infoDictionary(),
                        this->entitlements(),
                        this->identifier(),
-                       this->codeDirectory()
+                       this->codeDirectory(),
+                       NULL,
+                       kSecCodeSignatureNoHash,
+                       false,
+                       secureTimestamp,
+                       this->teamID()
                );
                return DRMaker(context).make();
                );
                return DRMaker(context).make();
+#else
+        MacOSError::throwMe(errSecCSUnimplemented);
+#endif
        }
 }
 
        }
 }
 
@@ -1547,7 +1995,15 @@ bool SecStaticCode::satisfiesRequirement(const Requirement *req, OSStatus failur
        bool result = false;
        assert(req);
        validateDirectory();
        bool result = false;
        assert(req);
        validateDirectory();
-       result = req->validates(Requirement::Context(mCertChain, infoDictionary(), entitlements(), codeDirectory()->identifier(), codeDirectory()), failure);
+       CFRef<CFDateRef> secureTimestamp;
+       if (CFAbsoluteTime time = this->signingTimestamp()) {
+               secureTimestamp.take(CFDateCreate(NULL, time));
+       }
+       result = req->validates(Requirement::Context(mCertChain, infoDictionary(), entitlements(),
+                                                                                                codeDirectory()->identifier(), codeDirectory(),
+                                                                                                NULL, kSecCodeSignatureNoHash, mRep->appleInternalForcePlatform(),
+                                                                                                secureTimestamp, teamID()),
+                                                       failure);
        return result;
 }
 
        return result;
 }
 
@@ -1616,12 +2072,16 @@ CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
        CFDictionaryAddValue(dict, kSecCodeInfoSource, CFTempString(this->signatureSource()));
        CFDictionaryAddValue(dict, kSecCodeInfoUnique, this->cdHash());
        CFDictionaryAddValue(dict, kSecCodeInfoCdHashes, this->cdHashes());
        CFDictionaryAddValue(dict, kSecCodeInfoSource, CFTempString(this->signatureSource()));
        CFDictionaryAddValue(dict, kSecCodeInfoUnique, this->cdHash());
        CFDictionaryAddValue(dict, kSecCodeInfoCdHashes, this->cdHashes());
+       CFDictionaryAddValue(dict, kSecCodeInfoCdHashesFull, this->cdHashesFull());
        const CodeDirectory* cd = this->codeDirectory(false);
        CFDictionaryAddValue(dict, kSecCodeInfoDigestAlgorithm, CFTempNumber(cd->hashType));
        CFRef<CFArrayRef> digests = makeCFArrayFrom(^CFTypeRef(CodeDirectory::HashAlgorithm type) { return CFTempNumber(type); }, hashAlgorithms());
        CFDictionaryAddValue(dict, kSecCodeInfoDigestAlgorithms, digests);
        if (cd->platform)
                CFDictionaryAddValue(dict, kSecCodeInfoPlatformIdentifier, CFTempNumber(cd->platform));
        const CodeDirectory* cd = this->codeDirectory(false);
        CFDictionaryAddValue(dict, kSecCodeInfoDigestAlgorithm, CFTempNumber(cd->hashType));
        CFRef<CFArrayRef> digests = makeCFArrayFrom(^CFTypeRef(CodeDirectory::HashAlgorithm type) { return CFTempNumber(type); }, hashAlgorithms());
        CFDictionaryAddValue(dict, kSecCodeInfoDigestAlgorithms, digests);
        if (cd->platform)
                CFDictionaryAddValue(dict, kSecCodeInfoPlatformIdentifier, CFTempNumber(cd->platform));
+       if (cd->runtimeVersion()) {
+               CFDictionaryAddValue(dict, kSecCodeInfoRuntimeVersion, CFTempNumber(cd->runtimeVersion()));
+       }
 
        //
        // Deliver any Info.plist only if it looks intact
 
        //
        // Deliver any Info.plist only if it looks intact
@@ -1656,6 +2116,9 @@ CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
        // kSecCSRequirementInformation adds information on requirements
        //
        if (flags & kSecCSRequirementInformation)
        // kSecCSRequirementInformation adds information on requirements
        //
        if (flags & kSecCSRequirementInformation)
+
+//DR not currently supported on iOS
+#if TARGET_OS_OSX
                try {
                        if (const Requirements *reqs = this->internalRequirements()) {
                                CFDictionaryAddValue(dict, kSecCodeInfoRequirements,
                try {
                        if (const Requirements *reqs = this->internalRequirements()) {
                                CFDictionaryAddValue(dict, kSecCodeInfoRequirements,
@@ -1673,14 +2136,31 @@ CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
                                CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, dreqRef);
                        }
                } catch (...) { }
                                CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, dreqRef);
                        }
                } catch (...) { }
+#endif
 
 
-               try {
-                  if (CFDataRef ent = this->component(cdEntitlementSlot)) {
-                          CFDictionaryAddValue(dict, kSecCodeInfoEntitlements, ent);
-                          if (CFDictionaryRef entdict = this->entitlements())
+       try {
+               if (CFDataRef ent = this->component(cdEntitlementSlot)) {
+                       CFDictionaryAddValue(dict, kSecCodeInfoEntitlements, ent);
+                       if (CFDictionaryRef entdict = this->entitlements()) {
+                               if (needsCatalystEntitlementFixup(entdict)) {
+                                       // If this entitlement dictionary needs catalyst entitlements, make a copy and stick that into the
+                                       // output dictionary instead.
+                                       secinfo("staticCode", "%p fixed catalyst entitlements", this);
+                                       CFRef<CFMutableDictionaryRef> tempEntitlements = makeCFMutableDictionary(entdict);
+                                       updateCatalystEntitlements(tempEntitlements);
+                                       CFRef<CFDictionaryRef> newEntitlements = CFDictionaryCreateCopy(NULL, tempEntitlements);
+                                       if (newEntitlements) {
+                                               CFDictionaryAddValue(dict, kSecCodeInfoEntitlementsDict, newEntitlements.get());
+                                       } else {
+                                               secerror("%p unable to fixup entitlement dictionary", this);
+                                               CFDictionaryAddValue(dict, kSecCodeInfoEntitlementsDict, entdict);
+                                       }
+                               } else {
                                        CFDictionaryAddValue(dict, kSecCodeInfoEntitlementsDict, entdict);
                                        CFDictionaryAddValue(dict, kSecCodeInfoEntitlementsDict, entdict);
+                               }
                        }
                        }
-               } catch (...) { }
+               }
+       } catch (...) { }
 
        //
        // kSecCSInternalInformation adds internal information meant to be for Apple internal
 
        //
        // kSecCSInternalInformation adds internal information meant to be for Apple internal
@@ -1693,20 +2173,44 @@ CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
                        if (mDir)
                                CFDictionaryAddValue(dict, kSecCodeInfoCodeDirectory, mDir);
                        CFDictionaryAddValue(dict, kSecCodeInfoCodeOffset, CFTempNumber(mRep->signingBase()));
                        if (mDir)
                                CFDictionaryAddValue(dict, kSecCodeInfoCodeDirectory, mDir);
                        CFDictionaryAddValue(dict, kSecCodeInfoCodeOffset, CFTempNumber(mRep->signingBase()));
-               if (CFRef<CFDictionaryRef> rdict = getDictionary(cdResourceDirSlot, false))     // suppress validation
-                       CFDictionaryAddValue(dict, kSecCodeInfoResourceDirectory, rdict);
+        if (!(flags & kSecCSSkipResourceDirectory)) {
+            if (CFRef<CFDictionaryRef> rdict = getDictionary(cdResourceDirSlot, false))        // suppress validation
+                CFDictionaryAddValue(dict, kSecCodeInfoResourceDirectory, rdict);
+        }
                if (CFRef<CFDictionaryRef> ddict = diskRepInformation())
                        CFDictionaryAddValue(dict, kSecCodeInfoDiskRepInfo, ddict);
                } catch (...) { }
                if (CFRef<CFDictionaryRef> ddict = diskRepInformation())
                        CFDictionaryAddValue(dict, kSecCodeInfoDiskRepInfo, ddict);
                } catch (...) { }
+               if (mNotarizationChecked && !isnan(mNotarizationDate)) {
+                       CFRef<CFDateRef> date = CFDateCreate(NULL, mNotarizationDate);
+                       if (date) {
+                               CFDictionaryAddValue(dict, kSecCodeInfoNotarizationDate, date.get());
+                       } else {
+                               secerror("Error creating date from timestamp: %f", mNotarizationDate);
+                       }
+               }
+               if (this->codeDirectory()) {
+                       uint32_t version = this->codeDirectory()->version;
+                       CFDictionaryAddValue(dict, kSecCodeInfoSignatureVersion, CFTempNumber(version));
+               }
        }
 
        }
 
+       if (flags & kSecCSCalculateCMSDigest) {
+               try {
+                       CFDictionaryAddValue(dict, kSecCodeInfoCMSDigestHashType, CFTempNumber(cmsDigestHashType()));
+                       
+                       CFRef<CFDataRef> cmsDigest = createCmsDigest();
+                       if (cmsDigest) {
+                               CFDictionaryAddValue(dict, kSecCodeInfoCMSDigest, cmsDigest.get());
+                       }
+               } catch (...) { }
+       }
 
        //
        // kSecCSContentInformation adds more information about the physical layout
        // of the signed code. This is (only) useful for packaging or patching-oriented
        // applications.
        //
 
        //
        // kSecCSContentInformation adds more information about the physical layout
        // of the signed code. This is (only) useful for packaging or patching-oriented
        // applications.
        //
-       if (flags & kSecCSContentInformation)
+       if (flags & kSecCSContentInformation && !(flags & kSecCSSkipResourceDirectory))
                if (CFRef<CFArrayRef> files = mRep->modifiedFiles())
                        CFDictionaryAddValue(dict, kSecCodeInfoChangedFiles, files);
 
                if (CFRef<CFArrayRef> files = mRep->modifiedFiles())
                        CFDictionaryAddValue(dict, kSecCodeInfoChangedFiles, files);
 
@@ -1769,11 +2273,31 @@ void SecStaticCode::staticValidate(SecCSFlags flags, const SecRequirement *req)
 {
        setValidationFlags(flags);
 
 {
        setValidationFlags(flags);
 
+#if TARGET_OS_OSX
+       if (!mStaplingChecked) {
+               mRep->registerStapledTicket();
+               mStaplingChecked = true;
+       }
+
+       if (mFlags & kSecCSForceOnlineNotarizationCheck) {
+               if (!mNotarizationChecked) {
+                       if (this->cdHash()) {
+                               bool is_revoked = checkNotarizationServiceForRevocation(this->cdHash(), (SecCSDigestAlgorithm)this->hashAlgorithm(), &mNotarizationDate);
+                               if (is_revoked) {
+                                       MacOSError::throwMe(errSecCSRevokedNotarization);
+                               }
+                       }
+                       mNotarizationChecked = true;
+               }
+       }
+#endif // TARGET_OS_OSX
+
        // initialize progress/cancellation state
        if (flags & kSecCSReportProgress)
                prepareProgress(estimateResourceWorkload() + 2);        // +1 head, +1 tail
 
        // initialize progress/cancellation state
        if (flags & kSecCSReportProgress)
                prepareProgress(estimateResourceWorkload() + 2);        // +1 head, +1 tail
 
-       // core components: once per architecture (if any)
+
+       // core components: once per architecture (if any)
        this->staticValidateCore(flags, req);
        if (flags & kSecCSCheckAllArchitectures)
                handleOtherArchitectures(^(SecStaticCode* subcode) {
        this->staticValidateCore(flags, req);
        if (flags & kSecCSCheckAllArchitectures)
                handleOtherArchitectures(^(SecStaticCode* subcode) {
@@ -1793,13 +2317,17 @@ void SecStaticCode::staticValidate(SecCSFlags flags, const SecRequirement *req)
        reportEvent(CFSTR("prepared"), NULL);
 
        // resources: once for all architectures
        reportEvent(CFSTR("prepared"), NULL);
 
        // resources: once for all architectures
-       if (!(flags & kSecCSDoNotValidateResources))
+       if (!(flags & kSecCSDoNotValidateResources)) {
                this->validateResources(flags);
                this->validateResources(flags);
+       }
 
        // perform strict validation if desired
 
        // perform strict validation if desired
-       if (flags & kSecCSStrictValidate)
+       if (flags & kSecCSStrictValidate) {
                mRep->strictValidate(codeDirectory(), mTolerateErrors, mValidationFlags);
        reportProgress();
                mRep->strictValidate(codeDirectory(), mTolerateErrors, mValidationFlags);
        reportProgress();
+       } else if (flags & kSecCSStrictValidateStructure) {
+               mRep->strictValidateStructure(codeDirectory(), mTolerateErrors, mValidationFlags);
+       }
 
        // allow monitor intervention
        if (CFRef<CFTypeRef> veto = reportEvent(CFSTR("validated"), NULL)) {
 
        // allow monitor intervention
        if (CFRef<CFTypeRef> veto = reportEvent(CFSTR("validated"), NULL)) {
@@ -1852,21 +2380,24 @@ void SecStaticCode::handleOtherArchitectures(void (^handle)(SecStaticCode* other
                fat->architectures(architectures);
                if (architectures.size() > 1) {
                        DiskRep::Context ctx;
                fat->architectures(architectures);
                if (architectures.size() > 1) {
                        DiskRep::Context ctx;
-                       size_t activeOffset = fat->archOffset();
+                       off_t activeOffset = fat->archOffset();
                        for (Universal::Architectures::const_iterator arch = architectures.begin(); arch != architectures.end(); ++arch) {
                        for (Universal::Architectures::const_iterator arch = architectures.begin(); arch != architectures.end(); ++arch) {
-                               ctx.offset = fat->archOffset(*arch);
-                               if (ctx.offset > SIZE_MAX)
-                                       MacOSError::throwMe(errSecCSBadObjectFormat);
-                               ctx.size = fat->lengthOfSlice((size_t)ctx.offset);
-                               if (ctx.offset != activeOffset) {       // inactive architecture; check it
-                                       SecPointer<SecStaticCode> subcode = new SecStaticCode(DiskRep::bestGuess(this->mainExecutablePath(), &ctx));
-                                       subcode->detachedSignature(this->mDetachedSig); // carry over explicit (but not implicit) detached signature
-                                       if (this->teamID() == NULL || subcode->teamID() == NULL) {
-                                               if (this->teamID() != subcode->teamID())
+                               try {
+                                       ctx.offset = int_cast<size_t, off_t>(fat->archOffset(*arch));
+                                       ctx.size = fat->lengthOfSlice(int_cast<off_t,size_t>(ctx.offset));
+                                       if (ctx.offset != activeOffset) {       // inactive architecture; check it
+                                               SecPointer<SecStaticCode> subcode = new SecStaticCode(DiskRep::bestGuess(this->mainExecutablePath(), &ctx));
+                                               subcode->detachedSignature(this->mDetachedSig); // carry over explicit (but not implicit) detached signature
+                                               if (this->teamID() == NULL || subcode->teamID() == NULL) {
+                                                       if (this->teamID() != subcode->teamID())
+                                                               MacOSError::throwMe(errSecCSSignatureInvalid);
+                                               } else if (strcmp(this->teamID(), subcode->teamID()) != 0)
                                                        MacOSError::throwMe(errSecCSSignatureInvalid);
                                                        MacOSError::throwMe(errSecCSSignatureInvalid);
-                                       } else if (strcmp(this->teamID(), subcode->teamID()) != 0)
-                                               MacOSError::throwMe(errSecCSSignatureInvalid);
-                                       handle(subcode);
+                                               handle(subcode);
+                                       }
+                               } catch(std::out_of_range e) {
+                                       // some of our int_casts fell over.
+                                       MacOSError::throwMe(errSecCSBadObjectFormat);
                                }
                        }
                }
                                }
                        }
                }
@@ -1882,10 +2413,35 @@ bool SecStaticCode::isAppleDeveloperCert(CFArrayRef certs)
 {
        static const std::string appleDeveloperRequirement = "(" + std::string(WWDRRequirement) + ") or (" + MACWWDRRequirement + ") or (" + developerID + ") or (" + distributionCertificate + ") or (" + iPhoneDistributionCert + ")";
        SecPointer<SecRequirement> req = new SecRequirement(parseRequirement(appleDeveloperRequirement), true);
 {
        static const std::string appleDeveloperRequirement = "(" + std::string(WWDRRequirement) + ") or (" + MACWWDRRequirement + ") or (" + developerID + ") or (" + distributionCertificate + ") or (" + iPhoneDistributionCert + ")";
        SecPointer<SecRequirement> req = new SecRequirement(parseRequirement(appleDeveloperRequirement), true);
-       Requirement::Context ctx(certs, NULL, NULL, "", NULL);
+       Requirement::Context ctx(certs, NULL, NULL, "", NULL, NULL, kSecCodeSignatureNoHash, false, NULL, "");
 
        return req->requirement()->validates(ctx);
 }
 
 
        return req->requirement()->validates(ctx);
 }
 
+CFDataRef SecStaticCode::createCmsDigest()
+{
+       /*
+        * The CMS digest is a hash of the primary (first, most compatible) code directory,
+        * but its hash algorithm is fixed and not related to the code directory's
+        * hash algorithm.
+        */
+       
+       auto it = codeDirectories()->begin();
+       
+       if (it == codeDirectories()->end()) {
+               return NULL;
+       }
+
+       CodeDirectory const * const cd = reinterpret_cast<CodeDirectory const*>(CFDataGetBytePtr(it->second));
+       
+       RefPointer<DynamicHash> hash = cd->hashFor(mCMSDigestHashType);
+       CFMutableDataRef data = CFDataCreateMutable(NULL, hash->digestLength());
+       CFDataSetLength(data, hash->digestLength());
+       hash->update(cd, cd->length());
+       hash->finish(CFDataGetMutableBytePtr(data));
+       
+       return data;
+}
+       
 } // end namespace CodeSigning
 } // end namespace Security
 } // end namespace CodeSigning
 } // end namespace Security