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
));
324 // get signing date (we've got the decoder handle right here)
325 mSigningTime
= 0; // "not present" marker (nobody could code sign on Jan 1, 2001 :-)
326 SecCmsMessageRef cmsg
;
327 MacOSError::check(CMSDecoderGetCmsMessage(cms
, &cmsg
));
328 SecCmsSignedDataRef signedData
= NULL
;
329 int numContentInfos
= SecCmsMessageContentLevelCount(cmsg
);
330 for(int dex
= 0; !signedData
&& dex
< numContentInfos
; dex
++) {
331 SecCmsContentInfoRef ci
= SecCmsMessageContentLevel(cmsg
, dex
);
332 SECOidTag tag
= SecCmsContentInfoGetContentTypeTag(ci
);
334 case SEC_OID_PKCS7_SIGNED_DATA
:
335 if (SecCmsSignedDataRef signedData
= SecCmsSignedDataRef(SecCmsContentInfoGetContent(ci
)))
336 if (SecCmsSignerInfoRef signerInfo
= SecCmsSignedDataGetSignerInfo(signedData
, 0))
337 SecCmsSignerInfoGetSigningTime(signerInfo
, &mSigningTime
);
344 // set up the environment for SecTrust
345 MacOSError::check(SecTrustSetAnchorCertificates(mTrust
, cfEmptyArray())); // no anchors
346 CSSM_APPLE_TP_ACTION_DATA actionData
= {
347 CSSM_APPLE_TP_ACTION_VERSION
, // version of data structure
348 CSSM_TP_ACTION_IMPLICIT_ANCHORS
// action flags
352 MacOSError::check(SecTrustSetParameters(mTrust
,
353 CSSM_TP_ACTION_DEFAULT
, CFTempData(&actionData
, sizeof(actionData
))));
355 // evaluate trust and extract results
356 SecTrustResultType trustResult
;
357 MacOSError::check(SecTrustEvaluate(mTrust
, &trustResult
));
358 MacOSError::check(SecTrustGetResult(mTrust
, &trustResult
, &mCertChain
.aref(), &mEvalDetails
));
359 secdebug("staticCode", "%p verification result=%d chain=%ld",
360 this, trustResult
, mCertChain
? CFArrayGetCount(mCertChain
) : -1);
361 switch (trustResult
) {
362 case kSecTrustResultProceed
:
363 case kSecTrustResultConfirm
:
364 case kSecTrustResultUnspecified
:
366 case kSecTrustResultDeny
:
367 MacOSError::throwMe(CSSMERR_APPLETP_TRUST_SETTING_DENY
); // user reject
368 case kSecTrustResultInvalid
:
369 assert(false); // should never happen
370 MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED
);
371 case kSecTrustResultRecoverableTrustFailure
:
372 case kSecTrustResultFatalTrustFailure
:
373 case kSecTrustResultOtherError
:
376 MacOSError::check(SecTrustGetCssmResultCode(mTrust
, &result
));
377 if (result
== CSSMERR_TP_CERT_EXPIRED
&& !(actionData
.ActionFlags
& CSSM_TP_ACTION_ALLOW_EXPIRED
)) {
378 secdebug("staticCode", "expired certificate(s); retrying validation");
379 actionData
.ActionFlags
|= CSSM_TP_ACTION_ALLOW_EXPIRED
;
380 continue; // retry validation
382 MacOSError::throwMe(result
);
385 return actionData
.ActionFlags
& CSSM_TP_ACTION_ALLOW_EXPIRED
;
391 // Return the TP policy used for signature verification.
392 // This policy object is cached and reused.
394 SecPolicyRef
SecStaticCode::verificationPolicy()
397 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3
,
398 &CSSMOID_APPLE_TP_CODE_SIGNING
, &mPolicy
.aref()));
404 // Validate a particular sealed, cached resource against its (special) CodeDirectory slot.
405 // The resource must already have been placed in the cache.
406 // This does NOT perform basic validation.
408 void SecStaticCode::validateComponent(CodeDirectory::SpecialSlot slot
)
410 CFDataRef data
= mCache
[slot
];
411 assert(data
); // must be cached
412 if (data
== CFDataRef(kCFNull
)) {
413 if (codeDirectory()->slotIsPresent(-slot
)) // was supposed to be there...
414 MacOSError::throwMe(errSecCSSignatureFailed
); // ... and is missing
416 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data
), CFDataGetLength(data
), -slot
))
417 MacOSError::throwMe(errSecCSSignatureFailed
);
423 // Perform static validation of the main executable.
424 // This reads the main executable from disk and validates it against the
425 // CodeDirectory code slot array.
426 // Note that this is NOT an in-memory validation, and is thus potentially
427 // subject to timing attacks.
429 void SecStaticCode::validateExecutable()
431 secdebug("staticCode", "%p performing static main exec validate of %s",
432 this, mainExecutablePath().c_str());
433 const CodeDirectory
*cd
= this->codeDirectory();
435 MacOSError::throwMe(errSecCSUnsigned
);
436 AutoFileDesc
fd(mainExecutablePath(), O_RDONLY
);
437 fd
.fcntl(F_NOCACHE
, true); // turn off page caching (one-pass)
438 if (Universal
*fat
= mRep
->mainExecutableImage())
439 fd
.seek(fat
->archOffset());
440 size_t pageSize
= cd
->pageSize
? (1 << cd
->pageSize
) : 0;
441 size_t remaining
= cd
->codeLimit
;
442 for (size_t slot
= 0; slot
< cd
->nCodeSlots
; ++slot
) {
443 size_t size
= min(remaining
, pageSize
);
444 if (!cd
->validateSlot(fd
, size
, slot
)) {
445 secdebug("staticCode", "%p failed static validation of code page %zd", this, slot
);
446 mExecutableValidated
= true; // we tried
447 mExecutableValid
= false; // it failed
448 MacOSError::throwMe(errSecCSSignatureFailed
);
452 secdebug("staticCode", "%p validated full executable (%d pages)",
453 this, int(cd
->nCodeSlots
));
454 mExecutableValidated
= true; // we tried
455 mExecutableValid
= true; // it worked
460 // Perform static validation of sealed resources.
462 // This performs a whole-code static resource scan and effectively
463 // computes a concordance between what's on disk and what's in the ResourceDirectory.
464 // Any unsanctioned difference causes an error.
466 void SecStaticCode::validateResources()
469 CFDictionaryRef sealedResources
= resourceDictionary();
470 if (this->resourceBase()) // disk has resources
472 /* go to work below */;
474 MacOSError::throwMe(errSecCSResourcesNotFound
);
475 else // disk has no resources
477 MacOSError::throwMe(errSecCSResourcesNotFound
);
479 return; // no resources, not sealed - fine (no work)
481 // found resources, and they are sealed
482 CFDictionaryRef rules
= cfget
<CFDictionaryRef
>(sealedResources
, "rules");
483 CFDictionaryRef files
= cfget
<CFDictionaryRef
>(sealedResources
, "files");
484 secdebug("staticCode", "%p verifying %d sealed resources",
485 this, int(CFDictionaryGetCount(files
)));
487 // make a shallow copy of the ResourceDirectory so we can "check off" what we find
488 CFRef
<CFMutableDictionaryRef
> resourceMap
= CFDictionaryCreateMutableCopy(NULL
,
489 CFDictionaryGetCount(files
), files
);
493 // scan through the resources on disk, checking each against the resourceDirectory
494 CollectingContext
ctx(*this); // collect all failures in here
495 ResourceBuilder
resources(cfString(this->resourceBase()), rules
);
496 mRep
->adjustResources(resources
);
498 ResourceBuilder::Rule
*rule
;
500 while (resources
.next(path
, rule
)) {
501 if (CFDataRef value
= resource(path
, ctx
))
503 CFDictionaryRemoveValue(resourceMap
, CFTempString(path
));
504 secdebug("staticCode", "%p validated %s", this, path
.c_str());
507 if (CFDictionaryGetCount(resourceMap
) > 0) {
508 secdebug("staticCode", "%p sealed resource(s) not found in code", this);
509 CFDictionaryApplyFunction(resourceMap
, SecStaticCode::checkOptionalResource
, &ctx
);
512 // now check for any errors found in the reporting context
516 secdebug("staticCode", "%p sealed resources okay", this);
520 void SecStaticCode::checkOptionalResource(CFTypeRef key
, CFTypeRef value
, void *context
)
522 CollectingContext
*ctx
= static_cast<CollectingContext
*>(context
);
523 ResourceSeal
seal(value
);
524 if (!seal
.optional())
525 if (key
&& CFGetTypeID(key
) == CFStringGetTypeID()) {
526 ctx
->reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
,
527 CFTempURL(CFStringRef(key
), false, ctx
->code
.resourceBase()));
529 ctx
->reportProblem(errSecCSBadResource
, kSecCFErrorResourceSeal
, key
);
534 // Load, validate, cache, and return CFDictionary forms of sealed resources.
536 CFDictionaryRef
SecStaticCode::infoDictionary()
539 mInfoDict
.take(getDictionary(cdInfoSlot
));
540 secdebug("staticCode", "%p loaded InfoDict %p", this, mInfoDict
.get());
545 CFDictionaryRef
SecStaticCode::entitlements()
547 if (!mEntitlements
) {
549 if (CFDataRef entitlementData
= component(cdEntitlementSlot
)) {
550 validateComponent(cdEntitlementSlot
);
551 const EntitlementBlob
*blob
= reinterpret_cast<const EntitlementBlob
*>(CFDataGetBytePtr(entitlementData
));
552 if (blob
->validateBlob()) {
553 mEntitlements
.take(blob
->entitlements());
554 secdebug("staticCode", "%p loaded Entitlements %p", this, mEntitlements
.get());
556 // we do not consider a different blob type to be an error. We think it's a new format we don't understand
559 return mEntitlements
;
562 CFDictionaryRef
SecStaticCode::resourceDictionary()
564 if (mResourceDict
) // cached
565 return mResourceDict
;
566 if (CFRef
<CFDictionaryRef
> dict
= getDictionary(cdResourceDirSlot
))
567 if (cfscan(dict
, "{rules=%Dn,files=%Dn}")) {
568 secdebug("staticCode", "%p loaded ResourceDict %p",
569 this, mResourceDict
.get());
570 return mResourceDict
= dict
;
578 // Load and cache the resource directory base.
579 // Note that the base is optional for each DiskRep.
581 CFURLRef
SecStaticCode::resourceBase()
583 if (!mGotResourceBase
) {
584 string base
= mRep
->resourcesRootPath();
586 mResourceBase
.take(makeCFURL(base
, true));
587 mGotResourceBase
= true;
589 return mResourceBase
;
594 // Load a component, validate it, convert it to a CFDictionary, and return that.
595 // This will force load and validation, which means that it will perform basic
596 // validation if it hasn't been done yet.
598 CFDictionaryRef
SecStaticCode::getDictionary(CodeDirectory::SpecialSlot slot
)
601 if (CFDataRef infoData
= component(slot
)) {
602 validateComponent(slot
);
603 if (CFDictionaryRef dict
= makeCFDictionaryFrom(infoData
))
606 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
613 // Load, validate, and return a sealed resource.
614 // The resource data (loaded in to memory as a blob) is returned and becomes
615 // the responsibility of the caller; it is NOT cached by SecStaticCode.
617 // A resource that is not sealed will not be returned, and an error will be thrown.
618 // A missing resource will cause an error unless it's marked optional in the Directory.
619 // Under no circumstances will a corrupt resource be returned.
620 // NULL will only be returned for a resource that is neither sealed nor present
621 // (or that is sealed, absent, and marked optional).
622 // If the ResourceDictionary itself is not sealed, this function will always fail.
624 // There is currently no interface for partial retrieval of the resource data.
625 // (Since the ResourceDirectory does not currently support segmentation, all the
626 // data would have to be read anyway, but it could be read into a reusable buffer.)
628 CFDataRef
SecStaticCode::resource(string path
, ValidationContext
&ctx
)
630 if (CFDictionaryRef rdict
= resourceDictionary()) {
631 if (CFTypeRef file
= cfget(rdict
, "files.%s", path
.c_str())) {
632 ResourceSeal seal
= file
;
633 if (!resourceBase()) // no resources in DiskRep
634 MacOSError::throwMe(errSecCSResourcesNotFound
);
635 CFRef
<CFURLRef
> fullpath
= makeCFURL(path
, false, resourceBase());
636 if (CFRef
<CFDataRef
> data
= cfLoadFile(fullpath
)) {
638 hasher(CFDataGetBytePtr(data
), CFDataGetLength(data
));
639 if (hasher
.verify(seal
.hash()))
640 return data
.yield(); // good
642 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAltered
, fullpath
); // altered
644 if (!seal
.optional())
645 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
, fullpath
); // was sealed but is now missing
647 return NULL
; // validly missing
650 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAdded
, CFTempURL(path
, false, resourceBase()));
653 MacOSError::throwMe(errSecCSResourcesNotSealed
);
656 CFDataRef
SecStaticCode::resource(string path
)
658 ValidationContext ctx
;
659 return resource(path
, ctx
);
664 // Test a CodeDirectory flag.
665 // Returns false if there is no CodeDirectory.
666 // May throw if the CodeDirectory is present but somehow invalid.
668 bool SecStaticCode::flag(uint32_t tested
)
670 if (const CodeDirectory
*cd
= this->codeDirectory(false))
671 return cd
->flags
& tested
;
678 // Retrieve the full SuperBlob containing all internal requirements.
680 const Requirements
*SecStaticCode::internalRequirements()
682 if (CFDataRef req
= component(cdRequirementsSlot
))
683 return (const Requirements
*)CFDataGetBytePtr(req
);
690 // Retrieve a particular internal requirement by type.
692 const Requirement
*SecStaticCode::internalRequirement(SecRequirementType type
)
694 if (const Requirements
*reqs
= internalRequirements())
695 return reqs
->find
<Requirement
>(type
);
702 // Return the Designated Requirement. This can be either explicit in the
703 // Internal Requirements resource, or implicitly generated.
705 const Requirement
*SecStaticCode::designatedRequirement()
707 if (const Requirement
*req
= internalRequirement(kSecDesignatedRequirementType
)) {
708 return req
; // explicit in signing data
711 mDesignatedReq
= defaultDesignatedRequirement();
712 return mDesignatedReq
;
718 // Generate the default (implicit) Designated Requirement for this StaticCode.
719 // This is a heuristic of sorts, and may change over time (for the better, we hope).
721 // The current logic is this:
722 // * If the code is ad-hoc signed, use the CodeDirectory hash directory.
723 // * Otherwise, use the form anchor (anchor) and identifier (CodeDirectory identifier).
724 // ** If the root CA is Apple's, we use the "anchor apple" construct. Otherwise,
725 // we default to the leaf (directly signing) certificate.
727 const Requirement
*SecStaticCode::defaultDesignatedRequirement()
729 validateDirectory(); // need the cert chain
730 Requirement::Maker maker
;
732 // if this is an ad-hoc (unsigned) object, return a cdhash requirement
733 if (flag(kSecCodeSignatureAdhoc
)) {
735 hash(codeDirectory(), codeDirectory()->length());
738 maker
.cdhash(digest
);
740 // always require the identifier
742 maker
.ident(codeDirectory()->identifier());
744 SHA1::Digest anchorHash
;
745 hashOfCertificate(cert(Requirement::anchorCert
), anchorHash
);
746 if (!memcmp(anchorHash
, Requirement::appleAnchorHash(), SHA1::digestLength
)
747 #if defined(TEST_APPLE_ANCHOR)
748 || !memcmp(anchorHash
, Requirement::testAppleAnchorHash(), SHA1::digestLength
)
751 defaultDesignatedAppleAnchor(maker
);
753 defaultDesignatedNonAppleAnchor(maker
);
759 static const uint8_t adcSdkMarker
[] = { APPLE_EXTENSION_OID
, 2, 1 };
760 static const CSSM_DATA adcSdkMarkerOID
= { sizeof(adcSdkMarker
), (uint8_t *)adcSdkMarker
};
762 void SecStaticCode::defaultDesignatedAppleAnchor(Requirement::Maker
&maker
)
764 if (isAppleSDKSignature()) {
765 // get the Common Name DN element for the leaf
766 CFRef
<CFStringRef
> leafCN
;
767 MacOSError::check(SecCertificateCopySubjectComponent(cert(Requirement::leafCert
),
768 &CSSMOID_CommonName
, &leafCN
.aref()));
770 // apple anchor generic and ...
772 maker
.anchorGeneric(); // apple generic anchor and...
773 // ... leaf[subject.CN] = <leaf's subject> and ...
775 maker
.put(opCertField
); // certificate
776 maker
.put(0); // leaf
777 maker
.put("subject.CN"); // [subject.CN]
778 maker
.put(matchEqual
); // =
779 maker
.putData(leafCN
); // <leaf CN>
780 // ... cert 1[field.<marker>] exists
781 maker
.put(opCertGeneric
); // certificate
783 maker
.putData(adcSdkMarkerOID
.Data
, adcSdkMarkerOID
.Length
); // [field.<marker>]
784 maker
.put(matchExists
); // exists
788 // otherwise, claim this program for Apple
792 bool SecStaticCode::isAppleSDKSignature()
794 if (CFArrayRef certChain
= certificates()) // got cert chain
795 if (CFArrayGetCount(certChain
) == 3) // leaf, one intermediate, anchor
796 if (SecCertificateRef intermediate
= cert(1)) // get intermediate
797 if (certificateHasField(intermediate
, CssmOid::overlay(adcSdkMarkerOID
)))
803 void SecStaticCode::defaultDesignatedNonAppleAnchor(Requirement::Maker
&maker
)
805 // get the Organization DN element for the leaf
806 CFRef
<CFStringRef
> leafOrganization
;
807 MacOSError::check(SecCertificateCopySubjectComponent(cert(Requirement::leafCert
),
808 &CSSMOID_OrganizationName
, &leafOrganization
.aref()));
810 // now step up the cert chain looking for the first cert with a different one
811 int slot
= Requirement::leafCert
; // start at leaf
812 if (leafOrganization
) {
813 while (SecCertificateRef ca
= cert(slot
+1)) { // NULL if you over-run the anchor slot
814 CFRef
<CFStringRef
> caOrganization
;
815 MacOSError::check(SecCertificateCopySubjectComponent(ca
, &CSSMOID_OrganizationName
, &caOrganization
.aref()));
816 if (CFStringCompare(leafOrganization
, caOrganization
, 0) != kCFCompareEqualTo
)
820 if (slot
== CFArrayGetCount(mCertChain
) - 1) // went all the way to the anchor...
821 slot
= Requirement::anchorCert
; // ... so say that
824 // nail the last cert with the leaf's Organization value
825 SHA1::Digest authorityHash
;
826 hashOfCertificate(cert(slot
), authorityHash
);
827 maker
.anchor(slot
, authorityHash
);
832 // Validate a SecStaticCode against the internal requirement of a particular type.
834 void SecStaticCode::validateRequirements(SecRequirementType type
, SecStaticCode
*target
,
835 OSStatus nullError
/* = noErr */)
837 secdebug("staticCode", "%p validating %s requirements for %p",
838 this, Requirement::typeNames
[type
], target
);
839 if (const Requirement
*req
= internalRequirement(type
))
840 target
->validateRequirements(req
, nullError
? nullError
: errSecCSReqFailed
);
841 else if (nullError
) {
842 secdebug("staticCode", "%p NULL validate for %s prohibited",
843 this, Requirement::typeNames
[type
]);
844 MacOSError::throwMe(nullError
);
846 secdebug("staticCode", "%p NULL validate (no requirements for %s)",
847 this, Requirement::typeNames
[type
]);
852 // Validate this StaticCode against an external Requirement
854 void SecStaticCode::validateRequirements(const Requirement
*req
, OSStatus failure
)
858 req
->validate(Requirement::Context(mCertChain
, infoDictionary(), entitlements(), codeDirectory()), failure
);
863 // Retrieve one certificate from the cert chain.
864 // Positive and negative indices can be used:
865 // [ leaf, intermed-1, ..., intermed-n, anchor ]
867 // Returns NULL if unavailable for any reason.
869 SecCertificateRef
SecStaticCode::cert(int ix
)
871 validateDirectory(); // need cert chain
873 CFIndex length
= CFArrayGetCount(mCertChain
);
876 if (ix
>= 0 && ix
< length
)
877 return SecCertificateRef(CFArrayGetValueAtIndex(mCertChain
, ix
));
882 CFArrayRef
SecStaticCode::certificates()
884 validateDirectory(); // need cert chain
890 // Gather API-official information about this StaticCode.
892 // This method lives in the twilight between the API and internal layers,
893 // since it generates API objects (Sec*Refs) for return.
895 CFDictionaryRef
SecStaticCode::signingInformation(SecCSFlags flags
)
898 // Start with the pieces that we return even for unsigned code.
899 // This makes Sec[Static]CodeRefs useful as API-level replacements
900 // of our internal OSXCode objects.
902 CFRef
<CFMutableDictionaryRef
> dict
= makeCFMutableDictionary(1,
903 kSecCodeInfoMainExecutable
, CFTempURL(this->mainExecutablePath()).get()
907 // If we're not signed, this is all you get
909 if (!this->isSigned())
914 // Now add the generic attributes that we always include
916 CFDictionaryAddValue(dict
, kSecCodeInfoIdentifier
, CFTempString(this->identifier()));
917 CFDictionaryAddValue(dict
, kSecCodeInfoFormat
, CFTempString(this->format()));
918 if (CFDictionaryRef info
= infoDictionary())
919 CFDictionaryAddValue(dict
, kSecCodeInfoPList
, info
);
922 // kSecCSSigningInformation adds information about signing certificates and chains
924 if (flags
& kSecCSSigningInformation
) {
925 if (CFArrayRef certs
= certificates())
926 CFDictionaryAddValue(dict
, kSecCodeInfoCertificates
, certs
);
927 if (CFDataRef sig
= signature())
928 CFDictionaryAddValue(dict
, kSecCodeInfoCMS
, sig
);
930 CFDictionaryAddValue(dict
, kSecCodeInfoTrust
, mTrust
);
931 if (CFAbsoluteTime time
= signingTime())
932 if (CFRef
<CFDateRef
> date
= CFDateCreate(NULL
, time
))
933 CFDictionaryAddValue(dict
, kSecCodeInfoTime
, date
);
937 // kSecCSRequirementInformation adds information on requirements
939 if (flags
& kSecCSRequirementInformation
) {
940 if (const Requirements
*reqs
= internalRequirements()) {
941 CFDictionaryAddValue(dict
, kSecCodeInfoRequirements
,
942 CFTempString(Dumper::dump(reqs
)));
943 CFDictionaryAddValue(dict
, kSecCodeInfoRequirementData
, CFTempData(*reqs
));
945 const Requirement
*dreq
= designatedRequirement();
946 const Requirement
*ddreq
= defaultDesignatedRequirement();
947 CFRef
<SecRequirementRef
> ddreqRef
= (new SecRequirement(ddreq
))->handle();
949 CFDictionaryAddValue(dict
, kSecCodeInfoDesignatedRequirement
, ddreqRef
);
950 CFDictionaryAddValue(dict
, kSecCodeInfoImplicitDesignatedRequirement
, ddreqRef
);
952 CFDictionaryAddValue(dict
, kSecCodeInfoDesignatedRequirement
,
953 CFRef
<SecRequirementRef
>((new SecRequirement(dreq
))->handle()));
954 CFDictionaryAddValue(dict
, kSecCodeInfoImplicitDesignatedRequirement
, ddreqRef
);
957 if (CFDataRef ent
= component(cdEntitlementSlot
))
958 CFDictionaryAddValue(dict
, kSecCodeInfoEntitlements
, ent
);
962 // kSecCSInternalInformation adds internal information meant to be for Apple internal
963 // use (SPI), and not guaranteed to be stable. Primarily, this is data we want
964 // to reliably transmit through the API wall so that code outside the Security.framework
965 // can use it without having to play nasty tricks to get it.
967 if (flags
& kSecCSInternalInformation
) {
969 CFDictionaryAddValue(dict
, CFSTR("CodeDirectory"), mDir
);
970 CFDictionaryAddValue(dict
, CFSTR("CodeOffset"), CFTempNumber(mRep
->signingBase()));
971 if (CFDictionaryRef resources
= resourceDictionary())
972 CFDictionaryAddValue(dict
, CFSTR("ResourceDirectory"), resources
);
977 // kSecCSContentInformation adds more information about the physical layout
978 // of the signed code. This is (only) useful for packaging or patching-oriented
981 if (flags
& kSecCSContentInformation
)
982 if (CFRef
<CFArrayRef
> files
= mRep
->modifiedFiles())
983 CFDictionaryAddValue(dict
, kSecCodeInfoChangedFiles
, files
);
990 // Resource validation contexts.
991 // The default context simply throws a CSError, rudely terminating the operation.
993 SecStaticCode::ValidationContext::~ValidationContext()
996 void SecStaticCode::ValidationContext::reportProblem(OSStatus rc
, CFStringRef type
, CFTypeRef value
)
998 CSError::throwMe(rc
, type
, value
);
1001 void SecStaticCode::CollectingContext::reportProblem(OSStatus rc
, CFStringRef type
, CFTypeRef value
)
1003 if (mStatus
== noErr
)
1004 mStatus
= rc
; // record first failure for eventual error return
1007 mCollection
.take(makeCFMutableDictionary(0));
1008 CFMutableArrayRef element
= CFMutableArrayRef(CFDictionaryGetValue(mCollection
, type
));
1010 element
= makeCFMutableArray(0);
1013 CFDictionaryAddValue(mCollection
, type
, element
);
1016 CFArrayAppendValue(element
, value
);
1020 void SecStaticCode::CollectingContext::throwMe()
1022 assert(mStatus
!= noErr
);
1023 throw CSError(mStatus
, mCollection
.yield());
1028 // DetachedRep construction
1030 DetachedRep::DetachedRep(CFDataRef sig
, DiskRep
*orig
)
1031 : original(orig
), mSignature(sig
)
1033 const BlobCore
*sigBlob
= reinterpret_cast<const BlobCore
*>(CFDataGetBytePtr(sig
));
1034 if (sigBlob
->is
<EmbeddedSignatureBlob
>()) { // architecture-less
1035 mArch
= EmbeddedSignatureBlob::specific(sigBlob
);
1038 } else if (sigBlob
->is
<DetachedSignatureBlob
>()) { // architecture collection
1039 const DetachedSignatureBlob
*dsblob
= DetachedSignatureBlob::specific(sigBlob
);
1040 if (Universal
*fat
= orig
->mainExecutableImage()) {
1041 if (const BlobCore
*blob
= dsblob
->find(fat
->bestNativeArch().cpuType())) {
1042 mArch
= EmbeddedSignatureBlob::specific(blob
);
1043 mGlobal
= EmbeddedSignatureBlob::specific(dsblob
->find(0));
1046 secdebug("staticcode", "detached signature missing architecture %s",
1047 fat
->bestNativeArch().name());
1049 secdebug("staticcode", "detached signature requires Mach-O binary");
1051 secdebug("staticcode", "detached signature bad magic 0x%x", sigBlob
->magic());
1052 MacOSError::throwMe(errSecCSSignatureInvalid
);
1055 CFDataRef
DetachedRep::component(CodeDirectory::SpecialSlot slot
)
1057 if (CFDataRef result
= mArch
->component(slot
))
1060 if (CFDataRef result
= mGlobal
->component(slot
))
1062 return original
->component(slot
);
1066 } // end namespace CodeSigning
1067 } // end namespace Security