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