]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_codesigning/lib/StaticCode.cpp
Security-55471.14.tar.gz
[apple/security.git] / libsecurity_codesigning / lib / StaticCode.cpp
index 9a12445831cd51cc03761a25825725b60209f450..a26a1fee33a0456ad3426013d744b12c3c17d63a 100644 (file)
@@ -29,9 +29,9 @@
 #include "reqmaker.h"
 #include "drmaker.h"
 #include "reqdumper.h"
 #include "reqmaker.h"
 #include "drmaker.h"
 #include "reqdumper.h"
+#include "reqparser.h"
 #include "sigblob.h"
 #include "resources.h"
 #include "sigblob.h"
 #include "resources.h"
-#include "renum.h"
 #include "detachedrep.h"
 #include "csdatabase.h"
 #include "csutilities.h"
 #include "detachedrep.h"
 #include "csdatabase.h"
 #include "csutilities.h"
@@ -55,13 +55,29 @@ namespace CodeSigning {
 using namespace UnixPlusPlus;
 
 
 using namespace UnixPlusPlus;
 
 
+//
+// Map a component slot number to a suitable error code for a failure
+//
+static inline OSStatus errorForSlot(CodeDirectory::SpecialSlot slot)
+{
+       switch (slot) {
+       case cdInfoSlot:
+               return errSecCSInfoPlistFailed;
+       case cdResourceDirSlot:
+               return errSecCSResourceDirectoryFailed;
+       default:
+               return errSecCSSignatureFailed;
+       }
+}
+
+
 //
 // Construct a SecStaticCode object given a disk representation object
 //
 SecStaticCode::SecStaticCode(DiskRep *rep)
        : mRep(rep),
          mValidated(false), mExecutableValidated(false), mResourcesValidated(false), mResourcesValidContext(NULL),
 //
 // Construct a SecStaticCode object given a disk representation object
 //
 SecStaticCode::SecStaticCode(DiskRep *rep)
        : mRep(rep),
          mValidated(false), mExecutableValidated(false), mResourcesValidated(false), mResourcesValidContext(NULL),
-         mDesignatedReq(NULL), mGotResourceBase(false), mEvalDetails(NULL)
+         mDesignatedReq(NULL), mGotResourceBase(false), mMonitor(NULL), mEvalDetails(NULL)
 {
        CODESIGN_STATIC_CREATE(this, rep);
        checkForSystemSignature();
 {
        CODESIGN_STATIC_CREATE(this, rep);
        checkForSystemSignature();
@@ -105,15 +121,29 @@ CFHashCode SecStaticCode::hash()
 }
 
 
 }
 
 
+//
+// Invoke a stage monitor if registered
+//
+CFTypeRef SecStaticCode::reportEvent(CFStringRef stage, CFDictionaryRef info)
+{
+       if (mMonitor)
+               return mMonitor(this->handle(false), stage, info);
+       else
+               return NULL;
+}
+
+
 //
 // Attach a detached signature.
 //
 void SecStaticCode::detachedSignature(CFDataRef sigData)
 {
        if (sigData) {
 //
 // Attach a detached signature.
 //
 void SecStaticCode::detachedSignature(CFDataRef sigData)
 {
        if (sigData) {
+               mDetachedSig = sigData;
                mRep = new DetachedRep(sigData, mRep->base(), "explicit detached");
                CODESIGN_STATIC_ATTACH_EXPLICIT(this, mRep);
        } else {
                mRep = new DetachedRep(sigData, mRep->base(), "explicit detached");
                CODESIGN_STATIC_ATTACH_EXPLICIT(this, mRep);
        } else {
+               mDetachedSig = NULL;
                mRep = mRep->base();
                CODESIGN_STATIC_ATTACH_EXPLICIT(this, NULL);
        }
                mRep = mRep->base();
                CODESIGN_STATIC_ATTACH_EXPLICIT(this, NULL);
        }
@@ -127,14 +157,17 @@ void SecStaticCode::detachedSignature(CFDataRef sigData)
 //
 void SecStaticCode::checkForSystemSignature()
 {
 //
 void SecStaticCode::checkForSystemSignature()
 {
-       if (!this->isSigned())
-               try {
-                       if (RefPointer<DiskRep> dsig = signatureDatabase().findCode(mRep)) {
-                               CODESIGN_STATIC_ATTACH_SYSTEM(this, dsig);
-                               mRep = dsig;
+       if (!this->isSigned()) {
+               SignatureDatabase db;
+               if (db.isOpen())
+                       try {
+                               if (RefPointer<DiskRep> dsig = db.findCode(mRep)) {
+                                       CODESIGN_STATIC_ATTACH_SYSTEM(this, dsig);
+                                       mRep = dsig;
+                               }
+                       } catch (...) {
                        }
                        }
-               } catch (...) {
-               }
+       }
 }
 
 
 }
 
 
@@ -189,8 +222,7 @@ void SecStaticCode::resetValidity()
 {
        CODESIGN_EVAL_STATIC_RESET(this);
        mValidated = false;
 {
        CODESIGN_EVAL_STATIC_RESET(this);
        mValidated = false;
-       mExecutableValidated = false;
-       mResourcesValidated = false;
+       mExecutableValidated = mResourcesValidated = false;
        if (mResourcesValidContext) {
                delete mResourcesValidContext;
                mResourcesValidContext = NULL;
        if (mResourcesValidContext) {
                delete mResourcesValidContext;
                mResourcesValidContext = NULL;
@@ -230,12 +262,12 @@ CFDataRef SecStaticCode::component(CodeDirectory::SpecialSlot slot, OSStatus fai
                        if (validated()) // if the directory has been validated...
                                if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), // ... and it's no good
                                                CFDataGetLength(data), -slot))
                        if (validated()) // if the directory has been validated...
                                if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), // ... and it's no good
                                                CFDataGetLength(data), -slot))
-                                       MacOSError::throwMe(fail); // ... then bail
+                                       MacOSError::throwMe(errorForSlot(slot)); // ... then bail
                        cache = data;   // it's okay, cache it
                } else {        // absent, mark so
                        if (validated())        // if directory has been validated...
                                if (codeDirectory()->slotIsPresent(-slot)) // ... and the slot is NOT missing
                        cache = data;   // it's okay, cache it
                } else {        // absent, mark so
                        if (validated())        // if directory has been validated...
                                if (codeDirectory()->slotIsPresent(-slot)) // ... and the slot is NOT missing
-                                       MacOSError::throwMe(fail);      // was supposed to be there
+                                       MacOSError::throwMe(errorForSlot(slot));        // was supposed to be there
                        cache = CFDataRef(kCFNull);             // white lie
                }
        }
                        cache = CFDataRef(kCFNull);             // white lie
                }
        }
@@ -311,12 +343,11 @@ void SecStaticCode::validateDirectory()
                        // perform validation (or die trying)
                        CODESIGN_EVAL_STATIC_DIRECTORY(this);
                        mValidationExpired = verifySignature();
                        // perform validation (or die trying)
                        CODESIGN_EVAL_STATIC_DIRECTORY(this);
                        mValidationExpired = verifySignature();
-                       component(cdInfoSlot, errSecCSInfoPlistFailed); // force load of Info Dictionary (if any)
                        for (CodeDirectory::SpecialSlot slot = codeDirectory()->maxSpecialSlot(); slot >= 1; --slot)
                                if (mCache[slot])       // if we already loaded that resource...
                        for (CodeDirectory::SpecialSlot slot = codeDirectory()->maxSpecialSlot(); slot >= 1; --slot)
                                if (mCache[slot])       // if we already loaded that resource...
-                                       validateComponent(slot); // ... then check it now
+                                       validateComponent(slot, errorForSlot(slot)); // ... then check it now
                        mValidated = true;                      // we've done the deed...
                        mValidated = true;                      // we've done the deed...
-                       mValidationResult = noErr;      // ... and it was good
+                       mValidationResult = errSecSuccess;      // ... and it was good
                } catch (const CommonError &err) {
                        mValidated = true;
                        mValidationResult = err.osStatus();
                } catch (const CommonError &err) {
                        mValidated = true;
                        mValidationResult = err.osStatus();
@@ -328,7 +359,7 @@ void SecStaticCode::validateDirectory()
                        throw;
                }
        assert(validated());
                        throw;
                }
        assert(validated());
-       if (mValidationResult == noErr) {
+       if (mValidationResult == errSecSuccess) {
                if (mValidationExpired)
                        if ((apiFlags() & kSecCSConsiderExpiration)
                                        || (codeDirectory()->flags & kSecCodeSignatureForceExpiration))
                if (mValidationExpired)
                        if ((apiFlags() & kSecCSConsiderExpiration)
                                        || (codeDirectory()->flags & kSecCodeSignatureForceExpiration))
@@ -383,6 +414,7 @@ CFAbsoluteTime SecStaticCode::signingTimestamp()
 // or throws if it is not. As a side effect, a successful return sets up the
 // cached certificate chain for future use.
 // Returns true if the signature is expired (the X.509 sense), false if it's not.
 // or throws if it is not. As a side effect, a successful return sets up the
 // cached certificate chain for future use.
 // Returns true if the signature is expired (the X.509 sense), false if it's not.
+// Expiration is fatal (throws) if a secure timestamp is included, but not otherwise.
 //
 bool SecStaticCode::verifySignature()
 {
 //
 bool SecStaticCode::verifySignature()
 {
@@ -413,7 +445,7 @@ bool SecStaticCode::verifySignature()
        // 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)) {
-       case noErr:
+       case errSecSuccess:
        case errSecSigningTimeMissing:
                break;
        default:
        case errSecSigningTimeMissing:
                break;
        default:
@@ -423,7 +455,7 @@ bool SecStaticCode::verifySignature()
        // certified signing time (as specified by a TSA; optional)
        mSigningTimestamp = 0;
        switch (OSStatus rc = CMSDecoderCopySignerTimestamp(cms, 0, &mSigningTimestamp)) {
        // certified signing time (as specified by a TSA; optional)
        mSigningTimestamp = 0;
        switch (OSStatus rc = CMSDecoderCopySignerTimestamp(cms, 0, &mSigningTimestamp)) {
-       case noErr:
+       case errSecSuccess:
        case errSecTimestampMissing:
                break;
        default:
        case errSecTimestampMissing:
                break;
        default:
@@ -446,7 +478,7 @@ bool SecStaticCode::verifySignature()
                SecTrustResultType trustResult;
                MacOSError::check(SecTrustEvaluate(mTrust, &trustResult));
                MacOSError::check(SecTrustGetResult(mTrust, &trustResult, &mCertChain.aref(), &mEvalDetails));
                SecTrustResultType trustResult;
                MacOSError::check(SecTrustEvaluate(mTrust, &trustResult));
                MacOSError::check(SecTrustGetResult(mTrust, &trustResult, &mCertChain.aref(), &mEvalDetails));
-               CODESIGN_EVAL_STATIC_SIGNATURE_RESULT(this, trustResult, mCertChain ? CFArrayGetCount(mCertChain) : 0);
+               CODESIGN_EVAL_STATIC_SIGNATURE_RESULT(this, trustResult, mCertChain ? (int)CFArrayGetCount(mCertChain) : 0);
                switch (trustResult) {
                case kSecTrustResultProceed:
                case kSecTrustResultUnspecified:
                switch (trustResult) {
                case kSecTrustResultProceed:
                case kSecTrustResultUnspecified:
@@ -472,6 +504,21 @@ bool SecStaticCode::verifySignature()
                                MacOSError::throwMe(result);
                        }
                }
                                MacOSError::throwMe(result);
                        }
                }
+               
+               if (mSigningTimestamp) {
+                       CFIndex rootix = CFArrayGetCount(mCertChain);
+                       if (SecCertificateRef mainRoot = SecCertificateRef(CFArrayGetValueAtIndex(mCertChain, rootix-1)))
+                               if (isAppleCA(mainRoot)) {
+                                       // impose policy: if the signature itself draws to Apple, then so must the timestamp signature
+                                       CFRef<CFArrayRef> tsCerts;
+                                       MacOSError::check(CMSDecoderCopySignerTimestampCertificates(cms, 0, &tsCerts.aref()));
+                                       CFIndex tsn = CFArrayGetCount(tsCerts);
+                                       bool good = tsn > 0 && isAppleCA(SecCertificateRef(CFArrayGetValueAtIndex(tsCerts, tsn-1)));
+                                       if (!good)
+                                               MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED);
+                               }
+               }
+               
                return actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED;
        }
 }
                return actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED;
        }
 }
@@ -565,16 +612,16 @@ void SecStaticCode::validateExecutable()
                                fd.seek(fat->archOffset());
                        size_t pageSize = cd->pageSize ? (1 << cd->pageSize) : 0;
                        size_t remaining = cd->codeLimit;
                                fd.seek(fat->archOffset());
                        size_t pageSize = cd->pageSize ? (1 << cd->pageSize) : 0;
                        size_t remaining = cd->codeLimit;
-                       for (size_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 size = min(remaining, pageSize);
                                if (!cd->validateSlot(fd, size, slot)) {
-                                       CODESIGN_EVAL_STATIC_EXECUTABLE_FAIL(this, slot);
+                                       CODESIGN_EVAL_STATIC_EXECUTABLE_FAIL(this, (int)slot);
                                        MacOSError::throwMe(errSecCSSignatureFailed);
                                }
                                remaining -= size;
                        }
                        mExecutableValidated = true;
                                        MacOSError::throwMe(errSecCSSignatureFailed);
                                }
                                remaining -= size;
                        }
                        mExecutableValidated = true;
-                       mExecutableValidResult = noErr;
+                       mExecutableValidResult = errSecSuccess;
                } catch (const CommonError &err) {
                        mExecutableValidated = true;
                        mExecutableValidResult = err.osStatus();
                } catch (const CommonError &err) {
                        mExecutableValidated = true;
                        mExecutableValidResult = err.osStatus();
@@ -587,21 +634,27 @@ void SecStaticCode::validateExecutable()
                }
        }
        assert(validatedExecutable());
                }
        }
        assert(validatedExecutable());
-       if (mExecutableValidResult != noErr)
+       if (mExecutableValidResult != errSecSuccess)
                MacOSError::throwMe(mExecutableValidResult);
 }
 
 
 //
                MacOSError::throwMe(mExecutableValidResult);
 }
 
 
 //
-// Perform static validation of sealed resources.
+// Perform static validation of sealed resources and nested code.
 //
 // This performs a whole-code static resource scan and effectively
 // computes a concordance between what's on disk and what's in the ResourceDirectory.
 // Any unsanctioned difference causes an error.
 //
 //
 // This performs a whole-code static resource scan and effectively
 // computes a concordance between what's on disk and what's in the ResourceDirectory.
 // Any unsanctioned difference causes an error.
 //
-void SecStaticCode::validateResources()
+void SecStaticCode::validateResources(SecCSFlags flags)
 {
 {
-       if (!validatedResources()) {
+       // do we have a superset of this requested validation cached?
+       bool doit = true;
+       if (mResourcesValidated) {      // have cached outcome
+               if (!(flags & kSecCSCheckNestedCode) || mResourcesDeep) // was deep or need no deep scan
+                       doit = false;
+       }
+       if (doit) {
                try {
                        // sanity first
                        CFDictionaryRef sealedResources = resourceDictionary();
                try {
                        // sanity first
                        CFDictionaryRef sealedResources = resourceDictionary();
@@ -617,25 +670,32 @@ void SecStaticCode::validateResources()
                                        return;                                 // no resources, not sealed - fine (no work)
                
                        // found resources, and they are sealed
                                        return;                                 // no resources, not sealed - fine (no work)
                
                        // found resources, and they are sealed
-                       CFDictionaryRef rules = cfget<CFDictionaryRef>(sealedResources, "rules");
-                       CFDictionaryRef files = cfget<CFDictionaryRef>(sealedResources, "files");
                        DTRACK(CODESIGN_EVAL_STATIC_RESOURCES, this,
                        DTRACK(CODESIGN_EVAL_STATIC_RESOURCES, this,
-                               (char*)this->mainExecutablePath().c_str(), int(CFDictionaryGetCount(files)));
-               
-                       // make a shallow copy of the ResourceDirectory so we can "check off" what we find
-                       CFRef<CFMutableDictionaryRef> resourceMap = makeCFMutableDictionary(files);
+                               (char*)this->mainExecutablePath().c_str(), 0);
                
                        // 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
-                       ResourceBuilder resources(cfString(this->resourceBase()), rules, codeDirectory()->hashType);
-                       mRep->adjustResources(resources);
-                       string path;
-                       ResourceBuilder::Rule *rule;
-               
-                       while (resources.next(path, rule)) {
-                               validateResource(path, *mResourcesValidContext);
-                               CFDictionaryRemoveValue(resourceMap, CFTempString(path));
+                       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);
+                       __block CFRef<CFMutableDictionaryRef> resourceMap = makeCFMutableDictionary(files);
+                       ResourceBuilder resources(cfString(this->resourceBase()), rules, codeDirectory()->hashType);
+                       diskRep()->adjustResources(resources);
+                       resources.scan(^(FTSENT *ent, uint32_t ruleFlags, const char *relpath, ResourceBuilder::Rule *rule) {
+                               validateResource(files, relpath, *mResourcesValidContext, flags, version);
+                               CFDictionaryRemoveValue(resourceMap, CFTempString(relpath));
+                       });
                        
                        if (CFDictionaryGetCount(resourceMap) > 0) {
                                secdebug("staticCode", "%p sealed resource(s) not found in code", this);
                        
                        if (CFDictionaryGetCount(resourceMap) > 0) {
                                secdebug("staticCode", "%p sealed resource(s) not found in code", this);
@@ -644,16 +704,18 @@ void SecStaticCode::validateResources()
                        
                        // now check for any errors found in the reporting context
                        mResourcesValidated = true;
                        
                        // now check for any errors found in the reporting context
                        mResourcesValidated = true;
-                       if (mResourcesValidContext->osStatus() != noErr)
+                       mResourcesDeep = flags & kSecCSCheckNestedCode;
+                       if (mResourcesValidContext->osStatus() != errSecSuccess)
                                mResourcesValidContext->throwMe();
                                mResourcesValidContext->throwMe();
-
                } catch (const CommonError &err) {
                        mResourcesValidated = true;
                } catch (const CommonError &err) {
                        mResourcesValidated = true;
+                       mResourcesDeep = flags & kSecCSCheckNestedCode;
                        mResourcesValidResult = err.osStatus();
                        throw;
                } catch (...) {
                        secdebug("staticCode", "%p executable validation threw non-common exception", this);
                        mResourcesValidated = true;
                        mResourcesValidResult = err.osStatus();
                        throw;
                } catch (...) {
                        secdebug("staticCode", "%p executable validation threw non-common exception", this);
                        mResourcesValidated = true;
+                       mResourcesDeep = flags & kSecCSCheckNestedCode;
                        mResourcesValidResult = errSecCSInternalError;
                        throw;
                }
                        mResourcesValidResult = errSecCSInternalError;
                        throw;
                }
@@ -661,7 +723,7 @@ void SecStaticCode::validateResources()
        assert(validatedResources());
        if (mResourcesValidResult)
                MacOSError::throwMe(mResourcesValidResult);
        assert(validatedResources());
        if (mResourcesValidResult)
                MacOSError::throwMe(mResourcesValidResult);
-       if (mResourcesValidContext->osStatus() != noErr)
+       if (mResourcesValidContext->osStatus() != errSecSuccess)
                mResourcesValidContext->throwMe();
 }
 
                mResourcesValidContext->throwMe();
 }
 
@@ -710,11 +772,11 @@ CFDictionaryRef SecStaticCode::entitlements()
        return mEntitlements;
 }
 
        return mEntitlements;
 }
 
-CFDictionaryRef SecStaticCode::resourceDictionary()
+CFDictionaryRef SecStaticCode::resourceDictionary(bool check /* = true */)
 {
        if (mResourceDict)      // cached
                return mResourceDict;
 {
        if (mResourceDict)      // cached
                return mResourceDict;
-       if (CFRef<CFDictionaryRef> dict = getDictionary(cdResourceDirSlot, errSecCSSignatureFailed))
+       if (CFRef<CFDictionaryRef> dict = getDictionary(cdResourceDirSlot, check))
                if (cfscan(dict, "{rules=%Dn,files=%Dn}")) {
                        secdebug("staticCode", "%p loaded ResourceDict %p",
                                this, mResourceDict.get());
                if (cfscan(dict, "{rules=%Dn,files=%Dn}")) {
                        secdebug("staticCode", "%p loaded ResourceDict %p",
                                this, mResourceDict.get());
@@ -746,11 +808,12 @@ CFURLRef SecStaticCode::resourceBase()
 // This will force load and validation, which means that it will perform basic
 // validation if it hasn't been done yet.
 //
 // This will force load and validation, which means that it will perform basic
 // validation if it hasn't been done yet.
 //
-CFDictionaryRef SecStaticCode::getDictionary(CodeDirectory::SpecialSlot slot, OSStatus fail /* = errSecCSSignatureFailed */)
+CFDictionaryRef SecStaticCode::getDictionary(CodeDirectory::SpecialSlot slot, bool check /* = true */)
 {
 {
-       validateDirectory();
-       if (CFDataRef infoData = component(slot, fail)) {
-               validateComponent(slot, fail);
+       if (check)
+               validateDirectory();
+       if (CFDataRef infoData = component(slot)) {
+               validateComponent(slot);
                if (CFDictionaryRef dict = makeCFDictionaryFrom(infoData))
                        return dict;
                else
                if (CFDictionaryRef dict = makeCFDictionaryFrom(infoData))
                        return dict;
                else
@@ -783,6 +846,8 @@ CFDataRef SecStaticCode::resource(string path, ValidationContext &ctx)
                        ResourceSeal seal = file;
                        if (!resourceBase())    // no resources in DiskRep
                                MacOSError::throwMe(errSecCSResourcesNotFound);
                        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());
                        CFRef<CFURLRef> fullpath = makeCFURL(path, false, resourceBase());
                        if (CFRef<CFDataRef> data = cfLoadFile(fullpath)) {
                                MakeHash<CodeDirectory> hasher(this->codeDirectory());
@@ -811,15 +876,25 @@ CFDataRef SecStaticCode::resource(string path)
 }
 
 
 }
 
 
-void SecStaticCode::validateResource(string path, ValidationContext &ctx)
+void SecStaticCode::validateResource(CFDictionaryRef files, string path, ValidationContext &ctx, SecCSFlags flags, uint32_t version)
 {
 {
-       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);
-                       CFRef<CFURLRef> fullpath = makeCFURL(path, false, resourceBase());
-                       AutoFileDesc fd(cfString(fullpath), O_RDONLY, FileDesc::modeMissingOk); // open optional filee
+       if (!resourceBase())    // no resources in DiskRep
+               MacOSError::throwMe(errSecCSResourcesNotFound);
+       CFRef<CFURLRef> fullpath = makeCFURL(path, false, resourceBase());
+       if (CFTypeRef file = CFDictionaryGetValue(files, CFTempString(path))) {
+               ResourceSeal seal = file;
+               if (seal.nested()) {
+                       validateNestedCode(fullpath, seal, flags);
+               } else if (seal.link()) {
+                       char target[PATH_MAX];
+                       ssize_t len = ::readlink(cfString(fullpath).c_str(), target, sizeof(target)-1);
+                       if (len < 0)
+                               UnixError::check(-1);
+                       target[len] = '\0';
+                       if (cfString(seal.link()) != target)
+                               ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath);
+               } else if (seal.hash()) {       // genuine file
+                       AutoFileDesc fd(cfString(fullpath), O_RDONLY, FileDesc::modeMissingOk); // open optional file
                        if (fd) {
                                MakeHash<CodeDirectory> hasher(this->codeDirectory());
                                hashFileData(fd, hasher.get());
                        if (fd) {
                                MakeHash<CodeDirectory> hasher(this->codeDirectory());
                                hashFileData(fd, hasher.get());
@@ -834,9 +909,44 @@ void SecStaticCode::validateResource(string path, ValidationContext &ctx)
                                        return;                 // validly missing
                        }
                } else
                                        return;                 // validly missing
                        }
                } else
-                       ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAdded, CFTempURL(path, false, resourceBase()));
-       } else
-               MacOSError::throwMe(errSecCSResourcesNotSealed);
+                       ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // changed type
+               return;
+       }
+       if (version == 1) {             // version 1 ignores symlinks altogether
+               char target[PATH_MAX];
+               if (::readlink(cfString(fullpath).c_str(), target, sizeof(target)) > 0)
+                       return;
+       }
+       ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAdded, CFTempURL(path, false, resourceBase()));
+}
+
+void SecStaticCode::validateNestedCode(CFURLRef path, const ResourceSeal &seal, SecCSFlags flags)
+{
+       CFRef<SecRequirementRef> req;
+       if (SecRequirementCreateWithString(seal.requirement(), kSecCSDefaultFlags, &req.aref()))
+               MacOSError::throwMe(errSecCSResourcesInvalid);
+       
+       // recursively verify this nested code
+       try {
+               if (!(flags & kSecCSCheckNestedCode))
+                       flags |= kSecCSBasicValidateOnly;
+               SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(cfString(path)));
+               code->setMonitor(this->monitor());
+               code->staticValidate(flags, SecRequirement::required(req));
+       } catch (CSError &err) {
+               if (err.error == errSecCSReqFailed) {
+                       mResourcesValidContext->reportProblem(errSecCSBadNestedCode, kSecCFErrorResourceAltered, path);
+                       return;
+               }
+               err.augment(kSecCFErrorPath, path);
+               throw;
+       } catch (const MacOSError &err) {
+               if (err.error == errSecCSReqFailed) {
+                       mResourcesValidContext->reportProblem(errSecCSBadNestedCode, kSecCFErrorResourceAltered, path);
+                       return;
+               }
+               CSError::throwMe(err.error, kSecCFErrorPath, path);
+       }
 }
 
 
 }
 
 
@@ -859,9 +969,12 @@ bool SecStaticCode::flag(uint32_t tested)
 //
 const Requirements *SecStaticCode::internalRequirements()
 {
 //
 const Requirements *SecStaticCode::internalRequirements()
 {
-       if (CFDataRef req = component(cdRequirementsSlot))
-               return (const Requirements *)CFDataGetBytePtr(req);
-       else
+       if (CFDataRef reqData = component(cdRequirementsSlot)) {
+               const Requirements *req = (const Requirements *)CFDataGetBytePtr(reqData);
+               if (!req->validateBlob())
+                       MacOSError::throwMe(errSecCSReqInvalid);
+               return req;
+       } else
                return NULL;
 }
 
                return NULL;
 }
 
@@ -903,13 +1016,19 @@ const Requirement *SecStaticCode::designatedRequirement()
 const Requirement *SecStaticCode::defaultDesignatedRequirement()
 {
        if (flag(kSecCodeSignatureAdhoc)) {
 const Requirement *SecStaticCode::defaultDesignatedRequirement()
 {
        if (flag(kSecCodeSignatureAdhoc)) {
-               // adhoc signature: return a plain cdhash requirement
-               Requirement::Maker maker;
-               SHA1 hash;
-               hash(codeDirectory(), codeDirectory()->length());
-               SHA1::Digest digest;
-               hash.finish(digest);
-               maker.cdhash(digest);
+               // adhoc signature: return a cdhash requirement for all architectures
+               __block Requirement::Maker maker;
+               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);
+                       }
+               });
                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
@@ -929,7 +1048,7 @@ const Requirement *SecStaticCode::defaultDesignatedRequirement()
 // Validate a SecStaticCode against the internal requirement of a particular type.
 //
 void SecStaticCode::validateRequirements(SecRequirementType type, SecStaticCode *target,
 // Validate a SecStaticCode against the internal requirement of a particular type.
 //
 void SecStaticCode::validateRequirements(SecRequirementType type, SecStaticCode *target,
-       OSStatus nullError /* = noErr */)
+       OSStatus nullError /* = errSecSuccess */)
 {
        DTRACK(CODESIGN_EVAL_STATIC_INTREQ, this, type, target, nullError);
        if (const Requirement *req = internalRequirement(type))
 {
        DTRACK(CODESIGN_EVAL_STATIC_INTREQ, this, type, target, nullError);
        if (const Requirement *req = internalRequirement(type))
@@ -1012,6 +1131,7 @@ CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
        // Add the generic attributes that we always include
        //
        CFDictionaryAddValue(dict, kSecCodeInfoIdentifier, CFTempString(this->identifier()));
        // Add the generic attributes that we always include
        //
        CFDictionaryAddValue(dict, kSecCodeInfoIdentifier, CFTempString(this->identifier()));
+       CFDictionaryAddValue(dict, kSecCodeInfoFlags, CFTempNumber(this->codeDirectory(false)->flags.get()));
        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());
@@ -1028,47 +1148,51 @@ CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
        //
        // kSecCSSigningInformation adds information about signing certificates and chains
        //
        //
        // kSecCSSigningInformation adds information about signing certificates and chains
        //
-       if (flags & kSecCSSigningInformation) {
-               if (CFArrayRef certs = this->certificates())
-               CFDictionaryAddValue(dict, kSecCodeInfoCertificates, certs);
-               if (CFDataRef sig = this->signature())
-                       CFDictionaryAddValue(dict, kSecCodeInfoCMS, sig);
-               if (mTrust)
-                       CFDictionaryAddValue(dict, kSecCodeInfoTrust, mTrust);
-               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 (flags & kSecCSSigningInformation)
+               try {
+                       if (CFArrayRef certs = this->certificates())
+                               CFDictionaryAddValue(dict, kSecCodeInfoCertificates, certs);
+                       if (CFDataRef sig = this->signature())
+                               CFDictionaryAddValue(dict, kSecCodeInfoCMS, sig);
+                       if (mTrust)
+                               CFDictionaryAddValue(dict, kSecCodeInfoTrust, mTrust);
+                       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);
+               } catch (...) { }
        
        //
        // kSecCSRequirementInformation adds information on requirements
        //
        
        //
        // kSecCSRequirementInformation adds information on requirements
        //
-       if (flags & kSecCSRequirementInformation) {
-               if (const Requirements *reqs = this->internalRequirements()) {
-                       CFDictionaryAddValue(dict, kSecCodeInfoRequirements,
-                               CFTempString(Dumper::dump(reqs)));
-                       CFDictionaryAddValue(dict, kSecCodeInfoRequirementData, CFTempData(*reqs));
-               }
-               
-               const Requirement *dreq = this->designatedRequirement();
-               CFRef<SecRequirementRef> dreqRef = (new SecRequirement(dreq))->handle();
-               CFDictionaryAddValue(dict, kSecCodeInfoDesignatedRequirement, dreqRef);
-               if (this->internalRequirement(kSecDesignatedRequirementType)) { // explicit
-                       CFRef<SecRequirementRef> ddreqRef = (new SecRequirement(this->defaultDesignatedRequirement(), true))->handle();
-                       CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, ddreqRef);
-               } else {        // implicit
-                       CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, dreqRef);
-               }
+       if (flags & kSecCSRequirementInformation)
+               try {
+                       if (const Requirements *reqs = this->internalRequirements()) {
+                               CFDictionaryAddValue(dict, kSecCodeInfoRequirements,
+                                       CFTempString(Dumper::dump(reqs)));
+                               CFDictionaryAddValue(dict, kSecCodeInfoRequirementData, CFTempData(*reqs));
+                       }
+                       
+                       const Requirement *dreq = this->designatedRequirement();
+                       CFRef<SecRequirementRef> dreqRef = (new SecRequirement(dreq))->handle();
+                       CFDictionaryAddValue(dict, kSecCodeInfoDesignatedRequirement, dreqRef);
+                       if (this->internalRequirement(kSecDesignatedRequirementType)) { // explicit
+                               CFRef<SecRequirementRef> ddreqRef = (new SecRequirement(this->defaultDesignatedRequirement(), true))->handle();
+                               CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, ddreqRef);
+                       } else {        // implicit
+                               CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, dreqRef);
+                       }
+               } catch (...) { }
                
                
-          if (CFDataRef ent = this->component(cdEntitlementSlot)) {
-                  CFDictionaryAddValue(dict, kSecCodeInfoEntitlements, ent);
-                  if (CFDictionaryRef entdict = this->entitlements())
-                               CFDictionaryAddValue(dict, kSecCodeInfoEntitlementsDict, entdict);
-               }
-       }
+               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
        
        //
        // kSecCSInternalInformation adds internal information meant to be for Apple internal
@@ -1076,13 +1200,14 @@ 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 (mDir)
-                       CFDictionaryAddValue(dict, kSecCodeInfoCodeDirectory, mDir);
-               CFDictionaryAddValue(dict, kSecCodeInfoCodeOffset, CFTempNumber(mRep->signingBase()));
-               if (CFDictionaryRef resources = resourceDictionary())
-                       CFDictionaryAddValue(dict, kSecCodeInfoResourceDirectory, resources);
-       }
+       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);
+               } catch (...) { }
        
        
        //
        
        
        //
@@ -1112,7 +1237,7 @@ void SecStaticCode::ValidationContext::reportProblem(OSStatus rc, CFStringRef ty
 
 void SecStaticCode::CollectingContext::reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value)
 {
 
 void SecStaticCode::CollectingContext::reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value)
 {
-       if (mStatus == noErr)
+       if (mStatus == errSecSuccess)
                mStatus = rc;                   // record first failure for eventual error return
        if (type) {
                if (!mCollection)
                mStatus = rc;                   // record first failure for eventual error return
        if (type) {
                if (!mCollection)
@@ -1131,59 +1256,97 @@ void SecStaticCode::CollectingContext::reportProblem(OSStatus rc, CFStringRef ty
 
 void SecStaticCode::CollectingContext::throwMe()
 {
 
 void SecStaticCode::CollectingContext::throwMe()
 {
-       assert(mStatus != noErr);
+       assert(mStatus != errSecSuccess);
        throw CSError(mStatus, mCollection.retain());
 }
 
 
 //
        throw CSError(mStatus, mCollection.retain());
 }
 
 
 //
-// SecStaticCode::AllArchitectures produces SecStaticCode objects separately
-// for each architecture represented by a base object.
+// Master validation driver.
+// This is the static validation (only) driver for the API.
 //
 //
-// Performance note: This is a simple, straight-forward implementation that
-// does not heroically try to share resources between the code objects produced.
-// In practice, this means we'll re-open files and re-read resource files.
-// In exchange, we enter all the code paths in the normal way, and do not have
-// special sharing paths to worry about.
-// If a performance tool brings you here because you have *proof* of a performance
-// problem, consider digging up MachO and Universal (for sharing file descriptors),
-// and SecStaticCode (for sharing resource iterators). That ought to cover most of
-// the big chunks. If you're just offended by the simplicity of this implementation,
-// go play somewhere else.
+// SecStaticCode exposes an à la carte menu of topical validators applying
+// to a given object. The static validation API pulls the 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
+// 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.)
 //
 //
-SecStaticCode::AllArchitectures::AllArchitectures(SecStaticCode *code)
-       : mBase(code)
+void SecStaticCode::staticValidate(SecCSFlags flags, const SecRequirement *req)
 {
 {
-       if (Universal *fat = code->diskRep()->mainExecutableImage()) {
-               fat->architectures(mArchitectures);
-               mCurrent = mArchitectures.begin();
-               mState = fatBinary;
-       } else {
-               mState = firstNonFat;
+       // core components: once per architecture (if any)
+       this->staticValidateCore(flags, req);
+       if (flags & kSecCSCheckAllArchitectures)
+               handleOtherArchitectures(^(SecStaticCode* subcode) {
+                       subcode->detachedSignature(this->mDetachedSig); // carry over explicit (but not implicit) architecture
+                       subcode->staticValidateCore(flags, req);
+               });
+       
+       // resources: once for all architectures
+       if (!(flags & kSecCSDoNotValidateResources))
+               this->validateResources(flags);
+
+       // allow monitor intervention
+       if (CFRef<CFTypeRef> veto = reportEvent(CFSTR("validated"), NULL)) {
+               if (CFGetTypeID(veto) == CFNumberGetTypeID())
+                       MacOSError::throwMe(cfNumber<OSStatus>(veto.as<CFNumberRef>()));
+               else
+                       MacOSError::throwMe(errSecCSBadCallbackValue);
        }
 }
 
        }
 }
 
-SecStaticCode *SecStaticCode::AllArchitectures::operator () ()
+void SecStaticCode::staticValidateCore(SecCSFlags flags, const SecRequirement *req)
 {
 {
-       switch (mState) {
-       case firstNonFat:
-               mState = atEnd;
-               return mBase;
-       case fatBinary:
-               {
-                       if (mCurrent == mArchitectures.end())
-                               return NULL;
-                       Architecture arch = *mCurrent++;
-                       if (arch == mBase->diskRep()->mainExecutableImage()->bestNativeArch()) {
-                               return mBase;
-                       } else {
-                               DiskRep::Context ctx;
-                               ctx.arch = arch;
-                               return new SecStaticCode(DiskRep::bestGuess(mBase->mainExecutablePath(), &ctx));
+       try {
+               this->validateNonResourceComponents();  // also validates the CodeDirectory
+               if (!(flags & kSecCSDoNotValidateExecutable))
+                       this->validateExecutable();
+               if (req)
+                       this->validateRequirement(req->requirement(), errSecCSReqFailed);
+    } catch (CSError &err) {
+        if (Universal *fat = this->diskRep()->mainExecutableImage())    // Mach-O
+            if (MachO *mach = fat->architecture()) {
+                err.augment(kSecCFErrorArchitecture, CFTempString(mach->architecture().displayName()));
+                delete mach;
+            }
+        throw;
+    } catch (const MacOSError &err) {
+        // add architecture information if we can get it
+        if (Universal *fat = this->diskRep()->mainExecutableImage())
+            if (MachO *mach = fat->architecture()) {
+                CFTempString arch(mach->architecture().displayName());
+                delete mach;
+                CSError::throwMe(err.error, kSecCFErrorArchitecture, arch);
+            }
+        throw;
+    }
+}
+
+
+//
+// A helper that generates SecStaticCode objects for all but the primary architecture
+// of a fat binary and calls a block on them.
+// If there's only one architecture (or this is an architecture-agnostic code),
+// nothing happens quickly.
+//
+void SecStaticCode::handleOtherArchitectures(void (^handle)(SecStaticCode* other))
+{
+       if (Universal *fat = this->diskRep()->mainExecutableImage()) {
+               Universal::Architectures architectures;
+               fat->architectures(architectures);
+               if (architectures.size() > 1) {
+                       DiskRep::Context ctx;
+                       size_t activeOffset = fat->archOffset();
+                       for (Universal::Architectures::const_iterator arch = architectures.begin(); arch != architectures.end(); ++arch) {
+                               ctx.offset = fat->archOffset(*arch);
+                               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
+                                       handle(subcode);
+                               }
                        }
                }
                        }
                }
-       default:
-               return NULL;
        }
 }
 
        }
 }