/ OSX / libsecurity_codesigning / lib / machorep.cpp
machorep.cpp
  1  /*
  2   * Copyright (c) 2006,2011-2012,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  // machorep - DiskRep mix-in for handling Mach-O main executables
 26  //
 27  #include "machorep.h"
 28  #include "notarization.h"
 29  #include "StaticCode.h"
 30  #include "reqmaker.h"
 31  #include <security_utilities/logging.h>
 32  #include <security_utilities/cfmunge.h>
 33  #include <security_utilities/casts.h>
 34  
 35  
 36  
 37  namespace Security {
 38  namespace CodeSigning {
 39  
 40  using namespace UnixPlusPlus;
 41  
 42  
 43  //
 44  // Object management.
 45  // We open the main executable lazily, so nothing much happens on construction.
 46  // If the context specifies a file offset, we directly pick that Mach-O binary (only).
 47  // if it specifies an architecture, we try to pick that. Otherwise, we deliver the whole
 48  // Universal object (which will usually deliver the "native" architecture later).
 49  //
 50  MachORep::MachORep(const char *path, const Context *ctx)
 51  	: SingleDiskRep(path), mSigningData(NULL)
 52  {
 53  	if (ctx)
 54  		if (ctx->offset)
 55  			mExecutable = new Universal(fd(), (size_t)ctx->offset, ctx->size);
 56  		else if (ctx->arch) {
 57  			unique_ptr<Universal> full(new Universal(fd()));
 58  			mExecutable = new Universal(fd(), full->archOffset(ctx->arch), full->archLength(ctx->arch));
 59  		} else
 60  			mExecutable = new Universal(fd());
 61  	else
 62  		mExecutable = new Universal(fd());
 63  
 64  	assert(mExecutable);
 65  	CODESIGN_DISKREP_CREATE_MACHO(this, (char*)path, (void*)ctx);
 66  }
 67  
 68  MachORep::~MachORep()
 69  {
 70  	delete mExecutable;
 71  	::free(mSigningData);
 72  }
 73  
 74  
 75  //
 76  // Sniffer function for "plausible Mach-O binary"
 77  //
 78  bool MachORep::candidate(FileDesc &fd)
 79  {
 80  	switch (Universal::typeOf(fd)) {
 81  	case MH_EXECUTE:
 82  	case MH_DYLIB:
 83  	case MH_DYLINKER:
 84  	case MH_BUNDLE:
 85  	case MH_KEXT_BUNDLE:
 86  	case MH_PRELOAD:
 87  		return true;		// dynamic image; supported
 88  	case MH_OBJECT:
 89  		return false;		// maybe later...
 90  	default:
 91  		return false;		// not Mach-O (or too exotic)
 92  	}
 93  }
 94  
 95  
 96  
 97  //
 98  // Nowadays, the main executable object is created upon construction.
 99  //
100  Universal *MachORep::mainExecutableImage()
101  {
102  	return mExecutable;
103  }
104  
105  	
106  //
107  // Explicitly default to SHA256 (only) digests if the minimum deployment
108  // target is young enough.
109  //
110  void MachORep::prepareForSigning(SigningContext &context)
111  {
112  	if (context.digestAlgorithms().empty()) {
113  		bool requiresAgileHashes = false;
114  
115  		Universal::Architectures architectures;
116  		mExecutable->architectures(architectures);
117  
118  		for (Universal::Architectures::const_iterator arch = architectures.begin(); arch != architectures.end(); ++arch) {
119  			unique_ptr<MachO> slice(mExecutable->architecture(*arch));
120  
121  			uint32_t limit = 0;
122  			switch (slice->platform()) {
123  				case 0:
124  					// If we don't know the platform, we stay agile.
125  					requiresAgileHashes = true;
126  					continue;
127  				case PLATFORM_MACOS:
128  					// 10.11.4 had first proper sha256 support.
129  					limit = (10 << 16 | 11 << 8 | 4 << 0);
130  					break;
131  				case PLATFORM_TVOS:
132  				case PLATFORM_IOS:
133  					// iOS 11 and tvOS 11 had first proper sha256 support.
134  					limit = (11 << 16 | 0 << 8 | 0 << 0);
135  					break;
136  				case PLATFORM_WATCHOS:
137  					// We stay agile on the watch right now.
138  					requiresAgileHashes = true;
139  					continue;
140  				default:
141  					// All other platforms are assumed to be new and support SHA256.
142  					continue;
143  			}
144  			if (slice->minVersion() < limit) {
145  				// If any slice has a min version less than the limit, than we must remain agile.
146  				requiresAgileHashes = true;
147  			}
148  		}
149  
150  		// Only if every slice met the minimum requirements can we set the digest algorithm to SHA256.
151  		// Otherwise, we leave it empty and let it pick the default which will include legacy hash types.
152  		if (!requiresAgileHashes) {
153  			context.setDigestAlgorithm(kSecCodeSignatureHashSHA256);
154  		}
155  	}
156  }
157  
158  
159  //
160  // Signing base is the start of the Mach-O architecture we're using
161  //
162  size_t MachORep::signingBase()
163  {
164  	return mainExecutableImage()->archOffset();
165  }
166  	
167  size_t MachORep::signingLimit()
168  {
169  	unique_ptr<MachO> macho(mExecutable->architecture());
170  	return macho->signingExtent();
171  }
172  
173  bool MachORep::needsExecSeg(const MachO& macho) {
174  	uint32_t platform = macho.platform();
175  	
176  	// Everything gets an exec segment. This is ignored
177  	// on non-PPL devices, and explicitly wastes some
178  	// space on those devices, but is simpler logic.
179  	return platform != 0;
180  }
181  
182  size_t MachORep::execSegBase(const Architecture *arch)
183  {
184  	unique_ptr<MachO> macho(arch ? mExecutable->architecture(*arch) : mExecutable->architecture());
185  
186  	if (!needsExecSeg(*macho)) {
187  		return 0;
188  	}
189  
190  	segment_command const * const text_cmd = macho->findSegment("__TEXT");
191  
192  	if (text_cmd == NULL) {
193  		return 0;
194  	}
195  
196  	size_t off = 0;
197  
198  	if (macho->is64()) {
199  		off = int_cast<uint64_t,size_t>(reinterpret_cast<segment_command_64 const * const>(text_cmd)->fileoff);
200  	} else {
201  		off = text_cmd->fileoff;
202  	}
203  
204  	return off;
205  }
206  
207  size_t MachORep::execSegLimit(const Architecture *arch)
208  {
209  	unique_ptr<MachO> macho(arch ? mExecutable->architecture(*arch) : mExecutable->architecture());
210  
211  	if (!needsExecSeg(*macho)) {
212  		return 0;
213  	}
214  
215  	segment_command const * const text_cmd = macho->findSegment("__TEXT");
216  
217  	if (text_cmd == NULL) {
218  		return 0;
219  	}
220  
221  	size_t size = 0;
222  
223  	if (macho->is64()) {
224  		size = int_cast<uint64_t,size_t>(reinterpret_cast<segment_command_64 const * const>(text_cmd)->filesize);
225  	} else {
226  		size = text_cmd->filesize;
227  	}
228  
229  	return size;
230  }
231  
232  
233  //
234  // We choose the binary identifier for a Mach-O binary as follows:
235  //	- If the Mach-O headers have a UUID command, use the UUID.
236  //	- Otherwise, use the SHA-1 hash of the (entire) load commands.
237  //
238  CFDataRef MachORep::identification()
239  {
240  	std::unique_ptr<MachO> macho(mainExecutableImage()->architecture());
241  	return identificationFor(macho.get());
242  }
243  
244  CFDataRef MachORep::identificationFor(MachO *macho)
245  {
246  	// if there is a LC_UUID load command, use the UUID contained therein
247  	if (const load_command *cmd = macho->findCommand(LC_UUID)) {
248  		const uuid_command *uuidc = reinterpret_cast<const uuid_command *>(cmd);
249  		// uuidc->cmdsize should be sizeof(uuid_command), so if it is not,
250  		// something is wrong. Fail out.
251  		if (macho->flip(uuidc->cmdsize) != sizeof(uuid_command))
252  			MacOSError::throwMe(errSecCSSignatureInvalid);
253  		char result[4 + sizeof(uuidc->uuid)];
254  		memcpy(result, "UUID", 4);
255  		memcpy(result+4, uuidc->uuid, sizeof(uuidc->uuid));
256  		return makeCFData(result, sizeof(result));
257  	}
258  	
259  	// otherwise, use the SHA-1 hash of the entire load command area (this is way, way obsolete)
260  	SHA1 hash;
261  	hash(&macho->header(), sizeof(mach_header));
262  	hash(macho->loadCommands(), macho->commandLength());
263  	SHA1::Digest digest;
264  	hash.finish(digest);
265  	return makeCFData(digest, sizeof(digest));
266  }
267  
268  
269  //
270  // Retrieve a component from the executable.
271  // This reads the entire signing SuperBlob when first called for an executable,
272  // and then caches it for further use.
273  // Note that we could read individual components directly off disk and only cache
274  // the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected
275  // to cache the pieces anyway.
276  //
277  CFDataRef MachORep::component(CodeDirectory::SpecialSlot slot)
278  {
279  	switch (slot) {
280  	case cdInfoSlot:
281  		return infoPlist();
282  	default:
283  		return embeddedComponent(slot);
284  	}
285  }
286  
287  //
288  // Retrieve all components, used for signature editing.
289  //
290  EditableDiskRep::RawComponentMap MachORep::createRawComponents()
291  {
292  	EditableDiskRep::RawComponentMap  blobMap;
293  
294  	// First call to signingData() caches the result, so this
295  	// _should_ not cause performance issues.
296  	if (NULL == signingData()) {
297  		MacOSError::throwMe(errSecCSUnsigned);
298  	}
299  	const EmbeddedSignatureBlob &blobs = *signingData();
300  	
301  	for (unsigned int i = 0; i < blobs.count(); ++i) {
302  		CodeDirectory::Slot slot = blobs.type(i);
303  		const BlobCore *blob = blobs.blob(i);
304  		blobMap[slot] = blobs.blobData(slot, blob);
305  	}
306  	return blobMap;
307  }
308  
309  // Retrieve a component from the embedded signature SuperBlob (if present).
310  // This reads the entire signing SuperBlob when first called for an executable,
311  // and then caches it for further use.
312  // Note that we could read individual components directly off disk and only cache
313  // the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected
314  // to cache the pieces anyway. But it's not clear that the resulting multiple I/O
315  // calls wouldn't be slower in the end.
316  //
317  CFDataRef MachORep::embeddedComponent(CodeDirectory::SpecialSlot slot)
318  {
319  	if (signingData()) {
320  		return signingData()->component(slot);
321  	}
322  	
323  	// not found
324  	return NULL;
325  }
326  	
327  	
328  
329  EmbeddedSignatureBlob *MachORep::signingData()
330  {
331  	if (!mSigningData) {		// fetch and cache
332  		unique_ptr<MachO> macho(mainExecutableImage()->architecture());
333  		if (macho.get())
334  			if (const linkedit_data_command *cs = macho->findCodeSignature()) {
335  				size_t offset = macho->flip(cs->dataoff);
336  				size_t length = macho->flip(cs->datasize);
337  				if ((mSigningData = EmbeddedSignatureBlob::readBlob(macho->fd(), macho->offset() + offset, length))) {
338  					secinfo("machorep", "%zd signing bytes in %d blob(s) from %s(%s)",
339  							mSigningData->length(), mSigningData->count(),
340  							mainExecutablePath().c_str(), macho->architecture().name());
341  				} else {
342  					secinfo("machorep", "failed to read signing bytes from %s(%s)",
343  							mainExecutablePath().c_str(), macho->architecture().name());
344  					MacOSError::throwMe(errSecCSSignatureInvalid);
345  				}
346  			}
347  	}
348  	return mSigningData;
349  }
350  
351  
352  //
353  // Extract an embedded Info.plist from the file.
354  // Returns NULL if none is found.
355  //
356  CFDataRef MachORep::infoPlist()
357  {
358  	CFRef<CFDataRef> info;
359  	try {
360  		unique_ptr<MachO> macho(mainExecutableImage()->architecture());
361  		if (const section *sect = macho->findSection("__TEXT", "__info_plist")) {
362  			if (macho->is64()) {
363  				const section_64 *sect64 = reinterpret_cast<const section_64 *>(sect);
364  				info.take(macho->dataAt(macho->flip(sect64->offset), (size_t)macho->flip(sect64->size)));
365  			} else {
366  				info.take(macho->dataAt(macho->flip(sect->offset), macho->flip(sect->size)));
367  			}
368  		}
369  	} catch (...) {
370  		secinfo("machorep", "exception reading embedded Info.plist");
371  	}
372  	return info.yield();
373  }
374  
375  
376  //
377  // Provide a (vaguely) human readable characterization of this code
378  //
379  string MachORep::format()
380  {
381  	if (Universal *fat = mainExecutableImage()) {
382  		Universal::Architectures archs;
383  		fat->architectures(archs);
384  		if (fat->isUniversal()) {
385  			string s = "Mach-O universal (";
386  			for (Universal::Architectures::const_iterator it = archs.begin();
387  					it != archs.end(); ++it) {
388  				if (it != archs.begin())
389  					s += " ";
390  				s += it->displayName();
391  			}
392  			return s + ")";
393  		} else {
394  			assert(archs.size() == 1);
395  			return string("Mach-O thin (") + archs.begin()->displayName() + ")";
396  		}
397  	} else
398  		return "Mach-O (unrecognized format)";
399  }
400  
401  
402  //
403  // Flush cached data
404  //
405  void MachORep::flush()
406  {
407  	size_t offset = mExecutable->offset();
408  	size_t length = mExecutable->length();
409  	delete mExecutable;
410  	mExecutable = NULL;
411  	::free(mSigningData);
412  	mSigningData = NULL;
413  	SingleDiskRep::flush();
414  	mExecutable = new Universal(fd(), offset, length);
415  }
416  
417  CFDictionaryRef MachORep::copyDiskRepInformation()
418  {
419      unique_ptr<MachO> macho (mainExecutableImage()->architecture());
420      CFRef<CFDictionaryRef> info;
421  
422  	uint32_t platform = 0;
423  	uint32_t minVersion = 0;
424  	uint32_t sdkVersion = 0;
425  	
426      if (macho->version(&platform, &minVersion, &sdkVersion)) {
427  
428  		/* These keys replace the old kSecCodeInfoDiskRepOSPlatform, kSecCodeInfoDiskRepOSVersionMin
429  		 * and kSecCodeInfoDiskRepOSSDKVersion. The keys were renamed because we changed what value
430  		 * "platform" represents: For the old key, the actual load command (e.g. LC_VERSION_MIN_MACOSX)
431  		 * was returned; for the new key, we return one of the PLATFORM_* values used by LC_BUILD_VERSION.
432  		 *
433  		 * The keys are private and undocumented, and maintaining a translation table between the old and
434  		 * new domain would provide little value at high cost, but we do remove the old keys to make
435  		 * the change obvious.
436  		 */
437  		
438          info.take(cfmake<CFMutableDictionaryRef>("{%O = %d,%O = %d,%O = %d}",
439                                                kSecCodeInfoDiskRepVersionPlatform, platform,
440                                                kSecCodeInfoDiskRepVersionMin, minVersion,
441                                                kSecCodeInfoDiskRepVersionSDK, sdkVersion));
442  
443          if (platform == PLATFORM_MACOS && sdkVersion < (10 << 16 | 9 << 8))
444          {
445              info.take(cfmake<CFMutableDictionaryRef>("{+%O, %O = 'OS X SDK version before 10.9 does not support Library Validation'}",
446                                                    info.get(),
447                                                    kSecCodeInfoDiskRepNoLibraryValidation));
448          }
449      }
450  
451      return info.yield();
452  }
453  
454  
455  //
456  // Return a recommended unique identifier.
457  // If our file has an embedded Info.plist, use the CFBundleIdentifier from that.
458  // Otherwise, use the default.
459  //
460  string MachORep::recommendedIdentifier(const SigningContext &ctx)
461  {
462  	if (CFDataRef info = infoPlist()) {
463  		if (CFRef<CFDictionaryRef> dict = makeCFDictionaryFrom(info)) {
464  			CFStringRef code = CFStringRef(CFDictionaryGetValue(dict, kCFBundleIdentifierKey));
465  			if (code && CFGetTypeID(code) != CFStringGetTypeID())
466  				MacOSError::throwMe(errSecCSBadDictionaryFormat);
467  			if (code)
468  				return cfString(code);
469  		} else
470  			MacOSError::throwMe(errSecCSBadDictionaryFormat);
471  	}
472  	
473  	// ah well. Use the default
474  	return SingleDiskRep::recommendedIdentifier(ctx);
475  }
476  
477  
478  //
479  // The default suggested requirements for Mach-O binaries are as follows:
480  // Library requirement: Composed from dynamic load commands.
481  //
482  const Requirements *MachORep::defaultRequirements(const Architecture *arch, const SigningContext &ctx)
483  {
484  	assert(arch);		// enforced by signing infrastructure
485  	Requirements::Maker maker;
486  		
487  	// add library requirements from DYLIB commands (if any)
488  	if (Requirement *libreq = libraryRequirements(arch, ctx))
489  		maker.add(kSecLibraryRequirementType, libreq);	// takes ownership
490  
491  	// that's all
492  	return maker.make();
493  }
494  
495  Requirement *MachORep::libraryRequirements(const Architecture *arch, const SigningContext &ctx)
496  {
497  	unique_ptr<MachO> macho(mainExecutableImage()->architecture(*arch));
498  	Requirement::Maker maker;
499  	Requirement::Maker::Chain chain(maker, opOr);
500  
501  	if (macho.get())
502  		if (const linkedit_data_command *ldep = macho->findLibraryDependencies()) {
503  			size_t offset = macho->flip(ldep->dataoff);
504  			size_t length = macho->flip(ldep->datasize);
505  			if (LibraryDependencyBlob *deplist = LibraryDependencyBlob::readBlob(macho->fd(), macho->offset() + offset, length)) {
506  				try {
507  					secinfo("machorep", "%zd library dependency bytes in %d blob(s) from %s(%s)",
508  						deplist->length(), deplist->count(),
509  						mainExecutablePath().c_str(), macho->architecture().name());
510  					unsigned count = deplist->count();
511  					// we could walk through DYLIB load commands in parallel. We just don't need anything from them so far
512  					for (unsigned n = 0; n < count; n++) {
513  						const Requirement *req = NULL;
514  						if (const BlobCore *dep = deplist->blob(n)) {
515  							if ((req = Requirement::specific(dep))) {
516  								// binary code requirement; good to go
517  							} else if (const BlobWrapper *wrap = BlobWrapper::specific(dep)) {
518  								// blob-wrapped text form - convert to binary requirement
519  								std::string reqString = std::string((const char *)wrap->data(), wrap->length());
520  								CFRef<SecRequirementRef> areq;
521  								MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &areq.aref()));
522  								CFRef<CFDataRef> reqData;
523  								MacOSError::check(SecRequirementCopyData(areq, kSecCSDefaultFlags, &reqData.aref()));
524  								req = Requirement::specific((const BlobCore *)CFDataGetBytePtr(reqData));
525  							} else {
526  								secinfo("machorep", "unexpected blob type 0x%x in slot %d of binary dependencies", dep->magic(), n);
527  								continue;
528  							}
529  							chain.add();
530  							maker.copy(req);
531  						} else
532  							secinfo("machorep", "missing DR info for library index %d", n);
533  					}
534  					::free(deplist);
535  				} catch (...) {
536  					::free(deplist);
537  					throw;
538  				}
539  			}
540  		}
541  	if (chain.empty())
542  		return NULL;
543  	else
544  		return maker.make();
545  }
546  
547  
548  //
549  // Default to system page size for segmented (paged) signatures
550  //
551  size_t MachORep::pageSize(const SigningContext &)
552  {
553  	return segmentedPageSize;
554  }
555  
556  
557  //
558  // Strict validation
559  //
560  void MachORep::strictValidate(const CodeDirectory* cd, const ToleratedErrors& tolerated, SecCSFlags flags)
561  {
562  	SingleDiskRep::strictValidate(cd, tolerated, flags);
563  	
564  	// if the constructor found suspicious issues, fail a struct validation now
565  	if (mExecutable->isSuspicious() && tolerated.find(errSecCSBadMainExecutable) == tolerated.end())
566  		MacOSError::throwMe(errSecCSBadMainExecutable);
567  }
568  
569  
570  //
571  // FileDiskRep::Writers
572  //
573  DiskRep::Writer *MachORep::writer()
574  {
575  	return new Writer(this);
576  }
577  
578  
579  //
580  // Write a component.
581  // MachORep::Writers don't write to components directly; the signing code uses special
582  // knowledge of the Mach-O format to build embedded signatures and blasts them directly
583  // to disk. Thus this implementation will never be called (and, if called, will simply fail).
584  //
585  void MachORep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
586  {
587  	assert(false);
588      Syslog::notice("code signing internal error: trying to write Mach-O component directly");
589  	MacOSError::throwMe(errSecCSInternalError);
590  }
591  
592  void MachORep::registerStapledTicket()
593  {
594  	CFRef<CFDataRef> data = NULL;
595  	if (mSigningData) {
596  		data.take(mSigningData->component(cdTicketSlot));
597  		registerStapledTicketInMachO(data);
598  	}
599  }
600  
601  } // end namespace CodeSigning
602  } // end namespace Security