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/SecCertificatePriv.h>
39 #include <Security/CMSPrivate.h>
40 #include <Security/SecCmsContentInfo.h>
41 #include <Security/SecCmsSignerInfo.h>
42 #include <Security/SecCmsSignedData.h>
43 #include <security_utilities/unix++.h>
44 #include <security_codesigning/cfmunge.h>
45 #include <Security/CMSDecoder.h>
49 namespace CodeSigning
{
51 using namespace UnixPlusPlus
;
55 // We use a DetachedRep to interpose (filter) the genuine DiskRep representing
56 // the code on disk, *if* a detached signature was set on this object. In this
57 // situation, mRep will point to a (2 element) chain of DiskReps.
59 // This is a neat way of dealing with the (unusual) detached-signature case
60 // without disturbing things unduly. Consider DetachedDiskRep to be closely
61 // married to SecStaticCode; it's unlikely to work right if you use it elsewhere.
63 class DetachedRep
: public DiskRep
{
65 DetachedRep(CFDataRef sig
, DiskRep
*orig
);
67 const RefPointer
<DiskRep
> original
; // underlying representation
69 DiskRep
*base() { return original
; }
70 CFDataRef
component(CodeDirectory::SpecialSlot slot
);
71 std::string
mainExecutablePath() { return original
->mainExecutablePath(); }
72 CFURLRef
canonicalPath() { return original
->canonicalPath(); }
73 std::string
recommendedIdentifier() { return original
->recommendedIdentifier(); }
74 std::string
resourcesRootPath() { return original
->resourcesRootPath(); }
75 CFDictionaryRef
defaultResourceRules() { return original
->defaultResourceRules(); }
76 Universal
*mainExecutableImage() { return original
->mainExecutableImage(); }
77 size_t signingBase() { return original
->signingBase(); }
78 size_t signingLimit() { return original
->signingLimit(); }
79 std::string
format() { return original
->format(); }
80 FileDesc
&fd() { return original
->fd(); }
81 void flush() { return original
->flush(); }
84 CFCopyRef
<CFDataRef
> mSignature
;
85 const EmbeddedSignatureBlob
*mArch
; // current architecture; points into mSignature
86 const EmbeddedSignatureBlob
*mGlobal
; // shared elements; points into mSignature
91 // Construct a SecStaticCode object given a disk representation object
93 SecStaticCode::SecStaticCode(DiskRep
*rep
)
95 mValidated(false), mExecutableValidated(false),
96 mDesignatedReq(NULL
), mGotResourceBase(false), mEvalDetails(NULL
)
102 // Clean up a SecStaticCode object
104 SecStaticCode::~SecStaticCode() throw()
106 ::free(const_cast<Requirement
*>(mDesignatedReq
));
111 // Attach a detached signature.
113 void SecStaticCode::detachedSignature(CFDataRef sigData
)
116 mRep
= new DetachedRep(sigData
, mRep
->base());
123 // Do ::required, but convert incoming SecCodeRefs to their SecStaticCodeRefs
126 SecStaticCode
*SecStaticCode::requiredStatic(SecStaticCodeRef ref
)
128 SecCFObject
*object
= SecCFObject::required(ref
, errSecCSInvalidObjectRef
);
129 if (SecStaticCode
*scode
= dynamic_cast<SecStaticCode
*>(object
))
131 else if (SecCode
*code
= dynamic_cast<SecCode
*>(object
))
132 return code
->staticCode();
133 else // neither (a SecSomethingElse)
134 MacOSError::throwMe(errSecCSInvalidObjectRef
);
137 SecCode
*SecStaticCode::optionalDynamic(SecStaticCodeRef ref
)
139 SecCFObject
*object
= SecCFObject::required(ref
, errSecCSInvalidObjectRef
);
140 if (dynamic_cast<SecStaticCode
*>(object
))
142 else if (SecCode
*code
= dynamic_cast<SecCode
*>(object
))
144 else // neither (a SecSomethingElse)
145 MacOSError::throwMe(errSecCSInvalidObjectRef
);
150 // Void all cached validity data.
152 // We also throw out cached components, because the new signature data may have
153 // a different idea of what components should be present. We could reconcile the
154 // cached data instead, if performance seems to be impacted.
156 void SecStaticCode::resetValidity()
158 secdebug("staticCode", "%p resetting validity status", this);
160 mExecutableValidated
= false;
163 for (unsigned n
= 0; n
< cdSlotCount
; n
++)
166 mEntitlements
= NULL
;
167 mResourceDict
= NULL
;
168 mDesignatedReq
= NULL
;
177 // Retrieve a sealed component by special slot index.
178 // If the CodeDirectory has already been validated, validate against that.
179 // Otherwise, retrieve the component without validation (but cache it). Validation
180 // will go through the cache and validate all cached components.
182 CFDataRef
SecStaticCode::component(CodeDirectory::SpecialSlot slot
)
184 assert(slot
<= cdSlotMax
);
186 CFRef
<CFDataRef
> &cache
= mCache
[slot
];
188 if (CFRef
<CFDataRef
> data
= mRep
->component(slot
)) {
189 if (validated()) // if the directory has been validated...
190 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data
), // ... and it's no good
191 CFDataGetLength(data
), -slot
))
192 MacOSError::throwMe(errSecCSSignatureFailed
); // ... then bail
193 cache
= data
; // it's okay, cache it
194 } else { // absent, mark so
195 if (validated()) // if directory has been validated...
196 if (codeDirectory()->slotIsPresent(-slot
)) // ... and the slot is NOT missing
197 MacOSError::throwMe(errSecCSSignatureFailed
); // was supposed to be there
198 cache
= CFDataRef(kCFNull
); // white lie
201 return (cache
== CFDataRef(kCFNull
)) ? NULL
: cache
.get();
206 // Get the CodeDirectory.
207 // Throws (if check==true) or returns NULL (check==false) if there is none.
208 // Always throws if the CodeDirectory exists but is invalid.
209 // NEVER validates against the signature.
211 const CodeDirectory
*SecStaticCode::codeDirectory(bool check
/* = true */)
214 if (mDir
.take(mRep
->codeDirectory())) {
215 const CodeDirectory
*dir
= reinterpret_cast<const CodeDirectory
*>(CFDataGetBytePtr(mDir
));
220 return reinterpret_cast<const CodeDirectory
*>(CFDataGetBytePtr(mDir
));
222 MacOSError::throwMe(errSecCSUnsigned
);
228 // Return the CMS signature blob; NULL if none found.
230 CFDataRef
SecStaticCode::signature()
233 mSignature
.take(mRep
->signature());
236 MacOSError::throwMe(errSecCSUnsigned
);
241 // Verify the signature on the CodeDirectory.
242 // If this succeeds (doesn't throw), the CodeDirectory is statically trustworthy.
243 // Any outcome (successful or not) is cached for the lifetime of the StaticCode.
245 void SecStaticCode::validateDirectory()
247 // echo previous outcome, if any
250 // perform validation (or die trying)
251 secdebug("staticCode", "%p validating directory", this);
252 mValidationExpired
= verifySignature();
253 component(cdInfoSlot
); // force load of Info Dictionary (if any)
254 for (CodeDirectory::SpecialSlot slot
= codeDirectory()->nSpecialSlots
;
256 if (mCache
[slot
]) // if we already loaded that resource...
257 validateComponent(slot
); // ... then check it now
258 mValidated
= true; // we've done the deed...
259 mValidationResult
= noErr
; // ... and it was good
260 } catch (const CommonError
&err
) {
262 mValidationResult
= err
.osStatus();
265 secdebug("staticCode", "%p validation threw non-common exception", this);
267 mValidationResult
= errSecCSInternalError
;
271 if (mValidationResult
== noErr
) {
272 if (mValidationExpired
)
273 if ((apiFlags() & kSecCSConsiderExpiration
)
274 || (codeDirectory()->flags
& kSecCodeSignatureForceExpiration
))
275 MacOSError::throwMe(CSSMERR_TP_CERT_EXPIRED
);
277 MacOSError::throwMe(mValidationResult
);
282 // Get the (signed) signing date from the code signature.
283 // Sadly, we need to validate the signature to get the date (as a side benefit).
284 // This means that you can't get the signing time for invalidly signed code.
286 // We could run the decoder "almost to" verification to avoid this, but there seems
287 // little practical point to such a duplication of effort.
289 CFAbsoluteTime
SecStaticCode::signingTime()
297 // Verify the CMS signature on the CodeDirectory.
298 // This performs the cryptographic tango. It returns if the signature is valid,
299 // or throws if it is not. As a side effect, a successful return sets up the
300 // cached certificate chain for future use.
302 bool SecStaticCode::verifySignature()
304 // ad-hoc signed code is considered validly signed by definition
305 if (flag(kSecCodeSignatureAdhoc
)) {
306 secdebug("staticCode", "%p considered verified since it is ad-hoc", this);
310 // decode CMS and extract SecTrust for verification
311 secdebug("staticCode", "%p verifying signature", this);
312 CFRef
<CMSDecoderRef
> cms
;
313 MacOSError::check(CMSDecoderCreate(&cms
.aref())); // create decoder
314 CFDataRef sig
= this->signature();
315 MacOSError::check(CMSDecoderUpdateMessage(cms
, CFDataGetBytePtr(sig
), CFDataGetLength(sig
)));
316 this->codeDirectory(); // load CodeDirectory (sets mDir)
317 MacOSError::check(CMSDecoderSetDetachedContent(cms
, mDir
));
318 MacOSError::check(CMSDecoderFinalizeMessage(cms
));
319 MacOSError::check(CMSDecoderSetSearchKeychain(cms
, cfEmptyArray()));
320 CMSSignerStatus status
;
321 MacOSError::check(CMSDecoderCopySignerStatus(cms
, 0, verificationPolicy(),
322 false, &status
, &mTrust
.aref(), NULL
));
323 if (status
!= kCMSSignerValid
)
324 MacOSError::throwMe(errSecCSSignatureFailed
);
326 // get signing date (we've got the decoder handle right here)
327 mSigningTime
= 0; // "not present" marker (nobody could code sign on Jan 1, 2001 :-)
328 SecCmsMessageRef cmsg
;
329 MacOSError::check(CMSDecoderGetCmsMessage(cms
, &cmsg
));
330 SecCmsSignedDataRef signedData
= NULL
;
331 int numContentInfos
= SecCmsMessageContentLevelCount(cmsg
);
332 for(int dex
= 0; !signedData
&& dex
< numContentInfos
; dex
++) {
333 SecCmsContentInfoRef ci
= SecCmsMessageContentLevel(cmsg
, dex
);
334 SECOidTag tag
= SecCmsContentInfoGetContentTypeTag(ci
);
336 case SEC_OID_PKCS7_SIGNED_DATA
:
337 if (SecCmsSignedDataRef signedData
= SecCmsSignedDataRef(SecCmsContentInfoGetContent(ci
)))
338 if (SecCmsSignerInfoRef signerInfo
= SecCmsSignedDataGetSignerInfo(signedData
, 0))
339 SecCmsSignerInfoGetSigningTime(signerInfo
, &mSigningTime
);
346 // set up the environment for SecTrust
347 MacOSError::check(SecTrustSetAnchorCertificates(mTrust
, cfEmptyArray())); // no anchors
348 CSSM_APPLE_TP_ACTION_DATA actionData
= {
349 CSSM_APPLE_TP_ACTION_VERSION
, // version of data structure
350 CSSM_TP_ACTION_IMPLICIT_ANCHORS
// action flags
354 MacOSError::check(SecTrustSetParameters(mTrust
,
355 CSSM_TP_ACTION_DEFAULT
, CFTempData(&actionData
, sizeof(actionData
))));
357 // evaluate trust and extract results
358 SecTrustResultType trustResult
;
359 MacOSError::check(SecTrustEvaluate(mTrust
, &trustResult
));
360 MacOSError::check(SecTrustGetResult(mTrust
, &trustResult
, &mCertChain
.aref(), &mEvalDetails
));
361 secdebug("staticCode", "%p verification result=%d chain=%ld",
362 this, trustResult
, mCertChain
? CFArrayGetCount(mCertChain
) : -1);
363 switch (trustResult
) {
364 case kSecTrustResultProceed
:
365 case kSecTrustResultConfirm
:
366 case kSecTrustResultUnspecified
:
368 case kSecTrustResultDeny
:
369 MacOSError::throwMe(CSSMERR_APPLETP_TRUST_SETTING_DENY
); // user reject
370 case kSecTrustResultInvalid
:
371 assert(false); // should never happen
372 MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED
);
373 case kSecTrustResultRecoverableTrustFailure
:
374 case kSecTrustResultFatalTrustFailure
:
375 case kSecTrustResultOtherError
:
378 MacOSError::check(SecTrustGetCssmResultCode(mTrust
, &result
));
379 if (result
== CSSMERR_TP_CERT_EXPIRED
&& !(actionData
.ActionFlags
& CSSM_TP_ACTION_ALLOW_EXPIRED
)) {
380 secdebug("staticCode", "expired certificate(s); retrying validation");
381 actionData
.ActionFlags
|= CSSM_TP_ACTION_ALLOW_EXPIRED
;
382 continue; // retry validation
384 MacOSError::throwMe(result
);
387 return actionData
.ActionFlags
& CSSM_TP_ACTION_ALLOW_EXPIRED
;
393 // Return the TP policy used for signature verification.
394 // This policy object is cached and reused.
396 SecPolicyRef
SecStaticCode::verificationPolicy()
399 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3
,
400 &CSSMOID_APPLE_TP_CODE_SIGNING
, &mPolicy
.aref()));
406 // Validate a particular sealed, cached resource against its (special) CodeDirectory slot.
407 // The resource must already have been placed in the cache.
408 // This does NOT perform basic validation.
410 void SecStaticCode::validateComponent(CodeDirectory::SpecialSlot slot
)
412 CFDataRef data
= mCache
[slot
];
413 assert(data
); // must be cached
414 if (data
== CFDataRef(kCFNull
)) {
415 if (codeDirectory()->slotIsPresent(-slot
)) // was supposed to be there...
416 MacOSError::throwMe(errSecCSSignatureFailed
); // ... and is missing
418 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data
), CFDataGetLength(data
), -slot
))
419 MacOSError::throwMe(errSecCSSignatureFailed
);
425 // Perform static validation of the main executable.
426 // This reads the main executable from disk and validates it against the
427 // CodeDirectory code slot array.
428 // Note that this is NOT an in-memory validation, and is thus potentially
429 // subject to timing attacks.
431 void SecStaticCode::validateExecutable()
433 secdebug("staticCode", "%p performing static main exec validate of %s",
434 this, mainExecutablePath().c_str());
435 const CodeDirectory
*cd
= this->codeDirectory();
437 MacOSError::throwMe(errSecCSUnsigned
);
438 AutoFileDesc
fd(mainExecutablePath(), O_RDONLY
);
439 fd
.fcntl(F_NOCACHE
, true); // turn off page caching (one-pass)
440 if (Universal
*fat
= mRep
->mainExecutableImage())
441 fd
.seek(fat
->archOffset());
442 size_t pageSize
= cd
->pageSize
? (1 << cd
->pageSize
) : 0;
443 size_t remaining
= cd
->codeLimit
;
444 for (size_t slot
= 0; slot
< cd
->nCodeSlots
; ++slot
) {
445 size_t size
= min(remaining
, pageSize
);
446 if (!cd
->validateSlot(fd
, size
, slot
)) {
447 secdebug("staticCode", "%p failed static validation of code page %zd", this, slot
);
448 mExecutableValidated
= true; // we tried
449 mExecutableValid
= false; // it failed
450 MacOSError::throwMe(errSecCSSignatureFailed
);
454 secdebug("staticCode", "%p validated full executable (%d pages)",
455 this, int(cd
->nCodeSlots
));
456 mExecutableValidated
= true; // we tried
457 mExecutableValid
= true; // it worked
462 // Perform static validation of sealed resources.
464 // This performs a whole-code static resource scan and effectively
465 // computes a concordance between what's on disk and what's in the ResourceDirectory.
466 // Any unsanctioned difference causes an error.
468 void SecStaticCode::validateResources()
471 CFDictionaryRef sealedResources
= resourceDictionary();
472 if (this->resourceBase()) // disk has resources
474 /* go to work below */;
476 MacOSError::throwMe(errSecCSResourcesNotFound
);
477 else // disk has no resources
479 MacOSError::throwMe(errSecCSResourcesNotFound
);
481 return; // no resources, not sealed - fine (no work)
483 // found resources, and they are sealed
484 CFDictionaryRef rules
= cfget
<CFDictionaryRef
>(sealedResources
, "rules");
485 CFDictionaryRef files
= cfget
<CFDictionaryRef
>(sealedResources
, "files");
486 secdebug("staticCode", "%p verifying %d sealed resources",
487 this, int(CFDictionaryGetCount(files
)));
489 // make a shallow copy of the ResourceDirectory so we can "check off" what we find
490 CFRef
<CFMutableDictionaryRef
> resourceMap
= CFDictionaryCreateMutableCopy(NULL
,
491 CFDictionaryGetCount(files
), files
);
495 // scan through the resources on disk, checking each against the resourceDirectory
496 CollectingContext
ctx(*this); // collect all failures in here
497 ResourceBuilder
resources(cfString(this->resourceBase()), rules
);
498 mRep
->adjustResources(resources
);
500 ResourceBuilder::Rule
*rule
;
502 while (resources
.next(path
, rule
)) {
503 if (CFDataRef value
= resource(path
, ctx
))
505 CFDictionaryRemoveValue(resourceMap
, CFTempString(path
));
506 secdebug("staticCode", "%p validated %s", this, path
.c_str());
509 if (CFDictionaryGetCount(resourceMap
) > 0) {
510 secdebug("staticCode", "%p sealed resource(s) not found in code", this);
511 CFDictionaryApplyFunction(resourceMap
, SecStaticCode::checkOptionalResource
, &ctx
);
514 // now check for any errors found in the reporting context
518 secdebug("staticCode", "%p sealed resources okay", this);
522 void SecStaticCode::checkOptionalResource(CFTypeRef key
, CFTypeRef value
, void *context
)
524 CollectingContext
*ctx
= static_cast<CollectingContext
*>(context
);
525 ResourceSeal
seal(value
);
526 if (!seal
.optional())
527 if (key
&& CFGetTypeID(key
) == CFStringGetTypeID()) {
528 ctx
->reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
,
529 CFTempURL(CFStringRef(key
), false, ctx
->code
.resourceBase()));
531 ctx
->reportProblem(errSecCSBadResource
, kSecCFErrorResourceSeal
, key
);
536 // Load, validate, cache, and return CFDictionary forms of sealed resources.
538 CFDictionaryRef
SecStaticCode::infoDictionary()
541 mInfoDict
.take(getDictionary(cdInfoSlot
));
542 secdebug("staticCode", "%p loaded InfoDict %p", this, mInfoDict
.get());
547 CFDictionaryRef
SecStaticCode::entitlements()
549 if (!mEntitlements
) {
551 if (CFDataRef entitlementData
= component(cdEntitlementSlot
)) {
552 validateComponent(cdEntitlementSlot
);
553 const EntitlementBlob
*blob
= reinterpret_cast<const EntitlementBlob
*>(CFDataGetBytePtr(entitlementData
));
554 if (blob
->validateBlob()) {
555 mEntitlements
.take(blob
->entitlements());
556 secdebug("staticCode", "%p loaded Entitlements %p", this, mEntitlements
.get());
558 // we do not consider a different blob type to be an error. We think it's a new format we don't understand
561 return mEntitlements
;
564 CFDictionaryRef
SecStaticCode::resourceDictionary()
566 if (mResourceDict
) // cached
567 return mResourceDict
;
568 if (CFRef
<CFDictionaryRef
> dict
= getDictionary(cdResourceDirSlot
))
569 if (cfscan(dict
, "{rules=%Dn,files=%Dn}")) {
570 secdebug("staticCode", "%p loaded ResourceDict %p",
571 this, mResourceDict
.get());
572 return mResourceDict
= dict
;
580 // Load and cache the resource directory base.
581 // Note that the base is optional for each DiskRep.
583 CFURLRef
SecStaticCode::resourceBase()
585 if (!mGotResourceBase
) {
586 string base
= mRep
->resourcesRootPath();
588 mResourceBase
.take(makeCFURL(base
, true));
589 mGotResourceBase
= true;
591 return mResourceBase
;
596 // Load a component, validate it, convert it to a CFDictionary, and return that.
597 // This will force load and validation, which means that it will perform basic
598 // validation if it hasn't been done yet.
600 CFDictionaryRef
SecStaticCode::getDictionary(CodeDirectory::SpecialSlot slot
)
603 if (CFDataRef infoData
= component(slot
)) {
604 validateComponent(slot
);
605 if (CFDictionaryRef dict
= makeCFDictionaryFrom(infoData
))
608 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
615 // Load, validate, and return a sealed resource.
616 // The resource data (loaded in to memory as a blob) is returned and becomes
617 // the responsibility of the caller; it is NOT cached by SecStaticCode.
619 // A resource that is not sealed will not be returned, and an error will be thrown.
620 // A missing resource will cause an error unless it's marked optional in the Directory.
621 // Under no circumstances will a corrupt resource be returned.
622 // NULL will only be returned for a resource that is neither sealed nor present
623 // (or that is sealed, absent, and marked optional).
624 // If the ResourceDictionary itself is not sealed, this function will always fail.
626 // There is currently no interface for partial retrieval of the resource data.
627 // (Since the ResourceDirectory does not currently support segmentation, all the
628 // data would have to be read anyway, but it could be read into a reusable buffer.)
630 CFDataRef
SecStaticCode::resource(string path
, ValidationContext
&ctx
)
632 if (CFDictionaryRef rdict
= resourceDictionary()) {
633 if (CFTypeRef file
= cfget(rdict
, "files.%s", path
.c_str())) {
634 ResourceSeal seal
= file
;
635 if (!resourceBase()) // no resources in DiskRep
636 MacOSError::throwMe(errSecCSResourcesNotFound
);
637 CFRef
<CFURLRef
> fullpath
= makeCFURL(path
, false, resourceBase());
638 if (CFRef
<CFDataRef
> data
= cfLoadFile(fullpath
)) {
640 hasher(CFDataGetBytePtr(data
), CFDataGetLength(data
));
641 if (hasher
.verify(seal
.hash()))
642 return data
.yield(); // good
644 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAltered
, fullpath
); // altered
646 if (!seal
.optional())
647 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
, fullpath
); // was sealed but is now missing
649 return NULL
; // validly missing
652 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAdded
, CFTempURL(path
, false, resourceBase()));
655 MacOSError::throwMe(errSecCSResourcesNotSealed
);
658 CFDataRef
SecStaticCode::resource(string path
)
660 ValidationContext ctx
;
661 return resource(path
, ctx
);
666 // Test a CodeDirectory flag.
667 // Returns false if there is no CodeDirectory.
668 // May throw if the CodeDirectory is present but somehow invalid.
670 bool SecStaticCode::flag(uint32_t tested
)
672 if (const CodeDirectory
*cd
= this->codeDirectory(false))
673 return cd
->flags
& tested
;
680 // Retrieve the full SuperBlob containing all internal requirements.
682 const Requirements
*SecStaticCode::internalRequirements()
684 if (CFDataRef req
= component(cdRequirementsSlot
))
685 return (const Requirements
*)CFDataGetBytePtr(req
);
692 // Retrieve a particular internal requirement by type.
694 const Requirement
*SecStaticCode::internalRequirement(SecRequirementType type
)
696 if (const Requirements
*reqs
= internalRequirements())
697 return reqs
->find
<Requirement
>(type
);
704 // Return the Designated Requirement. This can be either explicit in the
705 // Internal Requirements resource, or implicitly generated.
707 const Requirement
*SecStaticCode::designatedRequirement()
709 if (const Requirement
*req
= internalRequirement(kSecDesignatedRequirementType
)) {
710 return req
; // explicit in signing data
713 mDesignatedReq
= defaultDesignatedRequirement();
714 return mDesignatedReq
;
720 // Generate the default (implicit) Designated Requirement for this StaticCode.
721 // This is a heuristic of sorts, and may change over time (for the better, we hope).
723 // The current logic is this:
724 // * If the code is ad-hoc signed, use the CodeDirectory hash directory.
725 // * Otherwise, use the form anchor (anchor) and identifier (CodeDirectory identifier).
726 // ** If the root CA is Apple's, we use the "anchor apple" construct. Otherwise,
727 // we default to the leaf (directly signing) certificate.
729 const Requirement
*SecStaticCode::defaultDesignatedRequirement()
731 validateDirectory(); // need the cert chain
732 Requirement::Maker maker
;
734 // if this is an ad-hoc (unsigned) object, return a cdhash requirement
735 if (flag(kSecCodeSignatureAdhoc
)) {
737 hash(codeDirectory(), codeDirectory()->length());
740 maker
.cdhash(digest
);
742 // always require the identifier
744 maker
.ident(codeDirectory()->identifier());
746 SHA1::Digest anchorHash
;
747 hashOfCertificate(cert(Requirement::anchorCert
), anchorHash
);
748 if (!memcmp(anchorHash
, Requirement::appleAnchorHash(), SHA1::digestLength
)
749 #if defined(TEST_APPLE_ANCHOR)
750 || !memcmp(anchorHash
, Requirement::testAppleAnchorHash(), SHA1::digestLength
)
753 defaultDesignatedAppleAnchor(maker
);
755 defaultDesignatedNonAppleAnchor(maker
);
761 static const uint8_t adcSdkMarker
[] = { APPLE_EXTENSION_OID
, 2, 1 };
762 static const CSSM_DATA adcSdkMarkerOID
= { sizeof(adcSdkMarker
), (uint8_t *)adcSdkMarker
};
764 void SecStaticCode::defaultDesignatedAppleAnchor(Requirement::Maker
&maker
)
766 if (isAppleSDKSignature()) {
767 // get the Common Name DN element for the leaf
768 CFRef
<CFStringRef
> leafCN
;
769 MacOSError::check(SecCertificateCopySubjectComponent(cert(Requirement::leafCert
),
770 &CSSMOID_CommonName
, &leafCN
.aref()));
772 // apple anchor generic and ...
774 maker
.anchorGeneric(); // apple generic anchor and...
775 // ... leaf[subject.CN] = <leaf's subject> and ...
777 maker
.put(opCertField
); // certificate
778 maker
.put(0); // leaf
779 maker
.put("subject.CN"); // [subject.CN]
780 maker
.put(matchEqual
); // =
781 maker
.putData(leafCN
); // <leaf CN>
782 // ... cert 1[field.<marker>] exists
783 maker
.put(opCertGeneric
); // certificate
785 maker
.putData(adcSdkMarkerOID
.Data
, adcSdkMarkerOID
.Length
); // [field.<marker>]
786 maker
.put(matchExists
); // exists
790 // otherwise, claim this program for Apple
794 bool SecStaticCode::isAppleSDKSignature()
796 if (CFArrayRef certChain
= certificates()) // got cert chain
797 if (CFArrayGetCount(certChain
) == 3) // leaf, one intermediate, anchor
798 if (SecCertificateRef intermediate
= cert(1)) // get intermediate
799 if (certificateHasField(intermediate
, CssmOid::overlay(adcSdkMarkerOID
)))
805 void SecStaticCode::defaultDesignatedNonAppleAnchor(Requirement::Maker
&maker
)
807 // get the Organization DN element for the leaf
808 CFRef
<CFStringRef
> leafOrganization
;
809 MacOSError::check(SecCertificateCopySubjectComponent(cert(Requirement::leafCert
),
810 &CSSMOID_OrganizationName
, &leafOrganization
.aref()));
812 // now step up the cert chain looking for the first cert with a different one
813 int slot
= Requirement::leafCert
; // start at leaf
814 if (leafOrganization
) {
815 while (SecCertificateRef ca
= cert(slot
+1)) { // NULL if you over-run the anchor slot
816 CFRef
<CFStringRef
> caOrganization
;
817 MacOSError::check(SecCertificateCopySubjectComponent(ca
, &CSSMOID_OrganizationName
, &caOrganization
.aref()));
818 if (CFStringCompare(leafOrganization
, caOrganization
, 0) != kCFCompareEqualTo
)
822 if (slot
== CFArrayGetCount(mCertChain
) - 1) // went all the way to the anchor...
823 slot
= Requirement::anchorCert
; // ... so say that
826 // nail the last cert with the leaf's Organization value
827 SHA1::Digest authorityHash
;
828 hashOfCertificate(cert(slot
), authorityHash
);
829 maker
.anchor(slot
, authorityHash
);
834 // Validate a SecStaticCode against the internal requirement of a particular type.
836 void SecStaticCode::validateRequirements(SecRequirementType type
, SecStaticCode
*target
,
837 OSStatus nullError
/* = noErr */)
839 secdebug("staticCode", "%p validating %s requirements for %p",
840 this, Requirement::typeNames
[type
], target
);
841 if (const Requirement
*req
= internalRequirement(type
))
842 target
->validateRequirements(req
, nullError
? nullError
: errSecCSReqFailed
);
843 else if (nullError
) {
844 secdebug("staticCode", "%p NULL validate for %s prohibited",
845 this, Requirement::typeNames
[type
]);
846 MacOSError::throwMe(nullError
);
848 secdebug("staticCode", "%p NULL validate (no requirements for %s)",
849 this, Requirement::typeNames
[type
]);
854 // Validate this StaticCode against an external Requirement
856 void SecStaticCode::validateRequirements(const Requirement
*req
, OSStatus failure
)
860 req
->validate(Requirement::Context(mCertChain
, infoDictionary(), entitlements(), codeDirectory()), failure
);
865 // Retrieve one certificate from the cert chain.
866 // Positive and negative indices can be used:
867 // [ leaf, intermed-1, ..., intermed-n, anchor ]
869 // Returns NULL if unavailable for any reason.
871 SecCertificateRef
SecStaticCode::cert(int ix
)
873 validateDirectory(); // need cert chain
875 CFIndex length
= CFArrayGetCount(mCertChain
);
878 if (ix
>= 0 && ix
< length
)
879 return SecCertificateRef(CFArrayGetValueAtIndex(mCertChain
, ix
));
884 CFArrayRef
SecStaticCode::certificates()
886 validateDirectory(); // need cert chain
892 // Gather API-official information about this StaticCode.
894 // This method lives in the twilight between the API and internal layers,
895 // since it generates API objects (Sec*Refs) for return.
897 CFDictionaryRef
SecStaticCode::signingInformation(SecCSFlags flags
)
900 // Start with the pieces that we return even for unsigned code.
901 // This makes Sec[Static]CodeRefs useful as API-level replacements
902 // of our internal OSXCode objects.
904 CFRef
<CFMutableDictionaryRef
> dict
= makeCFMutableDictionary(1,
905 kSecCodeInfoMainExecutable
, CFTempURL(this->mainExecutablePath()).get()
909 // If we're not signed, this is all you get
911 if (!this->isSigned())
916 // Now add the generic attributes that we always include
918 CFDictionaryAddValue(dict
, kSecCodeInfoIdentifier
, CFTempString(this->identifier()));
919 CFDictionaryAddValue(dict
, kSecCodeInfoFormat
, CFTempString(this->format()));
920 if (CFDictionaryRef info
= infoDictionary())
921 CFDictionaryAddValue(dict
, kSecCodeInfoPList
, info
);
924 // kSecCSSigningInformation adds information about signing certificates and chains
926 if (flags
& kSecCSSigningInformation
) {
927 if (CFArrayRef certs
= certificates())
928 CFDictionaryAddValue(dict
, kSecCodeInfoCertificates
, certs
);
929 if (CFDataRef sig
= signature())
930 CFDictionaryAddValue(dict
, kSecCodeInfoCMS
, sig
);
932 CFDictionaryAddValue(dict
, kSecCodeInfoTrust
, mTrust
);
933 if (CFAbsoluteTime time
= signingTime())
934 if (CFRef
<CFDateRef
> date
= CFDateCreate(NULL
, time
))
935 CFDictionaryAddValue(dict
, kSecCodeInfoTime
, date
);
939 // kSecCSRequirementInformation adds information on requirements
941 if (flags
& kSecCSRequirementInformation
) {
942 if (const Requirements
*reqs
= internalRequirements()) {
943 CFDictionaryAddValue(dict
, kSecCodeInfoRequirements
,
944 CFTempString(Dumper::dump(reqs
)));
945 CFDictionaryAddValue(dict
, kSecCodeInfoRequirementData
, CFTempData(*reqs
));
947 const Requirement
*dreq
= designatedRequirement();
948 const Requirement
*ddreq
= defaultDesignatedRequirement();
949 CFRef
<SecRequirementRef
> ddreqRef
= (new SecRequirement(ddreq
))->handle();
951 CFDictionaryAddValue(dict
, kSecCodeInfoDesignatedRequirement
, ddreqRef
);
952 CFDictionaryAddValue(dict
, kSecCodeInfoImplicitDesignatedRequirement
, ddreqRef
);
954 CFDictionaryAddValue(dict
, kSecCodeInfoDesignatedRequirement
,
955 CFRef
<SecRequirementRef
>((new SecRequirement(dreq
))->handle()));
956 CFDictionaryAddValue(dict
, kSecCodeInfoImplicitDesignatedRequirement
, ddreqRef
);
959 if (CFDataRef ent
= component(cdEntitlementSlot
))
960 CFDictionaryAddValue(dict
, kSecCodeInfoEntitlements
, ent
);
964 // kSecCSInternalInformation adds internal information meant to be for Apple internal
965 // use (SPI), and not guaranteed to be stable. Primarily, this is data we want
966 // to reliably transmit through the API wall so that code outside the Security.framework
967 // can use it without having to play nasty tricks to get it.
969 if (flags
& kSecCSInternalInformation
) {
971 CFDictionaryAddValue(dict
, CFSTR("CodeDirectory"), mDir
);
972 CFDictionaryAddValue(dict
, CFSTR("CodeOffset"), CFTempNumber(mRep
->signingBase()));
973 if (CFDictionaryRef resources
= resourceDictionary())
974 CFDictionaryAddValue(dict
, CFSTR("ResourceDirectory"), resources
);
979 // kSecCSContentInformation adds more information about the physical layout
980 // of the signed code. This is (only) useful for packaging or patching-oriented
983 if (flags
& kSecCSContentInformation
)
984 if (CFRef
<CFArrayRef
> files
= mRep
->modifiedFiles())
985 CFDictionaryAddValue(dict
, kSecCodeInfoChangedFiles
, files
);
992 // Resource validation contexts.
993 // The default context simply throws a CSError, rudely terminating the operation.
995 SecStaticCode::ValidationContext::~ValidationContext()
998 void SecStaticCode::ValidationContext::reportProblem(OSStatus rc
, CFStringRef type
, CFTypeRef value
)
1000 CSError::throwMe(rc
, type
, value
);
1003 void SecStaticCode::CollectingContext::reportProblem(OSStatus rc
, CFStringRef type
, CFTypeRef value
)
1005 if (mStatus
== noErr
)
1006 mStatus
= rc
; // record first failure for eventual error return
1009 mCollection
.take(makeCFMutableDictionary(0));
1010 CFMutableArrayRef element
= CFMutableArrayRef(CFDictionaryGetValue(mCollection
, type
));
1012 element
= makeCFMutableArray(0);
1015 CFDictionaryAddValue(mCollection
, type
, element
);
1018 CFArrayAppendValue(element
, value
);
1022 void SecStaticCode::CollectingContext::throwMe()
1024 assert(mStatus
!= noErr
);
1025 throw CSError(mStatus
, mCollection
.yield());
1030 // DetachedRep construction
1032 DetachedRep::DetachedRep(CFDataRef sig
, DiskRep
*orig
)
1033 : original(orig
), mSignature(sig
)
1035 const BlobCore
*sigBlob
= reinterpret_cast<const BlobCore
*>(CFDataGetBytePtr(sig
));
1036 if (sigBlob
->is
<EmbeddedSignatureBlob
>()) { // architecture-less
1037 mArch
= EmbeddedSignatureBlob::specific(sigBlob
);
1040 } else if (sigBlob
->is
<DetachedSignatureBlob
>()) { // architecture collection
1041 const DetachedSignatureBlob
*dsblob
= DetachedSignatureBlob::specific(sigBlob
);
1042 if (Universal
*fat
= orig
->mainExecutableImage()) {
1043 if (const BlobCore
*blob
= dsblob
->find(fat
->bestNativeArch().cpuType())) {
1044 mArch
= EmbeddedSignatureBlob::specific(blob
);
1045 mGlobal
= EmbeddedSignatureBlob::specific(dsblob
->find(0));
1048 secdebug("staticcode", "detached signature missing architecture %s",
1049 fat
->bestNativeArch().name());
1051 secdebug("staticcode", "detached signature requires Mach-O binary");
1053 secdebug("staticcode", "detached signature bad magic 0x%x", sigBlob
->magic());
1054 MacOSError::throwMe(errSecCSSignatureInvalid
);
1057 CFDataRef
DetachedRep::component(CodeDirectory::SpecialSlot slot
)
1059 if (CFDataRef result
= mArch
->component(slot
))
1062 if (CFDataRef result
= mGlobal
->component(slot
))
1064 return original
->component(slot
);
1068 } // end namespace CodeSigning
1069 } // end namespace Security