]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_codesigning/lib/StaticCode.cpp
Security-57740.31.2.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / StaticCode.cpp
index 6bcfb5005208a7ca8ff1bf8dfb0eb62183b19c80..20c6d40a9cc42b0b50deea119b684a20426cd8ff 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2006-2015 Apple Inc. All Rights Reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -49,6 +49,7 @@
 #include <Security/CMSDecoder.h>
 #include <security_utilities/logging.h>
 #include <dirent.h>
 #include <Security/CMSDecoder.h>
 #include <security_utilities/logging.h>
 #include <dirent.h>
+#include <sys/xattr.h>
 #include <sstream>
 #include <IOKit/storage/IOStorageDeviceCharacteristics.h>
 #include <dispatch/private.h>
 #include <sstream>
 #include <IOKit/storage/IOStorageDeviceCharacteristics.h>
 #include <dispatch/private.h>
@@ -94,9 +95,6 @@ SecStaticCode::SecStaticCode(DiskRep *rep)
          mDesignatedReq(NULL), mGotResourceBase(false), mMonitor(NULL), mLimitedAsync(NULL), mEvalDetails(NULL)
 {
        CODESIGN_STATIC_CREATE(this, rep);
          mDesignatedReq(NULL), mGotResourceBase(false), mMonitor(NULL), mLimitedAsync(NULL), mEvalDetails(NULL)
 {
        CODESIGN_STATIC_CREATE(this, rep);
-       CFRef<CFDataRef> codeDirectory = rep->codeDirectory();
-       if (codeDirectory && CFDataGetLength(codeDirectory) <= 0)
-               MacOSError::throwMe(errSecCSSignatureInvalid);
        checkForSystemSignature();
 }
 
        checkForSystemSignature();
 }
 
@@ -377,14 +375,27 @@ CFDataRef SecStaticCode::component(CodeDirectory::SpecialSlot slot, OSStatus fai
 // Always throws if the CodeDirectory exists but is invalid.
 // NEVER validates against the signature.
 //
 // Always throws if the CodeDirectory exists but is invalid.
 // NEVER validates against the signature.
 //
-const CodeDirectory *SecStaticCode::codeDirectory(bool check /* = true */)
+const CodeDirectory *SecStaticCode::codeDirectory(bool check /* = true */) const
 {
        if (!mDir) {
 {
        if (!mDir) {
-               if (mDir.take(mRep->codeDirectory())) {
-                       const CodeDirectory *dir = reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(mDir));
-                       if (!dir->validateBlob(CFDataGetLength(mDir)))
-                               MacOSError::throwMe(errSecCSSignatureInvalid);
-                       dir->checkIntegrity();
+               // pick our favorite CodeDirectory from the choices we've got
+               try {
+                       CodeDirectoryMap candidates;
+                       if (loadCodeDirectories(candidates)) {
+                               CodeDirectory::HashAlgorithm type = CodeDirectory::bestHashOf(mHashAlgorithms);
+                               mDir = candidates[type];                                                                // and the winner is...
+                               candidates.swap(mCodeDirectories);
+                       }
+               } catch (...) {
+                       if (check)
+                               throw;
+                       // We wanted a NON-checked peek and failed to safely decode the existing CodeDirectory.
+                       // Pretend this is unsigned, but make sure we didn't somehow cache an invalid CodeDirectory.
+                       if (mDir) {
+                               assert(false);
+                               Syslog::warning("code signing internal problem: mDir set despite exception exit");
+                               MacOSError::throwMe(errSecCSInternalError);
+                       }
                }
        }
        if (mDir)
                }
        }
        if (mDir)
@@ -395,6 +406,46 @@ const CodeDirectory *SecStaticCode::codeDirectory(bool check /* = true */)
 }
 
 
 }
 
 
+//
+// Fetch an array of all available CodeDirectories.
+// Returns false if unsigned (no classic CD slot), true otherwise.
+//
+bool SecStaticCode::loadCodeDirectories(CodeDirectoryMap& cdMap) const
+{
+       __block CodeDirectoryMap candidates;
+       __block CodeDirectory::HashAlgorithms hashAlgorithms;
+       __block CFRef<CFDataRef> baseDir;
+       auto add = ^bool (CodeDirectory::SpecialSlot slot){
+               CFRef<CFDataRef> cdData = diskRep()->component(slot);
+               if (!cdData)
+                       return false;
+               const CodeDirectory* cd = reinterpret_cast<const CodeDirectory*>(CFDataGetBytePtr(cdData));
+               if (!cd->validateBlob(CFDataGetLength(cdData)))
+                       MacOSError::throwMe(errSecCSSignatureFailed);   // no recovery - any suspect CD fails
+               cd->checkIntegrity();
+               auto result = candidates.insert(make_pair(cd->hashType, cdData.get()));
+               if (!result.second)
+                       MacOSError::throwMe(errSecCSSignatureInvalid);  // duplicate hashType, go to heck
+               hashAlgorithms.insert(cd->hashType);
+               if (slot == cdCodeDirectorySlot)
+                       baseDir = cdData;
+               return true;
+       };
+       if (!add(cdCodeDirectorySlot))
+               return false;           // no classic slot CodeDirectory -> unsigned
+       for (CodeDirectory::SpecialSlot slot = cdAlternateCodeDirectorySlots; slot < cdAlternateCodeDirectoryLimit; slot++)
+               if (!add(slot))         // no CodeDirectory at this slot -> end of alternates
+                       break;
+       if (candidates.empty())
+               MacOSError::throwMe(errSecCSSignatureFailed);           // no viable CodeDirectory in sight
+       // commit to cached values
+       cdMap.swap(candidates);
+       mHashAlgorithms.swap(hashAlgorithms);
+       mBaseDir = baseDir;
+       return true;
+}
+
+
 //
 // Get the hash of the CodeDirectory.
 // Returns NULL if there is none.
 //
 // Get the hash of the CodeDirectory.
 // Returns NULL if there is none.
@@ -409,6 +460,25 @@ CFDataRef SecStaticCode::cdHash()
        }
        return mCDHash;
 }
        }
        return mCDHash;
 }
+       
+       
+//
+// Get an array of the cdhashes for all digest types in this signature
+// The array is sorted by cd->hashType.
+//
+CFArrayRef SecStaticCode::cdHashes()
+{
+       if (!mCDHashes) {
+               CFRef<CFMutableArrayRef> cdList = makeCFMutableArray(0);
+               for (auto it = mCodeDirectories.begin(); it != mCodeDirectories.end(); ++it) {
+                       const CodeDirectory *cd = (const CodeDirectory *)CFDataGetBytePtr(it->second);
+                       if (CFRef<CFDataRef> hash = cd->cdhash())
+                               CFArrayAppendValue(cdList, hash);
+               }
+               mCDHashes = cdList.get();
+       }
+       return mCDHashes;
+}
 
 
 //
 
 
 //
@@ -452,8 +522,9 @@ void SecStaticCode::validateDirectory()
                        mValidationResult = err.osStatus();
                        throw;
                } catch (...) {
                        mValidationResult = err.osStatus();
                        throw;
                } catch (...) {
-                       secdebug("staticCode", "%p validation threw non-common exception", this);
+                       secinfo("staticCode", "%p validation threw non-common exception", this);
                        mValidated = true;
                        mValidated = true;
+                       Syslog::notice("code signing internal problem: unknown exception thrown by validation");
                        mValidationResult = errSecCSInternalError;
                        throw;
                }
                        mValidationResult = errSecCSInternalError;
                        throw;
                }
@@ -484,6 +555,33 @@ void SecStaticCode::validateNonResourceComponents()
                        break;
                }
 }
                        break;
                }
 }
+       
+       
+//
+// Check that any "top index" sealed into the signature conforms to what's actually here.
+//
+void SecStaticCode::validateTopDirectory()
+{
+       assert(mDir);           // must already have loaded CodeDirectories
+       if (CFDataRef topDirectory = component(cdTopDirectorySlot)) {
+               const auto topData = (const Endian<uint32_t> *)CFDataGetBytePtr(topDirectory);
+               const auto topDataEnd = topData + CFDataGetLength(topDirectory) / sizeof(*topData);
+               std::vector<uint32_t> signedVector(topData, topDataEnd);
+               
+               std::vector<uint32_t> foundVector;
+               foundVector.push_back(cdCodeDirectorySlot);     // mandatory
+               for (CodeDirectory::Slot slot = 1; slot <= cdSlotMax; ++slot)
+                       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++)
+                       foundVector.push_back(cdAlternateCodeDirectorySlots + n);
+               foundVector.push_back(cdSignatureSlot);         // mandatory (may be empty)
+               
+               if (signedVector != foundVector)
+                       MacOSError::throwMe(errSecCSSignatureFailed);
+       }
+}
 
 
 //
 
 
 //
@@ -508,7 +606,7 @@ CFAbsoluteTime SecStaticCode::signingTimestamp()
 
 
 //
 
 
 //
-// Verify the CMS signature on the CodeDirectory.
+// Verify the CMS signature.
 // This performs the cryptographic tango. It returns if the signature is valid,
 // or throws if it is not. As a side effect, a successful return sets up the
 // cached certificate chain for future use.
 // This performs the cryptographic tango. It returns if the signature is valid,
 // or throws if it is not. As a side effect, a successful return sets up the
 // cached certificate chain for future use.
@@ -531,14 +629,15 @@ bool SecStaticCode::verifySignature()
        CFDataRef sig = this->signature();
        MacOSError::check(CMSDecoderUpdateMessage(cms, CFDataGetBytePtr(sig), CFDataGetLength(sig)));
        this->codeDirectory();  // load CodeDirectory (sets mDir)
        CFDataRef sig = this->signature();
        MacOSError::check(CMSDecoderUpdateMessage(cms, CFDataGetBytePtr(sig), CFDataGetLength(sig)));
        this->codeDirectory();  // load CodeDirectory (sets mDir)
-       MacOSError::check(CMSDecoderSetDetachedContent(cms, mDir));
+       MacOSError::check(CMSDecoderSetDetachedContent(cms, mBaseDir));
        MacOSError::check(CMSDecoderFinalizeMessage(cms));
        MacOSError::check(CMSDecoderSetSearchKeychain(cms, cfEmptyArray()));
        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));
+       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;
 
        if (status != kCMSSignerValid) {
                const char *reason;
@@ -555,6 +654,24 @@ bool SecStaticCode::verifySignature()
                MacOSError::throwMe(errSecCSSignatureFailed);
        }
 
                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);
+               }
+               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)) {
        // 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)) {
@@ -581,13 +698,17 @@ bool SecStaticCode::verifySignature()
     if (mValidationFlags & kSecCSNoNetworkAccess) {
         MacOSError::check(SecTrustSetNetworkFetchAllowed(mTrust,false)); // no network?
     }
     if (mValidationFlags & kSecCSNoNetworkAccess) {
         MacOSError::check(SecTrustSetNetworkFetchAllowed(mTrust,false)); // no network?
     }
+#if !SECTRUST_OSX
     MacOSError::check(SecTrustSetKeychains(mTrust, cfEmptyArray())); // no keychains
     MacOSError::check(SecTrustSetKeychains(mTrust, cfEmptyArray())); // no keychains
-       
+#else
+    MacOSError::check(SecTrustSetKeychainsAllowed(mTrust, false));
+#endif
+
        CSSM_APPLE_TP_ACTION_DATA actionData = {
                CSSM_APPLE_TP_ACTION_VERSION,   // version of data structure
                0       // action flags
        };
        CSSM_APPLE_TP_ACTION_DATA actionData = {
                CSSM_APPLE_TP_ACTION_VERSION,   // version of data structure
                0       // action flags
        };
-       
+
        if (!(mValidationFlags & kSecCSCheckTrustedAnchors)) {
                /* no need to evaluate anchor trust when building cert chain */
                MacOSError::check(SecTrustSetAnchorCertificates(mTrust, cfEmptyArray())); // no anchors
        if (!(mValidationFlags & kSecCSCheckTrustedAnchors)) {
                /* no need to evaluate anchor trust when building cert chain */
                MacOSError::check(SecTrustSetAnchorCertificates(mTrust, cfEmptyArray())); // no anchors
@@ -616,12 +737,13 @@ bool SecStaticCode::verifySignature()
                                        CFRef<CFStringRef> teamIDFromCD = CFStringCreateWithCString(NULL, teamID(), kCFStringEncodingUTF8);
                                        if (!teamIDFromCD) {
                                                Security::Syslog::error("Could not get team identifier (%s)", teamID());
                                        CFRef<CFStringRef> teamIDFromCD = CFStringCreateWithCString(NULL, teamID(), kCFStringEncodingUTF8);
                                        if (!teamIDFromCD) {
                                                Security::Syslog::error("Could not get team identifier (%s)", teamID());
-                                               MacOSError::throwMe(errSecCSInternalError);
+                                               MacOSError::throwMe(errSecCSInvalidTeamIdentifier);
                                        }
 
                                        if (CFStringCompare(teamIDFromCert, teamIDFromCD, 0) != kCFCompareEqualTo) {
                                        }
 
                                        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(errSecCSSignatureInvalid);
+                                               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);
                                        }
                                }
                        }
                                        }
                                }
                        }
@@ -687,6 +809,7 @@ bool SecStaticCode::verifySignature()
 // This may be a simple SecPolicyRef or a CFArray of policies.
 // The caller owns the return value.
 //
 // This may be a simple SecPolicyRef or a CFArray of policies.
 // The caller owns the return value.
 //
+#if !SECTRUST_OSX
 static SecPolicyRef makeCRLPolicy()
 {
        CFRef<SecPolicyRef> policy;
 static SecPolicyRef makeCRLPolicy()
 {
        CFRef<SecPolicyRef> policy;
@@ -712,23 +835,35 @@ static SecPolicyRef makeOCSPPolicy()
        MacOSError::check(SecPolicySetValue(policy, &optData));
        return policy.yield();
 }
        MacOSError::check(SecPolicySetValue(policy, &optData));
        return policy.yield();
 }
+#else
+static SecPolicyRef makeRevocationPolicy(CFOptionFlags flags)
+{
+       CFRef<SecPolicyRef> policy(SecPolicyCreateRevocation(flags));
+       return policy.yield();
+}
+#endif
 
 CFArrayRef SecStaticCode::verificationPolicies()
 {
        CFRef<SecPolicyRef> core;
        MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3,
                        &CSSMOID_APPLE_TP_CODE_SIGNING, &core.aref()));
 
 CFArrayRef SecStaticCode::verificationPolicies()
 {
        CFRef<SecPolicyRef> core;
        MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3,
                        &CSSMOID_APPLE_TP_CODE_SIGNING, &core.aref()));
-    if (mValidationFlags & kSecCSNoNetworkAccess) {
-        // Skips all revocation since they require network connectivity
-        // therefore annihilates kSecCSEnforceRevocationChecks if present
-        CFRef<SecPolicyRef> no_revoc = SecPolicyCreateRevocation(kSecRevocationNetworkAccessDisabled);
-        return makeCFArray(2, core.get(), no_revoc.get());
-    }
+       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, core.get(), no_revoc.get());
+       }
        else if (mValidationFlags & kSecCSEnforceRevocationChecks) {
        else if (mValidationFlags & kSecCSEnforceRevocationChecks) {
-        // Add CRL and OCSPPolicies
+        // Add CRL and OCSP policies
+#if !SECTRUST_OSX
                CFRef<SecPolicyRef> crl = makeCRLPolicy();
                CFRef<SecPolicyRef> ocsp = makeOCSPPolicy();
                return makeCFArray(3, core.get(), crl.get(), ocsp.get());
                CFRef<SecPolicyRef> crl = makeCRLPolicy();
                CFRef<SecPolicyRef> ocsp = makeOCSPPolicy();
                return makeCFArray(3, core.get(), crl.get(), ocsp.get());
+#else
+               CFRef<SecPolicyRef> revoc = makeRevocationPolicy(kSecRevocationUseAnyAvailableMethod);
+               return makeCFArray(2, core.get(), revoc.get());
+#endif
        } else {
                return makeCFArray(1, core.get());
        }
        } else {
                return makeCFArray(1, core.get());
        }
@@ -776,15 +911,24 @@ void SecStaticCode::validateExecutable()
                        if (Universal *fat = mRep->mainExecutableImage())
                                fd.seek(fat->archOffset());
                        size_t pageSize = cd->pageSize ? (1 << cd->pageSize) : 0;
                        if (Universal *fat = mRep->mainExecutableImage())
                                fd.seek(fat->archOffset());
                        size_t pageSize = cd->pageSize ? (1 << cd->pageSize) : 0;
-                       size_t remaining = cd->codeLimit;
+                       size_t remaining = cd->signingLimit();
                        for (uint32_t slot = 0; slot < cd->nCodeSlots; ++slot) {
                        for (uint32_t slot = 0; slot < cd->nCodeSlots; ++slot) {
-                               size_t size = min(remaining, pageSize);
-                               if (!cd->validateSlot(fd, size, slot)) {
+                               size_t thisPage = remaining;
+                               if (pageSize)
+                                       thisPage = min(thisPage, pageSize);
+                               __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]))
+                                               good = false;
+                               });
+                               if (!good) {
                                        CODESIGN_EVAL_STATIC_EXECUTABLE_FAIL(this, (int)slot);
                                        MacOSError::throwMe(errSecCSSignatureFailed);
                                }
                                        CODESIGN_EVAL_STATIC_EXECUTABLE_FAIL(this, (int)slot);
                                        MacOSError::throwMe(errSecCSSignatureFailed);
                                }
-                               remaining -= size;
+                               remaining -= thisPage;
                        }
                        }
+                       assert(remaining == 0);
                        mExecutableValidated = true;
                        mExecutableValidResult = errSecSuccess;
                } catch (const CommonError &err) {
                        mExecutableValidated = true;
                        mExecutableValidResult = errSecSuccess;
                } catch (const CommonError &err) {
@@ -792,9 +936,10 @@ void SecStaticCode::validateExecutable()
                        mExecutableValidResult = err.osStatus();
                        throw;
                } catch (...) {
                        mExecutableValidResult = err.osStatus();
                        throw;
                } catch (...) {
-                       secdebug("staticCode", "%p executable validation threw non-common exception", this);
+                       secinfo("staticCode", "%p executable validation threw non-common exception", this);
                        mExecutableValidated = true;
                        mExecutableValidResult = errSecCSInternalError;
                        mExecutableValidated = true;
                        mExecutableValidResult = errSecCSInternalError;
+                       Syslog::notice("code signing internal problem: unknown exception thrown by validation");
                        throw;
                }
        }
                        throw;
                }
        }
@@ -836,18 +981,11 @@ void SecStaticCode::validateResources(SecCSFlags flags)
                }
 
                try {
                }
 
                try {
-                       // sanity first
-                       CFDictionaryRef sealedResources = resourceDictionary();
-                       if (this->resourceBase())       // disk has resources
-                               if (sealedResources)
-                                       /* go to work below */;
-                               else
-                                       MacOSError::throwMe(errSecCSResourcesNotFound);
-                       else                                                    // disk has no resources
-                               if (sealedResources)
-                                       MacOSError::throwMe(errSecCSResourcesNotFound);
-                               else
-                                       return;                                 // no resources, not sealed - fine (no work)
+                       CFDictionaryRef rules;
+                       CFDictionaryRef files;
+                       uint32_t version;
+                       if (!loadResources(rules, files, version))
+                               return;         // validly no resources; nothing to do (ok)
 
                        // found resources, and they are sealed
                        DTRACK(CODESIGN_EVAL_STATIC_RESOURCES, this,
 
                        // found resources, and they are sealed
                        DTRACK(CODESIGN_EVAL_STATIC_RESOURCES, this,
@@ -856,22 +994,6 @@ void SecStaticCode::validateResources(SecCSFlags flags)
                        // scan through the resources on disk, checking each against the resourceDirectory
                        mResourcesValidContext = new CollectingContext(*this);          // collect all failures in here
 
                        // scan through the resources on disk, checking each against the resourceDirectory
                        mResourcesValidContext = new CollectingContext(*this);          // collect all failures in here
 
-                       // use V2 resource seal if available, otherwise fall back to V1
-                       CFDictionaryRef rules;
-                       CFDictionaryRef files;
-                       uint32_t version;
-                       if (CFDictionaryGetValue(sealedResources, CFSTR("files2"))) {   // have V2 signature
-                               rules = cfget<CFDictionaryRef>(sealedResources, "rules2");
-                               files = cfget<CFDictionaryRef>(sealedResources, "files2");
-                               version = 2;
-                       } else {        // only V1 available
-                               rules = cfget<CFDictionaryRef>(sealedResources, "rules");
-                               files = cfget<CFDictionaryRef>(sealedResources, "files");
-                               version = 1;
-                       }
-                       if (!rules || !files)
-                               MacOSError::throwMe(errSecCSResourcesInvalid);
-
                        // check for weak resource rules
                        bool strict = flags & kSecCSStrictValidate;
                        if (strict) {
                        // check for weak resource rules
                        bool strict = flags & kSecCSStrictValidate;
                        if (strict) {
@@ -889,7 +1011,7 @@ void SecStaticCode::validateResources(SecCSFlags flags)
                        // scan through the resources on disk, checking each against the resourceDirectory
                        __block CFRef<CFMutableDictionaryRef> resourceMap = makeCFMutableDictionary(files);
                        string base = cfString(this->resourceBase());
                        // scan through the resources on disk, checking each against the resourceDirectory
                        __block CFRef<CFMutableDictionaryRef> resourceMap = makeCFMutableDictionary(files);
                        string base = cfString(this->resourceBase());
-                       ResourceBuilder resources(base, base, rules, codeDirectory()->hashType, strict, mTolerateErrors);
+                       ResourceBuilder resources(base, base, rules, strict, mTolerateErrors);
                        this->mResourceScope = &resources;
                        diskRep()->adjustResources(resources);
 
                        this->mResourceScope = &resources;
                        diskRep()->adjustResources(resources);
 
@@ -908,7 +1030,7 @@ void SecStaticCode::validateResources(SecCSFlags flags)
 
                        unsigned leftovers = unsigned(CFDictionaryGetCount(resourceMap));
                        if (leftovers > 0) {
 
                        unsigned leftovers = unsigned(CFDictionaryGetCount(resourceMap));
                        if (leftovers > 0) {
-                               secdebug("staticCode", "%d sealed resource(s) not found in code", int(leftovers));
+                               secinfo("staticCode", "%d sealed resource(s) not found in code", int(leftovers));
                                CFDictionaryApplyFunction(resourceMap, SecStaticCode::checkOptionalResource, mResourcesValidContext);
                        }
 
                                CFDictionaryApplyFunction(resourceMap, SecStaticCode::checkOptionalResource, mResourcesValidContext);
                        }
 
@@ -923,10 +1045,11 @@ void SecStaticCode::validateResources(SecCSFlags flags)
                        mResourcesValidResult = err.osStatus();
                        throw;
                } catch (...) {
                        mResourcesValidResult = err.osStatus();
                        throw;
                } catch (...) {
-                       secdebug("staticCode", "%p executable validation threw non-common exception", this);
+                       secinfo("staticCode", "%p executable validation threw non-common exception", this);
                        mResourcesValidated = true;
                        mResourcesDeep = flags & kSecCSCheckNestedCode;
                        mResourcesValidResult = errSecCSInternalError;
                        mResourcesValidated = true;
                        mResourcesDeep = flags & kSecCSCheckNestedCode;
                        mResourcesValidResult = errSecCSInternalError;
+                       Syslog::notice("code signing internal problem: unknown exception thrown by validation");
                        throw;
                }
        }
                        throw;
                }
        }
@@ -938,6 +1061,38 @@ void SecStaticCode::validateResources(SecCSFlags flags)
 }
 
 
 }
 
 
+bool SecStaticCode::loadResources(CFDictionaryRef& rules, CFDictionaryRef& files, uint32_t& version)
+{
+       // sanity first
+       CFDictionaryRef sealedResources = resourceDictionary();
+       if (this->resourceBase()) {     // disk has resources
+               if (sealedResources)
+                       /* go to work below */;
+               else
+                       MacOSError::throwMe(errSecCSResourcesNotFound);
+       } else {                                                        // disk has no resources
+               if (sealedResources)
+                       MacOSError::throwMe(errSecCSResourcesNotFound);
+               else
+                       return false;                                   // no resources, not sealed - fine (no work)
+       }
+       
+       // use V2 resource seal if available, otherwise fall back to V1
+       if (CFDictionaryGetValue(sealedResources, CFSTR("files2"))) {   // have V2 signature
+               rules = cfget<CFDictionaryRef>(sealedResources, "rules2");
+               files = cfget<CFDictionaryRef>(sealedResources, "files2");
+               version = 2;
+       } else {        // only V1 available
+               rules = cfget<CFDictionaryRef>(sealedResources, "rules");
+               files = cfget<CFDictionaryRef>(sealedResources, "files");
+               version = 1;
+       }
+       if (!rules || !files)
+               MacOSError::throwMe(errSecCSResourcesInvalid);
+       return true;
+}
+       
+
 void SecStaticCode::checkOptionalResource(CFTypeRef key, CFTypeRef value, void *context)
 {
        ValidationContext *ctx = static_cast<ValidationContext *>(context);
 void SecStaticCode::checkOptionalResource(CFTypeRef key, CFTypeRef value, void *context)
 {
        ValidationContext *ctx = static_cast<ValidationContext *>(context);
@@ -969,8 +1124,10 @@ bool SecStaticCode::hasWeakResourceRules(CFDictionaryRef rulesDict, uint32_t ver
 {
        // compute allowed omissions
        CFRef<CFArrayRef> defaultOmissions = this->diskRep()->allowedResourceOmissions();
 {
        // compute allowed omissions
        CFRef<CFArrayRef> defaultOmissions = this->diskRep()->allowedResourceOmissions();
-       if (!defaultOmissions)
+       if (!defaultOmissions) {
+               Syslog::notice("code signing internal problem: diskRep returned no allowedResourceOmissions");
                MacOSError::throwMe(errSecCSInternalError);
                MacOSError::throwMe(errSecCSInternalError);
+       }
        CFRef<CFMutableArrayRef> allowed = CFArrayCreateMutableCopy(NULL, 0, defaultOmissions);
        if (allowedOmissions)
                CFArrayAppendArray(allowed, allowedOmissions, CFRangeMake(0, CFArrayGetCount(allowedOmissions)));
        CFRef<CFMutableArrayRef> allowed = CFArrayCreateMutableCopy(NULL, 0, defaultOmissions);
        if (allowedOmissions)
                CFArrayAppendArray(allowed, allowedOmissions, CFRangeMake(0, CFArrayGetCount(allowedOmissions)));
@@ -1003,7 +1160,7 @@ CFDictionaryRef SecStaticCode::infoDictionary()
 {
        if (!mInfoDict) {
                mInfoDict.take(getDictionary(cdInfoSlot, errSecCSInfoPlistFailed));
 {
        if (!mInfoDict) {
                mInfoDict.take(getDictionary(cdInfoSlot, errSecCSInfoPlistFailed));
-               secdebug("staticCode", "%p loaded InfoDict %p", this, mInfoDict.get());
+               secinfo("staticCode", "%p loaded InfoDict %p", this, mInfoDict.get());
        }
        return mInfoDict;
 }
        }
        return mInfoDict;
 }
@@ -1017,7 +1174,7 @@ CFDictionaryRef SecStaticCode::entitlements()
                        const EntitlementBlob *blob = reinterpret_cast<const EntitlementBlob *>(CFDataGetBytePtr(entitlementData));
                        if (blob->validateBlob()) {
                                mEntitlements.take(blob->entitlements());
                        const EntitlementBlob *blob = reinterpret_cast<const EntitlementBlob *>(CFDataGetBytePtr(entitlementData));
                        if (blob->validateBlob()) {
                                mEntitlements.take(blob->entitlements());
-                               secdebug("staticCode", "%p loaded Entitlements %p", this, mEntitlements.get());
+                               secinfo("staticCode", "%p loaded Entitlements %p", this, mEntitlements.get());
                        }
                        // we do not consider a different blob type to be an error. We think it's a new format we don't understand
                }
                        }
                        // we do not consider a different blob type to be an error. We think it's a new format we don't understand
                }
@@ -1031,13 +1188,31 @@ CFDictionaryRef SecStaticCode::resourceDictionary(bool check /* = true */)
                return mResourceDict;
        if (CFRef<CFDictionaryRef> dict = getDictionary(cdResourceDirSlot, check))
                if (cfscan(dict, "{rules=%Dn,files=%Dn}")) {
                return mResourceDict;
        if (CFRef<CFDictionaryRef> dict = getDictionary(cdResourceDirSlot, check))
                if (cfscan(dict, "{rules=%Dn,files=%Dn}")) {
-                       secdebug("staticCode", "%p loaded ResourceDict %p",
+                       secinfo("staticCode", "%p loaded ResourceDict %p",
                                this, mResourceDict.get());
                        return mResourceDict = dict;
                }
        // bad format
        return NULL;
 }
                                this, mResourceDict.get());
                        return mResourceDict = dict;
                }
        // bad format
        return NULL;
 }
+       
+       
+CFDataRef SecStaticCode::copyComponent(CodeDirectory::SpecialSlot slot, CFDataRef hash)
+{
+       const CodeDirectory* cd = this->codeDirectory();
+       if (CFCopyRef<CFDataRef> component = this->component(slot)) {
+               if (hash) {
+                       const void *slotHash = (*cd)[slot];
+                       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
+                       }
+               }
+               return component.yield();
+       }
+       return NULL;
+}
+
 
 
 //
 
 
 //
@@ -1075,66 +1250,28 @@ CFDictionaryRef SecStaticCode::getDictionary(CodeDirectory::SpecialSlot slot, bo
        return NULL;
 }
 
        return NULL;
 }
 
-
 //
 //
-// Load, validate, and return a sealed resource.
-// The resource data (loaded in to memory as a blob) is returned and becomes
-// the responsibility of the caller; it is NOT cached by SecStaticCode.
 //
 //
-// A resource that is not sealed will not be returned, and an error will be thrown.
-// A missing resource will cause an error unless it's marked optional in the Directory.
-// Under no circumstances will a corrupt resource be returned.
-// NULL will only be returned for a resource that is neither sealed nor present
-// (or that is sealed, absent, and marked optional).
-// If the ResourceDictionary itself is not sealed, this function will always fail.
 //
 //
-// There is currently no interface for partial retrieval of the resource data.
-// (Since the ResourceDirectory does not currently support segmentation, all the
-// data would have to be read anyway, but it could be read into a reusable buffer.)
-//
-CFDataRef SecStaticCode::resource(string path, ValidationContext &ctx)
+CFDictionaryRef SecStaticCode::diskRepInformation()
 {
 {
-       if (CFDictionaryRef rdict = resourceDictionary()) {
-               if (CFTypeRef file = cfget(rdict, "files.%s", path.c_str())) {
-                       ResourceSeal seal = file;
-                       if (!resourceBase())    // no resources in DiskRep
-                               MacOSError::throwMe(errSecCSResourcesNotFound);
-                       if (seal.nested())
-                               MacOSError::throwMe(errSecCSResourcesNotSealed);        // (it's nested code)
-                       CFRef<CFURLRef> fullpath = makeCFURL(path, false, resourceBase());
-                       if (CFRef<CFDataRef> data = cfLoadFile(fullpath)) {
-                               MakeHash<CodeDirectory> hasher(this->codeDirectory());
-                               hasher->update(CFDataGetBytePtr(data), CFDataGetLength(data));
-                               if (hasher->verify(seal.hash()))
-                                       return data.yield();    // good
-                               else
-                                       ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // altered
-                       } else {
-                               if (!seal.optional())
-                                       ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing, fullpath); // was sealed but is now missing
-                               else
-                                       return NULL;    // validly missing
-                       }
-               } else
-                       ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAdded, CFTempURL(path, false, resourceBase()));
-               return NULL;
-       } else
-               MacOSError::throwMe(errSecCSResourcesNotSealed);
+       return mRep->diskRepInformation();
 }
 
 }
 
-CFDataRef SecStaticCode::resource(string path)
-{
-       ValidationContext ctx(*this);
-       return resource(path, ctx);
-}
 
 void SecStaticCode::validateResource(CFDictionaryRef files, string path, bool isSymlink, ValidationContext &ctx, SecCSFlags flags, uint32_t version)
 {
        if (!resourceBase())    // no resources in DiskRep
                MacOSError::throwMe(errSecCSResourcesNotFound);
        CFRef<CFURLRef> fullpath = makeCFURL(path, false, resourceBase());
 
 void SecStaticCode::validateResource(CFDictionaryRef files, string path, bool isSymlink, ValidationContext &ctx, SecCSFlags flags, uint32_t version)
 {
        if (!resourceBase())    // no resources in DiskRep
                MacOSError::throwMe(errSecCSResourcesNotFound);
        CFRef<CFURLRef> fullpath = makeCFURL(path, false, resourceBase());
+       if (version > 1 && ((flags & (kSecCSStrictValidate|kSecCSRestrictSidebandData)) == (kSecCSStrictValidate|kSecCSRestrictSidebandData))) {
+               AutoFileDesc fd(cfString(fullpath));
+               if (fd.hasExtendedAttribute(XATTR_RESOURCEFORK_NAME) || fd.hasExtendedAttribute(XATTR_FINDERINFO_NAME))
+                       ctx.reportProblem(errSecCSInvalidAssociatedFileData, kSecCFErrorResourceSideband, fullpath);
+       }
        if (CFTypeRef file = CFDictionaryGetValue(files, CFTempString(path))) {
        if (CFTypeRef file = CFDictionaryGetValue(files, CFTempString(path))) {
-               ResourceSeal seal = file;
+               ResourceSeal seal(file);
+               const ResourceSeal& rseal = seal;
                if (seal.nested()) {
                        if (isSymlink)
                                return ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // changed type
                if (seal.nested()) {
                        if (isSymlink)
                                return ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // changed type
@@ -1146,16 +1283,17 @@ void SecStaticCode::validateResource(CFDictionaryRef files, string path, bool is
                        if (!isSymlink)
                                return ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // changed type
                        validateSymlinkResource(cfString(fullpath), cfString(seal.link()), ctx, flags);
                        if (!isSymlink)
                                return ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // changed type
                        validateSymlinkResource(cfString(fullpath), cfString(seal.link()), ctx, flags);
-               } else if (seal.hash()) {       // genuine file
+               } else if (seal.hash(hashAlgorithm())) {        // genuine file
                        if (isSymlink)
                                return ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // changed type
                        AutoFileDesc fd(cfString(fullpath), O_RDONLY, FileDesc::modeMissingOk); // open optional file
                        if (fd) {
                        if (isSymlink)
                                return ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // changed type
                        AutoFileDesc fd(cfString(fullpath), O_RDONLY, FileDesc::modeMissingOk); // open optional file
                        if (fd) {
-                               MakeHash<CodeDirectory> hasher(this->codeDirectory());
-                               hashFileData(fd, hasher.get());
-                               if (hasher->verify(seal.hash()))
-                                       return;                 // verify good
-                               else
+                               __block bool good = true;
+                               CodeDirectory::multipleHashFileData(fd, 0, hashAlgorithms(), ^(CodeDirectory::HashAlgorithm type, Security::DynamicHash *hasher) {
+                                       if (!hasher->verify(rseal.hash(type)))
+                                               good = false;
+                               });
+                               if (!good)
                                        ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // altered
                        } else {
                                if (!seal.optional())
                                        ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // altered
                        } else {
                                if (!seal.optional())
@@ -1174,6 +1312,24 @@ void SecStaticCode::validateResource(CFDictionaryRef files, string path, bool is
        }
        ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAdded, CFTempURL(path, false, resourceBase()));
 }
        }
        ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAdded, CFTempURL(path, false, resourceBase()));
 }
+
+void SecStaticCode::validatePlainMemoryResource(string path, CFDataRef fileData, SecCSFlags flags)
+{
+       CFDictionaryRef rules;
+       CFDictionaryRef files;
+       uint32_t version;
+       if (!loadResources(rules, files, version))
+               MacOSError::throwMe(errSecCSResourcesNotFound);         // no resources sealed; this can't be right
+       if (CFTypeRef file = CFDictionaryGetValue(files, CFTempString(path))) {
+               ResourceSeal seal(file);
+               const Byte *sealHash = seal.hash(hashAlgorithm());
+               if (sealHash) {
+                       if (codeDirectory()->verifyMemoryContent(fileData, sealHash))
+                               return;         // success
+               }
+       }
+       MacOSError::throwMe(errSecCSBadResource);
+}
        
 void SecStaticCode::validateSymlinkResource(std::string fullpath, std::string seal, ValidationContext &ctx, SecCSFlags flags)
 {
        
 void SecStaticCode::validateSymlinkResource(std::string fullpath, std::string seal, ValidationContext &ctx, SecCSFlags flags)
 {
@@ -1236,10 +1392,10 @@ void SecStaticCode::validateNestedCode(CFURLRef path, const ResourceSeal &seal,
        // recursively verify this nested code
        try {
                if (!(flags & kSecCSCheckNestedCode))
        // recursively verify this nested code
        try {
                if (!(flags & kSecCSCheckNestedCode))
-                       flags |= kSecCSBasicValidateOnly;
+                       flags |= kSecCSBasicValidateOnly | kSecCSQuickCheck;
                SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(cfString(path)));
                code->initializeFromParent(*this);
                SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(cfString(path)));
                code->initializeFromParent(*this);
-               code->staticValidate(flags, SecRequirement::required(req));
+               code->staticValidate(flags & ~kSecCSRestrictToAppLike, SecRequirement::required(req));
 
                if (isFramework && (flags & kSecCSStrictValidate))
                        try {
 
                if (isFramework && (flags & kSecCSStrictValidate))
                        try {
@@ -1289,10 +1445,7 @@ void SecStaticCode::validateOtherVersions(CFURLRef path, SecCSFlags flags, SecRe
                while ((entry = scanner.getNext()) != NULL) {
                        std::ostringstream fullPath;
 
                while ((entry = scanner.getNext()) != NULL) {
                        std::ostringstream fullPath;
 
-                       if (entry->d_type != DT_DIR ||
-                               strcmp(entry->d_name, ".") == 0 ||
-                               strcmp(entry->d_name, "..") == 0 ||
-                               strcmp(entry->d_name, "Current") == 0)
+                       if (entry->d_type != DT_DIR || strcmp(entry->d_name, "Current") == 0)
                                continue;
 
                        fullPath << versionsPath.str() << entry->d_name;
                                continue;
 
                        fullPath << versionsPath.str() << entry->d_name;
@@ -1384,14 +1537,16 @@ const Requirement *SecStaticCode::defaultDesignatedRequirement()
                Requirement::Maker::Chain chain(maker, opOr);
 
                // insert cdhash requirement for all architectures
                Requirement::Maker::Chain chain(maker, opOr);
 
                // insert cdhash requirement for all architectures
-               chain.add();
-               maker.cdhash(this->cdHash());
-               handleOtherArchitectures(^(SecStaticCode *subcode) {
-                       if (CFDataRef cdhash = subcode->cdHash()) {
-                               chain.add();
-                               maker.cdhash(cdhash);
-                       }
+               __block CFRef<CFMutableArrayRef> allHashes = CFArrayCreateMutableCopy(NULL, 0, this->cdHashes());
+               handleOtherArchitectures(^(SecStaticCode *other) {
+                       CFArrayRef hashes = other->cdHashes();
+                       CFArrayAppendArray(allHashes, hashes, CFRangeMake(0, CFArrayGetCount(hashes)));
                });
                });
+               CFIndex count = CFArrayGetCount(allHashes);
+               for (CFIndex n = 0; n < count; ++n) {
+                       chain.add();
+                       maker.cdhash(CFDataRef(CFArrayGetValueAtIndex(allHashes, n)));
+               }
                return maker.make();
        } else {
                // full signature: Gin up full context and let DRMaker do its thing
                return maker.make();
        } else {
                // full signature: Gin up full context and let DRMaker do its thing
@@ -1422,9 +1577,6 @@ void SecStaticCode::validateRequirements(SecRequirementType type, SecStaticCode
                /* accept it */;
 }
 
                /* accept it */;
 }
 
-/* Public Key Hash for root:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority */
-static const UInt8 retryRootBytes[] = {0x00,0xd8,0x5a,0x4c,0x25,0xc1,0x22,0xe5,0x8b,0x31,0xef,0x6d,0xba,0xf3,0xcc,0x5f,0x29,0xf1,0x0d,0x61};
-
 //
 // Validate this StaticCode against an external Requirement
 //
 //
 // Validate this StaticCode against an external Requirement
 //
@@ -1434,34 +1586,6 @@ bool SecStaticCode::satisfiesRequirement(const Requirement *req, OSStatus failur
        assert(req);
        validateDirectory();
        result = req->validates(Requirement::Context(mCertChain, infoDictionary(), entitlements(), codeDirectory()->identifier(), codeDirectory()), failure);
        assert(req);
        validateDirectory();
        result = req->validates(Requirement::Context(mCertChain, infoDictionary(), entitlements(), codeDirectory()->identifier(), codeDirectory()), failure);
-       if (result == false) {
-               /* Fix for rdar://problem/21437632: Work around untrusted root in validation chain */
-               CFArrayRef certs = certificates();
-               if (!certs || ((int)CFArrayGetCount(certs) < 1)) {
-                       return false;
-               }
-               SecCertificateRef root = cert((int)CFArrayGetCount(certs) - 1);
-               if (!root) {
-                       return false;
-               }
-               CFDataRef rootHash = SecCertificateCopyPublicKeySHA1Digest(root);
-               if (!rootHash) {
-                       return false;
-               }
-               
-               if ((CFDataGetLength(rootHash) == sizeof(retryRootBytes)) &&
-                       !memcmp(CFDataGetBytePtr(rootHash), retryRootBytes, sizeof(retryRootBytes))) {
-                       // retry with a rebuilt certificate chain, this time evaluating anchor trust
-                       Security::Syslog::debug("Requirements validation failed: retrying");
-                       mResourcesValidated = mValidated = false;
-                       setValidationFlags(mValidationFlags | kSecCSCheckTrustedAnchors);
-               
-                       validateDirectory();
-                       result = req->validates(Requirement::Context(mCertChain, infoDictionary(), entitlements(), codeDirectory()->identifier(), codeDirectory()), failure);
-               }
-               CFRelease(rootHash);
-       }
-       
        return result;
 }
 
        return result;
 }
 
@@ -1529,8 +1653,11 @@ CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
        CFDictionaryAddValue(dict, kSecCodeInfoFormat, CFTempString(this->format()));
        CFDictionaryAddValue(dict, kSecCodeInfoSource, CFTempString(this->signatureSource()));
        CFDictionaryAddValue(dict, kSecCodeInfoUnique, this->cdHash());
        CFDictionaryAddValue(dict, kSecCodeInfoFormat, CFTempString(this->format()));
        CFDictionaryAddValue(dict, kSecCodeInfoSource, CFTempString(this->signatureSource()));
        CFDictionaryAddValue(dict, kSecCodeInfoUnique, this->cdHash());
+       CFDictionaryAddValue(dict, kSecCodeInfoCdHashes, this->cdHashes());
        const CodeDirectory* cd = this->codeDirectory(false);
        CFDictionaryAddValue(dict, kSecCodeInfoDigestAlgorithm, CFTempNumber(cd->hashType));
        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->platform)
                CFDictionaryAddValue(dict, kSecCodeInfoPlatformIdentifier, CFTempNumber(cd->platform));
 
@@ -1547,20 +1674,20 @@ CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
        //
        if (flags & kSecCSSigningInformation)
                try {
        //
        if (flags & kSecCSSigningInformation)
                try {
-                       if (CFArrayRef certs = this->certificates())
-                               CFDictionaryAddValue(dict, kSecCodeInfoCertificates, certs);
                        if (CFDataRef sig = this->signature())
                                CFDictionaryAddValue(dict, kSecCodeInfoCMS, sig);
                        if (CFDataRef sig = this->signature())
                                CFDictionaryAddValue(dict, kSecCodeInfoCMS, sig);
+                       if (const char *teamID = this->teamID())
+                               CFDictionaryAddValue(dict, kSecCodeInfoTeamIdentifier, CFTempString(teamID));
                        if (mTrust)
                                CFDictionaryAddValue(dict, kSecCodeInfoTrust, mTrust);
                        if (mTrust)
                                CFDictionaryAddValue(dict, kSecCodeInfoTrust, mTrust);
+                       if (CFArrayRef certs = this->certificates())
+                               CFDictionaryAddValue(dict, kSecCodeInfoCertificates, certs);
                        if (CFAbsoluteTime time = this->signingTime())
                                if (CFRef<CFDateRef> date = CFDateCreate(NULL, time))
                                        CFDictionaryAddValue(dict, kSecCodeInfoTime, date);
                        if (CFAbsoluteTime time = this->signingTimestamp())
                                if (CFRef<CFDateRef> date = CFDateCreate(NULL, time))
                                        CFDictionaryAddValue(dict, kSecCodeInfoTimestamp, date);
                        if (CFAbsoluteTime time = this->signingTime())
                                if (CFRef<CFDateRef> date = CFDateCreate(NULL, time))
                                        CFDictionaryAddValue(dict, kSecCodeInfoTime, date);
                        if (CFAbsoluteTime time = this->signingTimestamp())
                                if (CFRef<CFDateRef> date = CFDateCreate(NULL, time))
                                        CFDictionaryAddValue(dict, kSecCodeInfoTimestamp, date);
-                       if (const char *teamID = this->teamID())
-                               CFDictionaryAddValue(dict, kSecCodeInfoTeamIdentifier, CFTempString(teamID));
                } catch (...) { }
 
        //
                } catch (...) { }
 
        //
@@ -1599,14 +1726,17 @@ CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
        // to reliably transmit through the API wall so that code outside the Security.framework
        // can use it without having to play nasty tricks to get it.
        //
        // to reliably transmit through the API wall so that code outside the Security.framework
        // can use it without having to play nasty tricks to get it.
        //
-       if (flags & kSecCSInternalInformation)
+       if (flags & kSecCSInternalInformation) {
                try {
                        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);
                try {
                        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 (CFRef<CFDictionaryRef> ddict = diskRepInformation())
+                       CFDictionaryAddValue(dict, kSecCodeInfoDiskRepInfo, ddict);
                } catch (...) { }
                } catch (...) { }
+       }
 
 
        //
 
 
        //
@@ -1667,8 +1797,8 @@ void SecStaticCode::CollectingContext::throwMe()
 //
 // SecStaticCode exposes an a la carte menu of topical validators applying
 // to a given object. The static validation API pulls them together reliably,
 //
 // SecStaticCode exposes an a la carte menu of topical validators applying
 // to a given object. The static validation API pulls them together reliably,
-// but it also adds two matrix dimensions: architecture (for "fat" Mach-O binaries)
-// and nested code. This function will crawl a suitable cross-section of this
+// but it also adds three matrix dimensions: architecture (for "fat" Mach-O binaries),
+// nested code, and multiple digests. This function will crawl a suitable cross-section of this
 // validation matrix based on which options it is given, creating temporary
 // SecStaticCode objects on the fly to complete the task.
 // (The point, of course, is to do as little duplicate work as possible.)
 // validation matrix based on which options it is given, creating temporary
 // SecStaticCode objects on the fly to complete the task.
 // (The point, of course, is to do as little duplicate work as possible.)
@@ -1692,7 +1822,7 @@ void SecStaticCode::staticValidate(SecCSFlags flags, const SecRequirement *req)
                                if ((arch.cpuType() & ~CPU_ARCH_MASK) == CPU_TYPE_POWERPC)
                                        return; // irrelevant to Gatekeeper
                        }
                                if ((arch.cpuType() & ~CPU_ARCH_MASK) == CPU_TYPE_POWERPC)
                                        return; // irrelevant to Gatekeeper
                        }
-                       subcode->detachedSignature(this->mDetachedSig); // carry over explicit (but not implicit) architecture
+                       subcode->detachedSignature(this->mDetachedSig); // carry over explicit (but not implicit) detached signature
                        subcode->staticValidateCore(flags, req);
                });
        reportProgress();
                        subcode->staticValidateCore(flags, req);
                });
        reportProgress();
@@ -1706,7 +1836,7 @@ void SecStaticCode::staticValidate(SecCSFlags flags, const SecRequirement *req)
 
        // perform strict validation if desired
        if (flags & kSecCSStrictValidate)
 
        // perform strict validation if desired
        if (flags & kSecCSStrictValidate)
-               mRep->strictValidate(codeDirectory(), mTolerateErrors);
+               mRep->strictValidate(codeDirectory(), mTolerateErrors, mValidationFlags);
        reportProgress();
 
        // allow monitor intervention
        reportProgress();
 
        // allow monitor intervention
@@ -1722,6 +1852,7 @@ void SecStaticCode::staticValidateCore(SecCSFlags flags, const SecRequirement *r
 {
        try {
                this->validateNonResourceComponents();  // also validates the CodeDirectory
 {
        try {
                this->validateNonResourceComponents();  // also validates the CodeDirectory
+               this->validateTopDirectory();
                if (!(flags & kSecCSDoNotValidateExecutable))
                        this->validateExecutable();
                if (req)
                if (!(flags & kSecCSDoNotValidateExecutable))
                        this->validateExecutable();
                if (req)
@@ -1763,7 +1894,7 @@ void SecStaticCode::handleOtherArchitectures(void (^handle)(SecStaticCode* other
                        for (Universal::Architectures::const_iterator arch = architectures.begin(); arch != architectures.end(); ++arch) {
                                ctx.offset = fat->archOffset(*arch);
                                if (ctx.offset > SIZE_MAX)
                        for (Universal::Architectures::const_iterator arch = architectures.begin(); arch != architectures.end(); ++arch) {
                                ctx.offset = fat->archOffset(*arch);
                                if (ctx.offset > SIZE_MAX)
-                                       MacOSError::throwMe(errSecCSInternalError);
+                                       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));
                                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));