/ OSX / libsecurity_codesigning / lib / codedirectory.cpp
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  	case cdTicketSlot:
115  		return 0; // global, raw
116  	default:
117  		return 0; // global, raw
118  	}
119  }
120  
121  
122  //
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.
126  //
127  #if !defined(NDEBUG)
128  const char * const CodeDirectory::debugSlotName[] = {
129  	"codedirectory",
130  	"info",
131  	"requirements",
132  	"resources",
133  	"rep-specific",
134  	"entitlement"
135  };
136  #endif //NDEBUG
137  
138  
139  //
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).
147  //
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.
152  //
153  void CodeDirectory::checkIntegrity() const
154  {
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);
165  
166  	bool hasPreEncryptHashes = version >= supportsPreEncrypt && preEncryptOffset != 0;
167  
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;
180  		for (;; scatter++) {
181  			if (!contains(scatter, sizeof(Scatter)))
182  				MacOSError::throwMe(errSecCSSignatureFailed);
183  			if (scatter->count == 0)
184  				break;
185  			pagesConsumed += scatter->count;
186  		}
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);
190  	}
191  	
192  	// check consistency between the page-coverage fields
193  	size_t limit = signingLimit();
194  	if (pageSize) {
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);
200  	} else {
201  		if ((limit > 0) != nCodeSlots)	// must have one code slot, or none if no code
202  			MacOSError::throwMe(errSecCSSignatureFailed);
203  	}
204  }
205  
206  
207  //
208  // Validate a slot against data in memory.
209  //
210  bool CodeDirectory::validateSlot(const void *data, size_t length, Slot slot, bool preEncrypt) const
211  {
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;
217  }
218  
219  
220  //
221  // Validate a slot against the contents of an open file. At most 'length' bytes
222  // will be read from the file.
223  //
224  bool CodeDirectory::validateSlot(FileDesc fd, size_t length, Slot slot, bool preEncrypt) const
225  {
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;
230  }
231  
232  
233  //
234  // Check whether a particular slot is present.
235  // Absense is indicated by either a zero hash, or by lying outside
236  // the slot range.
237  //
238  bool CodeDirectory::slotIsPresent(Slot slot) const
239  {
240  	if (slot >= -Slot(nSpecialSlots) && slot < Slot(nCodeSlots)) {
241  		const Hashing::Byte *digest = getSlot(slot, false);
242  		for (unsigned n = 0; n < hashSize; n++)
243  			if (digest[n])
244  				return true;	// non-zero digest => present
245  	}
246  	return false;	// absent
247  }
248  
249  
250  //
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.
255  //
256  DynamicHash *CodeDirectory::hashFor(HashAlgorithm hashType)
257  {
258  	switch (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);
263  	default:
264  		MacOSError::throwMe(errSecCSSignatureUnsupported);
265  	}
266  }
267  	
268  	
269  //
270  // Determine which of a set of possible digest types should be chosen as the "best" one
271  //
272  static const CodeDirectory::HashAlgorithm hashPriorities[] = {
273  	kSecCodeSignatureHashSHA384,
274  	kSecCodeSignatureHashSHA256,
275  	kSecCodeSignatureHashSHA256Truncated,
276  	kSecCodeSignatureHashSHA1,
277  	kSecCodeSignatureNoHash		// sentinel
278  };
279  	
280  bool CodeDirectory::viableHash(HashAlgorithm type)
281  {
282  	for (const HashAlgorithm* tp = hashPriorities; *tp != kSecCodeSignatureNoHash; tp++)
283  		if (*tp == type)
284  			return true;
285  	return false;
286  
287  }
288  
289  CodeDirectory::HashAlgorithm CodeDirectory::bestHashOf(const HashAlgorithms &types)
290  {
291  	for (const HashAlgorithm* type = hashPriorities; *type != kSecCodeSignatureNoHash; type++)
292  		if (types.find(*type) != types.end())
293  			return *type;
294  	MacOSError::throwMe(errSecCSUnsupportedDigestAlgorithm);
295  }
296  	
297  
298  //
299  // Hash a file range with multiple digest algorithms and then pass the resulting
300  // digests to a per-algorithm block.
301  //
302  void CodeDirectory::multipleHashFileData(FileDesc fd, size_t limit, CodeDirectory::HashAlgorithms types, void (^action)(HashAlgorithm type, DynamicHash* hasher))
303  {
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);
309  	}
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);
313  		}
314  	});
315  	CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary();
316  	for (auto it = hashes.begin(); it != hashes.end(); ++it) {
317  		action(it->first, it->second);
318  	}
319  }
320      
321      
322      //
323      // Hash data in memory using our hashAlgorithm()
324      //
325  bool CodeDirectory::verifyMemoryContent(CFDataRef data, const Byte* digest) const
326  {
327      RefPointer<DynamicHash> hasher = CodeDirectory::hashFor(this->hashType);
328      hasher->update(CFDataGetBytePtr(data), CFDataGetLength(data));
329      return hasher->verify(digest);
330  }
331  	
332  	
333  //
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.
336  //
337  CFDataRef CodeDirectory::cdhash(bool truncate) const
338  {
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());
346  }
347  
348  
349  //
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.
355  //
356  size_t CodeDirectory::generateHash(DynamicHash *hasher, FileDesc fd, Hashing::Byte *digest, size_t limit)
357  {
358  	size_t size = hashFileData(fd, hasher, limit);
359  	hasher->finish(digest);
360  	return size;
361  }
362  
363  
364  //
365  // Ditto, but hash a memory buffer instead.
366  //
367  size_t CodeDirectory::generateHash(DynamicHash *hasher, const void *data, size_t length, Hashing::Byte *digest)
368  {
369  	hasher->update(data, length);
370  	hasher->finish(digest);
371  	return length;
372  }
373  
374  
375  //
376  // Turn a hash of canonical type into a hex string
377  //
378  std::string CodeDirectory::hexHash(const unsigned char *hash) const
379  {
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]);
384  	return result;
385  }
386  
387  
388  //
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.
391  //
392  std::string CodeDirectory::screeningCode() const
393  {
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
401  }
402  
403  
404  }	// CodeSigning
405  }	// Security
406  
407  
408  //
409  // Canonical text form for user-settable code directory flags.
410  // Note: This table is actually exported from Security.framework.
411  //
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 },
423  	{ NULL }
424  };