1/* 2 * Copyright (c) 2002-2010,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// Trust.cpp 26// 27#include <security_keychain/Trust.h> 28#include <security_keychain/TrustSettingsSchema.h> 29#include <security_cdsa_utilities/cssmdates.h> 30#include <security_utilities/cfutilities.h> 31#include <CoreFoundation/CoreFoundation.h> 32#include <Security/SecCertificate.h> 33#include <Security/SecTrust.h> 34#include "SecBridge.h" 35#include "TrustAdditions.h" 36#include "TrustKeychains.h" 37#include <security_cdsa_client/dlclient.h> 38 39 40using namespace Security; 41using namespace KeychainCore; 42 43// 44// Translate CFDataRef to CssmData. The output shares the input's buffer. 45// 46static inline CssmData cfData(CFDataRef data) 47{ 48 return CssmData(const_cast<UInt8 *>(CFDataGetBytePtr(data)), 49 CFDataGetLength(data)); 50} 51 52// 53// Convert a SecPointer to a CF object. 54// 55static SecCertificateRef 56convert(const SecPointer<Certificate> &certificate) 57{ 58 return *certificate; 59} 60 61// 62// For now, we use a global TrustStore 63// 64ModuleNexus<TrustStore> Trust::gStore; 65 66#pragma mark -- TrustKeychains -- 67 68static const CSSM_DL_DB_HANDLE nullCSSMDLDBHandle = {0,}; 69// 70// TrustKeychains maintains a global reference to standard system keychains, 71// to avoid having them be opened anew for each Trust instance. 72// 73class TrustKeychains 74{ 75public: 76 TrustKeychains(); 77 ~TrustKeychains() {} 78 CSSM_DL_DB_HANDLE rootStoreHandle() { return mRootStoreHandle; } 79 CSSM_DL_DB_HANDLE systemKcHandle() { return mSystem ? mSystem->database()->handle() : nullCSSMDLDBHandle; } 80 Keychain &systemKc() { return mSystem; } 81 Keychain &rootStore() { return *mRootStore; } 82 83private: 84 DL* mRootStoreDL; 85 Db* mRootStoreDb; 86 Keychain* mRootStore; 87 CSSM_DL_DB_HANDLE mRootStoreHandle; 88 Keychain mSystem; 89}; 90 91// 92// Singleton maintaining open references to standard system keychains, 93// to avoid having them be opened anew every time SecTrust is used. 94// 95 96static ModuleNexus<TrustKeychains> trustKeychains; 97static ModuleNexus<RecursiveMutex> trustKeychainsMutex; 98 99extern "C" bool GetServerMode(); 100 101TrustKeychains::TrustKeychains() : 102 mRootStoreHandle(nullCSSMDLDBHandle), 103 mSystem(globals().storageManager.make(ADMIN_CERT_STORE_PATH, false)) 104{ 105 if (GetServerMode()) // in server mode? Don't make a keychain for the root store 106 { 107 mRootStoreDL = new DL(gGuidAppleFileDL), 108 mRootStoreDb = new Db(*mRootStoreDL, SYSTEM_ROOT_STORE_PATH), 109 (*mRootStoreDb)->activate(); 110 mRootStoreHandle = (*mRootStoreDb)->handle(); 111 } 112 else 113 { 114 mRootStore = new Keychain(globals().storageManager.make(SYSTEM_ROOT_STORE_PATH, false)); 115 (*mRootStore)->database()->activate(); 116 mRootStoreHandle = (*mRootStore)->database()->handle(); 117 } 118} 119 120RecursiveMutex& SecTrustKeychainsGetMutex() 121{ 122 return trustKeychainsMutex(); 123} 124 125#pragma mark -- Trust -- 126// 127// Construct a Trust object with suitable defaults. 128// Use setters for additional arguments before calling evaluate(). 129// 130Trust::Trust(CFTypeRef certificates, CFTypeRef policies) 131 : mTP(gGuidAppleX509TP), mAction(CSSM_TP_ACTION_DEFAULT), 132 mCerts(cfArrayize(certificates)), mPolicies(cfArrayize(policies)), 133 mSearchLibs(NULL), mSearchLibsSet(false), mResult(kSecTrustResultInvalid), 134 mUsingTrustSettings(false), mAnchorPolicy(useAnchorsDefault), mMutex(Mutex::recursive) 135{ 136 if (!mPolicies) { 137 mPolicies.take(CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks)); 138 } 139} 140 141 142// 143// Clean up a Trust object 144// 145Trust::~Trust() 146{ 147 clearResults(); 148 if (mSearchLibs) { 149 delete mSearchLibs; 150 } 151 152 mPolicies = NULL; 153} 154 155 156// 157// Get searchLibs (a vector of Keychain objects); 158// normally initialized to default search list 159// 160StorageManager::KeychainList& Trust::searchLibs(bool init) 161{ 162 if (!mSearchLibs) { 163 mSearchLibs = new StorageManager::KeychainList; 164 if (init) { 165 globals().storageManager.getSearchList(*mSearchLibs); 166 } 167 } 168 return *mSearchLibs; 169} 170 171 172// 173// Set searchLibs to provided vector of Keychain objects 174// 175void Trust::searchLibs(StorageManager::KeychainList &libs) 176{ 177 searchLibs(false) = libs; 178 mSearchLibsSet = true; 179} 180 181 182// 183// Retrieve the last TP evaluation result, if any 184// 185CSSM_TP_VERIFY_CONTEXT_RESULT_PTR Trust::cssmResult() 186{ 187 if (mResult == kSecTrustResultInvalid) 188 MacOSError::throwMe(errSecTrustNotAvailable); 189 return &mTpResult; 190} 191 192 193// SecCertificateRef -> CssmData 194static 195CssmData cfCertificateData(SecCertificateRef certificate) 196{ 197 return Certificate::required(certificate)->data(); 198} 199 200// SecPolicyRef -> CssmField (CFDataRef/NULL or oid/value of a SecPolicy) 201static 202CssmField cfField(SecPolicyRef item) 203{ 204 SecPointer<Policy> policy = Policy::required(SecPolicyRef(item)); 205 return CssmField(policy->oid(), policy->value()); 206} 207 208// SecKeychain -> CssmDlDbHandle 209#if 0 210static 211CSSM_DL_DB_HANDLE cfKeychain(SecKeychainRef ref) 212{ 213 Keychain keychain = KeychainImpl::required(ref); 214 return keychain->database()->handle(); 215} 216#endif 217 218#if !defined(NDEBUG) 219void showCertSKID(const void *value, void *context); 220#endif 221 222// 223// Here's the big "E" - evaluation. 224// We build most of the CSSM-layer input structures dynamically right here; 225// they will auto-destruct when we're done. The output structures are kept 226// around (in our data members) for later analysis. 227// Note that evaluate() can be called repeatedly, so we must be careful to 228// dispose of prior results. 229// 230void Trust::evaluate(bool disableEV) 231{ 232 bool isEVCandidate=false; 233 // begin evaluation block with stack-based mutex 234 { 235 StLock<Mutex>_(mMutex); 236 // if we have evaluated before, release prior result 237 clearResults(); 238 239 // determine whether the leaf certificate is an EV candidate 240 CFArrayRef allowedAnchors = allowedEVRootsForLeafCertificate(mCerts); 241 CFArrayRef filteredCerts = NULL; 242 isEVCandidate = (allowedAnchors && !disableEV) ? true : false; 243 if (isEVCandidate) { 244 secdebug("evTrust", "Trust::evaluate() certificate is EV candidate"); 245 filteredCerts = potentialEVChainWithCertificates(mCerts); 246 mCerts = filteredCerts; 247 } else { 248 secdebug("evTrust", "Trust::evaluate() performing standard evaluation"); 249 if (mCerts) { 250 filteredCerts = CFArrayCreateMutableCopy(NULL, 0, mCerts); 251 } 252 if (mAnchors) { 253 allowedAnchors = CFArrayCreateMutableCopy(NULL, 0, mAnchors); 254 } 255 } 256 // retain these certs as long as we potentially could have results involving them 257 // (note that assignment to a CFRef type performs an implicit retain) 258 mAllowedAnchors = allowedAnchors; 259 mFilteredCerts = filteredCerts; 260 261 if (allowedAnchors) 262 CFRelease(allowedAnchors); 263 if (filteredCerts) 264 CFRelease(filteredCerts); 265 266 if (mAllowedAnchors) 267 { 268 secdebug("trusteval", "Trust::evaluate: anchors: %ld", CFArrayGetCount(mAllowedAnchors)); 269#if !defined(NDEBUG) 270 CFArrayApplyFunction(mAllowedAnchors, CFRangeMake(0, CFArrayGetCount(mAllowedAnchors)), showCertSKID, NULL); 271#endif 272 } 273 274 // set default search list from user's default, if caller did not explicitly supply it 275 if(!mSearchLibsSet) { 276 globals().storageManager.getSearchList(searchLibs()); 277 mSearchLibsSet = true; 278 } 279 280 // build the target cert group 281 CFToVector<CssmData, SecCertificateRef, cfCertificateData> subjects(mFilteredCerts); 282 CertGroup subjectCertGroup(CSSM_CERT_X_509v3, 283 CSSM_CERT_ENCODING_BER, CSSM_CERTGROUP_DATA); 284 subjectCertGroup.count() = subjects; 285 subjectCertGroup.blobCerts() = subjects; 286 287 // build a TP_VERIFY_CONTEXT, a veritable nightmare of a data structure 288 TPBuildVerifyContext context(mAction); 289 290 /* 291 * Guarantee *some* action data... 292 * NOTE this only works with the local X509 TP. When this module can deal 293 * with other TPs, this must be revisited. 294 */ 295 CSSM_APPLE_TP_ACTION_DATA localActionData; 296 memset(&localActionData, 0, sizeof(localActionData)); 297 CssmData localActionCData((uint8 *)&localActionData, sizeof(localActionData)); 298 CSSM_APPLE_TP_ACTION_DATA *actionDataP = &localActionData; 299 if (mActionData) { 300 context.actionData() = cfData(mActionData); 301 actionDataP = (CSSM_APPLE_TP_ACTION_DATA *)context.actionData().data(); 302 } 303 else { 304 context.actionData() = localActionCData; 305 } 306 307 if (!mAnchors) { 308 // always check trust settings if caller did not provide explicit trust anchors 309 actionDataP->ActionFlags |= CSSM_TP_ACTION_TRUST_SETTINGS; 310 } 311 312 if (mNetworkPolicy == useNetworkDefault) { 313 if (policySpecified(mPolicies, CSSMOID_APPLE_TP_SSL)) { 314 // enable network cert fetch for SSL only: <rdar://7422356> 315 actionDataP->ActionFlags |= CSSM_TP_ACTION_FETCH_CERT_FROM_NET; 316 } 317 } 318 else if (mNetworkPolicy == useNetworkEnabled) 319 actionDataP->ActionFlags |= CSSM_TP_ACTION_FETCH_CERT_FROM_NET; 320 else if (mNetworkPolicy == useNetworkDisabled) 321 actionDataP->ActionFlags &= ~(CSSM_TP_ACTION_FETCH_CERT_FROM_NET); 322 323 /* 324 * Policies (one at least, please). 325 * For revocation policies, see if any have been explicitly specified... 326 */ 327 CFMutableArrayRef allPolicies = NULL; 328 uint32 numRevocationAdded = 0; 329 bool requirePerCert = (actionDataP->ActionFlags & CSSM_TP_ACTION_REQUIRE_REV_PER_CERT); 330 bool avoidRevChecks = (policySpecified(mPolicies, CSSMOID_APPLE_TP_EAP)); 331 332 // If a new unified revocation policy was explicitly specified, 333 // convert into old-style individual OCSP and CRL policies. 334 // Note that the caller could configure revocation policy options 335 // to explicitly disable both methods, so 0 policies might be added, 336 // in which case we must no longer consider the cert an EV candidate. 337 338 allPolicies = convertRevocationPolicy(numRevocationAdded, context.allocator); 339 if (allPolicies) { 340 // caller has explicitly set the revocation policy they want to use 341 secdebug("evTrust", "Trust::evaluate() using explicit revocation policy (%d)", 342 numRevocationAdded); 343 if (numRevocationAdded == 0) 344 isEVCandidate = false; 345 } 346 else if (mAnchors && (CFArrayGetCount(mAnchors)==0) && (searchLibs().size()==0)) { 347 // caller explicitly provided empty anchors and no keychain list, 348 // and did not explicitly specify the revocation policy; 349 // override global revocation check setting for this evaluation 350 secdebug("evTrust", "Trust::evaluate() has empty anchors and no keychains"); 351 allPolicies = NULL; // use only mPolicies 352 isEVCandidate = false; 353 } 354 else if ((isEVCandidate && !avoidRevChecks) || requirePerCert) { 355 // force revocation checking for this evaluation 356 secdebug("evTrust", "Trust::evaluate() forcing OCSP/CRL revocation check"); 357 allPolicies = forceRevocationPolicies(numRevocationAdded, 358 context.allocator, requirePerCert); 359 } 360 else if(!(revocationPolicySpecified(mPolicies)) && !avoidRevChecks) { 361 // none specified in mPolicies; try preferences 362 allPolicies = addPreferenceRevocationPolicies(numRevocationAdded, 363 context.allocator); 364 } 365 if (allPolicies == NULL) { 366 // use mPolicies; no revocation checking will be performed 367 secdebug("evTrust", "Trust::evaluate() will not perform revocation check"); 368 CFIndex numPolicies = CFArrayGetCount(mPolicies); 369 CFAllocatorRef allocator = CFGetAllocator(mPolicies); 370 allPolicies = CFArrayCreateMutableCopy(allocator, numPolicies, mPolicies); 371 } 372 orderRevocationPolicies(allPolicies); 373 CFToVector<CssmField, SecPolicyRef, cfField> policies(allPolicies); 374#if 0 375 // error exit here if empty policies are not supported 376 if (policies.empty()) 377 MacOSError::throwMe(CSSMERR_TP_INVALID_POLICY_IDENTIFIERS); 378#endif 379 context.setPolicies(policies, policies); 380 381 // anchor certificates (if caller provides them, or if cert requires EV) 382 CFCopyRef<CFArrayRef> anchors(mAllowedAnchors); 383 CFToVector<CssmData, SecCertificateRef, cfCertificateData> roots(anchors); 384 if (!anchors) { 385 // no anchor certificates were provided; 386 // built-in anchors will be trusted unless explicitly disabled. 387 mUsingTrustSettings = (mAnchorPolicy < useAnchorsOnly); 388 secdebug("userTrust", "Trust::evaluate() %s", 389 (mUsingTrustSettings) ? "using UserTrust" : "has no trusted anchors!"); 390 } 391 else { 392 // anchor certificates were provided; 393 // built-in anchors will NOT also be trusted unless explicitly enabled. 394 mUsingTrustSettings = (mAnchorPolicy == useAnchorsAndBuiltIns); 395 secdebug("userTrust", "Trust::evaluate() using %s %s anchors", 396 (mUsingTrustSettings) ? "UserTrust AND" : "only", 397 (isEVCandidate) ? "EV" : "caller"); 398 context.anchors(roots, roots); 399 } 400 401 // dlDbList (keychain list) 402 vector<CSSM_DL_DB_HANDLE> dlDbList; 403 { 404 StLock<Mutex> _(SecTrustKeychainsGetMutex()); 405 StorageManager::KeychainList& list = searchLibs(); 406 for (StorageManager::KeychainList::const_iterator it = list.begin(); 407 it != list.end(); it++) 408 { 409 try 410 { 411 // For the purpose of looking up intermediate certificates to establish trust, 412 // do not include the network-based LDAP or DotMac pseudo-keychains. (The only 413 // time the network should be consulted for certificates is if there is an AIA 414 // extension with a specific URL, which will be handled by the TP code.) 415 CSSM_DL_DB_HANDLE dldbHandle = (*it)->database()->handle(); 416 if (dldbHandle.DLHandle) { 417 CSSM_GUID guid = {}; 418 CSSM_RETURN crtn = CSSM_GetModuleGUIDFromHandle(dldbHandle.DLHandle, &guid); 419 if (crtn == CSSM_OK) { 420 if ((memcmp(&guid, &gGuidAppleLDAPDL, sizeof(CSSM_GUID))==0) || 421 (memcmp(&guid, &gGuidAppleDotMacDL, sizeof(CSSM_GUID))==0)) { 422 continue; // don't add to dlDbList 423 } 424 } 425 } 426 // This DB is OK to search for intermediate certificates. 427 dlDbList.push_back(dldbHandle); 428 } 429 catch (...) 430 { 431 } 432 } 433 if(mUsingTrustSettings) { 434 /* Append system anchors for use with Trust Settings */ 435 try { 436 CSSM_DL_DB_HANDLE rootStoreHandle = trustKeychains().rootStoreHandle(); 437 if (rootStoreHandle.DBHandle) 438 dlDbList.push_back(rootStoreHandle); 439 actionDataP->ActionFlags |= CSSM_TP_ACTION_TRUST_SETTINGS; 440 } 441 catch (...) { 442 // no root store or system keychain; don't use trust settings but continue 443 mUsingTrustSettings = false; 444 } 445 try { 446 CSSM_DL_DB_HANDLE systemKcHandle = trustKeychains().systemKcHandle(); 447 if (systemKcHandle.DBHandle) 448 dlDbList.push_back(systemKcHandle); 449 } 450 catch(...) { 451 /* Oh well, at least we got the root store DB */ 452 } 453 } 454 context.setDlDbList((uint32)dlDbList.size(), &dlDbList[0]); 455 } 456 457 // verification time 458 char timeString[15]; 459 if (mVerifyTime) { 460 CssmUniformDate(static_cast<CFDateRef>(mVerifyTime)).convertTo( 461 timeString, sizeof(timeString)); 462 context.time(timeString); 463 } 464 465 // to avoid keychain open/close thrashing, hold a copy of the search list 466 StorageManager::KeychainList *holdSearchList = NULL; 467 if (searchLibs().size() > 0) { 468 holdSearchList = new StorageManager::KeychainList; 469 globals().storageManager.getSearchList(*holdSearchList); 470 } 471 472 // Go TP! 473 try { 474 mTP->certGroupVerify(subjectCertGroup, context, &mTpResult); 475 mTpReturn = errSecSuccess; 476 } catch (CommonError &err) { 477 mTpReturn = err.osStatus(); 478 secdebug("trusteval", "certGroupVerify exception: %d", (int)mTpReturn); 479 } 480 mResult = diagnoseOutcome(); 481 482 // see if we can use the evidence 483 if (mTpResult.count() > 0 484 && mTpResult[0].form() == CSSM_EVIDENCE_FORM_APPLE_HEADER 485 && mTpResult[0].as<CSSM_TP_APPLE_EVIDENCE_HEADER>()->Version == CSSM_TP_APPLE_EVIDENCE_VERSION 486 && mTpResult.count() == 3 487 && mTpResult[1].form() == CSSM_EVIDENCE_FORM_APPLE_CERTGROUP 488 && mTpResult[2].form() == CSSM_EVIDENCE_FORM_APPLE_CERT_INFO) { 489 evaluateUserTrust(*mTpResult[1].as<CertGroup>(), 490 mTpResult[2].as<CSSM_TP_APPLE_EVIDENCE_INFO>(), anchors); 491 } else { 492 // unexpected evidence information. Can't use it 493 secdebug("trusteval", "unexpected evidence ignored"); 494 } 495 496 /* do post-processing for the evaluated certificate chain */ 497 CFArrayRef fullChain = makeCFArray(convert, mCertChain); 498 CFDictionaryRef etResult = extendedTrustResults(fullChain, mResult, mTpReturn, isEVCandidate); 499 mExtendedResult = etResult; // assignment to CFRef type is an implicit retain 500 if (etResult) { 501 CFRelease(etResult); 502 } 503 if (fullChain) { 504 CFRelease(fullChain); 505 } 506 507 if (allPolicies) { 508 /* clean up revocation policies we created implicitly */ 509 if(numRevocationAdded) { 510 freeAddedRevocationPolicyData(allPolicies, numRevocationAdded, context.allocator); 511 } 512 CFRelease(allPolicies); 513 } 514 515 if (holdSearchList) { 516 delete holdSearchList; 517 holdSearchList = NULL; 518 } 519 } // end evaluation block with mutex; releases all temporary allocations in this scope 520 521 522 if (isEVCandidate && mResult == kSecTrustResultRecoverableTrustFailure && 523 isRevocationServerMetaError(mTpReturn)) { 524 // re-do the evaluation, this time disabling EV 525 evaluate(true); 526 } 527} 528 529// CSSM_RETURN values that map to kSecTrustResultRecoverableTrustFailure. 530static const CSSM_RETURN recoverableErrors[] = 531{ 532 CSSMERR_TP_INVALID_ANCHOR_CERT, 533 CSSMERR_TP_NOT_TRUSTED, 534 CSSMERR_TP_VERIFICATION_FAILURE, 535 CSSMERR_TP_VERIFY_ACTION_FAILED, 536 CSSMERR_TP_INVALID_REQUEST_INPUTS, 537 CSSMERR_TP_CERT_EXPIRED, 538 CSSMERR_TP_CERT_NOT_VALID_YET, 539 CSSMERR_TP_CERTIFICATE_CANT_OPERATE, 540 CSSMERR_TP_INVALID_CERT_AUTHORITY, 541 CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK, 542 CSSMERR_APPLETP_HOSTNAME_MISMATCH, 543 CSSMERR_TP_VERIFY_ACTION_FAILED, 544 CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND, 545 CSSMERR_APPLETP_SMIME_NO_EMAIL_ADDRS, 546 CSSMERR_APPLETP_SMIME_BAD_EXT_KEY_USE, 547 CSSMERR_APPLETP_CS_BAD_CERT_CHAIN_LENGTH, 548 CSSMERR_APPLETP_CS_NO_BASIC_CONSTRAINTS, 549 CSSMERR_APPLETP_CS_BAD_PATH_LENGTH, 550 CSSMERR_APPLETP_CS_NO_EXTENDED_KEY_USAGE, 551 CSSMERR_APPLETP_INVALID_EXTENDED_KEY_USAGE, 552 CSSMERR_APPLETP_CODE_SIGN_DEVELOPMENT, 553 CSSMERR_APPLETP_RS_BAD_CERT_CHAIN_LENGTH, 554 CSSMERR_APPLETP_UNKNOWN_CRITICAL_EXTEN, 555 CSSMERR_APPLETP_CRL_NOT_FOUND, 556 CSSMERR_APPLETP_CRL_SERVER_DOWN, 557 CSSMERR_APPLETP_CRL_NOT_VALID_YET, 558 CSSMERR_APPLETP_OCSP_UNAVAILABLE, 559 CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK, 560 CSSMERR_APPLETP_NETWORK_FAILURE, 561 CSSMERR_APPLETP_OCSP_RESP_TRY_LATER, 562 CSSMERR_APPLETP_IDENTIFIER_MISSING, 563}; 564#define NUM_RECOVERABLE_ERRORS (sizeof(recoverableErrors) / sizeof(CSSM_RETURN)) 565 566// 567// Classify the TP outcome in terms of a SecTrustResultType 568// 569SecTrustResultType Trust::diagnoseOutcome() 570{ 571 StLock<Mutex>_(mMutex); 572 573 uint32 chainLength = 0; 574 if (mTpResult.count() == 3 && 575 mTpResult[1].form() == CSSM_EVIDENCE_FORM_APPLE_CERTGROUP && 576 mTpResult[2].form() == CSSM_EVIDENCE_FORM_APPLE_CERT_INFO) 577 { 578 const CertGroup &chain = *mTpResult[1].as<CertGroup>(); 579 chainLength = chain.count(); 580 } 581 582 switch (mTpReturn) { 583 case errSecSuccess: // peachy 584 if (mUsingTrustSettings) 585 { 586 if (chainLength) 587 { 588 const CSSM_TP_APPLE_EVIDENCE_INFO *infoList = mTpResult[2].as<CSSM_TP_APPLE_EVIDENCE_INFO>(); 589 const TPEvidenceInfo &info = TPEvidenceInfo::overlay(infoList[chainLength-1]); 590 const CSSM_TP_APPLE_CERT_STATUS resultCertStatus = info.status(); 591 bool hasUserDomainTrust = ((resultCertStatus & CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST) && 592 (resultCertStatus & CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_USER)); 593 bool hasAdminDomainTrust = ((resultCertStatus & CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST) && 594 (resultCertStatus & CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_ADMIN)); 595 if (hasUserDomainTrust || hasAdminDomainTrust) 596 { 597 return kSecTrustResultProceed; // explicitly allowed 598 } 599 } 600 } 601 return kSecTrustResultUnspecified; // cert evaluates OK 602 case CSSMERR_TP_INVALID_CERTIFICATE: // bad certificate 603 return kSecTrustResultFatalTrustFailure; 604 case CSSMERR_APPLETP_TRUST_SETTING_DENY: // authoritative denial 605 return kSecTrustResultDeny; 606 default: 607 break; 608 } 609 610 // a known list of returns maps to kSecTrustResultRecoverableTrustFailure 611 const CSSM_RETURN *errp=recoverableErrors; 612 for(unsigned dex=0; dex<NUM_RECOVERABLE_ERRORS; dex++, errp++) { 613 if(*errp == mTpReturn) { 614 return kSecTrustResultRecoverableTrustFailure; 615 } 616 } 617 return kSecTrustResultOtherError; // unknown 618} 619 620 621// 622// Assuming a good evidence chain, check user trust 623// settings and set mResult accordingly. 624// 625void Trust::evaluateUserTrust(const CertGroup &chain, 626 const CSSM_TP_APPLE_EVIDENCE_INFO *infoList, CFCopyRef<CFArrayRef> anchors) 627{ 628 StLock<Mutex>_(mMutex); 629 // extract cert chain as Certificate objects 630 mCertChain.resize(chain.count()); 631 for (uint32 n = 0; n < mCertChain.size(); n++) { 632 const TPEvidenceInfo &info = TPEvidenceInfo::overlay(infoList[n]); 633 if (info.recordId()) { 634 Keychain keychain = keychainByDLDb(info.DlDbHandle); 635 DbUniqueRecord uniqueId(keychain->database()->newDbUniqueRecord()); 636 secdebug("trusteval", "evidence %lu from keychain \"%s\"", (unsigned long)n, keychain->name()); 637 *static_cast<CSSM_DB_UNIQUE_RECORD_PTR *>(uniqueId) = info.UniqueRecord; 638 uniqueId->activate(); // transfers ownership 639 Item ii = keychain->item(CSSM_DL_DB_RECORD_X509_CERTIFICATE, uniqueId); 640 Certificate* cert = dynamic_cast<Certificate*>(ii.get()); 641 if (cert == NULL) { 642 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); 643 } 644 mCertChain[n] = cert; 645 } else if (info.status(CSSM_CERT_STATUS_IS_IN_INPUT_CERTS)) { 646 secdebug("trusteval", "evidence %lu from input cert %lu", (unsigned long)n, (unsigned long)info.index()); 647 assert(info.index() < uint32(CFArrayGetCount(mCerts))); 648 SecCertificateRef cert = SecCertificateRef(CFArrayGetValueAtIndex(mCerts, 649 info.index())); 650 mCertChain[n] = Certificate::required(cert); 651 } else if (info.status(CSSM_CERT_STATUS_IS_IN_ANCHORS)) { 652 secdebug("trusteval", "evidence %lu from anchor cert %lu", (unsigned long)n, (unsigned long)info.index()); 653 assert(info.index() < uint32(CFArrayGetCount(anchors))); 654 SecCertificateRef cert = SecCertificateRef(CFArrayGetValueAtIndex(anchors, 655 info.index())); 656 mCertChain[n] = Certificate::required(cert); 657 } else { 658 // unknown source; make a new Certificate for it 659 secdebug("trusteval", "evidence %lu from unknown source", (unsigned long)n); 660 mCertChain[n] = 661 new Certificate(chain.blobCerts()[n], 662 CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_BER); 663 } 664 } 665 666 // now walk the chain, leaf-to-root, checking for user settings 667 TrustStore &store = gStore(); 668 SecPointer<Policy> policy = (CFArrayGetCount(mPolicies)) ? 669 Policy::required(SecPolicyRef(CFArrayGetValueAtIndex(mPolicies, 0))) : NULL; 670 for (mResultIndex = 0; 671 mResult == kSecTrustResultUnspecified && mResultIndex < mCertChain.size() && policy; 672 mResultIndex++) { 673 if (!mCertChain[mResultIndex]) { 674 assert(false); 675 continue; 676 } 677 mResult = store.find(mCertChain[mResultIndex], policy, searchLibs()); 678 secdebug("trusteval", "trustResult=%d from cert %d", (int)mResult, (int)mResultIndex); 679 } 680} 681 682 683// 684// Release TP evidence information. 685// This information is severely under-defined by CSSM, so we proceed 686// as follows: 687// (a) If the evidence matches an Apple-defined pattern, use specific 688// knowledge of that format. 689// (b) Otherwise, assume that the void * are flat blocks of memory. 690// 691void Trust::releaseTPEvidence(TPVerifyResult &result, Allocator &allocator) 692{ 693 if (result.count() > 0) { // something to do 694 if (result[0].form() == CSSM_EVIDENCE_FORM_APPLE_HEADER) { 695 // Apple defined evidence form -- use intimate knowledge 696 if (result[0].as<CSSM_TP_APPLE_EVIDENCE_HEADER>()->Version == CSSM_TP_APPLE_EVIDENCE_VERSION 697 && result.count() == 3 698 && result[1].form() == CSSM_EVIDENCE_FORM_APPLE_CERTGROUP 699 && result[2].form() == CSSM_EVIDENCE_FORM_APPLE_CERT_INFO) { 700 // proper format 701 CertGroup& certs = *result[1].as<CertGroup>(); 702 CSSM_TP_APPLE_EVIDENCE_INFO *evidence = result[2].as<CSSM_TP_APPLE_EVIDENCE_INFO>(); 703 uint32 count = certs.count(); 704 allocator.free(result[0].data()); // just a struct 705 certs.destroy(allocator); // certgroup contents 706 allocator.free(result[1].data()); // the CertGroup itself 707 for (uint32 n = 0; n < count; n++) 708 allocator.free(evidence[n].StatusCodes); 709 allocator.free(result[2].data()); // array of (flat) info structs 710 } else { 711 secdebug("trusteval", "unrecognized Apple TP evidence format"); 712 // drop it -- better leak than kill 713 } 714 } else { 715 // unknown format -- blindly assume flat blobs 716 secdebug("trusteval", "destroying unknown TP evidence format"); 717 for (uint32 n = 0; n < result.count(); n++) 718 { 719 allocator.free(result[n].data()); 720 } 721 } 722 723 allocator.free (result.Evidence); 724 } 725} 726 727 728// 729// Clear evaluation results unless state is initial (invalid) 730// 731void Trust::clearResults() 732{ 733 StLock<Mutex>_(mMutex); 734 if (mResult != kSecTrustResultInvalid) { 735 releaseTPEvidence(mTpResult, mTP.allocator()); 736 mResult = kSecTrustResultInvalid; 737 } 738} 739 740 741// 742// Build evidence information 743// 744void Trust::buildEvidence(CFArrayRef &certChain, TPEvidenceInfo * &statusChain) 745{ 746 StLock<Mutex>_(mMutex); 747 if (mResult == kSecTrustResultInvalid) 748 MacOSError::throwMe(errSecTrustNotAvailable); 749 certChain = mEvidenceReturned = 750 makeCFArray(convert, mCertChain); 751 if(mTpResult.count() >= 3) { 752 statusChain = mTpResult[2].as<TPEvidenceInfo>(); 753 } 754 else { 755 statusChain = NULL; 756 } 757} 758 759 760// 761// Return extended result dictionary 762// 763void Trust::extendedResult(CFDictionaryRef &result) 764{ 765 if (mResult == kSecTrustResultInvalid) 766 MacOSError::throwMe(errSecTrustNotAvailable); 767 if (mExtendedResult) 768 CFRetain(mExtendedResult); // retain before handing out to caller 769 result = mExtendedResult; 770} 771 772 773// 774// Return properties array (a CFDictionaryRef for each certificate in chain) 775// 776CFArrayRef Trust::properties() 777{ 778 // Builds and returns an array which the caller must release. 779 StLock<Mutex>_(mMutex); 780 CFMutableArrayRef properties = CFArrayCreateMutable(NULL, 0, 781 &kCFTypeArrayCallBacks); 782 if (mResult == kSecTrustResultInvalid) // chain not built or evaluated 783 return properties; 784 785 // Walk the chain from leaf to anchor, building properties dictionaries 786 for (uint32 idx=0; idx < mCertChain.size(); idx++) { 787 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, 788 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 789 if (dict) { 790 CFStringRef title = NULL; 791 mCertChain[idx]->inferLabel(false, &title); 792 if (title) { 793 CFDictionarySetValue(dict, (const void *)kSecPropertyTypeTitle, (const void *)title); 794 CFRelease(title); 795 } 796 if (idx == 0 && mTpReturn != errSecSuccess) { 797 CFStringRef error = SecCopyErrorMessageString(mTpReturn, NULL); 798 if (error) { 799 CFDictionarySetValue(dict, (const void *)kSecPropertyTypeError, (const void *)error); 800 CFRelease(error); 801 } 802 } 803 CFArrayAppendValue(properties, (const void *)dict); 804 CFRelease(dict); 805 } 806 } 807 808 return properties; 809} 810 811// 812// Return dictionary of evaluation results 813// 814CFDictionaryRef Trust::results() 815{ 816 // Builds and returns a dictionary which the caller must release. 817 StLock<Mutex>_(mMutex); 818 CFMutableDictionaryRef results = CFDictionaryCreateMutable(NULL, 0, 819 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 820 821 // kSecTrustResultValue 822 CFNumberRef numValue = CFNumberCreate(NULL, kCFNumberSInt32Type, &mResult); 823 if (numValue) { 824 CFDictionarySetValue(results, (const void *)kSecTrustResultValue, (const void *)numValue); 825 CFRelease(numValue); 826 } 827 if (mResult == kSecTrustResultInvalid || !mExtendedResult) 828 return results; // we have nothing more to add 829 830 // kSecTrustEvaluationDate 831 CFTypeRef evaluationDate; 832 if (CFDictionaryGetValueIfPresent(mExtendedResult, kSecTrustEvaluationDate, &evaluationDate)) 833 CFDictionarySetValue(results, (const void *)kSecTrustEvaluationDate, (const void *)evaluationDate); 834 835 // kSecTrustExtendedValidation, kSecTrustOrganizationName 836 CFTypeRef organizationName; 837 if (CFDictionaryGetValueIfPresent(mExtendedResult, kSecEVOrganizationName, &organizationName)) { 838 CFDictionarySetValue(results, (const void *)kSecTrustOrganizationName, (const void *)organizationName); 839 CFDictionarySetValue(results, (const void *)kSecTrustExtendedValidation, (const void *)kCFBooleanTrue); 840 } 841 842 // kSecTrustRevocationChecked, kSecTrustRevocationValidUntilDate 843 CFTypeRef expirationDate; 844 if (CFDictionaryGetValueIfPresent(mExtendedResult, kSecTrustExpirationDate, &expirationDate)) { 845 CFDictionarySetValue(results, (const void *)kSecTrustRevocationValidUntilDate, (const void *)expirationDate); 846 CFDictionarySetValue(results, (const void *)kSecTrustRevocationChecked, (const void *)kCFBooleanTrue); 847 } 848 849 return results; 850} 851 852 853 854//* =========================================================================== 855//* We need a way to compare two CSSM_DL_DB_HANDLEs WITHOUT using a operator 856//* overload 857//* =========================================================================== 858static 859bool Compare_CSSM_DL_DB_HANDLE(const CSSM_DL_DB_HANDLE &h1, const CSSM_DL_DB_HANDLE &h2) 860{ 861 return (h1.DLHandle == h2.DLHandle && h1.DBHandle == h2.DBHandle); 862} 863 864 865 866// 867// Given a DL_DB_HANDLE, locate the Keychain object (from the search list) 868// 869Keychain Trust::keychainByDLDb(const CSSM_DL_DB_HANDLE &handle) 870{ 871 StLock<Mutex>_(mMutex); 872 StorageManager::KeychainList& list = searchLibs(); 873 for (StorageManager::KeychainList::const_iterator it = list.begin(); 874 it != list.end(); it++) 875 { 876 try 877 { 878 879 if (Compare_CSSM_DL_DB_HANDLE((*it)->database()->handle(), handle)) 880 return *it; 881 } 882 catch (...) 883 { 884 } 885 } 886 if(mUsingTrustSettings) { 887 try { 888 if(Compare_CSSM_DL_DB_HANDLE(trustKeychains().rootStoreHandle(), handle)) { 889 return trustKeychains().rootStore(); 890 } 891 if(Compare_CSSM_DL_DB_HANDLE(trustKeychains().systemKcHandle(), handle)) { 892 return trustKeychains().systemKc(); 893 } 894 } 895 catch(...) { 896 /* one of those is missing; proceed */ 897 } 898 } 899 900 // could not find in search list - internal error 901 902 // we now throw an error here rather than assert and silently fail. That way our application won't crash... 903 MacOSError::throwMe(errSecInternal); 904} 905