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"
33 #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 // Construct a SecStaticCode object given a disk representation object
61 SecStaticCode::SecStaticCode(DiskRep
*rep
)
63 mValidated(false), mExecutableValidated(false), mResourcesValidated(false), mResourcesValidContext(NULL
),
64 mDesignatedReq(NULL
), mGotResourceBase(false), mEvalDetails(NULL
)
66 CODESIGN_STATIC_CREATE(this, rep
);
67 checkForSystemSignature();
72 // Clean up a SecStaticCode object
74 SecStaticCode::~SecStaticCode() throw()
76 ::free(const_cast<Requirement
*>(mDesignatedReq
));
77 if (mResourcesValidContext
)
78 delete mResourcesValidContext
;
85 // CF-level comparison of SecStaticCode objects compares CodeDirectory hashes if signed,
86 // and falls back on comparing canonical paths if (both are) not.
88 bool SecStaticCode::equal(SecCFObject
&secOther
)
90 SecStaticCode
*other
= static_cast<SecStaticCode
*>(&secOther
);
91 CFDataRef mine
= this->cdHash();
92 CFDataRef his
= other
->cdHash();
94 return mine
&& his
&& CFEqual(mine
, his
);
96 return CFEqual(this->canonicalPath(), other
->canonicalPath());
99 CFHashCode
SecStaticCode::hash()
101 if (CFDataRef h
= this->cdHash())
104 return CFHash(this->canonicalPath());
109 // Attach a detached signature.
111 void SecStaticCode::detachedSignature(CFDataRef sigData
)
114 mRep
= new DetachedRep(sigData
, mRep
->base(), "explicit detached");
115 CODESIGN_STATIC_ATTACH_EXPLICIT(this, mRep
);
118 CODESIGN_STATIC_ATTACH_EXPLICIT(this, NULL
);
124 // Consult the system detached signature database to see if it contains
125 // a detached signature for this StaticCode. If it does, fetch and attach it.
126 // We do this only if the code has no signature already attached.
128 void SecStaticCode::checkForSystemSignature()
130 if (!this->isSigned())
132 if (RefPointer
<DiskRep
> dsig
= signatureDatabase().findCode(mRep
)) {
133 CODESIGN_STATIC_ATTACH_SYSTEM(this, dsig
);
142 // Return a descriptive string identifying the source of the code signature
144 string
SecStaticCode::signatureSource()
148 if (DetachedRep
*rep
= dynamic_cast<DetachedRep
*>(mRep
.get()))
149 return rep
->source();
155 // Do ::required, but convert incoming SecCodeRefs to their SecStaticCodeRefs
158 SecStaticCode
*SecStaticCode::requiredStatic(SecStaticCodeRef ref
)
160 SecCFObject
*object
= SecCFObject::required(ref
, errSecCSInvalidObjectRef
);
161 if (SecStaticCode
*scode
= dynamic_cast<SecStaticCode
*>(object
))
163 else if (SecCode
*code
= dynamic_cast<SecCode
*>(object
))
164 return code
->staticCode();
165 else // neither (a SecSomethingElse)
166 MacOSError::throwMe(errSecCSInvalidObjectRef
);
169 SecCode
*SecStaticCode::optionalDynamic(SecStaticCodeRef ref
)
171 SecCFObject
*object
= SecCFObject::required(ref
, errSecCSInvalidObjectRef
);
172 if (dynamic_cast<SecStaticCode
*>(object
))
174 else if (SecCode
*code
= dynamic_cast<SecCode
*>(object
))
176 else // neither (a SecSomethingElse)
177 MacOSError::throwMe(errSecCSInvalidObjectRef
);
182 // Void all cached validity data.
184 // We also throw out cached components, because the new signature data may have
185 // a different idea of what components should be present. We could reconcile the
186 // cached data instead, if performance seems to be impacted.
188 void SecStaticCode::resetValidity()
190 CODESIGN_EVAL_STATIC_RESET(this);
192 mExecutableValidated
= false;
193 mResourcesValidated
= false;
194 if (mResourcesValidContext
) {
195 delete mResourcesValidContext
;
196 mResourcesValidContext
= NULL
;
200 for (unsigned n
= 0; n
< cdSlotCount
; n
++)
203 mEntitlements
= NULL
;
204 mResourceDict
= NULL
;
205 mDesignatedReq
= NULL
;
206 mGotResourceBase
= false;
212 // we may just have updated the system database, so check again
213 checkForSystemSignature();
218 // Retrieve a sealed component by special slot index.
219 // If the CodeDirectory has already been validated, validate against that.
220 // Otherwise, retrieve the component without validation (but cache it). Validation
221 // will go through the cache and validate all cached components.
223 CFDataRef
SecStaticCode::component(CodeDirectory::SpecialSlot slot
, OSStatus fail
/* = errSecCSSignatureFailed */)
225 assert(slot
<= cdSlotMax
);
227 CFRef
<CFDataRef
> &cache
= mCache
[slot
];
229 if (CFRef
<CFDataRef
> data
= mRep
->component(slot
)) {
230 if (validated()) // if the directory has been validated...
231 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data
), // ... and it's no good
232 CFDataGetLength(data
), -slot
))
233 MacOSError::throwMe(fail
); // ... then bail
234 cache
= data
; // it's okay, cache it
235 } else { // absent, mark so
236 if (validated()) // if directory has been validated...
237 if (codeDirectory()->slotIsPresent(-slot
)) // ... and the slot is NOT missing
238 MacOSError::throwMe(fail
); // was supposed to be there
239 cache
= CFDataRef(kCFNull
); // white lie
242 return (cache
== CFDataRef(kCFNull
)) ? NULL
: cache
.get();
247 // Get the CodeDirectory.
248 // Throws (if check==true) or returns NULL (check==false) if there is none.
249 // Always throws if the CodeDirectory exists but is invalid.
250 // NEVER validates against the signature.
252 const CodeDirectory
*SecStaticCode::codeDirectory(bool check
/* = true */)
255 if (mDir
.take(mRep
->codeDirectory())) {
256 const CodeDirectory
*dir
= reinterpret_cast<const CodeDirectory
*>(CFDataGetBytePtr(mDir
));
257 dir
->checkIntegrity();
261 return reinterpret_cast<const CodeDirectory
*>(CFDataGetBytePtr(mDir
));
263 MacOSError::throwMe(errSecCSUnsigned
);
269 // Get the hash of the CodeDirectory.
270 // Returns NULL if there is none.
272 CFDataRef
SecStaticCode::cdHash()
275 if (const CodeDirectory
*cd
= codeDirectory(false)) {
277 hash(cd
, cd
->length());
280 mCDHash
.take(makeCFData(digest
, sizeof(digest
)));
281 CODESIGN_STATIC_CDHASH(this, digest
, sizeof(digest
));
289 // Return the CMS signature blob; NULL if none found.
291 CFDataRef
SecStaticCode::signature()
294 mSignature
.take(mRep
->signature());
297 MacOSError::throwMe(errSecCSUnsigned
);
302 // Verify the signature on the CodeDirectory.
303 // If this succeeds (doesn't throw), the CodeDirectory is statically trustworthy.
304 // Any outcome (successful or not) is cached for the lifetime of the StaticCode.
306 void SecStaticCode::validateDirectory()
308 // echo previous outcome, if any
311 // perform validation (or die trying)
312 CODESIGN_EVAL_STATIC_DIRECTORY(this);
313 mValidationExpired
= verifySignature();
314 component(cdInfoSlot
, errSecCSInfoPlistFailed
); // force load of Info Dictionary (if any)
315 for (CodeDirectory::SpecialSlot slot
= codeDirectory()->maxSpecialSlot(); slot
>= 1; --slot
)
316 if (mCache
[slot
]) // if we already loaded that resource...
317 validateComponent(slot
); // ... then check it now
318 mValidated
= true; // we've done the deed...
319 mValidationResult
= noErr
; // ... and it was good
320 } catch (const CommonError
&err
) {
322 mValidationResult
= err
.osStatus();
325 secdebug("staticCode", "%p validation threw non-common exception", this);
327 mValidationResult
= errSecCSInternalError
;
331 if (mValidationResult
== noErr
) {
332 if (mValidationExpired
)
333 if ((apiFlags() & kSecCSConsiderExpiration
)
334 || (codeDirectory()->flags
& kSecCodeSignatureForceExpiration
))
335 MacOSError::throwMe(CSSMERR_TP_CERT_EXPIRED
);
337 MacOSError::throwMe(mValidationResult
);
342 // Load and validate the CodeDirectory and all components *except* those related to the resource envelope.
343 // Those latter components are checked by validateResources().
345 void SecStaticCode::validateNonResourceComponents()
347 this->validateDirectory();
348 for (CodeDirectory::SpecialSlot slot
= codeDirectory()->maxSpecialSlot(); slot
>= 1; --slot
)
350 case cdResourceDirSlot
: // validated by validateResources
353 this->component(slot
); // loads and validates
360 // Get the (signed) signing date from the code signature.
361 // Sadly, we need to validate the signature to get the date (as a side benefit).
362 // This means that you can't get the signing time for invalidly signed code.
364 // We could run the decoder "almost to" verification to avoid this, but there seems
365 // little practical point to such a duplication of effort.
367 CFAbsoluteTime
SecStaticCode::signingTime()
373 CFAbsoluteTime
SecStaticCode::signingTimestamp()
376 return mSigningTimestamp
;
381 // Verify the CMS signature on the CodeDirectory.
382 // This performs the cryptographic tango. It returns if the signature is valid,
383 // or throws if it is not. As a side effect, a successful return sets up the
384 // cached certificate chain for future use.
385 // Returns true if the signature is expired (the X.509 sense), false if it's not.
386 // Expiration is fatal (throws) if a secure timestamp is included, but not otherwise.
388 bool SecStaticCode::verifySignature()
390 // ad-hoc signed code is considered validly signed by definition
391 if (flag(kSecCodeSignatureAdhoc
)) {
392 CODESIGN_EVAL_STATIC_SIGNATURE_ADHOC(this);
396 DTRACK(CODESIGN_EVAL_STATIC_SIGNATURE
, this, (char*)this->mainExecutablePath().c_str());
398 // decode CMS and extract SecTrust for verification
399 CFRef
<CMSDecoderRef
> cms
;
400 MacOSError::check(CMSDecoderCreate(&cms
.aref())); // create decoder
401 CFDataRef sig
= this->signature();
402 MacOSError::check(CMSDecoderUpdateMessage(cms
, CFDataGetBytePtr(sig
), CFDataGetLength(sig
)));
403 this->codeDirectory(); // load CodeDirectory (sets mDir)
404 MacOSError::check(CMSDecoderSetDetachedContent(cms
, mDir
));
405 MacOSError::check(CMSDecoderFinalizeMessage(cms
));
406 MacOSError::check(CMSDecoderSetSearchKeychain(cms
, cfEmptyArray()));
407 CFRef
<CFTypeRef
> policy
= verificationPolicy(apiFlags());
408 CMSSignerStatus status
;
409 MacOSError::check(CMSDecoderCopySignerStatus(cms
, 0, policy
,
410 false, &status
, &mTrust
.aref(), NULL
));
411 if (status
!= kCMSSignerValid
)
412 MacOSError::throwMe(errSecCSSignatureFailed
);
414 // internal signing time (as specified by the signer; optional)
415 mSigningTime
= 0; // "not present" marker (nobody could code sign on Jan 1, 2001 :-)
416 switch (OSStatus rc
= CMSDecoderCopySignerSigningTime(cms
, 0, &mSigningTime
)) {
418 case errSecSigningTimeMissing
:
421 MacOSError::throwMe(rc
);
424 // certified signing time (as specified by a TSA; optional)
425 mSigningTimestamp
= 0;
426 switch (OSStatus rc
= CMSDecoderCopySignerTimestamp(cms
, 0, &mSigningTimestamp
)) {
428 case errSecTimestampMissing
:
431 MacOSError::throwMe(rc
);
434 // set up the environment for SecTrust
435 MacOSError::check(SecTrustSetAnchorCertificates(mTrust
, cfEmptyArray())); // no anchors
436 MacOSError::check(SecTrustSetKeychains(mTrust
, cfEmptyArray())); // no keychains
437 CSSM_APPLE_TP_ACTION_DATA actionData
= {
438 CSSM_APPLE_TP_ACTION_VERSION
, // version of data structure
439 CSSM_TP_ACTION_IMPLICIT_ANCHORS
// action flags
442 for (;;) { // at most twice
443 MacOSError::check(SecTrustSetParameters(mTrust
,
444 CSSM_TP_ACTION_DEFAULT
, CFTempData(&actionData
, sizeof(actionData
))));
446 // evaluate trust and extract results
447 SecTrustResultType trustResult
;
448 MacOSError::check(SecTrustEvaluate(mTrust
, &trustResult
));
449 MacOSError::check(SecTrustGetResult(mTrust
, &trustResult
, &mCertChain
.aref(), &mEvalDetails
));
450 CODESIGN_EVAL_STATIC_SIGNATURE_RESULT(this, trustResult
, mCertChain
? CFArrayGetCount(mCertChain
) : 0);
451 switch (trustResult
) {
452 case kSecTrustResultProceed
:
453 case kSecTrustResultUnspecified
:
455 case kSecTrustResultDeny
:
456 MacOSError::throwMe(CSSMERR_APPLETP_TRUST_SETTING_DENY
); // user reject
457 case kSecTrustResultInvalid
:
458 assert(false); // should never happen
459 MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED
);
463 MacOSError::check(SecTrustGetCssmResultCode(mTrust
, &result
));
464 // if we have a valid timestamp, CMS validates against (that) signing time and all is well.
465 // If we don't have one, may validate against *now*, and must be able to tolerate expiration.
466 if (mSigningTimestamp
== 0) // no timestamp available
467 if (((result
== CSSMERR_TP_CERT_EXPIRED
) || (result
== CSSMERR_TP_CERT_NOT_VALID_YET
))
468 && !(actionData
.ActionFlags
& CSSM_TP_ACTION_ALLOW_EXPIRED
)) {
469 CODESIGN_EVAL_STATIC_SIGNATURE_EXPIRED(this);
470 actionData
.ActionFlags
|= CSSM_TP_ACTION_ALLOW_EXPIRED
; // (this also allows postdated certs)
471 continue; // retry validation while tolerating expiration
473 MacOSError::throwMe(result
);
477 if (mSigningTimestamp
) {
478 CFIndex rootix
= CFArrayGetCount(mCertChain
);
479 if (SecCertificateRef mainRoot
= SecCertificateRef(CFArrayGetValueAtIndex(mCertChain
, rootix
-1)))
480 if (isAppleCA(mainRoot
)) {
481 // impose policy: if the signature itself draws to Apple, then so must the timestamp signature
482 CFRef
<CFArrayRef
> tsCerts
;
483 MacOSError::check(CMSDecoderCopySignerTimestampCertificates(cms
, 0, &tsCerts
.aref()));
484 CFIndex tsn
= CFArrayGetCount(tsCerts
);
485 bool good
= tsn
> 0 && isAppleCA(SecCertificateRef(CFArrayGetValueAtIndex(tsCerts
, tsn
-1)));
486 #ifndef WORKAROUND_12007637
487 // TS certificates are reordered weirdly; check them all
488 for (CFIndex n
= 0; n
< tsn
; n
++)
489 if (SecCertificateRef tsRoot
= SecCertificateRef(CFArrayGetValueAtIndex(tsCerts
, n
)))
490 if ((good
= isAppleCA(tsRoot
))) {
491 secdebug("BUG", "Apple root at TS cert %ld", n
);
494 #endif //WORKAROUND_12007637
496 MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED
);
500 return actionData
.ActionFlags
& CSSM_TP_ACTION_ALLOW_EXPIRED
;
506 // Return the TP policy used for signature verification.
507 // This may be a simple SecPolicyRef or a CFArray of policies.
508 // The caller owns the return value.
510 static SecPolicyRef
makeCRLPolicy()
512 CFRef
<SecPolicyRef
> policy
;
513 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3
, &CSSMOID_APPLE_TP_REVOCATION_CRL
, &policy
.aref()));
514 CSSM_APPLE_TP_CRL_OPTIONS options
;
515 memset(&options
, 0, sizeof(options
));
516 options
.Version
= CSSM_APPLE_TP_CRL_OPTS_VERSION
;
517 options
.CrlFlags
= CSSM_TP_ACTION_FETCH_CRL_FROM_NET
| CSSM_TP_ACTION_CRL_SUFFICIENT
;
518 CSSM_DATA optData
= { sizeof(options
), (uint8
*)&options
};
519 MacOSError::check(SecPolicySetValue(policy
, &optData
));
520 return policy
.yield();
523 static SecPolicyRef
makeOCSPPolicy()
525 CFRef
<SecPolicyRef
> policy
;
526 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3
, &CSSMOID_APPLE_TP_REVOCATION_OCSP
, &policy
.aref()));
527 CSSM_APPLE_TP_OCSP_OPTIONS options
;
528 memset(&options
, 0, sizeof(options
));
529 options
.Version
= CSSM_APPLE_TP_OCSP_OPTS_VERSION
;
530 options
.Flags
= CSSM_TP_ACTION_OCSP_SUFFICIENT
;
531 CSSM_DATA optData
= { sizeof(options
), (uint8
*)&options
};
532 MacOSError::check(SecPolicySetValue(policy
, &optData
));
533 return policy
.yield();
536 CFTypeRef
SecStaticCode::verificationPolicy(SecCSFlags flags
)
538 CFRef
<SecPolicyRef
> core
;
539 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3
,
540 &CSSMOID_APPLE_TP_CODE_SIGNING
, &core
.aref()));
541 if (flags
& kSecCSEnforceRevocationChecks
) {
542 CFRef
<SecPolicyRef
> crl
= makeCRLPolicy();
543 CFRef
<SecPolicyRef
> ocsp
= makeOCSPPolicy();
544 return makeCFArray(3, core
.get(), crl
.get(), ocsp
.get());
552 // Validate a particular sealed, cached resource against its (special) CodeDirectory slot.
553 // The resource must already have been placed in the cache.
554 // This does NOT perform basic validation.
556 void SecStaticCode::validateComponent(CodeDirectory::SpecialSlot slot
, OSStatus fail
/* = errSecCSSignatureFailed */)
558 assert(slot
<= cdSlotMax
);
559 CFDataRef data
= mCache
[slot
];
560 assert(data
); // must be cached
561 if (data
== CFDataRef(kCFNull
)) {
562 if (codeDirectory()->slotIsPresent(-slot
)) // was supposed to be there...
563 MacOSError::throwMe(fail
); // ... and is missing
565 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data
), CFDataGetLength(data
), -slot
))
566 MacOSError::throwMe(fail
);
572 // Perform static validation of the main executable.
573 // This reads the main executable from disk and validates it against the
574 // CodeDirectory code slot array.
575 // Note that this is NOT an in-memory validation, and is thus potentially
576 // subject to timing attacks.
578 void SecStaticCode::validateExecutable()
580 if (!validatedExecutable()) {
582 DTRACK(CODESIGN_EVAL_STATIC_EXECUTABLE
, this,
583 (char*)this->mainExecutablePath().c_str(), codeDirectory()->nCodeSlots
);
584 const CodeDirectory
*cd
= this->codeDirectory();
586 MacOSError::throwMe(errSecCSUnsigned
);
587 AutoFileDesc
fd(mainExecutablePath(), O_RDONLY
);
588 fd
.fcntl(F_NOCACHE
, true); // turn off page caching (one-pass)
589 if (Universal
*fat
= mRep
->mainExecutableImage())
590 fd
.seek(fat
->archOffset());
591 size_t pageSize
= cd
->pageSize
? (1 << cd
->pageSize
) : 0;
592 size_t remaining
= cd
->codeLimit
;
593 for (size_t slot
= 0; slot
< cd
->nCodeSlots
; ++slot
) {
594 size_t size
= min(remaining
, pageSize
);
595 if (!cd
->validateSlot(fd
, size
, slot
)) {
596 CODESIGN_EVAL_STATIC_EXECUTABLE_FAIL(this, slot
);
597 MacOSError::throwMe(errSecCSSignatureFailed
);
601 mExecutableValidated
= true;
602 mExecutableValidResult
= noErr
;
603 } catch (const CommonError
&err
) {
604 mExecutableValidated
= true;
605 mExecutableValidResult
= err
.osStatus();
608 secdebug("staticCode", "%p executable validation threw non-common exception", this);
609 mExecutableValidated
= true;
610 mExecutableValidResult
= errSecCSInternalError
;
614 assert(validatedExecutable());
615 if (mExecutableValidResult
!= noErr
)
616 MacOSError::throwMe(mExecutableValidResult
);
621 // Perform static validation of sealed resources.
623 // This performs a whole-code static resource scan and effectively
624 // computes a concordance between what's on disk and what's in the ResourceDirectory.
625 // Any unsanctioned difference causes an error.
627 void SecStaticCode::validateResources()
629 if (!validatedResources()) {
632 CFDictionaryRef sealedResources
= resourceDictionary();
633 if (this->resourceBase()) // disk has resources
635 /* go to work below */;
637 MacOSError::throwMe(errSecCSResourcesNotFound
);
638 else // disk has no resources
640 MacOSError::throwMe(errSecCSResourcesNotFound
);
642 return; // no resources, not sealed - fine (no work)
644 // found resources, and they are sealed
645 CFDictionaryRef rules
= cfget
<CFDictionaryRef
>(sealedResources
, "rules");
646 CFDictionaryRef files
= cfget
<CFDictionaryRef
>(sealedResources
, "files");
647 DTRACK(CODESIGN_EVAL_STATIC_RESOURCES
, this,
648 (char*)this->mainExecutablePath().c_str(), int(CFDictionaryGetCount(files
)));
650 // make a shallow copy of the ResourceDirectory so we can "check off" what we find
651 CFRef
<CFMutableDictionaryRef
> resourceMap
= makeCFMutableDictionary(files
);
653 // scan through the resources on disk, checking each against the resourceDirectory
654 mResourcesValidContext
= new CollectingContext(*this); // collect all failures in here
655 ResourceBuilder
resources(cfString(this->resourceBase()), rules
, codeDirectory()->hashType
);
656 mRep
->adjustResources(resources
);
658 ResourceBuilder::Rule
*rule
;
660 while (resources
.next(path
, rule
)) {
661 validateResource(path
, *mResourcesValidContext
);
662 CFDictionaryRemoveValue(resourceMap
, CFTempString(path
));
665 if (CFDictionaryGetCount(resourceMap
) > 0) {
666 secdebug("staticCode", "%p sealed resource(s) not found in code", this);
667 CFDictionaryApplyFunction(resourceMap
, SecStaticCode::checkOptionalResource
, mResourcesValidContext
);
670 // now check for any errors found in the reporting context
671 mResourcesValidated
= true;
672 if (mResourcesValidContext
->osStatus() != noErr
)
673 mResourcesValidContext
->throwMe();
675 } catch (const CommonError
&err
) {
676 mResourcesValidated
= true;
677 mResourcesValidResult
= err
.osStatus();
680 secdebug("staticCode", "%p executable validation threw non-common exception", this);
681 mResourcesValidated
= true;
682 mResourcesValidResult
= errSecCSInternalError
;
686 assert(validatedResources());
687 if (mResourcesValidResult
)
688 MacOSError::throwMe(mResourcesValidResult
);
689 if (mResourcesValidContext
->osStatus() != noErr
)
690 mResourcesValidContext
->throwMe();
694 void SecStaticCode::checkOptionalResource(CFTypeRef key
, CFTypeRef value
, void *context
)
696 CollectingContext
*ctx
= static_cast<CollectingContext
*>(context
);
697 ResourceSeal
seal(value
);
698 if (!seal
.optional()) {
699 if (key
&& CFGetTypeID(key
) == CFStringGetTypeID()) {
700 ctx
->reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
,
701 CFTempURL(CFStringRef(key
), false, ctx
->code
.resourceBase()));
703 ctx
->reportProblem(errSecCSBadResource
, kSecCFErrorResourceSeal
, key
);
710 // Load, validate, cache, and return CFDictionary forms of sealed resources.
712 CFDictionaryRef
SecStaticCode::infoDictionary()
715 mInfoDict
.take(getDictionary(cdInfoSlot
, errSecCSInfoPlistFailed
));
716 secdebug("staticCode", "%p loaded InfoDict %p", this, mInfoDict
.get());
721 CFDictionaryRef
SecStaticCode::entitlements()
723 if (!mEntitlements
) {
725 if (CFDataRef entitlementData
= component(cdEntitlementSlot
)) {
726 validateComponent(cdEntitlementSlot
);
727 const EntitlementBlob
*blob
= reinterpret_cast<const EntitlementBlob
*>(CFDataGetBytePtr(entitlementData
));
728 if (blob
->validateBlob()) {
729 mEntitlements
.take(blob
->entitlements());
730 secdebug("staticCode", "%p loaded Entitlements %p", this, mEntitlements
.get());
732 // we do not consider a different blob type to be an error. We think it's a new format we don't understand
735 return mEntitlements
;
738 CFDictionaryRef
SecStaticCode::resourceDictionary()
740 if (mResourceDict
) // cached
741 return mResourceDict
;
742 if (CFRef
<CFDictionaryRef
> dict
= getDictionary(cdResourceDirSlot
, errSecCSSignatureFailed
))
743 if (cfscan(dict
, "{rules=%Dn,files=%Dn}")) {
744 secdebug("staticCode", "%p loaded ResourceDict %p",
745 this, mResourceDict
.get());
746 return mResourceDict
= dict
;
754 // Load and cache the resource directory base.
755 // Note that the base is optional for each DiskRep.
757 CFURLRef
SecStaticCode::resourceBase()
759 if (!mGotResourceBase
) {
760 string base
= mRep
->resourcesRootPath();
762 mResourceBase
.take(makeCFURL(base
, true));
763 mGotResourceBase
= true;
765 return mResourceBase
;
770 // Load a component, validate it, convert it to a CFDictionary, and return that.
771 // This will force load and validation, which means that it will perform basic
772 // validation if it hasn't been done yet.
774 CFDictionaryRef
SecStaticCode::getDictionary(CodeDirectory::SpecialSlot slot
, OSStatus fail
/* = errSecCSSignatureFailed */)
777 if (CFDataRef infoData
= component(slot
, fail
)) {
778 validateComponent(slot
, fail
);
779 if (CFDictionaryRef dict
= makeCFDictionaryFrom(infoData
))
782 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
789 // Load, validate, and return a sealed resource.
790 // The resource data (loaded in to memory as a blob) is returned and becomes
791 // the responsibility of the caller; it is NOT cached by SecStaticCode.
793 // A resource that is not sealed will not be returned, and an error will be thrown.
794 // A missing resource will cause an error unless it's marked optional in the Directory.
795 // Under no circumstances will a corrupt resource be returned.
796 // NULL will only be returned for a resource that is neither sealed nor present
797 // (or that is sealed, absent, and marked optional).
798 // If the ResourceDictionary itself is not sealed, this function will always fail.
800 // There is currently no interface for partial retrieval of the resource data.
801 // (Since the ResourceDirectory does not currently support segmentation, all the
802 // data would have to be read anyway, but it could be read into a reusable buffer.)
804 CFDataRef
SecStaticCode::resource(string path
, ValidationContext
&ctx
)
806 if (CFDictionaryRef rdict
= resourceDictionary()) {
807 if (CFTypeRef file
= cfget(rdict
, "files.%s", path
.c_str())) {
808 ResourceSeal seal
= file
;
809 if (!resourceBase()) // no resources in DiskRep
810 MacOSError::throwMe(errSecCSResourcesNotFound
);
811 CFRef
<CFURLRef
> fullpath
= makeCFURL(path
, false, resourceBase());
812 if (CFRef
<CFDataRef
> data
= cfLoadFile(fullpath
)) {
813 MakeHash
<CodeDirectory
> hasher(this->codeDirectory());
814 hasher
->update(CFDataGetBytePtr(data
), CFDataGetLength(data
));
815 if (hasher
->verify(seal
.hash()))
816 return data
.yield(); // good
818 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAltered
, fullpath
); // altered
820 if (!seal
.optional())
821 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
, fullpath
); // was sealed but is now missing
823 return NULL
; // validly missing
826 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAdded
, CFTempURL(path
, false, resourceBase()));
829 MacOSError::throwMe(errSecCSResourcesNotSealed
);
832 CFDataRef
SecStaticCode::resource(string path
)
834 ValidationContext ctx
;
835 return resource(path
, ctx
);
839 void SecStaticCode::validateResource(string path
, ValidationContext
&ctx
)
841 if (CFDictionaryRef rdict
= resourceDictionary()) {
842 if (CFTypeRef file
= cfget(rdict
, "files.%s", path
.c_str())) {
843 ResourceSeal seal
= file
;
844 if (!resourceBase()) // no resources in DiskRep
845 MacOSError::throwMe(errSecCSResourcesNotFound
);
846 CFRef
<CFURLRef
> fullpath
= makeCFURL(path
, false, resourceBase());
847 AutoFileDesc
fd(cfString(fullpath
), O_RDONLY
, FileDesc::modeMissingOk
); // open optional filee
849 MakeHash
<CodeDirectory
> hasher(this->codeDirectory());
850 hashFileData(fd
, hasher
.get());
851 if (hasher
->verify(seal
.hash()))
852 return; // verify good
854 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAltered
, fullpath
); // altered
856 if (!seal
.optional())
857 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
, fullpath
); // was sealed but is now missing
859 return; // validly missing
862 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAdded
, CFTempURL(path
, false, resourceBase()));
864 MacOSError::throwMe(errSecCSResourcesNotSealed
);
869 // Test a CodeDirectory flag.
870 // Returns false if there is no CodeDirectory.
871 // May throw if the CodeDirectory is present but somehow invalid.
873 bool SecStaticCode::flag(uint32_t tested
)
875 if (const CodeDirectory
*cd
= this->codeDirectory(false))
876 return cd
->flags
& tested
;
883 // Retrieve the full SuperBlob containing all internal requirements.
885 const Requirements
*SecStaticCode::internalRequirements()
887 if (CFDataRef req
= component(cdRequirementsSlot
))
888 return (const Requirements
*)CFDataGetBytePtr(req
);
895 // Retrieve a particular internal requirement by type.
897 const Requirement
*SecStaticCode::internalRequirement(SecRequirementType type
)
899 if (const Requirements
*reqs
= internalRequirements())
900 return reqs
->find
<Requirement
>(type
);
907 // Return the Designated Requirement (DR). This can be either explicit in the
908 // Internal Requirements component, or implicitly generated on demand here.
909 // Note that an explicit DR may have been implicitly generated at signing time;
910 // we don't distinguish this case.
912 const Requirement
*SecStaticCode::designatedRequirement()
914 if (const Requirement
*req
= internalRequirement(kSecDesignatedRequirementType
)) {
915 return req
; // explicit in signing data
918 mDesignatedReq
= defaultDesignatedRequirement();
919 return mDesignatedReq
;
925 // Generate the default Designated Requirement (DR) for this StaticCode.
926 // Ignore any explicit DR it may contain.
928 const Requirement
*SecStaticCode::defaultDesignatedRequirement()
930 if (flag(kSecCodeSignatureAdhoc
)) {
931 // adhoc signature: return a plain cdhash requirement
932 Requirement::Maker maker
;
934 hash(codeDirectory(), codeDirectory()->length());
937 maker
.cdhash(digest
);
940 // full signature: Gin up full context and let DRMaker do its thing
941 validateDirectory(); // need the cert chain
942 Requirement::Context
context(this->certificates(),
943 this->infoDictionary(),
944 this->entitlements(),
946 this->codeDirectory()
948 return DRMaker(context
).make();
954 // Validate a SecStaticCode against the internal requirement of a particular type.
956 void SecStaticCode::validateRequirements(SecRequirementType type
, SecStaticCode
*target
,
957 OSStatus nullError
/* = noErr */)
959 DTRACK(CODESIGN_EVAL_STATIC_INTREQ
, this, type
, target
, nullError
);
960 if (const Requirement
*req
= internalRequirement(type
))
961 target
->validateRequirement(req
, nullError
? nullError
: errSecCSReqFailed
);
963 MacOSError::throwMe(nullError
);
970 // Validate this StaticCode against an external Requirement
972 bool SecStaticCode::satisfiesRequirement(const Requirement
*req
, OSStatus failure
)
976 return req
->validates(Requirement::Context(mCertChain
, infoDictionary(), entitlements(), codeDirectory()->identifier(), codeDirectory()), failure
);
979 void SecStaticCode::validateRequirement(const Requirement
*req
, OSStatus failure
)
981 if (!this->satisfiesRequirement(req
, failure
))
982 MacOSError::throwMe(failure
);
987 // Retrieve one certificate from the cert chain.
988 // Positive and negative indices can be used:
989 // [ leaf, intermed-1, ..., intermed-n, anchor ]
991 // Returns NULL if unavailable for any reason.
993 SecCertificateRef
SecStaticCode::cert(int ix
)
995 validateDirectory(); // need cert chain
997 CFIndex length
= CFArrayGetCount(mCertChain
);
1000 if (ix
>= 0 && ix
< length
)
1001 return SecCertificateRef(CFArrayGetValueAtIndex(mCertChain
, ix
));
1006 CFArrayRef
SecStaticCode::certificates()
1008 validateDirectory(); // need cert chain
1014 // Gather (mostly) API-official information about this StaticCode.
1016 // This method lives in the twilight between the API and internal layers,
1017 // since it generates API objects (Sec*Refs) for return.
1019 CFDictionaryRef
SecStaticCode::signingInformation(SecCSFlags flags
)
1022 // Start with the pieces that we return even for unsigned code.
1023 // This makes Sec[Static]CodeRefs useful as API-level replacements
1024 // of our internal OSXCode objects.
1026 CFRef
<CFMutableDictionaryRef
> dict
= makeCFMutableDictionary(1,
1027 kSecCodeInfoMainExecutable
, CFTempURL(this->mainExecutablePath()).get()
1031 // If we're not signed, this is all you get
1033 if (!this->isSigned())
1034 return dict
.yield();
1037 // Add the generic attributes that we always include
1039 CFDictionaryAddValue(dict
, kSecCodeInfoIdentifier
, CFTempString(this->identifier()));
1040 CFDictionaryAddValue(dict
, kSecCodeInfoFormat
, CFTempString(this->format()));
1041 CFDictionaryAddValue(dict
, kSecCodeInfoSource
, CFTempString(this->signatureSource()));
1042 CFDictionaryAddValue(dict
, kSecCodeInfoUnique
, this->cdHash());
1043 CFDictionaryAddValue(dict
, kSecCodeInfoDigestAlgorithm
, CFTempNumber(this->codeDirectory(false)->hashType
));
1046 // Deliver any Info.plist only if it looks intact
1049 if (CFDictionaryRef info
= this->infoDictionary())
1050 CFDictionaryAddValue(dict
, kSecCodeInfoPList
, info
);
1051 } catch (...) { } // don't deliver Info.plist if questionable
1054 // kSecCSSigningInformation adds information about signing certificates and chains
1056 if (flags
& kSecCSSigningInformation
) {
1057 if (CFArrayRef certs
= this->certificates())
1058 CFDictionaryAddValue(dict
, kSecCodeInfoCertificates
, certs
);
1059 if (CFDataRef sig
= this->signature())
1060 CFDictionaryAddValue(dict
, kSecCodeInfoCMS
, sig
);
1062 CFDictionaryAddValue(dict
, kSecCodeInfoTrust
, mTrust
);
1063 if (CFAbsoluteTime time
= this->signingTime())
1064 if (CFRef
<CFDateRef
> date
= CFDateCreate(NULL
, time
))
1065 CFDictionaryAddValue(dict
, kSecCodeInfoTime
, date
);
1066 if (CFAbsoluteTime time
= this->signingTimestamp())
1067 if (CFRef
<CFDateRef
> date
= CFDateCreate(NULL
, time
))
1068 CFDictionaryAddValue(dict
, kSecCodeInfoTimestamp
, date
);
1072 // kSecCSRequirementInformation adds information on requirements
1074 if (flags
& kSecCSRequirementInformation
) {
1075 if (const Requirements
*reqs
= this->internalRequirements()) {
1076 CFDictionaryAddValue(dict
, kSecCodeInfoRequirements
,
1077 CFTempString(Dumper::dump(reqs
)));
1078 CFDictionaryAddValue(dict
, kSecCodeInfoRequirementData
, CFTempData(*reqs
));
1081 const Requirement
*dreq
= this->designatedRequirement();
1082 CFRef
<SecRequirementRef
> dreqRef
= (new SecRequirement(dreq
))->handle();
1083 CFDictionaryAddValue(dict
, kSecCodeInfoDesignatedRequirement
, dreqRef
);
1084 if (this->internalRequirement(kSecDesignatedRequirementType
)) { // explicit
1085 CFRef
<SecRequirementRef
> ddreqRef
= (new SecRequirement(this->defaultDesignatedRequirement(), true))->handle();
1086 CFDictionaryAddValue(dict
, kSecCodeInfoImplicitDesignatedRequirement
, ddreqRef
);
1087 } else { // implicit
1088 CFDictionaryAddValue(dict
, kSecCodeInfoImplicitDesignatedRequirement
, dreqRef
);
1091 if (CFDataRef ent
= this->component(cdEntitlementSlot
)) {
1092 CFDictionaryAddValue(dict
, kSecCodeInfoEntitlements
, ent
);
1093 if (CFDictionaryRef entdict
= this->entitlements())
1094 CFDictionaryAddValue(dict
, kSecCodeInfoEntitlementsDict
, entdict
);
1099 // kSecCSInternalInformation adds internal information meant to be for Apple internal
1100 // use (SPI), and not guaranteed to be stable. Primarily, this is data we want
1101 // to reliably transmit through the API wall so that code outside the Security.framework
1102 // can use it without having to play nasty tricks to get it.
1104 if (flags
& kSecCSInternalInformation
) {
1106 CFDictionaryAddValue(dict
, kSecCodeInfoCodeDirectory
, mDir
);
1107 CFDictionaryAddValue(dict
, kSecCodeInfoCodeOffset
, CFTempNumber(mRep
->signingBase()));
1108 if (CFDictionaryRef resources
= resourceDictionary())
1109 CFDictionaryAddValue(dict
, kSecCodeInfoResourceDirectory
, resources
);
1114 // kSecCSContentInformation adds more information about the physical layout
1115 // of the signed code. This is (only) useful for packaging or patching-oriented
1118 if (flags
& kSecCSContentInformation
)
1119 if (CFRef
<CFArrayRef
> files
= mRep
->modifiedFiles())
1120 CFDictionaryAddValue(dict
, kSecCodeInfoChangedFiles
, files
);
1122 return dict
.yield();
1127 // Resource validation contexts.
1128 // The default context simply throws a CSError, rudely terminating the operation.
1130 SecStaticCode::ValidationContext::~ValidationContext()
1133 void SecStaticCode::ValidationContext::reportProblem(OSStatus rc
, CFStringRef type
, CFTypeRef value
)
1135 CSError::throwMe(rc
, type
, value
);
1138 void SecStaticCode::CollectingContext::reportProblem(OSStatus rc
, CFStringRef type
, CFTypeRef value
)
1140 if (mStatus
== noErr
)
1141 mStatus
= rc
; // record first failure for eventual error return
1144 mCollection
.take(makeCFMutableDictionary());
1145 CFMutableArrayRef element
= CFMutableArrayRef(CFDictionaryGetValue(mCollection
, type
));
1147 element
= makeCFMutableArray(0);
1150 CFDictionaryAddValue(mCollection
, type
, element
);
1153 CFArrayAppendValue(element
, value
);
1157 void SecStaticCode::CollectingContext::throwMe()
1159 assert(mStatus
!= noErr
);
1160 throw CSError(mStatus
, mCollection
.retain());
1165 // SecStaticCode::AllArchitectures produces SecStaticCode objects separately
1166 // for each architecture represented by a base object.
1168 // Performance note: This is a simple, straight-forward implementation that
1169 // does not heroically try to share resources between the code objects produced.
1170 // In practice, this means we'll re-open files and re-read resource files.
1171 // In exchange, we enter all the code paths in the normal way, and do not have
1172 // special sharing paths to worry about.
1173 // If a performance tool brings you here because you have *proof* of a performance
1174 // problem, consider digging up MachO and Universal (for sharing file descriptors),
1175 // and SecStaticCode (for sharing resource iterators). That ought to cover most of
1176 // the big chunks. If you're just offended by the simplicity of this implementation,
1177 // go play somewhere else.
1179 SecStaticCode::AllArchitectures::AllArchitectures(SecStaticCode
*code
)
1182 if (Universal
*fat
= code
->diskRep()->mainExecutableImage()) {
1183 fat
->architectures(mArchitectures
);
1184 mCurrent
= mArchitectures
.begin();
1187 mState
= firstNonFat
;
1191 SecStaticCode
*SecStaticCode::AllArchitectures::operator () ()
1199 if (mCurrent
== mArchitectures
.end())
1201 Architecture arch
= *mCurrent
++;
1202 if (arch
== mBase
->diskRep()->mainExecutableImage()->bestNativeArch()) {
1205 DiskRep::Context ctx
;
1207 return new SecStaticCode(DiskRep::bestGuess(mBase
->mainExecutablePath(), &ctx
));
1216 } // end namespace CodeSigning
1217 } // end namespace Security