2 * Copyright (c) 2006-2010 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"
30 #include "reqdumper.h"
32 #include "resources.h"
34 #include "detachedrep.h"
35 #include "csdatabase.h"
36 #include "csutilities.h"
37 #include <CoreFoundation/CFURLAccess.h>
38 #include <Security/SecPolicyPriv.h>
39 #include <Security/SecTrustPriv.h>
40 #include <Security/SecCertificatePriv.h>
41 #include <Security/CMSPrivate.h>
42 #include <Security/SecCmsContentInfo.h>
43 #include <Security/SecCmsSignerInfo.h>
44 #include <Security/SecCmsSignedData.h>
45 #include <Security/cssmapplePriv.h>
46 #include <security_utilities/unix++.h>
47 #include <security_utilities/cfmunge.h>
48 #include <Security/CMSDecoder.h>
52 namespace CodeSigning
{
54 using namespace UnixPlusPlus
;
58 // Construct a SecStaticCode object given a disk representation object
60 SecStaticCode::SecStaticCode(DiskRep
*rep
)
62 mValidated(false), mExecutableValidated(false), mResourcesValidated(false), mResourcesValidContext(NULL
),
63 mDesignatedReq(NULL
), mGotResourceBase(false), mEvalDetails(NULL
)
65 CODESIGN_STATIC_CREATE(this, rep
);
66 checkForSystemSignature();
71 // Clean up a SecStaticCode object
73 SecStaticCode::~SecStaticCode() throw()
75 ::free(const_cast<Requirement
*>(mDesignatedReq
));
76 if (mResourcesValidContext
)
77 delete mResourcesValidContext
;
84 // CF-level comparison of SecStaticCode objects compares CodeDirectory hashes if signed,
85 // and falls back on comparing canonical paths if (both are) not.
87 bool SecStaticCode::equal(SecCFObject
&secOther
)
89 SecStaticCode
*other
= static_cast<SecStaticCode
*>(&secOther
);
90 CFDataRef mine
= this->cdHash();
91 CFDataRef his
= other
->cdHash();
93 return mine
&& his
&& CFEqual(mine
, his
);
95 return CFEqual(this->canonicalPath(), other
->canonicalPath());
98 CFHashCode
SecStaticCode::hash()
100 if (CFDataRef h
= this->cdHash())
103 return CFHash(this->canonicalPath());
108 // Attach a detached signature.
110 void SecStaticCode::detachedSignature(CFDataRef sigData
)
113 mRep
= new DetachedRep(sigData
, mRep
->base(), "explicit detached");
114 CODESIGN_STATIC_ATTACH_EXPLICIT(this, mRep
);
117 CODESIGN_STATIC_ATTACH_EXPLICIT(this, NULL
);
123 // Consult the system detached signature database to see if it contains
124 // a detached signature for this StaticCode. If it does, fetch and attach it.
125 // We do this only if the code has no signature already attached.
127 void SecStaticCode::checkForSystemSignature()
129 if (!this->isSigned())
131 if (RefPointer
<DiskRep
> dsig
= signatureDatabase().findCode(mRep
)) {
132 CODESIGN_STATIC_ATTACH_SYSTEM(this, dsig
);
141 // Return a descriptive string identifying the source of the code signature
143 string
SecStaticCode::signatureSource()
147 if (DetachedRep
*rep
= dynamic_cast<DetachedRep
*>(mRep
.get()))
148 return rep
->source();
154 // Do ::required, but convert incoming SecCodeRefs to their SecStaticCodeRefs
157 SecStaticCode
*SecStaticCode::requiredStatic(SecStaticCodeRef ref
)
159 SecCFObject
*object
= SecCFObject::required(ref
, errSecCSInvalidObjectRef
);
160 if (SecStaticCode
*scode
= dynamic_cast<SecStaticCode
*>(object
))
162 else if (SecCode
*code
= dynamic_cast<SecCode
*>(object
))
163 return code
->staticCode();
164 else // neither (a SecSomethingElse)
165 MacOSError::throwMe(errSecCSInvalidObjectRef
);
168 SecCode
*SecStaticCode::optionalDynamic(SecStaticCodeRef ref
)
170 SecCFObject
*object
= SecCFObject::required(ref
, errSecCSInvalidObjectRef
);
171 if (dynamic_cast<SecStaticCode
*>(object
))
173 else if (SecCode
*code
= dynamic_cast<SecCode
*>(object
))
175 else // neither (a SecSomethingElse)
176 MacOSError::throwMe(errSecCSInvalidObjectRef
);
181 // Void all cached validity data.
183 // We also throw out cached components, because the new signature data may have
184 // a different idea of what components should be present. We could reconcile the
185 // cached data instead, if performance seems to be impacted.
187 void SecStaticCode::resetValidity()
189 CODESIGN_EVAL_STATIC_RESET(this);
191 mExecutableValidated
= false;
192 mResourcesValidated
= false;
193 if (mResourcesValidContext
) {
194 delete mResourcesValidContext
;
195 mResourcesValidContext
= NULL
;
199 for (unsigned n
= 0; n
< cdSlotCount
; n
++)
202 mEntitlements
= NULL
;
203 mResourceDict
= NULL
;
204 mDesignatedReq
= NULL
;
205 mGotResourceBase
= false;
211 // we may just have updated the system database, so check again
212 checkForSystemSignature();
217 // Retrieve a sealed component by special slot index.
218 // If the CodeDirectory has already been validated, validate against that.
219 // Otherwise, retrieve the component without validation (but cache it). Validation
220 // will go through the cache and validate all cached components.
222 CFDataRef
SecStaticCode::component(CodeDirectory::SpecialSlot slot
, OSStatus fail
/* = errSecCSSignatureFailed */)
224 assert(slot
<= cdSlotMax
);
226 CFRef
<CFDataRef
> &cache
= mCache
[slot
];
228 if (CFRef
<CFDataRef
> data
= mRep
->component(slot
)) {
229 if (validated()) // if the directory has been validated...
230 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data
), // ... and it's no good
231 CFDataGetLength(data
), -slot
))
232 MacOSError::throwMe(fail
); // ... then bail
233 cache
= data
; // it's okay, cache it
234 } else { // absent, mark so
235 if (validated()) // if directory has been validated...
236 if (codeDirectory()->slotIsPresent(-slot
)) // ... and the slot is NOT missing
237 MacOSError::throwMe(fail
); // was supposed to be there
238 cache
= CFDataRef(kCFNull
); // white lie
241 return (cache
== CFDataRef(kCFNull
)) ? NULL
: cache
.get();
246 // Get the CodeDirectory.
247 // Throws (if check==true) or returns NULL (check==false) if there is none.
248 // Always throws if the CodeDirectory exists but is invalid.
249 // NEVER validates against the signature.
251 const CodeDirectory
*SecStaticCode::codeDirectory(bool check
/* = true */)
254 if (mDir
.take(mRep
->codeDirectory())) {
255 const CodeDirectory
*dir
= reinterpret_cast<const CodeDirectory
*>(CFDataGetBytePtr(mDir
));
256 dir
->checkIntegrity();
260 return reinterpret_cast<const CodeDirectory
*>(CFDataGetBytePtr(mDir
));
262 MacOSError::throwMe(errSecCSUnsigned
);
268 // Get the hash of the CodeDirectory.
269 // Returns NULL if there is none.
271 CFDataRef
SecStaticCode::cdHash()
274 if (const CodeDirectory
*cd
= codeDirectory(false)) {
276 hash(cd
, cd
->length());
279 mCDHash
.take(makeCFData(digest
, sizeof(digest
)));
280 CODESIGN_STATIC_CDHASH(this, digest
, sizeof(digest
));
288 // Return the CMS signature blob; NULL if none found.
290 CFDataRef
SecStaticCode::signature()
293 mSignature
.take(mRep
->signature());
296 MacOSError::throwMe(errSecCSUnsigned
);
301 // Verify the signature on the CodeDirectory.
302 // If this succeeds (doesn't throw), the CodeDirectory is statically trustworthy.
303 // Any outcome (successful or not) is cached for the lifetime of the StaticCode.
305 void SecStaticCode::validateDirectory()
307 // echo previous outcome, if any
310 // perform validation (or die trying)
311 CODESIGN_EVAL_STATIC_DIRECTORY(this);
312 mValidationExpired
= verifySignature();
313 component(cdInfoSlot
, errSecCSInfoPlistFailed
); // force load of Info Dictionary (if any)
314 for (CodeDirectory::SpecialSlot slot
= codeDirectory()->maxSpecialSlot(); slot
>= 1; --slot
)
315 if (mCache
[slot
]) // if we already loaded that resource...
316 validateComponent(slot
); // ... then check it now
317 mValidated
= true; // we've done the deed...
318 mValidationResult
= noErr
; // ... and it was good
319 } catch (const CommonError
&err
) {
321 mValidationResult
= err
.osStatus();
324 secdebug("staticCode", "%p validation threw non-common exception", this);
326 mValidationResult
= errSecCSInternalError
;
330 if (mValidationResult
== noErr
) {
331 if (mValidationExpired
)
332 if ((apiFlags() & kSecCSConsiderExpiration
)
333 || (codeDirectory()->flags
& kSecCodeSignatureForceExpiration
))
334 MacOSError::throwMe(CSSMERR_TP_CERT_EXPIRED
);
336 MacOSError::throwMe(mValidationResult
);
341 // Load and validate the CodeDirectory and all components *except* those related to the resource envelope.
342 // Those latter components are checked by validateResources().
344 void SecStaticCode::validateNonResourceComponents()
346 this->validateDirectory();
347 for (CodeDirectory::SpecialSlot slot
= codeDirectory()->maxSpecialSlot(); slot
>= 1; --slot
)
349 case cdResourceDirSlot
: // validated by validateResources
352 this->component(slot
); // loads and validates
359 // Get the (signed) signing date from the code signature.
360 // Sadly, we need to validate the signature to get the date (as a side benefit).
361 // This means that you can't get the signing time for invalidly signed code.
363 // We could run the decoder "almost to" verification to avoid this, but there seems
364 // little practical point to such a duplication of effort.
366 CFAbsoluteTime
SecStaticCode::signingTime()
374 // Verify the CMS signature on the CodeDirectory.
375 // This performs the cryptographic tango. It returns if the signature is valid,
376 // or throws if it is not. As a side effect, a successful return sets up the
377 // cached certificate chain for future use.
378 // Returns true if the signature is expired (the X.509 sense), false if it's not.
380 bool SecStaticCode::verifySignature()
382 // ad-hoc signed code is considered validly signed by definition
383 if (flag(kSecCodeSignatureAdhoc
)) {
384 CODESIGN_EVAL_STATIC_SIGNATURE_ADHOC(this);
388 DTRACK(CODESIGN_EVAL_STATIC_SIGNATURE
, this, (char*)this->mainExecutablePath().c_str());
390 // decode CMS and extract SecTrust for verification
391 CFRef
<CMSDecoderRef
> cms
;
392 MacOSError::check(CMSDecoderCreate(&cms
.aref())); // create decoder
393 CFDataRef sig
= this->signature();
394 MacOSError::check(CMSDecoderUpdateMessage(cms
, CFDataGetBytePtr(sig
), CFDataGetLength(sig
)));
395 this->codeDirectory(); // load CodeDirectory (sets mDir)
396 MacOSError::check(CMSDecoderSetDetachedContent(cms
, mDir
));
397 MacOSError::check(CMSDecoderFinalizeMessage(cms
));
398 MacOSError::check(CMSDecoderSetSearchKeychain(cms
, cfEmptyArray()));
399 CFRef
<CFTypeRef
> policy
= verificationPolicy(apiFlags());
400 CMSSignerStatus status
;
401 MacOSError::check(CMSDecoderCopySignerStatus(cms
, 0, policy
,
402 false, &status
, &mTrust
.aref(), NULL
));
403 if (status
!= kCMSSignerValid
)
404 MacOSError::throwMe(errSecCSSignatureFailed
);
406 // get signing date (we've got the decoder handle right here)
407 mSigningTime
= 0; // "not present" marker (nobody could code sign on Jan 1, 2001 :-)
408 SecCmsMessageRef cmsg
;
409 MacOSError::check(CMSDecoderGetCmsMessage(cms
, &cmsg
));
410 SecCmsSignedDataRef signedData
= NULL
;
411 int numContentInfos
= SecCmsMessageContentLevelCount(cmsg
);
412 for(int dex
= 0; !signedData
&& dex
< numContentInfos
; dex
++) {
413 SecCmsContentInfoRef ci
= SecCmsMessageContentLevel(cmsg
, dex
);
414 SECOidTag tag
= SecCmsContentInfoGetContentTypeTag(ci
);
416 case SEC_OID_PKCS7_SIGNED_DATA
:
417 if (SecCmsSignedDataRef signedData
= SecCmsSignedDataRef(SecCmsContentInfoGetContent(ci
)))
418 if (SecCmsSignerInfoRef signerInfo
= SecCmsSignedDataGetSignerInfo(signedData
, 0))
419 SecCmsSignerInfoGetSigningTime(signerInfo
, &mSigningTime
);
426 // set up the environment for SecTrust
427 MacOSError::check(SecTrustSetAnchorCertificates(mTrust
, cfEmptyArray())); // no anchors
428 CSSM_APPLE_TP_ACTION_DATA actionData
= {
429 CSSM_APPLE_TP_ACTION_VERSION
, // version of data structure
430 CSSM_TP_ACTION_IMPLICIT_ANCHORS
// action flags
433 for (;;) { // at most twice
434 MacOSError::check(SecTrustSetParameters(mTrust
,
435 CSSM_TP_ACTION_DEFAULT
, CFTempData(&actionData
, sizeof(actionData
))));
437 // evaluate trust and extract results
438 SecTrustResultType trustResult
;
439 MacOSError::check(SecTrustEvaluate(mTrust
, &trustResult
));
440 MacOSError::check(SecTrustGetResult(mTrust
, &trustResult
, &mCertChain
.aref(), &mEvalDetails
));
441 CODESIGN_EVAL_STATIC_SIGNATURE_RESULT(this, trustResult
, mCertChain
? CFArrayGetCount(mCertChain
) : 0);
442 switch (trustResult
) {
443 case kSecTrustResultProceed
:
444 case kSecTrustResultConfirm
:
445 case kSecTrustResultUnspecified
:
447 case kSecTrustResultDeny
:
448 MacOSError::throwMe(CSSMERR_APPLETP_TRUST_SETTING_DENY
); // user reject
449 case kSecTrustResultInvalid
:
450 assert(false); // should never happen
451 MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED
);
452 case kSecTrustResultRecoverableTrustFailure
:
453 case kSecTrustResultFatalTrustFailure
:
454 case kSecTrustResultOtherError
:
457 MacOSError::check(SecTrustGetCssmResultCode(mTrust
, &result
));
458 if (((result
== CSSMERR_TP_CERT_EXPIRED
) || (result
== CSSMERR_TP_CERT_NOT_VALID_YET
))
459 && !(actionData
.ActionFlags
& CSSM_TP_ACTION_ALLOW_EXPIRED
)) {
460 CODESIGN_EVAL_STATIC_SIGNATURE_EXPIRED(this);
461 actionData
.ActionFlags
|= CSSM_TP_ACTION_ALLOW_EXPIRED
; // (this also allows postdated certs)
462 continue; // retry validation
464 MacOSError::throwMe(result
);
467 return actionData
.ActionFlags
& CSSM_TP_ACTION_ALLOW_EXPIRED
;
473 // Return the TP policy used for signature verification.
474 // This may be a simple SecPolicyRef or a CFArray of policies.
475 // The caller owns the return value.
477 static SecPolicyRef
makeCRLPolicy()
479 CFRef
<SecPolicyRef
> policy
;
480 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3
, &CSSMOID_APPLE_TP_REVOCATION_CRL
, &policy
.aref()));
481 CSSM_APPLE_TP_CRL_OPTIONS options
;
482 memset(&options
, 0, sizeof(options
));
483 options
.Version
= CSSM_APPLE_TP_CRL_OPTS_VERSION
;
484 options
.CrlFlags
= CSSM_TP_ACTION_FETCH_CRL_FROM_NET
| CSSM_TP_ACTION_CRL_SUFFICIENT
;
485 CSSM_DATA optData
= { sizeof(options
), (uint8
*)&options
};
486 MacOSError::check(SecPolicySetValue(policy
, &optData
));
487 return policy
.yield();
490 static SecPolicyRef
makeOCSPPolicy()
492 CFRef
<SecPolicyRef
> policy
;
493 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3
, &CSSMOID_APPLE_TP_REVOCATION_OCSP
, &policy
.aref()));
494 CSSM_APPLE_TP_OCSP_OPTIONS options
;
495 memset(&options
, 0, sizeof(options
));
496 options
.Version
= CSSM_APPLE_TP_OCSP_OPTS_VERSION
;
497 options
.Flags
= CSSM_TP_ACTION_OCSP_SUFFICIENT
;
498 CSSM_DATA optData
= { sizeof(options
), (uint8
*)&options
};
499 MacOSError::check(SecPolicySetValue(policy
, &optData
));
500 return policy
.yield();
503 CFTypeRef
SecStaticCode::verificationPolicy(SecCSFlags flags
)
505 CFRef
<SecPolicyRef
> core
;
506 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3
,
507 &CSSMOID_APPLE_TP_CODE_SIGNING
, &core
.aref()));
508 if (flags
& kSecCSEnforceRevocationChecks
) {
509 CFRef
<SecPolicyRef
> crl
= makeCRLPolicy();
510 CFRef
<SecPolicyRef
> ocsp
= makeOCSPPolicy();
511 return makeCFArray(3, core
.get(), crl
.get(), ocsp
.get());
519 // Validate a particular sealed, cached resource against its (special) CodeDirectory slot.
520 // The resource must already have been placed in the cache.
521 // This does NOT perform basic validation.
523 void SecStaticCode::validateComponent(CodeDirectory::SpecialSlot slot
, OSStatus fail
/* = errSecCSSignatureFailed */)
525 assert(slot
<= cdSlotMax
);
526 CFDataRef data
= mCache
[slot
];
527 assert(data
); // must be cached
528 if (data
== CFDataRef(kCFNull
)) {
529 if (codeDirectory()->slotIsPresent(-slot
)) // was supposed to be there...
530 MacOSError::throwMe(fail
); // ... and is missing
532 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data
), CFDataGetLength(data
), -slot
))
533 MacOSError::throwMe(fail
);
539 // Perform static validation of the main executable.
540 // This reads the main executable from disk and validates it against the
541 // CodeDirectory code slot array.
542 // Note that this is NOT an in-memory validation, and is thus potentially
543 // subject to timing attacks.
545 void SecStaticCode::validateExecutable()
547 if (!validatedExecutable()) {
549 DTRACK(CODESIGN_EVAL_STATIC_EXECUTABLE
, this,
550 (char*)this->mainExecutablePath().c_str(), codeDirectory()->nCodeSlots
);
551 const CodeDirectory
*cd
= this->codeDirectory();
553 MacOSError::throwMe(errSecCSUnsigned
);
554 AutoFileDesc
fd(mainExecutablePath(), O_RDONLY
);
555 fd
.fcntl(F_NOCACHE
, true); // turn off page caching (one-pass)
556 if (Universal
*fat
= mRep
->mainExecutableImage())
557 fd
.seek(fat
->archOffset());
558 size_t pageSize
= cd
->pageSize
? (1 << cd
->pageSize
) : 0;
559 size_t remaining
= cd
->codeLimit
;
560 for (size_t slot
= 0; slot
< cd
->nCodeSlots
; ++slot
) {
561 size_t size
= min(remaining
, pageSize
);
562 if (!cd
->validateSlot(fd
, size
, slot
)) {
563 CODESIGN_EVAL_STATIC_EXECUTABLE_FAIL(this, slot
);
564 MacOSError::throwMe(errSecCSSignatureFailed
);
568 mExecutableValidated
= true;
569 mExecutableValidResult
= noErr
;
570 } catch (const CommonError
&err
) {
571 mExecutableValidated
= true;
572 mExecutableValidResult
= err
.osStatus();
575 secdebug("staticCode", "%p executable validation threw non-common exception", this);
576 mExecutableValidated
= true;
577 mExecutableValidResult
= errSecCSInternalError
;
581 assert(validatedExecutable());
582 if (mExecutableValidResult
!= noErr
)
583 MacOSError::throwMe(mExecutableValidResult
);
588 // Perform static validation of sealed resources.
590 // This performs a whole-code static resource scan and effectively
591 // computes a concordance between what's on disk and what's in the ResourceDirectory.
592 // Any unsanctioned difference causes an error.
594 void SecStaticCode::validateResources()
596 if (!validatedResources()) {
599 CFDictionaryRef sealedResources
= resourceDictionary();
600 if (this->resourceBase()) // disk has resources
602 /* go to work below */;
604 MacOSError::throwMe(errSecCSResourcesNotFound
);
605 else // disk has no resources
607 MacOSError::throwMe(errSecCSResourcesNotFound
);
609 return; // no resources, not sealed - fine (no work)
611 // found resources, and they are sealed
612 CFDictionaryRef rules
= cfget
<CFDictionaryRef
>(sealedResources
, "rules");
613 CFDictionaryRef files
= cfget
<CFDictionaryRef
>(sealedResources
, "files");
614 DTRACK(CODESIGN_EVAL_STATIC_RESOURCES
, this,
615 (char*)this->mainExecutablePath().c_str(), int(CFDictionaryGetCount(files
)));
617 // make a shallow copy of the ResourceDirectory so we can "check off" what we find
618 CFRef
<CFMutableDictionaryRef
> resourceMap
= makeCFMutableDictionary(files
);
620 // scan through the resources on disk, checking each against the resourceDirectory
621 mResourcesValidContext
= new CollectingContext(*this); // collect all failures in here
622 ResourceBuilder
resources(cfString(this->resourceBase()), rules
, codeDirectory()->hashType
);
623 mRep
->adjustResources(resources
);
625 ResourceBuilder::Rule
*rule
;
627 while (resources
.next(path
, rule
)) {
628 validateResource(path
, *mResourcesValidContext
);
629 CFDictionaryRemoveValue(resourceMap
, CFTempString(path
));
632 if (CFDictionaryGetCount(resourceMap
) > 0) {
633 secdebug("staticCode", "%p sealed resource(s) not found in code", this);
634 CFDictionaryApplyFunction(resourceMap
, SecStaticCode::checkOptionalResource
, mResourcesValidContext
);
637 // now check for any errors found in the reporting context
638 mResourcesValidated
= true;
639 if (mResourcesValidContext
->osStatus() != noErr
)
640 mResourcesValidContext
->throwMe();
642 } catch (const CommonError
&err
) {
643 mResourcesValidated
= true;
644 mResourcesValidResult
= err
.osStatus();
647 secdebug("staticCode", "%p executable validation threw non-common exception", this);
648 mResourcesValidated
= true;
649 mResourcesValidResult
= errSecCSInternalError
;
653 assert(!validatedResources());
654 if (mResourcesValidResult
)
655 MacOSError::throwMe(mResourcesValidResult
);
656 if (mResourcesValidContext
->osStatus() != noErr
)
657 mResourcesValidContext
->throwMe();
661 void SecStaticCode::checkOptionalResource(CFTypeRef key
, CFTypeRef value
, void *context
)
663 CollectingContext
*ctx
= static_cast<CollectingContext
*>(context
);
664 ResourceSeal
seal(value
);
665 if (!seal
.optional())
666 if (key
&& CFGetTypeID(key
) == CFStringGetTypeID()) {
667 ctx
->reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
,
668 CFTempURL(CFStringRef(key
), false, ctx
->code
.resourceBase()));
670 ctx
->reportProblem(errSecCSBadResource
, kSecCFErrorResourceSeal
, key
);
675 // Load, validate, cache, and return CFDictionary forms of sealed resources.
677 CFDictionaryRef
SecStaticCode::infoDictionary()
680 mInfoDict
.take(getDictionary(cdInfoSlot
, errSecCSInfoPlistFailed
));
681 secdebug("staticCode", "%p loaded InfoDict %p", this, mInfoDict
.get());
686 CFDictionaryRef
SecStaticCode::entitlements()
688 if (!mEntitlements
) {
690 if (CFDataRef entitlementData
= component(cdEntitlementSlot
)) {
691 validateComponent(cdEntitlementSlot
);
692 const EntitlementBlob
*blob
= reinterpret_cast<const EntitlementBlob
*>(CFDataGetBytePtr(entitlementData
));
693 if (blob
->validateBlob()) {
694 mEntitlements
.take(blob
->entitlements());
695 secdebug("staticCode", "%p loaded Entitlements %p", this, mEntitlements
.get());
697 // we do not consider a different blob type to be an error. We think it's a new format we don't understand
700 return mEntitlements
;
703 CFDictionaryRef
SecStaticCode::resourceDictionary()
705 if (mResourceDict
) // cached
706 return mResourceDict
;
707 if (CFRef
<CFDictionaryRef
> dict
= getDictionary(cdResourceDirSlot
, errSecCSSignatureFailed
))
708 if (cfscan(dict
, "{rules=%Dn,files=%Dn}")) {
709 secdebug("staticCode", "%p loaded ResourceDict %p",
710 this, mResourceDict
.get());
711 return mResourceDict
= dict
;
719 // Load and cache the resource directory base.
720 // Note that the base is optional for each DiskRep.
722 CFURLRef
SecStaticCode::resourceBase()
724 if (!mGotResourceBase
) {
725 string base
= mRep
->resourcesRootPath();
727 mResourceBase
.take(makeCFURL(base
, true));
728 mGotResourceBase
= true;
730 return mResourceBase
;
735 // Load a component, validate it, convert it to a CFDictionary, and return that.
736 // This will force load and validation, which means that it will perform basic
737 // validation if it hasn't been done yet.
739 CFDictionaryRef
SecStaticCode::getDictionary(CodeDirectory::SpecialSlot slot
, OSStatus fail
/* = errSecCSSignatureFailed */)
742 if (CFDataRef infoData
= component(slot
, fail
)) {
743 validateComponent(slot
, fail
);
744 if (CFDictionaryRef dict
= makeCFDictionaryFrom(infoData
))
747 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
754 // Load, validate, and return a sealed resource.
755 // The resource data (loaded in to memory as a blob) is returned and becomes
756 // the responsibility of the caller; it is NOT cached by SecStaticCode.
758 // A resource that is not sealed will not be returned, and an error will be thrown.
759 // A missing resource will cause an error unless it's marked optional in the Directory.
760 // Under no circumstances will a corrupt resource be returned.
761 // NULL will only be returned for a resource that is neither sealed nor present
762 // (or that is sealed, absent, and marked optional).
763 // If the ResourceDictionary itself is not sealed, this function will always fail.
765 // There is currently no interface for partial retrieval of the resource data.
766 // (Since the ResourceDirectory does not currently support segmentation, all the
767 // data would have to be read anyway, but it could be read into a reusable buffer.)
769 CFDataRef
SecStaticCode::resource(string path
, ValidationContext
&ctx
)
771 if (CFDictionaryRef rdict
= resourceDictionary()) {
772 if (CFTypeRef file
= cfget(rdict
, "files.%s", path
.c_str())) {
773 ResourceSeal seal
= file
;
774 if (!resourceBase()) // no resources in DiskRep
775 MacOSError::throwMe(errSecCSResourcesNotFound
);
776 CFRef
<CFURLRef
> fullpath
= makeCFURL(path
, false, resourceBase());
777 if (CFRef
<CFDataRef
> data
= cfLoadFile(fullpath
)) {
778 MakeHash
<CodeDirectory
> hasher(this->codeDirectory());
779 hasher
->update(CFDataGetBytePtr(data
), CFDataGetLength(data
));
780 if (hasher
->verify(seal
.hash()))
781 return data
.yield(); // good
783 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAltered
, fullpath
); // altered
785 if (!seal
.optional())
786 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
, fullpath
); // was sealed but is now missing
788 return NULL
; // validly missing
791 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAdded
, CFTempURL(path
, false, resourceBase()));
794 MacOSError::throwMe(errSecCSResourcesNotSealed
);
797 CFDataRef
SecStaticCode::resource(string path
)
799 ValidationContext ctx
;
800 return resource(path
, ctx
);
804 void SecStaticCode::validateResource(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 AutoFileDesc
fd(cfString(fullpath
), O_RDONLY
, FileDesc::modeMissingOk
); // open optional filee
814 MakeHash
<CodeDirectory
> hasher(this->codeDirectory());
815 hashFileData(fd
, hasher
.get());
816 if (hasher
->verify(seal
.hash()))
817 return; // verify good
819 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAltered
, fullpath
); // altered
821 if (!seal
.optional())
822 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
, fullpath
); // was sealed but is now missing
824 return; // validly missing
827 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAdded
, CFTempURL(path
, false, resourceBase()));
829 MacOSError::throwMe(errSecCSResourcesNotSealed
);
834 // Test a CodeDirectory flag.
835 // Returns false if there is no CodeDirectory.
836 // May throw if the CodeDirectory is present but somehow invalid.
838 bool SecStaticCode::flag(uint32_t tested
)
840 if (const CodeDirectory
*cd
= this->codeDirectory(false))
841 return cd
->flags
& tested
;
848 // Retrieve the full SuperBlob containing all internal requirements.
850 const Requirements
*SecStaticCode::internalRequirements()
852 if (CFDataRef req
= component(cdRequirementsSlot
))
853 return (const Requirements
*)CFDataGetBytePtr(req
);
860 // Retrieve a particular internal requirement by type.
862 const Requirement
*SecStaticCode::internalRequirement(SecRequirementType type
)
864 if (const Requirements
*reqs
= internalRequirements())
865 return reqs
->find
<Requirement
>(type
);
872 // Return the Designated Requirement. This can be either explicit in the
873 // Internal Requirements resource, or implicitly generated.
875 const Requirement
*SecStaticCode::designatedRequirement()
877 if (const Requirement
*req
= internalRequirement(kSecDesignatedRequirementType
)) {
878 return req
; // explicit in signing data
881 mDesignatedReq
= defaultDesignatedRequirement();
882 return mDesignatedReq
;
888 // Generate the default (implicit) Designated Requirement for this StaticCode.
889 // This is a heuristic of sorts, and may change over time (for the better, we hope).
891 // The current logic is this:
892 // * If the code is ad-hoc signed, use the CodeDirectory hash directory.
893 // * Otherwise, use the form anchor (anchor) and identifier (CodeDirectory identifier).
894 // ** If the root CA is Apple's, we use the "anchor apple" construct. Otherwise,
895 // we default to the leaf (directly signing) certificate.
897 const Requirement
*SecStaticCode::defaultDesignatedRequirement()
899 validateDirectory(); // need the cert chain
900 Requirement::Maker maker
;
902 // if this is an ad-hoc (unsigned) object, return a cdhash requirement
903 if (flag(kSecCodeSignatureAdhoc
)) {
905 hash(codeDirectory(), codeDirectory()->length());
908 maker
.cdhash(digest
);
910 // always require the identifier
912 maker
.ident(codeDirectory()->identifier());
914 SHA1::Digest anchorHash
;
915 hashOfCertificate(cert(Requirement::anchorCert
), anchorHash
);
916 if (!memcmp(anchorHash
, Requirement::appleAnchorHash(), SHA1::digestLength
)
917 #if defined(TEST_APPLE_ANCHOR)
918 || !memcmp(anchorHash
, Requirement::testAppleAnchorHash(), SHA1::digestLength
)
921 defaultDesignatedAppleAnchor(maker
);
923 defaultDesignatedNonAppleAnchor(maker
);
929 static const uint8_t adcSdkMarker
[] = { APPLE_EXTENSION_OID
, 2, 1 }; // iOS intermediate marker
930 static const CSSM_DATA adcSdkMarkerOID
= { sizeof(adcSdkMarker
), (uint8_t *)adcSdkMarker
};
932 static const uint8_t caspianSdkMarker
[] = { APPLE_EXTENSION_OID
, 2, 6 }; // Caspian intermediate marker
933 static const CSSM_DATA caspianSdkMarkerOID
= { sizeof(caspianSdkMarker
), (uint8_t *)caspianSdkMarker
};
934 static const uint8_t caspianLeafMarker
[] = { APPLE_EXTENSION_OID
, 1, 13 }; // Caspian leaf certificate marker
935 static const CSSM_DATA caspianLeafMarkerOID
= { sizeof(caspianLeafMarker
), (uint8_t *)caspianLeafMarker
};
937 void SecStaticCode::defaultDesignatedAppleAnchor(Requirement::Maker
&maker
)
939 if (isAppleSDKSignature()) {
940 // get the Common Name DN element for the leaf
941 CFRef
<CFStringRef
> leafCN
;
942 MacOSError::check(SecCertificateCopySubjectComponent(cert(Requirement::leafCert
),
943 &CSSMOID_CommonName
, &leafCN
.aref()));
945 // apple anchor generic and ...
947 maker
.anchorGeneric(); // apple generic anchor and...
948 // ... leaf[subject.CN] = <leaf's subject> and ...
950 maker
.put(opCertField
); // certificate
951 maker
.put(0); // leaf
952 maker
.put("subject.CN"); // [subject.CN]
953 maker
.put(matchEqual
); // =
954 maker
.putData(leafCN
); // <leaf CN>
955 // ... cert 1[field.<marker>] exists
956 maker
.put(opCertGeneric
); // certificate
958 maker
.putData(adcSdkMarkerOID
.Data
, adcSdkMarkerOID
.Length
); // [field.<marker>]
959 maker
.put(matchExists
); // exists
963 if (isAppleCaspianSignature()) {
964 // get the Organizational Unit DN element for the leaf (it contains the TEAMID)
965 CFRef
<CFStringRef
> teamID
;
966 MacOSError::check(SecCertificateCopySubjectComponent(cert(Requirement::leafCert
),
967 &CSSMOID_OrganizationalUnitName
, &teamID
.aref()));
969 // apple anchor generic and ...
971 maker
.anchorGeneric(); // apple generic anchor and...
973 // ... certificate 1[intermediate marker oid] exists and ...
975 maker
.put(opCertGeneric
); // certificate
977 maker
.putData(caspianSdkMarker
, sizeof(caspianSdkMarker
));
978 maker
.put(matchExists
); // exists
980 // ... certificate leaf[Caspian cert oid] exists and ...
982 maker
.put(opCertGeneric
); // certificate
983 maker
.put(0); // leaf
984 maker
.putData(caspianLeafMarker
, sizeof(caspianLeafMarker
));
985 maker
.put(matchExists
); // exists
987 // ... leaf[subject.OU] = <leaf's subject>
988 maker
.put(opCertField
); // certificate
989 maker
.put(0); // leaf
990 maker
.put("subject.OU"); // [subject.OU]
991 maker
.put(matchEqual
); // =
992 maker
.putData(teamID
); // TEAMID
996 // otherwise, claim this program for Apple Proper
1000 bool SecStaticCode::isAppleSDKSignature()
1002 if (CFArrayRef certChain
= certificates()) // got cert chain
1003 if (CFArrayGetCount(certChain
) == 3) // leaf, one intermediate, anchor
1004 if (SecCertificateRef intermediate
= cert(1)) // get intermediate
1005 if (certificateHasField(intermediate
, CssmOid::overlay(adcSdkMarkerOID
)))
1010 bool SecStaticCode::isAppleCaspianSignature()
1012 if (CFArrayRef certChain
= certificates()) // got cert chain
1013 if (CFArrayGetCount(certChain
) == 3) // leaf, one intermediate, anchor
1014 if (SecCertificateRef intermediate
= cert(1)) // get intermediate
1015 if (certificateHasField(intermediate
, CssmOid::overlay(caspianSdkMarkerOID
)))
1020 void SecStaticCode::defaultDesignatedNonAppleAnchor(Requirement::Maker
&maker
)
1022 // get the Organization DN element for the leaf
1023 CFRef
<CFStringRef
> leafOrganization
;
1024 MacOSError::check(SecCertificateCopySubjectComponent(cert(Requirement::leafCert
),
1025 &CSSMOID_OrganizationName
, &leafOrganization
.aref()));
1027 // now step up the cert chain looking for the first cert with a different one
1028 int slot
= Requirement::leafCert
; // start at leaf
1029 if (leafOrganization
) {
1030 while (SecCertificateRef ca
= cert(slot
+1)) { // NULL if you over-run the anchor slot
1031 CFRef
<CFStringRef
> caOrganization
;
1032 MacOSError::check(SecCertificateCopySubjectComponent(ca
, &CSSMOID_OrganizationName
, &caOrganization
.aref()));
1033 if (!caOrganization
|| CFStringCompare(leafOrganization
, caOrganization
, 0) != kCFCompareEqualTo
)
1037 if (slot
== CFArrayGetCount(mCertChain
) - 1) // went all the way to the anchor...
1038 slot
= Requirement::anchorCert
; // ... so say that
1041 // nail the last cert with the leaf's Organization value
1042 SHA1::Digest authorityHash
;
1043 hashOfCertificate(cert(slot
), authorityHash
);
1044 maker
.anchor(slot
, authorityHash
);
1049 // Validate a SecStaticCode against the internal requirement of a particular type.
1051 void SecStaticCode::validateRequirements(SecRequirementType type
, SecStaticCode
*target
,
1052 OSStatus nullError
/* = noErr */)
1054 DTRACK(CODESIGN_EVAL_STATIC_INTREQ
, this, type
, target
, nullError
);
1055 if (const Requirement
*req
= internalRequirement(type
))
1056 target
->validateRequirement(req
, nullError
? nullError
: errSecCSReqFailed
);
1058 MacOSError::throwMe(nullError
);
1065 // Validate this StaticCode against an external Requirement
1067 bool SecStaticCode::satisfiesRequirement(const Requirement
*req
, OSStatus failure
)
1070 validateDirectory();
1071 return req
->validates(Requirement::Context(mCertChain
, infoDictionary(), entitlements(), codeDirectory()), failure
);
1074 void SecStaticCode::validateRequirement(const Requirement
*req
, OSStatus failure
)
1076 if (!this->satisfiesRequirement(req
, failure
))
1077 MacOSError::throwMe(failure
);
1082 // Retrieve one certificate from the cert chain.
1083 // Positive and negative indices can be used:
1084 // [ leaf, intermed-1, ..., intermed-n, anchor ]
1086 // Returns NULL if unavailable for any reason.
1088 SecCertificateRef
SecStaticCode::cert(int ix
)
1090 validateDirectory(); // need cert chain
1092 CFIndex length
= CFArrayGetCount(mCertChain
);
1095 if (ix
>= 0 && ix
< length
)
1096 return SecCertificateRef(CFArrayGetValueAtIndex(mCertChain
, ix
));
1101 CFArrayRef
SecStaticCode::certificates()
1103 validateDirectory(); // need cert chain
1109 // Gather (mostly) API-official information about this StaticCode.
1111 // This method lives in the twilight between the API and internal layers,
1112 // since it generates API objects (Sec*Refs) for return.
1114 CFDictionaryRef
SecStaticCode::signingInformation(SecCSFlags flags
)
1117 // Start with the pieces that we return even for unsigned code.
1118 // This makes Sec[Static]CodeRefs useful as API-level replacements
1119 // of our internal OSXCode objects.
1121 CFRef
<CFMutableDictionaryRef
> dict
= makeCFMutableDictionary(1,
1122 kSecCodeInfoMainExecutable
, CFTempURL(this->mainExecutablePath()).get()
1126 // If we're not signed, this is all you get
1128 if (!this->isSigned())
1129 return dict
.yield();
1132 // Add the generic attributes that we always include
1134 CFDictionaryAddValue(dict
, kSecCodeInfoIdentifier
, CFTempString(this->identifier()));
1135 CFDictionaryAddValue(dict
, kSecCodeInfoFormat
, CFTempString(this->format()));
1136 CFDictionaryAddValue(dict
, kSecCodeInfoSource
, CFTempString(this->signatureSource()));
1137 if (CFDictionaryRef info
= this->infoDictionary())
1138 CFDictionaryAddValue(dict
, kSecCodeInfoPList
, info
);
1139 CFDictionaryAddValue(dict
, kSecCodeInfoUnique
, this->cdHash());
1140 CFDictionaryAddValue(dict
, kSecCodeInfoDigestAlgorithm
, CFTempNumber(this->codeDirectory(false)->hashType
));
1143 // kSecCSSigningInformation adds information about signing certificates and chains
1145 if (flags
& kSecCSSigningInformation
) {
1146 if (CFArrayRef certs
= this->certificates())
1147 CFDictionaryAddValue(dict
, kSecCodeInfoCertificates
, certs
);
1148 if (CFDataRef sig
= this->signature())
1149 CFDictionaryAddValue(dict
, kSecCodeInfoCMS
, sig
);
1151 CFDictionaryAddValue(dict
, kSecCodeInfoTrust
, mTrust
);
1152 if (CFAbsoluteTime time
= this->signingTime())
1153 if (CFRef
<CFDateRef
> date
= CFDateCreate(NULL
, time
))
1154 CFDictionaryAddValue(dict
, kSecCodeInfoTime
, date
);
1158 // kSecCSRequirementInformation adds information on requirements
1160 if (flags
& kSecCSRequirementInformation
) {
1161 if (const Requirements
*reqs
= this->internalRequirements()) {
1162 CFDictionaryAddValue(dict
, kSecCodeInfoRequirements
,
1163 CFTempString(Dumper::dump(reqs
)));
1164 CFDictionaryAddValue(dict
, kSecCodeInfoRequirementData
, CFTempData(*reqs
));
1167 const Requirement
*dreq
= this->designatedRequirement();
1168 CFRef
<SecRequirementRef
> dreqRef
= (new SecRequirement(dreq
))->handle();
1169 CFDictionaryAddValue(dict
, kSecCodeInfoDesignatedRequirement
, dreqRef
);
1170 if (this->internalRequirement(kSecDesignatedRequirementType
)) { // explicit
1171 CFRef
<SecRequirementRef
> ddreqRef
= (new SecRequirement(this->defaultDesignatedRequirement(), true))->handle();
1172 CFDictionaryAddValue(dict
, kSecCodeInfoImplicitDesignatedRequirement
, ddreqRef
);
1173 } else { // implicit
1174 CFDictionaryAddValue(dict
, kSecCodeInfoImplicitDesignatedRequirement
, dreqRef
);
1177 if (CFDataRef ent
= this->component(cdEntitlementSlot
))
1178 CFDictionaryAddValue(dict
, kSecCodeInfoEntitlements
, ent
);
1182 // kSecCSInternalInformation adds internal information meant to be for Apple internal
1183 // use (SPI), and not guaranteed to be stable. Primarily, this is data we want
1184 // to reliably transmit through the API wall so that code outside the Security.framework
1185 // can use it without having to play nasty tricks to get it.
1187 if (flags
& kSecCSInternalInformation
) {
1189 CFDictionaryAddValue(dict
, kSecCodeInfoCodeDirectory
, mDir
);
1190 CFDictionaryAddValue(dict
, kSecCodeInfoCodeOffset
, CFTempNumber(mRep
->signingBase()));
1191 if (CFDictionaryRef resources
= resourceDictionary())
1192 CFDictionaryAddValue(dict
, kSecCodeInfoResourceDirectory
, resources
);
1197 // kSecCSContentInformation adds more information about the physical layout
1198 // of the signed code. This is (only) useful for packaging or patching-oriented
1201 if (flags
& kSecCSContentInformation
)
1202 if (CFRef
<CFArrayRef
> files
= mRep
->modifiedFiles())
1203 CFDictionaryAddValue(dict
, kSecCodeInfoChangedFiles
, files
);
1205 return dict
.yield();
1210 // Resource validation contexts.
1211 // The default context simply throws a CSError, rudely terminating the operation.
1213 SecStaticCode::ValidationContext::~ValidationContext()
1216 void SecStaticCode::ValidationContext::reportProblem(OSStatus rc
, CFStringRef type
, CFTypeRef value
)
1218 CSError::throwMe(rc
, type
, value
);
1221 void SecStaticCode::CollectingContext::reportProblem(OSStatus rc
, CFStringRef type
, CFTypeRef value
)
1223 if (mStatus
== noErr
)
1224 mStatus
= rc
; // record first failure for eventual error return
1227 mCollection
.take(makeCFMutableDictionary());
1228 CFMutableArrayRef element
= CFMutableArrayRef(CFDictionaryGetValue(mCollection
, type
));
1230 element
= makeCFMutableArray(0);
1233 CFDictionaryAddValue(mCollection
, type
, element
);
1236 CFArrayAppendValue(element
, value
);
1240 void SecStaticCode::CollectingContext::throwMe()
1242 assert(mStatus
!= noErr
);
1243 throw CSError(mStatus
, mCollection
.retain());
1248 // SecStaticCode::AllArchitectures produces SecStaticCode objects separately
1249 // for each architecture represented by a base object.
1251 // Performance note: This is a simple, straight-forward implementation that
1252 // does not heroically try to share resources between the code objects produced.
1253 // In practice, this means we'll re-open files and re-read resource files.
1254 // In exchange, we enter all the code paths in the normal way, and do not have
1255 // special sharing paths to worry about.
1256 // If a performance tool brings you here because you have *proof* of a performance
1257 // problem, consider digging up MachO and Universal (for sharing file descriptors),
1258 // and SecStaticCode (for sharing resource iterators). That ought to cover most of
1259 // the big chunks. If you're just offended by the simplicity of this implementation,
1260 // go play somewhere else.
1262 SecStaticCode::AllArchitectures::AllArchitectures(SecStaticCode
*code
)
1265 if (Universal
*fat
= code
->diskRep()->mainExecutableImage()) {
1266 fat
->architectures(mArchitectures
);
1267 mCurrent
= mArchitectures
.begin();
1270 mState
= firstNonFat
;
1274 SecStaticCode
*SecStaticCode::AllArchitectures::operator () ()
1282 if (mCurrent
== mArchitectures
.end())
1284 Architecture arch
= *mCurrent
++;
1285 if (arch
== mBase
->diskRep()->mainExecutableImage()->bestNativeArch()) {
1288 DiskRep::Context ctx
;
1290 return new SecStaticCode(DiskRep::bestGuess(mBase
->mainExecutablePath(), &ctx
));
1299 } // end namespace CodeSigning
1300 } // end namespace Security