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

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

Issue 1557133002: Perform CRLSet evaluation during Path Building on Windows (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 11 months 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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_win.h" 5 #include "net/cert/cert_verify_proc_win.h"
6 6
7 #include <string> 7 #include <string>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/memory/scoped_ptr.h" 10 #include "base/memory/scoped_ptr.h"
11 #include "base/sha1.h" 11 #include "base/sha1.h"
12 #include "base/strings/string_util.h" 12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h" 13 #include "base/strings/utf_string_conversions.h"
14 #include "base/threading/thread_local.h"
14 #include "crypto/capi_util.h" 15 #include "crypto/capi_util.h"
15 #include "crypto/scoped_capi_types.h" 16 #include "crypto/scoped_capi_types.h"
16 #include "crypto/sha2.h" 17 #include "crypto/sha2.h"
17 #include "net/base/net_errors.h" 18 #include "net/base/net_errors.h"
18 #include "net/cert/asn1_util.h" 19 #include "net/cert/asn1_util.h"
19 #include "net/cert/cert_status_flags.h" 20 #include "net/cert/cert_status_flags.h"
20 #include "net/cert/cert_verifier.h" 21 #include "net/cert/cert_verifier.h"
21 #include "net/cert/cert_verify_result.h" 22 #include "net/cert/cert_verify_result.h"
22 #include "net/cert/crl_set.h" 23 #include "net/cert/crl_set.h"
23 #include "net/cert/ev_root_ca_metadata.h" 24 #include "net/cert/ev_root_ca_metadata.h"
(...skipping 356 matching lines...) Expand 10 before | Expand all | Expand 10 after
380 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, 381 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
381 &decode_para, 382 &decode_para,
382 &policies_info, 383 &policies_info,
383 &policies_info_size); 384 &policies_info_size);
384 if (rv) 385 if (rv)
385 output->reset(policies_info); 386 output->reset(policies_info);
386 } 387 }
387 388
388 enum CRLSetResult { 389 enum CRLSetResult {
389 kCRLSetOk, 390 kCRLSetOk,
391 kCRLSetError,
390 kCRLSetUnknown, 392 kCRLSetUnknown,
391 kCRLSetRevoked, 393 kCRLSetRevoked,
392 }; 394 };
393 395
394 // CheckRevocationWithCRLSet attempts to check each element of |chain| 396 CRLSetResult CheckRevocationWithCRLSet(CRLSet* crl_set,
davidben 2016/01/06 03:37:48 Documentation comment? It's somewhat unclear what
Ryan Sleevi 2016/01/06 04:26:20 Yeah, this part I threw up a little early to get f
397 PCCERT_CONTEXT subject_cert,
398 PCCERT_CONTEXT issuer_cert,
399 std::string* previous_hash) {
400 DCHECK(crl_set);
401 DCHECK(subject_cert);
402
403 // Check to see if |subject_cert|'s SPKI is revoked. The actual revocation
404 // is handled by the SHA-256 hash of the SPKI, so compute that.
405 base::StringPiece der_bytes(
406 reinterpret_cast<const char*>(subject_cert->pbCertEncoded),
407 subject_cert->cbCertEncoded);
408
409 base::StringPiece spki;
410 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) {
411 NOTREACHED();
davidben 2016/01/06 03:37:48 This NOTREACHED() is because, to have gotten this
Ryan Sleevi 2016/01/06 04:26:19 And it'll log, but behave consistently - but we ce
412 previous_hash->clear();
413 return kCRLSetError;
414 }
415 std::string subject_hash = crypto::SHA256HashString(spki);
416
417 CRLSet::Result result = crl_set->CheckSPKI(subject_hash);
418 if (result == CRLSet::REVOKED) {
419 previous_hash->swap(subject_hash);
420 return kCRLSetRevoked;
421 }
422
423 // If no issuer cert is provided, nor a hash of the issuer's SPKI, no
424 // further checks can be done.
425 if (!issuer_cert && previous_hash->empty()) {
426 previous_hash->swap(subject_hash);
427 return kCRLSetUnknown;
428 }
429
430 // Compute the subject's serial.
431 const CRYPT_INTEGER_BLOB* serial_blob =
432 &subject_cert->pCertInfo->SerialNumber;
433 scoped_ptr<uint8_t[]> serial_bytes(new uint8_t[serial_blob->cbData]);
434 // The bytes of the serial number are stored little-endian.
davidben 2016/01/06 03:37:48 ...what? There's also the leading 00 bytes and the
Ryan Sleevi 2016/01/06 04:26:20 But negative serials never happen! ;)
Ryan Sleevi 2016/01/14 01:13:15 Confirmed it's a documentation bug; it's talking a
435 for (unsigned j = 0; j < serial_blob->cbData; j++)
436 serial_bytes[j] = serial_blob->pbData[serial_blob->cbData - j - 1];
437 base::StringPiece serial(reinterpret_cast<const char*>(serial_bytes.get()),
438 serial_blob->cbData);
439
440 // Compute the issuer's hash. If it was provided (via previous_hash),
441 // use that; otherwise, compute it based on |issuer_cert|.
442 std::string issuer_hash_local;
443 std::string* issuer_hash = previous_hash;
444 if (issuer_hash->empty()) {
445 der_bytes = base::StringPiece(
446 reinterpret_cast<const char*>(issuer_cert->pbCertEncoded),
447 issuer_cert->cbCertEncoded);
448
449 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) {
450 NOTREACHED();
davidben 2016/01/06 03:37:48 Ditto about NOTREACHED().
451 return kCRLSetError;
davidben 2016/01/06 03:37:47 This codepath doesn't touch *previous_hash which a
Ryan Sleevi 2016/01/06 04:26:20 D'oh, good spot.
452 }
453
454 issuer_hash_local = crypto::SHA256HashString(spki);
455 issuer_hash = &issuer_hash_local;
456 }
457
458 // Look up by serial & issuer SPKI.
459 result = crl_set->CheckSerial(serial, *issuer_hash);
460 if (result == CRLSet::REVOKED)
davidben 2016/01/06 03:37:48 switch-case, so we catch it all?
Ryan Sleevi 2016/01/06 04:26:19 This was intentional; I think a switch hinders, ra
461 return kCRLSetRevoked;
462
463 previous_hash->swap(subject_hash);
davidben 2016/01/06 03:37:48 How come previous_hash does not get updated on rev
Ryan Sleevi 2016/01/06 04:26:20 Because revoke terminates processing.
464 if (result == CRLSet::GOOD)
465 return kCRLSetOk;
466 if (result == CRLSet::UNKNOWN)
467 return kCRLSetUnknown;
468
469 NOTREACHED();
470 return kCRLSetError;
471 }
472
473 // CheckChainRevocationWithCRLSet attempts to check each element of |chain|
395 // against |crl_set|. It returns: 474 // against |crl_set|. It returns:
396 // kCRLSetRevoked: if any element of the chain is known to have been revoked. 475 // kCRLSetRevoked: if any element of the chain is known to have been revoked.
397 // kCRLSetUnknown: if there is no fresh information about the leaf 476 // kCRLSetUnknown: if there is no fresh information about the leaf
398 // certificate in the chain or if the CRLSet has expired. 477 // certificate in the chain or if the CRLSet has expired.
399 // 478 //
400 // Only the leaf certificate is considered for coverage because some 479 // Only the leaf certificate is considered for coverage because some
401 // intermediates have CRLs with no revocations (after filtering) and 480 // intermediates have CRLs with no revocations (after filtering) and
402 // those CRLs are pruned from the CRLSet at generation time. This means 481 // those CRLs are pruned from the CRLSet at generation time. This means
403 // that some EV sites would otherwise take the hit of an OCSP lookup for 482 // that some EV sites would otherwise take the hit of an OCSP lookup for
404 // no reason. 483 // no reason.
405 // kCRLSetOk: otherwise. 484 // kCRLSetOk: otherwise.
406 CRLSetResult CheckRevocationWithCRLSet(PCCERT_CHAIN_CONTEXT chain, 485 CRLSetResult CheckChainRevocationWithCRLSet(PCCERT_CHAIN_CONTEXT chain,
407 CRLSet* crl_set) { 486 CRLSet* crl_set) {
408 if (chain->cChain == 0) 487 if (crl_set->IsExpired())
488 return kCRLSetUnknown;
davidben 2016/01/06 03:37:48 This check got moved from the end. It's a change i
Ryan Sleevi 2016/01/06 04:26:20 An attacker can suppress downloading an updated CR
489
490 if (chain->cChain == 0 || chain->rgpChain[0]->cElement == 0)
409 return kCRLSetOk; 491 return kCRLSetOk;
410 492
411 const PCERT_SIMPLE_CHAIN first_chain = chain->rgpChain[0]; 493 PCERT_CHAIN_ELEMENT* elements = chain->rgpChain[0]->rgpElement;
412 const PCERT_CHAIN_ELEMENT* element = first_chain->rgpElement; 494 DWORD num_elements = chain->rgpChain[0]->cElement;
413 495
414 const int num_elements = first_chain->cElement; 496 bool had_error = false;
415 if (num_elements == 0) 497 CRLSetResult result = kCRLSetError;
416 return kCRLSetOk;
417
418 // error is set to true if any errors are found. It causes such chains to be
419 // considered as not covered.
420 bool error = false;
421 // last_covered is set to the coverage state of the previous certificate. The
422 // certificates are iterated over backwards thus, after the iteration,
423 // |last_covered| contains the coverage state of the leaf certificate.
424 bool last_covered = false;
425
426 // We iterate from the root certificate down to the leaf, keeping track of
427 // the issuer's SPKI at each step.
428 std::string issuer_spki_hash; 498 std::string issuer_spki_hash;
429 for (int i = num_elements - 1; i >= 0; i--) { 499 for (DWORD i = num_elements - 1; i >= 0; --i) {
430 PCCERT_CONTEXT cert = element[i]->pCertContext; 500 PCCERT_CONTEXT subject = elements[i]->pCertContext;
431 501 result =
432 base::StringPiece der_bytes( 502 CheckRevocationWithCRLSet(crl_set, subject, nullptr, &issuer_spki_hash);
433 reinterpret_cast<const char*>(cert->pbCertEncoded), 503 if (result == kCRLSetRevoked)
434 cert->cbCertEncoded); 504 return result;
435 505 if (result == kCRLSetError)
436 base::StringPiece spki; 506 had_error = true;
437 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) {
438 NOTREACHED();
439 error = true;
440 continue;
441 }
442
443 const std::string spki_hash = crypto::SHA256HashString(spki);
444
445 const CRYPT_INTEGER_BLOB* serial_blob = &cert->pCertInfo->SerialNumber;
446 scoped_ptr<uint8_t[]> serial_bytes(new uint8_t[serial_blob->cbData]);
447 // The bytes of the serial number are stored little-endian.
448 for (unsigned j = 0; j < serial_blob->cbData; j++)
449 serial_bytes[j] = serial_blob->pbData[serial_blob->cbData - j - 1];
450 base::StringPiece serial(reinterpret_cast<const char*>(serial_bytes.get()),
451 serial_blob->cbData);
452
453 CRLSet::Result result = crl_set->CheckSPKI(spki_hash);
454
455 if (result != CRLSet::REVOKED && !issuer_spki_hash.empty())
456 result = crl_set->CheckSerial(serial, issuer_spki_hash);
457
458 issuer_spki_hash = spki_hash;
459
460 switch (result) {
461 case CRLSet::REVOKED:
462 return kCRLSetRevoked;
463 case CRLSet::UNKNOWN:
464 last_covered = false;
465 continue;
466 case CRLSet::GOOD:
467 last_covered = true;
468 continue;
469 default:
470 NOTREACHED();
471 error = true;
472 continue;
473 }
474 } 507 }
475 508 if (had_error)
476 if (error || !last_covered || crl_set->IsExpired())
477 return kCRLSetUnknown; 509 return kCRLSetUnknown;
478 return kCRLSetOk; 510 return result;
davidben 2016/01/06 03:37:48 This might warrant a comment like: // At this po
Ryan Sleevi 2016/01/06 04:26:19 I don't understand what you're suggesting we would
Ryan Sleevi 2016/01/14 01:13:15 I reparsed what you're saying - you're talking abo
479 } 511 }
480 512
481 void AppendPublicKeyHashes(PCCERT_CHAIN_CONTEXT chain, 513 void AppendPublicKeyHashes(PCCERT_CHAIN_CONTEXT chain,
482 HashValueVector* hashes) { 514 HashValueVector* hashes) {
483 if (chain->cChain == 0) 515 if (chain->cChain == 0)
484 return; 516 return;
485 517
486 PCERT_SIMPLE_CHAIN first_chain = chain->rgpChain[0]; 518 PCERT_SIMPLE_CHAIN first_chain = chain->rgpChain[0];
487 PCERT_CHAIN_ELEMENT* const element = first_chain->rgpElement; 519 PCERT_CHAIN_ELEMENT* const element = first_chain->rgpElement;
488 520
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
544 return false; 576 return false;
545 577
546 // Look up the EV policy OID of the root CA. 578 // Look up the EV policy OID of the root CA.
547 PCCERT_CONTEXT root_cert = element[num_elements - 1]->pCertContext; 579 PCCERT_CONTEXT root_cert = element[num_elements - 1]->pCertContext;
548 SHA1HashValue fingerprint = 580 SHA1HashValue fingerprint =
549 X509Certificate::CalculateFingerprint(root_cert); 581 X509Certificate::CalculateFingerprint(root_cert);
550 EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance(); 582 EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance();
551 return metadata->HasEVPolicyOID(fingerprint, policy_oid); 583 return metadata->HasEVPolicyOID(fingerprint, policy_oid);
552 } 584 }
553 585
586 // Custom revocation provider function that compares incoming certificates with
587 // those in CRLSets. This is called BEFORE the default CRL & OCSP handling
588 // is invoked (which is handled by the revocation provider function
589 // "CertDllVerifyRevocation" in cryptnet.dll)
590 BOOL WINAPI
591 CertDllVerifyRevocationWithCRLSet(DWORD encoding_type,
592 DWORD revocation_type,
593 DWORD num_contexts,
594 void* rgpvContext[],
595 DWORD flags,
596 PCERT_REVOCATION_PARA revocation_params,
597 PCERT_REVOCATION_STATUS revocation_status);
598
599 // Helper class that installs the CRLSet-based Revocation Provider as the
600 // default revocation provider. Because it is installed as a function address
601 // (meaning only scoped to the process, and not stored in the registry), it
602 // will be used before any registry-based providers, including Microsoft's
603 // default provider.
604 class RevocationInjector {
605 public:
606 CRLSet* GetCRLSet() { return thread_local_crlset.Get(); }
607
608 void SetCRLSet(CRLSet* crl_set) { thread_local_crlset.Set(crl_set); }
609
610 private:
611 friend struct base::DefaultLazyInstanceTraits<RevocationInjector>;
612
613 RevocationInjector() {
614 const CRYPT_OID_FUNC_ENTRY kInterceptFunction[] = {
615 {CRYPT_DEFAULT_OID, &CertDllVerifyRevocationWithCRLSet},
616 };
617 BOOL ok = CryptInstallOIDFunctionAddress(
618 NULL, X509_ASN_ENCODING, CRYPT_OID_VERIFY_REVOCATION_FUNC,
619 arraysize(kInterceptFunction), kInterceptFunction,
620 CRYPT_INSTALL_OID_FUNC_BEFORE_FLAG);
621 DCHECK(ok);
622 }
623
624 ~RevocationInjector() {}
625
626 // As the revocation parameters passed to CertVerifyProc::VerifyInternal
627 // cannot be officially smuggled to the Revocation Provider
628 base::ThreadLocalPointer<CRLSet> thread_local_crlset;
davidben 2016/01/06 03:37:48 Ooh, nice trick. We should use it to lessen some o
Ryan Sleevi 2016/01/06 04:26:20 Um, wat? That doesn't really reduce the globals at
Ryan Sleevi 2016/01/06 04:59:00 I guess I should mention what I'd mentioned to Eri
davidben 2016/01/21 02:37:38 Hrm, that's true. We would be assuming it calls th
Ryan Sleevi 2016/01/21 02:54:04 Which NSS's API contract is such that that is not
629 };
630
631 // Leaky, as CertVerifyProc workers are themselves leaky.
632 base::LazyInstance<RevocationInjector>::Leaky g_revocation_injector =
633 LAZY_INSTANCE_INITIALIZER;
634
635 BOOL WINAPI
636 CertDllVerifyRevocationWithCRLSet(DWORD encoding_type,
637 DWORD revocation_type,
638 DWORD num_contexts,
639 void* rgpvContext[],
640 DWORD flags,
641 PCERT_REVOCATION_PARA revocation_params,
642 PCERT_REVOCATION_STATUS revocation_status) {
643 PCERT_CONTEXT* cert_contexts = reinterpret_cast<PCERT_CONTEXT*>(rgpvContext);
644 // The dummy CRLSet provider never returns that something is affirmatively
645 // *un*revoked, as this would disable other revocation providers from being
646 // checked for this certificate (much like an OCSP "Good" status would).
647 // Instead, it merely indicates that insufficient information existed to
648 // determine if the certificate was revoked (in the good case), or that a cert
649 // is affirmatively revoked in the event it appears within the CRLSet.
650 // Because of this, set up some basic bookkeeping for the results.
651 CHECK(revocation_status);
652 revocation_status->dwIndex = 0;
653 revocation_status->dwError = static_cast<DWORD>(CRYPT_E_NO_REVOCATION_CHECK);
654 revocation_status->dwReason = 0;
655
656 if (num_contexts == 0) {
657 SetLastError(static_cast<DWORD>(E_INVALIDARG));
658 return FALSE;
659 }
660 if ((GET_CERT_ENCODING_TYPE(encoding_type) != X509_ASN_ENCODING) ||
davidben 2016/01/06 03:37:48 Just to confirm, this one should never happen due
Ryan Sleevi 2016/01/06 04:26:20 Right
661 revocation_type != CERT_CONTEXT_REVOCATION_TYPE) {
662 SetLastError(static_cast<DWORD>(CRYPT_E_NO_REVOCATION_CHECK));
663 return FALSE;
664 }
665
666 // |revocation_params| is an optional structure; to make life simple and avoid
667 // the need to constantly check whether or not it was supplied, create a local
668 // copy. If the caller didn't supply anything, it will be empty; otherwise,
669 // it will be (non-owning) copies of the caller's original params.
670 CERT_REVOCATION_PARA local_params;
671 memset(&local_params, 0, sizeof(local_params));
672 local_params.cbSize = sizeof(local_params);
673 if (revocation_params) {
674 DWORD bytes_to_copy =
675 std::min(revocation_params->cbSize, local_params.cbSize);
676 memcpy(&local_params, revocation_params, bytes_to_copy);
677 local_params.cbSize = sizeof(local_params);
davidben 2016/01/06 03:37:48 Nit: Could also take this and line 672 out and mov
678 }
679
680 PCERT_CONTEXT subject_cert = cert_contexts[0];
681 if (!subject_cert) {
682 SetLastError(static_cast<DWORD>(E_INVALIDARG));
683 return FALSE;
684 }
685
686 // Determine the issuer cert for the incoming cert
687 ScopedPCCERT_CONTEXT issuer_cert;
688 if ((flags & CERT_VERIFY_REV_CHAIN_FLAG) && num_contexts > 1) {
689 // Verifying a chain; issuer is the next cert in the chain.
davidben 2016/01/06 03:37:48 A priori I would have expected this to have expect
Ryan Sleevi 2016/01/06 04:26:19 Self-signed certs and janky-ass third-party invoca
690 issuer_cert.reset(CertDuplicateCertificateContext(cert_contexts[1]));
691 } else if (local_params.pIssuerCert) {
692 // Caller has already supplied the issuer cert via the revocation params;
693 // just use that.
694 issuer_cert.reset(
695 CertDuplicateCertificateContext(local_params.pIssuerCert));
696 } else if (CertCompareCertificateName(subject_cert->dwCertEncodingType,
697 &subject_cert->pCertInfo->Subject,
698 &subject_cert->pCertInfo->Issuer) &&
699 CryptVerifyCertificateSignatureEx(
700 NULL, subject_cert->dwCertEncodingType,
701 CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT, subject_cert,
702 CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, subject_cert, 0,
703 nullptr)) {
704 // Certificate is self-signed; use it as its own issuer.
705 issuer_cert.reset(CertDuplicateCertificateContext(subject_cert));
706 } else {
707 // Scan the caller-supplied stores first, to try and find the issuer cert.
708 for (DWORD i = 0; i < local_params.cCertStore && !issuer_cert; ++i) {
709 DWORD store_search_flags = CERT_STORE_SIGNATURE_FLAG;
710 PCCERT_CONTEXT previous_cert = nullptr;
711 while ((previous_cert = CertGetIssuerCertificateFromStore(
712 local_params.rgCertStore[i], subject_cert, previous_cert,
713 &store_search_flags),
714 previous_cert)) {
Ryan Sleevi 2016/01/04 22:33:33 Note: This was to avoid MSVC complaints about assi
davidben 2016/01/06 03:37:48 I am fond of Go's decl + comparison pattern, but I
715 // If a cert is found and meets the criteria, the flag will be reset to
716 // zero. Thus NOT having the bit set is equivalent to having found a
717 // matching certificate.
718 if (!(store_search_flags & CERT_STORE_SIGNATURE_FLAG)) {
719 // No need to dupe; reference is held.
720 issuer_cert.reset(previous_cert);
721 break;
722 }
723 store_search_flags = CERT_STORE_SIGNATURE_FLAG;
davidben 2016/01/06 03:37:48 Looks like previous_cert gets leaked if you hit th
Ryan Sleevi 2016/01/06 04:26:19 Line 711 frees it (all the enumerative functions t
724 }
725 if (issuer_cert)
726 break;
727 if (GetLastError() == CRYPT_E_SELF_SIGNED) {
728 issuer_cert.reset(CertDuplicateCertificateContext(subject_cert));
729 break;
730 }
731 }
732
733 // At this point, the Microsoft provider opens up the "CA", "Root", and
734 // "SPC" stores to search for the issuer certificate, if not found in the
735 // caller-supplied stores.
736 // TODO(rsleevi): Determine if this is necessary
737 }
738
739 if (!issuer_cert) {
740 revocation_status->dwError = static_cast<DWORD>(CRYPT_E_REVOCATION_OFFLINE);
davidben 2016/01/06 03:37:48 Why OFFLINE and not NO_REVOCATION_CHECK? The docs
Ryan Sleevi 2016/01/06 04:26:19 Because it has enough information (e.g. from the c
741 SetLastError(revocation_status->dwError);
742 return FALSE;
743 }
744
745 // TODO(rsleevi) Do baked-in certificate revocation.
davidben 2016/01/06 03:37:48 super nitpicky nit: comma after close-colon.
Ryan Sleevi 2016/01/06 04:26:20 super nitpicky nit nit: Did you mean colon after c
746
747 // Do dynamic certificate revocation.
748 CRLSet* crl_set = g_revocation_injector.Get().GetCRLSet();
749 if (!crl_set)
750 return FALSE;
751 if (crl_set->IsExpired())
davidben 2016/01/06 03:37:48 Ditto re comment on line 485. (Also this would be
Ryan Sleevi 2016/01/06 04:26:20 No - this is executed during path building, and Ch
752 return FALSE;
753
754 std::string unused;
davidben 2016/01/06 03:37:48 Nit: Since this is an input/output parameter, it's
Ryan Sleevi 2016/01/06 04:26:19 Is there a concrete suggestion here for what you t
755 CRLSetResult result = CheckRevocationWithCRLSet(crl_set, subject_cert,
756 issuer_cert.get(), &unused);
757 if (result == kCRLSetRevoked) {
davidben 2016/01/06 03:37:48 Nit: Should this be a switch-case just to explicit
Ryan Sleevi 2016/01/06 04:26:20 It is not what we want for kCRLSetError, which the
758 revocation_status->dwError = static_cast<DWORD>(CRYPT_E_REVOKED);
759 revocation_status->dwReason = CRL_REASON_UNSPECIFIED;
760 SetLastError(revocation_status->dwError);
761 return FALSE;
762 }
763
764 // The result is ALWAYS FALSE in order to allow the next revocation provider
765 // a chance to examine. The only possible time it is TRUE is when the
766 // certificate is already revoked.
davidben 2016/01/06 03:37:48 It doesn't return TRUE in that case either, right?
Ryan Sleevi 2016/01/06 04:26:19 Comment BUG, thanks :) Revoked gets a FALSE return
767 return FALSE;
768 }
769
770 class ScopedThreadLocalCRLSet {
771 public:
772 explicit ScopedThreadLocalCRLSet(CRLSet* crl_set) {
773 g_revocation_injector.Get().SetCRLSet(crl_set);
774 }
775 ~ScopedThreadLocalCRLSet() { g_revocation_injector.Get().SetCRLSet(nullptr); }
776 };
777
554 } // namespace 778 } // namespace
555 779
556 CertVerifyProcWin::CertVerifyProcWin() {} 780 CertVerifyProcWin::CertVerifyProcWin() {}
557 781
558 CertVerifyProcWin::~CertVerifyProcWin() {} 782 CertVerifyProcWin::~CertVerifyProcWin() {}
559 783
560 bool CertVerifyProcWin::SupportsAdditionalTrustAnchors() const { 784 bool CertVerifyProcWin::SupportsAdditionalTrustAnchors() const {
561 return false; 785 return false;
562 } 786 }
563 787
564 bool CertVerifyProcWin::SupportsOCSPStapling() const { 788 bool CertVerifyProcWin::SupportsOCSPStapling() const {
565 // CERT_OCSP_RESPONSE_PROP_ID is only implemented on Vista+, but it can be 789 // CERT_OCSP_RESPONSE_PROP_ID is only implemented on Vista+, but it can be
566 // set on Windows XP without error. There is some overhead from the server 790 // set on Windows XP without error. There is some overhead from the server
567 // sending the OCSP response if it supports the extension, for the subset of 791 // sending the OCSP response if it supports the extension, for the subset of
568 // XP clients who will request it but be unable to use it, but this is an 792 // XP clients who will request it but be unable to use it, but this is an
569 // acceptable trade-off for simplicity of implementation. 793 // acceptable trade-off for simplicity of implementation.
570 return true; 794 return true;
571 } 795 }
572 796
573 int CertVerifyProcWin::VerifyInternal( 797 int CertVerifyProcWin::VerifyInternal(
574 X509Certificate* cert, 798 X509Certificate* cert,
575 const std::string& hostname, 799 const std::string& hostname,
576 const std::string& ocsp_response, 800 const std::string& ocsp_response,
577 int flags, 801 int flags,
578 CRLSet* crl_set, 802 CRLSet* crl_set,
579 const CertificateList& additional_trust_anchors, 803 const CertificateList& additional_trust_anchors,
580 CertVerifyResult* verify_result) { 804 CertVerifyResult* verify_result) {
805 // Ensure the Revocation Provider has been installed and configured for this
806 // CRLSet.
807 ScopedThreadLocalCRLSet thread_local_crlset(crl_set);
808
581 PCCERT_CONTEXT cert_handle = cert->os_cert_handle(); 809 PCCERT_CONTEXT cert_handle = cert->os_cert_handle();
582 if (!cert_handle) 810 if (!cert_handle)
583 return ERR_UNEXPECTED; 811 return ERR_UNEXPECTED;
584 812
585 // Build and validate certificate chain. 813 // Build and validate certificate chain.
586 CERT_CHAIN_PARA chain_para; 814 CERT_CHAIN_PARA chain_para;
587 memset(&chain_para, 0, sizeof(chain_para)); 815 memset(&chain_para, 0, sizeof(chain_para));
588 chain_para.cbSize = sizeof(chain_para); 816 chain_para.cbSize = sizeof(chain_para);
589 // ExtendedKeyUsage. 817 // ExtendedKeyUsage.
590 // We still need to request szOID_SERVER_GATED_CRYPTO and szOID_SGC_NETSCAPE 818 // We still need to request szOID_SERVER_GATED_CRYPTO and szOID_SGC_NETSCAPE
(...skipping 23 matching lines...) Expand all
614 chain_para.RequestedIssuancePolicy.dwType = USAGE_MATCH_TYPE_AND; 842 chain_para.RequestedIssuancePolicy.dwType = USAGE_MATCH_TYPE_AND;
615 chain_para.RequestedIssuancePolicy.Usage.cUsageIdentifier = 1; 843 chain_para.RequestedIssuancePolicy.Usage.cUsageIdentifier = 1;
616 chain_para.RequestedIssuancePolicy.Usage.rgpszUsageIdentifier = 844 chain_para.RequestedIssuancePolicy.Usage.rgpszUsageIdentifier =
617 &ev_policy_oid; 845 &ev_policy_oid;
618 break; 846 break;
619 } 847 }
620 } 848 }
621 } 849 }
622 } 850 }
623 851
624 // We can set CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS to get more chains. 852 // Revocation checking is always enabled, in order to enable CRLSets to be
625 DWORD chain_flags = CERT_CHAIN_CACHE_END_CERT | 853 // evaluated as part of a revocation provider. However, when the caller did
626 CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; 854 // not explicitly request revocation checking (which is to say, online
855 // revocation checking), then only enable cached results. This disables OCSP
856 // and CRL fetching, but still allows the revocation provider to be called.
857 // Note: The root cert is also checked for revocation status, so that CRLSets
858 // will cover revoked SPKIs.
859 DWORD chain_flags = CERT_CHAIN_REVOCATION_CHECK_CHAIN;
Ryan Sleevi 2016/01/04 22:33:33 I stopped supplying CACHE_END_CERT for two reasons
627 bool rev_checking_enabled = 860 bool rev_checking_enabled =
628 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED); 861 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED);
629
630 if (rev_checking_enabled) { 862 if (rev_checking_enabled) {
631 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; 863 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
632 } else { 864 } else {
633 chain_flags |= CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY; 865 chain_flags |= CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY;
634 } 866 }
635 867
636 // For non-test scenarios, use the default HCERTCHAINENGINE, NULL, which 868 // By default, use the default HCERTCHAINENGINE (aka HCCE_CURRENT_USER). When
637 // corresponds to HCCE_CURRENT_USER and is is initialized as needed by 869 // running tests, use a dynamic HCERTCHAINENGINE. All of the status and cache
638 // crypt32. However, when testing, it is necessary to create a new 870 // of verified certificates and chains is tied to the HCERTCHAINENGINE. As
639 // HCERTCHAINENGINE and use that instead. This is because each 871 // each invocation may have changed the set of known roots, invalid the cache
640 // HCERTCHAINENGINE maintains a cache of information about certificates 872 // between runs.
641 // encountered, and each test run may modify the trust status of a 873 //
642 // certificate. 874 // This is not the most efficient means of doing so; it's possible to mark the
875 // Root store used by TestRootCerts as changed, via CertControlStore with the
davidben 2016/01/06 03:37:48 Root -> root? Or is this a Windows proper noun?
Ryan Sleevi 2016/01/06 04:26:20 It's the name of a specific Logical Store ("Root")
876 // CERT_STORE_CTRL_NOTIFY_CHANGE / CERT_STORE_CTRL_RESYNC, but that's more
877 // complexity for what is test-only code.
643 ScopedHCERTCHAINENGINE chain_engine(NULL); 878 ScopedHCERTCHAINENGINE chain_engine(NULL);
644 if (TestRootCerts::HasInstance()) 879 if (TestRootCerts::HasInstance())
645 chain_engine.reset(TestRootCerts::GetInstance()->GetChainEngine()); 880 chain_engine.reset(TestRootCerts::GetInstance()->GetChainEngine());
646 881
647 ScopedPCCERT_CONTEXT cert_list(cert->CreateOSCertChainForCert()); 882 ScopedPCCERT_CONTEXT cert_list(cert->CreateOSCertChainForCert());
648 883
884 // Add stapled OCSP response data, which will be preferred over online checks
885 // and used when in cache-only mode.
649 if (!ocsp_response.empty()) { 886 if (!ocsp_response.empty()) {
650 // Attach the OCSP response to the chain.
651 CRYPT_DATA_BLOB ocsp_response_blob; 887 CRYPT_DATA_BLOB ocsp_response_blob;
652 ocsp_response_blob.cbData = ocsp_response.size(); 888 ocsp_response_blob.cbData = ocsp_response.size();
653 ocsp_response_blob.pbData = 889 ocsp_response_blob.pbData =
654 reinterpret_cast<BYTE*>(const_cast<char*>(ocsp_response.data())); 890 reinterpret_cast<BYTE*>(const_cast<char*>(ocsp_response.data()));
655 CertSetCertificateContextProperty( 891 CertSetCertificateContextProperty(
656 cert_list.get(), CERT_OCSP_RESPONSE_PROP_ID, 892 cert_list.get(), CERT_OCSP_RESPONSE_PROP_ID,
657 CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG, &ocsp_response_blob); 893 CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG, &ocsp_response_blob);
658 } 894 }
659 895
660 PCCERT_CHAIN_CONTEXT chain_context; 896 PCCERT_CHAIN_CONTEXT chain_context = nullptr;
661 // IE passes a non-NULL pTime argument that specifies the current system
662 // time. IE passes CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT as the
663 // chain_flags argument.
664 if (!CertGetCertificateChain( 897 if (!CertGetCertificateChain(
665 chain_engine, 898 chain_engine,
666 cert_list.get(), 899 cert_list.get(),
667 NULL, // current system time 900 NULL, // current system time
668 cert_list->hCertStore, 901 cert_list->hCertStore,
669 &chain_para, 902 &chain_para,
670 chain_flags, 903 chain_flags,
671 NULL, // reserved 904 NULL, // reserved
672 &chain_context)) { 905 &chain_context)) {
673 verify_result->cert_status |= CERT_STATUS_INVALID; 906 verify_result->cert_status |= CERT_STATUS_INVALID;
674 return MapSecurityError(GetLastError()); 907 return MapSecurityError(GetLastError());
675 } 908 }
676 909
910 // Perform a second check with CRLSets. Although the Revocation Provider
911 // should have prevented invalid paths from being built, the behaviour and
912 // timing of how a Revocation Provider is invoked is not well documented. This
913 // is just defense in depth.
677 CRLSetResult crl_set_result = kCRLSetUnknown; 914 CRLSetResult crl_set_result = kCRLSetUnknown;
678 if (crl_set) 915 if (crl_set)
679 crl_set_result = CheckRevocationWithCRLSet(chain_context, crl_set); 916 crl_set_result = CheckChainRevocationWithCRLSet(chain_context, crl_set);
680 917
681 if (crl_set_result == kCRLSetRevoked) { 918 if (crl_set_result == kCRLSetRevoked) {
682 verify_result->cert_status |= CERT_STATUS_REVOKED; 919 verify_result->cert_status |= CERT_STATUS_REVOKED;
683 } else if (crl_set_result == kCRLSetUnknown && 920 } else if (crl_set_result == kCRLSetUnknown &&
684 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY) && 921 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY) &&
685 !rev_checking_enabled && 922 !rev_checking_enabled &&
686 ev_policy_oid != NULL) { 923 ev_policy_oid != NULL) {
687 // We don't have fresh information about this chain from the CRLSet and 924 // We don't have fresh information about this chain from the CRLSet and
688 // it's probably an EV certificate. Retry with online revocation checking. 925 // it's probably an EV certificate. Retry with online revocation checking.
689 rev_checking_enabled = true; 926 rev_checking_enabled = true;
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after
827 return MapCertStatusToNetError(verify_result->cert_status); 1064 return MapCertStatusToNetError(verify_result->cert_status);
828 1065
829 if (ev_policy_oid && 1066 if (ev_policy_oid &&
830 CheckEV(chain_context, rev_checking_enabled, ev_policy_oid)) { 1067 CheckEV(chain_context, rev_checking_enabled, ev_policy_oid)) {
831 verify_result->cert_status |= CERT_STATUS_IS_EV; 1068 verify_result->cert_status |= CERT_STATUS_IS_EV;
832 } 1069 }
833 return OK; 1070 return OK;
834 } 1071 }
835 1072
836 } // namespace net 1073 } // namespace net
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698