2 * Copyright (c) 2006-2007 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_utilities/unix++.h>
46 #include <security_utilities/cfmunge.h>
47 #include <Security/CMSDecoder.h>
51 namespace CodeSigning
{
53 using namespace UnixPlusPlus
;
57 // Construct a SecStaticCode object given a disk representation object
59 SecStaticCode::SecStaticCode(DiskRep
*rep
)
61 mValidated(false), mExecutableValidated(false),
62 mDesignatedReq(NULL
), mGotResourceBase(false), mEvalDetails(NULL
)
64 CODESIGN_STATIC_CREATE(this, rep
);
65 checkForSystemSignature();
70 // Clean up a SecStaticCode object
72 SecStaticCode::~SecStaticCode() throw()
74 ::free(const_cast<Requirement
*>(mDesignatedReq
));
79 // CF-level comparison of SecStaticCode objects compares CodeDirectory hashes if signed,
80 // and falls back on comparing canonical paths if (both are) not.
82 bool SecStaticCode::equal(SecCFObject
&secOther
)
84 SecStaticCode
*other
= static_cast<SecStaticCode
*>(&secOther
);
85 CFDataRef mine
= this->cdHash();
86 CFDataRef his
= other
->cdHash();
88 return mine
&& his
&& CFEqual(mine
, his
);
90 return CFEqual(this->canonicalPath(), other
->canonicalPath());
93 CFHashCode
SecStaticCode::hash()
95 if (CFDataRef h
= this->cdHash())
98 return CFHash(this->canonicalPath());
103 // Attach a detached signature.
105 void SecStaticCode::detachedSignature(CFDataRef sigData
)
108 mRep
= new DetachedRep(sigData
, mRep
->base(), "explicit detached");
109 CODESIGN_STATIC_ATTACH_EXPLICIT(this, mRep
);
112 CODESIGN_STATIC_ATTACH_EXPLICIT(this, NULL
);
118 // Consult the system detached signature database to see if it contains
119 // a detached signature for this StaticCode. If it does, fetch and attach it.
120 // We do this only if the code has no signature already attached.
122 void SecStaticCode::checkForSystemSignature()
124 if (!this->isSigned())
126 if (RefPointer
<DiskRep
> dsig
= signatureDatabase().findCode(mRep
)) {
127 CODESIGN_STATIC_ATTACH_SYSTEM(this, dsig
);
136 // Return a descriptive string identifying the source of the code signature
138 string
SecStaticCode::signatureSource()
142 if (DetachedRep
*rep
= dynamic_cast<DetachedRep
*>(mRep
.get()))
143 return rep
->source();
149 // Do ::required, but convert incoming SecCodeRefs to their SecStaticCodeRefs
152 SecStaticCode
*SecStaticCode::requiredStatic(SecStaticCodeRef ref
)
154 SecCFObject
*object
= SecCFObject::required(ref
, errSecCSInvalidObjectRef
);
155 if (SecStaticCode
*scode
= dynamic_cast<SecStaticCode
*>(object
))
157 else if (SecCode
*code
= dynamic_cast<SecCode
*>(object
))
158 return code
->staticCode();
159 else // neither (a SecSomethingElse)
160 MacOSError::throwMe(errSecCSInvalidObjectRef
);
163 SecCode
*SecStaticCode::optionalDynamic(SecStaticCodeRef ref
)
165 SecCFObject
*object
= SecCFObject::required(ref
, errSecCSInvalidObjectRef
);
166 if (dynamic_cast<SecStaticCode
*>(object
))
168 else if (SecCode
*code
= dynamic_cast<SecCode
*>(object
))
170 else // neither (a SecSomethingElse)
171 MacOSError::throwMe(errSecCSInvalidObjectRef
);
176 // Void all cached validity data.
178 // We also throw out cached components, because the new signature data may have
179 // a different idea of what components should be present. We could reconcile the
180 // cached data instead, if performance seems to be impacted.
182 void SecStaticCode::resetValidity()
184 CODESIGN_EVAL_STATIC_RESET(this);
186 mExecutableValidated
= false;
189 for (unsigned n
= 0; n
< cdSlotCount
; n
++)
192 mEntitlements
= NULL
;
193 mResourceDict
= NULL
;
194 mDesignatedReq
= NULL
;
195 mGotResourceBase
= false;
201 // we may just have updated the system database, so check again
202 checkForSystemSignature();
207 // Retrieve a sealed component by special slot index.
208 // If the CodeDirectory has already been validated, validate against that.
209 // Otherwise, retrieve the component without validation (but cache it). Validation
210 // will go through the cache and validate all cached components.
212 CFDataRef
SecStaticCode::component(CodeDirectory::SpecialSlot slot
)
214 assert(slot
<= cdSlotMax
);
216 CFRef
<CFDataRef
> &cache
= mCache
[slot
];
218 if (CFRef
<CFDataRef
> data
= mRep
->component(slot
)) {
219 if (validated()) // if the directory has been validated...
220 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data
), // ... and it's no good
221 CFDataGetLength(data
), -slot
))
222 MacOSError::throwMe(errSecCSSignatureFailed
); // ... then bail
223 cache
= data
; // it's okay, cache it
224 } else { // absent, mark so
225 if (validated()) // if directory has been validated...
226 if (codeDirectory()->slotIsPresent(-slot
)) // ... and the slot is NOT missing
227 MacOSError::throwMe(errSecCSSignatureFailed
); // was supposed to be there
228 cache
= CFDataRef(kCFNull
); // white lie
231 return (cache
== CFDataRef(kCFNull
)) ? NULL
: cache
.get();
236 // Get the CodeDirectory.
237 // Throws (if check==true) or returns NULL (check==false) if there is none.
238 // Always throws if the CodeDirectory exists but is invalid.
239 // NEVER validates against the signature.
241 const CodeDirectory
*SecStaticCode::codeDirectory(bool check
/* = true */)
244 if (mDir
.take(mRep
->codeDirectory())) {
245 const CodeDirectory
*dir
= reinterpret_cast<const CodeDirectory
*>(CFDataGetBytePtr(mDir
));
246 dir
->checkIntegrity();
250 return reinterpret_cast<const CodeDirectory
*>(CFDataGetBytePtr(mDir
));
252 MacOSError::throwMe(errSecCSUnsigned
);
258 // Get the hash of the CodeDirectory.
259 // Returns NULL if there is none.
261 CFDataRef
SecStaticCode::cdHash()
264 if (const CodeDirectory
*cd
= codeDirectory(false)) {
266 hash(cd
, cd
->length());
269 mCDHash
.take(makeCFData(digest
, sizeof(digest
)));
270 CODESIGN_STATIC_CDHASH(this, digest
, sizeof(digest
));
278 // Return the CMS signature blob; NULL if none found.
280 CFDataRef
SecStaticCode::signature()
283 mSignature
.take(mRep
->signature());
286 MacOSError::throwMe(errSecCSUnsigned
);
291 // Verify the signature on the CodeDirectory.
292 // If this succeeds (doesn't throw), the CodeDirectory is statically trustworthy.
293 // Any outcome (successful or not) is cached for the lifetime of the StaticCode.
295 void SecStaticCode::validateDirectory()
297 // echo previous outcome, if any
300 // perform validation (or die trying)
301 CODESIGN_EVAL_STATIC_DIRECTORY(this);
302 mValidationExpired
= verifySignature();
303 component(cdInfoSlot
); // force load of Info Dictionary (if any)
304 CodeDirectory::SpecialSlot slot
= codeDirectory()->nSpecialSlots
;
305 if (slot
> cdSlotMax
) // might have more special slots than we know about...
306 slot
= cdSlotMax
; // ... but only process the ones we understand
307 for ( ; slot
>= 1; --slot
)
308 if (mCache
[slot
]) // if we already loaded that resource...
309 validateComponent(slot
); // ... then check it now
310 mValidated
= true; // we've done the deed...
311 mValidationResult
= noErr
; // ... and it was good
312 } catch (const CommonError
&err
) {
314 mValidationResult
= err
.osStatus();
317 secdebug("staticCode", "%p validation threw non-common exception", this);
319 mValidationResult
= errSecCSInternalError
;
323 if (mValidationResult
== noErr
) {
324 if (mValidationExpired
)
325 if ((apiFlags() & kSecCSConsiderExpiration
)
326 || (codeDirectory()->flags
& kSecCodeSignatureForceExpiration
))
327 MacOSError::throwMe(CSSMERR_TP_CERT_EXPIRED
);
329 MacOSError::throwMe(mValidationResult
);
334 // Get the (signed) signing date from the code signature.
335 // Sadly, we need to validate the signature to get the date (as a side benefit).
336 // This means that you can't get the signing time for invalidly signed code.
338 // We could run the decoder "almost to" verification to avoid this, but there seems
339 // little practical point to such a duplication of effort.
341 CFAbsoluteTime
SecStaticCode::signingTime()
349 // Verify the CMS signature on the CodeDirectory.
350 // This performs the cryptographic tango. It returns if the signature is valid,
351 // or throws if it is not. As a side effect, a successful return sets up the
352 // cached certificate chain for future use.
353 // Returns true if the signature is expired (the X.509 sense), false if it's not.
355 bool SecStaticCode::verifySignature()
357 // ad-hoc signed code is considered validly signed by definition
358 if (flag(kSecCodeSignatureAdhoc
)) {
359 CODESIGN_EVAL_STATIC_SIGNATURE_ADHOC(this);
363 DTRACK(CODESIGN_EVAL_STATIC_SIGNATURE
, this, (char*)this->mainExecutablePath().c_str());
365 // decode CMS and extract SecTrust for verification
366 CFRef
<CMSDecoderRef
> cms
;
367 MacOSError::check(CMSDecoderCreate(&cms
.aref())); // create decoder
368 CFDataRef sig
= this->signature();
369 MacOSError::check(CMSDecoderUpdateMessage(cms
, CFDataGetBytePtr(sig
), CFDataGetLength(sig
)));
370 this->codeDirectory(); // load CodeDirectory (sets mDir)
371 MacOSError::check(CMSDecoderSetDetachedContent(cms
, mDir
));
372 MacOSError::check(CMSDecoderFinalizeMessage(cms
));
373 MacOSError::check(CMSDecoderSetSearchKeychain(cms
, cfEmptyArray()));
374 CMSSignerStatus status
;
375 MacOSError::check(CMSDecoderCopySignerStatus(cms
, 0, verificationPolicy(),
376 false, &status
, &mTrust
.aref(), NULL
));
377 if (status
!= kCMSSignerValid
)
378 MacOSError::throwMe(errSecCSSignatureFailed
);
380 // get signing date (we've got the decoder handle right here)
381 mSigningTime
= 0; // "not present" marker (nobody could code sign on Jan 1, 2001 :-)
382 SecCmsMessageRef cmsg
;
383 MacOSError::check(CMSDecoderGetCmsMessage(cms
, &cmsg
));
384 SecCmsSignedDataRef signedData
= NULL
;
385 int numContentInfos
= SecCmsMessageContentLevelCount(cmsg
);
386 for(int dex
= 0; !signedData
&& dex
< numContentInfos
; dex
++) {
387 SecCmsContentInfoRef ci
= SecCmsMessageContentLevel(cmsg
, dex
);
388 SECOidTag tag
= SecCmsContentInfoGetContentTypeTag(ci
);
390 case SEC_OID_PKCS7_SIGNED_DATA
:
391 if (SecCmsSignedDataRef signedData
= SecCmsSignedDataRef(SecCmsContentInfoGetContent(ci
)))
392 if (SecCmsSignerInfoRef signerInfo
= SecCmsSignedDataGetSignerInfo(signedData
, 0))
393 SecCmsSignerInfoGetSigningTime(signerInfo
, &mSigningTime
);
400 // set up the environment for SecTrust
401 MacOSError::check(SecTrustSetAnchorCertificates(mTrust
, cfEmptyArray())); // no anchors
402 CSSM_APPLE_TP_ACTION_DATA actionData
= {
403 CSSM_APPLE_TP_ACTION_VERSION
, // version of data structure
404 CSSM_TP_ACTION_IMPLICIT_ANCHORS
// action flags
407 for (;;) { // at most twice
408 MacOSError::check(SecTrustSetParameters(mTrust
,
409 CSSM_TP_ACTION_DEFAULT
, CFTempData(&actionData
, sizeof(actionData
))));
411 // evaluate trust and extract results
412 SecTrustResultType trustResult
;
413 MacOSError::check(SecTrustEvaluate(mTrust
, &trustResult
));
414 MacOSError::check(SecTrustGetResult(mTrust
, &trustResult
, &mCertChain
.aref(), &mEvalDetails
));
415 CODESIGN_EVAL_STATIC_SIGNATURE_RESULT(this, trustResult
, mCertChain
? CFArrayGetCount(mCertChain
) : 0);
416 switch (trustResult
) {
417 case kSecTrustResultProceed
:
418 case kSecTrustResultConfirm
:
419 case kSecTrustResultUnspecified
:
421 case kSecTrustResultDeny
:
422 MacOSError::throwMe(CSSMERR_APPLETP_TRUST_SETTING_DENY
); // user reject
423 case kSecTrustResultInvalid
:
424 assert(false); // should never happen
425 MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED
);
426 case kSecTrustResultRecoverableTrustFailure
:
427 case kSecTrustResultFatalTrustFailure
:
428 case kSecTrustResultOtherError
:
431 MacOSError::check(SecTrustGetCssmResultCode(mTrust
, &result
));
432 if (result
== CSSMERR_TP_CERT_EXPIRED
&& !(actionData
.ActionFlags
& CSSM_TP_ACTION_ALLOW_EXPIRED
)) {
433 CODESIGN_EVAL_STATIC_SIGNATURE_EXPIRED(this);
434 actionData
.ActionFlags
|= CSSM_TP_ACTION_ALLOW_EXPIRED
;
435 continue; // retry validation
437 MacOSError::throwMe(result
);
440 return actionData
.ActionFlags
& CSSM_TP_ACTION_ALLOW_EXPIRED
;
446 // Return the TP policy used for signature verification.
447 // This policy object is cached and reused.
449 SecPolicyRef
SecStaticCode::verificationPolicy()
452 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3
,
453 &CSSMOID_APPLE_TP_CODE_SIGNING
, &mPolicy
.aref()));
459 // Validate a particular sealed, cached resource against its (special) CodeDirectory slot.
460 // The resource must already have been placed in the cache.
461 // This does NOT perform basic validation.
463 void SecStaticCode::validateComponent(CodeDirectory::SpecialSlot slot
)
465 assert(slot
<= cdSlotMax
);
466 CFDataRef data
= mCache
[slot
];
467 assert(data
); // must be cached
468 if (data
== CFDataRef(kCFNull
)) {
469 if (codeDirectory()->slotIsPresent(-slot
)) // was supposed to be there...
470 MacOSError::throwMe(errSecCSSignatureFailed
); // ... and is missing
472 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data
), CFDataGetLength(data
), -slot
))
473 MacOSError::throwMe(errSecCSSignatureFailed
);
479 // Perform static validation of the main executable.
480 // This reads the main executable from disk and validates it against the
481 // CodeDirectory code slot array.
482 // Note that this is NOT an in-memory validation, and is thus potentially
483 // subject to timing attacks.
485 void SecStaticCode::validateExecutable()
487 DTRACK(CODESIGN_EVAL_STATIC_EXECUTABLE
, this,
488 (char*)this->mainExecutablePath().c_str(), codeDirectory()->nCodeSlots
);
489 const CodeDirectory
*cd
= this->codeDirectory();
491 MacOSError::throwMe(errSecCSUnsigned
);
492 AutoFileDesc
fd(mainExecutablePath(), O_RDONLY
);
493 fd
.fcntl(F_NOCACHE
, true); // turn off page caching (one-pass)
494 if (Universal
*fat
= mRep
->mainExecutableImage())
495 fd
.seek(fat
->archOffset());
496 size_t pageSize
= cd
->pageSize
? (1 << cd
->pageSize
) : 0;
497 size_t remaining
= cd
->codeLimit
;
498 for (size_t slot
= 0; slot
< cd
->nCodeSlots
; ++slot
) {
499 size_t size
= min(remaining
, pageSize
);
500 if (!cd
->validateSlot(fd
, size
, slot
)) {
501 CODESIGN_EVAL_STATIC_EXECUTABLE_FAIL(this, slot
);
502 mExecutableValidated
= true; // we tried
503 mExecutableValid
= false; // it failed
504 MacOSError::throwMe(errSecCSSignatureFailed
);
508 mExecutableValidated
= true; // we tried
509 mExecutableValid
= true; // it worked
514 // Perform static validation of sealed resources.
516 // This performs a whole-code static resource scan and effectively
517 // computes a concordance between what's on disk and what's in the ResourceDirectory.
518 // Any unsanctioned difference causes an error.
520 void SecStaticCode::validateResources()
523 CFDictionaryRef sealedResources
= resourceDictionary();
524 if (this->resourceBase()) // disk has resources
526 /* go to work below */;
528 MacOSError::throwMe(errSecCSResourcesNotFound
);
529 else // disk has no resources
531 MacOSError::throwMe(errSecCSResourcesNotFound
);
533 return; // no resources, not sealed - fine (no work)
535 // found resources, and they are sealed
536 CFDictionaryRef rules
= cfget
<CFDictionaryRef
>(sealedResources
, "rules");
537 CFDictionaryRef files
= cfget
<CFDictionaryRef
>(sealedResources
, "files");
538 DTRACK(CODESIGN_EVAL_STATIC_RESOURCES
, this,
539 (char*)this->mainExecutablePath().c_str(), int(CFDictionaryGetCount(files
)));
541 // make a shallow copy of the ResourceDirectory so we can "check off" what we find
542 CFRef
<CFMutableDictionaryRef
> resourceMap
= makeCFMutableDictionary(files
);
544 // scan through the resources on disk, checking each against the resourceDirectory
545 CollectingContext
ctx(*this); // collect all failures in here
546 ResourceBuilder
resources(cfString(this->resourceBase()), rules
);
547 mRep
->adjustResources(resources
);
549 ResourceBuilder::Rule
*rule
;
551 while (resources
.next(path
, rule
)) {
552 validateResource(path
, ctx
);
553 CFDictionaryRemoveValue(resourceMap
, CFTempString(path
));
556 if (CFDictionaryGetCount(resourceMap
) > 0) {
557 secdebug("staticCode", "%p sealed resource(s) not found in code", this);
558 CFDictionaryApplyFunction(resourceMap
, SecStaticCode::checkOptionalResource
, &ctx
);
561 // now check for any errors found in the reporting context
567 void SecStaticCode::checkOptionalResource(CFTypeRef key
, CFTypeRef value
, void *context
)
569 CollectingContext
*ctx
= static_cast<CollectingContext
*>(context
);
570 ResourceSeal
seal(value
);
571 if (!seal
.optional())
572 if (key
&& CFGetTypeID(key
) == CFStringGetTypeID()) {
573 ctx
->reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
,
574 CFTempURL(CFStringRef(key
), false, ctx
->code
.resourceBase()));
576 ctx
->reportProblem(errSecCSBadResource
, kSecCFErrorResourceSeal
, key
);
581 // Load, validate, cache, and return CFDictionary forms of sealed resources.
583 CFDictionaryRef
SecStaticCode::infoDictionary()
586 mInfoDict
.take(getDictionary(cdInfoSlot
));
587 secdebug("staticCode", "%p loaded InfoDict %p", this, mInfoDict
.get());
592 CFDictionaryRef
SecStaticCode::entitlements()
594 if (!mEntitlements
) {
596 if (CFDataRef entitlementData
= component(cdEntitlementSlot
)) {
597 validateComponent(cdEntitlementSlot
);
598 const EntitlementBlob
*blob
= reinterpret_cast<const EntitlementBlob
*>(CFDataGetBytePtr(entitlementData
));
599 if (blob
->validateBlob()) {
600 mEntitlements
.take(blob
->entitlements());
601 secdebug("staticCode", "%p loaded Entitlements %p", this, mEntitlements
.get());
603 // we do not consider a different blob type to be an error. We think it's a new format we don't understand
606 return mEntitlements
;
609 CFDictionaryRef
SecStaticCode::resourceDictionary()
611 if (mResourceDict
) // cached
612 return mResourceDict
;
613 if (CFRef
<CFDictionaryRef
> dict
= getDictionary(cdResourceDirSlot
))
614 if (cfscan(dict
, "{rules=%Dn,files=%Dn}")) {
615 secdebug("staticCode", "%p loaded ResourceDict %p",
616 this, mResourceDict
.get());
617 return mResourceDict
= dict
;
625 // Load and cache the resource directory base.
626 // Note that the base is optional for each DiskRep.
628 CFURLRef
SecStaticCode::resourceBase()
630 if (!mGotResourceBase
) {
631 string base
= mRep
->resourcesRootPath();
633 mResourceBase
.take(makeCFURL(base
, true));
634 mGotResourceBase
= true;
636 return mResourceBase
;
641 // Load a component, validate it, convert it to a CFDictionary, and return that.
642 // This will force load and validation, which means that it will perform basic
643 // validation if it hasn't been done yet.
645 CFDictionaryRef
SecStaticCode::getDictionary(CodeDirectory::SpecialSlot slot
)
648 if (CFDataRef infoData
= component(slot
)) {
649 validateComponent(slot
);
650 if (CFDictionaryRef dict
= makeCFDictionaryFrom(infoData
))
653 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
660 // Load, validate, and return a sealed resource.
661 // The resource data (loaded in to memory as a blob) is returned and becomes
662 // the responsibility of the caller; it is NOT cached by SecStaticCode.
664 // A resource that is not sealed will not be returned, and an error will be thrown.
665 // A missing resource will cause an error unless it's marked optional in the Directory.
666 // Under no circumstances will a corrupt resource be returned.
667 // NULL will only be returned for a resource that is neither sealed nor present
668 // (or that is sealed, absent, and marked optional).
669 // If the ResourceDictionary itself is not sealed, this function will always fail.
671 // There is currently no interface for partial retrieval of the resource data.
672 // (Since the ResourceDirectory does not currently support segmentation, all the
673 // data would have to be read anyway, but it could be read into a reusable buffer.)
675 CFDataRef
SecStaticCode::resource(string path
, ValidationContext
&ctx
)
677 if (CFDictionaryRef rdict
= resourceDictionary()) {
678 if (CFTypeRef file
= cfget(rdict
, "files.%s", path
.c_str())) {
679 ResourceSeal seal
= file
;
680 if (!resourceBase()) // no resources in DiskRep
681 MacOSError::throwMe(errSecCSResourcesNotFound
);
682 CFRef
<CFURLRef
> fullpath
= makeCFURL(path
, false, resourceBase());
683 if (CFRef
<CFDataRef
> data
= cfLoadFile(fullpath
)) {
685 hasher(CFDataGetBytePtr(data
), CFDataGetLength(data
));
686 if (hasher
.verify(seal
.hash()))
687 return data
.yield(); // good
689 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAltered
, fullpath
); // altered
691 if (!seal
.optional())
692 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
, fullpath
); // was sealed but is now missing
694 return NULL
; // validly missing
697 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAdded
, CFTempURL(path
, false, resourceBase()));
700 MacOSError::throwMe(errSecCSResourcesNotSealed
);
703 CFDataRef
SecStaticCode::resource(string path
)
705 ValidationContext ctx
;
706 return resource(path
, ctx
);
710 void SecStaticCode::validateResource(string path
, ValidationContext
&ctx
)
712 if (CFDictionaryRef rdict
= resourceDictionary()) {
713 if (CFTypeRef file
= cfget(rdict
, "files.%s", path
.c_str())) {
714 ResourceSeal seal
= file
;
715 if (!resourceBase()) // no resources in DiskRep
716 MacOSError::throwMe(errSecCSResourcesNotFound
);
717 CFRef
<CFURLRef
> fullpath
= makeCFURL(path
, false, resourceBase());
718 AutoFileDesc
fd(cfString(fullpath
), O_RDONLY
, FileDesc::modeMissingOk
); // open optional filee
721 hashFileData(fd
, hasher
);
722 if (hasher
.verify(seal
.hash()))
723 return; // verify good
725 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAltered
, fullpath
); // altered
727 if (!seal
.optional())
728 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
, fullpath
); // was sealed but is now missing
730 return; // validly missing
733 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAdded
, CFTempURL(path
, false, resourceBase()));
735 MacOSError::throwMe(errSecCSResourcesNotSealed
);
740 // Test a CodeDirectory flag.
741 // Returns false if there is no CodeDirectory.
742 // May throw if the CodeDirectory is present but somehow invalid.
744 bool SecStaticCode::flag(uint32_t tested
)
746 if (const CodeDirectory
*cd
= this->codeDirectory(false))
747 return cd
->flags
& tested
;
754 // Retrieve the full SuperBlob containing all internal requirements.
756 const Requirements
*SecStaticCode::internalRequirements()
758 if (CFDataRef req
= component(cdRequirementsSlot
))
759 return (const Requirements
*)CFDataGetBytePtr(req
);
766 // Retrieve a particular internal requirement by type.
768 const Requirement
*SecStaticCode::internalRequirement(SecRequirementType type
)
770 if (const Requirements
*reqs
= internalRequirements())
771 return reqs
->find
<Requirement
>(type
);
778 // Return the Designated Requirement. This can be either explicit in the
779 // Internal Requirements resource, or implicitly generated.
781 const Requirement
*SecStaticCode::designatedRequirement()
783 if (const Requirement
*req
= internalRequirement(kSecDesignatedRequirementType
)) {
784 return req
; // explicit in signing data
787 mDesignatedReq
= defaultDesignatedRequirement();
788 return mDesignatedReq
;
794 // Generate the default (implicit) Designated Requirement for this StaticCode.
795 // This is a heuristic of sorts, and may change over time (for the better, we hope).
797 // The current logic is this:
798 // * If the code is ad-hoc signed, use the CodeDirectory hash directory.
799 // * Otherwise, use the form anchor (anchor) and identifier (CodeDirectory identifier).
800 // ** If the root CA is Apple's, we use the "anchor apple" construct. Otherwise,
801 // we default to the leaf (directly signing) certificate.
803 const Requirement
*SecStaticCode::defaultDesignatedRequirement()
805 validateDirectory(); // need the cert chain
806 Requirement::Maker maker
;
808 // if this is an ad-hoc (unsigned) object, return a cdhash requirement
809 if (flag(kSecCodeSignatureAdhoc
)) {
811 hash(codeDirectory(), codeDirectory()->length());
814 maker
.cdhash(digest
);
816 // always require the identifier
818 maker
.ident(codeDirectory()->identifier());
820 SHA1::Digest anchorHash
;
821 hashOfCertificate(cert(Requirement::anchorCert
), anchorHash
);
822 if (!memcmp(anchorHash
, Requirement::appleAnchorHash(), SHA1::digestLength
)
823 #if defined(TEST_APPLE_ANCHOR)
824 || !memcmp(anchorHash
, Requirement::testAppleAnchorHash(), SHA1::digestLength
)
827 defaultDesignatedAppleAnchor(maker
);
829 defaultDesignatedNonAppleAnchor(maker
);
835 static const uint8_t adcSdkMarker
[] = { APPLE_EXTENSION_OID
, 2, 1 };
836 static const CSSM_DATA adcSdkMarkerOID
= { sizeof(adcSdkMarker
), (uint8_t *)adcSdkMarker
};
838 void SecStaticCode::defaultDesignatedAppleAnchor(Requirement::Maker
&maker
)
840 if (isAppleSDKSignature()) {
841 // get the Common Name DN element for the leaf
842 CFRef
<CFStringRef
> leafCN
;
843 MacOSError::check(SecCertificateCopySubjectComponent(cert(Requirement::leafCert
),
844 &CSSMOID_CommonName
, &leafCN
.aref()));
846 // apple anchor generic and ...
848 maker
.anchorGeneric(); // apple generic anchor and...
849 // ... leaf[subject.CN] = <leaf's subject> and ...
851 maker
.put(opCertField
); // certificate
852 maker
.put(0); // leaf
853 maker
.put("subject.CN"); // [subject.CN]
854 maker
.put(matchEqual
); // =
855 maker
.putData(leafCN
); // <leaf CN>
856 // ... cert 1[field.<marker>] exists
857 maker
.put(opCertGeneric
); // certificate
859 maker
.putData(adcSdkMarkerOID
.Data
, adcSdkMarkerOID
.Length
); // [field.<marker>]
860 maker
.put(matchExists
); // exists
864 // otherwise, claim this program for Apple
868 bool SecStaticCode::isAppleSDKSignature()
870 if (CFArrayRef certChain
= certificates()) // got cert chain
871 if (CFArrayGetCount(certChain
) == 3) // leaf, one intermediate, anchor
872 if (SecCertificateRef intermediate
= cert(1)) // get intermediate
873 if (certificateHasField(intermediate
, CssmOid::overlay(adcSdkMarkerOID
)))
878 void SecStaticCode::defaultDesignatedNonAppleAnchor(Requirement::Maker
&maker
)
880 // get the Organization DN element for the leaf
881 CFRef
<CFStringRef
> leafOrganization
;
882 MacOSError::check(SecCertificateCopySubjectComponent(cert(Requirement::leafCert
),
883 &CSSMOID_OrganizationName
, &leafOrganization
.aref()));
885 // now step up the cert chain looking for the first cert with a different one
886 int slot
= Requirement::leafCert
; // start at leaf
887 if (leafOrganization
) {
888 while (SecCertificateRef ca
= cert(slot
+1)) { // NULL if you over-run the anchor slot
889 CFRef
<CFStringRef
> caOrganization
;
890 MacOSError::check(SecCertificateCopySubjectComponent(ca
, &CSSMOID_OrganizationName
, &caOrganization
.aref()));
891 if (!caOrganization
|| CFStringCompare(leafOrganization
, caOrganization
, 0) != kCFCompareEqualTo
)
895 if (slot
== CFArrayGetCount(mCertChain
) - 1) // went all the way to the anchor...
896 slot
= Requirement::anchorCert
; // ... so say that
899 // nail the last cert with the leaf's Organization value
900 SHA1::Digest authorityHash
;
901 hashOfCertificate(cert(slot
), authorityHash
);
902 maker
.anchor(slot
, authorityHash
);
907 // Validate a SecStaticCode against the internal requirement of a particular type.
909 void SecStaticCode::validateRequirements(SecRequirementType type
, SecStaticCode
*target
,
910 OSStatus nullError
/* = noErr */)
912 DTRACK(CODESIGN_EVAL_STATIC_INTREQ
, this, type
, target
, nullError
);
913 if (const Requirement
*req
= internalRequirement(type
))
914 target
->validateRequirements(req
, nullError
? nullError
: errSecCSReqFailed
);
916 MacOSError::throwMe(nullError
);
923 // Validate this StaticCode against an external Requirement
925 void SecStaticCode::validateRequirements(const Requirement
*req
, OSStatus failure
)
929 req
->validate(Requirement::Context(mCertChain
, infoDictionary(), entitlements(), codeDirectory()), failure
);
934 // Retrieve one certificate from the cert chain.
935 // Positive and negative indices can be used:
936 // [ leaf, intermed-1, ..., intermed-n, anchor ]
938 // Returns NULL if unavailable for any reason.
940 SecCertificateRef
SecStaticCode::cert(int ix
)
942 validateDirectory(); // need cert chain
944 CFIndex length
= CFArrayGetCount(mCertChain
);
947 if (ix
>= 0 && ix
< length
)
948 return SecCertificateRef(CFArrayGetValueAtIndex(mCertChain
, ix
));
953 CFArrayRef
SecStaticCode::certificates()
955 validateDirectory(); // need cert chain
961 // Gather (mostly) API-official information about this StaticCode.
963 // This method lives in the twilight between the API and internal layers,
964 // since it generates API objects (Sec*Refs) for return.
966 CFDictionaryRef
SecStaticCode::signingInformation(SecCSFlags flags
)
969 // Start with the pieces that we return even for unsigned code.
970 // This makes Sec[Static]CodeRefs useful as API-level replacements
971 // of our internal OSXCode objects.
973 CFRef
<CFMutableDictionaryRef
> dict
= makeCFMutableDictionary(1,
974 kSecCodeInfoMainExecutable
, CFTempURL(this->mainExecutablePath()).get()
978 // If we're not signed, this is all you get
980 if (!this->isSigned())
984 // Add the generic attributes that we always include
986 CFDictionaryAddValue(dict
, kSecCodeInfoIdentifier
, CFTempString(this->identifier()));
987 CFDictionaryAddValue(dict
, kSecCodeInfoFormat
, CFTempString(this->format()));
988 CFDictionaryAddValue(dict
, kSecCodeInfoSource
, CFTempString(this->signatureSource()));
989 if (CFDictionaryRef info
= this->infoDictionary())
990 CFDictionaryAddValue(dict
, kSecCodeInfoPList
, info
);
991 CFDictionaryAddValue(dict
, kSecCodeInfoUnique
, this->cdHash());
994 // kSecCSSigningInformation adds information about signing certificates and chains
996 if (flags
& kSecCSSigningInformation
) {
997 if (CFArrayRef certs
= this->certificates())
998 CFDictionaryAddValue(dict
, kSecCodeInfoCertificates
, certs
);
999 if (CFDataRef sig
= this->signature())
1000 CFDictionaryAddValue(dict
, kSecCodeInfoCMS
, sig
);
1002 CFDictionaryAddValue(dict
, kSecCodeInfoTrust
, mTrust
);
1003 if (CFAbsoluteTime time
= this->signingTime())
1004 if (CFRef
<CFDateRef
> date
= CFDateCreate(NULL
, time
))
1005 CFDictionaryAddValue(dict
, kSecCodeInfoTime
, date
);
1009 // kSecCSRequirementInformation adds information on requirements
1011 if (flags
& kSecCSRequirementInformation
) {
1012 if (const Requirements
*reqs
= this->internalRequirements()) {
1013 CFDictionaryAddValue(dict
, kSecCodeInfoRequirements
,
1014 CFTempString(Dumper::dump(reqs
)));
1015 CFDictionaryAddValue(dict
, kSecCodeInfoRequirementData
, CFTempData(*reqs
));
1018 const Requirement
*dreq
= this->designatedRequirement();
1019 CFRef
<SecRequirementRef
> dreqRef
= (new SecRequirement(dreq
))->handle();
1020 CFDictionaryAddValue(dict
, kSecCodeInfoDesignatedRequirement
, dreqRef
);
1021 if (this->internalRequirement(kSecDesignatedRequirementType
)) { // explicit
1022 CFRef
<SecRequirementRef
> ddreqRef
= (new SecRequirement(this->defaultDesignatedRequirement(), true))->handle();
1023 CFDictionaryAddValue(dict
, kSecCodeInfoImplicitDesignatedRequirement
, ddreqRef
);
1024 } else { // implicit
1025 CFDictionaryAddValue(dict
, kSecCodeInfoImplicitDesignatedRequirement
, dreqRef
);
1028 if (CFDataRef ent
= this->component(cdEntitlementSlot
))
1029 CFDictionaryAddValue(dict
, kSecCodeInfoEntitlements
, ent
);
1033 // kSecCSInternalInformation adds internal information meant to be for Apple internal
1034 // use (SPI), and not guaranteed to be stable. Primarily, this is data we want
1035 // to reliably transmit through the API wall so that code outside the Security.framework
1036 // can use it without having to play nasty tricks to get it.
1038 if (flags
& kSecCSInternalInformation
) {
1040 CFDictionaryAddValue(dict
, kSecCodeInfoCodeDirectory
, mDir
);
1041 CFDictionaryAddValue(dict
, kSecCodeInfoCodeOffset
, CFTempNumber(mRep
->signingBase()));
1042 if (CFDictionaryRef resources
= resourceDictionary())
1043 CFDictionaryAddValue(dict
, kSecCodeInfoResourceDirectory
, resources
);
1048 // kSecCSContentInformation adds more information about the physical layout
1049 // of the signed code. This is (only) useful for packaging or patching-oriented
1052 if (flags
& kSecCSContentInformation
)
1053 if (CFRef
<CFArrayRef
> files
= mRep
->modifiedFiles())
1054 CFDictionaryAddValue(dict
, kSecCodeInfoChangedFiles
, files
);
1056 return dict
.yield();
1061 // Resource validation contexts.
1062 // The default context simply throws a CSError, rudely terminating the operation.
1064 SecStaticCode::ValidationContext::~ValidationContext()
1067 void SecStaticCode::ValidationContext::reportProblem(OSStatus rc
, CFStringRef type
, CFTypeRef value
)
1069 CSError::throwMe(rc
, type
, value
);
1072 void SecStaticCode::CollectingContext::reportProblem(OSStatus rc
, CFStringRef type
, CFTypeRef value
)
1074 if (mStatus
== noErr
)
1075 mStatus
= rc
; // record first failure for eventual error return
1078 mCollection
.take(makeCFMutableDictionary());
1079 CFMutableArrayRef element
= CFMutableArrayRef(CFDictionaryGetValue(mCollection
, type
));
1081 element
= makeCFMutableArray(0);
1084 CFDictionaryAddValue(mCollection
, type
, element
);
1087 CFArrayAppendValue(element
, value
);
1091 void SecStaticCode::CollectingContext::throwMe()
1093 assert(mStatus
!= noErr
);
1094 throw CSError(mStatus
, mCollection
.yield());
1098 } // end namespace CodeSigning
1099 } // end namespace Security