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 "csutilities.h"
35 #include <CoreFoundation/CFURLAccess.h>
36 #include <Security/SecPolicyPriv.h>
37 #include <Security/SecTrustPriv.h>
38 #include <Security/CMSPrivate.h>
39 #include <Security/SecCmsContentInfo.h>
40 #include <Security/SecCmsSignerInfo.h>
41 #include <Security/SecCmsSignedData.h>
42 #include <security_utilities/unix++.h>
43 #include <security_codesigning/cfmunge.h>
44 #include <Security/CMSDecoder.h>
48 namespace CodeSigning
{
50 using namespace UnixPlusPlus
;
54 // We use a DetachedRep to interpose (filter) the genuine DiskRep representing
55 // the code on disk, *if* a detached signature was set on this object. In this
56 // situation, mRep will point to a (2 element) chain of DiskReps.
58 // This is a neat way of dealing with the (unusual) detached-signature case
59 // without disturbing things unduly. Consider DetachedDiskRep to be closely
60 // married to SecStaticCode; it's unlikely to work right if you use it elsewhere.
62 class DetachedRep
: public DiskRep
{
64 DetachedRep(CFDataRef sig
, DiskRep
*orig
);
66 const RefPointer
<DiskRep
> original
; // underlying representation
68 DiskRep
*base() { return original
; }
69 CFDataRef
component(CodeDirectory::SpecialSlot slot
);
70 std::string
mainExecutablePath() { return original
->mainExecutablePath(); }
71 CFURLRef
canonicalPath() { return original
->canonicalPath(); }
72 std::string
recommendedIdentifier() { return original
->recommendedIdentifier(); }
73 std::string
resourcesRootPath() { return original
->resourcesRootPath(); }
74 CFDictionaryRef
defaultResourceRules() { return original
->defaultResourceRules(); }
75 Universal
*mainExecutableImage() { return original
->mainExecutableImage(); }
76 size_t signingBase() { return original
->signingBase(); }
77 size_t signingLimit() { return original
->signingLimit(); }
78 std::string
format() { return original
->format(); }
79 FileDesc
&fd() { return original
->fd(); }
80 void flush() { return original
->flush(); }
83 CFCopyRef
<CFDataRef
> mSignature
;
84 const EmbeddedSignatureBlob
*mArch
; // current architecture; points into mSignature
85 const EmbeddedSignatureBlob
*mGlobal
; // shared elements; points into mSignature
90 // Construct a SecStaticCode object given a disk representation object
92 SecStaticCode::SecStaticCode(DiskRep
*rep
)
94 mValidated(false), mExecutableValidated(false),
95 mDesignatedReq(NULL
), mGotResourceBase(false), mEvalDetails(NULL
)
101 // Clean up a SecStaticCode object
103 SecStaticCode::~SecStaticCode() throw()
105 ::free(const_cast<Requirement
*>(mDesignatedReq
));
110 // Attach a detached signature.
112 void SecStaticCode::detachedSignature(CFDataRef sigData
)
115 mRep
= new DetachedRep(sigData
, mRep
->base());
122 // Do ::required, but convert incoming SecCodeRefs to their SecStaticCodeRefs
125 SecStaticCode
*SecStaticCode::requiredStatic(SecStaticCodeRef ref
)
127 SecCFObject
*object
= SecCFObject::required(ref
, errSecCSInvalidObjectRef
);
128 if (SecStaticCode
*scode
= dynamic_cast<SecStaticCode
*>(object
))
130 else if (SecCode
*code
= dynamic_cast<SecCode
*>(object
))
131 return code
->staticCode();
132 else // neither (a SecSomethingElse)
133 MacOSError::throwMe(errSecCSInvalidObjectRef
);
136 SecCode
*SecStaticCode::optionalDynamic(SecStaticCodeRef ref
)
138 SecCFObject
*object
= SecCFObject::required(ref
, errSecCSInvalidObjectRef
);
139 if (dynamic_cast<SecStaticCode
*>(object
))
141 else if (SecCode
*code
= dynamic_cast<SecCode
*>(object
))
143 else // neither (a SecSomethingElse)
144 MacOSError::throwMe(errSecCSInvalidObjectRef
);
149 // Void all cached validity data.
151 // We also throw out cached components, because the new signature data may have
152 // a different idea of what components should be present. We could reconcile the
153 // cached data instead, if performance seems to be impacted.
155 void SecStaticCode::resetValidity()
157 secdebug("staticCode", "%p resetting validity status", this);
159 mExecutableValidated
= false;
162 for (unsigned n
= 0; n
< cdSlotCount
; n
++)
165 mResourceDict
= NULL
;
166 mDesignatedReq
= NULL
;
175 // Retrieve a sealed component by special slot index.
176 // If the CodeDirectory has already been validated, validate against that.
177 // Otherwise, retrieve the component without validation (but cache it). Validation
178 // will go through the cache and validate all cached components.
180 CFDataRef
SecStaticCode::component(CodeDirectory::SpecialSlot slot
)
182 assert(slot
<= cdSlotMax
);
184 CFRef
<CFDataRef
> &cache
= mCache
[slot
];
186 if (CFRef
<CFDataRef
> data
= mRep
->component(slot
)) {
187 if (validated()) // if the directory has been validated...
188 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data
), // ... and it's no good
189 CFDataGetLength(data
), -slot
))
190 MacOSError::throwMe(errSecCSSignatureFailed
); // ... then bail
191 cache
= data
; // it's okay, cache it
192 } else { // absent, mark so
193 if (validated()) // if directory has been validated...
194 if (codeDirectory()->slotIsPresent(-slot
)) // ... and the slot is NOT missing
195 MacOSError::throwMe(errSecCSSignatureFailed
); // was supposed to be there
196 cache
= CFDataRef(kCFNull
); // white lie
199 return (cache
== CFDataRef(kCFNull
)) ? NULL
: cache
.get();
204 // Get the CodeDirectory.
205 // Throws (if check==true) or returns NULL (check==false) if there is none.
206 // Always throws if the CodeDirectory exists but is invalid.
207 // NEVER validates against the signature.
209 const CodeDirectory
*SecStaticCode::codeDirectory(bool check
/* = true */)
212 if (mDir
.take(mRep
->codeDirectory())) {
213 const CodeDirectory
*dir
= reinterpret_cast<const CodeDirectory
*>(CFDataGetBytePtr(mDir
));
218 return reinterpret_cast<const CodeDirectory
*>(CFDataGetBytePtr(mDir
));
220 MacOSError::throwMe(errSecCSUnsigned
);
226 // Return the CMS signature blob; NULL if none found.
228 CFDataRef
SecStaticCode::signature()
231 mSignature
.take(mRep
->signature());
234 MacOSError::throwMe(errSecCSUnsigned
);
239 // Verify the signature on the CodeDirectory.
240 // If this succeeds (doesn't throw), the CodeDirectory is statically trustworthy.
241 // Any outcome (successful or not) is cached for the lifetime of the StaticCode.
243 void SecStaticCode::validateDirectory()
245 // echo previous outcome, if any
248 // perform validation (or die trying)
249 secdebug("staticCode", "%p validating directory", this);
250 mValidationExpired
= verifySignature();
251 component(cdInfoSlot
); // force load of Info Dictionary (if any)
252 for (CodeDirectory::SpecialSlot slot
= codeDirectory()->nSpecialSlots
;
254 if (mCache
[slot
]) // if we already loaded that resource...
255 validateComponent(slot
); // ... then check it now
256 mValidated
= true; // we've done the deed...
257 mValidationResult
= noErr
; // ... and it was good
258 } catch (const CommonError
&err
) {
260 mValidationResult
= err
.osStatus();
263 secdebug("staticCode", "%p validation threw non-common exception", this);
265 mValidationResult
= errSecCSInternalError
;
269 if (mValidationResult
== noErr
) {
270 if (mValidationExpired
)
271 if ((apiFlags() & kSecCSConsiderExpiration
)
272 || (codeDirectory()->flags
& kSecCodeSignatureForceExpiration
))
273 MacOSError::throwMe(CSSMERR_TP_CERT_EXPIRED
);
275 MacOSError::throwMe(mValidationResult
);
280 // Get the (signed) signing date from the code signature.
281 // Sadly, we need to validate the signature to get the date (as a side benefit).
282 // This means that you can't get the signing time for invalidly signed code.
284 // We could run the decoder "almost to" verification to avoid this, but there seems
285 // little practical point to such a duplication of effort.
287 CFAbsoluteTime
SecStaticCode::signingTime()
295 // Verify the CMS signature on the CodeDirectory.
296 // This performs the cryptographic tango. It returns if the signature is valid,
297 // or throws if it is not. As a side effect, a successful return sets up the
298 // cached certificate chain for future use.
300 bool SecStaticCode::verifySignature()
302 // ad-hoc signed code is considered validly signed by definition
303 if (flag(kSecCodeSignatureAdhoc
)) {
304 secdebug("staticCode", "%p considered verified since it is ad-hoc", this);
308 // decode CMS and extract SecTrust for verification
309 secdebug("staticCode", "%p verifying signature", this);
310 CFRef
<CMSDecoderRef
> cms
;
311 MacOSError::check(CMSDecoderCreate(&cms
.aref())); // create decoder
312 CFDataRef sig
= this->signature();
313 MacOSError::check(CMSDecoderUpdateMessage(cms
, CFDataGetBytePtr(sig
), CFDataGetLength(sig
)));
314 this->codeDirectory(); // load CodeDirectory (sets mDir)
315 MacOSError::check(CMSDecoderSetDetachedContent(cms
, mDir
));
316 MacOSError::check(CMSDecoderFinalizeMessage(cms
));
317 MacOSError::check(CMSDecoderSetSearchKeychain(cms
, cfEmptyArray()));
318 CMSSignerStatus status
;
319 MacOSError::check(CMSDecoderCopySignerStatus(cms
, 0, verificationPolicy(),
320 false, &status
, &mTrust
.aref(), NULL
));
322 // get signing date (we've got the decoder handle right here)
323 mSigningTime
= 0; // "not present" marker (nobody could code sign on Jan 1, 2001 :-)
324 SecCmsMessageRef cmsg
;
325 MacOSError::check(CMSDecoderGetCmsMessage(cms
, &cmsg
));
326 SecCmsSignedDataRef signedData
= NULL
;
327 int numContentInfos
= SecCmsMessageContentLevelCount(cmsg
);
328 for(int dex
= 0; !signedData
&& dex
< numContentInfos
; dex
++) {
329 SecCmsContentInfoRef ci
= SecCmsMessageContentLevel(cmsg
, dex
);
330 SECOidTag tag
= SecCmsContentInfoGetContentTypeTag(ci
);
332 case SEC_OID_PKCS7_SIGNED_DATA
:
333 if (SecCmsSignedDataRef signedData
= SecCmsSignedDataRef(SecCmsContentInfoGetContent(ci
)))
334 if (SecCmsSignerInfoRef signerInfo
= SecCmsSignedDataGetSignerInfo(signedData
, 0))
335 SecCmsSignerInfoGetSigningTime(signerInfo
, &mSigningTime
);
342 // set up the environment for SecTrust
343 MacOSError::check(SecTrustSetAnchorCertificates(mTrust
, cfEmptyArray())); // no anchors
344 CSSM_APPLE_TP_ACTION_DATA actionData
= {
345 CSSM_APPLE_TP_ACTION_VERSION
, // version of data structure
346 CSSM_TP_ACTION_IMPLICIT_ANCHORS
// action flags
350 MacOSError::check(SecTrustSetParameters(mTrust
,
351 CSSM_TP_ACTION_DEFAULT
, CFTempData(&actionData
, sizeof(actionData
))));
353 // evaluate trust and extract results
354 SecTrustResultType trustResult
;
355 MacOSError::check(SecTrustEvaluate(mTrust
, &trustResult
));
356 MacOSError::check(SecTrustGetResult(mTrust
, &trustResult
, &mCertChain
.aref(), &mEvalDetails
));
357 secdebug("staticCode", "%p verification result=%d chain=%ld",
358 this, trustResult
, mCertChain
? CFArrayGetCount(mCertChain
) : -1);
359 switch (trustResult
) {
360 case kSecTrustResultProceed
:
361 case kSecTrustResultConfirm
:
362 case kSecTrustResultUnspecified
:
364 case kSecTrustResultDeny
:
365 MacOSError::throwMe(CSSMERR_APPLETP_TRUST_SETTING_DENY
); // user reject
366 case kSecTrustResultInvalid
:
367 assert(false); // should never happen
368 MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED
);
369 case kSecTrustResultRecoverableTrustFailure
:
370 case kSecTrustResultFatalTrustFailure
:
371 case kSecTrustResultOtherError
:
374 MacOSError::check(SecTrustGetCssmResultCode(mTrust
, &result
));
375 if (result
== CSSMERR_TP_CERT_EXPIRED
&& !(actionData
.ActionFlags
& CSSM_TP_ACTION_ALLOW_EXPIRED
)) {
376 secdebug("staticCode", "expired certificate(s); retrying validation");
377 actionData
.ActionFlags
|= CSSM_TP_ACTION_ALLOW_EXPIRED
;
378 continue; // retry validation
380 MacOSError::throwMe(result
);
383 return actionData
.ActionFlags
& CSSM_TP_ACTION_ALLOW_EXPIRED
;
389 // Return the TP policy used for signature verification.
390 // This policy object is cached and reused.
392 SecPolicyRef
SecStaticCode::verificationPolicy()
395 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3
,
396 &CSSMOID_APPLE_TP_CODE_SIGNING
, &mPolicy
.aref()));
402 // Validate a particular sealed, cached resource against its (special) CodeDirectory slot.
403 // The resource must already have been placed in the cache.
404 // This does NOT perform basic validation.
406 void SecStaticCode::validateComponent(CodeDirectory::SpecialSlot slot
)
408 CFDataRef data
= mCache
[slot
];
409 assert(data
); // must be cached
410 if (data
== CFDataRef(kCFNull
)) {
411 if (codeDirectory()->slotIsPresent(-slot
)) // was supposed to be there...
412 MacOSError::throwMe(errSecCSSignatureFailed
); // ... and is missing
414 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data
), CFDataGetLength(data
), -slot
))
415 MacOSError::throwMe(errSecCSSignatureFailed
);
421 // Perform static validation of the main executable.
422 // This reads the main executable from disk and validates it against the
423 // CodeDirectory code slot array.
424 // Note that this is NOT an in-memory validation, and is thus potentially
425 // subject to timing attacks.
427 void SecStaticCode::validateExecutable()
429 secdebug("staticCode", "%p performing static main exec validate of %s",
430 this, mainExecutablePath().c_str());
431 const CodeDirectory
*cd
= this->codeDirectory();
433 MacOSError::throwMe(errSecCSUnsigned
);
434 AutoFileDesc
fd(mainExecutablePath(), O_RDONLY
);
435 fd
.fcntl(F_NOCACHE
, true); // turn off page caching (one-pass)
436 if (Universal
*fat
= mRep
->mainExecutableImage())
437 fd
.seek(fat
->archOffset());
438 size_t pageSize
= cd
->pageSize
? (1 << cd
->pageSize
) : 0;
439 size_t remaining
= cd
->codeLimit
;
440 for (size_t slot
= 0; slot
< cd
->nCodeSlots
; ++slot
) {
441 size_t size
= min(remaining
, pageSize
);
442 if (!cd
->validateSlot(fd
, size
, slot
)) {
443 secdebug("staticCode", "%p failed static validation of code page %zd", this, slot
);
444 mExecutableValidated
= true; // we tried
445 mExecutableValid
= false; // it failed
446 MacOSError::throwMe(errSecCSSignatureFailed
);
450 secdebug("staticCode", "%p validated full executable (%d pages)",
451 this, int(cd
->nCodeSlots
));
452 mExecutableValidated
= true; // we tried
453 mExecutableValid
= true; // it worked
458 // Perform static validation of sealed resources.
460 // This performs a whole-code static resource scan and effectively
461 // computes a concordance between what's on disk and what's in the ResourceDirectory.
462 // Any unsanctioned difference causes an error.
464 void SecStaticCode::validateResources()
467 CFDictionaryRef sealedResources
= resourceDictionary();
468 if (this->resourceBase()) // disk has resources
470 /* go to work below */;
472 MacOSError::throwMe(errSecCSResourcesNotFound
);
473 else // disk has no resources
475 MacOSError::throwMe(errSecCSResourcesNotFound
);
477 return; // no resources, not sealed - fine (no work)
479 // found resources, and they are sealed
480 CFDictionaryRef rules
= cfget
<CFDictionaryRef
>(sealedResources
, "rules");
481 CFDictionaryRef files
= cfget
<CFDictionaryRef
>(sealedResources
, "files");
482 secdebug("staticCode", "%p verifying %d sealed resources",
483 this, int(CFDictionaryGetCount(files
)));
485 // make a shallow copy of the ResourceDirectory so we can "check off" what we find
486 CFRef
<CFMutableDictionaryRef
> resourceMap
= CFDictionaryCreateMutableCopy(NULL
,
487 CFDictionaryGetCount(files
), files
);
491 // scan through the resources on disk, checking each against the resourceDirectory
492 CollectingContext
ctx(*this); // collect all failures in here
493 ResourceBuilder
resources(cfString(this->resourceBase()), rules
);
495 ResourceBuilder::Rule
*rule
;
497 while (resources
.next(path
, rule
)) {
498 if (CFDataRef value
= resource(path
, ctx
))
500 CFDictionaryRemoveValue(resourceMap
, CFTempString(path
));
501 secdebug("staticCode", "%p validated %s", this, path
.c_str());
504 if (CFDictionaryGetCount(resourceMap
) > 0) {
505 secdebug("staticCode", "%p sealed resource(s) not found in code", this);
506 CFDictionaryApplyFunction(resourceMap
, SecStaticCode::checkOptionalResource
, &ctx
);
509 // now check for any errors found in the reporting context
513 secdebug("staticCode", "%p sealed resources okay", this);
517 void SecStaticCode::checkOptionalResource(CFTypeRef key
, CFTypeRef value
, void *context
)
519 CollectingContext
*ctx
= static_cast<CollectingContext
*>(context
);
520 ResourceSeal
seal(value
);
521 if (!seal
.optional())
522 if (key
&& CFGetTypeID(key
) == CFStringGetTypeID()) {
523 ctx
->reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
,
524 CFTempURL(CFStringRef(key
), false, ctx
->code
.resourceBase()));
526 ctx
->reportProblem(errSecCSBadResource
, kSecCFErrorResourceSeal
, key
);
531 // Load, validate, cache, and return CFDictionary forms of sealed resources.
533 CFDictionaryRef
SecStaticCode::infoDictionary()
536 mInfoDict
.take(getDictionary(cdInfoSlot
));
537 secdebug("staticCode", "%p loaded InfoDict %p", this, mInfoDict
.get());
542 CFDictionaryRef
SecStaticCode::resourceDictionary()
544 if (mResourceDict
) // cached
545 return mResourceDict
;
546 if (CFRef
<CFDictionaryRef
> dict
= getDictionary(cdResourceDirSlot
))
547 if (cfscan(dict
, "{rules=%Dn,files=%Dn}")) {
548 secdebug("staticCode", "%p loaded ResourceDict %p",
549 this, mResourceDict
.get());
550 return mResourceDict
= dict
;
558 // Load and cache the resource directory base.
559 // Note that the base is optional for each DiskRep.
561 CFURLRef
SecStaticCode::resourceBase()
563 if (!mGotResourceBase
) {
564 string base
= mRep
->resourcesRootPath();
566 mResourceBase
.take(makeCFURL(base
, true));
567 mGotResourceBase
= true;
569 return mResourceBase
;
574 // Load a component, validate it, convert it to a CFDictionary, and return that.
575 // This will force load and validation, which means that it will perform basic
576 // validation if it hasn't been done yet.
578 CFDictionaryRef
SecStaticCode::getDictionary(CodeDirectory::SpecialSlot slot
)
581 if (CFDataRef infoData
= component(slot
)) {
582 validateComponent(slot
);
583 if (CFDictionaryRef dict
= makeCFDictionaryFrom(infoData
))
586 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
593 // Load, validate, and return a sealed resource.
594 // The resource data (loaded in to memory as a blob) is returned and becomes
595 // the responsibility of the caller; it is NOT cached by SecStaticCode.
597 // A resource that is not sealed will not be returned, and an error will be thrown.
598 // A missing resource will cause an error unless it's marked optional in the Directory.
599 // Under no circumstances will a corrupt resource be returned.
600 // NULL will only be returned for a resource that is neither sealed nor present
601 // (or that is sealed, absent, and marked optional).
602 // If the ResourceDictionary itself is not sealed, this function will always fail.
604 // There is currently no interface for partial retrieval of the resource data.
605 // (Since the ResourceDirectory does not currently support segmentation, all the
606 // data would have to be read anyway, but it could be read into a reusable buffer.)
608 CFDataRef
SecStaticCode::resource(string path
, ValidationContext
&ctx
)
610 if (CFDictionaryRef rdict
= resourceDictionary()) {
611 if (CFTypeRef file
= cfget(rdict
, "files.%s", path
.c_str())) {
612 ResourceSeal seal
= file
;
613 if (!resourceBase()) // no resources in DiskRep
614 MacOSError::throwMe(errSecCSResourcesNotFound
);
615 CFRef
<CFURLRef
> fullpath
= makeCFURL(path
, false, resourceBase());
616 if (CFRef
<CFDataRef
> data
= cfLoadFile(fullpath
)) {
618 hasher(CFDataGetBytePtr(data
), CFDataGetLength(data
));
619 if (hasher
.verify(seal
.hash()))
620 return data
.yield(); // good
622 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAltered
, fullpath
); // altered
624 if (!seal
.optional())
625 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
, fullpath
); // was sealed but is now missing
627 return NULL
; // validly missing
630 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAdded
, CFTempURL(path
, false, resourceBase()));
633 MacOSError::throwMe(errSecCSResourcesNotSealed
);
636 CFDataRef
SecStaticCode::resource(string path
)
638 ValidationContext ctx
;
639 return resource(path
, ctx
);
644 // Test a CodeDirectory flag.
645 // Returns false if there is no CodeDirectory.
646 // May throw if the CodeDirectory is present but somehow invalid.
648 bool SecStaticCode::flag(uint32_t tested
)
650 if (const CodeDirectory
*cd
= this->codeDirectory(false))
651 return cd
->flags
& tested
;
658 // Retrieve the full SuperBlob containing all internal requirements.
660 const Requirements
*SecStaticCode::internalRequirements()
662 if (CFDataRef req
= component(cdRequirementsSlot
))
663 return (const Requirements
*)CFDataGetBytePtr(req
);
670 // Retrieve a particular internal requirement by type.
672 const Requirement
*SecStaticCode::internalRequirement(SecRequirementType type
)
674 if (const Requirements
*reqs
= internalRequirements())
675 return reqs
->find
<Requirement
>(type
);
682 // Return the Designated Requirement. This can be either explicit in the
683 // Internal Requirements resource, or implicitly generated.
685 const Requirement
*SecStaticCode::designatedRequirement()
687 if (const Requirement
*req
= internalRequirement(kSecDesignatedRequirementType
)) {
688 return req
; // explicit in signing data
691 mDesignatedReq
= defaultDesignatedRequirement();
692 return mDesignatedReq
;
698 // Generate the default (implicit) Designated Requirement for this StaticCode.
699 // This is a heuristic of sorts, and may change over time (for the better, we hope).
701 // The current logic is this:
702 // * If the code is ad-hoc signed, use the CodeDirectory hash directory.
703 // * Otherwise, use the form anchor (anchor) and identifier (CodeDirectory identifier).
704 // ** If the root CA is Apple's, we use the "anchor apple" construct. Otherwise,
705 // we default to the leaf (directly signing) certificate.
707 const Requirement
*SecStaticCode::defaultDesignatedRequirement()
709 validateDirectory(); // need the cert chain
710 Requirement::Maker maker
;
712 // if this is an ad-hoc (unsigned) object, return a cdhash requirement
713 if (flag(kSecCodeSignatureAdhoc
)) {
715 hash(codeDirectory(), codeDirectory()->length());
718 maker
.cdhash(digest
);
720 // always require the identifier
722 maker
.ident(codeDirectory()->identifier());
724 SHA1::Digest anchorHash
;
725 hashOfCertificate(cert(Requirement::anchorCert
), anchorHash
);
726 if (!memcmp(anchorHash
, Requirement::appleAnchorHash(), SHA1::digestLength
)
727 #if defined(TEST_APPLE_ANCHOR)
728 || !memcmp(anchorHash
, Requirement::testAppleAnchorHash(), SHA1::digestLength
)
731 maker
.anchor(); // canonical Apple anchor
733 // we don't know anything more, so we'll (conservatively) pick the leaf
734 SHA1::Digest leafHash
;
735 hashOfCertificate(cert(Requirement::leafCert
), leafHash
);
736 maker
.anchor(Requirement::leafCert
, leafHash
);
745 // Validate a SecStaticCode against the internal requirement of a particular type.
747 void SecStaticCode::validateRequirements(SecRequirementType type
, SecStaticCode
*target
,
748 OSStatus nullError
/* = noErr */)
750 secdebug("staticCode", "%p validating %s requirements for %p",
751 this, Requirement::typeNames
[type
], target
);
752 if (const Requirement
*req
= internalRequirement(type
))
753 target
->validateRequirements(req
, nullError
? nullError
: errSecCSReqFailed
);
754 else if (nullError
) {
755 secdebug("staticCode", "%p NULL validate for %s prohibited",
756 this, Requirement::typeNames
[type
]);
757 MacOSError::throwMe(nullError
);
759 secdebug("staticCode", "%p NULL validate (no requirements for %s)",
760 this, Requirement::typeNames
[type
]);
765 // Validate this StaticCode against an external Requirement
767 void SecStaticCode::validateRequirements(const Requirement
*req
, OSStatus failure
)
771 req
->validate(Requirement::Context(mCertChain
, infoDictionary(), codeDirectory()), failure
);
776 // Retrieve one certificate from the cert chain.
777 // Positive and negative indices can be used:
778 // [ leaf, intermed-1, ..., intermed-n, anchor ]
780 // Returns NULL if unavailable for any reason.
782 SecCertificateRef
SecStaticCode::cert(int ix
)
784 validateDirectory(); // need cert chain
787 ix
+= CFArrayGetCount(mCertChain
);
788 if (CFTypeRef element
= CFArrayGetValueAtIndex(mCertChain
, ix
))
789 return SecCertificateRef(element
);
794 CFArrayRef
SecStaticCode::certificates()
796 validateDirectory(); // need cert chain
802 // Gather API-official information about this StaticCode.
804 // This method lives in the twilight between the API and internal layers,
805 // since it generates API objects (Sec*Refs) for return.
807 CFDictionaryRef
SecStaticCode::signingInformation(SecCSFlags flags
)
810 // Start with the pieces that we return even for unsigned code.
811 // This makes Sec[Static]CodeRefs useful as API-level replacements
812 // of our internal OSXCode objects.
814 CFRef
<CFMutableDictionaryRef
> dict
= makeCFMutableDictionary(1,
815 kSecCodeInfoMainExecutable
, CFTempURL(this->mainExecutablePath()).get()
819 // If we're not signed, this is all you get
821 if (!this->isSigned())
826 // Now add the generic attributes that we always include
828 CFDictionaryAddValue(dict
, kSecCodeInfoIdentifier
, CFTempString(this->identifier()));
829 CFDictionaryAddValue(dict
, kSecCodeInfoFormat
, CFTempString(this->format()));
830 if (CFDictionaryRef info
= infoDictionary())
831 CFDictionaryAddValue(dict
, kSecCodeInfoPList
, info
);
834 // kSecCSSigningInformation adds information about signing certificates and chains
836 if (flags
& kSecCSSigningInformation
) {
837 if (CFArrayRef certs
= certificates())
838 CFDictionaryAddValue(dict
, kSecCodeInfoCertificates
, certs
);
839 if (CFDataRef sig
= signature())
840 CFDictionaryAddValue(dict
, kSecCodeInfoCMS
, sig
);
842 CFDictionaryAddValue(dict
, kSecCodeInfoTrust
, mTrust
);
843 if (CFAbsoluteTime time
= signingTime())
844 if (CFRef
<CFDateRef
> date
= CFDateCreate(NULL
, time
))
845 CFDictionaryAddValue(dict
, kSecCodeInfoTime
, date
);
849 // kSecCSRequirementInformation adds information on requirements
851 if (flags
& kSecCSRequirementInformation
) {
852 if (const Requirements
*reqs
= internalRequirements()) {
853 CFDictionaryAddValue(dict
, kSecCodeInfoRequirements
,
854 CFTempString(Dumper::dump(reqs
)));
856 const Requirement
*ddreq
= defaultDesignatedRequirement();
857 CFRef
<SecRequirementRef
> ddreqRef
= (new SecRequirement(ddreq
))->handle();
858 const Requirement
*dreq
= designatedRequirement();
860 CFDictionaryAddValue(dict
, kSecCodeInfoDesignatedRequirement
, ddreqRef
);
861 CFDictionaryAddValue(dict
, kSecCodeInfoImplicitDesignatedRequirement
, ddreqRef
);
863 CFDictionaryAddValue(dict
, kSecCodeInfoDesignatedRequirement
,
864 CFRef
<SecRequirementRef
>((new SecRequirement(dreq
))->handle()));
865 CFDictionaryAddValue(dict
, kSecCodeInfoImplicitDesignatedRequirement
, ddreqRef
);
870 // kSecCSInternalInformation adds internal information meant to be for Apple internal
871 // use (SPI), and not guaranteed to be stable. Primarily, this is data we want
872 // to reliably transmit through the API wall so that code outside the Security.framework
873 // can use it without having to play nasty tricks to get it.
875 if (flags
& kSecCSInternalInformation
) {
877 CFDictionaryAddValue(dict
, CFSTR("CodeDirectory"), mDir
);
878 CFDictionaryAddValue(dict
, CFSTR("CodeOffset"), CFTempNumber(mRep
->signingBase()));
883 // kSecCSContentInformation adds more information about the physical layout
884 // of the signed code. This is (only) useful for packaging or patching-oriented
887 if (flags
& kSecCSContentInformation
)
888 if (CFRef
<CFArrayRef
> files
= mRep
->modifiedFiles())
889 CFDictionaryAddValue(dict
, kSecCodeInfoChangedFiles
, files
);
896 // Resource validation contexts.
897 // The default context simply throws a CSError, rudely terminating the operation.
899 SecStaticCode::ValidationContext::~ValidationContext()
902 void SecStaticCode::ValidationContext::reportProblem(OSStatus rc
, CFStringRef type
, CFTypeRef value
)
904 CSError::throwMe(rc
, type
, value
);
907 void SecStaticCode::CollectingContext::reportProblem(OSStatus rc
, CFStringRef type
, CFTypeRef value
)
909 if (mStatus
== noErr
)
910 mStatus
= rc
; // record first failure for eventual error return
913 mCollection
.take(makeCFMutableDictionary(0));
914 CFMutableArrayRef element
= CFMutableArrayRef(CFDictionaryGetValue(mCollection
, type
));
916 element
= makeCFMutableArray(0);
919 CFDictionaryAddValue(mCollection
, type
, element
);
922 CFArrayAppendValue(element
, value
);
926 void SecStaticCode::CollectingContext::throwMe()
928 assert(mStatus
!= noErr
);
929 throw CSError(mStatus
, mCollection
.yield());
934 // DetachedRep construction
936 DetachedRep::DetachedRep(CFDataRef sig
, DiskRep
*orig
)
937 : original(orig
), mSignature(sig
)
939 const BlobCore
*sigBlob
= reinterpret_cast<const BlobCore
*>(CFDataGetBytePtr(sig
));
940 if (sigBlob
->is
<EmbeddedSignatureBlob
>()) { // architecture-less
941 mArch
= EmbeddedSignatureBlob::specific(sigBlob
);
944 } else if (sigBlob
->is
<DetachedSignatureBlob
>()) { // architecture collection
945 const DetachedSignatureBlob
*dsblob
= DetachedSignatureBlob::specific(sigBlob
);
946 if (Universal
*fat
= orig
->mainExecutableImage()) {
947 if (const BlobCore
*blob
= dsblob
->find(fat
->bestNativeArch().cpuType())) {
948 mArch
= EmbeddedSignatureBlob::specific(blob
);
949 mGlobal
= EmbeddedSignatureBlob::specific(dsblob
->find(0));
952 secdebug("staticcode", "detached signature missing architecture %s",
953 fat
->bestNativeArch().name());
955 secdebug("staticcode", "detached signature requires Mach-O binary");
957 secdebug("staticcode", "detached signature bad magic 0x%x", sigBlob
->magic());
958 MacOSError::throwMe(errSecCSSignatureInvalid
);
961 CFDataRef
DetachedRep::component(CodeDirectory::SpecialSlot slot
)
963 if (CFDataRef result
= mArch
->component(slot
))
966 if (CFDataRef result
= mGlobal
->component(slot
))
968 return original
->component(slot
);
972 } // end namespace CodeSigning
973 } // end namespace Security