1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "net/cert/cert_verify_proc_mac.h" 6 7 #include <CommonCrypto/CommonDigest.h> 8 #include <CoreServices/CoreServices.h> 9 #include <Security/Security.h> 10 11 #include <string> 12 #include <vector> 13 14 #include "base/logging.h" 15 #include "base/mac/mac_logging.h" 16 #include "base/mac/scoped_cftyperef.h" 17 #include "base/sha1.h" 18 #include "base/strings/string_piece.h" 19 #include "base/synchronization/lock.h" 20 #include "crypto/mac_security_services_lock.h" 21 #include "crypto/sha2.h" 22 #include "net/base/net_errors.h" 23 #include "net/cert/asn1_util.h" 24 #include "net/cert/cert_status_flags.h" 25 #include "net/cert/cert_verifier.h" 26 #include "net/cert/cert_verify_result.h" 27 #include "net/cert/crl_set.h" 28 #include "net/cert/test_root_certs.h" 29 #include "net/cert/x509_certificate.h" 30 #include "net/cert/x509_certificate_known_roots_mac.h" 31 #include "net/cert/x509_util_mac.h" 32 33 // From 10.7.2 libsecurity_keychain-55035/lib/SecTrustPriv.h, for use with 34 // SecTrustCopyExtendedResult. 35 #ifndef kSecEVOrganizationName 36 #define kSecEVOrganizationName CFSTR("Organization") 37 #endif 38 39 using base::ScopedCFTypeRef; 40 41 namespace net { 42 43 namespace { 44 45 typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef, 46 CFDictionaryRef*); 47 48 int NetErrorFromOSStatus(OSStatus status) { 49 switch (status) { 50 case noErr: 51 return OK; 52 case errSecNotAvailable: 53 case errSecNoCertificateModule: 54 case errSecNoPolicyModule: 55 return ERR_NOT_IMPLEMENTED; 56 case errSecAuthFailed: 57 return ERR_ACCESS_DENIED; 58 default: { 59 OSSTATUS_LOG(ERROR, status) << "Unknown error mapped to ERR_FAILED"; 60 return ERR_FAILED; 61 } 62 } 63 } 64 65 CertStatus CertStatusFromOSStatus(OSStatus status) { 66 switch (status) { 67 case noErr: 68 return 0; 69 70 case CSSMERR_TP_INVALID_ANCHOR_CERT: 71 case CSSMERR_TP_NOT_TRUSTED: 72 case CSSMERR_TP_INVALID_CERT_AUTHORITY: 73 return CERT_STATUS_AUTHORITY_INVALID; 74 75 case CSSMERR_TP_CERT_EXPIRED: 76 case CSSMERR_TP_CERT_NOT_VALID_YET: 77 // "Expired" and "not yet valid" collapse into a single status. 78 return CERT_STATUS_DATE_INVALID; 79 80 case CSSMERR_TP_CERT_REVOKED: 81 case CSSMERR_TP_CERT_SUSPENDED: 82 return CERT_STATUS_REVOKED; 83 84 case CSSMERR_APPLETP_HOSTNAME_MISMATCH: 85 return CERT_STATUS_COMMON_NAME_INVALID; 86 87 case CSSMERR_APPLETP_CRL_NOT_FOUND: 88 case CSSMERR_APPLETP_OCSP_UNAVAILABLE: 89 case CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK: 90 return CERT_STATUS_NO_REVOCATION_MECHANISM; 91 92 case CSSMERR_APPLETP_CRL_EXPIRED: 93 case CSSMERR_APPLETP_CRL_NOT_VALID_YET: 94 case CSSMERR_APPLETP_CRL_SERVER_DOWN: 95 case CSSMERR_APPLETP_CRL_NOT_TRUSTED: 96 case CSSMERR_APPLETP_CRL_INVALID_ANCHOR_CERT: 97 case CSSMERR_APPLETP_CRL_POLICY_FAIL: 98 case CSSMERR_APPLETP_OCSP_BAD_RESPONSE: 99 case CSSMERR_APPLETP_OCSP_BAD_REQUEST: 100 case CSSMERR_APPLETP_OCSP_STATUS_UNRECOGNIZED: 101 case CSSMERR_APPLETP_NETWORK_FAILURE: 102 case CSSMERR_APPLETP_OCSP_NOT_TRUSTED: 103 case CSSMERR_APPLETP_OCSP_INVALID_ANCHOR_CERT: 104 case CSSMERR_APPLETP_OCSP_SIG_ERROR: 105 case CSSMERR_APPLETP_OCSP_NO_SIGNER: 106 case CSSMERR_APPLETP_OCSP_RESP_MALFORMED_REQ: 107 case CSSMERR_APPLETP_OCSP_RESP_INTERNAL_ERR: 108 case CSSMERR_APPLETP_OCSP_RESP_TRY_LATER: 109 case CSSMERR_APPLETP_OCSP_RESP_SIG_REQUIRED: 110 case CSSMERR_APPLETP_OCSP_RESP_UNAUTHORIZED: 111 case CSSMERR_APPLETP_OCSP_NONCE_MISMATCH: 112 // We asked for a revocation check, but didn't get it. 113 return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION; 114 115 case CSSMERR_APPLETP_SSL_BAD_EXT_KEY_USE: 116 // TODO(wtc): Should we add CERT_STATUS_WRONG_USAGE? 117 return CERT_STATUS_INVALID; 118 119 case CSSMERR_APPLETP_CRL_BAD_URI: 120 case CSSMERR_APPLETP_IDP_FAIL: 121 return CERT_STATUS_INVALID; 122 123 case CSSMERR_CSP_UNSUPPORTED_KEY_SIZE: 124 // Mapping UNSUPPORTED_KEY_SIZE to CERT_STATUS_WEAK_KEY is not strictly 125 // accurate, as the error may have been returned due to a key size 126 // that exceeded the maximum supported. However, within 127 // CertVerifyProcMac::VerifyInternal(), this code should only be 128 // encountered as a certificate status code, and only when the key size 129 // is smaller than the minimum required (1024 bits). 130 return CERT_STATUS_WEAK_KEY; 131 132 default: { 133 // Failure was due to something Chromium doesn't define a 134 // specific status for (such as basic constraints violation, or 135 // unknown critical extension) 136 OSSTATUS_LOG(WARNING, status) 137 << "Unknown error mapped to CERT_STATUS_INVALID"; 138 return CERT_STATUS_INVALID; 139 } 140 } 141 } 142 143 // Creates a series of SecPolicyRefs to be added to a SecTrustRef used to 144 // validate a certificate for an SSL server. |hostname| contains the name of 145 // the SSL server that the certificate should be verified against. |flags| is 146 // a bitwise-OR of VerifyFlags that can further alter how trust is validated, 147 // such as how revocation is checked. If successful, returns noErr, and 148 // stores the resultant array of SecPolicyRefs in |policies|. 149 OSStatus CreateTrustPolicies(const std::string& hostname, 150 int flags, 151 ScopedCFTypeRef<CFArrayRef>* policies) { 152 ScopedCFTypeRef<CFMutableArrayRef> local_policies( 153 CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks)); 154 if (!local_policies) 155 return memFullErr; 156 157 SecPolicyRef ssl_policy; 158 OSStatus status = x509_util::CreateSSLServerPolicy(hostname, &ssl_policy); 159 if (status) 160 return status; 161 CFArrayAppendValue(local_policies, ssl_policy); 162 CFRelease(ssl_policy); 163 164 // Explicitly add revocation policies, in order to override system 165 // revocation checking policies and instead respect the application-level 166 // revocation preference. 167 status = x509_util::CreateRevocationPolicies( 168 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED), 169 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY), 170 local_policies); 171 if (status) 172 return status; 173 174 policies->reset(local_policies.release()); 175 return noErr; 176 } 177 178 // Saves some information about the certificate chain |cert_chain| in 179 // |*verify_result|. The caller MUST initialize |*verify_result| before 180 // calling this function. 181 void GetCertChainInfo(CFArrayRef cert_chain, 182 CSSM_TP_APPLE_EVIDENCE_INFO* chain_info, 183 CertVerifyResult* verify_result) { 184 SecCertificateRef verified_cert = NULL; 185 std::vector<SecCertificateRef> verified_chain; 186 for (CFIndex i = 0, count = CFArrayGetCount(cert_chain); i < count; ++i) { 187 SecCertificateRef chain_cert = reinterpret_cast<SecCertificateRef>( 188 const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i))); 189 if (i == 0) { 190 verified_cert = chain_cert; 191 } else { 192 verified_chain.push_back(chain_cert); 193 } 194 195 if ((chain_info[i].StatusBits & CSSM_CERT_STATUS_IS_IN_ANCHORS) || 196 (chain_info[i].StatusBits & CSSM_CERT_STATUS_IS_ROOT)) { 197 // The current certificate is either in the user's trusted store or is 198 // a root (self-signed) certificate. Ignore the signature algorithm for 199 // these certificates, as it is meaningless for security. We allow 200 // self-signed certificates (i == 0 & IS_ROOT), since we accept that 201 // any security assertions by such a cert are inherently meaningless. 202 continue; 203 } 204 205 x509_util::CSSMCachedCertificate cached_cert; 206 OSStatus status = cached_cert.Init(chain_cert); 207 if (status) 208 continue; 209 x509_util::CSSMFieldValue signature_field; 210 status = cached_cert.GetField(&CSSMOID_X509V1SignatureAlgorithm, 211 &signature_field); 212 if (status || !signature_field.field()) 213 continue; 214 // Match the behaviour of OS X system tools and defensively check that 215 // sizes are appropriate. This would indicate a critical failure of the 216 // OS X certificate library, but based on history, it is best to play it 217 // safe. 218 const CSSM_X509_ALGORITHM_IDENTIFIER* sig_algorithm = 219 signature_field.GetAs<CSSM_X509_ALGORITHM_IDENTIFIER>(); 220 if (!sig_algorithm) 221 continue; 222 223 const CSSM_OID* alg_oid = &sig_algorithm->algorithm; 224 if (CSSMOIDEqual(alg_oid, &CSSMOID_MD2WithRSA)) { 225 verify_result->has_md2 = true; 226 } else if (CSSMOIDEqual(alg_oid, &CSSMOID_MD4WithRSA)) { 227 verify_result->has_md4 = true; 228 } else if (CSSMOIDEqual(alg_oid, &CSSMOID_MD5WithRSA)) { 229 verify_result->has_md5 = true; 230 } else if (CSSMOIDEqual(alg_oid, &CSSMOID_SHA1WithRSA) || 231 CSSMOIDEqual(alg_oid, &CSSMOID_SHA1WithRSA_OIW) || 232 CSSMOIDEqual(alg_oid, &CSSMOID_SHA1WithDSA) || 233 CSSMOIDEqual(alg_oid, &CSSMOID_SHA1WithDSA_CMS) || 234 CSSMOIDEqual(alg_oid, &CSSMOID_SHA1WithDSA_JDK) || 235 CSSMOIDEqual(alg_oid, &CSSMOID_ECDSA_WithSHA1)) { 236 verify_result->has_sha1 = true; 237 } 238 } 239 if (!verified_cert) 240 return; 241 242 verify_result->verified_cert = 243 X509Certificate::CreateFromHandle(verified_cert, verified_chain); 244 } 245 246 void AppendPublicKeyHashes(CFArrayRef chain, 247 HashValueVector* hashes) { 248 const CFIndex n = CFArrayGetCount(chain); 249 for (CFIndex i = 0; i < n; i++) { 250 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>( 251 const_cast<void*>(CFArrayGetValueAtIndex(chain, i))); 252 253 CSSM_DATA cert_data; 254 OSStatus err = SecCertificateGetData(cert, &cert_data); 255 DCHECK_EQ(err, noErr); 256 base::StringPiece der_bytes(reinterpret_cast<const char*>(cert_data.Data), 257 cert_data.Length); 258 base::StringPiece spki_bytes; 259 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki_bytes)) 260 continue; 261 262 HashValue sha1(HASH_VALUE_SHA1); 263 CC_SHA1(spki_bytes.data(), spki_bytes.size(), sha1.data()); 264 hashes->push_back(sha1); 265 266 HashValue sha256(HASH_VALUE_SHA256); 267 CC_SHA256(spki_bytes.data(), spki_bytes.size(), sha256.data()); 268 hashes->push_back(sha256); 269 } 270 } 271 272 bool CheckRevocationWithCRLSet(CFArrayRef chain, CRLSet* crl_set) { 273 if (CFArrayGetCount(chain) == 0) 274 return true; 275 276 // We iterate from the root certificate down to the leaf, keeping track of 277 // the issuer's SPKI at each step. 278 std::string issuer_spki_hash; 279 for (CFIndex i = CFArrayGetCount(chain) - 1; i >= 0; i--) { 280 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>( 281 const_cast<void*>(CFArrayGetValueAtIndex(chain, i))); 282 283 CSSM_DATA cert_data; 284 OSStatus err = SecCertificateGetData(cert, &cert_data); 285 if (err != noErr) { 286 NOTREACHED(); 287 continue; 288 } 289 base::StringPiece der_bytes(reinterpret_cast<const char*>(cert_data.Data), 290 cert_data.Length); 291 base::StringPiece spki; 292 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) { 293 NOTREACHED(); 294 continue; 295 } 296 297 const std::string spki_hash = crypto::SHA256HashString(spki); 298 x509_util::CSSMCachedCertificate cached_cert; 299 if (cached_cert.Init(cert) != CSSM_OK) { 300 NOTREACHED(); 301 continue; 302 } 303 x509_util::CSSMFieldValue serial_number; 304 err = cached_cert.GetField(&CSSMOID_X509V1SerialNumber, &serial_number); 305 if (err || !serial_number.field()) { 306 NOTREACHED(); 307 continue; 308 } 309 310 base::StringPiece serial( 311 reinterpret_cast<const char*>(serial_number.field()->Data), 312 serial_number.field()->Length); 313 314 CRLSet::Result result = crl_set->CheckSPKI(spki_hash); 315 316 if (result != CRLSet::REVOKED && !issuer_spki_hash.empty()) 317 result = crl_set->CheckSerial(serial, issuer_spki_hash); 318 319 issuer_spki_hash = spki_hash; 320 321 switch (result) { 322 case CRLSet::REVOKED: 323 return false; 324 case CRLSet::UNKNOWN: 325 case CRLSet::GOOD: 326 continue; 327 default: 328 NOTREACHED(); 329 return false; 330 } 331 } 332 333 return true; 334 } 335 336 // IsIssuedByKnownRoot returns true if the given chain is rooted at a root CA 337 // that we recognise as a standard root. 338 // static 339 bool IsIssuedByKnownRoot(CFArrayRef chain) { 340 int n = CFArrayGetCount(chain); 341 if (n < 1) 342 return false; 343 SecCertificateRef root_ref = reinterpret_cast<SecCertificateRef>( 344 const_cast<void*>(CFArrayGetValueAtIndex(chain, n - 1))); 345 SHA1HashValue hash = X509Certificate::CalculateFingerprint(root_ref); 346 return IsSHA1HashInSortedArray( 347 hash, &kKnownRootCertSHA1Hashes[0][0], sizeof(kKnownRootCertSHA1Hashes)); 348 } 349 350 // Builds and evaluates a SecTrustRef for the certificate chain contained 351 // in |cert_array|, using the verification policies in |trust_policies|. On 352 // success, returns OK, and updates |trust_ref|, |trust_result|, 353 // |verified_chain|, and |chain_info| with the verification results. On 354 // failure, no output parameters are modified. 355 // 356 // Note: An OK return does not mean that |cert_array| is trusted, merely that 357 // verification was performed successfully. 358 // 359 // This function should only be called while the Mac Security Services lock is 360 // held. 361 int BuildAndEvaluateSecTrustRef(CFArrayRef cert_array, 362 CFArrayRef trust_policies, 363 int flags, 364 ScopedCFTypeRef<SecTrustRef>* trust_ref, 365 SecTrustResultType* trust_result, 366 ScopedCFTypeRef<CFArrayRef>* verified_chain, 367 CSSM_TP_APPLE_EVIDENCE_INFO** chain_info) { 368 SecTrustRef tmp_trust = NULL; 369 OSStatus status = SecTrustCreateWithCertificates(cert_array, trust_policies, 370 &tmp_trust); 371 if (status) 372 return NetErrorFromOSStatus(status); 373 ScopedCFTypeRef<SecTrustRef> scoped_tmp_trust(tmp_trust); 374 375 if (TestRootCerts::HasInstance()) { 376 status = TestRootCerts::GetInstance()->FixupSecTrustRef(tmp_trust); 377 if (status) 378 return NetErrorFromOSStatus(status); 379 } 380 381 CSSM_APPLE_TP_ACTION_DATA tp_action_data; 382 memset(&tp_action_data, 0, sizeof(tp_action_data)); 383 tp_action_data.Version = CSSM_APPLE_TP_ACTION_VERSION; 384 // Allow CSSM to download any missing intermediate certificates if an 385 // authorityInfoAccess extension or issuerAltName extension is present. 386 tp_action_data.ActionFlags = CSSM_TP_ACTION_FETCH_CERT_FROM_NET | 387 CSSM_TP_ACTION_TRUST_SETTINGS; 388 389 // Note: For EV certificates, the Apple TP will handle setting these flags 390 // as part of EV evaluation. 391 if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED) { 392 // Require a positive result from an OCSP responder or a CRL (or both) 393 // for every certificate in the chain. The Apple TP automatically 394 // excludes the self-signed root from this requirement. If a certificate 395 // is missing both a crlDistributionPoints extension and an 396 // authorityInfoAccess extension with an OCSP responder URL, then we 397 // will get a kSecTrustResultRecoverableTrustFailure back from 398 // SecTrustEvaluate(), with a 399 // CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK error code. In that case, 400 // we'll set our own result to include 401 // CERT_STATUS_NO_REVOCATION_MECHANISM. If one or both extensions are 402 // present, and a check fails (server unavailable, OCSP retry later, 403 // signature mismatch), then we'll set our own result to include 404 // CERT_STATUS_UNABLE_TO_CHECK_REVOCATION. 405 tp_action_data.ActionFlags |= CSSM_TP_ACTION_REQUIRE_REV_PER_CERT; 406 407 // Note, even if revocation checking is disabled, SecTrustEvaluate() will 408 // modify the OCSP options so as to attempt OCSP checking if it believes a 409 // certificate may chain to an EV root. However, because network fetches 410 // are disabled in CreateTrustPolicies() when revocation checking is 411 // disabled, these will only go against the local cache. 412 } 413 414 CFDataRef action_data_ref = 415 CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, 416 reinterpret_cast<UInt8*>(&tp_action_data), 417 sizeof(tp_action_data), kCFAllocatorNull); 418 if (!action_data_ref) 419 return ERR_OUT_OF_MEMORY; 420 ScopedCFTypeRef<CFDataRef> scoped_action_data_ref(action_data_ref); 421 status = SecTrustSetParameters(tmp_trust, CSSM_TP_ACTION_DEFAULT, 422 action_data_ref); 423 if (status) 424 return NetErrorFromOSStatus(status); 425 426 // Verify the certificate. A non-zero result from SecTrustGetResult() 427 // indicates that some fatal error occurred and the chain couldn't be 428 // processed, not that the chain contains no errors. We need to examine the 429 // output of SecTrustGetResult() to determine that. 430 SecTrustResultType tmp_trust_result; 431 status = SecTrustEvaluate(tmp_trust, &tmp_trust_result); 432 if (status) 433 return NetErrorFromOSStatus(status); 434 CFArrayRef tmp_verified_chain = NULL; 435 CSSM_TP_APPLE_EVIDENCE_INFO* tmp_chain_info; 436 status = SecTrustGetResult(tmp_trust, &tmp_trust_result, &tmp_verified_chain, 437 &tmp_chain_info); 438 if (status) 439 return NetErrorFromOSStatus(status); 440 441 trust_ref->swap(scoped_tmp_trust); 442 *trust_result = tmp_trust_result; 443 verified_chain->reset(tmp_verified_chain); 444 *chain_info = tmp_chain_info; 445 446 return OK; 447 } 448 449 // OS X ships with both "GTE CyberTrust Global Root" and "Baltimore CyberTrust 450 // Root" as part of its trusted root store. However, a cross-certified version 451 // of the "Baltimore CyberTrust Root" exists that chains to "GTE CyberTrust 452 // Global Root". When OS X/Security.framework attempts to evaluate such a 453 // certificate chain, it disregards the "Baltimore CyberTrust Root" that exists 454 // within Keychain and instead attempts to terminate the chain in the "GTE 455 // CyberTrust Global Root". However, the GTE root is scheduled to be removed in 456 // a future OS X update (for sunsetting purposes), and once removed, such 457 // chains will fail validation, even though a trust anchor still exists. 458 // 459 // Rather than over-generalizing a solution that may mask a number of TLS 460 // misconfigurations, attempt to specifically match the affected 461 // cross-certified certificate and remove it from certificate chain processing. 462 bool IsBadBaltimoreGTECertificate(SecCertificateRef cert) { 463 // Matches the GTE-signed Baltimore CyberTrust Root 464 // https://cacert.omniroot.com/Baltimore-to-GTE-04-12.pem 465 static const SHA1HashValue kBadBaltimoreHashNew = 466 { { 0x4D, 0x34, 0xEA, 0x92, 0x76, 0x4B, 0x3A, 0x31, 0x49, 0x11, 467 0x99, 0x52, 0xF4, 0x19, 0x30, 0xCA, 0x11, 0x34, 0x83, 0x61 } }; 468 // Matches the legacy GTE-signed Baltimore CyberTrust Root 469 // https://cacert.omniroot.com/gte-2-2025.pem 470 static const SHA1HashValue kBadBaltimoreHashOld = 471 { { 0x54, 0xD8, 0xCB, 0x49, 0x1F, 0xA1, 0x6D, 0xF8, 0x87, 0xDC, 472 0x94, 0xA9, 0x34, 0xCC, 0x83, 0x6B, 0xDA, 0xA8, 0xA3, 0x69 } }; 473 474 SHA1HashValue fingerprint = X509Certificate::CalculateFingerprint(cert); 475 476 return fingerprint.Equals(kBadBaltimoreHashNew) || 477 fingerprint.Equals(kBadBaltimoreHashOld); 478 } 479 480 // Attempts to re-verify |cert_array| after adjusting the inputs to work around 481 // known issues in OS X. To be used if BuildAndEvaluateSecTrustRef fails to 482 // return a positive result for verification. 483 // 484 // This function should only be called while the Mac Security Services lock is 485 // held. 486 void RetrySecTrustEvaluateWithAdjustedChain( 487 CFArrayRef cert_array, 488 CFArrayRef trust_policies, 489 int flags, 490 ScopedCFTypeRef<SecTrustRef>* trust_ref, 491 SecTrustResultType* trust_result, 492 ScopedCFTypeRef<CFArrayRef>* verified_chain, 493 CSSM_TP_APPLE_EVIDENCE_INFO** chain_info) { 494 CFIndex count = CFArrayGetCount(*verified_chain); 495 CFIndex slice_point = 0; 496 497 for (CFIndex i = 1; i < count; ++i) { 498 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>( 499 const_cast<void*>(CFArrayGetValueAtIndex(*verified_chain, i))); 500 if (cert == NULL) 501 return; // Strange times; can't fix things up. 502 503 if (IsBadBaltimoreGTECertificate(cert)) { 504 slice_point = i; 505 break; 506 } 507 } 508 if (slice_point == 0) 509 return; // Nothing to do. 510 511 ScopedCFTypeRef<CFMutableArrayRef> adjusted_cert_array( 512 CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks)); 513 // Note: This excludes the certificate at |slice_point|. 514 CFArrayAppendArray(adjusted_cert_array, cert_array, 515 CFRangeMake(0, slice_point)); 516 517 // Ignore the result; failure will preserve the old verification results. 518 BuildAndEvaluateSecTrustRef( 519 adjusted_cert_array, trust_policies, flags, trust_ref, trust_result, 520 verified_chain, chain_info); 521 } 522 523 } // namespace 524 525 CertVerifyProcMac::CertVerifyProcMac() {} 526 527 CertVerifyProcMac::~CertVerifyProcMac() {} 528 529 bool CertVerifyProcMac::SupportsAdditionalTrustAnchors() const { 530 return false; 531 } 532 533 int CertVerifyProcMac::VerifyInternal( 534 X509Certificate* cert, 535 const std::string& hostname, 536 int flags, 537 CRLSet* crl_set, 538 const CertificateList& additional_trust_anchors, 539 CertVerifyResult* verify_result) { 540 ScopedCFTypeRef<CFArrayRef> trust_policies; 541 OSStatus status = CreateTrustPolicies(hostname, flags, &trust_policies); 542 if (status) 543 return NetErrorFromOSStatus(status); 544 545 // Create and configure a SecTrustRef, which takes our certificate(s) 546 // and our SSL SecPolicyRef. SecTrustCreateWithCertificates() takes an 547 // array of certificates, the first of which is the certificate we're 548 // verifying, and the subsequent (optional) certificates are used for 549 // chain building. 550 ScopedCFTypeRef<CFArrayRef> cert_array(cert->CreateOSCertChainForCert()); 551 552 // Serialize all calls that may use the Keychain, to work around various 553 // issues in OS X 10.6+ with multi-threaded access to Security.framework. 554 base::AutoLock lock(crypto::GetMacSecurityServicesLock()); 555 556 ScopedCFTypeRef<SecTrustRef> trust_ref; 557 SecTrustResultType trust_result = kSecTrustResultDeny; 558 ScopedCFTypeRef<CFArrayRef> completed_chain; 559 CSSM_TP_APPLE_EVIDENCE_INFO* chain_info = NULL; 560 561 int rv = BuildAndEvaluateSecTrustRef( 562 cert_array, trust_policies, flags, &trust_ref, &trust_result, 563 &completed_chain, &chain_info); 564 if (rv != OK) 565 return rv; 566 if (trust_result != kSecTrustResultUnspecified && 567 trust_result != kSecTrustResultProceed) { 568 RetrySecTrustEvaluateWithAdjustedChain( 569 cert_array, trust_policies, flags, &trust_ref, &trust_result, 570 &completed_chain, &chain_info); 571 } 572 573 if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED) 574 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; 575 576 if (crl_set && !CheckRevocationWithCRLSet(completed_chain, crl_set)) 577 verify_result->cert_status |= CERT_STATUS_REVOKED; 578 579 GetCertChainInfo(completed_chain, chain_info, verify_result); 580 581 // As of Security Update 2012-002/OS X 10.7.4, when an RSA key < 1024 bits 582 // is encountered, CSSM returns CSSMERR_TP_VERIFY_ACTION_FAILED and adds 583 // CSSMERR_CSP_UNSUPPORTED_KEY_SIZE as a certificate status. Avoid mapping 584 // the CSSMERR_TP_VERIFY_ACTION_FAILED to CERT_STATUS_INVALID if the only 585 // error was due to an unsupported key size. 586 bool policy_failed = false; 587 bool weak_key_or_signature_algorithm = false; 588 589 // Evaluate the results 590 OSStatus cssm_result; 591 switch (trust_result) { 592 case kSecTrustResultUnspecified: 593 case kSecTrustResultProceed: 594 // Certificate chain is valid and trusted ("unspecified" indicates that 595 // the user has not explicitly set a trust setting) 596 break; 597 598 // According to SecTrust.h, kSecTrustResultConfirm isn't returned on 10.5+, 599 // and it is marked deprecated in the 10.9 SDK. 600 case kSecTrustResultDeny: 601 // Certificate chain is explicitly untrusted. 602 verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID; 603 break; 604 605 case kSecTrustResultRecoverableTrustFailure: 606 // Certificate chain has a failure that can be overridden by the user. 607 status = SecTrustGetCssmResultCode(trust_ref, &cssm_result); 608 if (status) 609 return NetErrorFromOSStatus(status); 610 if (cssm_result == CSSMERR_TP_VERIFY_ACTION_FAILED) { 611 policy_failed = true; 612 } else { 613 verify_result->cert_status |= CertStatusFromOSStatus(cssm_result); 614 } 615 // Walk the chain of error codes in the CSSM_TP_APPLE_EVIDENCE_INFO 616 // structure which can catch multiple errors from each certificate. 617 for (CFIndex index = 0, chain_count = CFArrayGetCount(completed_chain); 618 index < chain_count; ++index) { 619 if (chain_info[index].StatusBits & CSSM_CERT_STATUS_EXPIRED || 620 chain_info[index].StatusBits & CSSM_CERT_STATUS_NOT_VALID_YET) 621 verify_result->cert_status |= CERT_STATUS_DATE_INVALID; 622 if (!IsCertStatusError(verify_result->cert_status) && 623 chain_info[index].NumStatusCodes == 0) { 624 LOG(WARNING) << "chain_info[" << index << "].NumStatusCodes is 0" 625 ", chain_info[" << index << "].StatusBits is " 626 << chain_info[index].StatusBits; 627 } 628 for (uint32 status_code_index = 0; 629 status_code_index < chain_info[index].NumStatusCodes; 630 ++status_code_index) { 631 // As of OS X 10.9, attempting to verify a certificate chain that 632 // contains a weak signature algorithm (MD2, MD5) in an intermediate 633 // or leaf cert will be treated as a (recoverable) policy validation 634 // failure, with the status code CSSMERR_TP_INVALID_CERTIFICATE 635 // added to the Status Codes. Don't treat this code as an invalid 636 // certificate; instead, map it to a weak key. Any truly invalid 637 // certificates will have the major error (cssm_result) set to 638 // CSSMERR_TP_INVALID_CERTIFICATE, rather than 639 // CSSMERR_TP_VERIFY_ACTION_FAILED. 640 CertStatus mapped_status = 0; 641 if (policy_failed && 642 chain_info[index].StatusCodes[status_code_index] == 643 CSSMERR_TP_INVALID_CERTIFICATE) { 644 mapped_status = CERT_STATUS_WEAK_SIGNATURE_ALGORITHM; 645 weak_key_or_signature_algorithm = true; 646 } else { 647 mapped_status = CertStatusFromOSStatus( 648 chain_info[index].StatusCodes[status_code_index]); 649 if (mapped_status == CERT_STATUS_WEAK_KEY) 650 weak_key_or_signature_algorithm = true; 651 } 652 verify_result->cert_status |= mapped_status; 653 } 654 } 655 if (policy_failed && !weak_key_or_signature_algorithm) { 656 // If CSSMERR_TP_VERIFY_ACTION_FAILED wasn't returned due to a weak 657 // key, map it back to an appropriate error code. 658 verify_result->cert_status |= CertStatusFromOSStatus(cssm_result); 659 } 660 if (!IsCertStatusError(verify_result->cert_status)) { 661 LOG(ERROR) << "cssm_result=" << cssm_result; 662 verify_result->cert_status |= CERT_STATUS_INVALID; 663 NOTREACHED(); 664 } 665 break; 666 667 default: 668 status = SecTrustGetCssmResultCode(trust_ref, &cssm_result); 669 if (status) 670 return NetErrorFromOSStatus(status); 671 verify_result->cert_status |= CertStatusFromOSStatus(cssm_result); 672 if (!IsCertStatusError(verify_result->cert_status)) { 673 LOG(WARNING) << "trust_result=" << trust_result; 674 verify_result->cert_status |= CERT_STATUS_INVALID; 675 } 676 break; 677 } 678 679 // Perform hostname verification independent of SecTrustEvaluate. In order to 680 // do so, mask off any reported name errors first. 681 verify_result->cert_status &= ~CERT_STATUS_COMMON_NAME_INVALID; 682 if (!cert->VerifyNameMatch(hostname, 683 &verify_result->common_name_fallback_used)) { 684 verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID; 685 } 686 687 // TODO(wtc): Suppress CERT_STATUS_NO_REVOCATION_MECHANISM for now to be 688 // compatible with Windows, which in turn implements this behavior to be 689 // compatible with WinHTTP, which doesn't report this error (bug 3004). 690 verify_result->cert_status &= ~CERT_STATUS_NO_REVOCATION_MECHANISM; 691 692 AppendPublicKeyHashes(completed_chain, &verify_result->public_key_hashes); 693 verify_result->is_issued_by_known_root = IsIssuedByKnownRoot(completed_chain); 694 695 if (IsCertStatusError(verify_result->cert_status)) 696 return MapCertStatusToNetError(verify_result->cert_status); 697 698 if (flags & CertVerifier::VERIFY_EV_CERT) { 699 // Determine the certificate's EV status using SecTrustCopyExtendedResult(), 700 // which is an internal/private API function added in OS X 10.5.7. 701 // Note: "ExtendedResult" means extended validation results. 702 CFBundleRef bundle = 703 CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security")); 704 if (bundle) { 705 SecTrustCopyExtendedResultFuncPtr copy_extended_result = 706 reinterpret_cast<SecTrustCopyExtendedResultFuncPtr>( 707 CFBundleGetFunctionPointerForName(bundle, 708 CFSTR("SecTrustCopyExtendedResult"))); 709 if (copy_extended_result) { 710 CFDictionaryRef ev_dict_temp = NULL; 711 status = copy_extended_result(trust_ref, &ev_dict_temp); 712 ScopedCFTypeRef<CFDictionaryRef> ev_dict(ev_dict_temp); 713 ev_dict_temp = NULL; 714 if (status == noErr && ev_dict) { 715 // In 10.7.3, SecTrustCopyExtendedResult returns noErr and populates 716 // ev_dict even for non-EV certificates, but only EV certificates 717 // will cause ev_dict to contain kSecEVOrganizationName. In previous 718 // releases, SecTrustCopyExtendedResult would only return noErr and 719 // populate ev_dict for EV certificates, but would always include 720 // kSecEVOrganizationName in that case, so checking for this key is 721 // appropriate for all known versions of SecTrustCopyExtendedResult. 722 // The actual organization name is unneeded here and can be accessed 723 // through other means. All that matters here is the OS' conception 724 // of whether or not the certificate is EV. 725 if (CFDictionaryContainsKey(ev_dict, 726 kSecEVOrganizationName)) { 727 verify_result->cert_status |= CERT_STATUS_IS_EV; 728 if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY) 729 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; 730 } 731 } 732 } 733 } 734 } 735 736 return OK; 737 } 738 739 } // namespace net 740