1/*
2 * Copyright (c) 2006 Apple Computer, 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// reqinterp - Requirement language (exprOp) interpreter
26//
27#include "reqinterp.h"
28#include "codesigning_dtrace.h"
29#include <Security/SecTrustSettingsPriv.h>
30#include <Security/SecCertificatePriv.h>
31#include <security_utilities/memutils.h>
32#include <security_utilities/logging.h>
33#include "csutilities.h"
34
35namespace Security {
36namespace CodeSigning {
37
38
39//
40// Fragment fetching, caching, and evaluation.
41//
42// Several language elements allow "calling" of separate requirement programs
43// stored on disk as (binary) requirement blobs. The Fragments class takes care
44// of finding, loading, caching, and evaluating them.
45//
46// This is a singleton for (process global) caching. It works fine as multiple instances,
47// at a loss of caching effectiveness.
48//
49class Fragments {
50public:
51	Fragments();
52
53	bool named(const std::string &name, const Requirement::Context &ctx)
54		{ return evalNamed("subreq", name, ctx); }
55	bool namedAnchor(const std::string &name, const Requirement::Context &ctx)
56		{ return evalNamed("anchorreq", name, ctx); }
57
58private:
59	bool evalNamed(const char *type, const std::string &name, const Requirement::Context &ctx);
60	CFDataRef fragment(const char *type, const std::string &name);
61
62	typedef std::map<std::string, CFRef<CFDataRef> > FragMap;
63
64private:
65	CFBundleRef mMyBundle;			// Security.framework bundle
66	Mutex mLock;					// lock for all of the below...
67	FragMap mFragments;				// cached fragments
68};
69
70static ModuleNexus<Fragments> fragments;
71
72
73//
74// Magic certificate features
75//
76static CFStringRef appleIntermediateCN = CFSTR("Apple Code Signing Certification Authority");
77static CFStringRef appleIntermediateO = CFSTR("Apple Inc.");
78
79
80//
81// Main interpreter function.
82//
83// ExprOp code is in Polish Notation (operator followed by operands),
84// and this engine uses opportunistic evaluation.
85//
86bool Requirement::Interpreter::evaluate()
87{
88	ExprOp op = ExprOp(get<uint32_t>());
89	CODESIGN_EVAL_REQINT_OP(op, this->pc() - sizeof(uint32_t));
90	switch (op & ~opFlagMask) {
91	case opFalse:
92		return false;
93	case opTrue:
94		return true;
95	case opIdent:
96		return mContext->directory && getString() == mContext->directory->identifier();
97	case opAppleAnchor:
98		return appleSigned();
99	case opAppleGenericAnchor:
100		return appleAnchored();
101	case opAnchorHash:
102		{
103			SecCertificateRef cert = mContext->cert(get<int32_t>());
104			return verifyAnchor(cert, getSHA1());
105		}
106	case opInfoKeyValue:	// [legacy; use opInfoKeyField]
107		{
108			string key = getString();
109			return infoKeyValue(key, Match(CFTempString(getString()), matchEqual));
110		}
111	case opAnd:
112		return evaluate() & evaluate();
113	case opOr:
114		return evaluate() | evaluate();
115	case opCDHash:
116		if (mContext->directory) {
117			SHA1 hash;
118			hash(mContext->directory, mContext->directory->length());
119			return hash.verify(getHash());
120		} else
121			return false;
122	case opNot:
123		return !evaluate();
124	case opInfoKeyField:
125		{
126			string key = getString();
127			Match match(*this);
128			return infoKeyValue(key, match);
129		}
130	case opEntitlementField:
131		{
132			string key = getString();
133			Match match(*this);
134			return entitlementValue(key, match);
135		}
136	case opCertField:
137		{
138			SecCertificateRef cert = mContext->cert(get<int32_t>());
139			string key = getString();
140			Match match(*this);
141			return certFieldValue(key, match, cert);
142		}
143	case opCertGeneric:
144		{
145			SecCertificateRef cert = mContext->cert(get<int32_t>());
146			string key = getString();
147			Match match(*this);
148			return certFieldGeneric(key, match, cert);
149		}
150	case opCertPolicy:
151		{
152			SecCertificateRef cert = mContext->cert(get<int32_t>());
153			string key = getString();
154			Match match(*this);
155			return certFieldPolicy(key, match, cert);
156		}
157	case opTrustedCert:
158		return trustedCert(get<int32_t>());
159	case opTrustedCerts:
160		return trustedCerts();
161	case opNamedAnchor:
162		return fragments().namedAnchor(getString(), *mContext);
163	case opNamedCode:
164		return fragments().named(getString(), *mContext);
165	default:
166		// opcode not recognized - handle generically if possible, fail otherwise
167		if (op & (opGenericFalse | opGenericSkip)) {
168			// unknown opcode, but it has a size field and can be safely bypassed
169			skip(get<uint32_t>());
170			if (op & opGenericFalse) {
171				CODESIGN_EVAL_REQINT_UNKNOWN_FALSE(op);
172				return false;
173			} else {
174				CODESIGN_EVAL_REQINT_UNKNOWN_SKIPPED(op);
175				return evaluate();
176			}
177		}
178		// unrecognized opcode and no way to interpret it
179		secdebug("csinterp", "opcode 0x%x cannot be handled; aborting", op);
180		MacOSError::throwMe(errSecCSUnimplemented);
181	}
182}
183
184
185//
186// Evaluate an Info.plist key condition
187//
188bool Requirement::Interpreter::infoKeyValue(const string &key, const Match &match)
189{
190	if (mContext->info)		// we have an Info.plist
191		if (CFTypeRef value = CFDictionaryGetValue(mContext->info, CFTempString(key)))
192			return match(value);
193	return false;
194}
195
196
197//
198// Evaluate an entitlement condition
199//
200bool Requirement::Interpreter::entitlementValue(const string &key, const Match &match)
201{
202	if (mContext->entitlements)		// we have an Info.plist
203		if (CFTypeRef value = CFDictionaryGetValue(mContext->entitlements, CFTempString(key)))
204			return match(value);
205	return false;
206}
207
208
209bool Requirement::Interpreter::certFieldValue(const string &key, const Match &match, SecCertificateRef cert)
210{
211	// no cert, no chance
212	if (cert == NULL)
213		return false;
214
215	// a table of recognized keys for the "certificate[foo]" syntax
216	static const struct CertField {
217		const char *name;
218		const CSSM_OID *oid;
219	} certFields[] = {
220		{ "subject.C", &CSSMOID_CountryName },
221		{ "subject.CN", &CSSMOID_CommonName },
222		{ "subject.D", &CSSMOID_Description },
223		{ "subject.L", &CSSMOID_LocalityName },
224//		{ "subject.C-L", &CSSMOID_CollectiveLocalityName },	// missing from Security.framework headers
225		{ "subject.O", &CSSMOID_OrganizationName },
226		{ "subject.C-O", &CSSMOID_CollectiveOrganizationName },
227		{ "subject.OU", &CSSMOID_OrganizationalUnitName },
228		{ "subject.C-OU", &CSSMOID_CollectiveOrganizationalUnitName },
229		{ "subject.ST", &CSSMOID_StateProvinceName },
230		{ "subject.C-ST", &CSSMOID_CollectiveStateProvinceName },
231		{ "subject.STREET", &CSSMOID_StreetAddress },
232		{ "subject.C-STREET", &CSSMOID_CollectiveStreetAddress },
233		{ "subject.UID", &CSSMOID_UserID },
234		{ NULL, NULL }
235	};
236
237	// DN-component single-value match
238	for (const CertField *cf = certFields; cf->name; cf++)
239		if (cf->name == key) {
240			CFRef<CFStringRef> value;
241			if (OSStatus rc = SecCertificateCopySubjectComponent(cert, cf->oid, &value.aref())) {
242				secdebug("csinterp", "cert %p lookup for DN.%s failed rc=%d", cert, key.c_str(), (int)rc);
243				return false;
244			}
245			return match(value);
246		}
247
248	// email multi-valued match (any of...)
249	if (key == "email") {
250		CFRef<CFArrayRef> value;
251		if (OSStatus rc = SecCertificateCopyEmailAddresses(cert, &value.aref())) {
252			secdebug("csinterp", "cert %p lookup for email failed rc=%d", cert, (int)rc);
253			return false;
254		}
255		return match(value);
256	}
257
258	// unrecognized key. Fail but do not abort to promote backward compatibility down the road
259	secdebug("csinterp", "cert field notation \"%s\" not understood", key.c_str());
260	return false;
261}
262
263
264bool Requirement::Interpreter::certFieldGeneric(const string &key, const Match &match, SecCertificateRef cert)
265{
266	// the key is actually a (binary) OID value
267	CssmOid oid((char *)key.data(), key.length());
268	return certFieldGeneric(oid, match, cert);
269}
270
271bool Requirement::Interpreter::certFieldGeneric(const CssmOid &oid, const Match &match, SecCertificateRef cert)
272{
273	return cert && certificateHasField(cert, oid) && match(kCFBooleanTrue);
274}
275
276bool Requirement::Interpreter::certFieldPolicy(const string &key, const Match &match, SecCertificateRef cert)
277{
278	// the key is actually a (binary) OID value
279	CssmOid oid((char *)key.data(), key.length());
280	return certFieldPolicy(oid, match, cert);
281}
282
283bool Requirement::Interpreter::certFieldPolicy(const CssmOid &oid, const Match &match, SecCertificateRef cert)
284{
285	return cert && certificateHasPolicy(cert, oid) && match(kCFBooleanTrue);
286}
287
288
289//
290// Check the Apple-signed condition
291//
292bool Requirement::Interpreter::appleAnchored()
293{
294	if (SecCertificateRef cert = mContext->cert(anchorCert))
295		if (isAppleCA(cert)
296#if defined(TEST_APPLE_ANCHOR)
297			|| verifyAnchor(cert, testAppleAnchorHash())
298#endif
299		)
300		return true;
301	return false;
302}
303
304bool Requirement::Interpreter::appleSigned()
305{
306	if (appleAnchored())
307		if (SecCertificateRef intermed = mContext->cert(-2))	// first intermediate
308			// first intermediate common name match (exact)
309			if (certFieldValue("subject.CN", Match(appleIntermediateCN, matchEqual), intermed)
310					&& certFieldValue("subject.O", Match(appleIntermediateO, matchEqual), intermed))
311				return true;
312	return false;
313}
314
315
316//
317// Verify an anchor requirement against the context
318//
319bool Requirement::Interpreter::verifyAnchor(SecCertificateRef cert, const unsigned char *digest)
320{
321	// get certificate bytes
322	if (cert) {
323		CSSM_DATA certData;
324		MacOSError::check(SecCertificateGetData(cert, &certData));
325
326		// verify hash
327		//@@@ should get SHA1(cert(-1).data) precalculated during chain verification
328		SHA1 hasher;
329		hasher(certData.Data, certData.Length);
330		return hasher.verify(digest);
331	}
332	return false;
333}
334
335
336//
337// Check one or all certificate(s) in the cert chain against the Trust Settings database.
338//
339bool Requirement::Interpreter::trustedCerts()
340{
341	int anchor = mContext->certCount() - 1;
342	for (int slot = 0; slot <= anchor; slot++)
343		if (SecCertificateRef cert = mContext->cert(slot))
344			switch (trustSetting(cert, slot == anchor)) {
345			case kSecTrustSettingsResultTrustRoot:
346			case kSecTrustSettingsResultTrustAsRoot:
347				return true;
348			case kSecTrustSettingsResultDeny:
349				return false;
350			case kSecTrustSettingsResultUnspecified:
351				break;
352			default:
353				assert(false);
354				return false;
355			}
356		else
357			return false;
358	return false;
359}
360
361bool Requirement::Interpreter::trustedCert(int slot)
362{
363	if (SecCertificateRef cert = mContext->cert(slot)) {
364		int anchorSlot = mContext->certCount() - 1;
365		switch (trustSetting(cert, slot == anchorCert || slot == anchorSlot)) {
366		case kSecTrustSettingsResultTrustRoot:
367		case kSecTrustSettingsResultTrustAsRoot:
368			return true;
369		case kSecTrustSettingsResultDeny:
370		case kSecTrustSettingsResultUnspecified:
371			return false;
372		default:
373			assert(false);
374			return false;
375		}
376	} else
377		return false;
378}
379
380
381//
382// Explicitly check one certificate against the Trust Settings database and report
383// the findings. This is a helper for the various Trust Settings evaluators.
384//
385SecTrustSettingsResult Requirement::Interpreter::trustSetting(SecCertificateRef cert, bool isAnchor)
386{
387	// the SPI input is the uppercase hex form of the SHA-1 of the certificate...
388	assert(cert);
389	SHA1::Digest digest;
390	hashOfCertificate(cert, digest);
391	string Certhex = CssmData(digest, sizeof(digest)).toHex();
392	for (string::iterator it = Certhex.begin(); it != Certhex.end(); ++it)
393		if (islower(*it))
394			*it = toupper(*it);
395
396	// call Trust Settings and see what it finds
397	SecTrustSettingsDomain domain;
398	SecTrustSettingsResult result;
399	CSSM_RETURN *errors = NULL;
400	uint32 errorCount = 0;
401	bool foundMatch, foundAny;
402	switch (OSStatus rc = SecTrustSettingsEvaluateCert(
403		CFTempString(Certhex),					// settings index
404		&CSSMOID_APPLE_TP_CODE_SIGNING,			// standard code signing policy
405		NULL, 0,								// policy string (unused)
406		kSecTrustSettingsKeyUseAny,				// no restriction on key usage @@@
407		isAnchor,								// consult system default anchor set
408
409		&domain,								// domain of found setting
410		&errors, &errorCount,					// error set and maximum count
411		&result,								// the actual setting
412		&foundMatch, &foundAny					// optimization hints (not used)
413		)) {
414	case errSecSuccess:
415		::free(errors);
416		if (foundMatch)
417			return result;
418		else
419			return kSecTrustSettingsResultUnspecified;
420	default:
421		::free(errors);
422		MacOSError::throwMe(rc);
423	}
424}
425
426
427//
428// Create a Match object from the interpreter stream
429//
430Requirement::Interpreter::Match::Match(Interpreter &interp)
431{
432	switch (mOp = interp.get<MatchOperation>()) {
433	case matchExists:
434		break;
435	case matchEqual:
436	case matchContains:
437	case matchBeginsWith:
438	case matchEndsWith:
439	case matchLessThan:
440	case matchGreaterThan:
441	case matchLessEqual:
442	case matchGreaterEqual:
443		mValue.take(makeCFString(interp.getString()));
444		break;
445	default:
446		// Assume this (unknown) match type has a single data argument.
447		// This gives us a chance to keep the instruction stream aligned.
448		interp.getString();			// discard
449		break;
450	}
451}
452
453
454//
455// Execute a match against a candidate value
456//
457bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate) const
458{
459	// null candidates always fail
460	if (!candidate)
461		return false;
462
463	// interpret an array as matching alternatives (any one succeeds)
464	if (CFGetTypeID(candidate) == CFArrayGetTypeID()) {
465		CFArrayRef array = CFArrayRef(candidate);
466		CFIndex count = CFArrayGetCount(array);
467		for (CFIndex n = 0; n < count; n++)
468			if ((*this)(CFArrayGetValueAtIndex(array, n)))	// yes, it's recursive
469				return true;
470	}
471
472	switch (mOp) {
473	case matchExists:		// anything but NULL and boolean false "exists"
474		return !CFEqual(candidate, kCFBooleanFalse);
475	case matchEqual:		// equality works for all CF types
476		return CFEqual(candidate, mValue);
477	case matchContains:
478		if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
479			CFStringRef value = CFStringRef(candidate);
480			if (CFStringFindWithOptions(value, mValue, CFRangeMake(0, CFStringGetLength(value)), 0, NULL))
481				return true;
482		}
483		return false;
484	case matchBeginsWith:
485		if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
486			CFStringRef value = CFStringRef(candidate);
487			if (CFStringFindWithOptions(value, mValue, CFRangeMake(0, CFStringGetLength(mValue)), 0, NULL))
488				return true;
489		}
490		return false;
491	case matchEndsWith:
492		if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
493			CFStringRef value = CFStringRef(candidate);
494			CFIndex matchLength = CFStringGetLength(mValue);
495			CFIndex start = CFStringGetLength(value) - matchLength;
496			if (start >= 0)
497				if (CFStringFindWithOptions(value, mValue, CFRangeMake(start, matchLength), 0, NULL))
498					return true;
499		}
500		return false;
501	case matchLessThan:
502		return inequality(candidate, kCFCompareNumerically, kCFCompareLessThan, true);
503	case matchGreaterThan:
504		return inequality(candidate, kCFCompareNumerically, kCFCompareGreaterThan, true);
505	case matchLessEqual:
506		return inequality(candidate, kCFCompareNumerically, kCFCompareGreaterThan, false);
507	case matchGreaterEqual:
508		return inequality(candidate, kCFCompareNumerically, kCFCompareLessThan, false);
509	default:
510		// unrecognized match types can never match
511		return false;
512	}
513}
514
515
516bool Requirement::Interpreter::Match::inequality(CFTypeRef candidate, CFStringCompareFlags flags,
517	CFComparisonResult outcome, bool negate) const
518{
519	if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
520		CFStringRef value = CFStringRef(candidate);
521		if ((CFStringCompare(value, mValue, flags) == outcome) == negate)
522			return true;
523	}
524	return false;
525}
526
527
528//
529// External fragments
530//
531Fragments::Fragments()
532{
533	mMyBundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security"));
534}
535
536
537bool Fragments::evalNamed(const char *type, const std::string &name, const Requirement::Context &ctx)
538{
539	if (CFDataRef fragData = fragment(type, name)) {
540		const Requirement *req = (const Requirement *)CFDataGetBytePtr(fragData);	// was prevalidated as Requirement
541		return req->validates(ctx);
542	}
543	return false;
544}
545
546
547CFDataRef Fragments::fragment(const char *type, const std::string &name)
548{
549	string key = name + "!!" + type;	// compound key
550	StLock<Mutex> _(mLock);				// lock for cache access
551	FragMap::const_iterator it = mFragments.find(key);
552	if (it == mFragments.end()) {
553		CFRef<CFDataRef> fragData;		// will always be set (NULL on any errors)
554		if (CFRef<CFURLRef> fragURL = CFBundleCopyResourceURL(mMyBundle, CFTempString(name), CFSTR("csreq"), CFTempString(type)))
555			if (CFRef<CFDataRef> data = cfLoadFile(fragURL)) {	// got data
556				const Requirement *req = (const Requirement *)CFDataGetBytePtr(data);
557				if (req->validateBlob(CFDataGetLength(data)))	// looks like a Requirement...
558					fragData = data;			// ... so accept it
559				else
560					Syslog::warning("Invalid sub-requirement at %s", cfString(fragURL).c_str());
561			}
562		if (CODESIGN_EVAL_REQINT_FRAGMENT_LOAD_ENABLED())
563			CODESIGN_EVAL_REQINT_FRAGMENT_LOAD(type, name.c_str(), fragData ? CFDataGetBytePtr(fragData) : NULL);
564		mFragments[key] = fragData;		// cache it, success or failure
565		return fragData;
566	}
567	CODESIGN_EVAL_REQINT_FRAGMENT_HIT(type, name.c_str());
568	return it->second;
569}
570
571
572}	// CodeSigning
573}	// Security
574