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
117 return 0; // global, raw
123 // Symbolic names for code directory special slots.
124 // These are only used for debug output. They are not API-official.
125 // Needs to be coordinated with the cd*Slot enumeration in codedirectory.h.
128 const char * const CodeDirectory::debugSlotName
[] = {
140 // Check a CodeDirectory for basic integrity. This should ensure that the
141 // version is understood by our code, and that the internal structure
142 // (offsets etc.) is intact. In particular, it must make sure that no offsets
143 // point outside the CodeDirectory.
144 // Throws if the directory is corrupted or out of versioning bounds.
145 // Returns if the version is usable (perhaps with degraded features due to
146 // compatibility hacks).
148 // Note: There are some things we don't bother checking because they won't
149 // cause crashes, and will just be flagged as nonsense later. For example,
150 // a Bad Guy could overlap the identifier and hash fields, which is nonsense
151 // but not dangerous.
153 void CodeDirectory::checkIntegrity() const
155 // check version for support
156 if (!this->validateBlob())
157 MacOSError::throwMe(errSecCSSignatureInvalid
); // busted
158 if (version
> compatibilityLimit
)
159 MacOSError::throwMe(errSecCSSignatureUnsupported
); // too new - no clue
160 if (version
< earliestVersion
)
161 MacOSError::throwMe(errSecCSSignatureUnsupported
); // too old - can't support
162 if (version
> currentVersion
)
163 secinfo("codedir", "%p version 0x%x newer than current 0x%x",
164 this, uint32_t(version
), currentVersion
);
166 bool hasPreEncryptHashes
= version
>= supportsPreEncrypt
&& preEncryptOffset
!= 0;
168 // now check interior offsets for validity
169 if (!stringAt(identOffset
))
170 MacOSError::throwMe(errSecCSSignatureFailed
); // identifier out of blob range
171 if (version
>= supportsTeamID
&& teamIDOffset
!= 0 && !stringAt(teamIDOffset
))
172 MacOSError::throwMe(errSecCSSignatureFailed
); // identifier out of blob range
173 if (!contains(hashOffset
- int64_t(hashSize
) * nSpecialSlots
, hashSize
* (int64_t(nSpecialSlots
) + nCodeSlots
)))
174 MacOSError::throwMe(errSecCSSignatureFailed
); // hash array out of blob range
175 if (hasPreEncryptHashes
&& !contains(preEncryptOffset
, hashSize
* (int64_t(nCodeSlots
))))
176 MacOSError::throwMe(errSecCSSignatureFailed
); // pre-encrypt array out of blob range
177 if (const Scatter
*scatter
= this->scatterVector()) {
178 // the optional scatter vector is terminated with an element having (count == 0)
179 unsigned int pagesConsumed
= 0;
181 if (!contains(scatter
, sizeof(Scatter
)))
182 MacOSError::throwMe(errSecCSSignatureFailed
);
183 if (scatter
->count
== 0)
185 pagesConsumed
+= scatter
->count
;
187 if (!contains(getSlot(pagesConsumed
-1, false), hashSize
) ||
188 (hasPreEncryptHashes
&& !contains(getSlot(pagesConsumed
-1, true), hashSize
))) // referenced too many main hash slots
189 MacOSError::throwMe(errSecCSSignatureFailed
);
192 // check consistency between the page-coverage fields
193 size_t limit
= signingLimit();
195 if (limit
== 0) // can't have paged signatures with no covered data
196 MacOSError::throwMe(errSecCSSignatureFailed
);
197 size_t coveredPages
= ((limit
-1) >> pageSize
) + 1; // page slots required to cover signingLimit
198 if (coveredPages
!= nCodeSlots
)
199 MacOSError::throwMe(errSecCSSignatureFailed
);
201 if ((limit
> 0) != nCodeSlots
) // must have one code slot, or none if no code
202 MacOSError::throwMe(errSecCSSignatureFailed
);
208 // Validate a slot against data in memory.
210 bool CodeDirectory::validateSlot(const void *data
, size_t length
, Slot slot
, bool preEncrypt
) const
212 secinfo("codedir", "%p validating slot %d", this, int(slot
));
213 MakeHash
<CodeDirectory
> hasher(this);
214 vector
<Hashing::Byte
> digest_vector(hasher
->digestLength());
215 generateHash(hasher
, data
, length
, digest_vector
.data());
216 return memcmp(digest_vector
.data(), getSlot(slot
, preEncrypt
), hasher
->digestLength()) == 0;
221 // Validate a slot against the contents of an open file. At most 'length' bytes
222 // will be read from the file.
224 bool CodeDirectory::validateSlot(FileDesc fd
, size_t length
, Slot slot
, bool preEncrypt
) const
226 MakeHash
<CodeDirectory
> hasher(this);
227 vector
<Hashing::Byte
> digest_vector(hasher
->digestLength());
228 generateHash(hasher
, fd
, digest_vector
.data(), length
);
229 return memcmp(digest_vector
.data(), getSlot(slot
, preEncrypt
), hasher
->digestLength()) == 0;
234 // Check whether a particular slot is present.
235 // Absense is indicated by either a zero hash, or by lying outside
238 bool CodeDirectory::slotIsPresent(Slot slot
) const
240 if (slot
>= -Slot(nSpecialSlots
) && slot
< Slot(nCodeSlots
)) {
241 const Hashing::Byte
*digest
= getSlot(slot
, false);
242 for (unsigned n
= 0; n
< hashSize
; n
++)
244 return true; // non-zero digest => present
246 return false; // absent
251 // Given a hash type code, create an appropriate subclass of DynamicHash
252 // and return it. The caller owns the object and must delete it when done.
253 // This function never returns NULL. It throws if the hashType is unsuupported,
254 // or if there's an error creating the hasher.
256 DynamicHash
*CodeDirectory::hashFor(HashAlgorithm hashType
)
259 case kSecCodeSignatureHashSHA1
: return new CCHashInstance(kCCDigestSHA1
);
260 case kSecCodeSignatureHashSHA256
: return new CCHashInstance(kCCDigestSHA256
);
261 case kSecCodeSignatureHashSHA384
: return new CCHashInstance(kCCDigestSHA384
);
262 case kSecCodeSignatureHashSHA256Truncated
: return new CCHashInstance(kCCDigestSHA256
, SHA1::digestLength
);
264 MacOSError::throwMe(errSecCSSignatureUnsupported
);
270 // Determine which of a set of possible digest types should be chosen as the "best" one
272 static const CodeDirectory::HashAlgorithm hashPriorities
[] = {
273 kSecCodeSignatureHashSHA384
,
274 kSecCodeSignatureHashSHA256
,
275 kSecCodeSignatureHashSHA256Truncated
,
276 kSecCodeSignatureHashSHA1
,
277 kSecCodeSignatureNoHash
// sentinel
280 bool CodeDirectory::viableHash(HashAlgorithm type
)
282 for (const HashAlgorithm
* tp
= hashPriorities
; *tp
!= kSecCodeSignatureNoHash
; tp
++)
289 CodeDirectory::HashAlgorithm
CodeDirectory::bestHashOf(const HashAlgorithms
&types
)
291 for (const HashAlgorithm
* type
= hashPriorities
; *type
!= kSecCodeSignatureNoHash
; type
++)
292 if (types
.find(*type
) != types
.end())
294 MacOSError::throwMe(errSecCSUnsupportedDigestAlgorithm
);
299 // Hash a file range with multiple digest algorithms and then pass the resulting
300 // digests to a per-algorithm block.
302 void CodeDirectory::multipleHashFileData(FileDesc fd
, size_t limit
, CodeDirectory::HashAlgorithms types
, void (^action
)(HashAlgorithm type
, DynamicHash
* hasher
))
304 assert(!types
.empty());
305 map
<HashAlgorithm
, RefPointer
<DynamicHash
> > hashes
;
306 for (auto it
= types
.begin(); it
!= types
.end(); ++it
) {
307 if (CodeDirectory::viableHash(*it
))
308 hashes
[*it
] = CodeDirectory::hashFor(*it
);
310 scanFileData(fd
, limit
, ^(const void *buffer
, size_t size
) {
311 for (auto it
= hashes
.begin(); it
!= hashes
.end(); ++it
) {
312 it
->second
->update(buffer
, size
);
315 CFRef
<CFMutableDictionaryRef
> result
= makeCFMutableDictionary();
316 for (auto it
= hashes
.begin(); it
!= hashes
.end(); ++it
) {
317 action(it
->first
, it
->second
);
323 // Hash data in memory using our hashAlgorithm()
325 bool CodeDirectory::verifyMemoryContent(CFDataRef data
, const Byte
* digest
) const
327 RefPointer
<DynamicHash
> hasher
= CodeDirectory::hashFor(this->hashType
);
328 hasher
->update(CFDataGetBytePtr(data
), CFDataGetLength(data
));
329 return hasher
->verify(digest
);
334 // Generate the canonical cdhash - the internal hash of the CodeDirectory itself.
335 // With 'truncate' truncates to 20 bytes, because that's what's commonly used.
337 CFDataRef
CodeDirectory::cdhash(bool truncate
) const
339 MakeHash
<CodeDirectory
> hash(this);
340 vector
<Hashing::Byte
> digest_vector(hash
->digestLength());
341 hash
->update(this, this->length());
342 hash
->finish(digest_vector
.data());
343 return makeCFData(digest_vector
.data(),
344 truncate
? min(hash
->digestLength(), size_t(kSecCodeCDHashLength
)) :
345 hash
->digestLength());
350 // Hash the next limit bytes of a file and return the digest.
351 // If the file is shorter, hash as much as you can.
352 // Limit==0 means unlimited (to end of file).
353 // Return how many bytes were actually hashed.
354 // Throw on any errors.
356 size_t CodeDirectory::generateHash(DynamicHash
*hasher
, FileDesc fd
, Hashing::Byte
*digest
, size_t limit
)
358 size_t size
= hashFileData(fd
, hasher
, limit
);
359 hasher
->finish(digest
);
365 // Ditto, but hash a memory buffer instead.
367 size_t CodeDirectory::generateHash(DynamicHash
*hasher
, const void *data
, size_t length
, Hashing::Byte
*digest
)
369 hasher
->update(data
, length
);
370 hasher
->finish(digest
);
376 // Turn a hash of canonical type into a hex string
378 std::string
CodeDirectory::hexHash(const unsigned char *hash
) const
380 size_t size
= this->hashSize
;
381 char result
[2*size
+1];
382 for (unsigned n
= 0; n
< size
; n
++)
383 sprintf(result
+2*n
, "%02.2x", hash
[n
]);
389 // Generate a screening code string from a (complete) CodeDirectory.
390 // This can be used to make a lightweight pre-screening code from (just) a CodeDirectory.
392 std::string
CodeDirectory::screeningCode() const
394 if (slotIsPresent(-cdInfoSlot
)) // has Info.plist
395 return "I" + hexHash(getSlot(-cdInfoSlot
, false)); // use Info.plist hash
396 if (slotIsPresent(-cdRepSpecificSlot
)) // has Info.plist
397 return "R" + hexHash(getSlot(-cdRepSpecificSlot
, false)); // use Info.plist hash
398 if (pageSize
== 0) // good-enough proxy for "not a Mach-O file"
399 return "M" + hexHash(getSlot(0, false)); // use hash of main executable
400 return "N"; // no suitable screening code
409 // Canonical text form for user-settable code directory flags.
410 // Note: This table is actually exported from Security.framework.
412 const SecCodeDirectoryFlagTable kSecCodeDirectoryFlagTable
[] = {
413 { "host", kSecCodeSignatureHost
, true },
414 { "adhoc", kSecCodeSignatureAdhoc
, false },
415 { "hard", kSecCodeSignatureForceHard
, true },
416 { "kill", kSecCodeSignatureForceKill
, true },
417 { "expires", kSecCodeSignatureForceExpiration
, true },
418 { "restrict", kSecCodeSignatureRestrict
, true },
419 { "enforcement", kSecCodeSignatureEnforcement
, true },
420 { "library-validation", kSecCodeSignatureLibraryValidation
, true },
421 { "runtime", kSecCodeSignatureRuntime
, true },
422 { "linker-signed", kSecCodeSignatureLinkerSigned
, true },