/ OSX / libsecurity_codesigning / lib / StaticCode.cpp
StaticCode.cpp
   1  /*
   2   * Copyright (c) 2006-2015 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  // StaticCode - SecStaticCode API objects
  26  //
  27  #include "StaticCode.h"
  28  #include "SecTask.h"
  29  #include "Code.h"
  30  #include "reqmaker.h"
  31  #include "machorep.h"
  32  #if TARGET_OS_OSX
  33  #include "drmaker.h"
  34  #include "notarization.h"
  35  #endif
  36  #include "reqdumper.h"
  37  #include "reqparser.h"
  38  #include "sigblob.h"
  39  #include "resources.h"
  40  #include "detachedrep.h"
  41  #include "signerutils.h"
  42  #if TARGET_OS_OSX
  43  #include "csdatabase.h"
  44  #endif
  45  #include "dirscanner.h"
  46  #include <CoreFoundation/CFURLAccess.h>
  47  #include <Security/SecPolicyPriv.h>
  48  #include <Security/SecTrustPriv.h>
  49  #include <Security/SecCertificatePriv.h>
  50  #if TARGET_OS_OSX
  51  #include <Security/CMSPrivate.h>
  52  #endif
  53  #import <Security/SecCMS.h>
  54  #include <Security/SecCmsContentInfo.h>
  55  #include <Security/SecCmsSignerInfo.h>
  56  #include <Security/SecCmsSignedData.h>
  57  #if TARGET_OS_OSX
  58  #include <Security/cssmapplePriv.h>
  59  #endif
  60  #include <security_utilities/unix++.h>
  61  #include <security_utilities/cfmunge.h>
  62  #include <security_utilities/casts.h>
  63  #include <Security/CMSDecoder.h>
  64  #include <security_utilities/logging.h>
  65  #include <dirent.h>
  66  #include <sys/xattr.h>
  67  #include <sstream>
  68  #include <IOKit/storage/IOStorageDeviceCharacteristics.h>
  69  #include <dispatch/private.h>
  70  #include <os/assumes.h>
  71  #include <os/feature_private.h>
  72  #include <os/variant_private.h>
  73  #include <regex.h>
  74  #import <utilities/entitlements.h>
  75  
  76  
  77  namespace Security {
  78  namespace CodeSigning {
  79  
  80  using namespace UnixPlusPlus;
  81  
  82  // A requirement representing a Mac or iOS dev cert, a Mac or iOS distribution cert, or a developer ID
  83  static const char WWDRRequirement[] = "anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.2] exists";
  84  static const char MACWWDRRequirement[] = "anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.12] exists";
  85  static const char developerID[] = "anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists"
  86  											" and certificate leaf[field.1.2.840.113635.100.6.1.13] exists";
  87  static const char distributionCertificate[] =	"anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.7] exists";
  88  static const char iPhoneDistributionCert[] =	"anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.4] exists";
  89  
  90  //
  91  // Map a component slot number to a suitable error code for a failure
  92  //
  93  static inline OSStatus errorForSlot(CodeDirectory::SpecialSlot slot)
  94  {
  95  	switch (slot) {
  96  	case cdInfoSlot:
  97  		return errSecCSInfoPlistFailed;
  98  	case cdResourceDirSlot:
  99  		return errSecCSResourceDirectoryFailed;
 100  	default:
 101  		return errSecCSSignatureFailed;
 102  	}
 103  }
 104  
 105  /// Determines if the current process is marked as platform, cached with dispatch_once.
 106  static bool isCurrentProcessPlatform(void)
 107  {
 108  	static dispatch_once_t sOnceToken;
 109  	static bool sIsPlatform = false;
 110  
 111  	dispatch_once(&sOnceToken, ^{
 112  		SecTaskRef task = SecTaskCreateFromSelf(NULL);
 113  		if (task) {
 114  			uint32_t flags = SecTaskGetCodeSignStatus(task);
 115  			if (flags & kSecCodeStatusPlatform) {
 116  				sIsPlatform = true;
 117  			}
 118  			CFRelease(task);
 119  		}
 120  	});
 121  
 122  	return sIsPlatform;
 123  }
 124  
 125  /// Determines if the item qualifies for a resource validity exemption based on its filesystem location.
 126  static bool itemQualifiesForResourceExemption(string &item)
 127  {
 128  	if (isOnRootFilesystem(item.c_str())) {
 129  		return true;
 130  	}
 131  	if (os_variant_allows_internal_security_policies("com.apple.security.codesigning")) {
 132  		if (isPathPrefix("/AppleInternal/", item)) {
 133  			return true;
 134  		}
 135  	}
 136  	return false;
 137  }
 138  
 139  //
 140  // Construct a SecStaticCode object given a disk representation object
 141  //
 142  SecStaticCode::SecStaticCode(DiskRep *rep, uint32_t flags)
 143  	: mCheckfix30814861builder1(NULL),
 144  	  mRep(rep),
 145  	  mValidated(false), mExecutableValidated(false), mResourcesValidated(false), mResourcesValidContext(NULL),
 146  	  mProgressQueue("com.apple.security.validation-progress", false, QOS_CLASS_UNSPECIFIED),
 147  	  mOuterScope(NULL), mResourceScope(NULL),
 148  	  mDesignatedReq(NULL), mGotResourceBase(false), mMonitor(NULL), mLimitedAsync(NULL),
 149  	  mFlags(flags), mNotarizationChecked(false), mStaplingChecked(false), mNotarizationDate(NAN),
 150  	  mNetworkEnabledByDefault(true), mTrustedSigningCertChain(false)
 151  
 152  {
 153  	CODESIGN_STATIC_CREATE(this, rep);
 154  #if TARGET_OS_OSX
 155  	checkForSystemSignature();
 156  
 157  	// By default, platform code will no longer use the network.
 158  	if (os_feature_enabled(Security, SecCodeOCSPDefault)) {
 159  		if (isCurrentProcessPlatform()) {
 160  			mNetworkEnabledByDefault = false;
 161  		}
 162  	}
 163  	secinfo("staticCode", "SecStaticCode network default: %s", mNetworkEnabledByDefault ? "YES" : "NO");
 164  #endif
 165  }
 166  
 167  
 168  //
 169  // Clean up a SecStaticCode object
 170  //
 171  SecStaticCode::~SecStaticCode() _NOEXCEPT
 172  try {
 173  	::free(const_cast<Requirement *>(mDesignatedReq));
 174  	delete mResourcesValidContext;
 175  	delete mLimitedAsync;
 176  	delete mCheckfix30814861builder1;
 177  } catch (...) {
 178  	return;
 179  }
 180  
 181  //
 182  // Initialize a nested SecStaticCode object from its parent
 183  //
 184  void SecStaticCode::initializeFromParent(const SecStaticCode& parent) {
 185  	mOuterScope = &parent;
 186  	setMonitor(parent.monitor());
 187  	if (parent.mLimitedAsync)
 188  		mLimitedAsync = new LimitedAsync(*parent.mLimitedAsync);
 189  }
 190  
 191  //
 192  // CF-level comparison of SecStaticCode objects compares CodeDirectory hashes if signed,
 193  // and falls back on comparing canonical paths if (both are) not.
 194  //
 195  bool SecStaticCode::equal(SecCFObject &secOther)
 196  {
 197  	SecStaticCode *other = static_cast<SecStaticCode *>(&secOther);
 198  	CFDataRef mine = this->cdHash();
 199  	CFDataRef his = other->cdHash();
 200  	if (mine || his)
 201  		return mine && his && CFEqual(mine, his);
 202  	else
 203  		return CFEqual(CFRef<CFURLRef>(this->copyCanonicalPath()), CFRef<CFURLRef>(other->copyCanonicalPath()));
 204  }
 205  
 206  CFHashCode SecStaticCode::hash()
 207  {
 208  	if (CFDataRef h = this->cdHash())
 209  		return CFHash(h);
 210  	else
 211  		return CFHash(CFRef<CFURLRef>(this->copyCanonicalPath()));
 212  }
 213  
 214  
 215  //
 216  // Invoke a stage monitor if registered
 217  //
 218  CFTypeRef SecStaticCode::reportEvent(CFStringRef stage, CFDictionaryRef info)
 219  {
 220  	if (mMonitor)
 221  		return mMonitor(this->handle(false), stage, info);
 222  	else
 223  		return NULL;
 224  }
 225  
 226  void SecStaticCode::prepareProgress(unsigned int workload)
 227  {
 228  	dispatch_sync(mProgressQueue, ^{
 229  		mCancelPending = false;			// not canceled
 230  	});
 231  	if (mValidationFlags & kSecCSReportProgress) {
 232  		mCurrentWork = 0;				// nothing done yet
 233  		mTotalWork = workload;			// totally fake - we don't know how many files we'll get to chew
 234  	}
 235  }
 236  
 237  void SecStaticCode::reportProgress(unsigned amount /* = 1 */)
 238  {
 239  	if (mMonitor && (mValidationFlags & kSecCSReportProgress)) {
 240  		// update progress and report
 241  		__block bool cancel = false;
 242  		dispatch_sync(mProgressQueue, ^{
 243  			if (mCancelPending)
 244  				cancel = true;
 245  			mCurrentWork += amount;
 246  			mMonitor(this->handle(false), CFSTR("progress"), CFTemp<CFDictionaryRef>("{current=%d,total=%d}", mCurrentWork, mTotalWork));
 247  		});
 248  		// if cancellation is pending, abort now
 249  		if (cancel)
 250  			MacOSError::throwMe(errSecCSCancelled);
 251  	}
 252  }
 253  
 254  
 255  //
 256  // Set validation conditions for fine-tuning legacy tolerance
 257  //
 258  static void addError(CFTypeRef cfError, void* context)
 259  {
 260  	if (CFGetTypeID(cfError) == CFNumberGetTypeID()) {
 261  		int64_t error;
 262  		CFNumberGetValue(CFNumberRef(cfError), kCFNumberSInt64Type, (void*)&error);
 263  		MacOSErrorSet* errors = (MacOSErrorSet*)context;
 264  		errors->insert(OSStatus(error));
 265  	}
 266  }
 267  
 268  void SecStaticCode::setValidationModifiers(CFDictionaryRef conditions)
 269  {
 270  	if (conditions) {
 271  		CFDictionary source(conditions, errSecCSDbCorrupt);
 272  		mAllowOmissions = source.get<CFArrayRef>("omissions");
 273  		if (CFArrayRef errors = source.get<CFArrayRef>("errors"))
 274  			CFArrayApplyFunction(errors, CFRangeMake(0, CFArrayGetCount(errors)), addError, &this->mTolerateErrors);
 275  	}
 276  }
 277  
 278  
 279  //
 280  // Request cancellation of a validation in progress.
 281  // We do this by posting an abort flag that is checked periodically.
 282  //
 283  void SecStaticCode::cancelValidation()
 284  {
 285  	if (!(mValidationFlags & kSecCSReportProgress))		// not using progress reporting; cancel won't make it through
 286  		MacOSError::throwMe(errSecCSInvalidFlags);
 287  	dispatch_assert_queue(mProgressQueue);
 288  	mCancelPending = true;
 289  }
 290  
 291  
 292  //
 293  // Attach a detached signature.
 294  //
 295  void SecStaticCode::detachedSignature(CFDataRef sigData)
 296  {
 297  	if (sigData) {
 298  		mDetachedSig = sigData;
 299  		mRep = new DetachedRep(sigData, mRep->base(), "explicit detached");
 300  		CODESIGN_STATIC_ATTACH_EXPLICIT(this, mRep);
 301  	} else {
 302  		mDetachedSig = NULL;
 303  		mRep = mRep->base();
 304  		CODESIGN_STATIC_ATTACH_EXPLICIT(this, NULL);
 305  	}
 306  }
 307  
 308  
 309  //
 310  // Consult the system detached signature database to see if it contains
 311  // a detached signature for this StaticCode. If it does, fetch and attach it.
 312  // We do this only if the code has no signature already attached.
 313  //
 314  void SecStaticCode::checkForSystemSignature()
 315  {
 316  #if TARGET_OS_OSX
 317  	if (!this->isSigned()) {
 318  		SignatureDatabase db;
 319  		if (db.isOpen())
 320  			try {
 321  				if (RefPointer<DiskRep> dsig = db.findCode(mRep)) {
 322  					CODESIGN_STATIC_ATTACH_SYSTEM(this, dsig);
 323  					mRep = dsig;
 324  				}
 325  			} catch (...) {
 326  			}
 327  	}
 328  #else
 329      MacOSError::throwMe(errSecUnimplemented);
 330  #endif
 331  }
 332  
 333  
 334  //
 335  // Return a descriptive string identifying the source of the code signature
 336  //
 337  string SecStaticCode::signatureSource()
 338  {
 339  	if (!isSigned())
 340  		return "unsigned";
 341  	if (DetachedRep *rep = dynamic_cast<DetachedRep *>(mRep.get()))
 342  		return rep->source();
 343  	return "embedded";
 344  }
 345  
 346  
 347  //
 348  // Do ::required, but convert incoming SecCodeRefs to their SecStaticCodeRefs
 349  // (if possible).
 350  //
 351  SecStaticCode *SecStaticCode::requiredStatic(SecStaticCodeRef ref)
 352  {
 353  	SecCFObject *object = SecCFObject::required(ref, errSecCSInvalidObjectRef);
 354  	if (SecStaticCode *scode = dynamic_cast<SecStaticCode *>(object))
 355  		return scode;
 356  	else if (SecCode *code = dynamic_cast<SecCode *>(object))
 357  		return code->staticCode();
 358  	else	// neither (a SecSomethingElse)
 359  		MacOSError::throwMe(errSecCSInvalidObjectRef);
 360  }
 361  
 362  SecCode *SecStaticCode::optionalDynamic(SecStaticCodeRef ref)
 363  {
 364  	SecCFObject *object = SecCFObject::required(ref, errSecCSInvalidObjectRef);
 365  	if (dynamic_cast<SecStaticCode *>(object))
 366  		return NULL;
 367  	else if (SecCode *code = dynamic_cast<SecCode *>(object))
 368  		return code;
 369  	else	// neither (a SecSomethingElse)
 370  		MacOSError::throwMe(errSecCSInvalidObjectRef);
 371  }
 372  
 373  
 374  //
 375  // Void all cached validity data.
 376  //
 377  // We also throw out cached components, because the new signature data may have
 378  // a different idea of what components should be present. We could reconcile the
 379  // cached data instead, if performance seems to be impacted.
 380  //
 381  void SecStaticCode::resetValidity()
 382  {
 383  	CODESIGN_EVAL_STATIC_RESET(this);
 384  	mValidated = false;
 385  	mExecutableValidated = mResourcesValidated = false;
 386  	if (mResourcesValidContext) {
 387  		delete mResourcesValidContext;
 388  		mResourcesValidContext = NULL;
 389  	}
 390  	mDir = NULL;
 391  	mCodeDirectories.clear();
 392  	mSignature = NULL;
 393  	for (unsigned n = 0; n < cdSlotCount; n++)
 394  		mCache[n] = NULL;
 395  	mInfoDict = NULL;
 396  	mEntitlements = NULL;
 397  	mResourceDict = NULL;
 398  	mDesignatedReq = NULL;
 399  	mCDHash = NULL;
 400  	mGotResourceBase = false;
 401  	mTrust = NULL;
 402  	mCertChain = NULL;
 403  	mNotarizationChecked = false;
 404  	mStaplingChecked = false;
 405  	mNotarizationDate = NAN;
 406  	mRep->flush();
 407  
 408  #if TARGET_OS_OSX
 409  	// we may just have updated the system database, so check again
 410  	checkForSystemSignature();
 411  #endif
 412  }
 413  
 414  
 415  //
 416  // Retrieve a sealed component by special slot index.
 417  // If the CodeDirectory has already been validated, validate against that.
 418  // Otherwise, retrieve the component without validation (but cache it). Validation
 419  // will go through the cache and validate all cached components.
 420  //
 421  CFDataRef SecStaticCode::component(CodeDirectory::SpecialSlot slot, OSStatus fail /* = errSecCSSignatureFailed */)
 422  {
 423  	assert(slot <= cdSlotMax);
 424  
 425  	CFRef<CFDataRef> &cache = mCache[slot];
 426  	if (!cache) {
 427  		if (CFRef<CFDataRef> data = mRep->component(slot)) {
 428  			if (validated()) { // if the directory has been validated...
 429  				if (!codeDirectory()->slotIsPresent(-slot))
 430  					return NULL;
 431  
 432  				if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), // ... and it's no good
 433  						CFDataGetLength(data), -slot, false))
 434  					MacOSError::throwMe(errorForSlot(slot)); // ... then bail
 435  			}
 436  			cache = data;	// it's okay, cache it
 437  		} else {	// absent, mark so
 438  			if (validated())	// if directory has been validated...
 439  				if (codeDirectory()->slotIsPresent(-slot)) // ... and the slot is NOT missing
 440  					MacOSError::throwMe(errorForSlot(slot));	// was supposed to be there
 441  			cache = CFDataRef(kCFNull);		// white lie
 442  		}
 443  	}
 444  	return (cache == CFDataRef(kCFNull)) ? NULL : cache.get();
 445  }
 446  
 447  
 448  //
 449  // Get the CodeDirectories.
 450  // Throws (if check==true) or returns NULL (check==false) if there are none.
 451  // Always throws if the CodeDirectories exist but are invalid.
 452  // NEVER validates against the signature.
 453  //
 454  const SecStaticCode::CodeDirectoryMap *
 455  SecStaticCode::codeDirectories(bool check /* = true */) const
 456  {
 457  	if (mCodeDirectories.empty()) {
 458  		try {
 459  			loadCodeDirectories(mCodeDirectories);
 460  		} catch (...) {
 461  			if (check)
 462  				throw;
 463  			// We wanted a NON-checked peek and failed to safely decode the existing CodeDirectories.
 464  			// Pretend this is unsigned, but make sure we didn't somehow cache an invalid CodeDirectory.
 465  			if (!mCodeDirectories.empty()) {
 466  				assert(false);
 467  				Syslog::warning("code signing internal problem: mCodeDirectories set despite exception exit");
 468  				MacOSError::throwMe(errSecCSInternalError);
 469  			}
 470  		}
 471  	} else {
 472  		return &mCodeDirectories;
 473  	}
 474  	if (!mCodeDirectories.empty()) {
 475  		return &mCodeDirectories;
 476  	}
 477  	if (check) {
 478  		MacOSError::throwMe(errSecCSUnsigned);
 479  	}
 480  	return NULL;
 481  }
 482  
 483  //
 484  // Get the CodeDirectory.
 485  // Throws (if check==true) or returns NULL (check==false) if there is none.
 486  // Always throws if the CodeDirectory exists but is invalid.
 487  // NEVER validates against the signature.
 488  //
 489  const CodeDirectory *SecStaticCode::codeDirectory(bool check /* = true */) const
 490  {
 491  	if (!mDir) {
 492  		// pick our favorite CodeDirectory from the choices we've got
 493  		try {
 494  			CodeDirectoryMap const *candidates = codeDirectories(check);
 495  			if (candidates != NULL) {
 496  				CodeDirectory::HashAlgorithm type = CodeDirectory::bestHashOf(mHashAlgorithms);
 497  				mDir = candidates->at(type);	// and the winner is...
 498  			}
 499  		} catch (...) {
 500  			if (check)
 501  				throw;
 502  			// We wanted a NON-checked peek and failed to safely decode the existing CodeDirectory.
 503  			// Pretend this is unsigned, but make sure we didn't somehow cache an invalid CodeDirectory.
 504  			if (mDir) {
 505  				assert(false);
 506  				Syslog::warning("code signing internal problem: mDir set despite exception exit");
 507  				MacOSError::throwMe(errSecCSInternalError);
 508  			}
 509  		}
 510  	}
 511  	if (mDir)
 512  		return reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(mDir));
 513  	if (check)
 514  		MacOSError::throwMe(errSecCSUnsigned);
 515  	return NULL;
 516  }
 517  
 518  
 519  //
 520  // Fetch an array of all available CodeDirectories.
 521  // Returns false if unsigned (no classic CD slot), true otherwise.
 522  //
 523  bool SecStaticCode::loadCodeDirectories(CodeDirectoryMap& cdMap) const
 524  {
 525  	__block CodeDirectoryMap candidates;
 526  	__block CodeDirectory::HashAlgorithms hashAlgorithms;
 527  	__block CFRef<CFDataRef> baseDir;
 528  	auto add = ^bool (CodeDirectory::SpecialSlot slot){
 529  		CFRef<CFDataRef> cdData = diskRep()->component(slot);
 530  		if (!cdData)
 531  			return false;
 532  		const CodeDirectory* cd = reinterpret_cast<const CodeDirectory*>(CFDataGetBytePtr(cdData));
 533  		if (!cd->validateBlob(CFDataGetLength(cdData)))
 534  			MacOSError::throwMe(errSecCSSignatureFailed);	// no recovery - any suspect CD fails
 535  		cd->checkIntegrity();
 536  		auto result = candidates.insert(make_pair(cd->hashType, cdData.get()));
 537  		if (!result.second)
 538  			MacOSError::throwMe(errSecCSSignatureInvalid);	// duplicate hashType, go to heck
 539  		hashAlgorithms.insert(cd->hashType);
 540  		if (slot == cdCodeDirectorySlot)
 541  			baseDir = cdData;
 542  		return true;
 543  	};
 544  	if (!add(cdCodeDirectorySlot))
 545  		return false;		// no classic slot CodeDirectory -> unsigned
 546  	for (CodeDirectory::SpecialSlot slot = cdAlternateCodeDirectorySlots; slot < cdAlternateCodeDirectoryLimit; slot++)
 547  		if (!add(slot))		// no CodeDirectory at this slot -> end of alternates
 548  			break;
 549  	if (candidates.empty())
 550  		MacOSError::throwMe(errSecCSSignatureFailed);		// no viable CodeDirectory in sight
 551  	// commit to cached values
 552  	cdMap.swap(candidates);
 553  	mHashAlgorithms.swap(hashAlgorithms);
 554  	mBaseDir = baseDir;
 555  	return true;
 556  }
 557  
 558  
 559  //
 560  // Get the hash of the CodeDirectory.
 561  // Returns NULL if there is none.
 562  //
 563  CFDataRef SecStaticCode::cdHash()
 564  {
 565  	if (!mCDHash) {
 566  		if (const CodeDirectory *cd = codeDirectory(false)) {
 567  			mCDHash.take(cd->cdhash());
 568  			CODESIGN_STATIC_CDHASH(this, CFDataGetBytePtr(mCDHash), (unsigned int)CFDataGetLength(mCDHash));
 569  		}
 570  	}
 571  	return mCDHash;
 572  }
 573  	
 574  	
 575  //
 576  // Get an array of the cdhashes for all digest types in this signature
 577  // The array is sorted by cd->hashType.
 578  //
 579  CFArrayRef SecStaticCode::cdHashes()
 580  {
 581  	if (!mCDHashes) {
 582  		CFRef<CFMutableArrayRef> cdList = makeCFMutableArray(0);
 583  		for (auto it = mCodeDirectories.begin(); it != mCodeDirectories.end(); ++it) {
 584  			const CodeDirectory *cd = (const CodeDirectory *)CFDataGetBytePtr(it->second);
 585  			if (CFRef<CFDataRef> hash = cd->cdhash())
 586  				CFArrayAppendValue(cdList, hash);
 587  		}
 588  		mCDHashes = cdList.get();
 589  	}
 590  	return mCDHashes;
 591  }
 592  
 593  //
 594  // Get a dictionary of untruncated cdhashes for all digest types in this signature.
 595  //
 596  CFDictionaryRef SecStaticCode::cdHashesFull()
 597  {
 598  	if (!mCDHashFullDict) {
 599  		CFRef<CFMutableDictionaryRef> cdDict = makeCFMutableDictionary();
 600  		for (auto const &it : mCodeDirectories) {
 601  			CodeDirectory::HashAlgorithm alg = it.first;
 602  			const CodeDirectory *cd = (const CodeDirectory *)CFDataGetBytePtr(it.second);
 603  			CFRef<CFDataRef> hash = cd->cdhash(false);
 604  			if (hash) {
 605  				CFDictionaryAddValue(cdDict, CFTempNumber(alg), hash);
 606  			}
 607  		}
 608  		mCDHashFullDict = cdDict.get();
 609  	}
 610  	return mCDHashFullDict;
 611  }
 612  
 613  
 614  //
 615  // Return the CMS signature blob; NULL if none found.
 616  //
 617  CFDataRef SecStaticCode::signature()
 618  {
 619  	if (!mSignature)
 620  		mSignature.take(mRep->signature());
 621  	if (mSignature)
 622  		return mSignature;
 623  	MacOSError::throwMe(errSecCSUnsigned);
 624  }
 625  
 626  
 627  //
 628  // Verify the signature on the CodeDirectory.
 629  // If this succeeds (doesn't throw), the CodeDirectory is statically trustworthy.
 630  // Any outcome (successful or not) is cached for the lifetime of the StaticCode.
 631  //
 632  void SecStaticCode::validateDirectory()
 633  {
 634  	// echo previous outcome, if any
 635  	// track revocation separately, as it may not have been checked
 636  	// during the initial validation
 637  	if (!validated() || ((mValidationFlags & kSecCSEnforceRevocationChecks) && !revocationChecked()))
 638  		try {
 639  			// perform validation (or die trying)
 640  			CODESIGN_EVAL_STATIC_DIRECTORY(this);
 641  			mValidationExpired = verifySignature();
 642  			if (mValidationFlags & kSecCSEnforceRevocationChecks)
 643  				mRevocationChecked = true;
 644  
 645  			for (CodeDirectory::SpecialSlot slot = codeDirectory()->maxSpecialSlot(); slot >= 1; --slot)
 646  				if (mCache[slot])	// if we already loaded that resource...
 647  					validateComponent(slot, errorForSlot(slot)); // ... then check it now
 648  			mValidated = true;			// we've done the deed...
 649  			mValidationResult = errSecSuccess;	// ... and it was good
 650  		} catch (const CommonError &err) {
 651  			mValidated = true;
 652  			mValidationResult = err.osStatus();
 653  			throw;
 654  		} catch (...) {
 655  			secinfo("staticCode", "%p validation threw non-common exception", this);
 656  			mValidated = true;
 657  			Syslog::notice("code signing internal problem: unknown exception thrown by validation");
 658  			mValidationResult = errSecCSInternalError;
 659  			throw;
 660  		}
 661  	assert(validated());
 662      // XXX: Embedded doesn't have CSSMERR_TP_CERT_EXPIRED so we can't throw it
 663      // XXX: This should be implemented for embedded once we implement
 664      // XXX: verifySignature and see how we're going to handle expired certs
 665  #if TARGET_OS_OSX
 666  	if (mValidationResult == errSecSuccess) {
 667  		if (mValidationExpired)
 668  			if ((mValidationFlags & kSecCSConsiderExpiration)
 669  					|| (codeDirectory()->flags & kSecCodeSignatureForceExpiration))
 670  				MacOSError::throwMe(CSSMERR_TP_CERT_EXPIRED);
 671  	} else
 672  		MacOSError::throwMe(mValidationResult);
 673  #endif
 674  }
 675  
 676  
 677  //
 678  // Load and validate the CodeDirectory and all components *except* those related to the resource envelope.
 679  // Those latter components are checked by validateResources().
 680  //
 681  void SecStaticCode::validateNonResourceComponents()
 682  {
 683  	this->validateDirectory();
 684  	for (CodeDirectory::SpecialSlot slot = codeDirectory()->maxSpecialSlot(); slot >= 1; --slot)
 685  		switch (slot) {
 686  		case cdResourceDirSlot:		// validated by validateResources
 687  			break;
 688  		default:
 689  			this->component(slot);		// loads and validates
 690  			break;
 691  		}
 692  }
 693  	
 694  	
 695  //
 696  // Check that any "top index" sealed into the signature conforms to what's actually here.
 697  //
 698  void SecStaticCode::validateTopDirectory()
 699  {
 700  	assert(mDir);		// must already have loaded CodeDirectories
 701  	if (CFDataRef topDirectory = component(cdTopDirectorySlot)) {
 702  		const auto topData = (const Endian<uint32_t> *)CFDataGetBytePtr(topDirectory);
 703  		const auto topDataEnd = topData + CFDataGetLength(topDirectory) / sizeof(*topData);
 704  		std::vector<uint32_t> signedVector(topData, topDataEnd);
 705  		
 706  		std::vector<uint32_t> foundVector;
 707  		foundVector.push_back(cdCodeDirectorySlot);	// mandatory
 708  		for (CodeDirectory::Slot slot = 1; slot <= cdSlotMax; ++slot)
 709  			if (component(slot))
 710  				foundVector.push_back(slot);
 711  		int alternateCount = int(mCodeDirectories.size() - 1);		// one will go into cdCodeDirectorySlot
 712  		for (int n = 0; n < alternateCount; n++)
 713  			foundVector.push_back(cdAlternateCodeDirectorySlots + n);
 714  		foundVector.push_back(cdSignatureSlot);		// mandatory (may be empty)
 715  		
 716  		if (signedVector != foundVector)
 717  			MacOSError::throwMe(errSecCSSignatureFailed);
 718  	}
 719  }
 720  
 721  
 722  //
 723  // Get the (signed) signing date from the code signature.
 724  // Sadly, we need to validate the signature to get the date (as a side benefit).
 725  // This means that you can't get the signing time for invalidly signed code.
 726  //
 727  // We could run the decoder "almost to" verification to avoid this, but there seems
 728  // little practical point to such a duplication of effort.
 729  //
 730  CFAbsoluteTime SecStaticCode::signingTime()
 731  {
 732  	validateDirectory();
 733  	return mSigningTime;
 734  }
 735  
 736  CFAbsoluteTime SecStaticCode::signingTimestamp()
 737  {
 738  	validateDirectory();
 739  	return mSigningTimestamp;
 740  }
 741  
 742  #if TARGET_OS_OSX
 743  #define kSecSHA256HashSize 32
 744  // subject:/C=US/ST=California/L=San Jose/O=Adobe Systems Incorporated/OU=Information Systems/OU=Digital ID Class 3 - Microsoft Software Validation v2/CN=Adobe Systems Incorporated
 745  // issuer :/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Code Signing 2010 CA
 746  // Not Before: Dec 15 00:00:00 2010 GMT
 747  // Not After : Dec 14 23:59:59 2012 GMT
 748  static const unsigned char ASI_CS_12[] = {
 749  	0x77,0x82,0x9C,0x64,0x33,0x45,0x2E,0x4A,0xD3,0xA8,0xE4,0x6F,0x00,0x6C,0x27,0xEA,
 750  	0xFB,0xD3,0xF2,0x6D,0x50,0xF3,0x6F,0xE0,0xE9,0x6D,0x06,0x59,0x19,0xB5,0x46,0xFF
 751  };
 752  
 753  bool SecStaticCode::checkfix41082220(OSStatus cssmTrustResult)
 754  {
 755  	// only applicable to revoked results
 756  	if (cssmTrustResult != CSSMERR_TP_CERT_REVOKED) {
 757  		return false;
 758  	}
 759  
 760  	// only this leaf certificate
 761  	if (CFArrayGetCount(mCertChain) == 0) {
 762  		return false;
 763  	}
 764  	CFRef<CFDataRef> leafHash(SecCertificateCopySHA256Digest((SecCertificateRef)CFArrayGetValueAtIndex(mCertChain, 0)));
 765  	if (memcmp(ASI_CS_12, CFDataGetBytePtr(leafHash), kSecSHA256HashSize) != 0) {
 766  		return false;
 767  	}
 768  
 769  	// detached dmg signature
 770  	if (!isDetached() || format() != std::string("disk image")) {
 771  		return false;
 772  	}
 773  
 774  	// sha-1 signed
 775  	if (hashAlgorithms().size() != 1 || hashAlgorithm() != kSecCodeSignatureHashSHA1) {
 776  		return false;
 777  	}
 778  
 779  	// not a privileged binary - no TeamID and no entitlements
 780  	if (component(cdEntitlementSlot) || teamID()) {
 781  		return false;
 782  	}
 783  
 784  	// no flags and old version
 785  	if (codeDirectory()->version != 0x20100 || codeDirectory()->flags != 0) {
 786  		return false;
 787  	}
 788  
 789  	Security::Syslog::warning("CodeSigning: Check-fix enabled for dmg '%s' with identifier '%s' signed with revoked certificates",
 790  							  mainExecutablePath().c_str(), identifier().c_str());
 791  	return true;
 792  }
 793  #endif // TARGET_OS_OSX
 794  
 795  //
 796  // Verify the CMS signature.
 797  // This performs the cryptographic tango. It returns if the signature is valid,
 798  // or throws if it is not. As a side effect, a successful return sets up the
 799  // cached certificate chain for future use.
 800  // Returns true if the signature is expired (the X.509 sense), false if it's not.
 801  // Expiration is fatal (throws) if a secure timestamp is included, but not otherwise.
 802  //
 803  bool SecStaticCode::verifySignature()
 804  {
 805  	// ad-hoc signed code is considered validly signed by definition
 806  	if (flag(kSecCodeSignatureAdhoc)) {
 807  		CODESIGN_EVAL_STATIC_SIGNATURE_ADHOC(this);
 808  		return false;
 809  	}
 810  
 811  	DTRACK(CODESIGN_EVAL_STATIC_SIGNATURE, this, (char*)this->mainExecutablePath().c_str());
 812  #if TARGET_OS_OSX
 813  	if (!(mValidationFlags & kSecCSApplyEmbeddedPolicy)) {
 814  		// decode CMS and extract SecTrust for verification
 815  		CFRef<CMSDecoderRef> cms;
 816  		MacOSError::check(CMSDecoderCreate(&cms.aref())); // create decoder
 817  		CFDataRef sig = this->signature();
 818  		MacOSError::check(CMSDecoderUpdateMessage(cms, CFDataGetBytePtr(sig), CFDataGetLength(sig)));
 819  		this->codeDirectory();	// load CodeDirectory (sets mDir)
 820  		MacOSError::check(CMSDecoderSetDetachedContent(cms, mBaseDir));
 821  		MacOSError::check(CMSDecoderFinalizeMessage(cms));
 822  		MacOSError::check(CMSDecoderSetSearchKeychain(cms, cfEmptyArray()));
 823  		CFRef<CFArrayRef> vf_policies(createVerificationPolicies());
 824  		CFRef<CFArrayRef> ts_policies(createTimeStampingAndRevocationPolicies());
 825  
 826  		CMSSignerStatus status;
 827  		MacOSError::check(CMSDecoderCopySignerStatus(cms, 0, vf_policies,
 828  					false, &status, &mTrust.aref(), NULL));
 829  
 830  		if (status != kCMSSignerValid) {
 831  			const char *reason;
 832  			switch (status) {
 833  				case kCMSSignerUnsigned: reason="kCMSSignerUnsigned"; break;
 834  				case kCMSSignerNeedsDetachedContent: reason="kCMSSignerNeedsDetachedContent"; break;
 835  				case kCMSSignerInvalidSignature: reason="kCMSSignerInvalidSignature"; break;
 836  				case kCMSSignerInvalidCert: reason="kCMSSignerInvalidCert"; break;
 837  				case kCMSSignerInvalidIndex: reason="kCMSSignerInvalidIndex"; break;
 838  				default: reason="unknown"; break;
 839  			}
 840  			Security::Syslog::error("CMSDecoderCopySignerStatus failed with %s error (%d)",
 841  									reason, (int)status);
 842  			MacOSError::throwMe(errSecCSSignatureFailed);
 843  		}
 844  
 845  		// retrieve auxiliary v1 data bag and verify against current state
 846  		CFRef<CFDataRef> hashAgilityV1;
 847  		switch (OSStatus rc = CMSDecoderCopySignerAppleCodesigningHashAgility(cms, 0, &hashAgilityV1.aref())) {
 848  		case noErr:
 849  			if (hashAgilityV1) {
 850  				CFRef<CFDictionaryRef> hashDict = makeCFDictionaryFrom(hashAgilityV1);
 851  				CFArrayRef cdList = CFArrayRef(CFDictionaryGetValue(hashDict, CFSTR("cdhashes")));
 852  				CFArrayRef myCdList = this->cdHashes();
 853  
 854  				/* Note that this is not very "agile": There's no way to calculate the exact
 855  				 * list for comparison if it contains hash algorithms we don't know yet... */
 856  				if (cdList == NULL || !CFEqual(cdList, myCdList))
 857  					MacOSError::throwMe(errSecCSSignatureFailed);
 858  			}
 859  			break;
 860  		case -1:	/* CMS used to return this for "no attribute found", so tolerate it. Now returning noErr/NULL */
 861  			break;
 862  		default:
 863  			MacOSError::throwMe(rc);
 864  		}
 865  
 866  		// retrieve auxiliary v2 data bag and verify against current state
 867  		CFRef<CFDictionaryRef> hashAgilityV2;
 868  		switch (OSStatus rc = CMSDecoderCopySignerAppleCodesigningHashAgilityV2(cms, 0, &hashAgilityV2.aref())) {
 869  			case noErr:
 870  				if (hashAgilityV2) {
 871  					/* Require number of code directoris and entries in the hash agility
 872  					 * dict to be the same size (no stripping out code directories).
 873  					 */
 874  					if (CFDictionaryGetCount(hashAgilityV2) != mCodeDirectories.size()) {
 875  						MacOSError::throwMe(errSecCSSignatureFailed);
 876  					}
 877  
 878  					/* Require every cdhash of every code directory whose hash
 879  					 * algorithm we know to be in the agility dictionary.
 880  					 *
 881  					 * We check untruncated cdhashes here because we can.
 882  					 */
 883  					bool foundOurs = false;
 884  					for (auto& entry : mCodeDirectories) {
 885  						SECOidTag tag = CodeDirectorySet::SECOidTagForAlgorithm(entry.first);
 886  
 887  						if (tag == SEC_OID_UNKNOWN) {
 888  							// Unknown hash algorithm, ignore.
 889  							continue;
 890  						}
 891  
 892  						CFRef<CFNumberRef> key = makeCFNumber(int(tag));
 893  						CFRef<CFDataRef> entryCdhash;
 894  						entryCdhash = (CFDataRef)CFDictionaryGetValue(hashAgilityV2, (void*)key.get());
 895  
 896  						CodeDirectory const *cd = (CodeDirectory const*)CFDataGetBytePtr(entry.second);
 897  						CFRef<CFDataRef> ourCdhash = cd->cdhash(false); // Untruncated cdhash!
 898  						if (!CFEqual(entryCdhash, ourCdhash)) {
 899  							MacOSError::throwMe(errSecCSSignatureFailed);
 900  						}
 901  
 902  						if (entry.first == this->hashAlgorithm()) {
 903  							foundOurs = true;
 904  						}
 905  					}
 906  
 907  					/* Require the cdhash of our chosen code directory to be in the dictionary.
 908  					 * In theory, the dictionary could be full of unsupported cdhashes, but we
 909  					 * really want ours, which is bound to be supported, to be covered.
 910  					 */
 911  					if (!foundOurs) {
 912  						MacOSError::throwMe(errSecCSSignatureFailed);
 913  					}
 914  				}
 915  				break;
 916  			case -1:	/* CMS used to return this for "no attribute found", so tolerate it. Now returning noErr/NULL */
 917  				break;
 918  			default:
 919  				MacOSError::throwMe(rc);
 920  		}
 921  
 922  		// internal signing time (as specified by the signer; optional)
 923  		mSigningTime = 0;       // "not present" marker (nobody could code sign on Jan 1, 2001 :-)
 924  		switch (OSStatus rc = CMSDecoderCopySignerSigningTime(cms, 0, &mSigningTime)) {
 925  		case errSecSuccess:
 926  		case errSecSigningTimeMissing:
 927  			break;
 928  		default:
 929  			Security::Syslog::error("Could not get signing time (error %d)", (int)rc);
 930  			MacOSError::throwMe(rc);
 931  		}
 932  
 933  		// certified signing time (as specified by a TSA; optional)
 934  		mSigningTimestamp = 0;
 935  		switch (OSStatus rc = CMSDecoderCopySignerTimestampWithPolicy(cms, ts_policies, 0, &mSigningTimestamp)) {
 936  		case errSecSuccess:
 937  		case errSecTimestampMissing:
 938  			break;
 939  		default:
 940  			Security::Syslog::error("Could not get timestamp (error %d)", (int)rc);
 941  			MacOSError::throwMe(rc);
 942  		}
 943  
 944  		// set up the environment for SecTrust
 945  		if (validationCannotUseNetwork()) {
 946  			MacOSError::check(SecTrustSetNetworkFetchAllowed(mTrust, false)); // no network?
 947  		}
 948  		MacOSError::check(SecTrustSetKeychainsAllowed(mTrust, false));
 949  
 950  		CSSM_APPLE_TP_ACTION_DATA actionData = {
 951  			CSSM_APPLE_TP_ACTION_VERSION,	// version of data structure
 952  			0	// action flags
 953  		};
 954  
 955  		if (!(mValidationFlags & kSecCSCheckTrustedAnchors)) {
 956  			/* no need to evaluate anchor trust when building cert chain */
 957  			MacOSError::check(SecTrustSetAnchorCertificates(mTrust, cfEmptyArray())); // no anchors
 958  			actionData.ActionFlags |= CSSM_TP_ACTION_IMPLICIT_ANCHORS;	// action flags
 959  		}
 960  
 961  		for (;;) {	// at most twice
 962  			MacOSError::check(SecTrustSetParameters(mTrust,
 963  				CSSM_TP_ACTION_DEFAULT, CFTempData(&actionData, sizeof(actionData))));
 964  
 965  			// evaluate trust and extract results
 966  			SecTrustResultType trustResult;
 967  			MacOSError::check(SecTrustEvaluate(mTrust, &trustResult));
 968  			mCertChain.take(copyCertChain(mTrust));
 969  
 970  			// if this is an Apple developer cert....
 971  			if (teamID() && SecStaticCode::isAppleDeveloperCert(mCertChain)) {
 972  				CFRef<CFStringRef> teamIDFromCert;
 973  				if (CFArrayGetCount(mCertChain) > 0) {
 974  					SecCertificateRef leaf = (SecCertificateRef)CFArrayGetValueAtIndex(mCertChain, Requirement::leafCert);
 975  					CFArrayRef organizationalUnits = SecCertificateCopyOrganizationalUnit(leaf);
 976  					if (organizationalUnits) {
 977  						teamIDFromCert.take((CFStringRef)CFRetain(CFArrayGetValueAtIndex(organizationalUnits, 0)));
 978  						CFRelease(organizationalUnits);
 979  					} else {
 980  						teamIDFromCert = NULL;
 981  					}
 982  
 983  					if (teamIDFromCert) {
 984  						CFRef<CFStringRef> teamIDFromCD = CFStringCreateWithCString(NULL, teamID(), kCFStringEncodingUTF8);
 985  						if (!teamIDFromCD) {
 986  							Security::Syslog::error("Could not get team identifier (%s)", teamID());
 987  							MacOSError::throwMe(errSecCSInvalidTeamIdentifier);
 988  						}
 989  
 990  						if (CFStringCompare(teamIDFromCert, teamIDFromCD, 0) != kCFCompareEqualTo) {
 991  							Security::Syslog::error("Team identifier in the signing certificate (%s) does not match the team identifier (%s) in the code directory",
 992  													cfString(teamIDFromCert).c_str(), teamID());
 993  							MacOSError::throwMe(errSecCSBadTeamIdentifier);
 994  						}
 995  					}
 996  				}
 997  			}
 998  
 999  			CODESIGN_EVAL_STATIC_SIGNATURE_RESULT(this, trustResult, mCertChain ? (int)CFArrayGetCount(mCertChain) : 0);
1000  			switch (trustResult) {
1001  			case kSecTrustResultProceed:
1002  			case kSecTrustResultUnspecified:
1003  				break;				// success
1004  			case kSecTrustResultDeny:
1005  				MacOSError::throwMe(CSSMERR_APPLETP_TRUST_SETTING_DENY);	// user reject
1006  			case kSecTrustResultInvalid:
1007  				assert(false);		// should never happen
1008  				MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED);
1009  			default:
1010  				{
1011  					OSStatus result;
1012  					MacOSError::check(SecTrustGetCssmResultCode(mTrust, &result));
1013  					// if we have a valid timestamp, CMS validates against (that) signing time and all is well.
1014  					// If we don't have one, may validate against *now*, and must be able to tolerate expiration.
1015  					if (mSigningTimestamp == 0) { // no timestamp available
1016  						if (((result == CSSMERR_TP_CERT_EXPIRED) || (result == CSSMERR_TP_CERT_NOT_VALID_YET))
1017  								&& !(actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED)) {
1018  							CODESIGN_EVAL_STATIC_SIGNATURE_EXPIRED(this);
1019  							actionData.ActionFlags |= CSSM_TP_ACTION_ALLOW_EXPIRED; // (this also allows postdated certs)
1020  							continue;		// retry validation while tolerating expiration
1021  						}
1022  					}
1023  					if (checkfix41082220(result)) {
1024  						break; // success
1025  					}
1026  					Security::Syslog::error("SecStaticCode: verification failed (trust result %d, error %d)", trustResult, (int)result);
1027  					MacOSError::throwMe(result);
1028  				}
1029  			}
1030  
1031  			if (mSigningTimestamp) {
1032  				CFIndex rootix = CFArrayGetCount(mCertChain);
1033  				if (SecCertificateRef mainRoot = SecCertificateRef(CFArrayGetValueAtIndex(mCertChain, rootix-1)))
1034  					if (isAppleCA(mainRoot)) {
1035  						// impose policy: if the signature itself draws to Apple, then so must the timestamp signature
1036  						CFRef<CFArrayRef> tsCerts;
1037  						OSStatus result = CMSDecoderCopySignerTimestampCertificates(cms, 0, &tsCerts.aref());
1038  						if (result) {
1039  							Security::Syslog::error("SecStaticCode: could not get timestamp certificates (error %d)", (int)result);
1040  							MacOSError::check(result);
1041  						}
1042  						CFIndex tsn = CFArrayGetCount(tsCerts);
1043  						bool good = tsn > 0 && isAppleCA(SecCertificateRef(CFArrayGetValueAtIndex(tsCerts, tsn-1)));
1044  						if (!good) {
1045  							result = CSSMERR_TP_NOT_TRUSTED;
1046  							Security::Syslog::error("SecStaticCode: timestamp policy verification failed (error %d)", (int)result);
1047  							MacOSError::throwMe(result);
1048  						}
1049  					}
1050  			}
1051  
1052  			return actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED;
1053  		}
1054  
1055  	} else
1056  #endif
1057  	{
1058  		// Do some pre-verification initialization
1059  		CFDataRef sig = this->signature();
1060  		this->codeDirectory();	// load CodeDirectory (sets mDir)
1061  		mSigningTime = 0;	// "not present" marker (nobody could code sign on Jan 1, 2001 :-)
1062  
1063  		CFRef<CFDictionaryRef> attrs;
1064  		CFRef<CFArrayRef> vf_policies(createVerificationPolicies());
1065  
1066  		// Verify the CMS signature against mBaseDir (SHA1)
1067  		MacOSError::check(SecCMSVerifyCopyDataAndAttributes(sig, mBaseDir, vf_policies, &mTrust.aref(), NULL, &attrs.aref()));
1068  
1069  		// Copy the signing time
1070  		mSigningTime = SecTrustGetVerifyTime(mTrust);
1071  
1072  		// Validate the cert chain
1073  		SecTrustResultType trustResult;
1074  		MacOSError::check(SecTrustEvaluate(mTrust, &trustResult));
1075  
1076  		// retrieve auxiliary data bag and verify against current state
1077  		CFRef<CFDataRef> hashBag;
1078  		hashBag = CFDataRef(CFDictionaryGetValue(attrs, kSecCMSHashAgility));
1079  		if (hashBag) {
1080  			CFRef<CFDictionaryRef> hashDict = makeCFDictionaryFrom(hashBag);
1081  			CFArrayRef cdList = CFArrayRef(CFDictionaryGetValue(hashDict, CFSTR("cdhashes")));
1082  			CFArrayRef myCdList = this->cdHashes();
1083  			if (cdList == NULL || !CFEqual(cdList, myCdList))
1084  				MacOSError::throwMe(errSecCSSignatureFailed);
1085  		}
1086  
1087  		/*
1088  		 * Populate mCertChain with the certs.  If we failed validation, the
1089  		 * signer's cert will be checked installed provisioning profiles as an
1090  		 * alternative to verification against the policy for store-signed binaries
1091  		 */
1092  		mCertChain.take(copyCertChain(mTrust));
1093  
1094  		// Did we implicitly trust the signer?
1095  		mTrustedSigningCertChain = (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed);
1096  
1097  		return false; // XXX: Not checking for expired certs
1098  	}
1099  }
1100  
1101  #if TARGET_OS_OSX
1102  //
1103  // Return the TP policy used for signature verification.
1104  // This may be a simple SecPolicyRef or a CFArray of policies.
1105  // The caller owns the return value.
1106  //
1107  static SecPolicyRef makeRevocationPolicy(CFOptionFlags flags)
1108  {
1109  	CFRef<SecPolicyRef> policy(SecPolicyCreateRevocation(flags));
1110  	return policy.yield();
1111  }
1112  #endif
1113  
1114  bool SecStaticCode::validationCannotUseNetwork()
1115  {
1116  	bool blockNetwork = false;
1117  	bool validationEnablesNetwork = ((mValidationFlags & kSecCSAllowNetworkAccess) != 0);
1118  	bool validationDisablesNetwork = ((mValidationFlags & kSecCSNoNetworkAccess) != 0);
1119  
1120  	if (mNetworkEnabledByDefault) {
1121  		// If network is enabled by default, block it only if the flags explicitly block.
1122  		blockNetwork = validationDisablesNetwork;
1123  	} else {
1124  		// If network is disabled by default, block it if the flags don't explicitly enable it.
1125  		blockNetwork = !validationEnablesNetwork;
1126  	}
1127  	secinfo("staticCode", "SecStaticCode network allowed: %s", blockNetwork ? "NO" : "YES");
1128  	return blockNetwork;
1129  }
1130  
1131  CFArrayRef SecStaticCode::createVerificationPolicies()
1132  {
1133  	if (mValidationFlags & kSecCSUseSoftwareSigningCert) {
1134  		CFRef<SecPolicyRef> ssRef = SecPolicyCreateAppleSoftwareSigning();
1135  		return makeCFArray(1, ssRef.get());
1136  	}
1137  #if TARGET_OS_OSX
1138  	if (mValidationFlags & kSecCSApplyEmbeddedPolicy) {
1139  		CFRef<SecPolicyRef> iOSRef = SecPolicyCreateiPhoneApplicationSigning();
1140  		return makeCFArray(1, iOSRef.get());
1141  	}
1142  
1143  	CFRef<SecPolicyRef> core;
1144  	MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3,
1145  									&CSSMOID_APPLE_TP_CODE_SIGNING, &core.aref()));
1146  	if (validationCannotUseNetwork()) {
1147  		// Skips all revocation since they require network connectivity
1148  		// therefore annihilates kSecCSEnforceRevocationChecks if present
1149  		CFRef<SecPolicyRef> no_revoc = makeRevocationPolicy(kSecRevocationNetworkAccessDisabled);
1150  		return makeCFArray(2, core.get(), no_revoc.get());
1151  	}
1152  	else if (mValidationFlags & kSecCSEnforceRevocationChecks) {
1153  		// Add CRL and OCSP policies
1154  		CFRef<SecPolicyRef> revoc = makeRevocationPolicy(kSecRevocationUseAnyAvailableMethod);
1155  		return makeCFArray(2, core.get(), revoc.get());
1156  	} else {
1157  		return makeCFArray(1, core.get());
1158  	}
1159  #elif TARGET_OS_TV
1160  	CFRef<SecPolicyRef> tvOSRef = SecPolicyCreateAppleTVOSApplicationSigning();
1161  	return makeCFArray(1, tvOSRef.get());
1162  #else
1163  	CFRef<SecPolicyRef> iOSRef = SecPolicyCreateiPhoneApplicationSigning();
1164  	return makeCFArray(1, iOSRef.get());
1165  #endif
1166  
1167  }
1168  
1169  CFArrayRef SecStaticCode::createTimeStampingAndRevocationPolicies()
1170  {
1171  	CFRef<SecPolicyRef> tsPolicy = SecPolicyCreateAppleTimeStamping();
1172  #if TARGET_OS_OSX
1173  	if (validationCannotUseNetwork()) {
1174  		// Skips all revocation since they require network connectivity
1175  		// therefore annihilates kSecCSEnforceRevocationChecks if present
1176  		CFRef<SecPolicyRef> no_revoc = makeRevocationPolicy(kSecRevocationNetworkAccessDisabled);
1177  		return makeCFArray(2, tsPolicy.get(), no_revoc.get());
1178  	}
1179  	else if (mValidationFlags & kSecCSEnforceRevocationChecks) {
1180  		// Add CRL and OCSP policies
1181  		CFRef<SecPolicyRef> revoc = makeRevocationPolicy(kSecRevocationUseAnyAvailableMethod);
1182  		return makeCFArray(2, tsPolicy.get(), revoc.get());
1183  	}
1184  	else {
1185  		return makeCFArray(1, tsPolicy.get());
1186  	}
1187  #else
1188  	return makeCFArray(1, tsPolicy.get());
1189  #endif
1190  
1191  }
1192  
1193  CFArrayRef SecStaticCode::copyCertChain(SecTrustRef trust)
1194  {
1195  	SecCertificateRef leafCert = SecTrustGetCertificateAtIndex(trust, 0);
1196  	if (leafCert != NULL) {
1197  		CFIndex count = SecTrustGetCertificateCount(trust);
1198  
1199  		CFMutableArrayRef certs = CFArrayCreateMutable(kCFAllocatorDefault, count,
1200  													   &kCFTypeArrayCallBacks);
1201  
1202  		CFArrayAppendValue(certs, leafCert);
1203  		for (CFIndex i = 1; i < count; ++i) {
1204  			CFArrayAppendValue(certs, SecTrustGetCertificateAtIndex(trust, i));
1205  		}
1206  
1207  		return certs;
1208  	}
1209  	return NULL;
1210  }
1211  
1212  
1213  //
1214  // Validate a particular sealed, cached resource against its (special) CodeDirectory slot.
1215  // The resource must already have been placed in the cache.
1216  // This does NOT perform basic validation.
1217  //
1218  void SecStaticCode::validateComponent(CodeDirectory::SpecialSlot slot, OSStatus fail /* = errSecCSSignatureFailed */)
1219  {
1220  	assert(slot <= cdSlotMax);
1221  	CFDataRef data = mCache[slot];
1222  	assert(data);		// must be cached
1223  	if (data == CFDataRef(kCFNull)) {
1224  		if (codeDirectory()->slotIsPresent(-slot)) // was supposed to be there...
1225  				MacOSError::throwMe(fail);	// ... and is missing
1226  	} else {
1227  		if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), CFDataGetLength(data), -slot, false))
1228  			MacOSError::throwMe(fail);
1229  	}
1230  }
1231  
1232  
1233  //
1234  // Perform static validation of the main executable.
1235  // This reads the main executable from disk and validates it against the
1236  // CodeDirectory code slot array.
1237  // Note that this is NOT an in-memory validation, and is thus potentially
1238  // subject to timing attacks.
1239  //
1240  void SecStaticCode::validateExecutable()
1241  {
1242  	if (!validatedExecutable()) {
1243  		try {
1244  			DTRACK(CODESIGN_EVAL_STATIC_EXECUTABLE, this,
1245  				(char*)this->mainExecutablePath().c_str(), codeDirectory()->nCodeSlots);
1246  			const CodeDirectory *cd = this->codeDirectory();
1247  			if (!cd)
1248  				MacOSError::throwMe(errSecCSUnsigned);
1249  			AutoFileDesc fd(mainExecutablePath(), O_RDONLY);
1250  			fd.fcntl(F_NOCACHE, true);		// turn off page caching (one-pass)
1251  			if (Universal *fat = mRep->mainExecutableImage())
1252  				fd.seek(fat->archOffset());
1253  			size_t pageSize = cd->pageSize ? (1 << cd->pageSize) : 0;
1254  			size_t remaining = cd->signingLimit();
1255  			for (uint32_t slot = 0; slot < cd->nCodeSlots; ++slot) {
1256  				size_t thisPage = remaining;
1257  				if (pageSize)
1258  					thisPage = min(thisPage, pageSize);
1259  				__block bool good = true;
1260  				CodeDirectory::multipleHashFileData(fd, thisPage, hashAlgorithms(), ^(CodeDirectory::HashAlgorithm type, Security::DynamicHash *hasher) {
1261  					const CodeDirectory* cd = (const CodeDirectory*)CFDataGetBytePtr(mCodeDirectories[type]);
1262  					if (!hasher->verify(cd->getSlot(slot,
1263  													mValidationFlags & kSecCSValidatePEH)))
1264  						good = false;
1265  				});
1266  				if (!good) {
1267  					CODESIGN_EVAL_STATIC_EXECUTABLE_FAIL(this, (int)slot);
1268  					MacOSError::throwMe(errSecCSSignatureFailed);
1269  				}
1270  				remaining -= thisPage;
1271  			}
1272  			assert(remaining == 0);
1273  			mExecutableValidated = true;
1274  			mExecutableValidResult = errSecSuccess;
1275  		} catch (const CommonError &err) {
1276  			mExecutableValidated = true;
1277  			mExecutableValidResult = err.osStatus();
1278  			throw;
1279  		} catch (...) {
1280  			secinfo("staticCode", "%p executable validation threw non-common exception", this);
1281  			mExecutableValidated = true;
1282  			mExecutableValidResult = errSecCSInternalError;
1283  			Syslog::notice("code signing internal problem: unknown exception thrown by validation");
1284  			throw;
1285  		}
1286  	}
1287  	assert(validatedExecutable());
1288  	if (mExecutableValidResult != errSecSuccess)
1289  		MacOSError::throwMe(mExecutableValidResult);
1290  }
1291  
1292  //
1293  // Perform static validation of sealed resources and nested code.
1294  //
1295  // This performs a whole-code static resource scan and effectively
1296  // computes a concordance between what's on disk and what's in the ResourceDirectory.
1297  // Any unsanctioned difference causes an error.
1298  //
1299  unsigned SecStaticCode::estimateResourceWorkload()
1300  {
1301  	// workload estimate = number of sealed files
1302  	CFDictionaryRef sealedResources = resourceDictionary();
1303  	CFDictionaryRef files = cfget<CFDictionaryRef>(sealedResources, "files2");
1304  	if (files == NULL)
1305  		files = cfget<CFDictionaryRef>(sealedResources, "files");
1306  	return files ? unsigned(CFDictionaryGetCount(files)) : 0;
1307  }
1308  
1309  void SecStaticCode::validateResources(SecCSFlags flags)
1310  {
1311  	// do we have a superset of this requested validation cached?
1312  	bool doit = true;
1313  	if (mResourcesValidated) {	// have cached outcome
1314  		if (!(flags & kSecCSCheckNestedCode) || mResourcesDeep)	// was deep or need no deep scan
1315  			doit = false;
1316  	}
1317  
1318  	if (doit) {
1319  		string root = cfStringRelease(copyCanonicalPath());
1320  		bool itemIsOnRootFS = itemQualifiesForResourceExemption(root);
1321  		bool skipRootVolumeExceptions = (mValidationFlags & kSecCSSkipRootVolumeExceptions);
1322  		bool useRootFSPolicy = itemIsOnRootFS && !skipRootVolumeExceptions;
1323  
1324  		bool itemMightUseXattrFiles = pathFileSystemUsesXattrFiles(root.c_str());
1325  		bool skipXattrFiles = itemMightUseXattrFiles && (mValidationFlags & kSecCSSkipXattrFiles);
1326  
1327  		secinfo("staticCode", "performing resource validation for %s (%d, %d, %d, %d, %d)", root.c_str(),
1328  				itemIsOnRootFS, skipRootVolumeExceptions, useRootFSPolicy, itemMightUseXattrFiles, skipXattrFiles);
1329  
1330  		if (mLimitedAsync == NULL) {
1331  			bool runMultiThreaded = ((flags & kSecCSSingleThreaded) == kSecCSSingleThreaded) ? false :
1332  					(diskRep()->fd().mediumType() == kIOPropertyMediumTypeSolidStateKey);
1333  			mLimitedAsync = new LimitedAsync(runMultiThreaded);
1334  		}
1335  
1336  		try {
1337  			CFDictionaryRef rules;
1338  			CFDictionaryRef files;
1339  			uint32_t version;
1340  			if (!loadResources(rules, files, version))
1341  				return;		// validly no resources; nothing to do (ok)
1342  
1343  			// found resources, and they are sealed
1344  			DTRACK(CODESIGN_EVAL_STATIC_RESOURCES, this,
1345  				(char*)this->mainExecutablePath().c_str(), 0);
1346  
1347  			// scan through the resources on disk, checking each against the resourceDirectory
1348  			mResourcesValidContext = new CollectingContext(*this);		// collect all failures in here
1349  
1350  			// check for weak resource rules
1351  			bool strict = flags & kSecCSStrictValidate;
1352  			if (!useRootFSPolicy) {
1353  				if (strict) {
1354  					if (hasWeakResourceRules(rules, version, mAllowOmissions))
1355  						if (mTolerateErrors.find(errSecCSWeakResourceRules) == mTolerateErrors.end())
1356  							MacOSError::throwMe(errSecCSWeakResourceRules);
1357  					if (version == 1)
1358  						if (mTolerateErrors.find(errSecCSWeakResourceEnvelope) == mTolerateErrors.end())
1359  							MacOSError::throwMe(errSecCSWeakResourceEnvelope);
1360  				}
1361  			}
1362  
1363  			Dispatch::Group group;
1364  			Dispatch::Group &groupRef = group;  // (into block)
1365  
1366  			// scan through the resources on disk, checking each against the resourceDirectory
1367  			__block CFRef<CFMutableDictionaryRef> resourceMap = makeCFMutableDictionary(files);
1368  			string base = cfString(this->resourceBase());
1369  			ResourceBuilder resources(base, base, rules, strict, mTolerateErrors);
1370  			this->mResourceScope = &resources;
1371  			diskRep()->adjustResources(resources);
1372  
1373  			void (^unhandledScanner)(FTSENT *, uint32_t , const string, ResourceBuilder::Rule *) = nil;
1374  
1375  			if (isFlagSet(flags, kSecCSEnforceRevocationChecks)) {
1376  				unhandledScanner = ^(FTSENT *ent, uint32_t ruleFlags, const string relpath, ResourceBuilder::Rule *rule) {
1377  					bool userControlledRule = ((ruleFlags & ResourceBuilder::user_controlled) == ResourceBuilder::user_controlled);
1378  					secinfo("staticCode", "Visiting unhandled file: %d, %s", userControlledRule, relpath.c_str());
1379  					if (!userControlledRule) {
1380  						// No need to look at exemptions added by the runtime rule adjustments (ex. main executable).
1381  						return;
1382  					}
1383  
1384  					CFRef<CFURLRef> validationURL;
1385  					bool doValidation = false;
1386  					switch (ent->fts_info) {
1387  						case FTS_SL:
1388  							char resolved[PATH_MAX];
1389  							if (realpath(ent->fts_path, resolved)) {
1390  								doValidation = true;
1391  								validationURL.take(makeCFURL(resolved));
1392  								secinfo("staticCode", "Checking symlink target: %s", resolved);
1393  							} else {
1394  								secerror("realpath failed checking symlink: %d", errno);
1395  							}
1396  							break;
1397  						case FTS_F:
1398  							doValidation = true;
1399  							validationURL.take(makeCFURL(relpath, false, resourceBase()));
1400  							break;
1401  						default:
1402  							// Unexpected type for the unhandled scanner.
1403  							doValidation = false;
1404  							secerror("Unexpected scan input: %d, %s", ent->fts_info, relpath.c_str());
1405  							break;
1406  					}
1407  
1408  					if (doValidation) {
1409  						// Here we yield our reference to hand over to the block's CFRef object, which will
1410  						// hold it until the block is complete and also handle releasing in case of an exception.
1411  						CFURLRef transferURL = validationURL.yield();
1412  
1413  						void (^validate)() = ^{
1414  							CFRef<CFURLRef> localURL = transferURL;
1415  							AutoFileDesc fd(cfString(localURL), O_RDONLY, FileDesc::modeMissingOk);
1416  							checkRevocationOnNestedBinary(fd, localURL, flags);
1417  						};
1418  						mLimitedAsync->perform(groupRef, validate);
1419  					}
1420  				};
1421  			}
1422  
1423  			void (^validationScanner)(FTSENT *, uint32_t , const string, ResourceBuilder::Rule *) = ^(FTSENT *ent, uint32_t ruleFlags, const string relpath, ResourceBuilder::Rule *rule) {
1424  				CFDictionaryRemoveValue(resourceMap, CFTempString(relpath));
1425  				bool isSymlink = (ent->fts_info == FTS_SL);
1426  
1427  				void (^validate)() = ^{
1428  					bool needsValidation = true;
1429  
1430  					if (skipXattrFiles && pathIsValidXattrFile(cfString(resourceBase()) + "/" + relpath, "staticCode")) {
1431  						secinfo("staticCode", "resource validation on xattr file skipped: %s", relpath.c_str());
1432  						needsValidation = false;
1433  					}
1434  
1435  					if (useRootFSPolicy) {
1436  						CFRef<CFURLRef> itemURL = makeCFURL(relpath, false, resourceBase());
1437  						string itemPath = cfString(itemURL);
1438  						if (itemQualifiesForResourceExemption(itemPath)) {
1439  							secinfo("staticCode", "resource validation on root volume skipped: %s", itemPath.c_str());
1440  							needsValidation = false;
1441  						}
1442  					}
1443  
1444  					if (needsValidation) {
1445  						secinfo("staticCode", "performing resource validation on item: %s", relpath.c_str());
1446  						validateResource(files, relpath, isSymlink, *mResourcesValidContext, flags, version);
1447  					}
1448  					reportProgress();
1449  				};
1450  
1451  				mLimitedAsync->perform(groupRef, validate);
1452  			};
1453  
1454  			resources.scan(validationScanner, unhandledScanner);
1455  			group.wait();	// wait until all async resources have been validated as well
1456  
1457  			if (useRootFSPolicy) {
1458  				// It's ok to allow leftovers on the root filesystem for now.
1459  			} else {
1460  				// Look through the leftovers and make sure they're all properly optional resources.
1461  				unsigned leftovers = unsigned(CFDictionaryGetCount(resourceMap));
1462  				if (leftovers > 0) {
1463  					secinfo("staticCode", "%d sealed resource(s) not found in code", int(leftovers));
1464  					CFDictionaryApplyFunction(resourceMap, SecStaticCode::checkOptionalResource, mResourcesValidContext);
1465  				}
1466  			}
1467  
1468  			// now check for any errors found in the reporting context
1469  			mResourcesValidated = true;
1470  			mResourcesDeep = flags & kSecCSCheckNestedCode;
1471  			if (mResourcesValidContext->osStatus() != errSecSuccess)
1472  				mResourcesValidContext->throwMe();
1473  		} catch (const CommonError &err) {
1474  			mResourcesValidated = true;
1475  			mResourcesDeep = flags & kSecCSCheckNestedCode;
1476  			mResourcesValidResult = err.osStatus();
1477  			throw;
1478  		} catch (...) {
1479  			secinfo("staticCode", "%p executable validation threw non-common exception", this);
1480  			mResourcesValidated = true;
1481  			mResourcesDeep = flags & kSecCSCheckNestedCode;
1482  			mResourcesValidResult = errSecCSInternalError;
1483  			Syslog::notice("code signing internal problem: unknown exception thrown by validation");
1484  			throw;
1485  		}
1486  	}
1487  	assert(validatedResources());
1488  	if (mResourcesValidResult)
1489  		MacOSError::throwMe(mResourcesValidResult);
1490  	if (mResourcesValidContext->osStatus() != errSecSuccess)
1491  		mResourcesValidContext->throwMe();
1492  }
1493  
1494  
1495  bool SecStaticCode::loadResources(CFDictionaryRef& rules, CFDictionaryRef& files, uint32_t& version)
1496  {
1497  	// sanity first
1498  	CFDictionaryRef sealedResources = resourceDictionary();
1499  	if (this->resourceBase()) {	// disk has resources
1500  		if (sealedResources)
1501  			/* go to work below */;
1502  		else
1503  			MacOSError::throwMe(errSecCSResourcesNotFound);
1504  	} else {							// disk has no resources
1505  		if (sealedResources)
1506  			MacOSError::throwMe(errSecCSResourcesNotFound);
1507  		else
1508  			return false;					// no resources, not sealed - fine (no work)
1509  	}
1510  	
1511  	// use V2 resource seal if available, otherwise fall back to V1
1512  	if (CFDictionaryGetValue(sealedResources, CFSTR("files2"))) {	// have V2 signature
1513  		rules = cfget<CFDictionaryRef>(sealedResources, "rules2");
1514  		files = cfget<CFDictionaryRef>(sealedResources, "files2");
1515  		version = 2;
1516  	} else {	// only V1 available
1517  		rules = cfget<CFDictionaryRef>(sealedResources, "rules");
1518  		files = cfget<CFDictionaryRef>(sealedResources, "files");
1519  		version = 1;
1520  	}
1521  	if (!rules || !files)
1522  		MacOSError::throwMe(errSecCSResourcesInvalid);
1523  	return true;
1524  }
1525  	
1526  
1527  void SecStaticCode::checkOptionalResource(CFTypeRef key, CFTypeRef value, void *context)
1528  {
1529  	ValidationContext *ctx = static_cast<ValidationContext *>(context);
1530  	ResourceSeal seal(value);
1531  	if (!seal.optional()) {
1532  		if (key && CFGetTypeID(key) == CFStringGetTypeID()) {
1533  			CFTempURL tempURL(CFStringRef(key), false, ctx->code.resourceBase());
1534  			if (!tempURL.get()) {
1535  				ctx->reportProblem(errSecCSBadDictionaryFormat, kSecCFErrorResourceSeal, key);
1536  			} else {
1537  				ctx->reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing, tempURL);
1538  			}
1539  		} else {
1540  			ctx->reportProblem(errSecCSBadResource, kSecCFErrorResourceSeal, key);
1541  		}
1542  	}
1543  }
1544  
1545  
1546  static bool isOmitRule(CFTypeRef value)
1547  {
1548  	if (CFGetTypeID(value) == CFBooleanGetTypeID())
1549  		return value == kCFBooleanFalse;
1550  	CFDictionary rule(value, errSecCSResourceRulesInvalid);
1551  	return rule.get<CFBooleanRef>("omit") == kCFBooleanTrue;
1552  }
1553  
1554  bool SecStaticCode::hasWeakResourceRules(CFDictionaryRef rulesDict, uint32_t version, CFArrayRef allowedOmissions)
1555  {
1556  	// compute allowed omissions
1557  	CFRef<CFArrayRef> defaultOmissions = this->diskRep()->allowedResourceOmissions();
1558  	if (!defaultOmissions) {
1559  		Syslog::notice("code signing internal problem: diskRep returned no allowedResourceOmissions");
1560  		MacOSError::throwMe(errSecCSInternalError);
1561  	}
1562  	CFRef<CFMutableArrayRef> allowed = CFArrayCreateMutableCopy(NULL, 0, defaultOmissions);
1563  	if (allowedOmissions)
1564  		CFArrayAppendArray(allowed, allowedOmissions, CFRangeMake(0, CFArrayGetCount(allowedOmissions)));
1565  	CFRange range = CFRangeMake(0, CFArrayGetCount(allowed));
1566  
1567  	// check all resource rules for weakness
1568  	string catchAllRule = (version == 1) ? "^Resources/" : "^.*";
1569  	__block bool coversAll = false;
1570  	__block bool forbiddenOmission = false;
1571  	CFArrayRef allowedRef = allowed.get();	// (into block)
1572  	CFDictionary rules(rulesDict, errSecCSResourceRulesInvalid);
1573  	rules.apply(^(CFStringRef key, CFTypeRef value) {
1574  		string pattern = cfString(key, errSecCSResourceRulesInvalid);
1575  		if (pattern == catchAllRule && value == kCFBooleanTrue) {
1576  			coversAll = true;
1577  			return;
1578  		}
1579  		if (isOmitRule(value))
1580  			forbiddenOmission |= !CFArrayContainsValue(allowedRef, range, key);
1581  	});
1582  
1583  	return !coversAll || forbiddenOmission;
1584  }
1585  
1586  
1587  //
1588  // Load, validate, cache, and return CFDictionary forms of sealed resources.
1589  //
1590  CFDictionaryRef SecStaticCode::infoDictionary()
1591  {
1592  	if (!mInfoDict) {
1593  		mInfoDict.take(getDictionary(cdInfoSlot, errSecCSInfoPlistFailed));
1594  		secinfo("staticCode", "%p loaded InfoDict %p", this, mInfoDict.get());
1595  	}
1596  	return mInfoDict;
1597  }
1598  
1599  CFDictionaryRef SecStaticCode::entitlements()
1600  {
1601  	if (!mEntitlements) {
1602  		validateDirectory();
1603  		if (CFDataRef entitlementData = component(cdEntitlementSlot)) {
1604  			validateComponent(cdEntitlementSlot);
1605  			const EntitlementBlob *blob = reinterpret_cast<const EntitlementBlob *>(CFDataGetBytePtr(entitlementData));
1606  			if (blob->validateBlob()) {
1607  				mEntitlements.take(blob->entitlements());
1608  				secinfo("staticCode", "%p loaded Entitlements %p", this, mEntitlements.get());
1609  			}
1610  			// we do not consider a different blob type to be an error. We think it's a new format we don't understand
1611  		}
1612  	}
1613  	return mEntitlements;
1614  }
1615  
1616  CFDictionaryRef SecStaticCode::resourceDictionary(bool check /* = true */)
1617  {
1618  	if (mResourceDict)	// cached
1619  		return mResourceDict;
1620  	if (CFRef<CFDictionaryRef> dict = getDictionary(cdResourceDirSlot, check))
1621  		if (cfscan(dict, "{rules=%Dn,files=%Dn}")) {
1622  			secinfo("staticCode", "%p loaded ResourceDict %p",
1623  				this, mResourceDict.get());
1624  			return mResourceDict = dict;
1625  		}
1626  	// bad format
1627  	return NULL;
1628  }
1629  	
1630  	
1631  CFDataRef SecStaticCode::copyComponent(CodeDirectory::SpecialSlot slot, CFDataRef hash)
1632  {
1633  	const CodeDirectory* cd = this->codeDirectory();
1634  	if (CFCopyRef<CFDataRef> component = this->component(slot)) {
1635  		if (hash) {
1636  			const void *slotHash = cd->getSlot(slot, false);
1637  			if (cd->hashSize != CFDataGetLength(hash) || 0 != memcmp(slotHash, CFDataGetBytePtr(hash), cd->hashSize)) {
1638  				Syslog::notice("copyComponent hash mismatch slot %d length %d", slot, int(CFDataGetLength(hash)));
1639  				return NULL;	// mismatch
1640  			}
1641  		}
1642  		return component.yield();
1643  	}
1644  	return NULL;
1645  }
1646  
1647  
1648  
1649  //
1650  // Load and cache the resource directory base.
1651  // Note that the base is optional for each DiskRep.
1652  //
1653  CFURLRef SecStaticCode::resourceBase()
1654  {
1655  	if (!mGotResourceBase) {
1656  		string base = mRep->resourcesRootPath();
1657  		if (!base.empty())
1658  			mResourceBase.take(makeCFURL(base, true));
1659  		mGotResourceBase = true;
1660  	}
1661  	return mResourceBase;
1662  }
1663  
1664  
1665  //
1666  // Load a component, validate it, convert it to a CFDictionary, and return that.
1667  // This will force load and validation, which means that it will perform basic
1668  // validation if it hasn't been done yet.
1669  //
1670  CFDictionaryRef SecStaticCode::getDictionary(CodeDirectory::SpecialSlot slot, bool check /* = true */)
1671  {
1672  	if (check)
1673  		validateDirectory();
1674  	if (CFDataRef infoData = component(slot)) {
1675  		validateComponent(slot);
1676  		if (CFDictionaryRef dict = makeCFDictionaryFrom(infoData))
1677  			return dict;
1678  		else
1679  			MacOSError::throwMe(errSecCSBadDictionaryFormat);
1680  	}
1681  	return NULL;
1682  }
1683  
1684  //
1685  //
1686  //
1687  CFDictionaryRef SecStaticCode::copyDiskRepInformation()
1688  {
1689  	return mRep->copyDiskRepInformation();
1690  }
1691  
1692  bool SecStaticCode::checkfix30814861(string path, bool addition) {
1693  	// <rdar://problem/30814861> v2 resource rules don't match v1 resource rules
1694  
1695  	//// Condition 1: Is the app an iOS app that was built with an SDK lower than 9.0?
1696  
1697  	// We started signing correctly in 2014, 9.0 was first seeded mid-2016.
1698  
1699  	CFRef<CFDictionaryRef> inf = copyDiskRepInformation();
1700  	try {
1701  		CFDictionary info(inf.get(), errSecCSNotSupported);
1702  		uint32_t platform =
1703  			cfNumber(info.get<CFNumberRef>(kSecCodeInfoDiskRepVersionPlatform, errSecCSNotSupported), 0);
1704  		uint32_t sdkVersion =
1705  			cfNumber(info.get<CFNumberRef>(kSecCodeInfoDiskRepVersionSDK, errSecCSNotSupported), 0);
1706  
1707  		if (platform != PLATFORM_IOS || sdkVersion >= 0x00090000) {
1708  			return false;
1709  		}
1710  	} catch (const MacOSError &error) {
1711  		return false;
1712  	}
1713  
1714  	//// Condition 2: Is it a .sinf/.supf/.supp file at the right location?
1715  
1716  	static regex_t pathre_sinf;
1717  	static regex_t pathre_supp_supf;
1718  	static dispatch_once_t once;
1719  
1720  	dispatch_once(&once, ^{
1721  		os_assert_zero(regcomp(&pathre_sinf,
1722  							   "^(Frameworks/[^/]+\\.framework/|PlugIns/[^/]+\\.appex/|())SC_Info/[^/]+\\.sinf$",
1723  							   REG_EXTENDED | REG_NOSUB));
1724  		os_assert_zero(regcomp(&pathre_supp_supf,
1725  							   "^(Frameworks/[^/]+\\.framework/|PlugIns/[^/]+\\.appex/|())SC_Info/[^/]+\\.(supf|supp)$",
1726  							   REG_EXTENDED | REG_NOSUB));
1727  	});
1728  
1729  	// .sinf is added, .supf/.supp are modified.
1730  	const regex_t &pathre = addition ? pathre_sinf : pathre_supp_supf;
1731  
1732  	const int result = regexec(&pathre, path.c_str(), 0, NULL, 0);
1733  
1734  	if (result == REG_NOMATCH) {
1735  		return false;
1736  	} else if (result != 0) {
1737  		// Huh?
1738  		secerror("unexpected regexec result %d for path '%s'", result, path.c_str());
1739  		return false;
1740  	}
1741  
1742  	//// Condition 3: Do the v1 rules actually exclude the file?
1743  
1744  	dispatch_once(&mCheckfix30814861builder1_once, ^{
1745  		// Create the v1 resource builder lazily.
1746  		CFDictionaryRef rules1 = cfget<CFDictionaryRef>(resourceDictionary(), "rules");
1747  		const string base = cfString(resourceBase());
1748  
1749  		mCheckfix30814861builder1 = new ResourceBuilder(base, base, rules1, false, mTolerateErrors);
1750  	});
1751  
1752  	ResourceBuilder::Rule const * const matchingRule = mCheckfix30814861builder1->findRule(path);
1753  
1754  	if (matchingRule == NULL || !(matchingRule->flags & ResourceBuilder::omitted)) {
1755  		return false;
1756  	}
1757  
1758  	//// All matched, this file is a check-fixed sinf/supf/supp.
1759  
1760  	return true;
1761  
1762  }
1763  
1764  void SecStaticCode::validateResource(CFDictionaryRef files, string path, bool isSymlink, ValidationContext &ctx, SecCSFlags flags, uint32_t version)
1765  {
1766  	if (!resourceBase())	// no resources in DiskRep
1767  		MacOSError::throwMe(errSecCSResourcesNotFound);
1768  	CFRef<CFURLRef> fullpath = makeCFURL(path, false, resourceBase());
1769  	if (version > 1 && ((flags & (kSecCSStrictValidate|kSecCSRestrictSidebandData)) == (kSecCSStrictValidate|kSecCSRestrictSidebandData))) {
1770  		AutoFileDesc fd(cfString(fullpath));
1771  		if (fd.hasExtendedAttribute(XATTR_RESOURCEFORK_NAME) || fd.hasExtendedAttribute(XATTR_FINDERINFO_NAME))
1772  			ctx.reportProblem(errSecCSInvalidAssociatedFileData, kSecCFErrorResourceSideband, fullpath);
1773  	}
1774  	if (CFTypeRef file = CFDictionaryGetValue(files, CFTempString(path))) {
1775  		ResourceSeal seal(file);
1776  		const ResourceSeal& rseal = seal;
1777  		if (seal.nested()) {
1778  			if (isSymlink) {
1779  				return ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // changed type
1780  			}
1781  			string suffix = ".framework";
1782  			bool isFramework = (path.length() > suffix.length()) &&
1783  							   (path.compare(path.length()-suffix.length(), suffix.length(), suffix) == 0);
1784  			validateNestedCode(fullpath, seal, flags, isFramework);
1785  		} else if (seal.link()) {
1786  			if (!isSymlink) {
1787  				return ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // changed type
1788  			}
1789  			validateSymlinkResource(cfString(fullpath), cfString(seal.link()), ctx, flags);
1790  		} else if (seal.hash(hashAlgorithm())) {	// genuine file
1791  			if (isSymlink) {
1792  				return ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // changed type
1793  			}
1794  			AutoFileDesc fd(cfString(fullpath), O_RDONLY, FileDesc::modeMissingOk);	// open optional file
1795  			if (fd) {
1796  				__block bool good = true;
1797  				CodeDirectory::multipleHashFileData(fd, 0, hashAlgorithms(), ^(CodeDirectory::HashAlgorithm type, Security::DynamicHash *hasher) {
1798  					if (!hasher->verify(rseal.hash(type)))
1799  						good = false;
1800  				});
1801  				if (!good) {
1802  					if (version == 2 && checkfix30814861(path, false)) {
1803  						secinfo("validateResource", "%s check-fixed (altered).", path.c_str());
1804  					} else {
1805  						ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // altered
1806  					}
1807  				}
1808  
1809  				if (good && isFlagSet(flags, kSecCSEnforceRevocationChecks)) {
1810  					checkRevocationOnNestedBinary(fd, fullpath, flags);
1811  				}
1812  			} else {
1813  				if (!seal.optional()) {
1814  					ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing, fullpath); // was sealed but is now missing
1815  				} else {
1816  					return;			// validly missing
1817  				}
1818  			}
1819  		} else {
1820  			ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // changed type
1821  		}
1822  		return;
1823  	}
1824  	if (version == 1) {		// version 1 ignores symlinks altogether
1825  		char target[PATH_MAX];
1826  		if (::readlink(cfString(fullpath).c_str(), target, sizeof(target)) > 0)
1827  			return;
1828  	}
1829  	if (version == 2 && checkfix30814861(path, true)) {
1830  		secinfo("validateResource", "%s check-fixed (added).", path.c_str());
1831  	} else {
1832  		ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAdded, CFTempURL(path, false, resourceBase()));
1833  	}
1834  }
1835  
1836  void SecStaticCode::validatePlainMemoryResource(string path, CFDataRef fileData, SecCSFlags flags)
1837  {
1838  	CFDictionaryRef rules;
1839  	CFDictionaryRef files;
1840  	uint32_t version;
1841  	if (!loadResources(rules, files, version))
1842  		MacOSError::throwMe(errSecCSResourcesNotFound);		// no resources sealed; this can't be right
1843  	if (CFTypeRef file = CFDictionaryGetValue(files, CFTempString(path))) {
1844  		ResourceSeal seal(file);
1845  		const Byte *sealHash = seal.hash(hashAlgorithm());
1846  		if (sealHash) {
1847  			if (codeDirectory()->verifyMemoryContent(fileData, sealHash))
1848  				return;		// success
1849  		}
1850  	}
1851  	MacOSError::throwMe(errSecCSBadResource);
1852  }
1853  	
1854  void SecStaticCode::validateSymlinkResource(std::string fullpath, std::string seal, ValidationContext &ctx, SecCSFlags flags)
1855  {
1856  	static const char* const allowedDestinations[] = {
1857  		"/System/",
1858  		"/Library/",
1859  		NULL
1860  	};
1861  	char target[PATH_MAX];
1862  	ssize_t len = ::readlink(fullpath.c_str(), target, sizeof(target)-1);
1863  	if (len < 0)
1864  		UnixError::check(-1);
1865  	target[len] = '\0';
1866  	std::string fulltarget = target;
1867  	if (target[0] != '/') {
1868  		size_t lastSlash = fullpath.rfind('/');
1869  		fulltarget = fullpath.substr(0, lastSlash) + '/' + target;
1870  	}
1871  	if (seal != target) {
1872  		ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, CFTempString(fullpath));
1873  		return;
1874  	}
1875  	if ((mValidationFlags & (kSecCSStrictValidate|kSecCSRestrictSymlinks)) == (kSecCSStrictValidate|kSecCSRestrictSymlinks)) {
1876  		char resolved[PATH_MAX];
1877  		if (realpath(fulltarget.c_str(), resolved)) {
1878  			assert(resolved[0] == '/');
1879  			size_t rlen = strlen(resolved);
1880  			if (target[0] == '/') {
1881  				// absolute symlink; only allow absolute links to system locations
1882  				for (const char* const* pathp = allowedDestinations; *pathp; pathp++) {
1883  					size_t dlen = strlen(*pathp);
1884  					if (rlen > dlen && strncmp(resolved, *pathp, dlen) == 0)
1885  					return;		// target inside /System, deemed okay
1886  				}
1887  			} else {
1888  				// everything else must be inside the bundle(s)
1889  				for (const SecStaticCode* code = this; code; code = code->mOuterScope) {
1890  					string root = code->mResourceScope->root();
1891  					if (strncmp(resolved, root.c_str(), root.size()) == 0) {
1892  						if (code->mResourceScope->includes(resolved + root.length() + 1))
1893  							return;		// located in resource stack && included in envelope
1894  						else
1895  							break;		// located but excluded from envelope (deny)
1896  					}
1897  				}
1898  			}
1899  		}
1900  		// if we fell through, flag a symlink error
1901  		if (mTolerateErrors.find(errSecCSInvalidSymlink) == mTolerateErrors.end())
1902  			ctx.reportProblem(errSecCSInvalidSymlink, kSecCFErrorResourceAltered, CFTempString(fullpath));
1903  	}
1904  }
1905  
1906  /// Uses the provided file descriptor to check if the file is macho, and if so validates the file at the url as a binary to check for a revoked certificate.
1907  void SecStaticCode::checkRevocationOnNestedBinary(UnixPlusPlus::FileDesc &fd, CFURLRef url, SecCSFlags flags)
1908  {
1909  #if TARGET_OS_OSX
1910  	secinfo("staticCode", "validating embedded resource: %@", url);
1911  
1912  	try {
1913  		SecPointer<SecStaticCode> code;
1914  
1915  		if (MachORep::candidate(fd)) {
1916  			DiskRep *rep = new MachORep(cfString(url).c_str(), NULL);
1917  			if (rep) {
1918  				code = new SecStaticCode(rep);
1919  			}
1920  		}
1921  
1922  		if (code) {
1923  			code->initializeFromParent(*this);
1924  			code->setValidationFlags(flags);
1925  			// Validate just the code directory, which performs signature validation.
1926  			code->validateDirectory();
1927  			secinfo("staticCode", "successfully validated nested resource binary: %@", url);
1928  		}
1929  	} catch (const MacOSError &err) {
1930  		if (err.error == CSSMERR_TP_CERT_REVOKED) {
1931  			secerror("Rejecting binary with revoked certificate: %@", url);
1932  			throw;
1933  		} else {
1934  			// Any other errors, but only revocation checks are fatal so just continue.
1935  			secinfo("staticCode", "Found unexpected error other error validating resource binary: %d, %@", err.error, url);
1936  		}
1937  	}
1938  #else
1939  	// This type of resource checking doesn't make sense on embedded devices right now, so just do nothing.
1940  	return;
1941  #endif // TARGET_OS_OSX
1942  }
1943  
1944  void SecStaticCode::validateNestedCode(CFURLRef path, const ResourceSeal &seal, SecCSFlags flags, bool isFramework)
1945  {
1946  	CFRef<SecRequirementRef> req;
1947  	if (SecRequirementCreateWithString(seal.requirement(), kSecCSDefaultFlags, &req.aref()))
1948  		MacOSError::throwMe(errSecCSResourcesInvalid);
1949  
1950  	// recursively verify this nested code
1951  	try {
1952  		if (!(flags & kSecCSCheckNestedCode)) {
1953  			flags |= kSecCSBasicValidateOnly | kSecCSQuickCheck;
1954  		}
1955  		SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(cfString(path)));
1956  		code->initializeFromParent(*this);
1957  		code->staticValidate(flags & (~kSecCSRestrictToAppLike), SecRequirement::required(req));
1958  
1959  		if (isFramework && (flags & kSecCSStrictValidate))
1960  			try {
1961  				validateOtherVersions(path, flags & (~kSecCSRestrictToAppLike), req, code);
1962  			} catch (const CSError &err) {
1963  				MacOSError::throwMe(errSecCSBadFrameworkVersion);
1964  			} catch (const MacOSError &err) {
1965  				MacOSError::throwMe(errSecCSBadFrameworkVersion);
1966  			}
1967  
1968  	} catch (CSError &err) {
1969  		if (err.error == errSecCSReqFailed) {
1970  			mResourcesValidContext->reportProblem(errSecCSBadNestedCode, kSecCFErrorResourceAltered, path);
1971  			return;
1972  		}
1973  		err.augment(kSecCFErrorPath, path);
1974  		throw;
1975  	} catch (const MacOSError &err) {
1976  		if (err.error == errSecCSReqFailed) {
1977  			mResourcesValidContext->reportProblem(errSecCSBadNestedCode, kSecCFErrorResourceAltered, path);
1978  			return;
1979  		}
1980  		CSError::throwMe(err.error, kSecCFErrorPath, path);
1981  	}
1982  }
1983  
1984  void SecStaticCode::validateOtherVersions(CFURLRef path, SecCSFlags flags, SecRequirementRef req, SecStaticCode *code)
1985  {
1986  	// Find out what current points to and do not revalidate
1987  	std::string mainPath = cfStringRelease(code->diskRep()->copyCanonicalPath());
1988  
1989  	char main_path[PATH_MAX];
1990  	bool foundTarget = false;
1991  
1992  	/* If it failed to get the target of the symlink, do not fail. It is a performance loss,
1993  	 not a security hole */
1994  	if (realpath(mainPath.c_str(), main_path) != NULL)
1995  		foundTarget = true;
1996  
1997  	std::ostringstream versionsPath;
1998  	versionsPath << cfString(path) << "/Versions/";
1999  
2000  	DirScanner scanner(versionsPath.str());
2001  
2002  	if (scanner.initialized()) {
2003  		struct dirent *entry = NULL;
2004  		while ((entry = scanner.getNext()) != NULL) {
2005  			std::ostringstream fullPath;
2006  
2007  			if (entry->d_type != DT_DIR || strcmp(entry->d_name, "Current") == 0)
2008  				continue;
2009  
2010  			fullPath << versionsPath.str() << entry->d_name;
2011  
2012  			char real_full_path[PATH_MAX];
2013  			if (realpath(fullPath.str().c_str(), real_full_path) == NULL)
2014  				UnixError::check(-1);
2015  
2016  			// Do case insensitive comparions because realpath() was called for both paths
2017  			if (foundTarget && strcmp(main_path, real_full_path) == 0)
2018  				continue;
2019  
2020  			SecPointer<SecStaticCode> frameworkVersion = new SecStaticCode(DiskRep::bestGuess(real_full_path));
2021  			frameworkVersion->initializeFromParent(*this);
2022  			frameworkVersion->staticValidate(flags, SecRequirement::required(req));
2023  		}
2024  	}
2025  }
2026  
2027  
2028  //
2029  // Test a CodeDirectory flag.
2030  // Returns false if there is no CodeDirectory.
2031  // May throw if the CodeDirectory is present but somehow invalid.
2032  //
2033  bool SecStaticCode::flag(uint32_t tested)
2034  {
2035  	if (const CodeDirectory *cd = this->codeDirectory(false))
2036  		return cd->flags & tested;
2037  	else
2038  		return false;
2039  }
2040  
2041  
2042  //
2043  // Retrieve the full SuperBlob containing all internal requirements.
2044  //
2045  const Requirements *SecStaticCode::internalRequirements()
2046  {
2047  	if (CFDataRef reqData = component(cdRequirementsSlot)) {
2048  		const Requirements *req = (const Requirements *)CFDataGetBytePtr(reqData);
2049  		if (!req->validateBlob())
2050  			MacOSError::throwMe(errSecCSReqInvalid);
2051  		return req;
2052  	} else
2053  		return NULL;
2054  }
2055  
2056  
2057  //
2058  // Retrieve a particular internal requirement by type.
2059  //
2060  const Requirement *SecStaticCode::internalRequirement(SecRequirementType type)
2061  {
2062  	if (const Requirements *reqs = internalRequirements())
2063  		return reqs->find<Requirement>(type);
2064  	else
2065  		return NULL;
2066  }
2067  
2068  
2069  //
2070  // Return the Designated Requirement (DR). This can be either explicit in the
2071  // Internal Requirements component, or implicitly generated on demand here.
2072  // Note that an explicit DR may have been implicitly generated at signing time;
2073  // we don't distinguish this case.
2074  //
2075  const Requirement *SecStaticCode::designatedRequirement()
2076  {
2077  	if (const Requirement *req = internalRequirement(kSecDesignatedRequirementType)) {
2078  		return req;		// explicit in signing data
2079  	} else {
2080  		if (!mDesignatedReq)
2081  			mDesignatedReq = defaultDesignatedRequirement();
2082  		return mDesignatedReq;
2083  	}
2084  }
2085  
2086  
2087  //
2088  // Generate the default Designated Requirement (DR) for this StaticCode.
2089  // Ignore any explicit DR it may contain.
2090  //
2091  const Requirement *SecStaticCode::defaultDesignatedRequirement()
2092  {
2093  	if (flag(kSecCodeSignatureAdhoc)) {
2094  		// adhoc signature: return a cdhash requirement for all architectures
2095  		__block Requirement::Maker maker;
2096  		Requirement::Maker::Chain chain(maker, opOr);
2097  
2098  		// insert cdhash requirement for all architectures
2099  		__block CFRef<CFMutableArrayRef> allHashes = CFArrayCreateMutableCopy(NULL, 0, this->cdHashes());
2100  		handleOtherArchitectures(^(SecStaticCode *other) {
2101  			CFArrayRef hashes = other->cdHashes();
2102  			CFArrayAppendArray(allHashes, hashes, CFRangeMake(0, CFArrayGetCount(hashes)));
2103  		});
2104  		CFIndex count = CFArrayGetCount(allHashes);
2105  		for (CFIndex n = 0; n < count; ++n) {
2106  			chain.add();
2107  			maker.cdhash(CFDataRef(CFArrayGetValueAtIndex(allHashes, n)));
2108  		}
2109  		return maker.make();
2110  	} else {
2111  #if TARGET_OS_OSX
2112  		// full signature: Gin up full context and let DRMaker do its thing
2113  		validateDirectory();		// need the cert chain
2114  		CFRef<CFDateRef> secureTimestamp;
2115  		if (CFAbsoluteTime time = this->signingTimestamp()) {
2116  			secureTimestamp.take(CFDateCreate(NULL, time));
2117  		}
2118  		Requirement::Context context(this->certificates(),
2119  			this->infoDictionary(),
2120  			this->entitlements(),
2121  			this->identifier(),
2122  			this->codeDirectory(),
2123  			NULL,
2124  			kSecCodeSignatureNoHash,
2125  			false,
2126  			secureTimestamp,
2127  			this->teamID()
2128  		);
2129  		return DRMaker(context).make();
2130  #else
2131          MacOSError::throwMe(errSecCSUnimplemented);
2132  #endif
2133  	}
2134  }
2135  
2136  
2137  //
2138  // Validate a SecStaticCode against the internal requirement of a particular type.
2139  //
2140  void SecStaticCode::validateRequirements(SecRequirementType type, SecStaticCode *target,
2141  	OSStatus nullError /* = errSecSuccess */)
2142  {
2143  	DTRACK(CODESIGN_EVAL_STATIC_INTREQ, this, type, target, nullError);
2144  	if (const Requirement *req = internalRequirement(type))
2145  		target->validateRequirement(req, nullError ? nullError : errSecCSReqFailed);
2146  	else if (nullError)
2147  		MacOSError::throwMe(nullError);
2148  	else
2149  		/* accept it */;
2150  }
2151  
2152  //
2153  // Validate this StaticCode against an external Requirement
2154  //
2155  bool SecStaticCode::satisfiesRequirement(const Requirement *req, OSStatus failure)
2156  {
2157  	bool result = false;
2158  	assert(req);
2159  	validateDirectory();
2160  	CFRef<CFDateRef> secureTimestamp;
2161  	if (CFAbsoluteTime time = this->signingTimestamp()) {
2162  		secureTimestamp.take(CFDateCreate(NULL, time));
2163  	}
2164  	result = req->validates(Requirement::Context(mCertChain, infoDictionary(), entitlements(),
2165  												 codeDirectory()->identifier(), codeDirectory(),
2166  												 NULL, kSecCodeSignatureNoHash, mRep->appleInternalForcePlatform(),
2167  												 secureTimestamp, teamID()),
2168  							failure);
2169  	return result;
2170  }
2171  
2172  void SecStaticCode::validateRequirement(const Requirement *req, OSStatus failure)
2173  {
2174  	if (!this->satisfiesRequirement(req, failure))
2175  		MacOSError::throwMe(failure);
2176  }
2177  
2178  //
2179  // Retrieve one certificate from the cert chain.
2180  // Positive and negative indices can be used:
2181  //    [ leaf, intermed-1, ..., intermed-n, anchor ]
2182  //        0       1       ...     -2         -1
2183  // Returns NULL if unavailable for any reason.
2184  //
2185  SecCertificateRef SecStaticCode::cert(int ix)
2186  {
2187  	validateDirectory();		// need cert chain
2188  	if (mCertChain) {
2189  		CFIndex length = CFArrayGetCount(mCertChain);
2190  		if (ix < 0)
2191  			ix += length;
2192  		if (ix >= 0 && ix < length)
2193  			return SecCertificateRef(CFArrayGetValueAtIndex(mCertChain, ix));
2194  	}
2195  	return NULL;
2196  }
2197  
2198  CFArrayRef SecStaticCode::certificates()
2199  {
2200  	validateDirectory();		// need cert chain
2201  	return mCertChain;
2202  }
2203  
2204  
2205  //
2206  // Gather (mostly) API-official information about this StaticCode.
2207  //
2208  // This method lives in the twilight between the API and internal layers,
2209  // since it generates API objects (Sec*Refs) for return.
2210  //
2211  CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
2212  {
2213  	//
2214  	// Start with the pieces that we return even for unsigned code.
2215  	// This makes Sec[Static]CodeRefs useful as API-level replacements
2216  	// of our internal OSXCode objects.
2217  	//
2218  	CFRef<CFMutableDictionaryRef> dict = makeCFMutableDictionary(1,
2219  		kSecCodeInfoMainExecutable, CFTempURL(this->mainExecutablePath()).get()
2220  	);
2221  
2222  	//
2223  	// If we're not signed, this is all you get
2224  	//
2225  	if (!this->isSigned())
2226  		return dict.yield();
2227  
2228  	//
2229  	// Add the generic attributes that we always include
2230  	//
2231  	CFDictionaryAddValue(dict, kSecCodeInfoIdentifier, CFTempString(this->identifier()));
2232  	CFDictionaryAddValue(dict, kSecCodeInfoFlags, CFTempNumber(this->codeDirectory(false)->flags.get()));
2233  	CFDictionaryAddValue(dict, kSecCodeInfoFormat, CFTempString(this->format()));
2234  	CFDictionaryAddValue(dict, kSecCodeInfoSource, CFTempString(this->signatureSource()));
2235  	CFDictionaryAddValue(dict, kSecCodeInfoUnique, this->cdHash());
2236  	CFDictionaryAddValue(dict, kSecCodeInfoCdHashes, this->cdHashes());
2237  	CFDictionaryAddValue(dict, kSecCodeInfoCdHashesFull, this->cdHashesFull());
2238  	const CodeDirectory* cd = this->codeDirectory(false);
2239  	CFDictionaryAddValue(dict, kSecCodeInfoDigestAlgorithm, CFTempNumber(cd->hashType));
2240  	CFRef<CFArrayRef> digests = makeCFArrayFrom(^CFTypeRef(CodeDirectory::HashAlgorithm type) { return CFTempNumber(type); }, hashAlgorithms());
2241  	CFDictionaryAddValue(dict, kSecCodeInfoDigestAlgorithms, digests);
2242  	if (cd->platform)
2243  		CFDictionaryAddValue(dict, kSecCodeInfoPlatformIdentifier, CFTempNumber(cd->platform));
2244  	if (cd->runtimeVersion()) {
2245  		CFDictionaryAddValue(dict, kSecCodeInfoRuntimeVersion, CFTempNumber(cd->runtimeVersion()));
2246  	}
2247  
2248  	//
2249  	// Deliver any Info.plist only if it looks intact
2250  	//
2251  	try {
2252  		if (CFDictionaryRef info = this->infoDictionary())
2253  			CFDictionaryAddValue(dict, kSecCodeInfoPList, info);
2254  	} catch (...) { }		// don't deliver Info.plist if questionable
2255  
2256  	//
2257  	// kSecCSSigningInformation adds information about signing certificates and chains
2258  	//
2259  	if (flags & kSecCSSigningInformation)
2260  		try {
2261  			if (CFDataRef sig = this->signature())
2262  				CFDictionaryAddValue(dict, kSecCodeInfoCMS, sig);
2263  			if (const char *teamID = this->teamID())
2264  				CFDictionaryAddValue(dict, kSecCodeInfoTeamIdentifier, CFTempString(teamID));
2265  			if (mTrust)
2266  				CFDictionaryAddValue(dict, kSecCodeInfoTrust, mTrust);
2267  			if (CFArrayRef certs = this->certificates())
2268  				CFDictionaryAddValue(dict, kSecCodeInfoCertificates, certs);
2269  			if (CFAbsoluteTime time = this->signingTime())
2270  				if (CFRef<CFDateRef> date = CFDateCreate(NULL, time))
2271  					CFDictionaryAddValue(dict, kSecCodeInfoTime, date);
2272  			if (CFAbsoluteTime time = this->signingTimestamp())
2273  				if (CFRef<CFDateRef> date = CFDateCreate(NULL, time))
2274  					CFDictionaryAddValue(dict, kSecCodeInfoTimestamp, date);
2275  		} catch (...) { }
2276  
2277  	//
2278  	// kSecCSRequirementInformation adds information on requirements
2279  	//
2280  	if (flags & kSecCSRequirementInformation)
2281  
2282  //DR not currently supported on iOS
2283  #if TARGET_OS_OSX
2284  		try {
2285  			if (const Requirements *reqs = this->internalRequirements()) {
2286  				CFDictionaryAddValue(dict, kSecCodeInfoRequirements,
2287  					CFTempString(Dumper::dump(reqs)));
2288  				CFDictionaryAddValue(dict, kSecCodeInfoRequirementData, CFTempData(*reqs));
2289  			}
2290  
2291  			const Requirement *dreq = this->designatedRequirement();
2292  			CFRef<SecRequirementRef> dreqRef = (new SecRequirement(dreq))->handle();
2293  			CFDictionaryAddValue(dict, kSecCodeInfoDesignatedRequirement, dreqRef);
2294  			if (this->internalRequirement(kSecDesignatedRequirementType)) {	// explicit
2295  				CFRef<SecRequirementRef> ddreqRef = (new SecRequirement(this->defaultDesignatedRequirement(), true))->handle();
2296  				CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, ddreqRef);
2297  			} else {	// implicit
2298  				CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, dreqRef);
2299  			}
2300  		} catch (...) { }
2301  #endif
2302  
2303  	try {
2304  		if (CFDataRef ent = this->component(cdEntitlementSlot)) {
2305  			CFDictionaryAddValue(dict, kSecCodeInfoEntitlements, ent);
2306  			if (CFDictionaryRef entdict = this->entitlements()) {
2307  				if (needsCatalystEntitlementFixup(entdict)) {
2308  					// If this entitlement dictionary needs catalyst entitlements, make a copy and stick that into the
2309  					// output dictionary instead.
2310  					secinfo("staticCode", "%p fixed catalyst entitlements", this);
2311  					CFRef<CFMutableDictionaryRef> tempEntitlements = makeCFMutableDictionary(entdict);
2312  					updateCatalystEntitlements(tempEntitlements);
2313  					CFRef<CFDictionaryRef> newEntitlements = CFDictionaryCreateCopy(NULL, tempEntitlements);
2314  					if (newEntitlements) {
2315  						CFDictionaryAddValue(dict, kSecCodeInfoEntitlementsDict, newEntitlements.get());
2316  					} else {
2317  						secerror("%p unable to fixup entitlement dictionary", this);
2318  						CFDictionaryAddValue(dict, kSecCodeInfoEntitlementsDict, entdict);
2319  					}
2320  				} else {
2321  					CFDictionaryAddValue(dict, kSecCodeInfoEntitlementsDict, entdict);
2322  				}
2323  			}
2324  		}
2325  	} catch (...) { }
2326  
2327  	//
2328  	// kSecCSInternalInformation adds internal information meant to be for Apple internal
2329  	// use (SPI), and not guaranteed to be stable. Primarily, this is data we want
2330  	// to reliably transmit through the API wall so that code outside the Security.framework
2331  	// can use it without having to play nasty tricks to get it.
2332  	//
2333  	if (flags & kSecCSInternalInformation) {
2334  		try {
2335  			if (mDir)
2336  				CFDictionaryAddValue(dict, kSecCodeInfoCodeDirectory, mDir);
2337  			CFDictionaryAddValue(dict, kSecCodeInfoCodeOffset, CFTempNumber(mRep->signingBase()));
2338          if (!(flags & kSecCSSkipResourceDirectory)) {
2339              if (CFRef<CFDictionaryRef> rdict = getDictionary(cdResourceDirSlot, false))	// suppress validation
2340                  CFDictionaryAddValue(dict, kSecCodeInfoResourceDirectory, rdict);
2341          }
2342  		if (CFRef<CFDictionaryRef> ddict = copyDiskRepInformation())
2343  			CFDictionaryAddValue(dict, kSecCodeInfoDiskRepInfo, ddict);
2344  		} catch (...) { }
2345  		if (mNotarizationChecked && !isnan(mNotarizationDate)) {
2346  			CFRef<CFDateRef> date = CFDateCreate(NULL, mNotarizationDate);
2347  			if (date) {
2348  				CFDictionaryAddValue(dict, kSecCodeInfoNotarizationDate, date.get());
2349  			} else {
2350  				secerror("Error creating date from timestamp: %f", mNotarizationDate);
2351  			}
2352  		}
2353  		if (this->codeDirectory()) {
2354  			uint32_t version = this->codeDirectory()->version;
2355  			CFDictionaryAddValue(dict, kSecCodeInfoSignatureVersion, CFTempNumber(version));
2356  		}
2357  	}
2358  
2359  	if (flags & kSecCSCalculateCMSDigest) {
2360  		try {
2361  			CFDictionaryAddValue(dict, kSecCodeInfoCMSDigestHashType, CFTempNumber(cmsDigestHashType()));
2362  			
2363  			CFRef<CFDataRef> cmsDigest = createCmsDigest();
2364  			if (cmsDigest) {
2365  				CFDictionaryAddValue(dict, kSecCodeInfoCMSDigest, cmsDigest.get());
2366  			}
2367  		} catch (...) { }
2368  	}
2369  
2370  	//
2371  	// kSecCSContentInformation adds more information about the physical layout
2372  	// of the signed code. This is (only) useful for packaging or patching-oriented
2373  	// applications.
2374  	//
2375  	if (flags & kSecCSContentInformation && !(flags & kSecCSSkipResourceDirectory))
2376  		if (CFRef<CFArrayRef> files = mRep->modifiedFiles())
2377  			CFDictionaryAddValue(dict, kSecCodeInfoChangedFiles, files);
2378  
2379  	return dict.yield();
2380  }
2381  
2382  
2383  //
2384  // Resource validation contexts.
2385  // The default context simply throws a CSError, rudely terminating the operation.
2386  //
2387  SecStaticCode::ValidationContext::~ValidationContext()
2388  { /* virtual */ }
2389  
2390  void SecStaticCode::ValidationContext::reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value)
2391  {
2392  	CSError::throwMe(rc, type, value);
2393  }
2394  
2395  void SecStaticCode::CollectingContext::reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value)
2396  {
2397  	StLock<Mutex> _(mLock);
2398  	if (mStatus == errSecSuccess)
2399  		mStatus = rc;			// record first failure for eventual error return
2400  	if (type) {
2401  		if (!mCollection)
2402  			mCollection.take(makeCFMutableDictionary());
2403  		CFMutableArrayRef element = CFMutableArrayRef(CFDictionaryGetValue(mCollection, type));
2404  		if (!element) {
2405  			element = makeCFMutableArray(0);
2406  			if (!element)
2407  				CFError::throwMe();
2408  			CFDictionaryAddValue(mCollection, type, element);
2409  			CFRelease(element);
2410  		}
2411  		CFArrayAppendValue(element, value);
2412  	}
2413  }
2414  
2415  void SecStaticCode::CollectingContext::throwMe()
2416  {
2417  	assert(mStatus != errSecSuccess);
2418  	throw CSError(mStatus, mCollection.retain());
2419  }
2420  
2421  
2422  //
2423  // Master validation driver.
2424  // This is the static validation (only) driver for the API.
2425  //
2426  // SecStaticCode exposes an a la carte menu of topical validators applying
2427  // to a given object. The static validation API pulls them together reliably,
2428  // but it also adds three matrix dimensions: architecture (for "fat" Mach-O binaries),
2429  // nested code, and multiple digests. This function will crawl a suitable cross-section of this
2430  // validation matrix based on which options it is given, creating temporary
2431  // SecStaticCode objects on the fly to complete the task.
2432  // (The point, of course, is to do as little duplicate work as possible.)
2433  //
2434  void SecStaticCode::staticValidate(SecCSFlags flags, const SecRequirement *req)
2435  {
2436  	setValidationFlags(flags);
2437  
2438  #if TARGET_OS_OSX
2439  	if (!mStaplingChecked) {
2440  		mRep->registerStapledTicket();
2441  		mStaplingChecked = true;
2442  	}
2443  
2444  	if (isFlagSet(mFlags, kSecCSForceOnlineNotarizationCheck) && !validationCannotUseNetwork()) {
2445  		if (!mNotarizationChecked) {
2446  			if (this->cdHash()) {
2447  				bool is_revoked = checkNotarizationServiceForRevocation(this->cdHash(), (SecCSDigestAlgorithm)this->hashAlgorithm(), &mNotarizationDate);
2448  				if (is_revoked) {
2449  					MacOSError::throwMe(errSecCSRevokedNotarization);
2450  				}
2451  			}
2452  			mNotarizationChecked = true;
2453  		}
2454  	}
2455  #endif // TARGET_OS_OSX
2456  
2457  	// initialize progress/cancellation state
2458  	if (flags & kSecCSReportProgress) {
2459  		prepareProgress(estimateResourceWorkload() + 2);	// +1 head, +1 tail
2460  	}
2461  
2462    	// core components: once per architecture (if any)
2463  	this->staticValidateCore(flags, req);
2464  	if (flags & kSecCSCheckAllArchitectures) {
2465  		handleOtherArchitectures(^(SecStaticCode* subcode) {
2466  			if (flags & kSecCSCheckGatekeeperArchitectures) {
2467  				Universal *fat = subcode->diskRep()->mainExecutableImage();
2468  				assert(fat && fat->narrowed());	// handleOtherArchitectures gave us a focused architecture slice
2469  				Architecture arch = fat->bestNativeArch();	// actually, the ONLY one
2470  				if ((arch.cpuType() & ~CPU_ARCH_MASK) == CPU_TYPE_POWERPC)
2471  					return;	// irrelevant to Gatekeeper
2472  			}
2473  			subcode->detachedSignature(this->mDetachedSig);	// carry over explicit (but not implicit) detached signature
2474  			subcode->staticValidateCore(flags, req);
2475  		});
2476  	}
2477  	reportProgress();
2478  
2479  	// allow monitor intervention in source validation phase
2480  	reportEvent(CFSTR("prepared"), NULL);
2481  
2482  	// resources: once for all architectures
2483  	if (!(flags & kSecCSDoNotValidateResources)) {
2484  		this->validateResources(flags);
2485  	}
2486  
2487  	// perform strict validation if desired
2488  	if (flags & kSecCSStrictValidate) {
2489  		mRep->strictValidate(codeDirectory(), mTolerateErrors, mValidationFlags);
2490  		reportProgress();
2491  	} else if (flags & kSecCSStrictValidateStructure) {
2492  		mRep->strictValidateStructure(codeDirectory(), mTolerateErrors, mValidationFlags);
2493  	}
2494  
2495  	// allow monitor intervention
2496  	if (CFRef<CFTypeRef> veto = reportEvent(CFSTR("validated"), NULL)) {
2497  		if (CFGetTypeID(veto) == CFNumberGetTypeID())
2498  			MacOSError::throwMe(cfNumber<OSStatus>(veto.as<CFNumberRef>()));
2499  		else
2500  			MacOSError::throwMe(errSecCSBadCallbackValue);
2501  	}
2502  }
2503  
2504  void SecStaticCode::staticValidateCore(SecCSFlags flags, const SecRequirement *req)
2505  {
2506  	try {
2507  		this->validateNonResourceComponents();	// also validates the CodeDirectory
2508  		this->validateTopDirectory();
2509  		if (!(flags & kSecCSDoNotValidateExecutable))
2510  			this->validateExecutable();
2511  		if (req)
2512  			this->validateRequirement(req->requirement(), errSecCSReqFailed);
2513      } catch (CSError &err) {
2514          if (Universal *fat = this->diskRep()->mainExecutableImage())    // Mach-O
2515              if (MachO *mach = fat->architecture()) {
2516                  err.augment(kSecCFErrorArchitecture, CFTempString(mach->architecture().displayName()));
2517                  delete mach;
2518              }
2519          throw;
2520      } catch (const MacOSError &err) {
2521          // add architecture information if we can get it
2522          if (Universal *fat = this->diskRep()->mainExecutableImage())
2523              if (MachO *mach = fat->architecture()) {
2524                  CFTempString arch(mach->architecture().displayName());
2525                  delete mach;
2526                  CSError::throwMe(err.error, kSecCFErrorArchitecture, arch);
2527              }
2528          throw;
2529      }
2530  }
2531  
2532  void SecStaticCode::staticValidateResource(string resourcePath, SecCSFlags flags, const SecRequirement *req)
2533  {
2534  	// resourcePath is always the absolute path to the resource, each analysis can make a relative path
2535  	// if it needs one.  Passing through relative paths but then needing to re-create the full path is
2536  	// more complicated in the case where a subpath is no longer contained within the resource envelope
2537  	// of the next subcode.
2538  
2539  	// Validate the resource is inside the outer bundle by finding the bundle's resource path in the string
2540  	// of the full resource.  If its a prefix match this will also compute the remaining relative path
2541  	// that we'll need later.
2542  	string baseResourcePath;
2543  	string relativePath;
2544  
2545  	if (this->mainExecutablePath() == resourcePath) {
2546  		// Nothing to do here, we're just validating the main executable so proceed
2547  		// to the validation below.
2548  	} else {
2549  		baseResourcePath = cfString(resourceBase());
2550  		relativePath = pathRemaining(resourcePath, baseResourcePath);
2551  		if (relativePath == "") {
2552  			// The resource is not a prefix match with the bundle or the arguments are bad.
2553  			secerror("Requested resource was not within the code object: %s, %s", resourcePath.c_str(), baseResourcePath.c_str());
2554  			MacOSError::throwMe(errSecParam);
2555  		}
2556  	}
2557  
2558  	// In general, we never want to be validating executables of the bundles as we traverse, so just ensure the
2559  	// bit is always set to skip them as we go.
2560  	flags = addFlags(flags, kSecCSDoNotValidateExecutable);
2561  
2562  	// First special case is the main executable, which means we're about to validate it as part of the
2563  	// static validation here.
2564  	bool needsAdditionalValidation = true;
2565  	if (this->mainExecutablePath() == resourcePath) {
2566  		needsAdditionalValidation = false;
2567  
2568  		// If the caller did not request fast validation of an executable, ensure we clear the 'do
2569  		// not validate' bit here before validating.
2570  		if (!isFlagSet(flags, kSecCSFastExecutableValidation)) {
2571  			flags = clearFlags(flags, kSecCSDoNotValidateExecutable);
2572  		}
2573  	}
2574  
2575  	// The Info.plist is covered by the core validation, so there's no more work to be done.
2576  	if (relativePath == "Info.plist") {
2577  		needsAdditionalValidation = false;
2578  	}
2579  
2580  	// Perform basic validation of the code object itself, since thats required for the rest of the comparison
2581  	// to be valid.
2582  	this->staticValidateCore(flags, NULL);
2583  	if (req) {
2584  		// If we have an explicit requirement we must meet and fail, then it should actually
2585  		// be recorded as the resource being modified.
2586  		this->validateRequirement(req->requirement(), errSecCSBadResource);
2587  	}
2588  
2589  	if (!needsAdditionalValidation) {
2590  		// staticValidateCore has already validated the main executable so we're all done!
2591  		return;
2592  	}
2593  
2594  	if (!isFlagSet(flags, kSecCSSkipRootVolumeExceptions)) {
2595  		if (itemQualifiesForResourceExemption(resourcePath)) {
2596  			secinfo("staticCode", "Requested resource was on root filesystem: %s", resourcePath.c_str());
2597  			return;
2598  		}
2599  	}
2600  
2601  	// We need to load resource rules to be able to do a single file resource comparison against.
2602  	CFDictionaryRef rules;
2603  	CFDictionaryRef files;
2604  	uint32_t version;
2605  	if (!loadResources(rules, files, version)) {
2606  		MacOSError::throwMe(errSecCSResourcesNotFound);
2607  	}
2608  
2609  	// Load up a full resource builder so we can properly parse all the rules.
2610  	bool strict = (flags & kSecCSStrictValidate);
2611  	MacOSErrorSet toleratedErrors;
2612  	ResourceBuilder resources(baseResourcePath, baseResourcePath, rules, strict, toleratedErrors);
2613  	diskRep()->adjustResources(resources);
2614  
2615  	// First, check if the path itself is inside of an omission or exclusion hole.
2616  	ResourceBuilder::Rule *rule = resources.findRule(relativePath);
2617  	if (rule) {
2618  		if (rule->flags & (ResourceBuilder::omitted | ResourceBuilder::exclusion)) {
2619  			secerror("Requested resource was not sealed: %d", rule->flags);
2620  			MacOSError::throwMe(errSecCSResourcesNotSealed);
2621  		}
2622  	}
2623  
2624  	// Otherwise look for an exact file match, or find the most deeply nested code.
2625  	CFTypeRef file = CFDictionaryGetValue(files, CFTempString(relativePath));
2626  	if (file) {
2627  		// This item matched a file rule exactly, so just validate it directly with this object.
2628  		AutoFileDesc fd = AutoFileDesc(resourcePath);
2629  		bool isSymlink = fd.isA(S_IFLNK);
2630  
2631  		// Since this is a direct file match, if its for a nested bundle then we want to enable executable
2632  		// validation based on whether fast executable validation was requested.
2633  		if (!isFlagSet(flags, kSecCSFastExecutableValidation)) {
2634  			flags = clearFlags(flags, kSecCSDoNotValidateExecutable);
2635  		}
2636  
2637  		ResourceSeal seal(file);
2638  		if (seal.nested()) {
2639  			CFRef<SecRequirementRef> req;
2640  			if (SecRequirementCreateWithString(seal.requirement(), kSecCSDefaultFlags, &req.aref())) {
2641  				MacOSError::throwMe(errSecCSResourcesInvalid);
2642  			}
2643  
2644  			// If the resource seal indicates this is nested code, create a new code object for this
2645  			// nested code and then validate the resource within that object.
2646  			SecPointer<SecStaticCode> subcode = new SecStaticCode(DiskRep::bestGuess(resourcePath));
2647  			subcode->initializeFromParent(*this);
2648  			// If there was an exact match but its nested code, then the ask is really to validate the
2649  			// main executable of the nested code.
2650  			subcode->staticValidateResource(subcode->mainExecutablePath(), flags, SecRequirement::required(req));
2651  		} else {
2652  			// For other resource types, just a single file resource validation with a ValidationContext that
2653  			// will immediately throw an error if any issues are encountered.
2654  			ValidationContext *context = new ValidationContext(*this);
2655  			validateResource(files, relativePath, isSymlink, *context, flags, version);
2656  		}
2657  	} else {
2658  		// It wasn't a simple file resource within the current code signature, so we're looking for a nested code.
2659  		__block bool itemFound = false;
2660  
2661  		// Iterate through the largest possible chunks of paths looking for nested code matches.
2662  		iterateLargestSubpaths(relativePath, ^bool(string subpath) {
2663  			CFTypeRef file = CFDictionaryGetValue(files, CFTempString(subpath));
2664  			if (file) {
2665  				itemFound = true;
2666  
2667  				ResourceSeal seal(file);
2668  				if (seal.nested()) {
2669  					CFRef<SecRequirementRef> req;
2670  					if (SecRequirementCreateWithString(seal.requirement(), kSecCSDefaultFlags, &req.aref())) {
2671  						MacOSError::throwMe(errSecCSResourcesInvalid);
2672  					}
2673  
2674  					// If the resource seal indicates this is nested code, create a new code object for this
2675  					// nested code and then validate the resource within that object.
2676  					CFRef<CFURLRef> itemURL = makeCFURL(subpath, false, resourceBase());
2677  					string fullPath = cfString(itemURL);
2678  					SecPointer<SecStaticCode> subcode = new SecStaticCode(DiskRep::bestGuess(fullPath));
2679  					subcode->initializeFromParent(*this);
2680  					subcode->staticValidateResource(resourcePath, flags, SecRequirement::required(req));
2681  				} else {
2682  					// Any other type of nested resource is not ok, so just bail.
2683  					secerror("Unexpected item hit traversing resource: %@", file);
2684  					MacOSError::throwMe(errSecCSBadResource);
2685  				}
2686  				// If we find a match, stop walking up for further matching.
2687  				return true;
2688  			}
2689  			return false;
2690  		});
2691  
2692  		// If we finished everything and didn't find the item, its not a valid resource.
2693  		if (!itemFound) {
2694  			secerror("Requested resource was not found: %s", resourcePath.c_str());
2695  			MacOSError::throwMe(errSecCSBadResource);
2696  		}
2697  	}
2698  }
2699  
2700  //
2701  // A helper that generates SecStaticCode objects for all but the primary architecture
2702  // of a fat binary and calls a block on them.
2703  // If there's only one architecture (or this is an architecture-agnostic code),
2704  // nothing happens quickly.
2705  //
2706  void SecStaticCode::handleOtherArchitectures(void (^handle)(SecStaticCode* other))
2707  {
2708  	if (Universal *fat = this->diskRep()->mainExecutableImage()) {
2709  		Universal::Architectures architectures;
2710  		fat->architectures(architectures);
2711  		if (architectures.size() > 1) {
2712  			DiskRep::Context ctx;
2713  			off_t activeOffset = fat->archOffset();
2714  			for (Universal::Architectures::const_iterator arch = architectures.begin(); arch != architectures.end(); ++arch) {
2715  				try {
2716  					ctx.offset = int_cast<size_t, off_t>(fat->archOffset(*arch));
2717  					ctx.size = fat->lengthOfSlice(int_cast<off_t,size_t>(ctx.offset));
2718  					if (ctx.offset != activeOffset) {	// inactive architecture; check it
2719  						SecPointer<SecStaticCode> subcode = new SecStaticCode(DiskRep::bestGuess(this->mainExecutablePath(), &ctx));
2720  
2721  						// There may not actually be a full validation happening, but any operations that do occur should respect the
2722  						// same network settings as the existing validation, so propagate those flags forward here.
2723  						SecCSFlags flagsToPropagate = (kSecCSAllowNetworkAccess | kSecCSNoNetworkAccess);
2724  						subcode->setValidationFlags(mValidationFlags & flagsToPropagate);
2725  
2726  						subcode->detachedSignature(this->mDetachedSig); // carry over explicit (but not implicit) detached signature
2727  						if (this->teamID() == NULL || subcode->teamID() == NULL) {
2728  							if (this->teamID() != subcode->teamID())
2729  								MacOSError::throwMe(errSecCSSignatureInvalid);
2730  						} else if (strcmp(this->teamID(), subcode->teamID()) != 0)
2731  							MacOSError::throwMe(errSecCSSignatureInvalid);
2732  						handle(subcode);
2733  					}
2734  				} catch(std::out_of_range e) {
2735  					// some of our int_casts fell over.
2736  					MacOSError::throwMe(errSecCSBadObjectFormat);
2737  				}
2738  			}
2739  		}
2740  	}
2741  }
2742  
2743  //
2744  // A method that takes a certificate chain (certs) and evaluates
2745  // if it is a Mac or IPhone developer cert, an app store distribution cert,
2746  // or a developer ID
2747  //
2748  bool SecStaticCode::isAppleDeveloperCert(CFArrayRef certs)
2749  {
2750  	static const std::string appleDeveloperRequirement = "(" + std::string(WWDRRequirement) + ") or (" + MACWWDRRequirement + ") or (" + developerID + ") or (" + distributionCertificate + ") or (" + iPhoneDistributionCert + ")";
2751  	SecPointer<SecRequirement> req = new SecRequirement(parseRequirement(appleDeveloperRequirement), true);
2752  	Requirement::Context ctx(certs, NULL, NULL, "", NULL, NULL, kSecCodeSignatureNoHash, false, NULL, "");
2753  
2754  	return req->requirement()->validates(ctx);
2755  }
2756  
2757  CFDataRef SecStaticCode::createCmsDigest()
2758  {
2759  	/*
2760  	 * The CMS digest is a hash of the primary (first, most compatible) code directory,
2761  	 * but its hash algorithm is fixed and not related to the code directory's
2762  	 * hash algorithm.
2763  	 */
2764  	
2765  	auto it = codeDirectories()->begin();
2766  	
2767  	if (it == codeDirectories()->end()) {
2768  		return NULL;
2769  	}
2770  
2771  	CodeDirectory const * const cd = reinterpret_cast<CodeDirectory const*>(CFDataGetBytePtr(it->second));
2772  	
2773  	RefPointer<DynamicHash> hash = cd->hashFor(mCMSDigestHashType);
2774  	CFMutableDataRef data = CFDataCreateMutable(NULL, hash->digestLength());
2775  	CFDataSetLength(data, hash->digestLength());
2776  	hash->update(cd, cd->length());
2777  	hash->finish(CFDataGetMutableBytePtr(data));
2778  	
2779  	return data;
2780  }
2781  	
2782  } // end namespace CodeSigning
2783  } // end namespace Security