]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/codedirectory.cpp
Security-58286.70.7.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / codedirectory.cpp
1 /*
2 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 //
25 // codedirectory - format and operations for code signing "code directory" structures
26 //
27 #include "codedirectory.h"
28 #include "csutilities.h"
29 #include "CSCommonPriv.h"
30 #include <vector>
31
32 using namespace UnixPlusPlus;
33
34
35 namespace Security {
36 namespace CodeSigning {
37
38
39 //
40 // Highest understood special slot in this CodeDirectory.
41 //
42 CodeDirectory::SpecialSlot CodeDirectory::maxSpecialSlot() const
43 {
44 SpecialSlot slot = this->nSpecialSlots;
45 if (slot > cdSlotMax)
46 slot = cdSlotMax;
47 return slot;
48 }
49
50
51 //
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.
56 //
57 const char *CodeDirectory::canonicalSlotName(SpecialSlot slot)
58 {
59 switch (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;
76 case cdSignatureSlot:
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;
86 default:
87 return NULL;
88 }
89 }
90
91
92 //
93 // Canonical attributes of SpecialSlots.
94 //
95 unsigned CodeDirectory::slotAttributes(SpecialSlot slot)
96 {
97 switch (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
114 default:
115 return 0; // global, raw
116 }
117 }
118
119
120 //
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.
124 //
125 #if !defined(NDEBUG)
126 const char * const CodeDirectory::debugSlotName[] = {
127 "codedirectory",
128 "info",
129 "requirements",
130 "resources",
131 "rep-specific",
132 "entitlement"
133 };
134 #endif //NDEBUG
135
136
137 //
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).
145 //
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.
150 //
151 void CodeDirectory::checkIntegrity() const
152 {
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);
163
164 bool hasPreEncryptHashes = version >= supportsPreEncrypt && preEncryptOffset != 0;
165
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;
178 for (;; scatter++) {
179 if (!contains(scatter, sizeof(Scatter)))
180 MacOSError::throwMe(errSecCSSignatureFailed);
181 if (scatter->count == 0)
182 break;
183 pagesConsumed += scatter->count;
184 }
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);
188 }
189
190 // check consistency between the page-coverage fields
191 size_t limit = signingLimit();
192 if (pageSize) {
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);
198 } else {
199 if ((limit > 0) != nCodeSlots) // must have one code slot, or none if no code
200 MacOSError::throwMe(errSecCSSignatureFailed);
201 }
202 }
203
204
205 //
206 // Validate a slot against data in memory.
207 //
208 bool CodeDirectory::validateSlot(const void *data, size_t length, Slot slot, bool preEncrypt) const
209 {
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;
215 }
216
217
218 //
219 // Validate a slot against the contents of an open file. At most 'length' bytes
220 // will be read from the file.
221 //
222 bool CodeDirectory::validateSlot(FileDesc fd, size_t length, Slot slot, bool preEncrypt) const
223 {
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;
228 }
229
230
231 //
232 // Check whether a particular slot is present.
233 // Absense is indicated by either a zero hash, or by lying outside
234 // the slot range.
235 //
236 bool CodeDirectory::slotIsPresent(Slot slot) const
237 {
238 if (slot >= -Slot(nSpecialSlots) && slot < Slot(nCodeSlots)) {
239 const Hashing::Byte *digest = getSlot(slot, false);
240 for (unsigned n = 0; n < hashSize; n++)
241 if (digest[n])
242 return true; // non-zero digest => present
243 }
244 return false; // absent
245 }
246
247
248 //
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.
253 //
254 DynamicHash *CodeDirectory::hashFor(HashAlgorithm hashType)
255 {
256 switch (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);
261 default:
262 MacOSError::throwMe(errSecCSSignatureUnsupported);
263 }
264 }
265
266
267 //
268 // Determine which of a set of possible digest types should be chosen as the "best" one
269 //
270 static const CodeDirectory::HashAlgorithm hashPriorities[] = {
271 kSecCodeSignatureHashSHA384,
272 kSecCodeSignatureHashSHA256,
273 kSecCodeSignatureHashSHA256Truncated,
274 kSecCodeSignatureHashSHA1,
275 kSecCodeSignatureNoHash // sentinel
276 };
277
278 bool CodeDirectory::viableHash(HashAlgorithm type)
279 {
280 for (const HashAlgorithm* tp = hashPriorities; *tp != kSecCodeSignatureNoHash; tp++)
281 if (*tp == type)
282 return true;
283 return false;
284
285 }
286
287 CodeDirectory::HashAlgorithm CodeDirectory::bestHashOf(const HashAlgorithms &types)
288 {
289 for (const HashAlgorithm* type = hashPriorities; *type != kSecCodeSignatureNoHash; type++)
290 if (types.find(*type) != types.end())
291 return *type;
292 MacOSError::throwMe(errSecCSUnsupportedDigestAlgorithm);
293 }
294
295
296 //
297 // Hash a file range with multiple digest algorithms and then pass the resulting
298 // digests to a per-algorithm block.
299 //
300 void CodeDirectory::multipleHashFileData(FileDesc fd, size_t limit, CodeDirectory::HashAlgorithms types, void (^action)(HashAlgorithm type, DynamicHash* hasher))
301 {
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);
307 }
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);
311 }
312 });
313 CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary();
314 for (auto it = hashes.begin(); it != hashes.end(); ++it) {
315 action(it->first, it->second);
316 }
317 }
318
319
320 //
321 // Hash data in memory using our hashAlgorithm()
322 //
323 bool CodeDirectory::verifyMemoryContent(CFDataRef data, const Byte* digest) const
324 {
325 RefPointer<DynamicHash> hasher = CodeDirectory::hashFor(this->hashType);
326 hasher->update(CFDataGetBytePtr(data), CFDataGetLength(data));
327 return hasher->verify(digest);
328 }
329
330
331 //
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.
334 //
335 CFDataRef CodeDirectory::cdhash(bool truncate) const
336 {
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());
344 }
345
346
347 //
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.
353 //
354 size_t CodeDirectory::generateHash(DynamicHash *hasher, FileDesc fd, Hashing::Byte *digest, size_t limit)
355 {
356 size_t size = hashFileData(fd, hasher, limit);
357 hasher->finish(digest);
358 return size;
359 }
360
361
362 //
363 // Ditto, but hash a memory buffer instead.
364 //
365 size_t CodeDirectory::generateHash(DynamicHash *hasher, const void *data, size_t length, Hashing::Byte *digest)
366 {
367 hasher->update(data, length);
368 hasher->finish(digest);
369 return length;
370 }
371
372
373 //
374 // Turn a hash of canonical type into a hex string
375 //
376 std::string CodeDirectory::hexHash(const unsigned char *hash) const
377 {
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]);
382 return result;
383 }
384
385
386 //
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.
389 //
390 std::string CodeDirectory::screeningCode() const
391 {
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
399 }
400
401
402 } // CodeSigning
403 } // Security
404
405
406 //
407 // Canonical text form for user-settable code directory flags.
408 // Note: This table is actually exported from Security.framework.
409 //
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 },
420 { NULL }
421 };