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