Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(72)

Side by Side Diff: net/cert/cert_verify_proc_mac.cc

Issue 2456523003: Mac EV verification using Chrome methods rather than OS methods. (Closed)
Patch Set: review changes Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/cert/cert_verify_proc_mac.h" 5 #include "net/cert/cert_verify_proc_mac.h"
6 6
7 #include <CommonCrypto/CommonDigest.h> 7 #include <CommonCrypto/CommonDigest.h>
8 #include <CoreServices/CoreServices.h> 8 #include <CoreServices/CoreServices.h>
9 #include <Security/Security.h> 9 #include <Security/Security.h>
10 10
11 #include <set> 11 #include <set>
12 #include <string> 12 #include <string>
13 #include <vector> 13 #include <vector>
14 14
15 #include "base/lazy_instance.h" 15 #include "base/lazy_instance.h"
16 #include "base/logging.h" 16 #include "base/logging.h"
17 #include "base/mac/mac_logging.h" 17 #include "base/mac/mac_logging.h"
18 #include "base/mac/mac_util.h"
18 #include "base/mac/scoped_cftyperef.h" 19 #include "base/mac/scoped_cftyperef.h"
19 #include "base/sha1.h" 20 #include "base/sha1.h"
20 #include "base/strings/string_piece.h" 21 #include "base/strings/string_piece.h"
21 #include "base/synchronization/lock.h" 22 #include "base/synchronization/lock.h"
22 #include "crypto/mac_security_services_lock.h" 23 #include "crypto/mac_security_services_lock.h"
23 #include "crypto/sha2.h" 24 #include "crypto/sha2.h"
24 #include "net/base/hash_value.h" 25 #include "net/base/hash_value.h"
25 #include "net/base/net_errors.h" 26 #include "net/base/net_errors.h"
26 #include "net/cert/asn1_util.h" 27 #include "net/cert/asn1_util.h"
27 #include "net/cert/cert_status_flags.h" 28 #include "net/cert/cert_status_flags.h"
28 #include "net/cert/cert_verifier.h" 29 #include "net/cert/cert_verifier.h"
29 #include "net/cert/cert_verify_result.h" 30 #include "net/cert/cert_verify_result.h"
30 #include "net/cert/crl_set.h" 31 #include "net/cert/crl_set.h"
32 #include "net/cert/ev_root_ca_metadata.h"
33 #include "net/cert/internal/certificate_policies.h"
34 #include "net/cert/internal/parsed_certificate.h"
31 #include "net/cert/test_keychain_search_list_mac.h" 35 #include "net/cert/test_keychain_search_list_mac.h"
32 #include "net/cert/test_root_certs.h" 36 #include "net/cert/test_root_certs.h"
33 #include "net/cert/x509_certificate.h" 37 #include "net/cert/x509_certificate.h"
34 #include "net/cert/x509_util_mac.h" 38 #include "net/cert/x509_util_mac.h"
35 39
36 // CSSM functions are deprecated as of OSX 10.7, but have no replacement. 40 // CSSM functions are deprecated as of OSX 10.7, but have no replacement.
37 // https://bugs.chromium.org/p/chromium/issues/detail?id=590914#c1 41 // https://bugs.chromium.org/p/chromium/issues/detail?id=590914#c1
38 #pragma clang diagnostic push 42 #pragma clang diagnostic push
39 #pragma clang diagnostic ignored "-Wdeprecated-declarations" 43 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
40 44
41 // From 10.7.2 libsecurity_keychain-55035/lib/SecTrustPriv.h, for use with
42 // SecTrustCopyExtendedResult.
43 #ifndef kSecEVOrganizationName
44 #define kSecEVOrganizationName CFSTR("Organization")
45 #endif
46
47 using base::ScopedCFTypeRef; 45 using base::ScopedCFTypeRef;
48 46
49 namespace net { 47 namespace net {
50 48
51 namespace { 49 namespace {
52 50
53 typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef, 51 typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef,
54 CFDictionaryRef*); 52 CFDictionaryRef*);
55 53
56 int NetErrorFromOSStatus(OSStatus status) { 54 int NetErrorFromOSStatus(OSStatus status) {
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
164 x509_util::CreateSSLServerPolicy(std::string(), &ssl_policy); 162 x509_util::CreateSSLServerPolicy(std::string(), &ssl_policy);
165 if (status) 163 if (status)
166 return status; 164 return status;
167 CFArrayAppendValue(local_policies, ssl_policy); 165 CFArrayAppendValue(local_policies, ssl_policy);
168 CFRelease(ssl_policy); 166 CFRelease(ssl_policy);
169 167
170 // Explicitly add revocation policies, in order to override system 168 // Explicitly add revocation policies, in order to override system
171 // revocation checking policies and instead respect the application-level 169 // revocation checking policies and instead respect the application-level
172 // revocation preference. 170 // revocation preference.
173 status = x509_util::CreateRevocationPolicies( 171 status = x509_util::CreateRevocationPolicies(
174 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED), 172 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED), local_policies);
175 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY),
176 local_policies);
177 if (status) 173 if (status)
178 return status; 174 return status;
179 175
180 policies->reset(local_policies.release()); 176 policies->reset(local_policies.release());
181 return noErr; 177 return noErr;
182 } 178 }
183 179
184 // Stores the constructed certificate chain |cert_chain| and information about 180 // Stores the constructed certificate chain |cert_chain| and information about
185 // the signature algorithms used into |*verify_result|. If the leaf cert in 181 // the signature algorithms used into |*verify_result|. If the leaf cert in
186 // |cert_chain| contains a weak (MD2, MD4, MD5, SHA-1) signature, stores that 182 // |cert_chain| contains a weak (MD2, MD4, MD5, SHA-1) signature, stores that
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
265 } 261 }
266 if (!verified_cert) { 262 if (!verified_cert) {
267 NOTREACHED(); 263 NOTREACHED();
268 return; 264 return;
269 } 265 }
270 266
271 verify_result->verified_cert = 267 verify_result->verified_cert =
272 X509Certificate::CreateFromHandle(verified_cert, verified_chain); 268 X509Certificate::CreateFromHandle(verified_cert, verified_chain);
273 } 269 }
274 270
271 using ExtensionsMap = std::map<net::der::Input, net::ParsedExtension>;
272
273 // Helper that looks up an extension by OID given a map of extensions.
274 bool GetExtensionValue(const ExtensionsMap& extensions,
275 const net::der::Input& oid,
276 net::der::Input* value) {
277 auto it = extensions.find(oid);
278 if (it == extensions.end())
279 return false;
280 *value = it->second.value;
281 return true;
282 }
283
284 // Checks if |*cert| has a Certificate Policies extension containing either
285 // of |ev_policy_oid| or anyPolicy.
286 bool HasPolicyOrAnyPolicy(const ParsedCertificate* cert,
287 const der::Input& ev_policy_oid) {
288 der::Input extension_value;
289 if (!GetExtensionValue(cert->unparsed_extensions(), CertificatePoliciesOid(),
290 &extension_value))
291 return false;
292
293 std::vector<der::Input> policies;
294 if (!ParseCertificatePoliciesExtension(extension_value, &policies))
295 return false;
296
297 for (const der::Input& policy_oid : policies) {
298 if (policy_oid == ev_policy_oid || policy_oid == AnyPolicy())
299 return true;
300 }
301 return false;
302 }
303
304 // Looks for known EV policy OIDs in |cert_input|, if one is found it will be
305 // stored in |*ev_policy_oid| as a DER-encoded OID value (no tag or length).
306 void GetCandidateEVPolicy(const X509Certificate* cert_input,
307 std::string* ev_policy_oid) {
308 *ev_policy_oid = std::string();
Ryan Sleevi 2016/11/08 23:54:09 ev_policy_oid->clear(); is slightly more efficient
mattm 2016/11/10 05:40:40 Done.
309
310 std::string der_cert;
311 if (!X509Certificate::GetDEREncoded(cert_input->os_cert_handle(),
312 &der_cert)) {
313 return;
314 }
315
316 scoped_refptr<ParsedCertificate> cert(
317 ParsedCertificate::Create(der_cert, {}, nullptr));
318 if (!cert)
319 return;
320
321 der::Input extension_value;
322 if (!GetExtensionValue(cert->unparsed_extensions(), CertificatePoliciesOid(),
323 &extension_value)) {
Ryan Sleevi 2016/11/08 23:54:09 Since you braced here, should you brace 289-291 fo
mattm 2016/11/10 05:40:40 Done.
324 return;
325 }
326
327 std::vector<der::Input> policies;
328 if (!ParseCertificatePoliciesExtension(extension_value, &policies))
329 return;
330
331 EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance();
332 for (const der::Input& policy_oid : policies) {
333 if (metadata->IsEVPolicyOID(policy_oid)) {
334 *ev_policy_oid = policy_oid.AsString();
335 return;
336 }
337 }
338 }
339
340 // Checks that the certificate chain of |cert| has policies consistent with
341 // |ev_policy_oid_string|. The leaf is not checked, as it is assumed that is
342 // where the policy came from.
343 bool CheckCertChainEV(const X509Certificate* cert,
344 const std::string& ev_policy_oid_string) {
345 der::Input ev_policy_oid(&ev_policy_oid_string);
346 X509Certificate::OSCertHandles os_cert_chain =
347 cert->GetIntermediateCertificates();
348
349 // Intermediates should have Certificate Policies extension with the EV policy
350 // or AnyPolicy.
351 for (size_t i = 0; i < os_cert_chain.size() - 1; ++i) {
352 std::string der_cert;
353 if (!X509Certificate::GetDEREncoded(os_cert_chain[i], &der_cert))
354 return false;
355 scoped_refptr<ParsedCertificate> intermediate_cert(
356 ParsedCertificate::Create(der_cert, {}, nullptr));
357 if (!intermediate_cert)
358 return false;
359 if (!HasPolicyOrAnyPolicy(intermediate_cert.get(), ev_policy_oid))
360 return false;
361 }
362
363 // Root should have matching policy in EVRootCAMetadata.
364 std::string der_cert;
365 if (!X509Certificate::GetDEREncoded(os_cert_chain.back(), &der_cert))
366 return false;
367 SHA1HashValue weak_fingerprint;
368 base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(der_cert.data()),
369 der_cert.size(), weak_fingerprint.data);
370 EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance();
371 return metadata->HasEVPolicyOID(weak_fingerprint, ev_policy_oid);
Ryan Sleevi 2016/11/08 23:54:09 It seems like this check would be faster/a quicker
mattm 2016/11/10 05:40:40 Done.
372 }
373
275 void AppendPublicKeyHashes(CFArrayRef chain, 374 void AppendPublicKeyHashes(CFArrayRef chain,
276 HashValueVector* hashes) { 375 HashValueVector* hashes) {
277 const CFIndex n = CFArrayGetCount(chain); 376 const CFIndex n = CFArrayGetCount(chain);
278 for (CFIndex i = 0; i < n; i++) { 377 for (CFIndex i = 0; i < n; i++) {
279 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>( 378 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
280 const_cast<void*>(CFArrayGetValueAtIndex(chain, i))); 379 const_cast<void*>(CFArrayGetValueAtIndex(chain, i)));
281 380
282 CSSM_DATA cert_data; 381 CSSM_DATA cert_data;
283 OSStatus err = SecCertificateGetData(cert, &cert_data); 382 OSStatus err = SecCertificateGetData(cert, &cert_data);
284 DCHECK_EQ(err, noErr); 383 DCHECK_EQ(err, noErr);
285 base::StringPiece der_bytes(reinterpret_cast<const char*>(cert_data.Data), 384 base::StringPiece der_bytes(reinterpret_cast<const char*>(cert_data.Data),
286 cert_data.Length); 385 cert_data.Length);
287 base::StringPiece spki_bytes; 386 base::StringPiece spki_bytes;
288 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki_bytes)) 387 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki_bytes))
289 continue; 388 continue;
290 389
291 HashValue sha1(HASH_VALUE_SHA1); 390 HashValue sha1(HASH_VALUE_SHA1);
292 CC_SHA1(spki_bytes.data(), spki_bytes.size(), sha1.data()); 391 CC_SHA1(spki_bytes.data(), spki_bytes.size(), sha1.data());
293 hashes->push_back(sha1); 392 hashes->push_back(sha1);
294 393
295 HashValue sha256(HASH_VALUE_SHA256); 394 HashValue sha256(HASH_VALUE_SHA256);
296 CC_SHA256(spki_bytes.data(), spki_bytes.size(), sha256.data()); 395 CC_SHA256(spki_bytes.data(), spki_bytes.size(), sha256.data());
297 hashes->push_back(sha256); 396 hashes->push_back(sha256);
298 } 397 }
299 } 398 }
300 399
301 bool CheckRevocationWithCRLSet(CFArrayRef chain, CRLSet* crl_set) { 400 enum CRLSetResult {
401 kCRLSetOk,
402 kCRLSetRevoked,
403 kCRLSetUnknown,
404 };
405
406 // CheckRevocationWithCRLSet attempts to check each element of |cert_list|
407 // against |crl_set|. It returns:
408 // kCRLSetRevoked: if any element of the chain is known to have been revoked.
409 // kCRLSetUnknown: if there is no fresh information about the leaf
410 // certificate in the chain or if the CRLSet has expired.
411 //
412 // Only the leaf certificate is considered for coverage because some
413 // intermediates have CRLs with no revocations (after filtering) and
414 // those CRLs are pruned from the CRLSet at generation time. This means
415 // that some EV sites would otherwise take the hit of an OCSP lookup for
416 // no reason.
417 // kCRLSetOk: otherwise.
418 CRLSetResult CheckRevocationWithCRLSet(CFArrayRef chain, CRLSet* crl_set) {
302 if (CFArrayGetCount(chain) == 0) 419 if (CFArrayGetCount(chain) == 0)
303 return true; 420 return kCRLSetOk;
421
422 // error is set to true if any errors are found. It causes such chains to be
423 // considered as not covered.
424 bool error = false;
425 // last_covered is set to the coverage state of the previous certificate. The
426 // certificates are iterated over backwards thus, after the iteration,
427 // |last_covered| contains the coverage state of the leaf certificate.
Ryan Sleevi 2016/11/08 23:54:09 Inconsistency with respect to || vs no ||. I'd dro
mattm 2016/11/10 05:40:41 Done.
428 bool last_covered = false;
304 429
305 // We iterate from the root certificate down to the leaf, keeping track of 430 // We iterate from the root certificate down to the leaf, keeping track of
306 // the issuer's SPKI at each step. 431 // the issuer's SPKI at each step.
307 std::string issuer_spki_hash; 432 std::string issuer_spki_hash;
308 for (CFIndex i = CFArrayGetCount(chain) - 1; i >= 0; i--) { 433 for (CFIndex i = CFArrayGetCount(chain) - 1; i >= 0; i--) {
309 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>( 434 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
310 const_cast<void*>(CFArrayGetValueAtIndex(chain, i))); 435 const_cast<void*>(CFArrayGetValueAtIndex(chain, i)));
311 436
312 CSSM_DATA cert_data; 437 CSSM_DATA cert_data;
313 OSStatus err = SecCertificateGetData(cert, &cert_data); 438 OSStatus err = SecCertificateGetData(cert, &cert_data);
314 if (err != noErr) { 439 if (err != noErr) {
315 NOTREACHED(); 440 NOTREACHED();
441 error = true;
316 continue; 442 continue;
317 } 443 }
318 base::StringPiece der_bytes(reinterpret_cast<const char*>(cert_data.Data), 444 base::StringPiece der_bytes(reinterpret_cast<const char*>(cert_data.Data),
319 cert_data.Length); 445 cert_data.Length);
320 base::StringPiece spki; 446 base::StringPiece spki;
321 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) { 447 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) {
322 NOTREACHED(); 448 NOTREACHED();
449 error = true;
323 continue; 450 continue;
324 } 451 }
325 452
326 const std::string spki_hash = crypto::SHA256HashString(spki); 453 const std::string spki_hash = crypto::SHA256HashString(spki);
327 x509_util::CSSMCachedCertificate cached_cert; 454 x509_util::CSSMCachedCertificate cached_cert;
328 if (cached_cert.Init(cert) != CSSM_OK) { 455 if (cached_cert.Init(cert) != CSSM_OK) {
329 NOTREACHED(); 456 NOTREACHED();
457 error = true;
330 continue; 458 continue;
331 } 459 }
332 x509_util::CSSMFieldValue serial_number; 460 x509_util::CSSMFieldValue serial_number;
333 err = cached_cert.GetField(&CSSMOID_X509V1SerialNumber, &serial_number); 461 err = cached_cert.GetField(&CSSMOID_X509V1SerialNumber, &serial_number);
334 if (err || !serial_number.field()) { 462 if (err || !serial_number.field()) {
335 NOTREACHED(); 463 NOTREACHED();
464 error = true;
336 continue; 465 continue;
337 } 466 }
338 467
339 base::StringPiece serial( 468 base::StringPiece serial(
340 reinterpret_cast<const char*>(serial_number.field()->Data), 469 reinterpret_cast<const char*>(serial_number.field()->Data),
341 serial_number.field()->Length); 470 serial_number.field()->Length);
342 471
343 CRLSet::Result result = crl_set->CheckSPKI(spki_hash); 472 CRLSet::Result result = crl_set->CheckSPKI(spki_hash);
344 473
345 if (result != CRLSet::REVOKED && !issuer_spki_hash.empty()) 474 if (result != CRLSet::REVOKED && !issuer_spki_hash.empty())
346 result = crl_set->CheckSerial(serial, issuer_spki_hash); 475 result = crl_set->CheckSerial(serial, issuer_spki_hash);
347 476
348 issuer_spki_hash = spki_hash; 477 issuer_spki_hash = spki_hash;
349 478
350 switch (result) { 479 switch (result) {
351 case CRLSet::REVOKED: 480 case CRLSet::REVOKED:
352 return false; 481 return kCRLSetRevoked;
353 case CRLSet::UNKNOWN: 482 case CRLSet::UNKNOWN:
483 last_covered = false;
484 continue;
354 case CRLSet::GOOD: 485 case CRLSet::GOOD:
486 last_covered = true;
355 continue; 487 continue;
356 default: 488 default:
357 NOTREACHED(); 489 NOTREACHED();
358 return false; 490 error = true;
491 continue;
359 } 492 }
360 } 493 }
361 494
362 return true; 495 if (error || !last_covered || crl_set->IsExpired())
496 return kCRLSetUnknown;
497 return kCRLSetOk;
363 } 498 }
364 499
365 // Builds and evaluates a SecTrustRef for the certificate chain contained 500 // Builds and evaluates a SecTrustRef for the certificate chain contained
366 // in |cert_array|, using the verification policies in |trust_policies|. On 501 // in |cert_array|, using the verification policies in |trust_policies|. On
367 // success, returns OK, and updates |trust_ref|, |trust_result|, 502 // success, returns OK, and updates |trust_ref|, |trust_result|,
368 // |verified_chain|, and |chain_info| with the verification results. On 503 // |verified_chain|, and |chain_info| with the verification results. On
369 // failure, no output parameters are modified. 504 // failure, no output parameters are modified.
370 // 505 //
371 // Note: An OK return does not mean that |cert_array| is trusted, merely that 506 // Note: An OK return does not mean that |cert_array| is trusted, merely that
372 // verification was performed successfully. 507 // verification was performed successfully.
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after
514 } 649 }
515 650
516 ~OSXKnownRootHelper() {} 651 ~OSXKnownRootHelper() {}
517 652
518 std::set<SHA256HashValue, SHA256HashValueLessThan> known_roots_; 653 std::set<SHA256HashValue, SHA256HashValueLessThan> known_roots_;
519 }; 654 };
520 655
521 base::LazyInstance<OSXKnownRootHelper>::Leaky g_known_roots = 656 base::LazyInstance<OSXKnownRootHelper>::Leaky g_known_roots =
522 LAZY_INSTANCE_INITIALIZER; 657 LAZY_INSTANCE_INITIALIZER;
523 658
524 } // namespace 659 // Runs path building & verification loop for |cert|, given |flags|. This is
525 660 // split into a separate function so verification can be repeated with different
526 CertVerifyProcMac::CertVerifyProcMac() {} 661 // flags. This function does not handle EV.
527 662 int VerifyWithGivenFlags(X509Certificate* cert,
528 CertVerifyProcMac::~CertVerifyProcMac() {} 663 const std::string& hostname,
529 664 const int flags,
530 bool CertVerifyProcMac::SupportsAdditionalTrustAnchors() const { 665 CRLSet* crl_set,
531 return false; 666 CertVerifyResult* verify_result,
532 } 667 CRLSetResult* completed_chain_crl_result) {
533
534 bool CertVerifyProcMac::SupportsOCSPStapling() const {
535 // TODO(rsleevi): Plumb an OCSP response into the Mac system library.
536 // https://crbug.com/430714
537 return false;
538 }
539
540 int CertVerifyProcMac::VerifyInternal(
541 X509Certificate* cert,
542 const std::string& hostname,
543 const std::string& ocsp_response,
544 int flags,
545 CRLSet* crl_set,
546 const CertificateList& additional_trust_anchors,
547 CertVerifyResult* verify_result) {
548 ScopedCFTypeRef<CFArrayRef> trust_policies; 668 ScopedCFTypeRef<CFArrayRef> trust_policies;
549 OSStatus status = CreateTrustPolicies(flags, &trust_policies); 669 OSStatus status = CreateTrustPolicies(flags, &trust_policies);
550 if (status) 670 if (status)
551 return NetErrorFromOSStatus(status); 671 return NetErrorFromOSStatus(status);
552 672
673 *completed_chain_crl_result = kCRLSetUnknown;
674
553 // Serialize all calls that may use the Keychain, to work around various 675 // Serialize all calls that may use the Keychain, to work around various
554 // issues in OS X 10.6+ with multi-threaded access to Security.framework. 676 // issues in OS X 10.6+ with multi-threaded access to Security.framework.
555 base::AutoLock lock(crypto::GetMacSecurityServicesLock()); 677 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
556 678
557 ScopedCFTypeRef<SecTrustRef> trust_ref; 679 ScopedCFTypeRef<SecTrustRef> trust_ref;
558 SecTrustResultType trust_result = kSecTrustResultDeny; 680 SecTrustResultType trust_result = kSecTrustResultDeny;
559 ScopedCFTypeRef<CFArrayRef> completed_chain; 681 ScopedCFTypeRef<CFArrayRef> completed_chain;
560 CSSM_TP_APPLE_EVIDENCE_INFO* chain_info = NULL; 682 CSSM_TP_APPLE_EVIDENCE_INFO* chain_info = NULL;
561 bool candidate_untrusted = true; 683 bool candidate_untrusted = true;
562 bool candidate_weak = false; 684 bool candidate_weak = false;
563 bool completed_chain_revoked = false;
564 685
565 // OS X lacks proper path discovery; it will take the input certs and never 686 // OS X lacks proper path discovery; it will take the input certs and never
566 // backtrack the graph attempting to discover valid paths. 687 // backtrack the graph attempting to discover valid paths.
567 // This can create issues in some situations: 688 // This can create issues in some situations:
568 // - When OS X changes the trust store, there may be a chain 689 // - When OS X changes the trust store, there may be a chain
569 // A -> B -> C -> D 690 // A -> B -> C -> D
570 // where OS X trusts D (on some versions) and trusts C (on some versions). 691 // where OS X trusts D (on some versions) and trusts C (on some versions).
571 // If a server supplies a chain A, B, C (cross-signed by D), then this chain 692 // If a server supplies a chain A, B, C (cross-signed by D), then this chain
572 // will successfully validate on systems that trust D, but fail for systems 693 // will successfully validate on systems that trust D, but fail for systems
573 // that trust C. If the server supplies a chain of A -> B, then it forces 694 // that trust C. If the server supplies a chain of A -> B, then it forces
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
697 // The CRLSet checking is performed inside the loop in the hope that if a 818 // The CRLSet checking is performed inside the loop in the hope that if a
698 // path is revoked, it's an older path, and the only reason it was built 819 // path is revoked, it's an older path, and the only reason it was built
699 // is because the server forced it (by supplying an older or less 820 // is because the server forced it (by supplying an older or less
700 // desirable intermediate) or because the user had installed a 821 // desirable intermediate) or because the user had installed a
701 // certificate in their Keychain forcing this path. However, this means 822 // certificate in their Keychain forcing this path. However, this means
702 // its still possible for a CRLSet block of an intermediate to prevent 823 // its still possible for a CRLSet block of an intermediate to prevent
703 // access, even when there is a 'good' chain. To fully remedy this, a 824 // access, even when there is a 'good' chain. To fully remedy this, a
704 // solution might be to have CRLSets contain enough knowledge about what 825 // solution might be to have CRLSets contain enough knowledge about what
705 // the 'desired' path might be, but for the time being, the 826 // the 'desired' path might be, but for the time being, the
706 // implementation is kept as 'simple' as it can be. 827 // implementation is kept as 'simple' as it can be.
707 bool revoked = 828 CRLSetResult crl_result = kCRLSetUnknown;
708 (crl_set && !CheckRevocationWithCRLSet(temp_chain, crl_set)); 829 if (crl_set)
830 crl_result = CheckRevocationWithCRLSet(temp_chain, crl_set);
709 bool untrusted = (temp_trust_result != kSecTrustResultUnspecified && 831 bool untrusted = (temp_trust_result != kSecTrustResultUnspecified &&
710 temp_trust_result != kSecTrustResultProceed) || 832 temp_trust_result != kSecTrustResultProceed) ||
711 revoked; 833 crl_result == kCRLSetRevoked;
712 bool weak_chain = false; 834 bool weak_chain = false;
713 if (CFArrayGetCount(temp_chain) == 0) { 835 if (CFArrayGetCount(temp_chain) == 0) {
714 // If the chain is empty, it cannot be trusted or have recoverable 836 // If the chain is empty, it cannot be trusted or have recoverable
715 // errors. 837 // errors.
716 DCHECK(untrusted); 838 DCHECK(untrusted);
717 DCHECK_NE(kSecTrustResultRecoverableTrustFailure, temp_trust_result); 839 DCHECK_NE(kSecTrustResultRecoverableTrustFailure, temp_trust_result);
718 } else { 840 } else {
719 CertVerifyResult temp_verify_result; 841 CertVerifyResult temp_verify_result;
720 bool leaf_is_weak = false; 842 bool leaf_is_weak = false;
721 GetCertChainInfo(temp_chain, temp_chain_info, &temp_verify_result, 843 GetCertChainInfo(temp_chain, temp_chain_info, &temp_verify_result,
(...skipping 16 matching lines...) Expand all
738 // old chain. 860 // old chain.
739 // 861 //
740 // Note: If the leaf certificate itself is weak, then the only 862 // Note: If the leaf certificate itself is weak, then the only
741 // consideration is whether or not there is a trusted chain. That's 863 // consideration is whether or not there is a trusted chain. That's
742 // because no amount of path discovery will fix a weak leaf. 864 // because no amount of path discovery will fix a weak leaf.
743 if (!trust_ref || (!untrusted && (candidate_untrusted || 865 if (!trust_ref || (!untrusted && (candidate_untrusted ||
744 (candidate_weak && !weak_chain)))) { 866 (candidate_weak && !weak_chain)))) {
745 trust_ref = temp_ref; 867 trust_ref = temp_ref;
746 trust_result = temp_trust_result; 868 trust_result = temp_trust_result;
747 completed_chain = temp_chain; 869 completed_chain = temp_chain;
748 completed_chain_revoked = revoked; 870 *completed_chain_crl_result = crl_result;
749 chain_info = temp_chain_info; 871 chain_info = temp_chain_info;
750 872
751 candidate_untrusted = untrusted; 873 candidate_untrusted = untrusted;
752 candidate_weak = weak_chain; 874 candidate_weak = weak_chain;
753 } 875 }
754 // Short-circuit when a current, trusted chain is found. 876 // Short-circuit when a current, trusted chain is found.
755 if (!untrusted && !weak_chain) 877 if (!untrusted && !weak_chain)
756 break; 878 break;
757 CFArrayRemoveValueAtIndex(cert_array, CFArrayGetCount(cert_array) - 1); 879 CFArrayRemoveValueAtIndex(cert_array, CFArrayGetCount(cert_array) - 1);
758 } 880 }
759 // Short-circuit when a current, trusted chain is found. 881 // Short-circuit when a current, trusted chain is found.
760 if (!candidate_untrusted && !candidate_weak) 882 if (!candidate_untrusted && !candidate_weak)
761 break; 883 break;
762 } 884 }
763 885
764 if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED) 886 if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED)
765 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; 887 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
766 888
767 if (completed_chain_revoked) 889 if (*completed_chain_crl_result == kCRLSetRevoked)
768 verify_result->cert_status |= CERT_STATUS_REVOKED; 890 verify_result->cert_status |= CERT_STATUS_REVOKED;
769 891
770 if (CFArrayGetCount(completed_chain) > 0) { 892 if (CFArrayGetCount(completed_chain) > 0) {
771 bool leaf_is_weak_unused = false; 893 bool leaf_is_weak_unused = false;
772 GetCertChainInfo(completed_chain, chain_info, verify_result, 894 GetCertChainInfo(completed_chain, chain_info, verify_result,
773 &leaf_is_weak_unused); 895 &leaf_is_weak_unused);
774 } 896 }
775 897
776 // As of Security Update 2012-002/OS X 10.7.4, when an RSA key < 1024 bits 898 // As of Security Update 2012-002/OS X 10.7.4, when an RSA key < 1024 bits
777 // is encountered, CSSM returns CSSMERR_TP_VERIFY_ACTION_FAILED and adds 899 // is encountered, CSSM returns CSSMERR_TP_VERIFY_ACTION_FAILED and adds
778 // CSSMERR_CSP_UNSUPPORTED_KEY_SIZE as a certificate status. Avoid mapping 900 // CSSMERR_CSP_UNSUPPORTED_KEY_SIZE as a certificate status. Avoid mapping
779 // the CSSMERR_TP_VERIFY_ACTION_FAILED to CERT_STATUS_INVALID if the only 901 // the CSSMERR_TP_VERIFY_ACTION_FAILED to CERT_STATUS_INVALID if the only
780 // error was due to an unsupported key size. 902 // error was due to an unsupported key size.
781 bool policy_failed = false; 903 bool policy_failed = false;
904 bool policy_fail_already_mapped = false;
782 bool weak_key_or_signature_algorithm = false; 905 bool weak_key_or_signature_algorithm = false;
783 906
784 // Evaluate the results 907 // Evaluate the results
785 OSStatus cssm_result; 908 OSStatus cssm_result;
786 switch (trust_result) { 909 switch (trust_result) {
787 case kSecTrustResultUnspecified: 910 case kSecTrustResultUnspecified:
788 case kSecTrustResultProceed: 911 case kSecTrustResultProceed:
789 // Certificate chain is valid and trusted ("unspecified" indicates that 912 // Certificate chain is valid and trusted ("unspecified" indicates that
790 // the user has not explicitly set a trust setting) 913 // the user has not explicitly set a trust setting)
791 break; 914 break;
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
829 // failure, with the status code CSSMERR_TP_INVALID_CERTIFICATE 952 // failure, with the status code CSSMERR_TP_INVALID_CERTIFICATE
830 // added to the Status Codes. Don't treat this code as an invalid 953 // added to the Status Codes. Don't treat this code as an invalid
831 // certificate; instead, map it to a weak key. Any truly invalid 954 // certificate; instead, map it to a weak key. Any truly invalid
832 // certificates will have the major error (cssm_result) set to 955 // certificates will have the major error (cssm_result) set to
833 // CSSMERR_TP_INVALID_CERTIFICATE, rather than 956 // CSSMERR_TP_INVALID_CERTIFICATE, rather than
834 // CSSMERR_TP_VERIFY_ACTION_FAILED. 957 // CSSMERR_TP_VERIFY_ACTION_FAILED.
835 CertStatus mapped_status = 0; 958 CertStatus mapped_status = 0;
836 if (policy_failed && 959 if (policy_failed &&
837 chain_info[index].StatusCodes[status_code_index] == 960 chain_info[index].StatusCodes[status_code_index] ==
838 CSSMERR_TP_INVALID_CERTIFICATE) { 961 CSSMERR_TP_INVALID_CERTIFICATE) {
839 mapped_status = CERT_STATUS_WEAK_SIGNATURE_ALGORITHM; 962 mapped_status = CERT_STATUS_WEAK_SIGNATURE_ALGORITHM;
963 weak_key_or_signature_algorithm = true;
964 policy_fail_already_mapped = true;
965 } else if (policy_failed &&
966 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED) &&
967 chain_info[index].StatusCodes[status_code_index] ==
968 CSSMERR_TP_VERIFY_ACTION_FAILED &&
969 base::mac::IsAtLeastOS10_12()) {
970 // On 10.12, using kSecRevocationRequirePositiveResponse flag
971 // causes a CSSMERR_TP_VERIFY_ACTION_FAILED status if revocation
972 // couldn't be checked. (Note: even if the cert had no
973 // crlDistributionPoints or OCSP AIA.)
974 mapped_status = CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
975 policy_fail_already_mapped = true;
976 } else {
977 mapped_status = CertStatusFromOSStatus(
978 chain_info[index].StatusCodes[status_code_index]);
979 if (mapped_status == CERT_STATUS_WEAK_KEY) {
840 weak_key_or_signature_algorithm = true; 980 weak_key_or_signature_algorithm = true;
841 } else { 981 policy_fail_already_mapped = true;
842 mapped_status = CertStatusFromOSStatus( 982 }
843 chain_info[index].StatusCodes[status_code_index]);
844 if (mapped_status == CERT_STATUS_WEAK_KEY)
845 weak_key_or_signature_algorithm = true;
846 } 983 }
847 verify_result->cert_status |= mapped_status; 984 verify_result->cert_status |= mapped_status;
848 } 985 }
849 } 986 }
850 if (policy_failed && !weak_key_or_signature_algorithm) { 987 if (policy_failed && !policy_fail_already_mapped) {
851 // If CSSMERR_TP_VERIFY_ACTION_FAILED wasn't returned due to a weak 988 // If CSSMERR_TP_VERIFY_ACTION_FAILED wasn't returned due to a weak
852 // key, map it back to an appropriate error code. 989 // key or problem checking revocation, map it back to an appropriate
990 // error code.
853 verify_result->cert_status |= CertStatusFromOSStatus(cssm_result); 991 verify_result->cert_status |= CertStatusFromOSStatus(cssm_result);
854 } 992 }
855 if (!IsCertStatusError(verify_result->cert_status)) { 993 if (!IsCertStatusError(verify_result->cert_status)) {
856 LOG(ERROR) << "cssm_result=" << cssm_result; 994 LOG(ERROR) << "cssm_result=" << cssm_result;
857 verify_result->cert_status |= CERT_STATUS_INVALID; 995 verify_result->cert_status |= CERT_STATUS_INVALID;
858 NOTREACHED(); 996 NOTREACHED();
859 } 997 }
860 break; 998 break;
861 999
862 default: 1000 default:
(...skipping 21 matching lines...) Expand all
884 // compatible with WinHTTP, which doesn't report this error (bug 3004). 1022 // compatible with WinHTTP, which doesn't report this error (bug 3004).
885 verify_result->cert_status &= ~CERT_STATUS_NO_REVOCATION_MECHANISM; 1023 verify_result->cert_status &= ~CERT_STATUS_NO_REVOCATION_MECHANISM;
886 1024
887 AppendPublicKeyHashes(completed_chain, &verify_result->public_key_hashes); 1025 AppendPublicKeyHashes(completed_chain, &verify_result->public_key_hashes);
888 verify_result->is_issued_by_known_root = 1026 verify_result->is_issued_by_known_root =
889 g_known_roots.Get().IsIssuedByKnownRoot(completed_chain); 1027 g_known_roots.Get().IsIssuedByKnownRoot(completed_chain);
890 1028
891 if (IsCertStatusError(verify_result->cert_status)) 1029 if (IsCertStatusError(verify_result->cert_status))
892 return MapCertStatusToNetError(verify_result->cert_status); 1030 return MapCertStatusToNetError(verify_result->cert_status);
893 1031
894 if (flags & CertVerifier::VERIFY_EV_CERT) { 1032 return OK;
895 // Determine the certificate's EV status using SecTrustCopyExtendedResult(), 1033 }
896 // which is an internal/private API function added in OS X 10.5.7. 1034
897 // Note: "ExtendedResult" means extended validation results. 1035 } // namespace
898 CFBundleRef bundle = 1036
899 CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security")); 1037 CertVerifyProcMac::CertVerifyProcMac() {}
900 if (bundle) { 1038
901 SecTrustCopyExtendedResultFuncPtr copy_extended_result = 1039 CertVerifyProcMac::~CertVerifyProcMac() {}
902 reinterpret_cast<SecTrustCopyExtendedResultFuncPtr>( 1040
903 CFBundleGetFunctionPointerForName(bundle, 1041 bool CertVerifyProcMac::SupportsAdditionalTrustAnchors() const {
904 CFSTR("SecTrustCopyExtendedResult"))); 1042 return false;
905 if (copy_extended_result) { 1043 }
906 CFDictionaryRef ev_dict_temp = NULL; 1044
907 status = copy_extended_result(trust_ref, &ev_dict_temp); 1045 bool CertVerifyProcMac::SupportsOCSPStapling() const {
908 ScopedCFTypeRef<CFDictionaryRef> ev_dict(ev_dict_temp); 1046 // TODO(rsleevi): Plumb an OCSP response into the Mac system library.
909 ev_dict_temp = NULL; 1047 // https://crbug.com/430714
910 if (status == noErr && ev_dict) { 1048 return false;
911 // In 10.7.3, SecTrustCopyExtendedResult returns noErr and populates 1049 }
912 // ev_dict even for non-EV certificates, but only EV certificates 1050
913 // will cause ev_dict to contain kSecEVOrganizationName. In previous 1051 int CertVerifyProcMac::VerifyInternal(
914 // releases, SecTrustCopyExtendedResult would only return noErr and 1052 X509Certificate* cert,
915 // populate ev_dict for EV certificates, but would always include 1053 const std::string& hostname,
916 // kSecEVOrganizationName in that case, so checking for this key is 1054 const std::string& ocsp_response,
917 // appropriate for all known versions of SecTrustCopyExtendedResult. 1055 int flags,
918 // The actual organization name is unneeded here and can be accessed 1056 CRLSet* crl_set,
919 // through other means. All that matters here is the OS' conception 1057 const CertificateList& additional_trust_anchors,
920 // of whether or not the certificate is EV. 1058 CertVerifyResult* verify_result) {
921 if (CFDictionaryContainsKey(ev_dict, 1059 // Save the input state of |*verify_result|, which may be needed to re-do
922 kSecEVOrganizationName)) { 1060 // verification with different flags.
923 verify_result->cert_status |= CERT_STATUS_IS_EV; 1061 const CertVerifyResult input_verify_result(*verify_result);
924 if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY) 1062
925 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; 1063 // If EV verification is enabled, check for EV policy in leaf cert.
926 } 1064 std::string candidate_ev_policy_oid;
927 } 1065 if (flags & CertVerifier::VERIFY_EV_CERT)
928 } 1066 GetCandidateEVPolicy(cert, &candidate_ev_policy_oid);
1067
1068 CRLSetResult completed_chain_crl_result;
1069 int rv = VerifyWithGivenFlags(cert, hostname, flags, crl_set, verify_result,
1070 &completed_chain_crl_result);
1071 if (rv != OK)
1072 return rv;
1073
1074 if (!candidate_ev_policy_oid.empty() &&
1075 CheckCertChainEV(verify_result->verified_cert.get(),
1076 candidate_ev_policy_oid)) {
1077 // EV policies check out and the verification succeeded. See if revocation
1078 // checking still needs to be done before it can be marked as EV.
1079 if (completed_chain_crl_result == kCRLSetUnknown &&
1080 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY) &&
1081 !(flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED)) {
1082 // If this is an EV cert and it wasn't covered by CRLSets and revocation
1083 // checking wasn't already on, try again with revocation forced on.
1084 //
1085 // Restore the input state of |*verify_result|, so that the
1086 // re-verification starts with a clean slate.
1087 *verify_result = input_verify_result;
1088 int tmp_rv = VerifyWithGivenFlags(
1089 verify_result->verified_cert.get(), hostname,
1090 flags | CertVerifier::VERIFY_REV_CHECKING_ENABLED, crl_set,
1091 verify_result, &completed_chain_crl_result);
1092 // If re-verification failed, return those results without setting EV
1093 // status.
1094 if (tmp_rv != OK)
1095 return tmp_rv;
1096 // Otherwise, fall through and add the EV status flag.
929 } 1097 }
1098 // EV cert and it was covered by CRLSets or revocation checking passed.
1099 verify_result->cert_status |= CERT_STATUS_IS_EV;
930 } 1100 }
931 1101
932 return OK; 1102 return OK;
933 } 1103 }
934 1104
935 } // namespace net 1105 } // namespace net
936 1106
937 #pragma clang diagnostic pop // "-Wdeprecated-declarations" 1107 #pragma clang diagnostic pop // "-Wdeprecated-declarations"
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698