2 * Copyright (c) 2006-2012 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 // StaticCode - SecStaticCode API objects
27 #include "StaticCode.h"
31 #include "reqdumper.h"
32 #include "reqparser.h"
34 #include "resources.h"
35 #include "detachedrep.h"
36 #include "csdatabase.h"
37 #include "csutilities.h"
38 #include <CoreFoundation/CFURLAccess.h>
39 #include <Security/SecPolicyPriv.h>
40 #include <Security/SecTrustPriv.h>
41 #include <Security/SecCertificatePriv.h>
42 #include <Security/CMSPrivate.h>
43 #include <Security/SecCmsContentInfo.h>
44 #include <Security/SecCmsSignerInfo.h>
45 #include <Security/SecCmsSignedData.h>
46 #include <Security/cssmapplePriv.h>
47 #include <security_utilities/unix++.h>
48 #include <security_utilities/cfmunge.h>
49 #include <Security/CMSDecoder.h>
53 namespace CodeSigning
{
55 using namespace UnixPlusPlus
;
59 // Map a component slot number to a suitable error code for a failure
61 static inline OSStatus
errorForSlot(CodeDirectory::SpecialSlot slot
)
65 return errSecCSInfoPlistFailed
;
66 case cdResourceDirSlot
:
67 return errSecCSResourceDirectoryFailed
;
69 return errSecCSSignatureFailed
;
75 // Construct a SecStaticCode object given a disk representation object
77 SecStaticCode::SecStaticCode(DiskRep
*rep
)
79 mValidated(false), mExecutableValidated(false), mResourcesValidated(false), mResourcesValidContext(NULL
),
80 mDesignatedReq(NULL
), mGotResourceBase(false), mMonitor(NULL
), mEvalDetails(NULL
)
82 CODESIGN_STATIC_CREATE(this, rep
);
83 checkForSystemSignature();
88 // Clean up a SecStaticCode object
90 SecStaticCode::~SecStaticCode() throw()
92 ::free(const_cast<Requirement
*>(mDesignatedReq
));
93 if (mResourcesValidContext
)
94 delete mResourcesValidContext
;
101 // CF-level comparison of SecStaticCode objects compares CodeDirectory hashes if signed,
102 // and falls back on comparing canonical paths if (both are) not.
104 bool SecStaticCode::equal(SecCFObject
&secOther
)
106 SecStaticCode
*other
= static_cast<SecStaticCode
*>(&secOther
);
107 CFDataRef mine
= this->cdHash();
108 CFDataRef his
= other
->cdHash();
110 return mine
&& his
&& CFEqual(mine
, his
);
112 return CFEqual(this->canonicalPath(), other
->canonicalPath());
115 CFHashCode
SecStaticCode::hash()
117 if (CFDataRef h
= this->cdHash())
120 return CFHash(this->canonicalPath());
125 // Invoke a stage monitor if registered
127 CFTypeRef
SecStaticCode::reportEvent(CFStringRef stage
, CFDictionaryRef info
)
130 return mMonitor(this->handle(false), stage
, info
);
137 // Attach a detached signature.
139 void SecStaticCode::detachedSignature(CFDataRef sigData
)
142 mDetachedSig
= sigData
;
143 mRep
= new DetachedRep(sigData
, mRep
->base(), "explicit detached");
144 CODESIGN_STATIC_ATTACH_EXPLICIT(this, mRep
);
148 CODESIGN_STATIC_ATTACH_EXPLICIT(this, NULL
);
154 // Consult the system detached signature database to see if it contains
155 // a detached signature for this StaticCode. If it does, fetch and attach it.
156 // We do this only if the code has no signature already attached.
158 void SecStaticCode::checkForSystemSignature()
160 if (!this->isSigned()) {
161 SignatureDatabase db
;
164 if (RefPointer
<DiskRep
> dsig
= db
.findCode(mRep
)) {
165 CODESIGN_STATIC_ATTACH_SYSTEM(this, dsig
);
175 // Return a descriptive string identifying the source of the code signature
177 string
SecStaticCode::signatureSource()
181 if (DetachedRep
*rep
= dynamic_cast<DetachedRep
*>(mRep
.get()))
182 return rep
->source();
188 // Do ::required, but convert incoming SecCodeRefs to their SecStaticCodeRefs
191 SecStaticCode
*SecStaticCode::requiredStatic(SecStaticCodeRef ref
)
193 SecCFObject
*object
= SecCFObject::required(ref
, errSecCSInvalidObjectRef
);
194 if (SecStaticCode
*scode
= dynamic_cast<SecStaticCode
*>(object
))
196 else if (SecCode
*code
= dynamic_cast<SecCode
*>(object
))
197 return code
->staticCode();
198 else // neither (a SecSomethingElse)
199 MacOSError::throwMe(errSecCSInvalidObjectRef
);
202 SecCode
*SecStaticCode::optionalDynamic(SecStaticCodeRef ref
)
204 SecCFObject
*object
= SecCFObject::required(ref
, errSecCSInvalidObjectRef
);
205 if (dynamic_cast<SecStaticCode
*>(object
))
207 else if (SecCode
*code
= dynamic_cast<SecCode
*>(object
))
209 else // neither (a SecSomethingElse)
210 MacOSError::throwMe(errSecCSInvalidObjectRef
);
215 // Void all cached validity data.
217 // We also throw out cached components, because the new signature data may have
218 // a different idea of what components should be present. We could reconcile the
219 // cached data instead, if performance seems to be impacted.
221 void SecStaticCode::resetValidity()
223 CODESIGN_EVAL_STATIC_RESET(this);
225 mExecutableValidated
= mResourcesValidated
= false;
226 if (mResourcesValidContext
) {
227 delete mResourcesValidContext
;
228 mResourcesValidContext
= NULL
;
232 for (unsigned n
= 0; n
< cdSlotCount
; n
++)
235 mEntitlements
= NULL
;
236 mResourceDict
= NULL
;
237 mDesignatedReq
= NULL
;
238 mGotResourceBase
= false;
244 // we may just have updated the system database, so check again
245 checkForSystemSignature();
250 // Retrieve a sealed component by special slot index.
251 // If the CodeDirectory has already been validated, validate against that.
252 // Otherwise, retrieve the component without validation (but cache it). Validation
253 // will go through the cache and validate all cached components.
255 CFDataRef
SecStaticCode::component(CodeDirectory::SpecialSlot slot
, OSStatus fail
/* = errSecCSSignatureFailed */)
257 assert(slot
<= cdSlotMax
);
259 CFRef
<CFDataRef
> &cache
= mCache
[slot
];
261 if (CFRef
<CFDataRef
> data
= mRep
->component(slot
)) {
262 if (validated()) // if the directory has been validated...
263 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data
), // ... and it's no good
264 CFDataGetLength(data
), -slot
))
265 MacOSError::throwMe(errorForSlot(slot
)); // ... then bail
266 cache
= data
; // it's okay, cache it
267 } else { // absent, mark so
268 if (validated()) // if directory has been validated...
269 if (codeDirectory()->slotIsPresent(-slot
)) // ... and the slot is NOT missing
270 MacOSError::throwMe(errorForSlot(slot
)); // was supposed to be there
271 cache
= CFDataRef(kCFNull
); // white lie
274 return (cache
== CFDataRef(kCFNull
)) ? NULL
: cache
.get();
279 // Get the CodeDirectory.
280 // Throws (if check==true) or returns NULL (check==false) if there is none.
281 // Always throws if the CodeDirectory exists but is invalid.
282 // NEVER validates against the signature.
284 const CodeDirectory
*SecStaticCode::codeDirectory(bool check
/* = true */)
287 if (mDir
.take(mRep
->codeDirectory())) {
288 const CodeDirectory
*dir
= reinterpret_cast<const CodeDirectory
*>(CFDataGetBytePtr(mDir
));
289 dir
->checkIntegrity();
293 return reinterpret_cast<const CodeDirectory
*>(CFDataGetBytePtr(mDir
));
295 MacOSError::throwMe(errSecCSUnsigned
);
301 // Get the hash of the CodeDirectory.
302 // Returns NULL if there is none.
304 CFDataRef
SecStaticCode::cdHash()
307 if (const CodeDirectory
*cd
= codeDirectory(false)) {
309 hash(cd
, cd
->length());
312 mCDHash
.take(makeCFData(digest
, sizeof(digest
)));
313 CODESIGN_STATIC_CDHASH(this, digest
, sizeof(digest
));
321 // Return the CMS signature blob; NULL if none found.
323 CFDataRef
SecStaticCode::signature()
326 mSignature
.take(mRep
->signature());
329 MacOSError::throwMe(errSecCSUnsigned
);
334 // Verify the signature on the CodeDirectory.
335 // If this succeeds (doesn't throw), the CodeDirectory is statically trustworthy.
336 // Any outcome (successful or not) is cached for the lifetime of the StaticCode.
338 void SecStaticCode::validateDirectory()
340 // echo previous outcome, if any
343 // perform validation (or die trying)
344 CODESIGN_EVAL_STATIC_DIRECTORY(this);
345 mValidationExpired
= verifySignature();
346 for (CodeDirectory::SpecialSlot slot
= codeDirectory()->maxSpecialSlot(); slot
>= 1; --slot
)
347 if (mCache
[slot
]) // if we already loaded that resource...
348 validateComponent(slot
, errorForSlot(slot
)); // ... then check it now
349 mValidated
= true; // we've done the deed...
350 mValidationResult
= errSecSuccess
; // ... and it was good
351 } catch (const CommonError
&err
) {
353 mValidationResult
= err
.osStatus();
356 secdebug("staticCode", "%p validation threw non-common exception", this);
358 mValidationResult
= errSecCSInternalError
;
362 if (mValidationResult
== errSecSuccess
) {
363 if (mValidationExpired
)
364 if ((apiFlags() & kSecCSConsiderExpiration
)
365 || (codeDirectory()->flags
& kSecCodeSignatureForceExpiration
))
366 MacOSError::throwMe(CSSMERR_TP_CERT_EXPIRED
);
368 MacOSError::throwMe(mValidationResult
);
373 // Load and validate the CodeDirectory and all components *except* those related to the resource envelope.
374 // Those latter components are checked by validateResources().
376 void SecStaticCode::validateNonResourceComponents()
378 this->validateDirectory();
379 for (CodeDirectory::SpecialSlot slot
= codeDirectory()->maxSpecialSlot(); slot
>= 1; --slot
)
381 case cdResourceDirSlot
: // validated by validateResources
384 this->component(slot
); // loads and validates
391 // Get the (signed) signing date from the code signature.
392 // Sadly, we need to validate the signature to get the date (as a side benefit).
393 // This means that you can't get the signing time for invalidly signed code.
395 // We could run the decoder "almost to" verification to avoid this, but there seems
396 // little practical point to such a duplication of effort.
398 CFAbsoluteTime
SecStaticCode::signingTime()
404 CFAbsoluteTime
SecStaticCode::signingTimestamp()
407 return mSigningTimestamp
;
412 // Verify the CMS signature on the CodeDirectory.
413 // This performs the cryptographic tango. It returns if the signature is valid,
414 // or throws if it is not. As a side effect, a successful return sets up the
415 // cached certificate chain for future use.
416 // Returns true if the signature is expired (the X.509 sense), false if it's not.
417 // Expiration is fatal (throws) if a secure timestamp is included, but not otherwise.
419 bool SecStaticCode::verifySignature()
421 // ad-hoc signed code is considered validly signed by definition
422 if (flag(kSecCodeSignatureAdhoc
)) {
423 CODESIGN_EVAL_STATIC_SIGNATURE_ADHOC(this);
427 DTRACK(CODESIGN_EVAL_STATIC_SIGNATURE
, this, (char*)this->mainExecutablePath().c_str());
429 // decode CMS and extract SecTrust for verification
430 CFRef
<CMSDecoderRef
> cms
;
431 MacOSError::check(CMSDecoderCreate(&cms
.aref())); // create decoder
432 CFDataRef sig
= this->signature();
433 MacOSError::check(CMSDecoderUpdateMessage(cms
, CFDataGetBytePtr(sig
), CFDataGetLength(sig
)));
434 this->codeDirectory(); // load CodeDirectory (sets mDir)
435 MacOSError::check(CMSDecoderSetDetachedContent(cms
, mDir
));
436 MacOSError::check(CMSDecoderFinalizeMessage(cms
));
437 MacOSError::check(CMSDecoderSetSearchKeychain(cms
, cfEmptyArray()));
438 CFRef
<CFTypeRef
> policy
= verificationPolicy(apiFlags());
439 CMSSignerStatus status
;
440 MacOSError::check(CMSDecoderCopySignerStatus(cms
, 0, policy
,
441 false, &status
, &mTrust
.aref(), NULL
));
442 if (status
!= kCMSSignerValid
)
443 MacOSError::throwMe(errSecCSSignatureFailed
);
445 // internal signing time (as specified by the signer; optional)
446 mSigningTime
= 0; // "not present" marker (nobody could code sign on Jan 1, 2001 :-)
447 switch (OSStatus rc
= CMSDecoderCopySignerSigningTime(cms
, 0, &mSigningTime
)) {
449 case errSecSigningTimeMissing
:
452 MacOSError::throwMe(rc
);
455 // certified signing time (as specified by a TSA; optional)
456 mSigningTimestamp
= 0;
457 switch (OSStatus rc
= CMSDecoderCopySignerTimestamp(cms
, 0, &mSigningTimestamp
)) {
459 case errSecTimestampMissing
:
462 MacOSError::throwMe(rc
);
465 // set up the environment for SecTrust
466 MacOSError::check(SecTrustSetAnchorCertificates(mTrust
, cfEmptyArray())); // no anchors
467 MacOSError::check(SecTrustSetKeychains(mTrust
, cfEmptyArray())); // no keychains
468 CSSM_APPLE_TP_ACTION_DATA actionData
= {
469 CSSM_APPLE_TP_ACTION_VERSION
, // version of data structure
470 CSSM_TP_ACTION_IMPLICIT_ANCHORS
// action flags
473 for (;;) { // at most twice
474 MacOSError::check(SecTrustSetParameters(mTrust
,
475 CSSM_TP_ACTION_DEFAULT
, CFTempData(&actionData
, sizeof(actionData
))));
477 // evaluate trust and extract results
478 SecTrustResultType trustResult
;
479 MacOSError::check(SecTrustEvaluate(mTrust
, &trustResult
));
480 MacOSError::check(SecTrustGetResult(mTrust
, &trustResult
, &mCertChain
.aref(), &mEvalDetails
));
481 CODESIGN_EVAL_STATIC_SIGNATURE_RESULT(this, trustResult
, mCertChain
? (int)CFArrayGetCount(mCertChain
) : 0);
482 switch (trustResult
) {
483 case kSecTrustResultProceed
:
484 case kSecTrustResultUnspecified
:
486 case kSecTrustResultDeny
:
487 MacOSError::throwMe(CSSMERR_APPLETP_TRUST_SETTING_DENY
); // user reject
488 case kSecTrustResultInvalid
:
489 assert(false); // should never happen
490 MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED
);
494 MacOSError::check(SecTrustGetCssmResultCode(mTrust
, &result
));
495 // if we have a valid timestamp, CMS validates against (that) signing time and all is well.
496 // If we don't have one, may validate against *now*, and must be able to tolerate expiration.
497 if (mSigningTimestamp
== 0) // no timestamp available
498 if (((result
== CSSMERR_TP_CERT_EXPIRED
) || (result
== CSSMERR_TP_CERT_NOT_VALID_YET
))
499 && !(actionData
.ActionFlags
& CSSM_TP_ACTION_ALLOW_EXPIRED
)) {
500 CODESIGN_EVAL_STATIC_SIGNATURE_EXPIRED(this);
501 actionData
.ActionFlags
|= CSSM_TP_ACTION_ALLOW_EXPIRED
; // (this also allows postdated certs)
502 continue; // retry validation while tolerating expiration
504 MacOSError::throwMe(result
);
508 if (mSigningTimestamp
) {
509 CFIndex rootix
= CFArrayGetCount(mCertChain
);
510 if (SecCertificateRef mainRoot
= SecCertificateRef(CFArrayGetValueAtIndex(mCertChain
, rootix
-1)))
511 if (isAppleCA(mainRoot
)) {
512 // impose policy: if the signature itself draws to Apple, then so must the timestamp signature
513 CFRef
<CFArrayRef
> tsCerts
;
514 MacOSError::check(CMSDecoderCopySignerTimestampCertificates(cms
, 0, &tsCerts
.aref()));
515 CFIndex tsn
= CFArrayGetCount(tsCerts
);
516 bool good
= tsn
> 0 && isAppleCA(SecCertificateRef(CFArrayGetValueAtIndex(tsCerts
, tsn
-1)));
518 MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED
);
522 return actionData
.ActionFlags
& CSSM_TP_ACTION_ALLOW_EXPIRED
;
528 // Return the TP policy used for signature verification.
529 // This may be a simple SecPolicyRef or a CFArray of policies.
530 // The caller owns the return value.
532 static SecPolicyRef
makeCRLPolicy()
534 CFRef
<SecPolicyRef
> policy
;
535 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3
, &CSSMOID_APPLE_TP_REVOCATION_CRL
, &policy
.aref()));
536 CSSM_APPLE_TP_CRL_OPTIONS options
;
537 memset(&options
, 0, sizeof(options
));
538 options
.Version
= CSSM_APPLE_TP_CRL_OPTS_VERSION
;
539 options
.CrlFlags
= CSSM_TP_ACTION_FETCH_CRL_FROM_NET
| CSSM_TP_ACTION_CRL_SUFFICIENT
;
540 CSSM_DATA optData
= { sizeof(options
), (uint8
*)&options
};
541 MacOSError::check(SecPolicySetValue(policy
, &optData
));
542 return policy
.yield();
545 static SecPolicyRef
makeOCSPPolicy()
547 CFRef
<SecPolicyRef
> policy
;
548 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3
, &CSSMOID_APPLE_TP_REVOCATION_OCSP
, &policy
.aref()));
549 CSSM_APPLE_TP_OCSP_OPTIONS options
;
550 memset(&options
, 0, sizeof(options
));
551 options
.Version
= CSSM_APPLE_TP_OCSP_OPTS_VERSION
;
552 options
.Flags
= CSSM_TP_ACTION_OCSP_SUFFICIENT
;
553 CSSM_DATA optData
= { sizeof(options
), (uint8
*)&options
};
554 MacOSError::check(SecPolicySetValue(policy
, &optData
));
555 return policy
.yield();
558 CFTypeRef
SecStaticCode::verificationPolicy(SecCSFlags flags
)
560 CFRef
<SecPolicyRef
> core
;
561 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3
,
562 &CSSMOID_APPLE_TP_CODE_SIGNING
, &core
.aref()));
563 if (flags
& kSecCSEnforceRevocationChecks
) {
564 CFRef
<SecPolicyRef
> crl
= makeCRLPolicy();
565 CFRef
<SecPolicyRef
> ocsp
= makeOCSPPolicy();
566 return makeCFArray(3, core
.get(), crl
.get(), ocsp
.get());
574 // Validate a particular sealed, cached resource against its (special) CodeDirectory slot.
575 // The resource must already have been placed in the cache.
576 // This does NOT perform basic validation.
578 void SecStaticCode::validateComponent(CodeDirectory::SpecialSlot slot
, OSStatus fail
/* = errSecCSSignatureFailed */)
580 assert(slot
<= cdSlotMax
);
581 CFDataRef data
= mCache
[slot
];
582 assert(data
); // must be cached
583 if (data
== CFDataRef(kCFNull
)) {
584 if (codeDirectory()->slotIsPresent(-slot
)) // was supposed to be there...
585 MacOSError::throwMe(fail
); // ... and is missing
587 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data
), CFDataGetLength(data
), -slot
))
588 MacOSError::throwMe(fail
);
594 // Perform static validation of the main executable.
595 // This reads the main executable from disk and validates it against the
596 // CodeDirectory code slot array.
597 // Note that this is NOT an in-memory validation, and is thus potentially
598 // subject to timing attacks.
600 void SecStaticCode::validateExecutable()
602 if (!validatedExecutable()) {
604 DTRACK(CODESIGN_EVAL_STATIC_EXECUTABLE
, this,
605 (char*)this->mainExecutablePath().c_str(), codeDirectory()->nCodeSlots
);
606 const CodeDirectory
*cd
= this->codeDirectory();
608 MacOSError::throwMe(errSecCSUnsigned
);
609 AutoFileDesc
fd(mainExecutablePath(), O_RDONLY
);
610 fd
.fcntl(F_NOCACHE
, true); // turn off page caching (one-pass)
611 if (Universal
*fat
= mRep
->mainExecutableImage())
612 fd
.seek(fat
->archOffset());
613 size_t pageSize
= cd
->pageSize
? (1 << cd
->pageSize
) : 0;
614 size_t remaining
= cd
->codeLimit
;
615 for (uint32_t slot
= 0; slot
< cd
->nCodeSlots
; ++slot
) {
616 size_t size
= min(remaining
, pageSize
);
617 if (!cd
->validateSlot(fd
, size
, slot
)) {
618 CODESIGN_EVAL_STATIC_EXECUTABLE_FAIL(this, (int)slot
);
619 MacOSError::throwMe(errSecCSSignatureFailed
);
623 mExecutableValidated
= true;
624 mExecutableValidResult
= errSecSuccess
;
625 } catch (const CommonError
&err
) {
626 mExecutableValidated
= true;
627 mExecutableValidResult
= err
.osStatus();
630 secdebug("staticCode", "%p executable validation threw non-common exception", this);
631 mExecutableValidated
= true;
632 mExecutableValidResult
= errSecCSInternalError
;
636 assert(validatedExecutable());
637 if (mExecutableValidResult
!= errSecSuccess
)
638 MacOSError::throwMe(mExecutableValidResult
);
643 // Perform static validation of sealed resources and nested code.
645 // This performs a whole-code static resource scan and effectively
646 // computes a concordance between what's on disk and what's in the ResourceDirectory.
647 // Any unsanctioned difference causes an error.
649 void SecStaticCode::validateResources(SecCSFlags flags
)
651 // do we have a superset of this requested validation cached?
653 if (mResourcesValidated
) { // have cached outcome
654 if (!(flags
& kSecCSCheckNestedCode
) || mResourcesDeep
) // was deep or need no deep scan
660 CFDictionaryRef sealedResources
= resourceDictionary();
661 if (this->resourceBase()) // disk has resources
663 /* go to work below */;
665 MacOSError::throwMe(errSecCSResourcesNotFound
);
666 else // disk has no resources
668 MacOSError::throwMe(errSecCSResourcesNotFound
);
670 return; // no resources, not sealed - fine (no work)
672 // found resources, and they are sealed
673 DTRACK(CODESIGN_EVAL_STATIC_RESOURCES
, this,
674 (char*)this->mainExecutablePath().c_str(), 0);
676 // scan through the resources on disk, checking each against the resourceDirectory
677 mResourcesValidContext
= new CollectingContext(*this); // collect all failures in here
678 CFDictionaryRef rules
;
679 CFDictionaryRef files
;
681 if (CFDictionaryGetValue(sealedResources
, CFSTR("files2"))) { // have V2 signature
682 rules
= cfget
<CFDictionaryRef
>(sealedResources
, "rules2");
683 files
= cfget
<CFDictionaryRef
>(sealedResources
, "files2");
685 } else { // only V1 available
686 rules
= cfget
<CFDictionaryRef
>(sealedResources
, "rules");
687 files
= cfget
<CFDictionaryRef
>(sealedResources
, "files");
690 if (!rules
|| !files
)
691 MacOSError::throwMe(errSecCSResourcesInvalid
);
692 __block CFRef
<CFMutableDictionaryRef
> resourceMap
= makeCFMutableDictionary(files
);
693 ResourceBuilder
resources(cfString(this->resourceBase()), rules
, codeDirectory()->hashType
);
694 diskRep()->adjustResources(resources
);
695 resources
.scan(^(FTSENT
*ent
, uint32_t ruleFlags
, const char *relpath
, ResourceBuilder::Rule
*rule
) {
696 validateResource(files
, relpath
, *mResourcesValidContext
, flags
, version
);
697 CFDictionaryRemoveValue(resourceMap
, CFTempString(relpath
));
700 if (CFDictionaryGetCount(resourceMap
) > 0) {
701 secdebug("staticCode", "%p sealed resource(s) not found in code", this);
702 CFDictionaryApplyFunction(resourceMap
, SecStaticCode::checkOptionalResource
, mResourcesValidContext
);
705 // now check for any errors found in the reporting context
706 mResourcesValidated
= true;
707 mResourcesDeep
= flags
& kSecCSCheckNestedCode
;
708 if (mResourcesValidContext
->osStatus() != errSecSuccess
)
709 mResourcesValidContext
->throwMe();
710 } catch (const CommonError
&err
) {
711 mResourcesValidated
= true;
712 mResourcesDeep
= flags
& kSecCSCheckNestedCode
;
713 mResourcesValidResult
= err
.osStatus();
716 secdebug("staticCode", "%p executable validation threw non-common exception", this);
717 mResourcesValidated
= true;
718 mResourcesDeep
= flags
& kSecCSCheckNestedCode
;
719 mResourcesValidResult
= errSecCSInternalError
;
723 assert(validatedResources());
724 if (mResourcesValidResult
)
725 MacOSError::throwMe(mResourcesValidResult
);
726 if (mResourcesValidContext
->osStatus() != errSecSuccess
)
727 mResourcesValidContext
->throwMe();
731 void SecStaticCode::checkOptionalResource(CFTypeRef key
, CFTypeRef value
, void *context
)
733 CollectingContext
*ctx
= static_cast<CollectingContext
*>(context
);
734 ResourceSeal
seal(value
);
735 if (!seal
.optional()) {
736 if (key
&& CFGetTypeID(key
) == CFStringGetTypeID()) {
737 ctx
->reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
,
738 CFTempURL(CFStringRef(key
), false, ctx
->code
.resourceBase()));
740 ctx
->reportProblem(errSecCSBadResource
, kSecCFErrorResourceSeal
, key
);
747 // Load, validate, cache, and return CFDictionary forms of sealed resources.
749 CFDictionaryRef
SecStaticCode::infoDictionary()
752 mInfoDict
.take(getDictionary(cdInfoSlot
, errSecCSInfoPlistFailed
));
753 secdebug("staticCode", "%p loaded InfoDict %p", this, mInfoDict
.get());
758 CFDictionaryRef
SecStaticCode::entitlements()
760 if (!mEntitlements
) {
762 if (CFDataRef entitlementData
= component(cdEntitlementSlot
)) {
763 validateComponent(cdEntitlementSlot
);
764 const EntitlementBlob
*blob
= reinterpret_cast<const EntitlementBlob
*>(CFDataGetBytePtr(entitlementData
));
765 if (blob
->validateBlob()) {
766 mEntitlements
.take(blob
->entitlements());
767 secdebug("staticCode", "%p loaded Entitlements %p", this, mEntitlements
.get());
769 // we do not consider a different blob type to be an error. We think it's a new format we don't understand
772 return mEntitlements
;
775 CFDictionaryRef
SecStaticCode::resourceDictionary(bool check
/* = true */)
777 if (mResourceDict
) // cached
778 return mResourceDict
;
779 if (CFRef
<CFDictionaryRef
> dict
= getDictionary(cdResourceDirSlot
, check
))
780 if (cfscan(dict
, "{rules=%Dn,files=%Dn}")) {
781 secdebug("staticCode", "%p loaded ResourceDict %p",
782 this, mResourceDict
.get());
783 return mResourceDict
= dict
;
791 // Load and cache the resource directory base.
792 // Note that the base is optional for each DiskRep.
794 CFURLRef
SecStaticCode::resourceBase()
796 if (!mGotResourceBase
) {
797 string base
= mRep
->resourcesRootPath();
799 mResourceBase
.take(makeCFURL(base
, true));
800 mGotResourceBase
= true;
802 return mResourceBase
;
807 // Load a component, validate it, convert it to a CFDictionary, and return that.
808 // This will force load and validation, which means that it will perform basic
809 // validation if it hasn't been done yet.
811 CFDictionaryRef
SecStaticCode::getDictionary(CodeDirectory::SpecialSlot slot
, bool check
/* = true */)
815 if (CFDataRef infoData
= component(slot
)) {
816 validateComponent(slot
);
817 if (CFDictionaryRef dict
= makeCFDictionaryFrom(infoData
))
820 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
827 // Load, validate, and return a sealed resource.
828 // The resource data (loaded in to memory as a blob) is returned and becomes
829 // the responsibility of the caller; it is NOT cached by SecStaticCode.
831 // A resource that is not sealed will not be returned, and an error will be thrown.
832 // A missing resource will cause an error unless it's marked optional in the Directory.
833 // Under no circumstances will a corrupt resource be returned.
834 // NULL will only be returned for a resource that is neither sealed nor present
835 // (or that is sealed, absent, and marked optional).
836 // If the ResourceDictionary itself is not sealed, this function will always fail.
838 // There is currently no interface for partial retrieval of the resource data.
839 // (Since the ResourceDirectory does not currently support segmentation, all the
840 // data would have to be read anyway, but it could be read into a reusable buffer.)
842 CFDataRef
SecStaticCode::resource(string path
, ValidationContext
&ctx
)
844 if (CFDictionaryRef rdict
= resourceDictionary()) {
845 if (CFTypeRef file
= cfget(rdict
, "files.%s", path
.c_str())) {
846 ResourceSeal seal
= file
;
847 if (!resourceBase()) // no resources in DiskRep
848 MacOSError::throwMe(errSecCSResourcesNotFound
);
850 MacOSError::throwMe(errSecCSResourcesNotSealed
); // (it's nested code)
851 CFRef
<CFURLRef
> fullpath
= makeCFURL(path
, false, resourceBase());
852 if (CFRef
<CFDataRef
> data
= cfLoadFile(fullpath
)) {
853 MakeHash
<CodeDirectory
> hasher(this->codeDirectory());
854 hasher
->update(CFDataGetBytePtr(data
), CFDataGetLength(data
));
855 if (hasher
->verify(seal
.hash()))
856 return data
.yield(); // good
858 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAltered
, fullpath
); // altered
860 if (!seal
.optional())
861 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
, fullpath
); // was sealed but is now missing
863 return NULL
; // validly missing
866 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAdded
, CFTempURL(path
, false, resourceBase()));
869 MacOSError::throwMe(errSecCSResourcesNotSealed
);
872 CFDataRef
SecStaticCode::resource(string path
)
874 ValidationContext ctx
;
875 return resource(path
, ctx
);
879 void SecStaticCode::validateResource(CFDictionaryRef files
, string path
, ValidationContext
&ctx
, SecCSFlags flags
, uint32_t version
)
881 if (!resourceBase()) // no resources in DiskRep
882 MacOSError::throwMe(errSecCSResourcesNotFound
);
883 CFRef
<CFURLRef
> fullpath
= makeCFURL(path
, false, resourceBase());
884 if (CFTypeRef file
= CFDictionaryGetValue(files
, CFTempString(path
))) {
885 ResourceSeal seal
= file
;
887 validateNestedCode(fullpath
, seal
, flags
);
888 } else if (seal
.link()) {
889 char target
[PATH_MAX
];
890 ssize_t len
= ::readlink(cfString(fullpath
).c_str(), target
, sizeof(target
)-1);
892 UnixError::check(-1);
894 if (cfString(seal
.link()) != target
)
895 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAltered
, fullpath
);
896 } else if (seal
.hash()) { // genuine file
897 AutoFileDesc
fd(cfString(fullpath
), O_RDONLY
, FileDesc::modeMissingOk
); // open optional file
899 MakeHash
<CodeDirectory
> hasher(this->codeDirectory());
900 hashFileData(fd
, hasher
.get());
901 if (hasher
->verify(seal
.hash()))
902 return; // verify good
904 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAltered
, fullpath
); // altered
906 if (!seal
.optional())
907 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
, fullpath
); // was sealed but is now missing
909 return; // validly missing
912 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAltered
, fullpath
); // changed type
915 if (version
== 1) { // version 1 ignores symlinks altogether
916 char target
[PATH_MAX
];
917 if (::readlink(cfString(fullpath
).c_str(), target
, sizeof(target
)) > 0)
920 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAdded
, CFTempURL(path
, false, resourceBase()));
923 void SecStaticCode::validateNestedCode(CFURLRef path
, const ResourceSeal
&seal
, SecCSFlags flags
)
925 CFRef
<SecRequirementRef
> req
;
926 if (SecRequirementCreateWithString(seal
.requirement(), kSecCSDefaultFlags
, &req
.aref()))
927 MacOSError::throwMe(errSecCSResourcesInvalid
);
929 // recursively verify this nested code
931 if (!(flags
& kSecCSCheckNestedCode
))
932 flags
|= kSecCSBasicValidateOnly
;
933 SecPointer
<SecStaticCode
> code
= new SecStaticCode(DiskRep::bestGuess(cfString(path
)));
934 code
->setMonitor(this->monitor());
935 code
->staticValidate(flags
, SecRequirement::required(req
));
936 } catch (CSError
&err
) {
937 if (err
.error
== errSecCSReqFailed
) {
938 mResourcesValidContext
->reportProblem(errSecCSBadNestedCode
, kSecCFErrorResourceAltered
, path
);
941 err
.augment(kSecCFErrorPath
, path
);
943 } catch (const MacOSError
&err
) {
944 if (err
.error
== errSecCSReqFailed
) {
945 mResourcesValidContext
->reportProblem(errSecCSBadNestedCode
, kSecCFErrorResourceAltered
, path
);
948 CSError::throwMe(err
.error
, kSecCFErrorPath
, path
);
954 // Test a CodeDirectory flag.
955 // Returns false if there is no CodeDirectory.
956 // May throw if the CodeDirectory is present but somehow invalid.
958 bool SecStaticCode::flag(uint32_t tested
)
960 if (const CodeDirectory
*cd
= this->codeDirectory(false))
961 return cd
->flags
& tested
;
968 // Retrieve the full SuperBlob containing all internal requirements.
970 const Requirements
*SecStaticCode::internalRequirements()
972 if (CFDataRef reqData
= component(cdRequirementsSlot
)) {
973 const Requirements
*req
= (const Requirements
*)CFDataGetBytePtr(reqData
);
974 if (!req
->validateBlob())
975 MacOSError::throwMe(errSecCSReqInvalid
);
983 // Retrieve a particular internal requirement by type.
985 const Requirement
*SecStaticCode::internalRequirement(SecRequirementType type
)
987 if (const Requirements
*reqs
= internalRequirements())
988 return reqs
->find
<Requirement
>(type
);
995 // Return the Designated Requirement (DR). This can be either explicit in the
996 // Internal Requirements component, or implicitly generated on demand here.
997 // Note that an explicit DR may have been implicitly generated at signing time;
998 // we don't distinguish this case.
1000 const Requirement
*SecStaticCode::designatedRequirement()
1002 if (const Requirement
*req
= internalRequirement(kSecDesignatedRequirementType
)) {
1003 return req
; // explicit in signing data
1005 if (!mDesignatedReq
)
1006 mDesignatedReq
= defaultDesignatedRequirement();
1007 return mDesignatedReq
;
1013 // Generate the default Designated Requirement (DR) for this StaticCode.
1014 // Ignore any explicit DR it may contain.
1016 const Requirement
*SecStaticCode::defaultDesignatedRequirement()
1018 if (flag(kSecCodeSignatureAdhoc
)) {
1019 // adhoc signature: return a cdhash requirement for all architectures
1020 __block
Requirement::Maker maker
;
1021 Requirement::Maker::Chain
chain(maker
, opOr
);
1023 // insert cdhash requirement for all architectures
1025 maker
.cdhash(this->cdHash());
1026 handleOtherArchitectures(^(SecStaticCode
*subcode
) {
1027 if (CFDataRef cdhash
= subcode
->cdHash()) {
1029 maker
.cdhash(cdhash
);
1032 return maker
.make();
1034 // full signature: Gin up full context and let DRMaker do its thing
1035 validateDirectory(); // need the cert chain
1036 Requirement::Context
context(this->certificates(),
1037 this->infoDictionary(),
1038 this->entitlements(),
1040 this->codeDirectory()
1042 return DRMaker(context
).make();
1048 // Validate a SecStaticCode against the internal requirement of a particular type.
1050 void SecStaticCode::validateRequirements(SecRequirementType type
, SecStaticCode
*target
,
1051 OSStatus nullError
/* = errSecSuccess */)
1053 DTRACK(CODESIGN_EVAL_STATIC_INTREQ
, this, type
, target
, nullError
);
1054 if (const Requirement
*req
= internalRequirement(type
))
1055 target
->validateRequirement(req
, nullError
? nullError
: errSecCSReqFailed
);
1057 MacOSError::throwMe(nullError
);
1064 // Validate this StaticCode against an external Requirement
1066 bool SecStaticCode::satisfiesRequirement(const Requirement
*req
, OSStatus failure
)
1069 validateDirectory();
1070 return req
->validates(Requirement::Context(mCertChain
, infoDictionary(), entitlements(), codeDirectory()->identifier(), codeDirectory()), failure
);
1073 void SecStaticCode::validateRequirement(const Requirement
*req
, OSStatus failure
)
1075 if (!this->satisfiesRequirement(req
, failure
))
1076 MacOSError::throwMe(failure
);
1081 // Retrieve one certificate from the cert chain.
1082 // Positive and negative indices can be used:
1083 // [ leaf, intermed-1, ..., intermed-n, anchor ]
1085 // Returns NULL if unavailable for any reason.
1087 SecCertificateRef
SecStaticCode::cert(int ix
)
1089 validateDirectory(); // need cert chain
1091 CFIndex length
= CFArrayGetCount(mCertChain
);
1094 if (ix
>= 0 && ix
< length
)
1095 return SecCertificateRef(CFArrayGetValueAtIndex(mCertChain
, ix
));
1100 CFArrayRef
SecStaticCode::certificates()
1102 validateDirectory(); // need cert chain
1108 // Gather (mostly) API-official information about this StaticCode.
1110 // This method lives in the twilight between the API and internal layers,
1111 // since it generates API objects (Sec*Refs) for return.
1113 CFDictionaryRef
SecStaticCode::signingInformation(SecCSFlags flags
)
1116 // Start with the pieces that we return even for unsigned code.
1117 // This makes Sec[Static]CodeRefs useful as API-level replacements
1118 // of our internal OSXCode objects.
1120 CFRef
<CFMutableDictionaryRef
> dict
= makeCFMutableDictionary(1,
1121 kSecCodeInfoMainExecutable
, CFTempURL(this->mainExecutablePath()).get()
1125 // If we're not signed, this is all you get
1127 if (!this->isSigned())
1128 return dict
.yield();
1131 // Add the generic attributes that we always include
1133 CFDictionaryAddValue(dict
, kSecCodeInfoIdentifier
, CFTempString(this->identifier()));
1134 CFDictionaryAddValue(dict
, kSecCodeInfoFlags
, CFTempNumber(this->codeDirectory(false)->flags
.get()));
1135 CFDictionaryAddValue(dict
, kSecCodeInfoFormat
, CFTempString(this->format()));
1136 CFDictionaryAddValue(dict
, kSecCodeInfoSource
, CFTempString(this->signatureSource()));
1137 CFDictionaryAddValue(dict
, kSecCodeInfoUnique
, this->cdHash());
1138 CFDictionaryAddValue(dict
, kSecCodeInfoDigestAlgorithm
, CFTempNumber(this->codeDirectory(false)->hashType
));
1141 // Deliver any Info.plist only if it looks intact
1144 if (CFDictionaryRef info
= this->infoDictionary())
1145 CFDictionaryAddValue(dict
, kSecCodeInfoPList
, info
);
1146 } catch (...) { } // don't deliver Info.plist if questionable
1149 // kSecCSSigningInformation adds information about signing certificates and chains
1151 if (flags
& kSecCSSigningInformation
)
1153 if (CFArrayRef certs
= this->certificates())
1154 CFDictionaryAddValue(dict
, kSecCodeInfoCertificates
, certs
);
1155 if (CFDataRef sig
= this->signature())
1156 CFDictionaryAddValue(dict
, kSecCodeInfoCMS
, sig
);
1158 CFDictionaryAddValue(dict
, kSecCodeInfoTrust
, mTrust
);
1159 if (CFAbsoluteTime time
= this->signingTime())
1160 if (CFRef
<CFDateRef
> date
= CFDateCreate(NULL
, time
))
1161 CFDictionaryAddValue(dict
, kSecCodeInfoTime
, date
);
1162 if (CFAbsoluteTime time
= this->signingTimestamp())
1163 if (CFRef
<CFDateRef
> date
= CFDateCreate(NULL
, time
))
1164 CFDictionaryAddValue(dict
, kSecCodeInfoTimestamp
, date
);
1168 // kSecCSRequirementInformation adds information on requirements
1170 if (flags
& kSecCSRequirementInformation
)
1172 if (const Requirements
*reqs
= this->internalRequirements()) {
1173 CFDictionaryAddValue(dict
, kSecCodeInfoRequirements
,
1174 CFTempString(Dumper::dump(reqs
)));
1175 CFDictionaryAddValue(dict
, kSecCodeInfoRequirementData
, CFTempData(*reqs
));
1178 const Requirement
*dreq
= this->designatedRequirement();
1179 CFRef
<SecRequirementRef
> dreqRef
= (new SecRequirement(dreq
))->handle();
1180 CFDictionaryAddValue(dict
, kSecCodeInfoDesignatedRequirement
, dreqRef
);
1181 if (this->internalRequirement(kSecDesignatedRequirementType
)) { // explicit
1182 CFRef
<SecRequirementRef
> ddreqRef
= (new SecRequirement(this->defaultDesignatedRequirement(), true))->handle();
1183 CFDictionaryAddValue(dict
, kSecCodeInfoImplicitDesignatedRequirement
, ddreqRef
);
1184 } else { // implicit
1185 CFDictionaryAddValue(dict
, kSecCodeInfoImplicitDesignatedRequirement
, dreqRef
);
1190 if (CFDataRef ent
= this->component(cdEntitlementSlot
)) {
1191 CFDictionaryAddValue(dict
, kSecCodeInfoEntitlements
, ent
);
1192 if (CFDictionaryRef entdict
= this->entitlements())
1193 CFDictionaryAddValue(dict
, kSecCodeInfoEntitlementsDict
, entdict
);
1198 // kSecCSInternalInformation adds internal information meant to be for Apple internal
1199 // use (SPI), and not guaranteed to be stable. Primarily, this is data we want
1200 // to reliably transmit through the API wall so that code outside the Security.framework
1201 // can use it without having to play nasty tricks to get it.
1203 if (flags
& kSecCSInternalInformation
)
1206 CFDictionaryAddValue(dict
, kSecCodeInfoCodeDirectory
, mDir
);
1207 CFDictionaryAddValue(dict
, kSecCodeInfoCodeOffset
, CFTempNumber(mRep
->signingBase()));
1208 if (CFRef
<CFDictionaryRef
> rdict
= getDictionary(cdResourceDirSlot
, false)) // suppress validation
1209 CFDictionaryAddValue(dict
, kSecCodeInfoResourceDirectory
, rdict
);
1214 // kSecCSContentInformation adds more information about the physical layout
1215 // of the signed code. This is (only) useful for packaging or patching-oriented
1218 if (flags
& kSecCSContentInformation
)
1219 if (CFRef
<CFArrayRef
> files
= mRep
->modifiedFiles())
1220 CFDictionaryAddValue(dict
, kSecCodeInfoChangedFiles
, files
);
1222 return dict
.yield();
1227 // Resource validation contexts.
1228 // The default context simply throws a CSError, rudely terminating the operation.
1230 SecStaticCode::ValidationContext::~ValidationContext()
1233 void SecStaticCode::ValidationContext::reportProblem(OSStatus rc
, CFStringRef type
, CFTypeRef value
)
1235 CSError::throwMe(rc
, type
, value
);
1238 void SecStaticCode::CollectingContext::reportProblem(OSStatus rc
, CFStringRef type
, CFTypeRef value
)
1240 if (mStatus
== errSecSuccess
)
1241 mStatus
= rc
; // record first failure for eventual error return
1244 mCollection
.take(makeCFMutableDictionary());
1245 CFMutableArrayRef element
= CFMutableArrayRef(CFDictionaryGetValue(mCollection
, type
));
1247 element
= makeCFMutableArray(0);
1250 CFDictionaryAddValue(mCollection
, type
, element
);
1253 CFArrayAppendValue(element
, value
);
1257 void SecStaticCode::CollectingContext::throwMe()
1259 assert(mStatus
!= errSecSuccess
);
1260 throw CSError(mStatus
, mCollection
.retain());
1265 // Master validation driver.
1266 // This is the static validation (only) driver for the API.
1268 // SecStaticCode exposes an à la carte menu of topical validators applying
1269 // to a given object. The static validation API pulls the together reliably,
1270 // but it also adds two matrix dimensions: architecture (for "fat" Mach-O binaries)
1271 // and nested code. This function will crawl a suitable cross-section of this
1272 // validation matrix based on which options it is given, creating temporary
1273 // SecStaticCode objects on the fly to complete the task.
1274 // (The point, of course, is to do as little duplicate work as possible.)
1276 void SecStaticCode::staticValidate(SecCSFlags flags
, const SecRequirement
*req
)
1278 // core components: once per architecture (if any)
1279 this->staticValidateCore(flags
, req
);
1280 if (flags
& kSecCSCheckAllArchitectures
)
1281 handleOtherArchitectures(^(SecStaticCode
* subcode
) {
1282 subcode
->detachedSignature(this->mDetachedSig
); // carry over explicit (but not implicit) architecture
1283 subcode
->staticValidateCore(flags
, req
);
1286 // resources: once for all architectures
1287 if (!(flags
& kSecCSDoNotValidateResources
))
1288 this->validateResources(flags
);
1290 // allow monitor intervention
1291 if (CFRef
<CFTypeRef
> veto
= reportEvent(CFSTR("validated"), NULL
)) {
1292 if (CFGetTypeID(veto
) == CFNumberGetTypeID())
1293 MacOSError::throwMe(cfNumber
<OSStatus
>(veto
.as
<CFNumberRef
>()));
1295 MacOSError::throwMe(errSecCSBadCallbackValue
);
1299 void SecStaticCode::staticValidateCore(SecCSFlags flags
, const SecRequirement
*req
)
1302 this->validateNonResourceComponents(); // also validates the CodeDirectory
1303 if (!(flags
& kSecCSDoNotValidateExecutable
))
1304 this->validateExecutable();
1306 this->validateRequirement(req
->requirement(), errSecCSReqFailed
);
1307 } catch (CSError
&err
) {
1308 if (Universal
*fat
= this->diskRep()->mainExecutableImage()) // Mach-O
1309 if (MachO
*mach
= fat
->architecture()) {
1310 err
.augment(kSecCFErrorArchitecture
, CFTempString(mach
->architecture().displayName()));
1314 } catch (const MacOSError
&err
) {
1315 // add architecture information if we can get it
1316 if (Universal
*fat
= this->diskRep()->mainExecutableImage())
1317 if (MachO
*mach
= fat
->architecture()) {
1318 CFTempString
arch(mach
->architecture().displayName());
1320 CSError::throwMe(err
.error
, kSecCFErrorArchitecture
, arch
);
1328 // A helper that generates SecStaticCode objects for all but the primary architecture
1329 // of a fat binary and calls a block on them.
1330 // If there's only one architecture (or this is an architecture-agnostic code),
1331 // nothing happens quickly.
1333 void SecStaticCode::handleOtherArchitectures(void (^handle
)(SecStaticCode
* other
))
1335 if (Universal
*fat
= this->diskRep()->mainExecutableImage()) {
1336 Universal::Architectures architectures
;
1337 fat
->architectures(architectures
);
1338 if (architectures
.size() > 1) {
1339 DiskRep::Context ctx
;
1340 size_t activeOffset
= fat
->archOffset();
1341 for (Universal::Architectures::const_iterator arch
= architectures
.begin(); arch
!= architectures
.end(); ++arch
) {
1342 ctx
.offset
= fat
->archOffset(*arch
);
1343 if (ctx
.offset
!= activeOffset
) { // inactive architecture; check it
1344 SecPointer
<SecStaticCode
> subcode
= new SecStaticCode(DiskRep::bestGuess(this->mainExecutablePath(), &ctx
));
1345 subcode
->detachedSignature(this->mDetachedSig
); // carry over explicit (but not implicit) detached signature
1354 } // end namespace CodeSigning
1355 } // end namespace Security