2  * Copyright (c) 2006-2014 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 // codedirectory - format and operations for code signing "code directory" structures 
  27 #include "codedirectory.h" 
  28 #include "csutilities.h" 
  29 #include "CSCommonPriv.h" 
  32 using namespace UnixPlusPlus
; 
  36 namespace CodeSigning 
{ 
  40 // Highest understood special slot in this CodeDirectory. 
  42 CodeDirectory::SpecialSlot 
CodeDirectory::maxSpecialSlot() const 
  44         SpecialSlot slot 
= this->nSpecialSlots
; 
  52 // Canonical filesystem names for select slot numbers. 
  53 // These are variously used for filenames, extended attribute names, etc. 
  54 // to get some consistency in naming. These are for storing signing-related 
  55 // data; they have no bearing on the actual hash slots in the CodeDirectory. 
  57 const char *CodeDirectory::canonicalSlotName(SpecialSlot slot
) 
  60         case cdRequirementsSlot
: 
  61                 return kSecCS_REQUIREMENTSFILE
; 
  62         case cdAlternateCodeDirectorySlots
: 
  63                 return kSecCS_REQUIREMENTSFILE 
"-1"; 
  64         case cdAlternateCodeDirectorySlots
+1: 
  65                 return kSecCS_REQUIREMENTSFILE 
"-2"; 
  66         case cdAlternateCodeDirectorySlots
+2: 
  67                 return kSecCS_REQUIREMENTSFILE 
"-3"; 
  68         case cdAlternateCodeDirectorySlots
+3: 
  69                 return kSecCS_REQUIREMENTSFILE 
"-4"; 
  70         case cdAlternateCodeDirectorySlots
+4: 
  71                 return kSecCS_REQUIREMENTSFILE 
"-5"; 
  72         case cdResourceDirSlot
: 
  73                 return kSecCS_RESOURCEDIRFILE
; 
  74         case cdCodeDirectorySlot
: 
  75                 return kSecCS_CODEDIRECTORYFILE
; 
  77                 return kSecCS_SIGNATUREFILE
; 
  78         case cdTopDirectorySlot
: 
  79                 return kSecCS_TOPDIRECTORYFILE
; 
  80         case cdEntitlementSlot
: 
  81                 return kSecCS_ENTITLEMENTFILE
; 
  82         case cdEntitlementDERSlot
: 
  83                 return kSecCS_ENTITLEMENTDERFILE
; 
  84         case cdRepSpecificSlot
: 
  85                 return kSecCS_REPSPECIFICFILE
; 
  93 // Canonical attributes of SpecialSlots. 
  95 unsigned CodeDirectory::slotAttributes(SpecialSlot slot
) 
  98         case cdRequirementsSlot
: 
  99                 return cdComponentIsBlob
; // global 
 100         case cdCodeDirectorySlot
: 
 101         case cdAlternateCodeDirectorySlots
: 
 102         case cdAlternateCodeDirectorySlots
+1: 
 103         case cdAlternateCodeDirectorySlots
+2: 
 104         case cdAlternateCodeDirectorySlots
+3: 
 105         case cdAlternateCodeDirectorySlots
+4: 
 106                         return cdComponentPerArchitecture 
| cdComponentIsBlob
; 
 107         case cdSignatureSlot
: 
 108                 return cdComponentPerArchitecture
; // raw 
 109         case cdEntitlementSlot
: 
 110         case cdEntitlementDERSlot
: 
 111                 return cdComponentIsBlob
; // global 
 112         case cdIdentificationSlot
: 
 113                 return cdComponentPerArchitecture
; // raw 
 115                 return 0; // global, raw 
 121 // Symbolic names for code directory special slots. 
 122 // These are only used for debug output. They are not API-official. 
 123 // Needs to be coordinated with the cd*Slot enumeration in codedirectory.h. 
 126 const char * const CodeDirectory::debugSlotName
[] = { 
 138 // Check a CodeDirectory for basic integrity. This should ensure that the 
 139 // version is understood by our code, and that the internal structure 
 140 // (offsets etc.) is intact. In particular, it must make sure that no offsets 
 141 // point outside the CodeDirectory. 
 142 // Throws if the directory is corrupted or out of versioning bounds. 
 143 // Returns if the version is usable (perhaps with degraded features due to 
 144 // compatibility hacks). 
 146 // Note: There are some things we don't bother checking because they won't 
 147 // cause crashes, and will just be flagged as nonsense later. For example, 
 148 // a Bad Guy could overlap the identifier and hash fields, which is nonsense 
 149 // but not dangerous. 
 151 void CodeDirectory::checkIntegrity() const 
 153         // check version for support 
 154         if (!this->validateBlob()) 
 155                 MacOSError::throwMe(errSecCSSignatureInvalid
);  // busted 
 156         if (version 
> compatibilityLimit
) 
 157                 MacOSError::throwMe(errSecCSSignatureUnsupported
);      // too new - no clue 
 158         if (version 
< earliestVersion
) 
 159                 MacOSError::throwMe(errSecCSSignatureUnsupported
);      // too old - can't support 
 160         if (version 
> currentVersion
) 
 161                 secinfo("codedir", "%p version 0x%x newer than current 0x%x", 
 162                         this, uint32_t(version
), currentVersion
); 
 164         bool hasPreEncryptHashes 
= version 
>= supportsPreEncrypt 
&& preEncryptOffset 
!= 0; 
 166         // now check interior offsets for validity 
 167         if (!stringAt(identOffset
)) 
 168                 MacOSError::throwMe(errSecCSSignatureFailed
); // identifier out of blob range 
 169         if (version 
>= supportsTeamID 
&& teamIDOffset 
!= 0 && !stringAt(teamIDOffset
)) 
 170                         MacOSError::throwMe(errSecCSSignatureFailed
); // identifier out of blob range 
 171         if (!contains(hashOffset 
- int64_t(hashSize
) * nSpecialSlots
, hashSize 
* (int64_t(nSpecialSlots
) + nCodeSlots
))) 
 172                 MacOSError::throwMe(errSecCSSignatureFailed
); // hash array out of blob range 
 173         if (hasPreEncryptHashes 
&& !contains(preEncryptOffset
, hashSize 
* (int64_t(nCodeSlots
)))) 
 174                 MacOSError::throwMe(errSecCSSignatureFailed
); // pre-encrypt array out of blob range 
 175         if (const Scatter 
*scatter 
= this->scatterVector()) { 
 176                 // the optional scatter vector is terminated with an element having (count == 0) 
 177                 unsigned int pagesConsumed 
= 0; 
 179                         if (!contains(scatter
, sizeof(Scatter
))) 
 180                                 MacOSError::throwMe(errSecCSSignatureFailed
); 
 181                         if (scatter
->count 
== 0) 
 183                         pagesConsumed 
+= scatter
->count
; 
 185                 if (!contains(getSlot(pagesConsumed
-1, false), hashSize
) || 
 186                         (hasPreEncryptHashes 
&& !contains(getSlot(pagesConsumed
-1, true), hashSize
)))   // referenced too many main hash slots 
 187                         MacOSError::throwMe(errSecCSSignatureFailed
); 
 190         // check consistency between the page-coverage fields 
 191         size_t limit 
= signingLimit(); 
 193                 if (limit 
== 0)                                                                 // can't have paged signatures with no covered data 
 194                         MacOSError::throwMe(errSecCSSignatureFailed
); 
 195                 size_t coveredPages 
= ((limit
-1) >> pageSize
) + 1; // page slots required to cover signingLimit 
 196                 if (coveredPages 
!= nCodeSlots
) 
 197                         MacOSError::throwMe(errSecCSSignatureFailed
); 
 199                 if ((limit 
> 0) != nCodeSlots
)  // must have one code slot, or none if no code 
 200                         MacOSError::throwMe(errSecCSSignatureFailed
); 
 206 // Validate a slot against data in memory. 
 208 bool CodeDirectory::validateSlot(const void *data
, size_t length
, Slot slot
, bool preEncrypt
) const 
 210         secinfo("codedir", "%p validating slot %d", this, int(slot
)); 
 211         MakeHash
<CodeDirectory
> hasher(this); 
 212         Hashing::Byte digest
[hasher
->digestLength()]; 
 213         generateHash(hasher
, data
, length
, digest
); 
 214         return memcmp(digest
, getSlot(slot
, preEncrypt
), hasher
->digestLength()) == 0; 
 219 // Validate a slot against the contents of an open file. At most 'length' bytes 
 220 // will be read from the file. 
 222 bool CodeDirectory::validateSlot(FileDesc fd
, size_t length
, Slot slot
, bool preEncrypt
) const 
 224         MakeHash
<CodeDirectory
> hasher(this); 
 225         Hashing::Byte digest
[hasher
->digestLength()]; 
 226         generateHash(hasher
, fd
, digest
, length
); 
 227         return memcmp(digest
, getSlot(slot
, preEncrypt
), hasher
->digestLength()) == 0; 
 232 // Check whether a particular slot is present. 
 233 // Absense is indicated by either a zero hash, or by lying outside 
 236 bool CodeDirectory::slotIsPresent(Slot slot
) const 
 238         if (slot 
>= -Slot(nSpecialSlots
) && slot 
< Slot(nCodeSlots
)) { 
 239                 const Hashing::Byte 
*digest 
= getSlot(slot
, false); 
 240                 for (unsigned n 
= 0; n 
< hashSize
; n
++) 
 242                                 return true;    // non-zero digest => present 
 244         return false;   // absent 
 249 // Given a hash type code, create an appropriate subclass of DynamicHash 
 250 // and return it. The caller owns the object and  must delete it when done. 
 251 // This function never returns NULL. It throws if the hashType is unsuupported, 
 252 // or if there's an error creating the hasher. 
 254 DynamicHash 
*CodeDirectory::hashFor(HashAlgorithm hashType
) 
 257         case kSecCodeSignatureHashSHA1
:                                         return new CCHashInstance(kCCDigestSHA1
); 
 258         case kSecCodeSignatureHashSHA256
:                                       return new CCHashInstance(kCCDigestSHA256
); 
 259         case kSecCodeSignatureHashSHA384
:                                       return new CCHashInstance(kCCDigestSHA384
); 
 260         case kSecCodeSignatureHashSHA256Truncated
:                      return new CCHashInstance(kCCDigestSHA256
, SHA1::digestLength
); 
 262                 MacOSError::throwMe(errSecCSSignatureUnsupported
); 
 268 // Determine which of a set of possible digest types should be chosen as the "best" one 
 270 static const CodeDirectory::HashAlgorithm hashPriorities
[] = { 
 271         kSecCodeSignatureHashSHA384
, 
 272         kSecCodeSignatureHashSHA256
, 
 273         kSecCodeSignatureHashSHA256Truncated
, 
 274         kSecCodeSignatureHashSHA1
, 
 275         kSecCodeSignatureNoHash         
// sentinel 
 278 bool CodeDirectory::viableHash(HashAlgorithm type
) 
 280         for (const HashAlgorithm
* tp 
= hashPriorities
; *tp 
!= kSecCodeSignatureNoHash
; tp
++) 
 287 CodeDirectory::HashAlgorithm 
CodeDirectory::bestHashOf(const HashAlgorithms 
&types
) 
 289         for (const HashAlgorithm
* type 
= hashPriorities
; *type 
!= kSecCodeSignatureNoHash
; type
++) 
 290                 if (types
.find(*type
) != types
.end()) 
 292         MacOSError::throwMe(errSecCSUnsupportedDigestAlgorithm
); 
 297 // Hash a file range with multiple digest algorithms and then pass the resulting 
 298 // digests to a per-algorithm block. 
 300 void CodeDirectory::multipleHashFileData(FileDesc fd
, size_t limit
, CodeDirectory::HashAlgorithms types
, void (^action
)(HashAlgorithm type
, DynamicHash
* hasher
)) 
 302         assert(!types
.empty()); 
 303         map
<HashAlgorithm
, RefPointer
<DynamicHash
> > hashes
; 
 304         for (auto it 
= types
.begin(); it 
!= types
.end(); ++it
) { 
 305                 if (CodeDirectory::viableHash(*it
)) 
 306                         hashes
[*it
] = CodeDirectory::hashFor(*it
); 
 308         scanFileData(fd
, limit
, ^(const void *buffer
, size_t size
) { 
 309                 for (auto it 
= hashes
.begin(); it 
!= hashes
.end(); ++it
) { 
 310             it
->second
->update(buffer
, size
); 
 313         CFRef
<CFMutableDictionaryRef
> result 
= makeCFMutableDictionary(); 
 314         for (auto it 
= hashes
.begin(); it 
!= hashes
.end(); ++it
) { 
 315                 action(it
->first
, it
->second
); 
 321     // Hash data in memory using our hashAlgorithm() 
 323 bool CodeDirectory::verifyMemoryContent(CFDataRef data
, const Byte
* digest
) const 
 325     RefPointer
<DynamicHash
> hasher 
= CodeDirectory::hashFor(this->hashType
); 
 326     hasher
->update(CFDataGetBytePtr(data
), CFDataGetLength(data
)); 
 327     return hasher
->verify(digest
); 
 332 // Generate the canonical cdhash - the internal hash of the CodeDirectory itself. 
 333 // With 'truncate' truncates to 20 bytes, because that's what's commonly used. 
 335 CFDataRef 
CodeDirectory::cdhash(bool truncate
) const 
 337         MakeHash
<CodeDirectory
> hash(this); 
 338         Hashing::Byte digest
[hash
->digestLength()]; 
 339         hash
->update(this, this->length()); 
 340         hash
->finish(digest
); 
 341         return makeCFData(digest
, 
 342                                           truncate 
? min(hash
->digestLength(), size_t(kSecCodeCDHashLength
)) : 
 343                                           hash
->digestLength()); 
 348 // Hash the next limit bytes of a file and return the digest. 
 349 // If the file is shorter, hash as much as you can. 
 350 // Limit==0 means unlimited (to end of file). 
 351 // Return how many bytes were actually hashed. 
 352 // Throw on any errors. 
 354 size_t CodeDirectory::generateHash(DynamicHash 
*hasher
, FileDesc fd
, Hashing::Byte 
*digest
, size_t limit
) 
 356         size_t size 
= hashFileData(fd
, hasher
, limit
); 
 357         hasher
->finish(digest
); 
 363 // Ditto, but hash a memory buffer instead. 
 365 size_t CodeDirectory::generateHash(DynamicHash 
*hasher
, const void *data
, size_t length
, Hashing::Byte 
*digest
) 
 367         hasher
->update(data
, length
); 
 368         hasher
->finish(digest
); 
 374 // Turn a hash of canonical type into a hex string 
 376 std::string 
CodeDirectory::hexHash(const unsigned char *hash
) const 
 378         size_t size 
= this->hashSize
; 
 379         char result
[2*size
+1]; 
 380         for (unsigned n 
= 0; n 
< size
; n
++) 
 381                 sprintf(result
+2*n
, "%02.2x", hash
[n
]); 
 387 // Generate a screening code string from a (complete) CodeDirectory. 
 388 // This can be used to make a lightweight pre-screening code from (just) a CodeDirectory. 
 390 std::string 
CodeDirectory::screeningCode() const 
 392         if (slotIsPresent(-cdInfoSlot
))         // has Info.plist 
 393                 return "I" + hexHash(getSlot(-cdInfoSlot
, false)); // use Info.plist hash 
 394         if (slotIsPresent(-cdRepSpecificSlot
))          // has Info.plist 
 395                 return "R" + hexHash(getSlot(-cdRepSpecificSlot
, false)); // use Info.plist hash 
 396         if (pageSize 
== 0)                                      // good-enough proxy for "not a Mach-O file" 
 397                 return "M" + hexHash(getSlot(0, false)); // use hash of main executable 
 398         return "N";                                                     // no suitable screening code 
 407 // Canonical text form for user-settable code directory flags. 
 408 // Note: This table is actually exported from Security.framework. 
 410 const SecCodeDirectoryFlagTable kSecCodeDirectoryFlagTable
[] = { 
 411         { "host",               kSecCodeSignatureHost
,                  true }, 
 412         { "adhoc",              kSecCodeSignatureAdhoc
,                 false }, 
 413         { "hard",               kSecCodeSignatureForceHard
,             true }, 
 414         { "kill",               kSecCodeSignatureForceKill
,             true }, 
 415         { "expires",            kSecCodeSignatureForceExpiration
,       true }, 
 416         { "restrict",           kSecCodeSignatureRestrict
,              true }, 
 417         { "enforcement",        kSecCodeSignatureEnforcement
,           true }, 
 418         { "library-validation", kSecCodeSignatureLibraryValidation
,             true }, 
 419         { "runtime",    kSecCodeSignatureRuntime
,               true },