1/*
2 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24//
25// StaticCode - SecStaticCode API objects
26//
27#include "StaticCode.h"
28#include "Code.h"
29#include "reqmaker.h"
30#include "drmaker.h"
31#include "reqdumper.h"
32#include "reqparser.h"
33#include "sigblob.h"
34#include "resources.h"
35#include "detachedrep.h"
36#include "csdatabase.h"
37#include "csutilities.h"
38#include "dirscanner.h"
39#include <CoreFoundation/CFURLAccess.h>
40#include <Security/SecPolicyPriv.h>
41#include <Security/SecTrustPriv.h>
42#include <Security/SecCertificatePriv.h>
43#include <Security/CMSPrivate.h>
44#include <Security/SecCmsContentInfo.h>
45#include <Security/SecCmsSignerInfo.h>
46#include <Security/SecCmsSignedData.h>
47#include <Security/cssmapplePriv.h>
48#include <security_utilities/unix++.h>
49#include <security_utilities/cfmunge.h>
50#include <Security/CMSDecoder.h>
51#include <security_utilities/logging.h>
52#include <dirent.h>
53#include <sstream>
54
55
56namespace Security {
57namespace CodeSigning {
58
59using namespace UnixPlusPlus;
60
61// A requirement representing a Mac or iOS dev cert, a Mac or iOS distribution cert, or a developer ID
62static const char WWDRRequirement[] = "anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.2] exists";
63static const char MACWWDRRequirement[] = "anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.12] exists";
64static const char developerID[] = "anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists"
65											" and certificate leaf[field.1.2.840.113635.100.6.1.13] exists";
66static const char distributionCertificate[] =	"anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.7] exists";
67static const char iPhoneDistributionCert[] =	"anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.4] exists";
68
69//
70// Map a component slot number to a suitable error code for a failure
71//
72static inline OSStatus errorForSlot(CodeDirectory::SpecialSlot slot)
73{
74	switch (slot) {
75	case cdInfoSlot:
76		return errSecCSInfoPlistFailed;
77	case cdResourceDirSlot:
78		return errSecCSResourceDirectoryFailed;
79	default:
80		return errSecCSSignatureFailed;
81	}
82}
83
84
85//
86// Construct a SecStaticCode object given a disk representation object
87//
88SecStaticCode::SecStaticCode(DiskRep *rep)
89	: mRep(rep),
90	  mValidated(false), mExecutableValidated(false), mResourcesValidated(false), mResourcesValidContext(NULL),
91	  mDesignatedReq(NULL), mGotResourceBase(false), mMonitor(NULL), mEvalDetails(NULL)
92{
93	CODESIGN_STATIC_CREATE(this, rep);
94	CFRef<CFDataRef> codeDirectory = rep->codeDirectory();
95	if (codeDirectory && CFDataGetLength(codeDirectory) <= 0)
96		MacOSError::throwMe(errSecCSSignatureInvalid);
97	checkForSystemSignature();
98}
99
100
101//
102// Clean up a SecStaticCode object
103//
104SecStaticCode::~SecStaticCode() throw()
105try {
106	::free(const_cast<Requirement *>(mDesignatedReq));
107	if (mResourcesValidContext)
108		delete mResourcesValidContext;
109} catch (...) {
110	return;
111}
112
113
114//
115// CF-level comparison of SecStaticCode objects compares CodeDirectory hashes if signed,
116// and falls back on comparing canonical paths if (both are) not.
117//
118bool SecStaticCode::equal(SecCFObject &secOther)
119{
120	SecStaticCode *other = static_cast<SecStaticCode *>(&secOther);
121	CFDataRef mine = this->cdHash();
122	CFDataRef his = other->cdHash();
123	if (mine || his)
124		return mine && his && CFEqual(mine, his);
125	else
126		return CFEqual(CFRef<CFURLRef>(this->copyCanonicalPath()), CFRef<CFURLRef>(other->copyCanonicalPath()));
127}
128
129CFHashCode SecStaticCode::hash()
130{
131	if (CFDataRef h = this->cdHash())
132		return CFHash(h);
133	else
134		return CFHash(CFRef<CFURLRef>(this->copyCanonicalPath()));
135}
136
137
138//
139// Invoke a stage monitor if registered
140//
141CFTypeRef SecStaticCode::reportEvent(CFStringRef stage, CFDictionaryRef info)
142{
143	if (mMonitor)
144		return mMonitor(this->handle(false), stage, info);
145	else
146		return NULL;
147}
148
149void SecStaticCode::prepareProgress(unsigned int workload)
150{
151	{
152		StLock<Mutex> _(mCancelLock);
153		mCancelPending = false;			// not cancelled
154	}
155	if (mValidationFlags & kSecCSReportProgress) {
156		mCurrentWork = 0;				// nothing done yet
157		mTotalWork = workload;			// totally fake - we don't know how many files we'll get to chew
158	}
159}
160
161void SecStaticCode::reportProgress(unsigned amount /* = 1 */)
162{
163	if (mMonitor && (mValidationFlags & kSecCSReportProgress)) {
164		{
165			// if cancellation is pending, abort now
166			StLock<Mutex> _(mCancelLock);
167			if (mCancelPending)
168				MacOSError::throwMe(errSecCSCancelled);
169		}
170		// update progress and report
171		mCurrentWork += amount;
172		mMonitor(this->handle(false), CFSTR("progress"), CFTemp<CFDictionaryRef>("{current=%d,total=%d}", mCurrentWork, mTotalWork));
173	}
174}
175
176
177//
178// Set validation conditions for fine-tuning legacy tolerance
179//
180static void addError(CFTypeRef cfError, void* context)
181{
182	if (CFGetTypeID(cfError) == CFNumberGetTypeID()) {
183		int64_t error;
184		CFNumberGetValue(CFNumberRef(cfError), kCFNumberSInt64Type, (void*)&error);
185		MacOSErrorSet* errors = (MacOSErrorSet*)context;
186		errors->insert(OSStatus(error));
187	}
188}
189
190void SecStaticCode::setValidationModifiers(CFDictionaryRef conditions)
191{
192	if (conditions) {
193		CFDictionary source(conditions, errSecCSDbCorrupt);
194		mAllowOmissions = source.get<CFArrayRef>("omissions");
195		if (CFArrayRef errors = source.get<CFArrayRef>("errors"))
196			CFArrayApplyFunction(errors, CFRangeMake(0, CFArrayGetCount(errors)), addError, &this->mTolerateErrors);
197	}
198}
199
200
201//
202// Request cancellation of a validation in progress.
203// We do this by posting an abort flag that is checked periodically.
204//
205void SecStaticCode::cancelValidation()
206{
207	StLock<Mutex> _(mCancelLock);
208	if (!(mValidationFlags & kSecCSReportProgress))		// not using progress reporting; cancel won't make it through
209		MacOSError::throwMe(errSecCSInvalidFlags);
210	mCancelPending = true;
211}
212
213
214//
215// Attach a detached signature.
216//
217void SecStaticCode::detachedSignature(CFDataRef sigData)
218{
219	if (sigData) {
220		mDetachedSig = sigData;
221		mRep = new DetachedRep(sigData, mRep->base(), "explicit detached");
222		CODESIGN_STATIC_ATTACH_EXPLICIT(this, mRep);
223	} else {
224		mDetachedSig = NULL;
225		mRep = mRep->base();
226		CODESIGN_STATIC_ATTACH_EXPLICIT(this, NULL);
227	}
228}
229
230
231//
232// Consult the system detached signature database to see if it contains
233// a detached signature for this StaticCode. If it does, fetch and attach it.
234// We do this only if the code has no signature already attached.
235//
236void SecStaticCode::checkForSystemSignature()
237{
238	if (!this->isSigned()) {
239		SignatureDatabase db;
240		if (db.isOpen())
241			try {
242				if (RefPointer<DiskRep> dsig = db.findCode(mRep)) {
243					CODESIGN_STATIC_ATTACH_SYSTEM(this, dsig);
244					mRep = dsig;
245				}
246			} catch (...) {
247			}
248	}
249}
250
251
252//
253// Return a descriptive string identifying the source of the code signature
254//
255string SecStaticCode::signatureSource()
256{
257	if (!isSigned())
258		return "unsigned";
259	if (DetachedRep *rep = dynamic_cast<DetachedRep *>(mRep.get()))
260		return rep->source();
261	return "embedded";
262}
263
264
265//
266// Do ::required, but convert incoming SecCodeRefs to their SecStaticCodeRefs
267// (if possible).
268//
269SecStaticCode *SecStaticCode::requiredStatic(SecStaticCodeRef ref)
270{
271	SecCFObject *object = SecCFObject::required(ref, errSecCSInvalidObjectRef);
272	if (SecStaticCode *scode = dynamic_cast<SecStaticCode *>(object))
273		return scode;
274	else if (SecCode *code = dynamic_cast<SecCode *>(object))
275		return code->staticCode();
276	else	// neither (a SecSomethingElse)
277		MacOSError::throwMe(errSecCSInvalidObjectRef);
278}
279
280SecCode *SecStaticCode::optionalDynamic(SecStaticCodeRef ref)
281{
282	SecCFObject *object = SecCFObject::required(ref, errSecCSInvalidObjectRef);
283	if (dynamic_cast<SecStaticCode *>(object))
284		return NULL;
285	else if (SecCode *code = dynamic_cast<SecCode *>(object))
286		return code;
287	else	// neither (a SecSomethingElse)
288		MacOSError::throwMe(errSecCSInvalidObjectRef);
289}
290
291
292//
293// Void all cached validity data.
294//
295// We also throw out cached components, because the new signature data may have
296// a different idea of what components should be present. We could reconcile the
297// cached data instead, if performance seems to be impacted.
298//
299void SecStaticCode::resetValidity()
300{
301	CODESIGN_EVAL_STATIC_RESET(this);
302	mValidated = false;
303	mExecutableValidated = mResourcesValidated = false;
304	if (mResourcesValidContext) {
305		delete mResourcesValidContext;
306		mResourcesValidContext = NULL;
307	}
308	mDir = NULL;
309	mSignature = NULL;
310	for (unsigned n = 0; n < cdSlotCount; n++)
311		mCache[n] = NULL;
312	mInfoDict = NULL;
313	mEntitlements = NULL;
314	mResourceDict = NULL;
315	mDesignatedReq = NULL;
316	mCDHash = NULL;
317	mGotResourceBase = false;
318	mTrust = NULL;
319	mCertChain = NULL;
320	mEvalDetails = NULL;
321	mRep->flush();
322
323	// we may just have updated the system database, so check again
324	checkForSystemSignature();
325}
326
327
328//
329// Retrieve a sealed component by special slot index.
330// If the CodeDirectory has already been validated, validate against that.
331// Otherwise, retrieve the component without validation (but cache it). Validation
332// will go through the cache and validate all cached components.
333//
334CFDataRef SecStaticCode::component(CodeDirectory::SpecialSlot slot, OSStatus fail /* = errSecCSSignatureFailed */)
335{
336	assert(slot <= cdSlotMax);
337
338	CFRef<CFDataRef> &cache = mCache[slot];
339	if (!cache) {
340		if (CFRef<CFDataRef> data = mRep->component(slot)) {
341			if (validated()) // if the directory has been validated...
342				if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), // ... and it's no good
343						CFDataGetLength(data), -slot))
344					MacOSError::throwMe(errorForSlot(slot)); // ... then bail
345			cache = data;	// it's okay, cache it
346		} else {	// absent, mark so
347			if (validated())	// if directory has been validated...
348				if (codeDirectory()->slotIsPresent(-slot)) // ... and the slot is NOT missing
349					MacOSError::throwMe(errorForSlot(slot));	// was supposed to be there
350			cache = CFDataRef(kCFNull);		// white lie
351		}
352	}
353	return (cache == CFDataRef(kCFNull)) ? NULL : cache.get();
354}
355
356
357//
358// Get the CodeDirectory.
359// Throws (if check==true) or returns NULL (check==false) if there is none.
360// Always throws if the CodeDirectory exists but is invalid.
361// NEVER validates against the signature.
362//
363const CodeDirectory *SecStaticCode::codeDirectory(bool check /* = true */)
364{
365	if (!mDir) {
366		if (mDir.take(mRep->codeDirectory())) {
367			const CodeDirectory *dir = reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(mDir));
368			dir->checkIntegrity();
369		}
370	}
371	if (mDir)
372		return reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(mDir));
373	if (check)
374		MacOSError::throwMe(errSecCSUnsigned);
375	return NULL;
376}
377
378
379//
380// Get the hash of the CodeDirectory.
381// Returns NULL if there is none.
382//
383CFDataRef SecStaticCode::cdHash()
384{
385	if (!mCDHash) {
386		if (const CodeDirectory *cd = codeDirectory(false)) {
387			SHA1 hash;
388			hash(cd, cd->length());
389			SHA1::Digest digest;
390			hash.finish(digest);
391			mCDHash.take(makeCFData(digest, sizeof(digest)));
392			CODESIGN_STATIC_CDHASH(this, digest, sizeof(digest));
393		}
394	}
395	return mCDHash;
396}
397
398
399//
400// Return the CMS signature blob; NULL if none found.
401//
402CFDataRef SecStaticCode::signature()
403{
404	if (!mSignature)
405		mSignature.take(mRep->signature());
406	if (mSignature)
407		return mSignature;
408	MacOSError::throwMe(errSecCSUnsigned);
409}
410
411
412//
413// Verify the signature on the CodeDirectory.
414// If this succeeds (doesn't throw), the CodeDirectory is statically trustworthy.
415// Any outcome (successful or not) is cached for the lifetime of the StaticCode.
416//
417void SecStaticCode::validateDirectory()
418{
419	// echo previous outcome, if any
420	if (!validated())
421		try {
422			// perform validation (or die trying)
423			CODESIGN_EVAL_STATIC_DIRECTORY(this);
424			mValidationExpired = verifySignature();
425			for (CodeDirectory::SpecialSlot slot = codeDirectory()->maxSpecialSlot(); slot >= 1; --slot)
426				if (mCache[slot])	// if we already loaded that resource...
427					validateComponent(slot, errorForSlot(slot)); // ... then check it now
428			mValidated = true;			// we've done the deed...
429			mValidationResult = errSecSuccess;	// ... and it was good
430		} catch (const CommonError &err) {
431			mValidated = true;
432			mValidationResult = err.osStatus();
433			throw;
434		} catch (...) {
435			secdebug("staticCode", "%p validation threw non-common exception", this);
436			mValidated = true;
437			mValidationResult = errSecCSInternalError;
438			throw;
439		}
440	assert(validated());
441	if (mValidationResult == errSecSuccess) {
442		if (mValidationExpired)
443			if ((mValidationFlags & kSecCSConsiderExpiration)
444					|| (codeDirectory()->flags & kSecCodeSignatureForceExpiration))
445				MacOSError::throwMe(CSSMERR_TP_CERT_EXPIRED);
446	} else
447		MacOSError::throwMe(mValidationResult);
448}
449
450
451//
452// Load and validate the CodeDirectory and all components *except* those related to the resource envelope.
453// Those latter components are checked by validateResources().
454//
455void SecStaticCode::validateNonResourceComponents()
456{
457	this->validateDirectory();
458	for (CodeDirectory::SpecialSlot slot = codeDirectory()->maxSpecialSlot(); slot >= 1; --slot)
459		switch (slot) {
460		case cdResourceDirSlot:		// validated by validateResources
461			break;
462		default:
463			this->component(slot);		// loads and validates
464			break;
465		}
466}
467
468
469//
470// Get the (signed) signing date from the code signature.
471// Sadly, we need to validate the signature to get the date (as a side benefit).
472// This means that you can't get the signing time for invalidly signed code.
473//
474// We could run the decoder "almost to" verification to avoid this, but there seems
475// little practical point to such a duplication of effort.
476//
477CFAbsoluteTime SecStaticCode::signingTime()
478{
479	validateDirectory();
480	return mSigningTime;
481}
482
483CFAbsoluteTime SecStaticCode::signingTimestamp()
484{
485	validateDirectory();
486	return mSigningTimestamp;
487}
488
489
490//
491// Verify the CMS signature on the CodeDirectory.
492// This performs the cryptographic tango. It returns if the signature is valid,
493// or throws if it is not. As a side effect, a successful return sets up the
494// cached certificate chain for future use.
495// Returns true if the signature is expired (the X.509 sense), false if it's not.
496// Expiration is fatal (throws) if a secure timestamp is included, but not otherwise.
497//
498bool SecStaticCode::verifySignature()
499{
500	// ad-hoc signed code is considered validly signed by definition
501	if (flag(kSecCodeSignatureAdhoc)) {
502		CODESIGN_EVAL_STATIC_SIGNATURE_ADHOC(this);
503		return false;
504	}
505
506	DTRACK(CODESIGN_EVAL_STATIC_SIGNATURE, this, (char*)this->mainExecutablePath().c_str());
507
508	// decode CMS and extract SecTrust for verification
509	CFRef<CMSDecoderRef> cms;
510	MacOSError::check(CMSDecoderCreate(&cms.aref())); // create decoder
511	CFDataRef sig = this->signature();
512	MacOSError::check(CMSDecoderUpdateMessage(cms, CFDataGetBytePtr(sig), CFDataGetLength(sig)));
513	this->codeDirectory();	// load CodeDirectory (sets mDir)
514	MacOSError::check(CMSDecoderSetDetachedContent(cms, mDir));
515	MacOSError::check(CMSDecoderFinalizeMessage(cms));
516	MacOSError::check(CMSDecoderSetSearchKeychain(cms, cfEmptyArray()));
517	CFRef<CFArrayRef> vf_policies = verificationPolicies();
518    CFRef<CFArrayRef> ts_policies = SecPolicyCreateAppleTimeStampingAndRevocationPolicies(vf_policies);
519    CMSSignerStatus status;
520    MacOSError::check(CMSDecoderCopySignerStatus(cms, 0, vf_policies,
521    false, &status, &mTrust.aref(), NULL));
522
523	if (status != kCMSSignerValid)
524		MacOSError::throwMe(errSecCSSignatureFailed);
525
526	// internal signing time (as specified by the signer; optional)
527	mSigningTime = 0;       // "not present" marker (nobody could code sign on Jan 1, 2001 :-)
528	switch (OSStatus rc = CMSDecoderCopySignerSigningTime(cms, 0, &mSigningTime)) {
529	case errSecSuccess:
530	case errSecSigningTimeMissing:
531		break;
532	default:
533		MacOSError::throwMe(rc);
534	}
535
536	// certified signing time (as specified by a TSA; optional)
537	mSigningTimestamp = 0;
538	switch (OSStatus rc = CMSDecoderCopySignerTimestampWithPolicy(cms, ts_policies, 0, &mSigningTimestamp)) {
539	case errSecSuccess:
540	case errSecTimestampMissing:
541		break;
542	default:
543		MacOSError::throwMe(rc);
544	}
545
546	// set up the environment for SecTrust
547    if (mValidationFlags & kSecCSNoNetworkAccess) {
548        MacOSError::check(SecTrustSetNetworkFetchAllowed(mTrust,false)); // no network?
549    }
550	MacOSError::check(SecTrustSetAnchorCertificates(mTrust, cfEmptyArray())); // no anchors
551    MacOSError::check(SecTrustSetKeychains(mTrust, cfEmptyArray())); // no keychains
552	CSSM_APPLE_TP_ACTION_DATA actionData = {
553		CSSM_APPLE_TP_ACTION_VERSION,	// version of data structure
554		CSSM_TP_ACTION_IMPLICIT_ANCHORS	// action flags
555	};
556
557	for (;;) {	// at most twice
558		MacOSError::check(SecTrustSetParameters(mTrust,
559			CSSM_TP_ACTION_DEFAULT, CFTempData(&actionData, sizeof(actionData))));
560
561		// evaluate trust and extract results
562		SecTrustResultType trustResult;
563		MacOSError::check(SecTrustEvaluate(mTrust, &trustResult));
564		MacOSError::check(SecTrustGetResult(mTrust, &trustResult, &mCertChain.aref(), &mEvalDetails));
565
566		// if this is an Apple developer cert....
567		if (teamID() && SecStaticCode::isAppleDeveloperCert(mCertChain)) {
568			CFRef<CFStringRef> teamIDFromCert;
569			if (CFArrayGetCount(mCertChain) > 0) {
570				/* Note that SecCertificateCopySubjectComponent sets the out paramater to NULL if there is no field present */
571				MacOSError::check(SecCertificateCopySubjectComponent((SecCertificateRef)CFArrayGetValueAtIndex(mCertChain, Requirement::leafCert),
572																	 &CSSMOID_OrganizationalUnitName,
573																	 &teamIDFromCert.aref()));
574
575				if (teamIDFromCert) {
576					CFRef<CFStringRef> teamIDFromCD = CFStringCreateWithCString(NULL, teamID(), kCFStringEncodingUTF8);
577					if (!teamIDFromCD) {
578						MacOSError::throwMe(errSecCSInternalError);
579					}
580
581					if (CFStringCompare(teamIDFromCert, teamIDFromCD, 0) != kCFCompareEqualTo) {
582						Security::Syslog::error("Team identifier in the signing certificate (%s) does not match the team identifier (%s) in the code directory", cfString(teamIDFromCert).c_str(), teamID());
583						MacOSError::throwMe(errSecCSSignatureInvalid);
584					}
585				}
586			}
587		}
588
589		CODESIGN_EVAL_STATIC_SIGNATURE_RESULT(this, trustResult, mCertChain ? (int)CFArrayGetCount(mCertChain) : 0);
590		switch (trustResult) {
591		case kSecTrustResultProceed:
592		case kSecTrustResultUnspecified:
593			break;				// success
594		case kSecTrustResultDeny:
595			MacOSError::throwMe(CSSMERR_APPLETP_TRUST_SETTING_DENY);	// user reject
596		case kSecTrustResultInvalid:
597			assert(false);		// should never happen
598			MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED);
599		default:
600			{
601				OSStatus result;
602				MacOSError::check(SecTrustGetCssmResultCode(mTrust, &result));
603				// if we have a valid timestamp, CMS validates against (that) signing time and all is well.
604				// If we don't have one, may validate against *now*, and must be able to tolerate expiration.
605				if (mSigningTimestamp == 0) // no timestamp available
606					if (((result == CSSMERR_TP_CERT_EXPIRED) || (result == CSSMERR_TP_CERT_NOT_VALID_YET))
607							&& !(actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED)) {
608						CODESIGN_EVAL_STATIC_SIGNATURE_EXPIRED(this);
609						actionData.ActionFlags |= CSSM_TP_ACTION_ALLOW_EXPIRED; // (this also allows postdated certs)
610						continue;		// retry validation while tolerating expiration
611					}
612				MacOSError::throwMe(result);
613			}
614		}
615
616		if (mSigningTimestamp) {
617			CFIndex rootix = CFArrayGetCount(mCertChain);
618			if (SecCertificateRef mainRoot = SecCertificateRef(CFArrayGetValueAtIndex(mCertChain, rootix-1)))
619				if (isAppleCA(mainRoot)) {
620					// impose policy: if the signature itself draws to Apple, then so must the timestamp signature
621					CFRef<CFArrayRef> tsCerts;
622					MacOSError::check(CMSDecoderCopySignerTimestampCertificates(cms, 0, &tsCerts.aref()));
623					CFIndex tsn = CFArrayGetCount(tsCerts);
624					bool good = tsn > 0 && isAppleCA(SecCertificateRef(CFArrayGetValueAtIndex(tsCerts, tsn-1)));
625					if (!good)
626						MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED);
627				}
628		}
629
630		return actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED;
631	}
632}
633
634
635//
636// Return the TP policy used for signature verification.
637// This may be a simple SecPolicyRef or a CFArray of policies.
638// The caller owns the return value.
639//
640static SecPolicyRef makeCRLPolicy()
641{
642	CFRef<SecPolicyRef> policy;
643	MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_REVOCATION_CRL, &policy.aref()));
644	CSSM_APPLE_TP_CRL_OPTIONS options;
645	memset(&options, 0, sizeof(options));
646	options.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION;
647	options.CrlFlags = CSSM_TP_ACTION_FETCH_CRL_FROM_NET | CSSM_TP_ACTION_CRL_SUFFICIENT;
648	CSSM_DATA optData = { sizeof(options), (uint8 *)&options };
649	MacOSError::check(SecPolicySetValue(policy, &optData));
650	return policy.yield();
651}
652
653static SecPolicyRef makeOCSPPolicy()
654{
655	CFRef<SecPolicyRef> policy;
656	MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_REVOCATION_OCSP, &policy.aref()));
657	CSSM_APPLE_TP_OCSP_OPTIONS options;
658	memset(&options, 0, sizeof(options));
659	options.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION;
660	options.Flags = CSSM_TP_ACTION_OCSP_SUFFICIENT;
661	CSSM_DATA optData = { sizeof(options), (uint8 *)&options };
662	MacOSError::check(SecPolicySetValue(policy, &optData));
663	return policy.yield();
664}
665
666CFArrayRef SecStaticCode::verificationPolicies()
667{
668	CFRef<SecPolicyRef> core;
669	MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3,
670			&CSSMOID_APPLE_TP_CODE_SIGNING, &core.aref()));
671    if (mValidationFlags & kSecCSNoNetworkAccess) {
672        // Skips all revocation since they require network connectivity
673        // therefore annihilates kSecCSEnforceRevocationChecks if present
674        CFRef<SecPolicyRef> no_revoc = SecPolicyCreateRevocation(kSecRevocationNetworkAccessDisabled);
675        return makeCFArray(2, core.get(), no_revoc.get());
676    }
677	else if (mValidationFlags & kSecCSEnforceRevocationChecks) {
678        // Add CRL and OCSPPolicies
679		CFRef<SecPolicyRef> crl = makeCRLPolicy();
680		CFRef<SecPolicyRef> ocsp = makeOCSPPolicy();
681		return makeCFArray(3, core.get(), crl.get(), ocsp.get());
682	} else {
683		return makeCFArray(1, core.get());
684	}
685}
686
687
688//
689// Validate a particular sealed, cached resource against its (special) CodeDirectory slot.
690// The resource must already have been placed in the cache.
691// This does NOT perform basic validation.
692//
693void SecStaticCode::validateComponent(CodeDirectory::SpecialSlot slot, OSStatus fail /* = errSecCSSignatureFailed */)
694{
695	assert(slot <= cdSlotMax);
696	CFDataRef data = mCache[slot];
697	assert(data);		// must be cached
698	if (data == CFDataRef(kCFNull)) {
699		if (codeDirectory()->slotIsPresent(-slot)) // was supposed to be there...
700				MacOSError::throwMe(fail);	// ... and is missing
701	} else {
702		if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), CFDataGetLength(data), -slot))
703			MacOSError::throwMe(fail);
704	}
705}
706
707
708//
709// Perform static validation of the main executable.
710// This reads the main executable from disk and validates it against the
711// CodeDirectory code slot array.
712// Note that this is NOT an in-memory validation, and is thus potentially
713// subject to timing attacks.
714//
715void SecStaticCode::validateExecutable()
716{
717	if (!validatedExecutable()) {
718		try {
719			DTRACK(CODESIGN_EVAL_STATIC_EXECUTABLE, this,
720				(char*)this->mainExecutablePath().c_str(), codeDirectory()->nCodeSlots);
721			const CodeDirectory *cd = this->codeDirectory();
722			if (!cd)
723				MacOSError::throwMe(errSecCSUnsigned);
724			AutoFileDesc fd(mainExecutablePath(), O_RDONLY);
725			fd.fcntl(F_NOCACHE, true);		// turn off page caching (one-pass)
726			if (Universal *fat = mRep->mainExecutableImage())
727				fd.seek(fat->archOffset());
728			size_t pageSize = cd->pageSize ? (1 << cd->pageSize) : 0;
729			size_t remaining = cd->codeLimit;
730			for (uint32_t slot = 0; slot < cd->nCodeSlots; ++slot) {
731				size_t size = min(remaining, pageSize);
732				if (!cd->validateSlot(fd, size, slot)) {
733					CODESIGN_EVAL_STATIC_EXECUTABLE_FAIL(this, (int)slot);
734					MacOSError::throwMe(errSecCSSignatureFailed);
735				}
736				remaining -= size;
737			}
738			mExecutableValidated = true;
739			mExecutableValidResult = errSecSuccess;
740		} catch (const CommonError &err) {
741			mExecutableValidated = true;
742			mExecutableValidResult = err.osStatus();
743			throw;
744		} catch (...) {
745			secdebug("staticCode", "%p executable validation threw non-common exception", this);
746			mExecutableValidated = true;
747			mExecutableValidResult = errSecCSInternalError;
748			throw;
749		}
750	}
751	assert(validatedExecutable());
752	if (mExecutableValidResult != errSecSuccess)
753		MacOSError::throwMe(mExecutableValidResult);
754}
755
756
757//
758// Perform static validation of sealed resources and nested code.
759//
760// This performs a whole-code static resource scan and effectively
761// computes a concordance between what's on disk and what's in the ResourceDirectory.
762// Any unsanctioned difference causes an error.
763//
764unsigned SecStaticCode::estimateResourceWorkload()
765{
766	// workload estimate = number of sealed files
767	CFDictionaryRef sealedResources = resourceDictionary();
768	CFDictionaryRef files = cfget<CFDictionaryRef>(sealedResources, "files2");
769	if (files == NULL)
770		files = cfget<CFDictionaryRef>(sealedResources, "files");
771	return files ? unsigned(CFDictionaryGetCount(files)) : 0;
772}
773
774void SecStaticCode::validateResources(SecCSFlags flags)
775{
776	// do we have a superset of this requested validation cached?
777	bool doit = true;
778	if (mResourcesValidated) {	// have cached outcome
779		if (!(flags & kSecCSCheckNestedCode) || mResourcesDeep)	// was deep or need no deep scan
780			doit = false;
781	}
782	if (doit) {
783		try {
784			// sanity first
785			CFDictionaryRef sealedResources = resourceDictionary();
786			if (this->resourceBase())	// disk has resources
787				if (sealedResources)
788					/* go to work below */;
789				else
790					MacOSError::throwMe(errSecCSResourcesNotFound);
791			else							// disk has no resources
792				if (sealedResources)
793					MacOSError::throwMe(errSecCSResourcesNotFound);
794				else
795					return;					// no resources, not sealed - fine (no work)
796
797			// found resources, and they are sealed
798			DTRACK(CODESIGN_EVAL_STATIC_RESOURCES, this,
799				(char*)this->mainExecutablePath().c_str(), 0);
800
801			// scan through the resources on disk, checking each against the resourceDirectory
802			if (mValidationFlags & kSecCSFullReport)
803				mResourcesValidContext = new CollectingContext(*this);		// collect all failures in here
804			else
805				mResourcesValidContext = new ValidationContext(*this);		// simple bug-out on first error
806
807			CFDictionaryRef rules;
808			CFDictionaryRef files;
809			uint32_t version;
810			if (CFDictionaryGetValue(sealedResources, CFSTR("files2"))) {	// have V2 signature
811				rules = cfget<CFDictionaryRef>(sealedResources, "rules2");
812				files = cfget<CFDictionaryRef>(sealedResources, "files2");
813				version = 2;
814			} else {	// only V1 available
815				rules = cfget<CFDictionaryRef>(sealedResources, "rules");
816				files = cfget<CFDictionaryRef>(sealedResources, "files");
817				version = 1;
818			}
819			if (!rules || !files)
820				MacOSError::throwMe(errSecCSResourcesInvalid);
821			// check for weak resource rules
822			bool strict = flags & kSecCSStrictValidate;
823			if (strict) {
824				if (hasWeakResourceRules(rules, version, mAllowOmissions))
825					if (mTolerateErrors.find(errSecCSWeakResourceRules) == mTolerateErrors.end())
826						MacOSError::throwMe(errSecCSWeakResourceRules);
827				if (version == 1)
828					if (mTolerateErrors.find(errSecCSWeakResourceEnvelope) == mTolerateErrors.end())
829						MacOSError::throwMe(errSecCSWeakResourceEnvelope);
830			}
831			__block CFRef<CFMutableDictionaryRef> resourceMap = makeCFMutableDictionary(files);
832			string base = cfString(this->resourceBase());
833			ResourceBuilder resources(base, base, rules, codeDirectory()->hashType, strict, mTolerateErrors);
834			diskRep()->adjustResources(resources);
835			resources.scan(^(FTSENT *ent, uint32_t ruleFlags, const char *relpath, ResourceBuilder::Rule *rule) {
836				validateResource(files, relpath, ent->fts_info == FTS_SL, *mResourcesValidContext, flags, version);
837				reportProgress();
838				CFDictionaryRemoveValue(resourceMap, CFTempString(relpath));
839			});
840
841			unsigned leftovers = unsigned(CFDictionaryGetCount(resourceMap));
842			if (leftovers > 0) {
843				secdebug("staticCode", "%d sealed resource(s) not found in code", int(leftovers));
844				CFDictionaryApplyFunction(resourceMap, SecStaticCode::checkOptionalResource, mResourcesValidContext);
845			}
846
847			// now check for any errors found in the reporting context
848			mResourcesValidated = true;
849			mResourcesDeep = flags & kSecCSCheckNestedCode;
850			if (mResourcesValidContext->osStatus() != errSecSuccess)
851				mResourcesValidContext->throwMe();
852		} catch (const CommonError &err) {
853			mResourcesValidated = true;
854			mResourcesDeep = flags & kSecCSCheckNestedCode;
855			mResourcesValidResult = err.osStatus();
856			throw;
857		} catch (...) {
858			secdebug("staticCode", "%p executable validation threw non-common exception", this);
859			mResourcesValidated = true;
860			mResourcesDeep = flags & kSecCSCheckNestedCode;
861			mResourcesValidResult = errSecCSInternalError;
862			throw;
863		}
864	}
865	assert(validatedResources());
866	if (mResourcesValidResult)
867		MacOSError::throwMe(mResourcesValidResult);
868	if (mResourcesValidContext->osStatus() != errSecSuccess)
869		mResourcesValidContext->throwMe();
870}
871
872
873void SecStaticCode::checkOptionalResource(CFTypeRef key, CFTypeRef value, void *context)
874{
875	ValidationContext *ctx = static_cast<ValidationContext *>(context);
876	ResourceSeal seal(value);
877	if (!seal.optional()) {
878		if (key && CFGetTypeID(key) == CFStringGetTypeID()) {
879			CFTempURL tempURL(CFStringRef(key), false, ctx->code.resourceBase());
880			if (!tempURL.get()) {
881				ctx->reportProblem(errSecCSBadDictionaryFormat, kSecCFErrorResourceSeal, key);
882			} else {
883				ctx->reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing, tempURL);
884			}
885		} else {
886			ctx->reportProblem(errSecCSBadResource, kSecCFErrorResourceSeal, key);
887		}
888	}
889}
890
891
892static bool isOmitRule(CFTypeRef value)
893{
894	if (CFGetTypeID(value) == CFBooleanGetTypeID())
895		return value == kCFBooleanFalse;
896	CFDictionary rule(value, errSecCSResourceRulesInvalid);
897	return rule.get<CFBooleanRef>("omit") == kCFBooleanTrue;
898}
899
900bool SecStaticCode::hasWeakResourceRules(CFDictionaryRef rulesDict, uint32_t version, CFArrayRef allowedOmissions)
901{
902	// compute allowed omissions
903	CFRef<CFArrayRef> defaultOmissions = this->diskRep()->allowedResourceOmissions();
904	if (!defaultOmissions)
905		MacOSError::throwMe(errSecCSInternalError);
906	CFRef<CFMutableArrayRef> allowed = CFArrayCreateMutableCopy(NULL, 0, defaultOmissions);
907	if (allowedOmissions)
908		CFArrayAppendArray(allowed, allowedOmissions, CFRangeMake(0, CFArrayGetCount(allowedOmissions)));
909	CFRange range = CFRangeMake(0, CFArrayGetCount(allowed));
910
911	// check all resource rules for weakness
912	string catchAllRule = (version == 1) ? "^Resources/" : "^.*";
913	__block bool coversAll = false;
914	__block bool forbiddenOmission = false;
915	CFDictionary rules(rulesDict, errSecCSResourceRulesInvalid);
916	rules.apply(^(CFStringRef key, CFTypeRef value) {
917		string pattern = cfString(key, errSecCSResourceRulesInvalid);
918		if (pattern == catchAllRule && value == kCFBooleanTrue) {
919			coversAll = true;
920			return;
921		}
922		if (isOmitRule(value))
923			forbiddenOmission |= !CFArrayContainsValue(allowed, range, key);
924	});
925
926	return !coversAll || forbiddenOmission;
927}
928
929
930//
931// Load, validate, cache, and return CFDictionary forms of sealed resources.
932//
933CFDictionaryRef SecStaticCode::infoDictionary()
934{
935	if (!mInfoDict) {
936		mInfoDict.take(getDictionary(cdInfoSlot, errSecCSInfoPlistFailed));
937		secdebug("staticCode", "%p loaded InfoDict %p", this, mInfoDict.get());
938	}
939	return mInfoDict;
940}
941
942CFDictionaryRef SecStaticCode::entitlements()
943{
944	if (!mEntitlements) {
945		validateDirectory();
946		if (CFDataRef entitlementData = component(cdEntitlementSlot)) {
947			validateComponent(cdEntitlementSlot);
948			const EntitlementBlob *blob = reinterpret_cast<const EntitlementBlob *>(CFDataGetBytePtr(entitlementData));
949			if (blob->validateBlob()) {
950				mEntitlements.take(blob->entitlements());
951				secdebug("staticCode", "%p loaded Entitlements %p", this, mEntitlements.get());
952			}
953			// we do not consider a different blob type to be an error. We think it's a new format we don't understand
954		}
955	}
956	return mEntitlements;
957}
958
959CFDictionaryRef SecStaticCode::resourceDictionary(bool check /* = true */)
960{
961	if (mResourceDict)	// cached
962		return mResourceDict;
963	if (CFRef<CFDictionaryRef> dict = getDictionary(cdResourceDirSlot, check))
964		if (cfscan(dict, "{rules=%Dn,files=%Dn}")) {
965			secdebug("staticCode", "%p loaded ResourceDict %p",
966				this, mResourceDict.get());
967			return mResourceDict = dict;
968		}
969	// bad format
970	return NULL;
971}
972
973
974//
975// Load and cache the resource directory base.
976// Note that the base is optional for each DiskRep.
977//
978CFURLRef SecStaticCode::resourceBase()
979{
980	if (!mGotResourceBase) {
981		string base = mRep->resourcesRootPath();
982		if (!base.empty())
983			mResourceBase.take(makeCFURL(base, true));
984		mGotResourceBase = true;
985	}
986	return mResourceBase;
987}
988
989
990//
991// Load a component, validate it, convert it to a CFDictionary, and return that.
992// This will force load and validation, which means that it will perform basic
993// validation if it hasn't been done yet.
994//
995CFDictionaryRef SecStaticCode::getDictionary(CodeDirectory::SpecialSlot slot, bool check /* = true */)
996{
997	if (check)
998		validateDirectory();
999	if (CFDataRef infoData = component(slot)) {
1000		validateComponent(slot);
1001		if (CFDictionaryRef dict = makeCFDictionaryFrom(infoData))
1002			return dict;
1003		else
1004			MacOSError::throwMe(errSecCSBadDictionaryFormat);
1005	}
1006	return NULL;
1007}
1008
1009
1010//
1011// Load, validate, and return a sealed resource.
1012// The resource data (loaded in to memory as a blob) is returned and becomes
1013// the responsibility of the caller; it is NOT cached by SecStaticCode.
1014//
1015// A resource that is not sealed will not be returned, and an error will be thrown.
1016// A missing resource will cause an error unless it's marked optional in the Directory.
1017// Under no circumstances will a corrupt resource be returned.
1018// NULL will only be returned for a resource that is neither sealed nor present
1019// (or that is sealed, absent, and marked optional).
1020// If the ResourceDictionary itself is not sealed, this function will always fail.
1021//
1022// There is currently no interface for partial retrieval of the resource data.
1023// (Since the ResourceDirectory does not currently support segmentation, all the
1024// data would have to be read anyway, but it could be read into a reusable buffer.)
1025//
1026CFDataRef SecStaticCode::resource(string path, ValidationContext &ctx)
1027{
1028	if (CFDictionaryRef rdict = resourceDictionary()) {
1029		if (CFTypeRef file = cfget(rdict, "files.%s", path.c_str())) {
1030			ResourceSeal seal = file;
1031			if (!resourceBase())	// no resources in DiskRep
1032				MacOSError::throwMe(errSecCSResourcesNotFound);
1033			if (seal.nested())
1034				MacOSError::throwMe(errSecCSResourcesNotSealed);	// (it's nested code)
1035			CFRef<CFURLRef> fullpath = makeCFURL(path, false, resourceBase());
1036			if (CFRef<CFDataRef> data = cfLoadFile(fullpath)) {
1037				MakeHash<CodeDirectory> hasher(this->codeDirectory());
1038				hasher->update(CFDataGetBytePtr(data), CFDataGetLength(data));
1039				if (hasher->verify(seal.hash()))
1040					return data.yield();	// good
1041				else
1042					ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // altered
1043			} else {
1044				if (!seal.optional())
1045					ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing, fullpath); // was sealed but is now missing
1046				else
1047					return NULL;	// validly missing
1048			}
1049		} else
1050			ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAdded, CFTempURL(path, false, resourceBase()));
1051		return NULL;
1052	} else
1053		MacOSError::throwMe(errSecCSResourcesNotSealed);
1054}
1055
1056CFDataRef SecStaticCode::resource(string path)
1057{
1058	ValidationContext ctx(*this);
1059	return resource(path, ctx);
1060}
1061
1062void SecStaticCode::validateResource(CFDictionaryRef files, string path, bool isSymlink, ValidationContext &ctx, SecCSFlags flags, uint32_t version)
1063{
1064	if (!resourceBase())	// no resources in DiskRep
1065		MacOSError::throwMe(errSecCSResourcesNotFound);
1066	CFRef<CFURLRef> fullpath = makeCFURL(path, false, resourceBase());
1067	if (CFTypeRef file = CFDictionaryGetValue(files, CFTempString(path))) {
1068		ResourceSeal seal = file;
1069		if (seal.nested()) {
1070			if (isSymlink)
1071				return ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // changed type
1072			string suffix = ".framework";
1073			bool isFramework = (path.length() > suffix.length())
1074				&& (path.compare(path.length()-suffix.length(), suffix.length(), suffix) == 0);
1075			validateNestedCode(fullpath, seal, flags, isFramework);
1076		} else if (seal.link()) {
1077			char target[PATH_MAX];
1078			ssize_t len = ::readlink(cfString(fullpath).c_str(), target, sizeof(target)-1);
1079			if (len < 0)
1080				UnixError::check(-1);
1081			target[len] = '\0';
1082			if (cfString(seal.link()) != target)
1083				ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath);
1084		} else if (seal.hash()) {	// genuine file
1085			AutoFileDesc fd(cfString(fullpath), O_RDONLY, FileDesc::modeMissingOk);	// open optional file
1086			if (fd) {
1087				MakeHash<CodeDirectory> hasher(this->codeDirectory());
1088				hashFileData(fd, hasher.get());
1089				if (hasher->verify(seal.hash()))
1090					return;			// verify good
1091				else
1092					ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // altered
1093			} else {
1094				if (!seal.optional())
1095					ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing, fullpath); // was sealed but is now missing
1096				else
1097					return;			// validly missing
1098			}
1099		} else
1100			ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // changed type
1101		return;
1102	}
1103	if (version == 1) {		// version 1 ignores symlinks altogether
1104		char target[PATH_MAX];
1105		if (::readlink(cfString(fullpath).c_str(), target, sizeof(target)) > 0)
1106			return;
1107	}
1108	ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAdded, CFTempURL(path, false, resourceBase()));
1109}
1110
1111void SecStaticCode::validateNestedCode(CFURLRef path, const ResourceSeal &seal, SecCSFlags flags, bool isFramework)
1112{
1113	CFRef<SecRequirementRef> req;
1114	if (SecRequirementCreateWithString(seal.requirement(), kSecCSDefaultFlags, &req.aref()))
1115		MacOSError::throwMe(errSecCSResourcesInvalid);
1116
1117	// recursively verify this nested code
1118	try {
1119		if (!(flags & kSecCSCheckNestedCode))
1120			flags |= kSecCSBasicValidateOnly;
1121		SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(cfString(path)));
1122		code->setMonitor(this->monitor());
1123		code->staticValidate(flags, SecRequirement::required(req));
1124
1125		if (isFramework && (flags & kSecCSStrictValidate))
1126			try {
1127				validateOtherVersions(path, flags, req, code);
1128			} catch (const CSError &err) {
1129				MacOSError::throwMe(errSecCSBadFrameworkVersion);
1130			} catch (const MacOSError &err) {
1131				MacOSError::throwMe(errSecCSBadFrameworkVersion);
1132			}
1133
1134	} catch (CSError &err) {
1135		if (err.error == errSecCSReqFailed) {
1136			mResourcesValidContext->reportProblem(errSecCSBadNestedCode, kSecCFErrorResourceAltered, path);
1137			return;
1138		}
1139		err.augment(kSecCFErrorPath, path);
1140		throw;
1141	} catch (const MacOSError &err) {
1142		if (err.error == errSecCSReqFailed) {
1143			mResourcesValidContext->reportProblem(errSecCSBadNestedCode, kSecCFErrorResourceAltered, path);
1144			return;
1145		}
1146		CSError::throwMe(err.error, kSecCFErrorPath, path);
1147	}
1148}
1149
1150void SecStaticCode::validateOtherVersions(CFURLRef path, SecCSFlags flags, SecRequirementRef req, SecStaticCode *code)
1151{
1152	// Find out what current points to and do not revalidate
1153	std::string mainPath = cfStringRelease(code->diskRep()->copyCanonicalPath());
1154
1155	char main_path[PATH_MAX];
1156	bool foundTarget = false;
1157
1158	/* If it failed to get the target of the symlink, do not fail. It is a performance loss,
1159	 not a security hole */
1160	if (realpath(mainPath.c_str(), main_path) != NULL)
1161		foundTarget = true;
1162
1163	std::ostringstream versionsPath;
1164	versionsPath << cfString(path) << "/Versions/";
1165
1166	DirScanner scanner(versionsPath.str());
1167
1168	if (scanner.initialized()) {
1169		struct dirent *entry = NULL;
1170		while ((entry = scanner.getNext()) != NULL) {
1171			std::ostringstream fullPath;
1172
1173			if (entry->d_type != DT_DIR ||
1174				strcmp(entry->d_name, ".") == 0 ||
1175				strcmp(entry->d_name, "..") == 0 ||
1176				strcmp(entry->d_name, "Current") == 0)
1177				continue;
1178
1179			fullPath << versionsPath.str() << entry->d_name;
1180
1181			char real_full_path[PATH_MAX];
1182			if (realpath(fullPath.str().c_str(), real_full_path) == NULL)
1183				UnixError::check(-1);
1184
1185			// Do case insensitive comparions because realpath() was called for both paths
1186			if (foundTarget && strcmp(main_path, real_full_path) == 0)
1187				continue;
1188
1189			SecPointer<SecStaticCode> frameworkVersion = new SecStaticCode(DiskRep::bestGuess(real_full_path));
1190			frameworkVersion->setMonitor(this->monitor());
1191			frameworkVersion->staticValidate(flags, SecRequirement::required(req));
1192		}
1193	}
1194}
1195
1196
1197//
1198// Test a CodeDirectory flag.
1199// Returns false if there is no CodeDirectory.
1200// May throw if the CodeDirectory is present but somehow invalid.
1201//
1202bool SecStaticCode::flag(uint32_t tested)
1203{
1204	if (const CodeDirectory *cd = this->codeDirectory(false))
1205		return cd->flags & tested;
1206	else
1207		return false;
1208}
1209
1210
1211//
1212// Retrieve the full SuperBlob containing all internal requirements.
1213//
1214const Requirements *SecStaticCode::internalRequirements()
1215{
1216	if (CFDataRef reqData = component(cdRequirementsSlot)) {
1217		const Requirements *req = (const Requirements *)CFDataGetBytePtr(reqData);
1218		if (!req->validateBlob())
1219			MacOSError::throwMe(errSecCSReqInvalid);
1220		return req;
1221	} else
1222		return NULL;
1223}
1224
1225
1226//
1227// Retrieve a particular internal requirement by type.
1228//
1229const Requirement *SecStaticCode::internalRequirement(SecRequirementType type)
1230{
1231	if (const Requirements *reqs = internalRequirements())
1232		return reqs->find<Requirement>(type);
1233	else
1234		return NULL;
1235}
1236
1237
1238//
1239// Return the Designated Requirement (DR). This can be either explicit in the
1240// Internal Requirements component, or implicitly generated on demand here.
1241// Note that an explicit DR may have been implicitly generated at signing time;
1242// we don't distinguish this case.
1243//
1244const Requirement *SecStaticCode::designatedRequirement()
1245{
1246	if (const Requirement *req = internalRequirement(kSecDesignatedRequirementType)) {
1247		return req;		// explicit in signing data
1248	} else {
1249		if (!mDesignatedReq)
1250			mDesignatedReq = defaultDesignatedRequirement();
1251		return mDesignatedReq;
1252	}
1253}
1254
1255
1256//
1257// Generate the default Designated Requirement (DR) for this StaticCode.
1258// Ignore any explicit DR it may contain.
1259//
1260const Requirement *SecStaticCode::defaultDesignatedRequirement()
1261{
1262	if (flag(kSecCodeSignatureAdhoc)) {
1263		// adhoc signature: return a cdhash requirement for all architectures
1264		__block Requirement::Maker maker;
1265		Requirement::Maker::Chain chain(maker, opOr);
1266
1267		// insert cdhash requirement for all architectures
1268		chain.add();
1269		maker.cdhash(this->cdHash());
1270		handleOtherArchitectures(^(SecStaticCode *subcode) {
1271			if (CFDataRef cdhash = subcode->cdHash()) {
1272				chain.add();
1273				maker.cdhash(cdhash);
1274			}
1275		});
1276		return maker.make();
1277	} else {
1278		// full signature: Gin up full context and let DRMaker do its thing
1279		validateDirectory();		// need the cert chain
1280		Requirement::Context context(this->certificates(),
1281			this->infoDictionary(),
1282			this->entitlements(),
1283			this->identifier(),
1284			this->codeDirectory()
1285		);
1286		return DRMaker(context).make();
1287	}
1288}
1289
1290
1291//
1292// Validate a SecStaticCode against the internal requirement of a particular type.
1293//
1294void SecStaticCode::validateRequirements(SecRequirementType type, SecStaticCode *target,
1295	OSStatus nullError /* = errSecSuccess */)
1296{
1297	DTRACK(CODESIGN_EVAL_STATIC_INTREQ, this, type, target, nullError);
1298	if (const Requirement *req = internalRequirement(type))
1299		target->validateRequirement(req, nullError ? nullError : errSecCSReqFailed);
1300	else if (nullError)
1301		MacOSError::throwMe(nullError);
1302	else
1303		/* accept it */;
1304}
1305
1306
1307//
1308// Validate this StaticCode against an external Requirement
1309//
1310bool SecStaticCode::satisfiesRequirement(const Requirement *req, OSStatus failure)
1311{
1312	assert(req);
1313	validateDirectory();
1314	return req->validates(Requirement::Context(mCertChain, infoDictionary(), entitlements(), codeDirectory()->identifier(), codeDirectory()), failure);
1315}
1316
1317void SecStaticCode::validateRequirement(const Requirement *req, OSStatus failure)
1318{
1319	if (!this->satisfiesRequirement(req, failure))
1320		MacOSError::throwMe(failure);
1321}
1322
1323
1324//
1325// Retrieve one certificate from the cert chain.
1326// Positive and negative indices can be used:
1327//    [ leaf, intermed-1, ..., intermed-n, anchor ]
1328//        0       1       ...     -2         -1
1329// Returns NULL if unavailable for any reason.
1330//
1331SecCertificateRef SecStaticCode::cert(int ix)
1332{
1333	validateDirectory();		// need cert chain
1334	if (mCertChain) {
1335		CFIndex length = CFArrayGetCount(mCertChain);
1336		if (ix < 0)
1337			ix += length;
1338		if (ix >= 0 && ix < length)
1339			return SecCertificateRef(CFArrayGetValueAtIndex(mCertChain, ix));
1340	}
1341	return NULL;
1342}
1343
1344CFArrayRef SecStaticCode::certificates()
1345{
1346	validateDirectory();		// need cert chain
1347	return mCertChain;
1348}
1349
1350
1351//
1352// Gather (mostly) API-official information about this StaticCode.
1353//
1354// This method lives in the twilight between the API and internal layers,
1355// since it generates API objects (Sec*Refs) for return.
1356//
1357CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
1358{
1359	//
1360	// Start with the pieces that we return even for unsigned code.
1361	// This makes Sec[Static]CodeRefs useful as API-level replacements
1362	// of our internal OSXCode objects.
1363	//
1364	CFRef<CFMutableDictionaryRef> dict = makeCFMutableDictionary(1,
1365		kSecCodeInfoMainExecutable, CFTempURL(this->mainExecutablePath()).get()
1366	);
1367
1368	//
1369	// If we're not signed, this is all you get
1370	//
1371	if (!this->isSigned())
1372		return dict.yield();
1373
1374	//
1375	// Add the generic attributes that we always include
1376	//
1377	CFDictionaryAddValue(dict, kSecCodeInfoIdentifier, CFTempString(this->identifier()));
1378	CFDictionaryAddValue(dict, kSecCodeInfoFlags, CFTempNumber(this->codeDirectory(false)->flags.get()));
1379	CFDictionaryAddValue(dict, kSecCodeInfoFormat, CFTempString(this->format()));
1380	CFDictionaryAddValue(dict, kSecCodeInfoSource, CFTempString(this->signatureSource()));
1381	CFDictionaryAddValue(dict, kSecCodeInfoUnique, this->cdHash());
1382	CFDictionaryAddValue(dict, kSecCodeInfoDigestAlgorithm, CFTempNumber(this->codeDirectory(false)->hashType));
1383
1384	//
1385	// Deliver any Info.plist only if it looks intact
1386	//
1387	try {
1388		if (CFDictionaryRef info = this->infoDictionary())
1389			CFDictionaryAddValue(dict, kSecCodeInfoPList, info);
1390	} catch (...) { }		// don't deliver Info.plist if questionable
1391
1392	//
1393	// kSecCSSigningInformation adds information about signing certificates and chains
1394	//
1395	if (flags & kSecCSSigningInformation)
1396		try {
1397			if (CFArrayRef certs = this->certificates())
1398				CFDictionaryAddValue(dict, kSecCodeInfoCertificates, certs);
1399			if (CFDataRef sig = this->signature())
1400				CFDictionaryAddValue(dict, kSecCodeInfoCMS, sig);
1401			if (mTrust)
1402				CFDictionaryAddValue(dict, kSecCodeInfoTrust, mTrust);
1403			if (CFAbsoluteTime time = this->signingTime())
1404				if (CFRef<CFDateRef> date = CFDateCreate(NULL, time))
1405					CFDictionaryAddValue(dict, kSecCodeInfoTime, date);
1406			if (CFAbsoluteTime time = this->signingTimestamp())
1407				if (CFRef<CFDateRef> date = CFDateCreate(NULL, time))
1408					CFDictionaryAddValue(dict, kSecCodeInfoTimestamp, date);
1409			if (const char *teamID = this->teamID())
1410				CFDictionaryAddValue(dict, kSecCodeInfoTeamIdentifier, CFTempString(teamID));
1411		} catch (...) { }
1412
1413	//
1414	// kSecCSRequirementInformation adds information on requirements
1415	//
1416	if (flags & kSecCSRequirementInformation)
1417		try {
1418			if (const Requirements *reqs = this->internalRequirements()) {
1419				CFDictionaryAddValue(dict, kSecCodeInfoRequirements,
1420					CFTempString(Dumper::dump(reqs)));
1421				CFDictionaryAddValue(dict, kSecCodeInfoRequirementData, CFTempData(*reqs));
1422			}
1423
1424			const Requirement *dreq = this->designatedRequirement();
1425			CFRef<SecRequirementRef> dreqRef = (new SecRequirement(dreq))->handle();
1426			CFDictionaryAddValue(dict, kSecCodeInfoDesignatedRequirement, dreqRef);
1427			if (this->internalRequirement(kSecDesignatedRequirementType)) {	// explicit
1428				CFRef<SecRequirementRef> ddreqRef = (new SecRequirement(this->defaultDesignatedRequirement(), true))->handle();
1429				CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, ddreqRef);
1430			} else {	// implicit
1431				CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, dreqRef);
1432			}
1433		} catch (...) { }
1434
1435		try {
1436		   if (CFDataRef ent = this->component(cdEntitlementSlot)) {
1437			   CFDictionaryAddValue(dict, kSecCodeInfoEntitlements, ent);
1438			   if (CFDictionaryRef entdict = this->entitlements())
1439					CFDictionaryAddValue(dict, kSecCodeInfoEntitlementsDict, entdict);
1440			}
1441		} catch (...) { }
1442
1443	//
1444	// kSecCSInternalInformation adds internal information meant to be for Apple internal
1445	// use (SPI), and not guaranteed to be stable. Primarily, this is data we want
1446	// to reliably transmit through the API wall so that code outside the Security.framework
1447	// can use it without having to play nasty tricks to get it.
1448	//
1449	if (flags & kSecCSInternalInformation)
1450		try {
1451			if (mDir)
1452				CFDictionaryAddValue(dict, kSecCodeInfoCodeDirectory, mDir);
1453			CFDictionaryAddValue(dict, kSecCodeInfoCodeOffset, CFTempNumber(mRep->signingBase()));
1454		if (CFRef<CFDictionaryRef> rdict = getDictionary(cdResourceDirSlot, false))	// suppress validation
1455			CFDictionaryAddValue(dict, kSecCodeInfoResourceDirectory, rdict);
1456		} catch (...) { }
1457
1458
1459	//
1460	// kSecCSContentInformation adds more information about the physical layout
1461	// of the signed code. This is (only) useful for packaging or patching-oriented
1462	// applications.
1463	//
1464	if (flags & kSecCSContentInformation)
1465		if (CFRef<CFArrayRef> files = mRep->modifiedFiles())
1466			CFDictionaryAddValue(dict, kSecCodeInfoChangedFiles, files);
1467
1468	return dict.yield();
1469}
1470
1471
1472//
1473// Resource validation contexts.
1474// The default context simply throws a CSError, rudely terminating the operation.
1475//
1476SecStaticCode::ValidationContext::~ValidationContext()
1477{ /* virtual */ }
1478
1479void SecStaticCode::ValidationContext::reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value)
1480{
1481	CSError::throwMe(rc, type, value);
1482}
1483
1484void SecStaticCode::CollectingContext::reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value)
1485{
1486	if (mStatus == errSecSuccess)
1487		mStatus = rc;			// record first failure for eventual error return
1488	if (type) {
1489		if (!mCollection)
1490			mCollection.take(makeCFMutableDictionary());
1491		CFMutableArrayRef element = CFMutableArrayRef(CFDictionaryGetValue(mCollection, type));
1492		if (!element) {
1493			element = makeCFMutableArray(0);
1494			if (!element)
1495				CFError::throwMe();
1496			CFDictionaryAddValue(mCollection, type, element);
1497			CFRelease(element);
1498		}
1499		CFArrayAppendValue(element, value);
1500	}
1501}
1502
1503void SecStaticCode::CollectingContext::throwMe()
1504{
1505	assert(mStatus != errSecSuccess);
1506	throw CSError(mStatus, mCollection.retain());
1507}
1508
1509
1510//
1511// Master validation driver.
1512// This is the static validation (only) driver for the API.
1513//
1514// SecStaticCode exposes an a la carte menu of topical validators applying
1515// to a given object. The static validation API pulls them together reliably,
1516// but it also adds two matrix dimensions: architecture (for "fat" Mach-O binaries)
1517// and nested code. This function will crawl a suitable cross-section of this
1518// validation matrix based on which options it is given, creating temporary
1519// SecStaticCode objects on the fly to complete the task.
1520// (The point, of course, is to do as little duplicate work as possible.)
1521//
1522void SecStaticCode::staticValidate(SecCSFlags flags, const SecRequirement *req)
1523{
1524	setValidationFlags(flags);
1525
1526	// initialize progress/cancellation state
1527	prepareProgress(estimateResourceWorkload() + 2);	// +1 head, +1 tail
1528
1529	// core components: once per architecture (if any)
1530	this->staticValidateCore(flags, req);
1531	if (flags & kSecCSCheckAllArchitectures)
1532		handleOtherArchitectures(^(SecStaticCode* subcode) {
1533			subcode->detachedSignature(this->mDetachedSig);	// carry over explicit (but not implicit) architecture
1534			subcode->staticValidateCore(flags, req);
1535		});
1536	reportProgress();
1537
1538	// allow monitor intervention in source validation phase
1539	reportEvent(CFSTR("prepared"), NULL);
1540
1541	// resources: once for all architectures
1542	if (!(flags & kSecCSDoNotValidateResources))
1543		this->validateResources(flags);
1544
1545	// perform strict validation if desired
1546	if (flags & kSecCSStrictValidate)
1547		mRep->strictValidate(mTolerateErrors);
1548	reportProgress();
1549
1550	// allow monitor intervention
1551	if (CFRef<CFTypeRef> veto = reportEvent(CFSTR("validated"), NULL)) {
1552		if (CFGetTypeID(veto) == CFNumberGetTypeID())
1553			MacOSError::throwMe(cfNumber<OSStatus>(veto.as<CFNumberRef>()));
1554		else
1555			MacOSError::throwMe(errSecCSBadCallbackValue);
1556	}
1557}
1558
1559void SecStaticCode::staticValidateCore(SecCSFlags flags, const SecRequirement *req)
1560{
1561	try {
1562		this->validateNonResourceComponents();	// also validates the CodeDirectory
1563		if (!(flags & kSecCSDoNotValidateExecutable))
1564			this->validateExecutable();
1565		if (req)
1566			this->validateRequirement(req->requirement(), errSecCSReqFailed);
1567    } catch (CSError &err) {
1568        if (Universal *fat = this->diskRep()->mainExecutableImage())    // Mach-O
1569            if (MachO *mach = fat->architecture()) {
1570                err.augment(kSecCFErrorArchitecture, CFTempString(mach->architecture().displayName()));
1571                delete mach;
1572            }
1573        throw;
1574    } catch (const MacOSError &err) {
1575        // add architecture information if we can get it
1576        if (Universal *fat = this->diskRep()->mainExecutableImage())
1577            if (MachO *mach = fat->architecture()) {
1578                CFTempString arch(mach->architecture().displayName());
1579                delete mach;
1580                CSError::throwMe(err.error, kSecCFErrorArchitecture, arch);
1581            }
1582        throw;
1583    }
1584}
1585
1586
1587//
1588// A helper that generates SecStaticCode objects for all but the primary architecture
1589// of a fat binary and calls a block on them.
1590// If there's only one architecture (or this is an architecture-agnostic code),
1591// nothing happens quickly.
1592//
1593void SecStaticCode::handleOtherArchitectures(void (^handle)(SecStaticCode* other))
1594{
1595	if (Universal *fat = this->diskRep()->mainExecutableImage()) {
1596		Universal::Architectures architectures;
1597		fat->architectures(architectures);
1598		if (architectures.size() > 1) {
1599			DiskRep::Context ctx;
1600			size_t activeOffset = fat->archOffset();
1601			for (Universal::Architectures::const_iterator arch = architectures.begin(); arch != architectures.end(); ++arch) {
1602				ctx.offset = fat->archOffset(*arch);
1603				if (ctx.offset > SIZE_MAX)
1604					MacOSError::throwMe(errSecCSInternalError);
1605				ctx.size = fat->lengthOfSlice((size_t)ctx.offset);
1606				if (ctx.offset != activeOffset) {	// inactive architecture; check it
1607					SecPointer<SecStaticCode> subcode = new SecStaticCode(DiskRep::bestGuess(this->mainExecutablePath(), &ctx));
1608					subcode->detachedSignature(this->mDetachedSig); // carry over explicit (but not implicit) detached signature
1609					if (this->teamID() == NULL || subcode->teamID() == NULL) {
1610						if (this->teamID() != subcode->teamID())
1611							MacOSError::throwMe(errSecCSSignatureInvalid);
1612					} else if (strcmp(this->teamID(), subcode->teamID()) != 0)
1613						MacOSError::throwMe(errSecCSSignatureInvalid);
1614					handle(subcode);
1615				}
1616			}
1617		}
1618	}
1619}
1620
1621//
1622// A method that takes a certificate chain (certs) and evaluates
1623// if it is a Mac or IPhone developer cert, an app store distribution cert,
1624// or a developer ID
1625//
1626bool SecStaticCode::isAppleDeveloperCert(CFArrayRef certs)
1627{
1628	static const std::string appleDeveloperRequirement = "(" + std::string(WWDRRequirement) + ") or (" + MACWWDRRequirement + ") or (" + developerID + ") or (" + distributionCertificate + ") or (" + iPhoneDistributionCert + ")";
1629	SecPointer<SecRequirement> req = new SecRequirement(parseRequirement(appleDeveloperRequirement), true);
1630	Requirement::Context ctx(certs, NULL, NULL, "", NULL);
1631
1632	return req->requirement()->validates(ctx);
1633}
1634
1635} // end namespace CodeSigning
1636} // end namespace Security
1637