/ OSX / libsecurity_codesigning / lib / reqinterp.cpp
reqinterp.cpp
  1  /*
  2   * Copyright (c) 2006,2011-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  // reqinterp - Requirement language (exprOp) interpreter
 26  //
 27  
 28  #include "reqinterp.h"
 29  #include "codesigning_dtrace.h"
 30  #include <Security/SecTrustSettingsPriv.h>
 31  #include <Security/SecCertificatePriv.h>
 32  #include <security_utilities/memutils.h>
 33  #include <security_utilities/logging.h>
 34  #include <sys/csr.h>
 35  #include <IOKit/IOKitLib.h>
 36  #include <IOKit/IOCFUnserialize.h>
 37  #include <libDER/oids.h>
 38  #include "csutilities.h"
 39  #include "notarization.h"
 40  #include "legacydevid.h"
 41  
 42  #define WAITING_FOR_LIB_AMFI_INTERFACE 1
 43  
 44  #if WAITING_FOR_LIB_AMFI_INTERFACE
 45  #define __mac_syscall __sandbox_ms
 46  #include <security/mac.h>
 47  
 48  #define AMFI_INTF_CD_HASH_LEN 20
 49  #endif
 50  
 51  namespace Security {
 52  namespace CodeSigning {
 53  
 54  
 55  //
 56  // Fragment fetching, caching, and evaluation.
 57  //
 58  // Several language elements allow "calling" of separate requirement programs
 59  // stored on disk as (binary) requirement blobs. The Fragments class takes care
 60  // of finding, loading, caching, and evaluating them.
 61  //
 62  // This is a singleton for (process global) caching. It works fine as multiple instances,
 63  // at a loss of caching effectiveness.
 64  //
 65  class Fragments {
 66  public:
 67  	Fragments();
 68  	
 69  	bool named(const std::string &name, const Requirement::Context &ctx)
 70  		{ return evalNamed("subreq", name, ctx); }
 71  	bool namedAnchor(const std::string &name, const Requirement::Context &ctx)
 72  		{ return evalNamed("anchorreq", name, ctx); }
 73  
 74  private:
 75  	bool evalNamed(const char *type, const std::string &name, const Requirement::Context &ctx);
 76  	CFDataRef fragment(const char *type, const std::string &name);
 77  	
 78  	typedef std::map<std::string, CFRef<CFDataRef> > FragMap;
 79  	
 80  private:
 81  	CFBundleRef mMyBundle;			// Security.framework bundle
 82  	Mutex mLock;					// lock for all of the below...
 83  	FragMap mFragments;				// cached fragments
 84  };
 85  
 86  static ModuleNexus<Fragments> fragments;
 87  
 88  
 89  //
 90  // Magic certificate features
 91  //
 92  static CFStringRef appleIntermediateCN = CFSTR("Apple Code Signing Certification Authority");
 93  static CFStringRef appleIntermediateO = CFSTR("Apple Inc.");
 94  
 95  
 96  //
 97  // Main interpreter function.
 98  //
 99  // ExprOp code is in Polish Notation (operator followed by operands),
100  // and this engine uses opportunistic evaluation.
101  //
102  bool Requirement::Interpreter::evaluate()
103  { return eval(stackLimit); }
104  
105  bool Requirement::Interpreter::eval(int depth)
106  {
107  	if (--depth <= 0)		// nested too deeply - protect the stack
108  		MacOSError::throwMe(errSecCSReqInvalid);
109  	
110  	ExprOp op = ExprOp(get<uint32_t>());
111  	CODESIGN_EVAL_REQINT_OP(op, this->pc() - sizeof(uint32_t));
112  	switch (op & ~opFlagMask) {
113  	case opFalse:
114  		return false;
115  	case opTrue:
116  		return true;
117  	case opIdent:
118  		return mContext->directory && getString() == mContext->directory->identifier();
119  	case opAppleAnchor:
120  		return appleSigned();
121  	case opAppleGenericAnchor:
122  		return appleAnchored();
123  	case opAnchorHash:
124  		{
125  			SecCertificateRef cert = mContext->cert(get<int32_t>());
126  			return verifyAnchor(cert, getSHA1());
127  		}
128  	case opInfoKeyValue:	// [legacy; use opInfoKeyField]
129  		{
130  			string key = getString();
131  			return infoKeyValue(key, Match(CFTempString(getString()), matchEqual));
132  		}
133  	case opAnd:
134  		return eval(depth) & eval(depth);
135  	case opOr:
136  		return eval(depth) | eval(depth);
137  	case opCDHash:
138  		if (mContext->directory) {
139  			CFRef<CFDataRef> cdhash = mContext->directory->cdhash();
140  			CFRef<CFDataRef> required = getHash();
141  			return CFEqual(cdhash, required);
142  		} else
143  			return false;
144  	case opNot:
145  		return !eval(depth);
146  	case opInfoKeyField:
147  		{
148  			string key = getString();
149  			Match match(*this);
150  			return infoKeyValue(key, match);
151  		}
152  	case opEntitlementField:
153  		{
154  			string key = getString();
155  			Match match(*this);
156  			return entitlementValue(key, match);
157  		}
158  	case opCertField:
159  		{
160  			SecCertificateRef cert = mContext->cert(get<int32_t>());
161  			string key = getString();
162  			Match match(*this);
163  			return certFieldValue(key, match, cert);
164  		}
165  #if TARGET_OS_OSX
166  	case opCertGeneric:
167  		{
168  			SecCertificateRef cert = mContext->cert(get<int32_t>());
169  			string key = getString();
170  			Match match(*this);
171  			return certFieldGeneric(key, match, cert);
172  		}
173  	case opCertFieldDate:
174  		{
175  			SecCertificateRef cert = mContext->cert(get<int32_t>());
176  			string key = getString();
177  			Match match(*this);
178  			return certFieldDate(key, match, cert);
179  		}
180  	case opCertPolicy:
181  		{
182  			SecCertificateRef cert = mContext->cert(get<int32_t>());
183  			string key = getString();
184  			Match match(*this);
185  			return certFieldPolicy(key, match, cert);
186  		}
187  #endif
188  	case opTrustedCert:
189  		return trustedCert(get<int32_t>());
190  	case opTrustedCerts:
191  		return trustedCerts();
192  	case opNamedAnchor:
193  		return fragments().namedAnchor(getString(), *mContext);
194  	case opNamedCode:
195  		return fragments().named(getString(), *mContext);
196  	case opPlatform:
197  		{
198  			int32_t targetPlatform = get<int32_t>();
199  			return mContext->directory && mContext->directory->platform == targetPlatform;
200  		}
201  	case opNotarized:
202  		{
203  			return isNotarized(mContext);
204  		}
205  	case opLegacyDevID:
206  		{
207  			return meetsDeveloperIDLegacyAllowedPolicy(mContext);
208  		}
209  	default:
210  		// opcode not recognized - handle generically if possible, fail otherwise
211  		if (op & (opGenericFalse | opGenericSkip)) {
212  			// unknown opcode, but it has a size field and can be safely bypassed
213  			skip(get<uint32_t>());
214  			if (op & opGenericFalse) {
215  				CODESIGN_EVAL_REQINT_UNKNOWN_FALSE(op);
216  				return false;
217  			} else {
218  				CODESIGN_EVAL_REQINT_UNKNOWN_SKIPPED(op);
219  				return eval(depth);
220  			}
221  		}
222  		// unrecognized opcode and no way to interpret it
223  		secinfo("csinterp", "opcode 0x%x cannot be handled; aborting", op);
224  		MacOSError::throwMe(errSecCSUnimplemented);
225  	}
226  }
227  
228  
229  //
230  // Evaluate an Info.plist key condition
231  //
232  bool Requirement::Interpreter::infoKeyValue(const string &key, const Match &match)
233  {
234  	if (mContext->info)		// we have an Info.plist
235  		if (CFTypeRef value = CFDictionaryGetValue(mContext->info, CFTempString(key)))
236  			return match(value);
237  	return match(kCFNull);
238  }
239  
240  
241  //
242  // Evaluate an entitlement condition
243  //
244  bool Requirement::Interpreter::entitlementValue(const string &key, const Match &match)
245  {
246  	if (mContext->entitlements)		// we have an Info.plist
247  		if (CFTypeRef value = CFDictionaryGetValue(mContext->entitlements, CFTempString(key)))
248  			return match(value);
249  	return match(kCFNull);
250  }
251  
252  
253  bool Requirement::Interpreter::certFieldValue(const string &key, const Match &match, SecCertificateRef cert)
254  {
255  #if TARGET_OS_OSX
256  	// no cert, no chance
257  	if (cert == NULL)
258  		return false;
259  
260  	// a table of recognized keys for the "certificate[foo]" syntax
261  	static const struct CertField {
262  		const char *name;
263  		const DERItem *oid;
264  	} certFields[] = {
265  		{ "subject.C", &oidCountryName},
266  		{ "subject.CN", &oidCommonName },
267  		{ "subject.D", &oidDescription },
268  		{ "subject.L", &oidLocalityName },
269  //		{ "subject.C-L", &CSSMOID_CollectiveLocalityName },	// missing from Security.framework headers
270  		{ "subject.O", &oidOrganizationName },
271  		{ "subject.C-O", &oidCollectiveOrganizationName },
272  		{ "subject.OU", &oidOrganizationalUnitName},
273  		{ "subject.C-OU", &oidCollectiveOrganizationalUnitName},
274  		{ "subject.ST", &oidStateOrProvinceName},
275  		{ "subject.C-ST", &oidCollectiveStateOrProvinceName },
276  		{ "subject.STREET", &oidStreetAddress },
277  		{ "subject.C-STREET", &oidCollectiveStreetAddress },
278  		{ "subject.UID", &oidUserId },
279  		{ NULL, NULL }
280  	};
281  
282  	// DN-component single-value match
283  	for (const CertField *cf = certFields; cf->name; cf++)
284  		if (cf->name == key) {
285  			CFRef<CFStringRef> value(SecCertificateCopySubjectAttributeValue(cert, (DERItem *)cf->oid));
286  			if (!value.get()) {
287  				secinfo("csinterp", "cert %p lookup for DN.%s failed", cert, key.c_str());
288  				return false;
289  			}
290  			return match(value);
291  		}
292  
293  	// email multi-valued match (any of...)
294  	if (key == "email") {
295  		CFRef<CFArrayRef> value;
296          OSStatus rc = SecCertificateCopyEmailAddresses(cert, &value.aref());
297  		if (rc) {
298  			secinfo("csinterp", "cert %p lookup for email failed rc=%d", cert, (int)rc);
299  			return false;
300  		}
301  		return match(value);
302  	}
303  
304  	// unrecognized key. Fail but do not abort to promote backward compatibility down the road
305  	secinfo("csinterp", "cert field notation \"%s\" not understood", key.c_str());
306  #endif
307  	return false;
308  }
309  
310  #if TARGET_OS_OSX
311  bool Requirement::Interpreter::certFieldGeneric(const string &key, const Match &match, SecCertificateRef cert)
312  {
313  	// the key is actually a (binary) OID value
314  	CssmOid oid((char *)key.data(), key.length());
315  	return certFieldGeneric(oid, match, cert);
316  }
317  
318  bool Requirement::Interpreter::certFieldGeneric(const CssmOid &oid, const Match &match, SecCertificateRef cert)
319  {
320  	return cert && match(certificateHasField(cert, oid) ? (CFTypeRef)kCFBooleanTrue : (CFTypeRef)kCFNull);
321  }
322  
323  bool Requirement::Interpreter::certFieldDate(const string &key, const Match &match, SecCertificateRef cert)
324  {
325  	// the key is actually a (binary) OID value
326  	CssmOid oid((char *)key.data(), key.length());
327  	return certFieldDate(oid, match, cert);
328  }
329  	
330  bool Requirement::Interpreter::certFieldDate(const CssmOid &oid, const Match &match, SecCertificateRef cert)
331  {
332  	CFTypeRef value = cert != NULL ? certificateCopyFieldDate(cert, oid) : NULL;
333  	bool matching = match(value != NULL ? value : kCFNull);
334  	
335  	if (value) {
336  		CFRelease(value);
337  	}
338  	
339  	return matching;
340  }
341  
342  bool Requirement::Interpreter::certFieldPolicy(const string &key, const Match &match, SecCertificateRef cert)
343  {
344  	// the key is actually a (binary) OID value
345  	CssmOid oid((char *)key.data(), key.length());
346  	return certFieldPolicy(oid, match, cert);
347  }
348  
349  bool Requirement::Interpreter::certFieldPolicy(const CssmOid &oid, const Match &match, SecCertificateRef cert)
350  {
351  	return cert && match(certificateHasPolicy(cert, oid) ? (CFTypeRef)kCFBooleanTrue : (CFTypeRef)kCFNull);
352  }
353  #endif
354  
355  //
356  // Check the Apple-signed condition
357  //
358  bool Requirement::Interpreter::appleAnchored()
359  {
360  	if (SecCertificateRef cert = mContext->cert(anchorCert))
361  		if (isAppleCA(cert))
362  		return true;
363  	return false;
364  }
365  
366  static CFStringRef kAMFINVRAMTrustedKeys = CFSTR("AMFITrustedKeys");
367  
368  CFArrayRef Requirement::Interpreter::getAdditionalTrustedAnchors()
369  {
370      __block CFRef<CFMutableArrayRef> keys = makeCFMutableArray(0);
371  
372      try {
373          io_registry_entry_t entry = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/options");
374          if (entry == IO_OBJECT_NULL)
375              return NULL;
376  
377          CFRef<CFDataRef> configData = (CFDataRef)IORegistryEntryCreateCFProperty(entry, kAMFINVRAMTrustedKeys, kCFAllocatorDefault, 0);
378          IOObjectRelease(entry);
379          if (!configData)
380              return NULL;
381  
382          CFRef<CFDictionaryRef> configDict = CFDictionaryRef(IOCFUnserializeWithSize((const char *)CFDataGetBytePtr(configData),
383                                                                                      (size_t)CFDataGetLength(configData),
384                                                                                      kCFAllocatorDefault, 0, NULL));
385          if (!configDict)
386              return NULL;
387  
388          CFArrayRef trustedKeys = CFArrayRef(CFDictionaryGetValue(configDict, CFSTR("trustedKeys")));
389          if (!trustedKeys && CFGetTypeID(trustedKeys) != CFArrayGetTypeID())
390              return NULL;
391  
392          cfArrayApplyBlock(trustedKeys, ^(const void *value) {
393              CFDictionaryRef key = CFDictionaryRef(value);
394              if (!key && CFGetTypeID(key) != CFDictionaryGetTypeID())
395                  return;
396  
397              CFDataRef hash = CFDataRef(CFDictionaryGetValue(key, CFSTR("certDigest")));
398              if (!hash && CFGetTypeID(hash) != CFDataGetTypeID())
399                  return;
400              CFArrayAppendValue(keys, hash);
401          });
402  
403      } catch (...) {
404      }
405  
406      if (CFArrayGetCount(keys) == 0)
407          return NULL;
408  
409      return keys.yield();
410  }
411  
412  bool Requirement::Interpreter::appleLocalAnchored()
413  {
414  	static CFArrayRef additionalTrustedCertificates = NULL;
415  
416      if (csr_check(CSR_ALLOW_APPLE_INTERNAL)) {
417          return false;
418      }
419  
420      if (mContext->forcePlatform) {
421          return true;
422      }
423  
424      static dispatch_once_t onceToken;
425      dispatch_once(&onceToken, ^{
426          additionalTrustedCertificates = getAdditionalTrustedAnchors();
427      });
428  
429      if (additionalTrustedCertificates == NULL)
430          return false;
431  
432      CFRef<CFDataRef> hash = SecCertificateCopySHA256Digest(mContext->cert(leafCert));
433      if (!hash)
434          return false;
435  
436      if (CFArrayContainsValue(additionalTrustedCertificates, CFRangeMake(0, CFArrayGetCount(additionalTrustedCertificates)), hash))
437          return true;
438  
439      return false;
440  }
441  
442  #if WAITING_FOR_LIB_AMFI_INTERFACE
443  // These bits are here until we get get a new build alias for libamfi-interface.
444  
445  #define MAC_AMFI_POLICY_NAME "AMFI"
446  
447  #define AMFI_SYSCALL_CDHASH_IN_TRUSTCACHE  95
448  
449  typedef struct amfi_cdhash_in_trustcache_ {
450      uint8_t cdhash[20];
451      uint64_t result;
452  } amfi_cdhash_in_trustcache_t;
453  
454  static int
455  __amfi_interface_cdhash_in_trustcache(const uint8_t cdhash[], uint64_t* trustcache_result)
456  {
457      amfi_cdhash_in_trustcache_t args;
458      static_assert(AMFI_INTF_CD_HASH_LEN == sizeof(args.cdhash), "Error: cdhash length mismatch");
459      int err;
460      memcpy(args.cdhash, cdhash, sizeof(args.cdhash));
461      args.result = 0;
462      err = __mac_syscall(MAC_AMFI_POLICY_NAME, AMFI_SYSCALL_CDHASH_IN_TRUSTCACHE, &args);
463      if (err) {
464          err = errno;
465      }
466      *trustcache_result = args.result;
467      return err;
468  }
469  
470  static int
471  amfi_interface_cdhash_in_trustcache(const uint8_t cdhash[], size_t cdhash_len, uint64_t* trustcache_result)
472  {
473      int err = EINVAL;
474  
475      if (cdhash == nullptr || cdhash_len != AMFI_INTF_CD_HASH_LEN || trustcache_result == nullptr) {
476          goto lb_end;
477      }
478      *trustcache_result = 0;
479  
480      err = __amfi_interface_cdhash_in_trustcache(cdhash, trustcache_result);
481  
482  lb_end:
483      return err;
484  }
485  #endif
486  
487  bool Requirement::Interpreter::inTrustCache()
488  {
489      uint64_t result = 0;
490      CFRef<CFDataRef> cdhashRef = mContext->directory->cdhash(true);
491      const uint8_t *cdhash = CFDataGetBytePtr(cdhashRef);
492      size_t cdhash_len = CFDataGetLength(cdhashRef);
493      int err = amfi_interface_cdhash_in_trustcache(cdhash, cdhash_len, &result);
494      return (err == 0) && (result != 0);
495  }
496  
497  bool Requirement::Interpreter::appleSigned()
498  {
499      if (inTrustCache()) {
500          return true;
501      }
502      else if (appleAnchored()) {
503          if (SecCertificateRef intermed = mContext->cert(-2))	// first intermediate
504              // first intermediate common name match (exact)
505              if (certFieldValue("subject.CN", Match(appleIntermediateCN, matchEqual), intermed)
506                  && certFieldValue("subject.O", Match(appleIntermediateO, matchEqual), intermed))
507                  return true;
508      } else if (appleLocalAnchored()) {
509          return true;
510      }
511      return false;
512  }
513  
514  
515  //
516  // Verify an anchor requirement against the context
517  //
518  bool Requirement::Interpreter::verifyAnchor(SecCertificateRef cert, const unsigned char *digest)
519  {
520  	// get certificate bytes
521  	if (cert) {
522          SHA1 hasher;
523          hasher(SecCertificateGetBytePtr(cert), SecCertificateGetLength(cert));
524  		return hasher.verify(digest);
525  	}
526  	return false;
527  }
528  
529  
530  //
531  // Check one or all certificate(s) in the cert chain against the Trust Settings database.
532  //
533  bool Requirement::Interpreter::trustedCerts()
534  {
535  	int anchor = mContext->certCount() - 1;
536  	for (int slot = 0; slot <= anchor; slot++)
537  		if (SecCertificateRef cert = mContext->cert(slot))
538  			switch (trustSetting(cert, slot == anchor)) {
539  			case kSecTrustSettingsResultTrustRoot:
540  			case kSecTrustSettingsResultTrustAsRoot:
541  				return true;
542  			case kSecTrustSettingsResultDeny:
543  				return false;
544  			case kSecTrustSettingsResultUnspecified:
545  				break;
546  			default:
547  				assert(false);
548  				return false;
549  			}
550  		else
551  			return false;
552  	return false;
553  }
554  
555  bool Requirement::Interpreter::trustedCert(int slot)
556  {
557  	if (SecCertificateRef cert = mContext->cert(slot)) {
558  		int anchorSlot = mContext->certCount() - 1;
559  		switch (trustSetting(cert, slot == anchorCert || slot == anchorSlot)) {
560  		case kSecTrustSettingsResultTrustRoot:
561  		case kSecTrustSettingsResultTrustAsRoot:
562  			return true;
563  		case kSecTrustSettingsResultDeny:
564  		case kSecTrustSettingsResultUnspecified:
565  			return false;
566  		default:
567  			assert(false);
568  			return false;
569  		}
570  	} else
571  		return false;
572  }
573  
574  
575  //
576  // Explicitly check one certificate against the Trust Settings database and report
577  // the findings. This is a helper for the various Trust Settings evaluators.
578  //
579  SecTrustSettingsResult Requirement::Interpreter::trustSetting(SecCertificateRef cert, bool isAnchor)
580  {
581      // XXX: Not supported on embedded yet due to lack of supporting API
582  #if TARGET_OS_OSX
583  	// the SPI input is the uppercase hex form of the SHA-1 of the certificate...
584  	assert(cert);
585  	SHA1::Digest digest;
586  	hashOfCertificate(cert, digest);
587  	string Certhex = CssmData(digest, sizeof(digest)).toHex();
588  	for (string::iterator it = Certhex.begin(); it != Certhex.end(); ++it)
589  		if (islower(*it))
590  			*it = toupper(*it);
591  	
592  	// call Trust Settings and see what it finds
593  	SecTrustSettingsDomain domain;
594  	SecTrustSettingsResult result;
595  	CSSM_RETURN *errors = NULL;
596  	uint32 errorCount = 0;
597  	bool foundMatch, foundAny;
598  	switch (OSStatus rc = SecTrustSettingsEvaluateCert(
599  		CFTempString(Certhex),					// settings index
600  		&CSSMOID_APPLE_TP_CODE_SIGNING,			// standard code signing policy
601  		NULL, 0,								// policy string (unused)
602  		kSecTrustSettingsKeyUseAny,				// no restriction on key usage @@@
603  		isAnchor,								// consult system default anchor set
604  
605  		&domain,								// domain of found setting
606  		&errors, &errorCount,					// error set and maximum count
607  		&result,								// the actual setting
608  		&foundMatch, &foundAny					// optimization hints (not used)
609  		)) {
610  	case errSecSuccess:
611  		::free(errors);
612  		if (foundMatch)
613  			return result;
614  		else
615  			return kSecTrustSettingsResultUnspecified;
616  	default:
617  		::free(errors);
618  		MacOSError::throwMe(rc);
619  	}
620  #else
621      return kSecTrustSettingsResultUnspecified;
622  #endif
623  }
624  
625  
626  //
627  // Create a Match object from the interpreter stream
628  //
629  Requirement::Interpreter::Match::Match(Interpreter &interp)
630  {
631  	switch (mOp = interp.get<MatchOperation>()) {
632  	case matchAbsent:
633  	case matchExists:
634  		break;
635  	case matchEqual:
636  	case matchContains:
637  	case matchBeginsWith:
638  	case matchEndsWith:
639  	case matchLessThan:
640  	case matchGreaterThan:
641  	case matchLessEqual:
642  	case matchGreaterEqual:
643  		mValue.take(makeCFString(interp.getString()));
644  		break;
645  	case matchOn:
646  	case matchBefore:
647  	case matchAfter:
648  	case matchOnOrBefore:
649  	case matchOnOrAfter: {
650  		mValue.take(CFDateCreate(NULL, interp.getAbsoluteTime()));
651  		break;
652  	}
653  	default:
654  		// Assume this (unknown) match type has a single data argument.
655  		// This gives us a chance to keep the instruction stream aligned.
656  		interp.getString();			// discard
657  		break;
658  	}
659  }
660  
661  
662  //
663  // Execute a match against a candidate value
664  //
665  bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate) const
666  {
667  	// null candidates always fail
668  	if (!candidate)
669  		return false;
670  
671  	if (candidate == kCFNull) {
672  		return mOp == matchAbsent; // only 'absent' matches
673  	}
674  	
675  	// interpret an array as matching alternatives (any one succeeds)
676  	if (CFGetTypeID(candidate) == CFArrayGetTypeID()) {
677  		CFArrayRef array = CFArrayRef(candidate);
678  		CFIndex count = CFArrayGetCount(array);
679  		for (CFIndex n = 0; n < count; n++)
680  			if ((*this)(CFArrayGetValueAtIndex(array, n)))	// yes, it's recursive
681  				return true;
682  	}
683  
684  	switch (mOp) {
685  	case matchAbsent:
686  		return false;		// it exists, so it cannot be absent
687  	case matchExists:		// anything but NULL and boolean false "exists"
688  		return !CFEqual(candidate, kCFBooleanFalse);
689  	case matchEqual:		// equality works for all CF types
690  		return CFEqual(candidate, mValue);
691  	case matchContains:
692  		if (isStringValue() && CFGetTypeID(candidate) == CFStringGetTypeID()) {
693  			CFStringRef value = CFStringRef(candidate);
694  			if (CFStringFindWithOptions(value, cfStringValue(), CFRangeMake(0, CFStringGetLength(value)), 0, NULL))
695  				return true;
696  		}
697  		return false;
698  	case matchBeginsWith:
699  		if (isStringValue() && CFGetTypeID(candidate) == CFStringGetTypeID()) {
700  			CFStringRef value = CFStringRef(candidate);
701  			if (CFStringFindWithOptions(value, cfStringValue(), CFRangeMake(0, CFStringGetLength(cfStringValue())), 0, NULL))
702  				return true;
703  		}
704  		return false;
705  	case matchEndsWith:
706  		if (isStringValue() && CFGetTypeID(candidate) == CFStringGetTypeID()) {
707  			CFStringRef value = CFStringRef(candidate);
708  			CFIndex matchLength = CFStringGetLength(cfStringValue());
709  			CFIndex start = CFStringGetLength(value) - matchLength;
710  			if (start >= 0)
711  				if (CFStringFindWithOptions(value, cfStringValue(), CFRangeMake(start, matchLength), 0, NULL))
712  					return true;
713  		}
714  		return false;
715  	case matchLessThan:
716  		return inequality(candidate, kCFCompareNumerically, kCFCompareLessThan, true);
717  	case matchGreaterThan:
718  		return inequality(candidate, kCFCompareNumerically, kCFCompareGreaterThan, true);
719  	case matchLessEqual:
720  		return inequality(candidate, kCFCompareNumerically, kCFCompareGreaterThan, false);
721  	case matchGreaterEqual:
722  		return inequality(candidate, kCFCompareNumerically, kCFCompareLessThan, false);
723  	case matchOn:
724  	case matchBefore:
725  	case matchAfter:
726  	case matchOnOrBefore:
727  	case matchOnOrAfter: {
728  		if (!isDateValue() || CFGetTypeID(candidate) != CFDateGetTypeID()) {
729  			return false;
730  		}
731  		
732  		CFComparisonResult res = CFDateCompare((CFDateRef)candidate, cfDateValue(), NULL);
733  
734  		switch (mOp) {
735  			case matchOn: return res == 0;
736  			case matchBefore: return res < 0;
737  			case matchAfter: return res > 0;
738  			case matchOnOrBefore: return res <= 0;
739  			case matchOnOrAfter: return res >= 0;
740  			default: abort();
741  		}
742  	}
743  	default:
744  		// unrecognized match types can never match
745  		return false;
746  	}
747  }
748  
749  
750  bool Requirement::Interpreter::Match::inequality(CFTypeRef candidate, CFStringCompareFlags flags,
751  	CFComparisonResult outcome, bool negate) const
752  {
753  	if (isStringValue() && CFGetTypeID(candidate) == CFStringGetTypeID()) {
754  		CFStringRef value = CFStringRef(candidate);
755  		if ((CFStringCompare(value, cfStringValue(), flags) == outcome) == negate)
756  			return true;
757  	}
758  	return false;
759  }
760  
761  
762  //
763  // External fragments
764  //
765  Fragments::Fragments()
766  {
767  	mMyBundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security"));
768  }
769  
770  
771  bool Fragments::evalNamed(const char *type, const std::string &name, const Requirement::Context &ctx)
772  {
773  	if (CFDataRef fragData = fragment(type, name)) {
774  		const Requirement *req = (const Requirement *)CFDataGetBytePtr(fragData);	// was prevalidated as Requirement
775  		return req->validates(ctx);
776  	}
777  	return false;
778  }
779  
780  
781  CFDataRef Fragments::fragment(const char *type, const std::string &name)
782  {
783  	string key = name + "!!" + type;	// compound key
784  	StLock<Mutex> _(mLock);				// lock for cache access
785  	FragMap::const_iterator it = mFragments.find(key);
786  	if (it == mFragments.end()) {
787  		CFRef<CFDataRef> fragData;		// will always be set (NULL on any errors)
788  		if (CFRef<CFURLRef> fragURL = CFBundleCopyResourceURL(mMyBundle, CFTempString(name), CFSTR("csreq"), CFTempString(type)))
789  			if (CFRef<CFDataRef> data = cfLoadFile(fragURL)) {	// got data
790  				const Requirement *req = (const Requirement *)CFDataGetBytePtr(data);
791  				if (req->validateBlob(CFDataGetLength(data)))	// looks like a Requirement...
792  					fragData = data;			// ... so accept it
793  				else
794  					Syslog::warning("Invalid sub-requirement at %s", cfString(fragURL).c_str());
795  			}
796  		if (CODESIGN_EVAL_REQINT_FRAGMENT_LOAD_ENABLED())
797  			CODESIGN_EVAL_REQINT_FRAGMENT_LOAD(type, name.c_str(), fragData ? CFDataGetBytePtr(fragData) : NULL);
798  		mFragments[key] = fragData;		// cache it, success or failure
799  		return fragData;
800  	}
801  	CODESIGN_EVAL_REQINT_FRAGMENT_HIT(type, name.c_str());
802  	return it->second;
803  }
804  
805  
806  }	// CodeSigning
807  }	// Security