2  * Copyright (c) 2006-2015 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" 
  33 #include "reqdumper.h" 
  34 #include "reqparser.h" 
  36 #include "resources.h" 
  37 #include "detachedrep.h" 
  39 #include "csdatabase.h" 
  41 #include "dirscanner.h" 
  42 #include <CoreFoundation/CFURLAccess.h> 
  43 #include <Security/SecPolicyPriv.h> 
  44 #include <Security/SecTrustPriv.h> 
  45 #include <Security/SecCertificatePriv.h> 
  47 #include <Security/CMSPrivate.h> 
  49 #import <Security/SecCMS.h> 
  50 #include <Security/SecCmsContentInfo.h> 
  51 #include <Security/SecCmsSignerInfo.h> 
  52 #include <Security/SecCmsSignedData.h> 
  54 #include <Security/cssmapplePriv.h> 
  56 #include <security_utilities/unix++.h> 
  57 #include <security_utilities/cfmunge.h> 
  58 #include <security_utilities/casts.h> 
  59 #include <Security/CMSDecoder.h> 
  60 #include <security_utilities/logging.h> 
  62 #include <sys/xattr.h> 
  64 #include <IOKit/storage/IOStorageDeviceCharacteristics.h> 
  65 #include <dispatch/private.h> 
  66 #include <os/assumes.h> 
  71 namespace CodeSigning 
{ 
  73 using namespace UnixPlusPlus
; 
  75 // A requirement representing a Mac or iOS dev cert, a Mac or iOS distribution cert, or a developer ID 
  76 static const char WWDRRequirement
[] = "anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.2] exists"; 
  77 static const char MACWWDRRequirement
[] = "anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.12] exists"; 
  78 static const char developerID
[] = "anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists" 
  79                                                                                         " and certificate leaf[field.1.2.840.113635.100.6.1.13] exists"; 
  80 static const char distributionCertificate
[] =   "anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.7] exists"; 
  81 static const char iPhoneDistributionCert
[] =    "anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.4] exists"; 
  84 // Map a component slot number to a suitable error code for a failure 
  86 static inline OSStatus 
errorForSlot(CodeDirectory::SpecialSlot slot
) 
  90                 return errSecCSInfoPlistFailed
; 
  91         case cdResourceDirSlot
: 
  92                 return errSecCSResourceDirectoryFailed
; 
  94                 return errSecCSSignatureFailed
; 
 100 // Construct a SecStaticCode object given a disk representation object 
 102 SecStaticCode::SecStaticCode(DiskRep 
*rep
) 
 103         : mCheckfix30814861builder1(NULL
), 
 105           mValidated(false), mExecutableValidated(false), mResourcesValidated(false), mResourcesValidContext(NULL
), 
 106           mProgressQueue("com.apple.security.validation-progress", false, QOS_CLASS_UNSPECIFIED
), 
 107           mOuterScope(NULL
), mResourceScope(NULL
), 
 108           mDesignatedReq(NULL
), mGotResourceBase(false), mMonitor(NULL
), mLimitedAsync(NULL
) 
 112     , mTrustedSigningCertChain(false) 
 116         CODESIGN_STATIC_CREATE(this, rep
); 
 118         checkForSystemSignature(); 
 124 // Clean up a SecStaticCode object 
 126 SecStaticCode::~SecStaticCode() throw() 
 128         ::free(const_cast<Requirement 
*>(mDesignatedReq
)); 
 129         delete mResourcesValidContext
; 
 130         delete mLimitedAsync
; 
 131         delete mCheckfix30814861builder1
; 
 137 // Initialize a nested SecStaticCode object from its parent 
 139 void SecStaticCode::initializeFromParent(const SecStaticCode
& parent
) { 
 140         mOuterScope 
= &parent
; 
 141         setMonitor(parent
.monitor()); 
 142         if (parent
.mLimitedAsync
) 
 143                 mLimitedAsync 
= new LimitedAsync(*parent
.mLimitedAsync
); 
 147 // CF-level comparison of SecStaticCode objects compares CodeDirectory hashes if signed, 
 148 // and falls back on comparing canonical paths if (both are) not. 
 150 bool SecStaticCode::equal(SecCFObject 
&secOther
) 
 152         SecStaticCode 
*other 
= static_cast<SecStaticCode 
*>(&secOther
); 
 153         CFDataRef mine 
= this->cdHash(); 
 154         CFDataRef his 
= other
->cdHash(); 
 156                 return mine 
&& his 
&& CFEqual(mine
, his
); 
 158                 return CFEqual(CFRef
<CFURLRef
>(this->copyCanonicalPath()), CFRef
<CFURLRef
>(other
->copyCanonicalPath())); 
 161 CFHashCode 
SecStaticCode::hash() 
 163         if (CFDataRef h 
= this->cdHash()) 
 166                 return CFHash(CFRef
<CFURLRef
>(this->copyCanonicalPath())); 
 171 // Invoke a stage monitor if registered 
 173 CFTypeRef 
SecStaticCode::reportEvent(CFStringRef stage
, CFDictionaryRef info
) 
 176                 return mMonitor(this->handle(false), stage
, info
); 
 181 void SecStaticCode::prepareProgress(unsigned int workload
) 
 183         dispatch_sync(mProgressQueue
, ^{ 
 184                 mCancelPending 
= false;                 // not canceled 
 186         if (mValidationFlags 
& kSecCSReportProgress
) { 
 187                 mCurrentWork 
= 0;                               // nothing done yet 
 188                 mTotalWork 
= workload
;                  // totally fake - we don't know how many files we'll get to chew 
 192 void SecStaticCode::reportProgress(unsigned amount 
/* = 1 */) 
 194         if (mMonitor 
&& (mValidationFlags 
& kSecCSReportProgress
)) { 
 195                 // update progress and report 
 196                 __block 
bool cancel 
= false; 
 197                 dispatch_sync(mProgressQueue
, ^{ 
 200                         mCurrentWork 
+= amount
; 
 201                         mMonitor(this->handle(false), CFSTR("progress"), CFTemp
<CFDictionaryRef
>("{current=%d,total=%d}", mCurrentWork
, mTotalWork
)); 
 203                 // if cancellation is pending, abort now 
 205                         MacOSError::throwMe(errSecCSCancelled
); 
 211 // Set validation conditions for fine-tuning legacy tolerance 
 213 static void addError(CFTypeRef cfError
, void* context
) 
 215         if (CFGetTypeID(cfError
) == CFNumberGetTypeID()) { 
 217                 CFNumberGetValue(CFNumberRef(cfError
), kCFNumberSInt64Type
, (void*)&error
); 
 218                 MacOSErrorSet
* errors 
= (MacOSErrorSet
*)context
; 
 219                 errors
->insert(OSStatus(error
)); 
 223 void SecStaticCode::setValidationModifiers(CFDictionaryRef conditions
) 
 226                 CFDictionary 
source(conditions
, errSecCSDbCorrupt
); 
 227                 mAllowOmissions 
= source
.get
<CFArrayRef
>("omissions"); 
 228                 if (CFArrayRef errors 
= source
.get
<CFArrayRef
>("errors")) 
 229                         CFArrayApplyFunction(errors
, CFRangeMake(0, CFArrayGetCount(errors
)), addError
, &this->mTolerateErrors
); 
 235 // Request cancellation of a validation in progress. 
 236 // We do this by posting an abort flag that is checked periodically. 
 238 void SecStaticCode::cancelValidation() 
 240         if (!(mValidationFlags 
& kSecCSReportProgress
))         // not using progress reporting; cancel won't make it through 
 241                 MacOSError::throwMe(errSecCSInvalidFlags
); 
 242         dispatch_assert_queue(mProgressQueue
); 
 243         mCancelPending 
= true; 
 248 // Attach a detached signature. 
 250 void SecStaticCode::detachedSignature(CFDataRef sigData
) 
 253                 mDetachedSig 
= sigData
; 
 254                 mRep 
= new DetachedRep(sigData
, mRep
->base(), "explicit detached"); 
 255                 CODESIGN_STATIC_ATTACH_EXPLICIT(this, mRep
); 
 259                 CODESIGN_STATIC_ATTACH_EXPLICIT(this, NULL
); 
 265 // Consult the system detached signature database to see if it contains 
 266 // a detached signature for this StaticCode. If it does, fetch and attach it. 
 267 // We do this only if the code has no signature already attached. 
 269 void SecStaticCode::checkForSystemSignature() 
 272         if (!this->isSigned()) { 
 273                 SignatureDatabase db
; 
 276                                 if (RefPointer
<DiskRep
> dsig 
= db
.findCode(mRep
)) { 
 277                                         CODESIGN_STATIC_ATTACH_SYSTEM(this, dsig
); 
 284     MacOSError::throwMe(errSecUnimplemented
); 
 290 // Return a descriptive string identifying the source of the code signature 
 292 string 
SecStaticCode::signatureSource() 
 296         if (DetachedRep 
*rep 
= dynamic_cast<DetachedRep 
*>(mRep
.get())) 
 297                 return rep
->source(); 
 303 // Do ::required, but convert incoming SecCodeRefs to their SecStaticCodeRefs 
 306 SecStaticCode 
*SecStaticCode::requiredStatic(SecStaticCodeRef ref
) 
 308         SecCFObject 
*object 
= SecCFObject::required(ref
, errSecCSInvalidObjectRef
); 
 309         if (SecStaticCode 
*scode 
= dynamic_cast<SecStaticCode 
*>(object
)) 
 311         else if (SecCode 
*code 
= dynamic_cast<SecCode 
*>(object
)) 
 312                 return code
->staticCode(); 
 313         else    // neither (a SecSomethingElse) 
 314                 MacOSError::throwMe(errSecCSInvalidObjectRef
); 
 317 SecCode 
*SecStaticCode::optionalDynamic(SecStaticCodeRef ref
) 
 319         SecCFObject 
*object 
= SecCFObject::required(ref
, errSecCSInvalidObjectRef
); 
 320         if (dynamic_cast<SecStaticCode 
*>(object
)) 
 322         else if (SecCode 
*code 
= dynamic_cast<SecCode 
*>(object
)) 
 324         else    // neither (a SecSomethingElse) 
 325                 MacOSError::throwMe(errSecCSInvalidObjectRef
); 
 330 // Void all cached validity data. 
 332 // We also throw out cached components, because the new signature data may have 
 333 // a different idea of what components should be present. We could reconcile the 
 334 // cached data instead, if performance seems to be impacted. 
 336 void SecStaticCode::resetValidity() 
 338         CODESIGN_EVAL_STATIC_RESET(this); 
 340         mExecutableValidated 
= mResourcesValidated 
= false; 
 341         if (mResourcesValidContext
) { 
 342                 delete mResourcesValidContext
; 
 343                 mResourcesValidContext 
= NULL
; 
 347         for (unsigned n 
= 0; n 
< cdSlotCount
; n
++) 
 350         mEntitlements 
= NULL
; 
 351         mResourceDict 
= NULL
; 
 352         mDesignatedReq 
= NULL
; 
 354         mGotResourceBase 
= false; 
 363         // we may just have updated the system database, so check again 
 364         checkForSystemSignature(); 
 370 // Retrieve a sealed component by special slot index. 
 371 // If the CodeDirectory has already been validated, validate against that. 
 372 // Otherwise, retrieve the component without validation (but cache it). Validation 
 373 // will go through the cache and validate all cached components. 
 375 CFDataRef 
SecStaticCode::component(CodeDirectory::SpecialSlot slot
, OSStatus fail 
/* = errSecCSSignatureFailed */) 
 377         assert(slot 
<= cdSlotMax
); 
 379         CFRef
<CFDataRef
> &cache 
= mCache
[slot
]; 
 381                 if (CFRef
<CFDataRef
> data 
= mRep
->component(slot
)) { 
 382                         if (validated()) { // if the directory has been validated... 
 383                                 if (!codeDirectory()->slotIsPresent(-slot
)) 
 386                                 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data
), // ... and it's no good 
 387                                                 CFDataGetLength(data
), -slot
)) 
 388                                         MacOSError::throwMe(errorForSlot(slot
)); // ... then bail 
 390                         cache 
= data
;   // it's okay, cache it 
 391                 } else {        // absent, mark so 
 392                         if (validated())        // if directory has been validated... 
 393                                 if (codeDirectory()->slotIsPresent(-slot
)) // ... and the slot is NOT missing 
 394                                         MacOSError::throwMe(errorForSlot(slot
));        // was supposed to be there 
 395                         cache 
= CFDataRef(kCFNull
);             // white lie 
 398         return (cache 
== CFDataRef(kCFNull
)) ? NULL 
: cache
.get(); 
 403 // Get the CodeDirectory. 
 404 // Throws (if check==true) or returns NULL (check==false) if there is none. 
 405 // Always throws if the CodeDirectory exists but is invalid. 
 406 // NEVER validates against the signature. 
 408 const CodeDirectory 
*SecStaticCode::codeDirectory(bool check 
/* = true */) const 
 411                 // pick our favorite CodeDirectory from the choices we've got 
 413                         CodeDirectoryMap candidates
; 
 414                         if (loadCodeDirectories(candidates
)) { 
 415                                 CodeDirectory::HashAlgorithm type 
= CodeDirectory::bestHashOf(mHashAlgorithms
); 
 416                                 mDir 
= candidates
[type
];                                                                // and the winner is... 
 417                                 candidates
.swap(mCodeDirectories
); 
 422                         // We wanted a NON-checked peek and failed to safely decode the existing CodeDirectory. 
 423                         // Pretend this is unsigned, but make sure we didn't somehow cache an invalid CodeDirectory. 
 426                                 Syslog::warning("code signing internal problem: mDir set despite exception exit"); 
 427                                 MacOSError::throwMe(errSecCSInternalError
); 
 432                 return reinterpret_cast<const CodeDirectory 
*>(CFDataGetBytePtr(mDir
)); 
 434                 MacOSError::throwMe(errSecCSUnsigned
); 
 440 // Fetch an array of all available CodeDirectories. 
 441 // Returns false if unsigned (no classic CD slot), true otherwise. 
 443 bool SecStaticCode::loadCodeDirectories(CodeDirectoryMap
& cdMap
) const 
 445         __block CodeDirectoryMap candidates
; 
 446         __block 
CodeDirectory::HashAlgorithms hashAlgorithms
; 
 447         __block CFRef
<CFDataRef
> baseDir
; 
 448         auto add 
= ^bool (CodeDirectory::SpecialSlot slot
){ 
 449                 CFRef
<CFDataRef
> cdData 
= diskRep()->component(slot
); 
 452                 const CodeDirectory
* cd 
= reinterpret_cast<const CodeDirectory
*>(CFDataGetBytePtr(cdData
)); 
 453                 if (!cd
->validateBlob(CFDataGetLength(cdData
))) 
 454                         MacOSError::throwMe(errSecCSSignatureFailed
);   // no recovery - any suspect CD fails 
 455                 cd
->checkIntegrity(); 
 456                 auto result 
= candidates
.insert(make_pair(cd
->hashType
, cdData
.get())); 
 458                         MacOSError::throwMe(errSecCSSignatureInvalid
);  // duplicate hashType, go to heck 
 459                 hashAlgorithms
.insert(cd
->hashType
); 
 460                 if (slot 
== cdCodeDirectorySlot
) 
 464         if (!add(cdCodeDirectorySlot
)) 
 465                 return false;           // no classic slot CodeDirectory -> unsigned 
 466         for (CodeDirectory::SpecialSlot slot 
= cdAlternateCodeDirectorySlots
; slot 
< cdAlternateCodeDirectoryLimit
; slot
++) 
 467                 if (!add(slot
))         // no CodeDirectory at this slot -> end of alternates 
 469         if (candidates
.empty()) 
 470                 MacOSError::throwMe(errSecCSSignatureFailed
);           // no viable CodeDirectory in sight 
 471         // commit to cached values 
 472         cdMap
.swap(candidates
); 
 473         mHashAlgorithms
.swap(hashAlgorithms
); 
 480 // Get the hash of the CodeDirectory. 
 481 // Returns NULL if there is none. 
 483 CFDataRef 
SecStaticCode::cdHash() 
 486                 if (const CodeDirectory 
*cd 
= codeDirectory(false)) { 
 487                         mCDHash
.take(cd
->cdhash()); 
 488                         CODESIGN_STATIC_CDHASH(this, CFDataGetBytePtr(mCDHash
), (unsigned int)CFDataGetLength(mCDHash
)); 
 496 // Get an array of the cdhashes for all digest types in this signature 
 497 // The array is sorted by cd->hashType. 
 499 CFArrayRef 
SecStaticCode::cdHashes() 
 502                 CFRef
<CFMutableArrayRef
> cdList 
= makeCFMutableArray(0); 
 503                 for (auto it 
= mCodeDirectories
.begin(); it 
!= mCodeDirectories
.end(); ++it
) { 
 504                         const CodeDirectory 
*cd 
= (const CodeDirectory 
*)CFDataGetBytePtr(it
->second
); 
 505                         if (CFRef
<CFDataRef
> hash 
= cd
->cdhash()) 
 506                                 CFArrayAppendValue(cdList
, hash
); 
 508                 mCDHashes 
= cdList
.get(); 
 515 // Return the CMS signature blob; NULL if none found. 
 517 CFDataRef 
SecStaticCode::signature() 
 520                 mSignature
.take(mRep
->signature()); 
 523         MacOSError::throwMe(errSecCSUnsigned
); 
 528 // Verify the signature on the CodeDirectory. 
 529 // If this succeeds (doesn't throw), the CodeDirectory is statically trustworthy. 
 530 // Any outcome (successful or not) is cached for the lifetime of the StaticCode. 
 532 void SecStaticCode::validateDirectory() 
 534         // echo previous outcome, if any 
 535         // track revocation separately, as it may not have been checked 
 536         // during the initial validation 
 537         if (!validated() || ((mValidationFlags 
& kSecCSEnforceRevocationChecks
) && !revocationChecked())) 
 539                         // perform validation (or die trying) 
 540                         CODESIGN_EVAL_STATIC_DIRECTORY(this); 
 541                         mValidationExpired 
= verifySignature(); 
 542                         if (mValidationFlags 
& kSecCSEnforceRevocationChecks
) 
 543                                 mRevocationChecked 
= true; 
 545                         for (CodeDirectory::SpecialSlot slot 
= codeDirectory()->maxSpecialSlot(); slot 
>= 1; --slot
) 
 546                                 if (mCache
[slot
])       // if we already loaded that resource... 
 547                                         validateComponent(slot
, errorForSlot(slot
)); // ... then check it now 
 548                         mValidated 
= true;                      // we've done the deed... 
 549                         mValidationResult 
= errSecSuccess
;      // ... and it was good 
 550                 } catch (const CommonError 
&err
) { 
 552                         mValidationResult 
= err
.osStatus(); 
 555                         secinfo("staticCode", "%p validation threw non-common exception", this); 
 557                         Syslog::notice("code signing internal problem: unknown exception thrown by validation"); 
 558                         mValidationResult 
= errSecCSInternalError
; 
 562     // XXX: Embedded doesn't have CSSMERR_TP_CERT_EXPIRED so we can't throw it 
 563     // XXX: This should be implemented for embedded once we implement 
 564     // XXX: verifySignature and see how we're going to handle expired certs 
 566         if (mValidationResult 
== errSecSuccess
) { 
 567                 if (mValidationExpired
) 
 568                         if ((mValidationFlags 
& kSecCSConsiderExpiration
) 
 569                                         || (codeDirectory()->flags 
& kSecCodeSignatureForceExpiration
)) 
 570                                 MacOSError::throwMe(CSSMERR_TP_CERT_EXPIRED
); 
 572                 MacOSError::throwMe(mValidationResult
); 
 578 // Load and validate the CodeDirectory and all components *except* those related to the resource envelope. 
 579 // Those latter components are checked by validateResources(). 
 581 void SecStaticCode::validateNonResourceComponents() 
 583         this->validateDirectory(); 
 584         for (CodeDirectory::SpecialSlot slot 
= codeDirectory()->maxSpecialSlot(); slot 
>= 1; --slot
) 
 586                 case cdResourceDirSlot
:         // validated by validateResources 
 589                         this->component(slot
);          // loads and validates 
 596 // Check that any "top index" sealed into the signature conforms to what's actually here. 
 598 void SecStaticCode::validateTopDirectory() 
 600         assert(mDir
);           // must already have loaded CodeDirectories 
 601         if (CFDataRef topDirectory 
= component(cdTopDirectorySlot
)) { 
 602                 const auto topData 
= (const Endian
<uint32_t> *)CFDataGetBytePtr(topDirectory
); 
 603                 const auto topDataEnd 
= topData 
+ CFDataGetLength(topDirectory
) / sizeof(*topData
); 
 604                 std::vector
<uint32_t> signedVector(topData
, topDataEnd
); 
 606                 std::vector
<uint32_t> foundVector
; 
 607                 foundVector
.push_back(cdCodeDirectorySlot
);     // mandatory 
 608                 for (CodeDirectory::Slot slot 
= 1; slot 
<= cdSlotMax
; ++slot
) 
 610                                 foundVector
.push_back(slot
); 
 611                 int alternateCount 
= int(mCodeDirectories
.size() - 1);          // one will go into cdCodeDirectorySlot 
 612                 for (int n 
= 0; n 
< alternateCount
; n
++) 
 613                         foundVector
.push_back(cdAlternateCodeDirectorySlots 
+ n
); 
 614                 foundVector
.push_back(cdSignatureSlot
);         // mandatory (may be empty) 
 616                 if (signedVector 
!= foundVector
) 
 617                         MacOSError::throwMe(errSecCSSignatureFailed
); 
 623 // Get the (signed) signing date from the code signature. 
 624 // Sadly, we need to validate the signature to get the date (as a side benefit). 
 625 // This means that you can't get the signing time for invalidly signed code. 
 627 // We could run the decoder "almost to" verification to avoid this, but there seems 
 628 // little practical point to such a duplication of effort. 
 630 CFAbsoluteTime 
SecStaticCode::signingTime() 
 636 CFAbsoluteTime 
SecStaticCode::signingTimestamp() 
 639         return mSigningTimestamp
; 
 644 // Verify the CMS signature. 
 645 // This performs the cryptographic tango. It returns if the signature is valid, 
 646 // or throws if it is not. As a side effect, a successful return sets up the 
 647 // cached certificate chain for future use. 
 648 // Returns true if the signature is expired (the X.509 sense), false if it's not. 
 649 // Expiration is fatal (throws) if a secure timestamp is included, but not otherwise. 
 651 bool SecStaticCode::verifySignature() 
 653         // ad-hoc signed code is considered validly signed by definition 
 654         if (flag(kSecCodeSignatureAdhoc
)) { 
 655                 CODESIGN_EVAL_STATIC_SIGNATURE_ADHOC(this); 
 659         DTRACK(CODESIGN_EVAL_STATIC_SIGNATURE
, this, (char*)this->mainExecutablePath().c_str()); 
 661         // decode CMS and extract SecTrust for verification 
 662         CFRef
<CMSDecoderRef
> cms
; 
 663         MacOSError::check(CMSDecoderCreate(&cms
.aref())); // create decoder 
 664         CFDataRef sig 
= this->signature(); 
 665         MacOSError::check(CMSDecoderUpdateMessage(cms
, CFDataGetBytePtr(sig
), CFDataGetLength(sig
))); 
 666         this->codeDirectory();  // load CodeDirectory (sets mDir) 
 667         MacOSError::check(CMSDecoderSetDetachedContent(cms
, mBaseDir
)); 
 668         MacOSError::check(CMSDecoderFinalizeMessage(cms
)); 
 669         MacOSError::check(CMSDecoderSetSearchKeychain(cms
, cfEmptyArray())); 
 670         CFRef
<CFArrayRef
> vf_policies(createVerificationPolicies()); 
 671         CFRef
<CFArrayRef
> ts_policies(createTimeStampingAndRevocationPolicies()); 
 673         CMSSignerStatus status
; 
 674         MacOSError::check(CMSDecoderCopySignerStatus(cms
, 0, vf_policies
, 
 675                                 false, &status
, &mTrust
.aref(), NULL
)); 
 677         if (status 
!= kCMSSignerValid
) { 
 680                         case kCMSSignerUnsigned
: reason
="kCMSSignerUnsigned"; break; 
 681                         case kCMSSignerNeedsDetachedContent
: reason
="kCMSSignerNeedsDetachedContent"; break; 
 682                         case kCMSSignerInvalidSignature
: reason
="kCMSSignerInvalidSignature"; break; 
 683                         case kCMSSignerInvalidCert
: reason
="kCMSSignerInvalidCert"; break; 
 684                         case kCMSSignerInvalidIndex
: reason
="kCMSSignerInvalidIndex"; break; 
 685                         default: reason
="unknown"; break; 
 687                 Security::Syslog::error("CMSDecoderCopySignerStatus failed with %s error (%d)", 
 688                                                                 reason
, (int)status
); 
 689                 MacOSError::throwMe(errSecCSSignatureFailed
); 
 692         // retrieve auxiliary data bag and verify against current state 
 693         CFRef
<CFDataRef
> hashBag
; 
 694         switch (OSStatus rc 
= CMSDecoderCopySignerAppleCodesigningHashAgility(cms
, 0, &hashBag
.aref())) { 
 697                         CFRef
<CFDictionaryRef
> hashDict 
= makeCFDictionaryFrom(hashBag
); 
 698                         CFArrayRef cdList 
= CFArrayRef(CFDictionaryGetValue(hashDict
, CFSTR("cdhashes"))); 
 699                         CFArrayRef myCdList 
= this->cdHashes(); 
 700                         if (cdList 
== NULL 
|| !CFEqual(cdList
, myCdList
)) 
 701                                 MacOSError::throwMe(errSecCSSignatureFailed
); 
 704         case -1:        /* CMS used to return this for "no attribute found", so tolerate it. Now returning noErr/NULL */ 
 707                 MacOSError::throwMe(rc
); 
 710         // internal signing time (as specified by the signer; optional) 
 711         mSigningTime 
= 0;       // "not present" marker (nobody could code sign on Jan 1, 2001 :-) 
 712         switch (OSStatus rc 
= CMSDecoderCopySignerSigningTime(cms
, 0, &mSigningTime
)) { 
 714         case errSecSigningTimeMissing
: 
 717                 Security::Syslog::error("Could not get signing time (error %d)", (int)rc
); 
 718                 MacOSError::throwMe(rc
); 
 721         // certified signing time (as specified by a TSA; optional) 
 722         mSigningTimestamp 
= 0; 
 723         switch (OSStatus rc 
= CMSDecoderCopySignerTimestampWithPolicy(cms
, ts_policies
, 0, &mSigningTimestamp
)) { 
 725         case errSecTimestampMissing
: 
 728                 Security::Syslog::error("Could not get timestamp (error %d)", (int)rc
); 
 729                 MacOSError::throwMe(rc
); 
 732         // set up the environment for SecTrust 
 733     if (mValidationFlags 
& kSecCSNoNetworkAccess
) { 
 734         MacOSError::check(SecTrustSetNetworkFetchAllowed(mTrust
,false)); // no network? 
 736     MacOSError::check(SecTrustSetKeychainsAllowed(mTrust
, false)); 
 738         CSSM_APPLE_TP_ACTION_DATA actionData 
= { 
 739                 CSSM_APPLE_TP_ACTION_VERSION
,   // version of data structure 
 743         if (!(mValidationFlags 
& kSecCSCheckTrustedAnchors
)) { 
 744                 /* no need to evaluate anchor trust when building cert chain */ 
 745                 MacOSError::check(SecTrustSetAnchorCertificates(mTrust
, cfEmptyArray())); // no anchors 
 746                 actionData
.ActionFlags 
|= CSSM_TP_ACTION_IMPLICIT_ANCHORS
;      // action flags 
 749         for (;;) {      // at most twice 
 750                 MacOSError::check(SecTrustSetParameters(mTrust
, 
 751                         CSSM_TP_ACTION_DEFAULT
, CFTempData(&actionData
, sizeof(actionData
)))); 
 753                 // evaluate trust and extract results 
 754                 SecTrustResultType trustResult
; 
 755                 MacOSError::check(SecTrustEvaluate(mTrust
, &trustResult
)); 
 756                 MacOSError::check(SecTrustGetResult(mTrust
, &trustResult
, &mCertChain
.aref(), &mEvalDetails
)); 
 758                 // if this is an Apple developer cert.... 
 759                 if (teamID() && SecStaticCode::isAppleDeveloperCert(mCertChain
)) { 
 760                         CFRef
<CFStringRef
> teamIDFromCert
; 
 761                         if (CFArrayGetCount(mCertChain
) > 0) { 
 762                                 /* Note that SecCertificateCopySubjectComponent sets the out parameter to NULL if there is no field present */ 
 763                                 MacOSError::check(SecCertificateCopySubjectComponent((SecCertificateRef
)CFArrayGetValueAtIndex(mCertChain
, Requirement::leafCert
), 
 764                                                                                                                                          &CSSMOID_OrganizationalUnitName
, 
 765                                                                                                                                          &teamIDFromCert
.aref())); 
 767                                 if (teamIDFromCert
) { 
 768                                         CFRef
<CFStringRef
> teamIDFromCD 
= CFStringCreateWithCString(NULL
, teamID(), kCFStringEncodingUTF8
); 
 770                                                 Security::Syslog::error("Could not get team identifier (%s)", teamID()); 
 771                                                 MacOSError::throwMe(errSecCSInvalidTeamIdentifier
); 
 774                                         if (CFStringCompare(teamIDFromCert
, teamIDFromCD
, 0) != kCFCompareEqualTo
) { 
 775                                                 Security::Syslog::error("Team identifier in the signing certificate (%s) does not match the team identifier (%s) in the code directory", 
 776                                                                                                 cfString(teamIDFromCert
).c_str(), teamID()); 
 777                                                 MacOSError::throwMe(errSecCSBadTeamIdentifier
); 
 783                 CODESIGN_EVAL_STATIC_SIGNATURE_RESULT(this, trustResult
, mCertChain 
? (int)CFArrayGetCount(mCertChain
) : 0); 
 784                 switch (trustResult
) { 
 785                 case kSecTrustResultProceed
: 
 786                 case kSecTrustResultUnspecified
: 
 788                 case kSecTrustResultDeny
: 
 789                         MacOSError::throwMe(CSSMERR_APPLETP_TRUST_SETTING_DENY
);        // user reject 
 790                 case kSecTrustResultInvalid
: 
 791                         assert(false);          // should never happen 
 792                         MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED
); 
 796                                 MacOSError::check(SecTrustGetCssmResultCode(mTrust
, &result
)); 
 797                                 // if we have a valid timestamp, CMS validates against (that) signing time and all is well. 
 798                                 // If we don't have one, may validate against *now*, and must be able to tolerate expiration. 
 799                                 if (mSigningTimestamp 
== 0) { // no timestamp available 
 800                                         if (((result 
== CSSMERR_TP_CERT_EXPIRED
) || (result 
== CSSMERR_TP_CERT_NOT_VALID_YET
)) 
 801                                                         && !(actionData
.ActionFlags 
& CSSM_TP_ACTION_ALLOW_EXPIRED
)) { 
 802                                                 CODESIGN_EVAL_STATIC_SIGNATURE_EXPIRED(this); 
 803                                                 actionData
.ActionFlags 
|= CSSM_TP_ACTION_ALLOW_EXPIRED
; // (this also allows postdated certs) 
 804                                                 continue;               // retry validation while tolerating expiration 
 807                                 Security::Syslog::error("SecStaticCode: verification failed (trust result %d, error %d)", trustResult
, (int)result
); 
 808                                 MacOSError::throwMe(result
); 
 812                 if (mSigningTimestamp
) { 
 813                         CFIndex rootix 
= CFArrayGetCount(mCertChain
); 
 814                         if (SecCertificateRef mainRoot 
= SecCertificateRef(CFArrayGetValueAtIndex(mCertChain
, rootix
-1))) 
 815                                 if (isAppleCA(mainRoot
)) { 
 816                                         // impose policy: if the signature itself draws to Apple, then so must the timestamp signature 
 817                                         CFRef
<CFArrayRef
> tsCerts
; 
 818                                         OSStatus result 
= CMSDecoderCopySignerTimestampCertificates(cms
, 0, &tsCerts
.aref()); 
 820                                                 Security::Syslog::error("SecStaticCode: could not get timestamp certificates (error %d)", (int)result
); 
 821                                                 MacOSError::check(result
); 
 823                                         CFIndex tsn 
= CFArrayGetCount(tsCerts
); 
 824                                         bool good 
= tsn 
> 0 && isAppleCA(SecCertificateRef(CFArrayGetValueAtIndex(tsCerts
, tsn
-1))); 
 826                                                 result 
= CSSMERR_TP_NOT_TRUSTED
; 
 827                                                 Security::Syslog::error("SecStaticCode: timestamp policy verification failed (error %d)", (int)result
); 
 828                                                 MacOSError::throwMe(result
); 
 833                 return actionData
.ActionFlags 
& CSSM_TP_ACTION_ALLOW_EXPIRED
; 
 836     // Do some pre-verification initialization 
 837     CFDataRef sig 
= this->signature(); 
 838     this->codeDirectory();      // load CodeDirectory (sets mDir) 
 839     mSigningTime 
= 0;   // "not present" marker (nobody could code sign on Jan 1, 2001 :-) 
 841     CFRef
<CFDictionaryRef
> attrs
; 
 842         CFRef
<CFArrayRef
> vf_policies(createVerificationPolicies()); 
 844     // Verify the CMS signature against mBaseDir (SHA1) 
 845     MacOSError::check(SecCMSVerifyCopyDataAndAttributes(sig
, mBaseDir
, vf_policies
, &mTrust
.aref(), NULL
, &attrs
.aref())); 
 847     // Copy the signing time 
 848     mSigningTime 
= SecTrustGetVerifyTime(mTrust
); 
 850     // Validate the cert chain 
 851     SecTrustResultType trustResult
; 
 852     MacOSError::check(SecTrustEvaluate(mTrust
, &trustResult
)); 
 854     // retrieve auxiliary data bag and verify against current state 
 855     CFRef
<CFDataRef
> hashBag
; 
 856     hashBag 
= CFDataRef(CFDictionaryGetValue(attrs
, kSecCMSHashAgility
)); 
 858         CFRef
<CFDictionaryRef
> hashDict 
= makeCFDictionaryFrom(hashBag
); 
 859         CFArrayRef cdList 
= CFArrayRef(CFDictionaryGetValue(hashDict
, CFSTR("cdhashes"))); 
 860         CFArrayRef myCdList 
= this->cdHashes(); 
 861         if (cdList 
== NULL 
|| !CFEqual(cdList
, myCdList
)) 
 862             MacOSError::throwMe(errSecCSSignatureFailed
); 
 866      * Populate mCertChain with the certs.  If we failed validation, the 
 867      * signer's cert will be checked installed provisioning profiles as an 
 868      * alternative to verification against the policy for store-signed binaries 
 870     SecCertificateRef leafCert 
= SecTrustGetCertificateAtIndex(mTrust
, 0); 
 871     if (leafCert 
!= NULL
) { 
 872         CFIndex count 
= SecTrustGetCertificateCount(mTrust
); 
 874         CFMutableArrayRef certs 
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, 
 875                                                        &kCFTypeArrayCallBacks
); 
 877         CFArrayAppendValue(certs
, leafCert
); 
 878         for (CFIndex i 
= 1; i 
< count
; ++i
) { 
 879             CFArrayAppendValue(certs
, SecTrustGetCertificateAtIndex(mTrust
, i
)); 
 882         mCertChain
.take((CFArrayRef
)certs
); 
 885     // Did we implicitly trust the signer? 
 886     mTrustedSigningCertChain 
= (trustResult 
== kSecTrustResultUnspecified
); 
 888     return false; // XXX: Not checking for expired certs 
 894 // Return the TP policy used for signature verification. 
 895 // This may be a simple SecPolicyRef or a CFArray of policies. 
 896 // The caller owns the return value. 
 898 static SecPolicyRef 
makeRevocationPolicy(CFOptionFlags flags
) 
 900         CFRef
<SecPolicyRef
> policy(SecPolicyCreateRevocation(flags
)); 
 901         return policy
.yield(); 
 905 CFArrayRef 
SecStaticCode::createVerificationPolicies() 
 907         if (mValidationFlags 
& kSecCSUseSoftwareSigningCert
) { 
 908                 CFRef
<SecPolicyRef
> ssRef 
= SecPolicyCreateAppleSoftwareSigning(); 
 909                 return makeCFArray(1, ssRef
.get()); 
 912         CFRef
<SecPolicyRef
> core
; 
 913         MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3
, 
 914                                                                         &CSSMOID_APPLE_TP_CODE_SIGNING
, &core
.aref())); 
 915         if (mValidationFlags 
& kSecCSNoNetworkAccess
) { 
 916                 // Skips all revocation since they require network connectivity 
 917                 // therefore annihilates kSecCSEnforceRevocationChecks if present 
 918                 CFRef
<SecPolicyRef
> no_revoc 
= makeRevocationPolicy(kSecRevocationNetworkAccessDisabled
); 
 919                 return makeCFArray(2, core
.get(), no_revoc
.get()); 
 921         else if (mValidationFlags 
& kSecCSEnforceRevocationChecks
) { 
 922                 // Add CRL and OCSP policies 
 923                 CFRef
<SecPolicyRef
> revoc 
= makeRevocationPolicy(kSecRevocationUseAnyAvailableMethod
); 
 924                 return makeCFArray(2, core
.get(), revoc
.get()); 
 926                 return makeCFArray(1, core
.get()); 
 929         CFRef
<SecPolicyRef
> tvOSRef 
= SecPolicyCreateAppleTVOSApplicationSigning(); 
 930         return makeCFArray(1, tvOSRef
.get()); 
 932         CFRef
<SecPolicyRef
> iOSRef 
= SecPolicyCreateiPhoneApplicationSigning(); 
 933         return makeCFArray(1, iOSRef
.get()); 
 938 CFArrayRef 
SecStaticCode::createTimeStampingAndRevocationPolicies() 
 940         CFRef
<SecPolicyRef
> tsPolicy 
= SecPolicyCreateAppleTimeStamping(); 
 942         if (mValidationFlags 
& kSecCSNoNetworkAccess
) { 
 943                 // Skips all revocation since they require network connectivity 
 944                 // therefore annihilates kSecCSEnforceRevocationChecks if present 
 945                 CFRef
<SecPolicyRef
> no_revoc 
= makeRevocationPolicy(kSecRevocationNetworkAccessDisabled
); 
 946                 return makeCFArray(2, tsPolicy
.get(), no_revoc
.get()); 
 948         else if (mValidationFlags 
& kSecCSEnforceRevocationChecks
) { 
 949                 // Add CRL and OCSP policies 
 950                 CFRef
<SecPolicyRef
> revoc 
= makeRevocationPolicy(kSecRevocationUseAnyAvailableMethod
); 
 951                 return makeCFArray(2, tsPolicy
.get(), revoc
.get()); 
 954                 return makeCFArray(1, tsPolicy
.get()); 
 957         return makeCFArray(1, tsPolicy
.get()); 
 964 // Validate a particular sealed, cached resource against its (special) CodeDirectory slot. 
 965 // The resource must already have been placed in the cache. 
 966 // This does NOT perform basic validation. 
 968 void SecStaticCode::validateComponent(CodeDirectory::SpecialSlot slot
, OSStatus fail 
/* = errSecCSSignatureFailed */) 
 970         assert(slot 
<= cdSlotMax
); 
 971         CFDataRef data 
= mCache
[slot
]; 
 972         assert(data
);           // must be cached 
 973         if (data 
== CFDataRef(kCFNull
)) { 
 974                 if (codeDirectory()->slotIsPresent(-slot
)) // was supposed to be there... 
 975                                 MacOSError::throwMe(fail
);      // ... and is missing 
 977                 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data
), CFDataGetLength(data
), -slot
)) 
 978                         MacOSError::throwMe(fail
); 
 984 // Perform static validation of the main executable. 
 985 // This reads the main executable from disk and validates it against the 
 986 // CodeDirectory code slot array. 
 987 // Note that this is NOT an in-memory validation, and is thus potentially 
 988 // subject to timing attacks. 
 990 void SecStaticCode::validateExecutable() 
 992         if (!validatedExecutable()) { 
 994                         DTRACK(CODESIGN_EVAL_STATIC_EXECUTABLE
, this, 
 995                                 (char*)this->mainExecutablePath().c_str(), codeDirectory()->nCodeSlots
); 
 996                         const CodeDirectory 
*cd 
= this->codeDirectory(); 
 998                                 MacOSError::throwMe(errSecCSUnsigned
); 
 999                         AutoFileDesc 
fd(mainExecutablePath(), O_RDONLY
); 
1000                         fd
.fcntl(F_NOCACHE
, true);              // turn off page caching (one-pass) 
1001                         if (Universal 
*fat 
= mRep
->mainExecutableImage()) 
1002                                 fd
.seek(fat
->archOffset()); 
1003                         size_t pageSize 
= cd
->pageSize 
? (1 << cd
->pageSize
) : 0; 
1004                         size_t remaining 
= cd
->signingLimit(); 
1005                         for (uint32_t slot 
= 0; slot 
< cd
->nCodeSlots
; ++slot
) { 
1006                                 size_t thisPage 
= remaining
; 
1008                                         thisPage 
= min(thisPage
, pageSize
); 
1009                                 __block 
bool good 
= true; 
1010                                 CodeDirectory::multipleHashFileData(fd
, thisPage
, hashAlgorithms(), ^(CodeDirectory::HashAlgorithm type
, Security::DynamicHash 
*hasher
) { 
1011                                         const CodeDirectory
* cd 
= (const CodeDirectory
*)CFDataGetBytePtr(mCodeDirectories
[type
]); 
1012                                         if (!hasher
->verify((*cd
)[slot
])) 
1016                                         CODESIGN_EVAL_STATIC_EXECUTABLE_FAIL(this, (int)slot
); 
1017                                         MacOSError::throwMe(errSecCSSignatureFailed
); 
1019                                 remaining 
-= thisPage
; 
1021                         assert(remaining 
== 0); 
1022                         mExecutableValidated 
= true; 
1023                         mExecutableValidResult 
= errSecSuccess
; 
1024                 } catch (const CommonError 
&err
) { 
1025                         mExecutableValidated 
= true; 
1026                         mExecutableValidResult 
= err
.osStatus(); 
1029                         secinfo("staticCode", "%p executable validation threw non-common exception", this); 
1030                         mExecutableValidated 
= true; 
1031                         mExecutableValidResult 
= errSecCSInternalError
; 
1032                         Syslog::notice("code signing internal problem: unknown exception thrown by validation"); 
1036         assert(validatedExecutable()); 
1037         if (mExecutableValidResult 
!= errSecSuccess
) 
1038                 MacOSError::throwMe(mExecutableValidResult
); 
1043 // Perform static validation of sealed resources and nested code. 
1045 // This performs a whole-code static resource scan and effectively 
1046 // computes a concordance between what's on disk and what's in the ResourceDirectory. 
1047 // Any unsanctioned difference causes an error. 
1049 unsigned SecStaticCode::estimateResourceWorkload() 
1051         // workload estimate = number of sealed files 
1052         CFDictionaryRef sealedResources 
= resourceDictionary(); 
1053         CFDictionaryRef files 
= cfget
<CFDictionaryRef
>(sealedResources
, "files2"); 
1055                 files 
= cfget
<CFDictionaryRef
>(sealedResources
, "files"); 
1056         return files 
? unsigned(CFDictionaryGetCount(files
)) : 0; 
1059 void SecStaticCode::validateResources(SecCSFlags flags
) 
1061         // do we have a superset of this requested validation cached? 
1063         if (mResourcesValidated
) {      // have cached outcome 
1064                 if (!(flags 
& kSecCSCheckNestedCode
) || mResourcesDeep
) // was deep or need no deep scan 
1069                 if (mLimitedAsync 
== NULL
) { 
1070                         mLimitedAsync 
= new LimitedAsync(diskRep()->fd().mediumType() == kIOPropertyMediumTypeSolidStateKey
); 
1074                         CFDictionaryRef rules
; 
1075                         CFDictionaryRef files
; 
1077                         if (!loadResources(rules
, files
, version
)) 
1078                                 return;         // validly no resources; nothing to do (ok) 
1080                         // found resources, and they are sealed 
1081                         DTRACK(CODESIGN_EVAL_STATIC_RESOURCES
, this, 
1082                                 (char*)this->mainExecutablePath().c_str(), 0); 
1084                         // scan through the resources on disk, checking each against the resourceDirectory 
1085                         mResourcesValidContext 
= new CollectingContext(*this);          // collect all failures in here 
1087                         // check for weak resource rules 
1088                         bool strict 
= flags 
& kSecCSStrictValidate
; 
1090                                 if (hasWeakResourceRules(rules
, version
, mAllowOmissions
)) 
1091                                         if (mTolerateErrors
.find(errSecCSWeakResourceRules
) == mTolerateErrors
.end()) 
1092                                                 MacOSError::throwMe(errSecCSWeakResourceRules
); 
1094                                         if (mTolerateErrors
.find(errSecCSWeakResourceEnvelope
) == mTolerateErrors
.end()) 
1095                                                 MacOSError::throwMe(errSecCSWeakResourceEnvelope
); 
1098                         Dispatch::Group group
; 
1099                         Dispatch::Group 
&groupRef 
= group
;  // (into block) 
1101                         // scan through the resources on disk, checking each against the resourceDirectory 
1102                         __block CFRef
<CFMutableDictionaryRef
> resourceMap 
= makeCFMutableDictionary(files
); 
1103                         string base 
= cfString(this->resourceBase()); 
1104                         ResourceBuilder 
resources(base
, base
, rules
, strict
, mTolerateErrors
); 
1105                         this->mResourceScope 
= &resources
; 
1106                         diskRep()->adjustResources(resources
); 
1108                         resources
.scan(^(FTSENT 
*ent
, uint32_t ruleFlags
, const string relpath
, ResourceBuilder::Rule 
*rule
) { 
1109                                 CFDictionaryRemoveValue(resourceMap
, CFTempString(relpath
)); 
1110                                 bool isSymlink 
= (ent
->fts_info 
== FTS_SL
); 
1112                                 void (^validate
)() = ^{ 
1113                                         validateResource(files
, relpath
, isSymlink
, *mResourcesValidContext
, flags
, version
); 
1117                                 mLimitedAsync
->perform(groupRef
, validate
); 
1119                         group
.wait();   // wait until all async resources have been validated as well 
1121                         unsigned leftovers 
= unsigned(CFDictionaryGetCount(resourceMap
)); 
1122                         if (leftovers 
> 0) { 
1123                                 secinfo("staticCode", "%d sealed resource(s) not found in code", int(leftovers
)); 
1124                                 CFDictionaryApplyFunction(resourceMap
, SecStaticCode::checkOptionalResource
, mResourcesValidContext
); 
1127                         // now check for any errors found in the reporting context 
1128                         mResourcesValidated 
= true; 
1129                         mResourcesDeep 
= flags 
& kSecCSCheckNestedCode
; 
1130                         if (mResourcesValidContext
->osStatus() != errSecSuccess
) 
1131                                 mResourcesValidContext
->throwMe(); 
1132                 } catch (const CommonError 
&err
) { 
1133                         mResourcesValidated 
= true; 
1134                         mResourcesDeep 
= flags 
& kSecCSCheckNestedCode
; 
1135                         mResourcesValidResult 
= err
.osStatus(); 
1138                         secinfo("staticCode", "%p executable validation threw non-common exception", this); 
1139                         mResourcesValidated 
= true; 
1140                         mResourcesDeep 
= flags 
& kSecCSCheckNestedCode
; 
1141                         mResourcesValidResult 
= errSecCSInternalError
; 
1142                         Syslog::notice("code signing internal problem: unknown exception thrown by validation"); 
1146         assert(validatedResources()); 
1147         if (mResourcesValidResult
) 
1148                 MacOSError::throwMe(mResourcesValidResult
); 
1149         if (mResourcesValidContext
->osStatus() != errSecSuccess
) 
1150                 mResourcesValidContext
->throwMe(); 
1154 bool SecStaticCode::loadResources(CFDictionaryRef
& rules
, CFDictionaryRef
& files
, uint32_t& version
) 
1157         CFDictionaryRef sealedResources 
= resourceDictionary(); 
1158         if (this->resourceBase()) {     // disk has resources 
1159                 if (sealedResources
) 
1160                         /* go to work below */; 
1162                         MacOSError::throwMe(errSecCSResourcesNotFound
); 
1163         } else {                                                        // disk has no resources 
1164                 if (sealedResources
) 
1165                         MacOSError::throwMe(errSecCSResourcesNotFound
); 
1167                         return false;                                   // no resources, not sealed - fine (no work) 
1170         // use V2 resource seal if available, otherwise fall back to V1 
1171         if (CFDictionaryGetValue(sealedResources
, CFSTR("files2"))) {   // have V2 signature 
1172                 rules 
= cfget
<CFDictionaryRef
>(sealedResources
, "rules2"); 
1173                 files 
= cfget
<CFDictionaryRef
>(sealedResources
, "files2"); 
1175         } else {        // only V1 available 
1176                 rules 
= cfget
<CFDictionaryRef
>(sealedResources
, "rules"); 
1177                 files 
= cfget
<CFDictionaryRef
>(sealedResources
, "files"); 
1180         if (!rules 
|| !files
) 
1181                 MacOSError::throwMe(errSecCSResourcesInvalid
); 
1186 void SecStaticCode::checkOptionalResource(CFTypeRef key
, CFTypeRef value
, void *context
) 
1188         ValidationContext 
*ctx 
= static_cast<ValidationContext 
*>(context
); 
1189         ResourceSeal 
seal(value
); 
1190         if (!seal
.optional()) { 
1191                 if (key 
&& CFGetTypeID(key
) == CFStringGetTypeID()) { 
1192                         CFTempURL 
tempURL(CFStringRef(key
), false, ctx
->code
.resourceBase()); 
1193                         if (!tempURL
.get()) { 
1194                                 ctx
->reportProblem(errSecCSBadDictionaryFormat
, kSecCFErrorResourceSeal
, key
); 
1196                                 ctx
->reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
, tempURL
); 
1199                         ctx
->reportProblem(errSecCSBadResource
, kSecCFErrorResourceSeal
, key
); 
1205 static bool isOmitRule(CFTypeRef value
) 
1207         if (CFGetTypeID(value
) == CFBooleanGetTypeID()) 
1208                 return value 
== kCFBooleanFalse
; 
1209         CFDictionary 
rule(value
, errSecCSResourceRulesInvalid
); 
1210         return rule
.get
<CFBooleanRef
>("omit") == kCFBooleanTrue
; 
1213 bool SecStaticCode::hasWeakResourceRules(CFDictionaryRef rulesDict
, uint32_t version
, CFArrayRef allowedOmissions
) 
1215         // compute allowed omissions 
1216         CFRef
<CFArrayRef
> defaultOmissions 
= this->diskRep()->allowedResourceOmissions(); 
1217         if (!defaultOmissions
) { 
1218                 Syslog::notice("code signing internal problem: diskRep returned no allowedResourceOmissions"); 
1219                 MacOSError::throwMe(errSecCSInternalError
); 
1221         CFRef
<CFMutableArrayRef
> allowed 
= CFArrayCreateMutableCopy(NULL
, 0, defaultOmissions
); 
1222         if (allowedOmissions
) 
1223                 CFArrayAppendArray(allowed
, allowedOmissions
, CFRangeMake(0, CFArrayGetCount(allowedOmissions
))); 
1224         CFRange range 
= CFRangeMake(0, CFArrayGetCount(allowed
)); 
1226         // check all resource rules for weakness 
1227         string catchAllRule 
= (version 
== 1) ? "^Resources/" : "^.*"; 
1228         __block 
bool coversAll 
= false; 
1229         __block 
bool forbiddenOmission 
= false; 
1230         CFArrayRef allowedRef 
= allowed
.get();  // (into block) 
1231         CFDictionary 
rules(rulesDict
, errSecCSResourceRulesInvalid
); 
1232         rules
.apply(^(CFStringRef key
, CFTypeRef value
) { 
1233                 string pattern 
= cfString(key
, errSecCSResourceRulesInvalid
); 
1234                 if (pattern 
== catchAllRule 
&& value 
== kCFBooleanTrue
) { 
1238                 if (isOmitRule(value
)) 
1239                         forbiddenOmission 
|= !CFArrayContainsValue(allowedRef
, range
, key
); 
1242         return !coversAll 
|| forbiddenOmission
; 
1247 // Load, validate, cache, and return CFDictionary forms of sealed resources. 
1249 CFDictionaryRef 
SecStaticCode::infoDictionary() 
1252                 mInfoDict
.take(getDictionary(cdInfoSlot
, errSecCSInfoPlistFailed
)); 
1253                 secinfo("staticCode", "%p loaded InfoDict %p", this, mInfoDict
.get()); 
1258 CFDictionaryRef 
SecStaticCode::entitlements() 
1260         if (!mEntitlements
) { 
1261                 validateDirectory(); 
1262                 if (CFDataRef entitlementData 
= component(cdEntitlementSlot
)) { 
1263                         validateComponent(cdEntitlementSlot
); 
1264                         const EntitlementBlob 
*blob 
= reinterpret_cast<const EntitlementBlob 
*>(CFDataGetBytePtr(entitlementData
)); 
1265                         if (blob
->validateBlob()) { 
1266                                 mEntitlements
.take(blob
->entitlements()); 
1267                                 secinfo("staticCode", "%p loaded Entitlements %p", this, mEntitlements
.get()); 
1269                         // we do not consider a different blob type to be an error. We think it's a new format we don't understand 
1272         return mEntitlements
; 
1275 CFDictionaryRef 
SecStaticCode::resourceDictionary(bool check 
/* = true */) 
1277         if (mResourceDict
)      // cached 
1278                 return mResourceDict
; 
1279         if (CFRef
<CFDictionaryRef
> dict 
= getDictionary(cdResourceDirSlot
, check
)) 
1280                 if (cfscan(dict
, "{rules=%Dn,files=%Dn}")) { 
1281                         secinfo("staticCode", "%p loaded ResourceDict %p", 
1282                                 this, mResourceDict
.get()); 
1283                         return mResourceDict 
= dict
; 
1290 CFDataRef 
SecStaticCode::copyComponent(CodeDirectory::SpecialSlot slot
, CFDataRef hash
) 
1292         const CodeDirectory
* cd 
= this->codeDirectory(); 
1293         if (CFCopyRef
<CFDataRef
> component 
= this->component(slot
)) { 
1295                         const void *slotHash 
= (*cd
)[slot
]; 
1296                         if (cd
->hashSize 
!= CFDataGetLength(hash
) || 0 != memcmp(slotHash
, CFDataGetBytePtr(hash
), cd
->hashSize
)) { 
1297                                 Syslog::notice("copyComponent hash mismatch slot %d length %d", slot
, int(CFDataGetLength(hash
))); 
1298                                 return NULL
;    // mismatch 
1301                 return component
.yield(); 
1309 // Load and cache the resource directory base. 
1310 // Note that the base is optional for each DiskRep. 
1312 CFURLRef 
SecStaticCode::resourceBase() 
1314         if (!mGotResourceBase
) { 
1315                 string base 
= mRep
->resourcesRootPath(); 
1317                         mResourceBase
.take(makeCFURL(base
, true)); 
1318                 mGotResourceBase 
= true; 
1320         return mResourceBase
; 
1325 // Load a component, validate it, convert it to a CFDictionary, and return that. 
1326 // This will force load and validation, which means that it will perform basic 
1327 // validation if it hasn't been done yet. 
1329 CFDictionaryRef 
SecStaticCode::getDictionary(CodeDirectory::SpecialSlot slot
, bool check 
/* = true */) 
1332                 validateDirectory(); 
1333         if (CFDataRef infoData 
= component(slot
)) { 
1334                 validateComponent(slot
); 
1335                 if (CFDictionaryRef dict 
= makeCFDictionaryFrom(infoData
)) 
1338                         MacOSError::throwMe(errSecCSBadDictionaryFormat
); 
1346 CFDictionaryRef 
SecStaticCode::diskRepInformation() 
1348         return mRep
->diskRepInformation(); 
1351 bool SecStaticCode::checkfix30814861(string path
, bool addition
) { 
1352         // <rdar://problem/30814861> v2 resource rules don't match v1 resource rules 
1354         //// Condition 1: Is the app an iOS app that was built with an SDK lower than 9.0? 
1356         // We started signing correctly in 2014, 9.0 was first seeded mid-2016. 
1358         CFRef
<CFDictionaryRef
> inf 
= diskRepInformation(); 
1360                 CFDictionary 
info(diskRepInformation(), errSecCSNotSupported
); 
1361                 uint32_t platformCmd 
= 
1362                         cfNumber(info
.get
<CFNumberRef
>(kSecCodeInfoDiskRepOSPlatform
, errSecCSNotSupported
), 0); 
1363                 uint32_t sdkVersion 
= 
1364                         cfNumber(info
.get
<CFNumberRef
>(kSecCodeInfoDiskRepOSSDKVersion
, errSecCSNotSupported
), 0); 
1366                 if (platformCmd 
!= LC_VERSION_MIN_IPHONEOS 
|| sdkVersion 
>= 0x00090000) { 
1369         } catch (const MacOSError 
&error
) { 
1373         //// Condition 2: Is it a .sinf/.supf/.supp file at the right location? 
1375         static regex_t pathre_sinf
; 
1376         static regex_t pathre_supp_supf
; 
1377         static dispatch_once_t once
; 
1379         dispatch_once(&once
, ^{ 
1380                 os_assert_zero(regcomp(&pathre_sinf
, 
1381                                                            "^(Frameworks/[^/]+\\.framework/|PlugIns/[^/]+\\.appex/|())SC_Info/[^/]+\\.sinf$", 
1382                                                            REG_EXTENDED 
| REG_NOSUB
)); 
1383                 os_assert_zero(regcomp(&pathre_supp_supf
, 
1384                                                            "^(Frameworks/[^/]+\\.framework/|PlugIns/[^/]+\\.appex/|())SC_Info/[^/]+\\.(supf|supp)$", 
1385                                                            REG_EXTENDED 
| REG_NOSUB
)); 
1388         // .sinf is added, .supf/.supp are modified. 
1389         const regex_t 
&pathre 
= addition 
? pathre_sinf 
: pathre_supp_supf
; 
1391         const int result 
= regexec(&pathre
, path
.c_str(), 0, NULL
, 0); 
1393         if (result 
== REG_NOMATCH
) { 
1395         } else if (result 
!= 0) { 
1397                 secerror("unexpected regexec result %d for path '%s'", result
, path
.c_str()); 
1401         //// Condition 3: Do the v1 rules actually exclude the file? 
1403         dispatch_once(&mCheckfix30814861builder1_once
, ^{ 
1404                 // Create the v1 resource builder lazily. 
1405                 CFDictionaryRef rules1 
= cfget
<CFDictionaryRef
>(resourceDictionary(), "rules"); 
1406                 const string base 
= cfString(resourceBase()); 
1408                 mCheckfix30814861builder1 
= new ResourceBuilder(base
, base
, rules1
, false, mTolerateErrors
); 
1411         ResourceBuilder::Rule 
const * const matchingRule 
= mCheckfix30814861builder1
->findRule(path
); 
1413         if (matchingRule 
== NULL 
|| !(matchingRule
->flags 
& ResourceBuilder::omitted
)) { 
1417         //// All matched, this file is a check-fixed sinf/supf/supp. 
1423 void SecStaticCode::validateResource(CFDictionaryRef files
, string path
, bool isSymlink
, ValidationContext 
&ctx
, SecCSFlags flags
, uint32_t version
) 
1425         if (!resourceBase())    // no resources in DiskRep 
1426                 MacOSError::throwMe(errSecCSResourcesNotFound
); 
1427         CFRef
<CFURLRef
> fullpath 
= makeCFURL(path
, false, resourceBase()); 
1428         if (version 
> 1 && ((flags 
& (kSecCSStrictValidate
|kSecCSRestrictSidebandData
)) == (kSecCSStrictValidate
|kSecCSRestrictSidebandData
))) { 
1429                 AutoFileDesc 
fd(cfString(fullpath
)); 
1430                 if (fd
.hasExtendedAttribute(XATTR_RESOURCEFORK_NAME
) || fd
.hasExtendedAttribute(XATTR_FINDERINFO_NAME
)) 
1431                         ctx
.reportProblem(errSecCSInvalidAssociatedFileData
, kSecCFErrorResourceSideband
, fullpath
); 
1433         if (CFTypeRef file 
= CFDictionaryGetValue(files
, CFTempString(path
))) { 
1434                 ResourceSeal 
seal(file
); 
1435                 const ResourceSeal
& rseal 
= seal
; 
1436                 if (seal
.nested()) { 
1438                                 return ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAltered
, fullpath
); // changed type 
1439                         string suffix 
= ".framework"; 
1440                         bool isFramework 
= (path
.length() > suffix
.length()) 
1441                                 && (path
.compare(path
.length()-suffix
.length(), suffix
.length(), suffix
) == 0); 
1442                         validateNestedCode(fullpath
, seal
, flags
, isFramework
); 
1443                 } else if (seal
.link()) { 
1445                                 return ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAltered
, fullpath
); // changed type 
1446                         validateSymlinkResource(cfString(fullpath
), cfString(seal
.link()), ctx
, flags
); 
1447                 } else if (seal
.hash(hashAlgorithm())) {        // genuine file 
1449                                 return ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAltered
, fullpath
); // changed type 
1450                         AutoFileDesc 
fd(cfString(fullpath
), O_RDONLY
, FileDesc::modeMissingOk
); // open optional file 
1452                                 __block 
bool good 
= true; 
1453                                 CodeDirectory::multipleHashFileData(fd
, 0, hashAlgorithms(), ^(CodeDirectory::HashAlgorithm type
, Security::DynamicHash 
*hasher
) { 
1454                                         if (!hasher
->verify(rseal
.hash(type
))) 
1458                                         if (version 
== 2 && checkfix30814861(path
, false)) { 
1459                                                 secinfo("validateResource", "%s check-fixed (altered).", path
.c_str()); 
1461                                                 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAltered
, fullpath
); // altered 
1465                                 if (!seal
.optional()) 
1466                                         ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceMissing
, fullpath
); // was sealed but is now missing 
1468                                         return;                 // validly missing 
1471                         ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAltered
, fullpath
); // changed type 
1474         if (version 
== 1) {             // version 1 ignores symlinks altogether 
1475                 char target
[PATH_MAX
]; 
1476                 if (::readlink(cfString(fullpath
).c_str(), target
, sizeof(target
)) > 0) 
1479         if (version 
== 2 && checkfix30814861(path
, true)) { 
1480                 secinfo("validateResource", "%s check-fixed (added).", path
.c_str()); 
1482                 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAdded
, CFTempURL(path
, false, resourceBase())); 
1486 void SecStaticCode::validatePlainMemoryResource(string path
, CFDataRef fileData
, SecCSFlags flags
) 
1488         CFDictionaryRef rules
; 
1489         CFDictionaryRef files
; 
1491         if (!loadResources(rules
, files
, version
)) 
1492                 MacOSError::throwMe(errSecCSResourcesNotFound
);         // no resources sealed; this can't be right 
1493         if (CFTypeRef file 
= CFDictionaryGetValue(files
, CFTempString(path
))) { 
1494                 ResourceSeal 
seal(file
); 
1495                 const Byte 
*sealHash 
= seal
.hash(hashAlgorithm()); 
1497                         if (codeDirectory()->verifyMemoryContent(fileData
, sealHash
)) 
1501         MacOSError::throwMe(errSecCSBadResource
); 
1504 void SecStaticCode::validateSymlinkResource(std::string fullpath
, std::string seal
, ValidationContext 
&ctx
, SecCSFlags flags
) 
1506         static const char* const allowedDestinations
[] = { 
1511         char target
[PATH_MAX
]; 
1512         ssize_t len 
= ::readlink(fullpath
.c_str(), target
, sizeof(target
)-1); 
1514                 UnixError::check(-1); 
1516         std::string fulltarget 
= target
; 
1517         if (target
[0] != '/') { 
1518                 size_t lastSlash 
= fullpath
.rfind('/'); 
1519                 fulltarget 
= fullpath
.substr(0, lastSlash
) + '/' + target
; 
1521         if (seal 
!= target
) { 
1522                 ctx
.reportProblem(errSecCSBadResource
, kSecCFErrorResourceAltered
, CFTempString(fullpath
)); 
1525         if ((mValidationFlags 
& (kSecCSStrictValidate
|kSecCSRestrictSymlinks
)) == (kSecCSStrictValidate
|kSecCSRestrictSymlinks
)) { 
1526                 char resolved
[PATH_MAX
]; 
1527                 if (realpath(fulltarget
.c_str(), resolved
)) { 
1528                         assert(resolved
[0] == '/'); 
1529                         size_t rlen 
= strlen(resolved
); 
1530                         if (target
[0] == '/') { 
1531                                 // absolute symlink; only allow absolute links to system locations 
1532                                 for (const char* const* pathp 
= allowedDestinations
; *pathp
; pathp
++) { 
1533                                         size_t dlen 
= strlen(*pathp
); 
1534                                         if (rlen 
> dlen 
&& strncmp(resolved
, *pathp
, dlen
) == 0) 
1535                                         return;         // target inside /System, deemed okay 
1538                                 // everything else must be inside the bundle(s) 
1539                                 for (const SecStaticCode
* code 
= this; code
; code 
= code
->mOuterScope
) { 
1540                                         string root 
= code
->mResourceScope
->root(); 
1541                                         if (strncmp(resolved
, root
.c_str(), root
.size()) == 0) { 
1542                                                 if (code
->mResourceScope
->includes(resolved 
+ root
.length() + 1)) 
1543                                                         return;         // located in resource stack && included in envelope 
1545                                                         break;          // located but excluded from envelope (deny) 
1550                 // if we fell through, flag a symlink error 
1551                 if (mTolerateErrors
.find(errSecCSInvalidSymlink
) == mTolerateErrors
.end()) 
1552                         ctx
.reportProblem(errSecCSInvalidSymlink
, kSecCFErrorResourceAltered
, CFTempString(fullpath
)); 
1556 void SecStaticCode::validateNestedCode(CFURLRef path
, const ResourceSeal 
&seal
, SecCSFlags flags
, bool isFramework
) 
1558         CFRef
<SecRequirementRef
> req
; 
1559         if (SecRequirementCreateWithString(seal
.requirement(), kSecCSDefaultFlags
, &req
.aref())) 
1560                 MacOSError::throwMe(errSecCSResourcesInvalid
); 
1562         // recursively verify this nested code 
1564                 if (!(flags 
& kSecCSCheckNestedCode
)) 
1565                         flags 
|= kSecCSBasicValidateOnly 
| kSecCSQuickCheck
; 
1566                 SecPointer
<SecStaticCode
> code 
= new SecStaticCode(DiskRep::bestGuess(cfString(path
))); 
1567                 code
->initializeFromParent(*this); 
1568                 code
->staticValidate(flags 
& (~kSecCSRestrictToAppLike
), SecRequirement::required(req
)); 
1570                 if (isFramework 
&& (flags 
& kSecCSStrictValidate
)) 
1572                                 validateOtherVersions(path
, flags 
& (~kSecCSRestrictToAppLike
), req
, code
); 
1573                         } catch (const CSError 
&err
) { 
1574                                 MacOSError::throwMe(errSecCSBadFrameworkVersion
); 
1575                         } catch (const MacOSError 
&err
) { 
1576                                 MacOSError::throwMe(errSecCSBadFrameworkVersion
); 
1579         } catch (CSError 
&err
) { 
1580                 if (err
.error 
== errSecCSReqFailed
) { 
1581                         mResourcesValidContext
->reportProblem(errSecCSBadNestedCode
, kSecCFErrorResourceAltered
, path
); 
1584                 err
.augment(kSecCFErrorPath
, path
); 
1586         } catch (const MacOSError 
&err
) { 
1587                 if (err
.error 
== errSecCSReqFailed
) { 
1588                         mResourcesValidContext
->reportProblem(errSecCSBadNestedCode
, kSecCFErrorResourceAltered
, path
); 
1591                 CSError::throwMe(err
.error
, kSecCFErrorPath
, path
); 
1595 void SecStaticCode::validateOtherVersions(CFURLRef path
, SecCSFlags flags
, SecRequirementRef req
, SecStaticCode 
*code
) 
1597         // Find out what current points to and do not revalidate 
1598         std::string mainPath 
= cfStringRelease(code
->diskRep()->copyCanonicalPath()); 
1600         char main_path
[PATH_MAX
]; 
1601         bool foundTarget 
= false; 
1603         /* If it failed to get the target of the symlink, do not fail. It is a performance loss, 
1604          not a security hole */ 
1605         if (realpath(mainPath
.c_str(), main_path
) != NULL
) 
1608         std::ostringstream versionsPath
; 
1609         versionsPath 
<< cfString(path
) << "/Versions/"; 
1611         DirScanner 
scanner(versionsPath
.str()); 
1613         if (scanner
.initialized()) { 
1614                 struct dirent 
*entry 
= NULL
; 
1615                 while ((entry 
= scanner
.getNext()) != NULL
) { 
1616                         std::ostringstream fullPath
; 
1618                         if (entry
->d_type 
!= DT_DIR 
|| strcmp(entry
->d_name
, "Current") == 0) 
1621                         fullPath 
<< versionsPath
.str() << entry
->d_name
; 
1623                         char real_full_path
[PATH_MAX
]; 
1624                         if (realpath(fullPath
.str().c_str(), real_full_path
) == NULL
) 
1625                                 UnixError::check(-1); 
1627                         // Do case insensitive comparions because realpath() was called for both paths 
1628                         if (foundTarget 
&& strcmp(main_path
, real_full_path
) == 0) 
1631                         SecPointer
<SecStaticCode
> frameworkVersion 
= new SecStaticCode(DiskRep::bestGuess(real_full_path
)); 
1632                         frameworkVersion
->initializeFromParent(*this); 
1633                         frameworkVersion
->staticValidate(flags
, SecRequirement::required(req
)); 
1640 // Test a CodeDirectory flag. 
1641 // Returns false if there is no CodeDirectory. 
1642 // May throw if the CodeDirectory is present but somehow invalid. 
1644 bool SecStaticCode::flag(uint32_t tested
) 
1646         if (const CodeDirectory 
*cd 
= this->codeDirectory(false)) 
1647                 return cd
->flags 
& tested
; 
1654 // Retrieve the full SuperBlob containing all internal requirements. 
1656 const Requirements 
*SecStaticCode::internalRequirements() 
1658         if (CFDataRef reqData 
= component(cdRequirementsSlot
)) { 
1659                 const Requirements 
*req 
= (const Requirements 
*)CFDataGetBytePtr(reqData
); 
1660                 if (!req
->validateBlob()) 
1661                         MacOSError::throwMe(errSecCSReqInvalid
); 
1669 // Retrieve a particular internal requirement by type. 
1671 const Requirement 
*SecStaticCode::internalRequirement(SecRequirementType type
) 
1673         if (const Requirements 
*reqs 
= internalRequirements()) 
1674                 return reqs
->find
<Requirement
>(type
); 
1681 // Return the Designated Requirement (DR). This can be either explicit in the 
1682 // Internal Requirements component, or implicitly generated on demand here. 
1683 // Note that an explicit DR may have been implicitly generated at signing time; 
1684 // we don't distinguish this case. 
1686 const Requirement 
*SecStaticCode::designatedRequirement() 
1688         if (const Requirement 
*req 
= internalRequirement(kSecDesignatedRequirementType
)) { 
1689                 return req
;             // explicit in signing data 
1691                 if (!mDesignatedReq
) 
1692                         mDesignatedReq 
= defaultDesignatedRequirement(); 
1693                 return mDesignatedReq
; 
1699 // Generate the default Designated Requirement (DR) for this StaticCode. 
1700 // Ignore any explicit DR it may contain. 
1702 const Requirement 
*SecStaticCode::defaultDesignatedRequirement() 
1704         if (flag(kSecCodeSignatureAdhoc
)) { 
1705                 // adhoc signature: return a cdhash requirement for all architectures 
1706                 __block 
Requirement::Maker maker
; 
1707                 Requirement::Maker::Chain 
chain(maker
, opOr
); 
1709                 // insert cdhash requirement for all architectures 
1710                 __block CFRef
<CFMutableArrayRef
> allHashes 
= CFArrayCreateMutableCopy(NULL
, 0, this->cdHashes()); 
1711                 handleOtherArchitectures(^(SecStaticCode 
*other
) { 
1712                         CFArrayRef hashes 
= other
->cdHashes(); 
1713                         CFArrayAppendArray(allHashes
, hashes
, CFRangeMake(0, CFArrayGetCount(hashes
))); 
1715                 CFIndex count 
= CFArrayGetCount(allHashes
); 
1716                 for (CFIndex n 
= 0; n 
< count
; ++n
) { 
1718                         maker
.cdhash(CFDataRef(CFArrayGetValueAtIndex(allHashes
, n
))); 
1720                 return maker
.make(); 
1723                 // full signature: Gin up full context and let DRMaker do its thing 
1724                 validateDirectory();            // need the cert chain 
1725                 Requirement::Context 
context(this->certificates(), 
1726                         this->infoDictionary(), 
1727                         this->entitlements(), 
1729                         this->codeDirectory() 
1731                 return DRMaker(context
).make(); 
1733         MacOSError::throwMe(errSecCSUnimplemented
); 
1740 // Validate a SecStaticCode against the internal requirement of a particular type. 
1742 void SecStaticCode::validateRequirements(SecRequirementType type
, SecStaticCode 
*target
, 
1743         OSStatus nullError 
/* = errSecSuccess */) 
1745         DTRACK(CODESIGN_EVAL_STATIC_INTREQ
, this, type
, target
, nullError
); 
1746         if (const Requirement 
*req 
= internalRequirement(type
)) 
1747                 target
->validateRequirement(req
, nullError 
? nullError 
: errSecCSReqFailed
); 
1749                 MacOSError::throwMe(nullError
); 
1755 // Validate this StaticCode against an external Requirement 
1757 bool SecStaticCode::satisfiesRequirement(const Requirement 
*req
, OSStatus failure
) 
1759         bool result 
= false; 
1761         validateDirectory(); 
1762         result 
= req
->validates(Requirement::Context(mCertChain
, infoDictionary(), entitlements(), codeDirectory()->identifier(), codeDirectory()), failure
); 
1766 void SecStaticCode::validateRequirement(const Requirement 
*req
, OSStatus failure
) 
1768         if (!this->satisfiesRequirement(req
, failure
)) 
1769                 MacOSError::throwMe(failure
); 
1773 // Retrieve one certificate from the cert chain. 
1774 // Positive and negative indices can be used: 
1775 //    [ leaf, intermed-1, ..., intermed-n, anchor ] 
1777 // Returns NULL if unavailable for any reason. 
1779 SecCertificateRef 
SecStaticCode::cert(int ix
) 
1781         validateDirectory();            // need cert chain 
1783                 CFIndex length 
= CFArrayGetCount(mCertChain
); 
1786                 if (ix 
>= 0 && ix 
< length
) 
1787                         return SecCertificateRef(CFArrayGetValueAtIndex(mCertChain
, ix
)); 
1792 CFArrayRef 
SecStaticCode::certificates() 
1794         validateDirectory();            // need cert chain 
1800 // Gather (mostly) API-official information about this StaticCode. 
1802 // This method lives in the twilight between the API and internal layers, 
1803 // since it generates API objects (Sec*Refs) for return. 
1805 CFDictionaryRef 
SecStaticCode::signingInformation(SecCSFlags flags
) 
1808         // Start with the pieces that we return even for unsigned code. 
1809         // This makes Sec[Static]CodeRefs useful as API-level replacements 
1810         // of our internal OSXCode objects. 
1812         CFRef
<CFMutableDictionaryRef
> dict 
= makeCFMutableDictionary(1, 
1813                 kSecCodeInfoMainExecutable
, CFTempURL(this->mainExecutablePath()).get() 
1817         // If we're not signed, this is all you get 
1819         if (!this->isSigned()) 
1820                 return dict
.yield(); 
1823         // Add the generic attributes that we always include 
1825         CFDictionaryAddValue(dict
, kSecCodeInfoIdentifier
, CFTempString(this->identifier())); 
1826         CFDictionaryAddValue(dict
, kSecCodeInfoFlags
, CFTempNumber(this->codeDirectory(false)->flags
.get())); 
1827         CFDictionaryAddValue(dict
, kSecCodeInfoFormat
, CFTempString(this->format())); 
1828         CFDictionaryAddValue(dict
, kSecCodeInfoSource
, CFTempString(this->signatureSource())); 
1829         CFDictionaryAddValue(dict
, kSecCodeInfoUnique
, this->cdHash()); 
1830         CFDictionaryAddValue(dict
, kSecCodeInfoCdHashes
, this->cdHashes()); 
1831         const CodeDirectory
* cd 
= this->codeDirectory(false); 
1832         CFDictionaryAddValue(dict
, kSecCodeInfoDigestAlgorithm
, CFTempNumber(cd
->hashType
)); 
1833         CFRef
<CFArrayRef
> digests 
= makeCFArrayFrom(^CFTypeRef(CodeDirectory::HashAlgorithm type
) { return CFTempNumber(type
); }, hashAlgorithms()); 
1834         CFDictionaryAddValue(dict
, kSecCodeInfoDigestAlgorithms
, digests
); 
1836                 CFDictionaryAddValue(dict
, kSecCodeInfoPlatformIdentifier
, CFTempNumber(cd
->platform
)); 
1839         // Deliver any Info.plist only if it looks intact 
1842                 if (CFDictionaryRef info 
= this->infoDictionary()) 
1843                         CFDictionaryAddValue(dict
, kSecCodeInfoPList
, info
); 
1844         } catch (...) { }               // don't deliver Info.plist if questionable 
1847         // kSecCSSigningInformation adds information about signing certificates and chains 
1849         if (flags 
& kSecCSSigningInformation
) 
1851                         if (CFDataRef sig 
= this->signature()) 
1852                                 CFDictionaryAddValue(dict
, kSecCodeInfoCMS
, sig
); 
1853                         if (const char *teamID 
= this->teamID()) 
1854                                 CFDictionaryAddValue(dict
, kSecCodeInfoTeamIdentifier
, CFTempString(teamID
)); 
1856                                 CFDictionaryAddValue(dict
, kSecCodeInfoTrust
, mTrust
); 
1857                         if (CFArrayRef certs 
= this->certificates()) 
1858                                 CFDictionaryAddValue(dict
, kSecCodeInfoCertificates
, certs
); 
1859                         if (CFAbsoluteTime time 
= this->signingTime()) 
1860                                 if (CFRef
<CFDateRef
> date 
= CFDateCreate(NULL
, time
)) 
1861                                         CFDictionaryAddValue(dict
, kSecCodeInfoTime
, date
); 
1862                         if (CFAbsoluteTime time 
= this->signingTimestamp()) 
1863                                 if (CFRef
<CFDateRef
> date 
= CFDateCreate(NULL
, time
)) 
1864                                         CFDictionaryAddValue(dict
, kSecCodeInfoTimestamp
, date
); 
1868         // kSecCSRequirementInformation adds information on requirements 
1870         if (flags 
& kSecCSRequirementInformation
) 
1872 //DR not currently supported on iOS 
1875                         if (const Requirements 
*reqs 
= this->internalRequirements()) { 
1876                                 CFDictionaryAddValue(dict
, kSecCodeInfoRequirements
, 
1877                                         CFTempString(Dumper::dump(reqs
))); 
1878                                 CFDictionaryAddValue(dict
, kSecCodeInfoRequirementData
, CFTempData(*reqs
)); 
1881                         const Requirement 
*dreq 
= this->designatedRequirement(); 
1882                         CFRef
<SecRequirementRef
> dreqRef 
= (new SecRequirement(dreq
))->handle(); 
1883                         CFDictionaryAddValue(dict
, kSecCodeInfoDesignatedRequirement
, dreqRef
); 
1884                         if (this->internalRequirement(kSecDesignatedRequirementType
)) { // explicit 
1885                                 CFRef
<SecRequirementRef
> ddreqRef 
= (new SecRequirement(this->defaultDesignatedRequirement(), true))->handle(); 
1886                                 CFDictionaryAddValue(dict
, kSecCodeInfoImplicitDesignatedRequirement
, ddreqRef
); 
1887                         } else {        // implicit 
1888                                 CFDictionaryAddValue(dict
, kSecCodeInfoImplicitDesignatedRequirement
, dreqRef
); 
1894            if (CFDataRef ent 
= this->component(cdEntitlementSlot
)) { 
1895                    CFDictionaryAddValue(dict
, kSecCodeInfoEntitlements
, ent
); 
1896                    if (CFDictionaryRef entdict 
= this->entitlements()) 
1897                                 CFDictionaryAddValue(dict
, kSecCodeInfoEntitlementsDict
, entdict
); 
1902         // kSecCSInternalInformation adds internal information meant to be for Apple internal 
1903         // use (SPI), and not guaranteed to be stable. Primarily, this is data we want 
1904         // to reliably transmit through the API wall so that code outside the Security.framework 
1905         // can use it without having to play nasty tricks to get it. 
1907         if (flags 
& kSecCSInternalInformation
) { 
1910                                 CFDictionaryAddValue(dict
, kSecCodeInfoCodeDirectory
, mDir
); 
1911                         CFDictionaryAddValue(dict
, kSecCodeInfoCodeOffset
, CFTempNumber(mRep
->signingBase())); 
1912         if (!(flags 
& kSecCSSkipResourceDirectory
)) { 
1913             if (CFRef
<CFDictionaryRef
> rdict 
= getDictionary(cdResourceDirSlot
, false)) // suppress validation 
1914                 CFDictionaryAddValue(dict
, kSecCodeInfoResourceDirectory
, rdict
); 
1916                 if (CFRef
<CFDictionaryRef
> ddict 
= diskRepInformation()) 
1917                         CFDictionaryAddValue(dict
, kSecCodeInfoDiskRepInfo
, ddict
); 
1923         // kSecCSContentInformation adds more information about the physical layout 
1924         // of the signed code. This is (only) useful for packaging or patching-oriented 
1927         if (flags 
& kSecCSContentInformation 
&& !(flags 
& kSecCSSkipResourceDirectory
)) 
1928                 if (CFRef
<CFArrayRef
> files 
= mRep
->modifiedFiles()) 
1929                         CFDictionaryAddValue(dict
, kSecCodeInfoChangedFiles
, files
); 
1931         return dict
.yield(); 
1936 // Resource validation contexts. 
1937 // The default context simply throws a CSError, rudely terminating the operation. 
1939 SecStaticCode::ValidationContext::~ValidationContext() 
1942 void SecStaticCode::ValidationContext::reportProblem(OSStatus rc
, CFStringRef type
, CFTypeRef value
) 
1944         CSError::throwMe(rc
, type
, value
); 
1947 void SecStaticCode::CollectingContext::reportProblem(OSStatus rc
, CFStringRef type
, CFTypeRef value
) 
1949         StLock
<Mutex
> _(mLock
); 
1950         if (mStatus 
== errSecSuccess
) 
1951                 mStatus 
= rc
;                   // record first failure for eventual error return 
1954                         mCollection
.take(makeCFMutableDictionary()); 
1955                 CFMutableArrayRef element 
= CFMutableArrayRef(CFDictionaryGetValue(mCollection
, type
)); 
1957                         element 
= makeCFMutableArray(0); 
1960                         CFDictionaryAddValue(mCollection
, type
, element
); 
1963                 CFArrayAppendValue(element
, value
); 
1967 void SecStaticCode::CollectingContext::throwMe() 
1969         assert(mStatus 
!= errSecSuccess
); 
1970         throw CSError(mStatus
, mCollection
.retain()); 
1975 // Master validation driver. 
1976 // This is the static validation (only) driver for the API. 
1978 // SecStaticCode exposes an a la carte menu of topical validators applying 
1979 // to a given object. The static validation API pulls them together reliably, 
1980 // but it also adds three matrix dimensions: architecture (for "fat" Mach-O binaries), 
1981 // nested code, and multiple digests. This function will crawl a suitable cross-section of this 
1982 // validation matrix based on which options it is given, creating temporary 
1983 // SecStaticCode objects on the fly to complete the task. 
1984 // (The point, of course, is to do as little duplicate work as possible.) 
1986 void SecStaticCode::staticValidate(SecCSFlags flags
, const SecRequirement 
*req
) 
1988         setValidationFlags(flags
); 
1990         // initialize progress/cancellation state 
1991         if (flags 
& kSecCSReportProgress
) 
1992                 prepareProgress(estimateResourceWorkload() + 2);        // +1 head, +1 tail 
1995         // core components: once per architecture (if any) 
1996         this->staticValidateCore(flags
, req
); 
1997         if (flags 
& kSecCSCheckAllArchitectures
) 
1998                 handleOtherArchitectures(^(SecStaticCode
* subcode
) { 
1999                         if (flags 
& kSecCSCheckGatekeeperArchitectures
) { 
2000                                 Universal 
*fat 
= subcode
->diskRep()->mainExecutableImage(); 
2001                                 assert(fat 
&& fat
->narrowed()); // handleOtherArchitectures gave us a focused architecture slice 
2002                                 Architecture arch 
= fat
->bestNativeArch();      // actually, the ONLY one 
2003                                 if ((arch
.cpuType() & ~CPU_ARCH_MASK
) == CPU_TYPE_POWERPC
) 
2004                                         return; // irrelevant to Gatekeeper 
2006                         subcode
->detachedSignature(this->mDetachedSig
); // carry over explicit (but not implicit) detached signature 
2007                         subcode
->staticValidateCore(flags
, req
); 
2011         // allow monitor intervention in source validation phase 
2012         reportEvent(CFSTR("prepared"), NULL
); 
2014         // resources: once for all architectures 
2015         if (!(flags 
& kSecCSDoNotValidateResources
)) 
2016                 this->validateResources(flags
); 
2018         // perform strict validation if desired 
2019         if (flags 
& kSecCSStrictValidate
) 
2020                 mRep
->strictValidate(codeDirectory(), mTolerateErrors
, mValidationFlags
); 
2023         // allow monitor intervention 
2024         if (CFRef
<CFTypeRef
> veto 
= reportEvent(CFSTR("validated"), NULL
)) { 
2025                 if (CFGetTypeID(veto
) == CFNumberGetTypeID()) 
2026                         MacOSError::throwMe(cfNumber
<OSStatus
>(veto
.as
<CFNumberRef
>())); 
2028                         MacOSError::throwMe(errSecCSBadCallbackValue
); 
2032 void SecStaticCode::staticValidateCore(SecCSFlags flags
, const SecRequirement 
*req
) 
2035                 this->validateNonResourceComponents();  // also validates the CodeDirectory 
2036                 this->validateTopDirectory(); 
2037                 if (!(flags 
& kSecCSDoNotValidateExecutable
)) 
2038                         this->validateExecutable(); 
2040                         this->validateRequirement(req
->requirement(), errSecCSReqFailed
); 
2041     } catch (CSError 
&err
) { 
2042         if (Universal 
*fat 
= this->diskRep()->mainExecutableImage())    // Mach-O 
2043             if (MachO 
*mach 
= fat
->architecture()) { 
2044                 err
.augment(kSecCFErrorArchitecture
, CFTempString(mach
->architecture().displayName())); 
2048     } catch (const MacOSError 
&err
) { 
2049         // add architecture information if we can get it 
2050         if (Universal 
*fat 
= this->diskRep()->mainExecutableImage()) 
2051             if (MachO 
*mach 
= fat
->architecture()) { 
2052                 CFTempString 
arch(mach
->architecture().displayName()); 
2054                 CSError::throwMe(err
.error
, kSecCFErrorArchitecture
, arch
); 
2062 // A helper that generates SecStaticCode objects for all but the primary architecture 
2063 // of a fat binary and calls a block on them. 
2064 // If there's only one architecture (or this is an architecture-agnostic code), 
2065 // nothing happens quickly. 
2067 void SecStaticCode::handleOtherArchitectures(void (^handle
)(SecStaticCode
* other
)) 
2069         if (Universal 
*fat 
= this->diskRep()->mainExecutableImage()) { 
2070                 Universal::Architectures architectures
; 
2071                 fat
->architectures(architectures
); 
2072                 if (architectures
.size() > 1) { 
2073                         DiskRep::Context ctx
; 
2074                         off_t activeOffset 
= fat
->archOffset(); 
2075                         for (Universal::Architectures::const_iterator arch 
= architectures
.begin(); arch 
!= architectures
.end(); ++arch
) { 
2077                                         ctx
.offset 
= int_cast
<size_t, off_t
>(fat
->archOffset(*arch
)); 
2078                                         ctx
.size 
= fat
->lengthOfSlice(int_cast
<off_t
,size_t>(ctx
.offset
)); 
2079                                         if (ctx
.offset 
!= activeOffset
) {       // inactive architecture; check it 
2080                                                 SecPointer
<SecStaticCode
> subcode 
= new SecStaticCode(DiskRep::bestGuess(this->mainExecutablePath(), &ctx
)); 
2081                                                 subcode
->detachedSignature(this->mDetachedSig
); // carry over explicit (but not implicit) detached signature 
2082                                                 if (this->teamID() == NULL 
|| subcode
->teamID() == NULL
) { 
2083                                                         if (this->teamID() != subcode
->teamID()) 
2084                                                                 MacOSError::throwMe(errSecCSSignatureInvalid
); 
2085                                                 } else if (strcmp(this->teamID(), subcode
->teamID()) != 0) 
2086                                                         MacOSError::throwMe(errSecCSSignatureInvalid
); 
2089                                 } catch(std::out_of_range e
) { 
2090                                         // some of our int_casts fell over. 
2091                                         MacOSError::throwMe(errSecCSBadObjectFormat
); 
2099 // A method that takes a certificate chain (certs) and evaluates 
2100 // if it is a Mac or IPhone developer cert, an app store distribution cert, 
2101 // or a developer ID 
2103 bool SecStaticCode::isAppleDeveloperCert(CFArrayRef certs
) 
2105         static const std::string appleDeveloperRequirement 
= "(" + std::string(WWDRRequirement
) + ") or (" + MACWWDRRequirement 
+ ") or (" + developerID 
+ ") or (" + distributionCertificate 
+ ") or (" + iPhoneDistributionCert 
+ ")"; 
2106         SecPointer
<SecRequirement
> req 
= new SecRequirement(parseRequirement(appleDeveloperRequirement
), true); 
2107         Requirement::Context 
ctx(certs
, NULL
, NULL
, "", NULL
); 
2109         return req
->requirement()->validates(ctx
); 
2112 } // end namespace CodeSigning 
2113 } // end namespace Security