]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_codesigning/lib/StaticCode.cpp
Security-58286.60.28.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / StaticCode.cpp
index 0b993cc7358cf874ddf443d289e4ef63c3917cb8..ff621d537a71b80e2d21ce347b2186535a0b897b 100644 (file)
 #include "StaticCode.h"
 #include "Code.h"
 #include "reqmaker.h"
+#if TARGET_OS_OSX
 #include "drmaker.h"
+#endif
 #include "reqdumper.h"
 #include "reqparser.h"
 #include "sigblob.h"
 #include "resources.h"
 #include "detachedrep.h"
+#if TARGET_OS_OSX
 #include "csdatabase.h"
+#endif
 #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>
+#endif
+#import <Security/SecCMS.h>
 #include <Security/SecCmsContentInfo.h>
 #include <Security/SecCmsSignerInfo.h>
 #include <Security/SecCmsSignedData.h>
+#if TARGET_OS_OSX
 #include <Security/cssmapplePriv.h>
+#endif
 #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 <sys/xattr.h>
 #include <sstream>
 #include <IOKit/storage/IOStorageDeviceCharacteristics.h>
 #include <dispatch/private.h>
+#include <os/assumes.h>
+#include <regex.h>
 
 
 namespace Security {
@@ -87,14 +100,23 @@ static inline OSStatus errorForSlot(CodeDirectory::SpecialSlot slot)
 // Construct a SecStaticCode object given a disk representation object
 //
 SecStaticCode::SecStaticCode(DiskRep *rep)
-       : mRep(rep),
+       : mCheckfix30814861builder1(NULL),
+         mRep(rep),
          mValidated(false), mExecutableValidated(false), mResourcesValidated(false), mResourcesValidContext(NULL),
-         mProgressQueue("com.apple.security.validation-progress", false, DISPATCH_QUEUE_PRIORITY_DEFAULT),
+         mProgressQueue("com.apple.security.validation-progress", false, QOS_CLASS_UNSPECIFIED),
          mOuterScope(NULL), mResourceScope(NULL),
-         mDesignatedReq(NULL), mGotResourceBase(false), mMonitor(NULL), mLimitedAsync(NULL), mEvalDetails(NULL)
+         mDesignatedReq(NULL), mGotResourceBase(false), mMonitor(NULL), mLimitedAsync(NULL)
+#if TARGET_OS_OSX
+    , mEvalDetails(NULL)
+#else
+    , mTrustedSigningCertChain(false)
+#endif
+
 {
        CODESIGN_STATIC_CREATE(this, rep);
+#if TARGET_OS_OSX
        checkForSystemSignature();
+#endif
 }
 
 
@@ -106,6 +128,7 @@ try {
        ::free(const_cast<Requirement *>(mDesignatedReq));
        delete mResourcesValidContext;
        delete mLimitedAsync;
+       delete mCheckfix30814861builder1;
 } catch (...) {
        return;
 }
@@ -158,7 +181,7 @@ CFTypeRef SecStaticCode::reportEvent(CFStringRef stage, CFDictionaryRef info)
 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
@@ -245,6 +268,7 @@ void SecStaticCode::detachedSignature(CFDataRef sigData)
 //
 void SecStaticCode::checkForSystemSignature()
 {
+#if TARGET_OS_OSX
        if (!this->isSigned()) {
                SignatureDatabase db;
                if (db.isOpen())
@@ -256,6 +280,9 @@ void SecStaticCode::checkForSystemSignature()
                        } catch (...) {
                        }
        }
+#else
+    MacOSError::throwMe(errSecUnimplemented);
+#endif
 }
 
 
@@ -327,11 +354,15 @@ void SecStaticCode::resetValidity()
        mGotResourceBase = false;
        mTrust = NULL;
        mCertChain = NULL;
+#if TARGET_OS_OSX
        mEvalDetails = NULL;
+#endif
        mRep->flush();
 
+#if TARGET_OS_OSX
        // we may just have updated the system database, so check again
        checkForSystemSignature();
+#endif
 }
 
 
@@ -378,11 +409,23 @@ const CodeDirectory *SecStaticCode::codeDirectory(bool check /* = true */) const
 {
        if (!mDir) {
                // pick our favorite CodeDirectory from the choices we've got
-               CodeDirectoryMap candidates;
-               if (loadCodeDirectories(candidates)) {
-                       CodeDirectory::HashAlgorithm type = CodeDirectory::bestHashOf(mHashAlgorithms);
-                       mDir = candidates[type];                                                                // and the winner is...
-                       candidates.swap(mCodeDirectories);
+               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)
@@ -400,6 +443,8 @@ const CodeDirectory *SecStaticCode::codeDirectory(bool check /* = true */) const
 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)
@@ -410,10 +455,10 @@ bool SecStaticCode::loadCodeDirectories(CodeDirectoryMap& cdMap) const
                cd->checkIntegrity();
                auto result = candidates.insert(make_pair(cd->hashType, cdData.get()));
                if (!result.second)
-                       MacOSError::throwMe(errSecCSSignatureFailed);   // duplicate hashType, go to heck
-               mHashAlgorithms.insert(cd->hashType);
+                       MacOSError::throwMe(errSecCSSignatureInvalid);  // duplicate hashType, go to heck
+               hashAlgorithms.insert(cd->hashType);
                if (slot == cdCodeDirectorySlot)
-                       mBaseDir = cdData;
+                       baseDir = cdData;
                return true;
        };
        if (!add(cdCodeDirectorySlot))
@@ -423,7 +468,10 @@ bool SecStaticCode::loadCodeDirectories(CodeDirectoryMap& cdMap) const
                        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;
 }
 
@@ -504,12 +552,17 @@ void SecStaticCode::validateDirectory()
                        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;
+                       Syslog::notice("code signing internal problem: unknown exception thrown by validation");
                        mValidationResult = errSecCSInternalError;
                        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)
@@ -517,6 +570,7 @@ void SecStaticCode::validateDirectory()
                                MacOSError::throwMe(CSSMERR_TP_CERT_EXPIRED);
        } else
                MacOSError::throwMe(mValidationResult);
+#endif
 }
 
 
@@ -555,7 +609,7 @@ void SecStaticCode::validateTopDirectory()
                        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)
                
@@ -603,7 +657,7 @@ bool SecStaticCode::verifySignature()
        }
 
        DTRACK(CODESIGN_EVAL_STATIC_SIGNATURE, this, (char*)this->mainExecutablePath().c_str());
-
+#if TARGET_OS_OSX
        // decode CMS and extract SecTrust for verification
        CFRef<CMSDecoderRef> cms;
        MacOSError::check(CMSDecoderCreate(&cms.aref())); // create decoder
@@ -613,11 +667,12 @@ bool SecStaticCode::verifySignature()
        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));
+       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;
@@ -633,7 +688,7 @@ bool SecStaticCode::verifySignature()
                                                                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())) {
@@ -678,13 +733,13 @@ bool SecStaticCode::verifySignature()
     if (mValidationFlags & kSecCSNoNetworkAccess) {
         MacOSError::check(SecTrustSetNetworkFetchAllowed(mTrust,false)); // no network?
     }
-    MacOSError::check(SecTrustSetKeychains(mTrust, cfEmptyArray())); // no keychains
-       
+    MacOSError::check(SecTrustSetKeychainsAllowed(mTrust, false));
+
        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
@@ -713,12 +768,13 @@ bool SecStaticCode::verifySignature()
                                        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) {
-                                               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);
                                        }
                                }
                        }
@@ -776,59 +832,131 @@ bool SecStaticCode::verifySignature()
 
                return actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED;
        }
-}
+#else
+    // 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);
+    }
 
+    /*
+     * 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
+     */
+    SecCertificateRef leafCert = SecTrustGetCertificateAtIndex(mTrust, 0);
+    if (leafCert != NULL) {
+        CFIndex count = SecTrustGetCertificateCount(mTrust);
+
+        CFMutableArrayRef certs = CFArrayCreateMutable(kCFAllocatorDefault, count,
+                                                       &kCFTypeArrayCallBacks);
+
+        CFArrayAppendValue(certs, leafCert);
+        for (CFIndex i = 1; i < count; ++i) {
+            CFArrayAppendValue(certs, SecTrustGetCertificateAtIndex(mTrust, i));
+        }
+        
+        mCertChain.take((CFArrayRef)certs);
+    }
+    
+    // Did we implicitly trust the signer?
+    mTrustedSigningCertChain = (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed);
+
+    return false; // XXX: Not checking for expired certs
+#endif
+}
 
+#if TARGET_OS_OSX
 //
 // Return the TP policy used for signature verification.
 // This may be a simple SecPolicyRef or a CFArray of policies.
 // The caller owns the return value.
 //
-static SecPolicyRef makeCRLPolicy()
+static SecPolicyRef makeRevocationPolicy(CFOptionFlags flags)
 {
-       CFRef<SecPolicyRef> policy;
-       MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_REVOCATION_CRL, &policy.aref()));
-       CSSM_APPLE_TP_CRL_OPTIONS options;
-       memset(&options, 0, sizeof(options));
-       options.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION;
-       options.CrlFlags = CSSM_TP_ACTION_FETCH_CRL_FROM_NET | CSSM_TP_ACTION_CRL_SUFFICIENT;
-       CSSM_DATA optData = { sizeof(options), (uint8 *)&options };
-       MacOSError::check(SecPolicySetValue(policy, &optData));
+       CFRef<SecPolicyRef> policy(SecPolicyCreateRevocation(flags));
        return policy.yield();
 }
+#endif
 
-static SecPolicyRef makeOCSPPolicy()
-{
-       CFRef<SecPolicyRef> policy;
-       MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_REVOCATION_OCSP, &policy.aref()));
-       CSSM_APPLE_TP_OCSP_OPTIONS options;
-       memset(&options, 0, sizeof(options));
-       options.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION;
-       options.Flags = CSSM_TP_ACTION_OCSP_SUFFICIENT;
-       CSSM_DATA optData = { sizeof(options), (uint8 *)&options };
-       MacOSError::check(SecPolicySetValue(policy, &optData));
-       return policy.yield();
-}
-
-CFArrayRef SecStaticCode::verificationPolicies()
+CFArrayRef SecStaticCode::createVerificationPolicies()
 {
+       if (mValidationFlags & kSecCSUseSoftwareSigningCert) {
+               CFRef<SecPolicyRef> ssRef = SecPolicyCreateAppleSoftwareSigning();
+               return makeCFArray(1, ssRef.get());
+       }
+#if TARGET_OS_OSX
        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());
-    }
+                                                                       &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 = makeRevocationPolicy(kSecRevocationNetworkAccessDisabled);
+               return makeCFArray(2, core.get(), no_revoc.get());
+       }
        else if (mValidationFlags & kSecCSEnforceRevocationChecks) {
-        // Add CRL and OCSPPolicies
-               CFRef<SecPolicyRef> crl = makeCRLPolicy();
-               CFRef<SecPolicyRef> ocsp = makeOCSPPolicy();
-               return makeCFArray(3, core.get(), crl.get(), ocsp.get());
+               // Add CRL and OCSP policies
+               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
+
 }
 
 
@@ -898,9 +1026,10 @@ void SecStaticCode::validateExecutable()
                        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;
+                       Syslog::notice("code signing internal problem: unknown exception thrown by validation");
                        throw;
                }
        }
@@ -942,18 +1071,11 @@ void SecStaticCode::validateResources(SecCSFlags flags)
                }
 
                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,
@@ -962,22 +1084,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
 
-                       // 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) {
@@ -1014,7 +1120,7 @@ void SecStaticCode::validateResources(SecCSFlags flags)
 
                        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);
                        }
 
@@ -1029,10 +1135,11 @@ void SecStaticCode::validateResources(SecCSFlags flags)
                        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;
+                       Syslog::notice("code signing internal problem: unknown exception thrown by validation");
                        throw;
                }
        }
@@ -1044,6 +1151,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);
@@ -1075,8 +1214,10 @@ bool SecStaticCode::hasWeakResourceRules(CFDictionaryRef rulesDict, uint32_t ver
 {
        // 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);
+       }
        CFRef<CFMutableArrayRef> allowed = CFArrayCreateMutableCopy(NULL, 0, defaultOmissions);
        if (allowedOmissions)
                CFArrayAppendArray(allowed, allowedOmissions, CFRangeMake(0, CFArrayGetCount(allowedOmissions)));
@@ -1109,7 +1250,7 @@ CFDictionaryRef SecStaticCode::infoDictionary()
 {
        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;
 }
@@ -1123,7 +1264,7 @@ CFDictionaryRef SecStaticCode::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
                }
@@ -1137,13 +1278,31 @@ CFDictionaryRef SecStaticCode::resourceDictionary(bool check /* = true */)
                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;
 }
+       
+       
+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;
+}
+
 
 
 //
@@ -1181,57 +1340,84 @@ CFDictionaryRef SecStaticCode::getDictionary(CodeDirectory::SpecialSlot slot, bo
        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(hashAlgorithm())))
-                                       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);
+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 platformCmd =
+                       cfNumber(info.get<CFNumberRef>(kSecCodeInfoDiskRepOSPlatform, errSecCSNotSupported), 0);
+               uint32_t sdkVersion =
+                       cfNumber(info.get<CFNumberRef>(kSecCodeInfoDiskRepOSSDKVersion, errSecCSNotSupported), 0);
+
+               if (platformCmd != LC_VERSION_MIN_IPHONEOS || 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)
@@ -1239,6 +1425,11 @@ void SecStaticCode::validateResource(CFDictionaryRef files, string path, bool is
        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))) {
                ResourceSeal seal(file);
                const ResourceSeal& rseal = seal;
@@ -1263,8 +1454,13 @@ void SecStaticCode::validateResource(CFDictionaryRef files, string path, bool is
                                        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
@@ -1280,9 +1476,31 @@ void SecStaticCode::validateResource(CFDictionaryRef files, string path, bool is
                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)
+{
+       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)
 {
        static const char* const allowedDestinations[] = {
@@ -1344,14 +1562,14 @@ void SecStaticCode::validateNestedCode(CFURLRef path, const ResourceSeal &seal,
        // 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);
-               code->staticValidate(flags & ~kSecCSRestrictToAppLike, SecRequirement::required(req));
+               code->staticValidate(flags & (~kSecCSRestrictToAppLike), SecRequirement::required(req));
 
                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) {
@@ -1397,10 +1615,7 @@ void SecStaticCode::validateOtherVersions(CFURLRef path, SecCSFlags flags, SecRe
                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;
@@ -1504,6 +1719,7 @@ const Requirement *SecStaticCode::defaultDesignatedRequirement()
                }
                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
                Requirement::Context context(this->certificates(),
@@ -1513,6 +1729,9 @@ const Requirement *SecStaticCode::defaultDesignatedRequirement()
                        this->codeDirectory()
                );
                return DRMaker(context).make();
+#else
+        MacOSError::throwMe(errSecCSUnimplemented);
+#endif
        }
 }
 
@@ -1532,9 +1751,6 @@ void SecStaticCode::validateRequirements(SecRequirementType type, SecStaticCode
                /* 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
 //
@@ -1544,34 +1760,6 @@ bool SecStaticCode::satisfiesRequirement(const Requirement *req, OSStatus failur
        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;
 }
 
@@ -1680,7 +1868,10 @@ CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
        // kSecCSRequirementInformation adds information on requirements
        //
        if (flags & kSecCSRequirementInformation)
-               try {
+
+//DR not currently supported on iOS
+#if TARGET_OS_OSX
+        try {
                        if (const Requirements *reqs = this->internalRequirements()) {
                                CFDictionaryAddValue(dict, kSecCodeInfoRequirements,
                                        CFTempString(Dumper::dump(reqs)));
@@ -1697,14 +1888,15 @@ CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
                                CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, dreqRef);
                        }
                } catch (...) { }
+#endif
 
-               try {
-                  if (CFDataRef ent = this->component(cdEntitlementSlot)) {
-                          CFDictionaryAddValue(dict, kSecCodeInfoEntitlements, ent);
-                          if (CFDictionaryRef entdict = this->entitlements())
-                                       CFDictionaryAddValue(dict, kSecCodeInfoEntitlementsDict, entdict);
-                       }
-               } catch (...) { }
+       try {
+          if (CFDataRef ent = this->component(cdEntitlementSlot)) {
+                  CFDictionaryAddValue(dict, kSecCodeInfoEntitlements, ent);
+                  if (CFDictionaryRef entdict = this->entitlements())
+                               CFDictionaryAddValue(dict, kSecCodeInfoEntitlementsDict, entdict);
+               }
+       } catch (...) { }
 
        //
        // kSecCSInternalInformation adds internal information meant to be for Apple internal
@@ -1712,14 +1904,19 @@ 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.
        //
-       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);
+        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 (...) { }
+       }
 
 
        //
@@ -1727,7 +1924,7 @@ CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
        // 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);
 
@@ -1794,7 +1991,8 @@ void SecStaticCode::staticValidate(SecCSFlags flags, const SecRequirement *req)
        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) {
@@ -1873,21 +2071,24 @@ void SecStaticCode::handleOtherArchitectures(void (^handle)(SecStaticCode* other
                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) {
-                               ctx.offset = fat->archOffset(*arch);
-                               if (ctx.offset > SIZE_MAX)
-                                       MacOSError::throwMe(errSecCSInternalError);
-                               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);
-                                       } 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);
                                }
                        }
                }