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