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

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: More tests 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
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 355 matching lines...) Expand 10 before | Expand all | Expand 10 after
379 extension->Value.cbData, 380 extension->Value.cbData,
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 {
390 // Indicates an error happened while attempting to determine CRLSet status.
391 // For example, if the certificate's SPKI could not be extracted.
392 kCRLSetError,
393
394 // Indicates there is no fresh information about the certificate, or if the
395 // CRLSet has expired.
396 // In the case of certificate chains, this is only returned if the leaf
397 // certificate is not covered by the CRLSet; this is because some
398 // intermediates are fully covered, but after filtering, the issuer's CRL
399 // is empty and thus omitted from the CRLSet. Since online checking is
400 // performed for EV certificates when this status is returned, this would
401 // result in needless online lookups for certificates known not-revoked.
402 kCRLSetUnknown,
403
404 // Indicates that the certificate (or a certificate in the chain) has been
405 // revoked.
406 kCRLSetRevoked,
407
408 // The certificate (or certificate chain) has no revocations.
389 kCRLSetOk, 409 kCRLSetOk,
390 kCRLSetUnknown,
391 kCRLSetRevoked,
392 }; 410 };
393 411
394 // CheckRevocationWithCRLSet attempts to check each element of |chain| 412 // Determines if |subject_cert| is revoked within |crl_set|,
413 // storing the SubjectPublicKeyInfo hash of |subject_cert| in
414 // |*previous_hash|.
415 //
416 // CRLSets store revocations by both SPKI and by the tuple of Issuer SPKI
417 // Hash & Serial. While |subject_cert| contains enough information to check
418 // for SPKI revocations, to determine the issuer's SPKI, either |issuer_cert|
419 // must be supplied, or the hash of the issuer's SPKI provided in
420 // |previous_hash|. If |issuer_cert| is omitted, and |previous_hash| is empty,
davidben 2016/01/21 02:37:39 Nit: Most of these |previous_hash|s should probabl
421 // only SPKI checks are performed.
422 //
423 // To avoid recomputing SPKI hashes, the hash of |subject_cert| is stored in
424 // |*previous_hash|. This allows chaining revocation checking, by starting
425 // at the root and iterating to the leaf, supplying |previous_hash| each time.
426 //
427 // In the event of a parsing error, |previous_hash| is cleared, to prevent the
428 // wrong Issuer&Serial tuple from being used.
429 CRLSetResult CheckRevocationWithCRLSet(CRLSet* crl_set,
430 PCCERT_CONTEXT subject_cert,
431 PCCERT_CONTEXT issuer_cert,
432 std::string* previous_hash) {
433 DCHECK(crl_set);
434 DCHECK(subject_cert);
435
436 // Check to see if |subject_cert|'s SPKI is revoked. The actual revocation
437 // is handled by the SHA-256 hash of the SPKI, so compute that.
438 base::StringPiece der_bytes(
439 reinterpret_cast<const char*>(subject_cert->pbCertEncoded),
440 subject_cert->cbCertEncoded);
441
442 base::StringPiece spki;
443 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) {
444 // Should not reach here; it indicates Windows accepted something that
445 // could not even be remotely parsed.
446 NOTREACHED();
447 previous_hash->clear();
448 return kCRLSetError;
449 }
450 std::string subject_hash = crypto::SHA256HashString(spki);
davidben 2016/01/21 02:37:39 Nit: I'd probably take lines 436 through 450 and p
451
452 CRLSet::Result result = crl_set->CheckSPKI(subject_hash);
453 if (result == CRLSet::REVOKED) {
454 previous_hash->swap(subject_hash);
davidben 2016/01/21 02:37:39 This is inconsistent with line 500's CRLSet::REVOK
Ryan Sleevi 2016/01/21 02:54:04 I agree, we shouldn't update it here, but I disagr
455 return kCRLSetRevoked;
456 }
457
458 // If no issuer cert is provided, nor a hash of the issuer's SPKI, no
459 // further checks can be done.
460 if (!issuer_cert && previous_hash->empty()) {
461 previous_hash->swap(subject_hash);
462 return kCRLSetUnknown;
463 }
464
465 // Compute the subject's serial.
466 const CRYPT_INTEGER_BLOB* serial_blob =
467 &subject_cert->pCertInfo->SerialNumber;
468 scoped_ptr<uint8_t[]> serial_bytes(new uint8_t[serial_blob->cbData]);
469 // The bytes of the serial number are stored little-endian.
470 // Note: While MSDN implies that bytes are stripped from this serial,
471 // they are not - only CertCompareIntegerBlob actually removes bytes.
472 for (unsigned j = 0; j < serial_blob->cbData; j++)
davidben 2016/01/21 02:37:39 Nit: unsigned -> DWORD, to match cbData's type?
Ryan Sleevi 2016/01/21 02:54:04 Yeah, good point; copy pasta.
473 serial_bytes[j] = serial_blob->pbData[serial_blob->cbData - j - 1];
474 base::StringPiece serial(reinterpret_cast<const char*>(serial_bytes.get()),
475 serial_blob->cbData);
476
477 // Compute the issuer's hash. If it was provided (via previous_hash),
478 // use that; otherwise, compute it based on |issuer_cert|.
davidben 2016/01/21 02:37:39 Nit: I would take this block and switch it with th
Ryan Sleevi 2016/01/21 02:54:04 I find that (the folding) substantially less reada
479 std::string issuer_hash_local;
480 std::string* issuer_hash = previous_hash;
481 if (issuer_hash->empty()) {
482 der_bytes = base::StringPiece(
483 reinterpret_cast<const char*>(issuer_cert->pbCertEncoded),
484 issuer_cert->cbCertEncoded);
485
486 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) {
487 // Should not reach here; it indicates Windows accepted something that
488 // could not even be remotely parsed.
489 NOTREACHED();
490 previous_hash->clear();
491 return kCRLSetError;
492 }
493
494 issuer_hash_local = crypto::SHA256HashString(spki);
495 issuer_hash = &issuer_hash_local;
496 }
497
498 // Look up by serial & issuer SPKI.
499 result = crl_set->CheckSerial(serial, *issuer_hash);
500 if (result == CRLSet::REVOKED)
501 return kCRLSetRevoked;
502
503 previous_hash->swap(subject_hash);
504 if (result == CRLSet::GOOD)
505 return kCRLSetOk;
506 if (result == CRLSet::UNKNOWN)
507 return kCRLSetUnknown;
508
509 NOTREACHED();
510 return kCRLSetError;
511 }
512
513 // CheckChainRevocationWithCRLSet attempts to check each element of |chain|
395 // against |crl_set|. It returns: 514 // against |crl_set|. It returns:
396 // kCRLSetRevoked: if any element of the chain is known to have been revoked. 515 // kCRLSetRevoked: if any element of the chain is known to have been revoked.
397 // kCRLSetUnknown: if there is no fresh information about the leaf 516 // kCRLSetUnknown: if there is no fresh information about the leaf
398 // certificate in the chain or if the CRLSet has expired. 517 // certificate in the chain or if the CRLSet has expired.
399 // 518 //
400 // Only the leaf certificate is considered for coverage because some 519 // Only the leaf certificate is considered for coverage because some
401 // intermediates have CRLs with no revocations (after filtering) and 520 // intermediates have CRLs with no revocations (after filtering) and
402 // those CRLs are pruned from the CRLSet at generation time. This means 521 // 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 522 // that some EV sites would otherwise take the hit of an OCSP lookup for
404 // no reason. 523 // no reason.
405 // kCRLSetOk: otherwise. 524 // kCRLSetOk: otherwise.
406 CRLSetResult CheckRevocationWithCRLSet(PCCERT_CHAIN_CONTEXT chain, 525 CRLSetResult CheckChainRevocationWithCRLSet(PCCERT_CHAIN_CONTEXT chain,
407 CRLSet* crl_set) { 526 CRLSet* crl_set) {
408 if (chain->cChain == 0) 527 if (chain->cChain == 0 || chain->rgpChain[0]->cElement == 0)
409 return kCRLSetOk; 528 return kCRLSetOk;
410 529
411 const PCERT_SIMPLE_CHAIN first_chain = chain->rgpChain[0]; 530 PCERT_CHAIN_ELEMENT* elements = chain->rgpChain[0]->rgpElement;
412 const PCERT_CHAIN_ELEMENT* element = first_chain->rgpElement; 531 DWORD num_elements = chain->rgpChain[0]->cElement;
413 532
414 const int num_elements = first_chain->cElement; 533 bool had_error = false;
415 if (num_elements == 0) 534 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; 535 std::string issuer_spki_hash;
429 for (int i = num_elements - 1; i >= 0; i--) { 536 for (DWORD i = 0; i < num_elements; ++i) {
430 PCCERT_CONTEXT cert = element[i]->pCertContext; 537 PCCERT_CONTEXT subject = elements[num_elements - i - 1]->pCertContext;
davidben 2016/01/21 02:37:39 Optional: If you want to avoid the num_elements -
Ryan Sleevi 2016/01/21 02:54:04 I intentionally wanted to avoid relying on "stupid
431 538 result =
432 base::StringPiece der_bytes( 539 CheckRevocationWithCRLSet(crl_set, subject, nullptr, &issuer_spki_hash);
433 reinterpret_cast<const char*>(cert->pbCertEncoded), 540 if (result == kCRLSetRevoked)
434 cert->cbCertEncoded); 541 return result;
435 542 if (result == kCRLSetError)
436 base::StringPiece spki; 543 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 } 544 }
475 545 if (had_error || crl_set->IsExpired())
476 if (error || !last_covered || crl_set->IsExpired())
477 return kCRLSetUnknown; 546 return kCRLSetUnknown;
478 return kCRLSetOk; 547 return result;
479 } 548 }
480 549
481 void AppendPublicKeyHashes(PCCERT_CHAIN_CONTEXT chain, 550 void AppendPublicKeyHashes(PCCERT_CHAIN_CONTEXT chain,
482 HashValueVector* hashes) { 551 HashValueVector* hashes) {
483 if (chain->cChain == 0) 552 if (chain->cChain == 0)
484 return; 553 return;
485 554
486 PCERT_SIMPLE_CHAIN first_chain = chain->rgpChain[0]; 555 PCERT_SIMPLE_CHAIN first_chain = chain->rgpChain[0];
487 PCERT_CHAIN_ELEMENT* const element = first_chain->rgpElement; 556 PCERT_CHAIN_ELEMENT* const element = first_chain->rgpElement;
488 557
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
544 return false; 613 return false;
545 614
546 // Look up the EV policy OID of the root CA. 615 // Look up the EV policy OID of the root CA.
547 PCCERT_CONTEXT root_cert = element[num_elements - 1]->pCertContext; 616 PCCERT_CONTEXT root_cert = element[num_elements - 1]->pCertContext;
548 SHA1HashValue fingerprint = 617 SHA1HashValue fingerprint =
549 X509Certificate::CalculateFingerprint(root_cert); 618 X509Certificate::CalculateFingerprint(root_cert);
550 EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance(); 619 EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance();
551 return metadata->HasEVPolicyOID(fingerprint, policy_oid); 620 return metadata->HasEVPolicyOID(fingerprint, policy_oid);
552 } 621 }
553 622
623 // Custom revocation provider function that compares incoming certificates with
624 // those in CRLSets. This is called BEFORE the default CRL & OCSP handling
625 // is invoked (which is handled by the revocation provider function
626 // "CertDllVerifyRevocation" in cryptnet.dll)
627 BOOL WINAPI
628 CertDllVerifyRevocationWithCRLSet(DWORD encoding_type,
629 DWORD revocation_type,
630 DWORD num_contexts,
631 void* rgpvContext[],
632 DWORD flags,
633 PCERT_REVOCATION_PARA revocation_params,
634 PCERT_REVOCATION_STATUS revocation_status);
635
636 // Helper class that installs the CRLSet-based Revocation Provider as the
637 // default revocation provider. Because it is installed as a function address
638 // (meaning only scoped to the process, and not stored in the registry), it
639 // will be used before any registry-based providers, including Microsoft's
640 // default provider.
641 class RevocationInjector {
642 public:
643 CRLSet* GetCRLSet() { return thread_local_crlset.Get(); }
644
645 void SetCRLSet(CRLSet* crl_set) { thread_local_crlset.Set(crl_set); }
646
647 private:
648 friend struct base::DefaultLazyInstanceTraits<RevocationInjector>;
649
650 RevocationInjector() {
651 const CRYPT_OID_FUNC_ENTRY kInterceptFunction[] = {
652 {CRYPT_DEFAULT_OID, &CertDllVerifyRevocationWithCRLSet},
653 };
654 BOOL ok = CryptInstallOIDFunctionAddress(
655 NULL, X509_ASN_ENCODING, CRYPT_OID_VERIFY_REVOCATION_FUNC,
656 arraysize(kInterceptFunction), kInterceptFunction,
657 CRYPT_INSTALL_OID_FUNC_BEFORE_FLAG);
658 DCHECK(ok);
659 }
660
661 ~RevocationInjector() {}
662
663 // As the revocation parameters passed to CertVerifyProc::VerifyInternal
664 // cannot be officially smuggled to the Revocation Provider
665 base::ThreadLocalPointer<CRLSet> thread_local_crlset;
666 };
667
668 // Leaky, as CertVerifyProc workers are themselves leaky.
669 base::LazyInstance<RevocationInjector>::Leaky g_revocation_injector =
670 LAZY_INSTANCE_INITIALIZER;
671
672 BOOL WINAPI
673 CertDllVerifyRevocationWithCRLSet(DWORD encoding_type,
674 DWORD revocation_type,
675 DWORD num_contexts,
676 void* rgpvContext[],
677 DWORD flags,
678 PCERT_REVOCATION_PARA revocation_params,
679 PCERT_REVOCATION_STATUS revocation_status) {
680 PCERT_CONTEXT* cert_contexts = reinterpret_cast<PCERT_CONTEXT*>(rgpvContext);
davidben 2016/01/21 02:37:39 Nit: newline here? Comments that aren't preceded b
Ryan Sleevi 2016/01/21 02:54:05 Agreed.
681 // The dummy CRLSet provider never returns that something is affirmatively
682 // *un*revoked, as this would disable other revocation providers from being
683 // checked for this certificate (much like an OCSP "Good" status would).
684 // Instead, it merely indicates that insufficient information existed to
685 // determine if the certificate was revoked (in the good case), or that a cert
686 // is affirmatively revoked in the event it appears within the CRLSet.
687 // Because of this, set up some basic bookkeeping for the results.
688 CHECK(revocation_status);
689 revocation_status->dwIndex = 0;
690 revocation_status->dwError = static_cast<DWORD>(CRYPT_E_NO_REVOCATION_CHECK);
691 revocation_status->dwReason = 0;
692
693 if (num_contexts == 0 || !cert_contexts[0]) {
694 SetLastError(static_cast<DWORD>(E_INVALIDARG));
695 return FALSE;
696 }
697
698 if ((GET_CERT_ENCODING_TYPE(encoding_type) != X509_ASN_ENCODING) ||
davidben 2016/01/21 02:37:39 Style nit: Unnecessary parens. (Alternatively, mis
699 revocation_type != CERT_CONTEXT_REVOCATION_TYPE) {
700 SetLastError(static_cast<DWORD>(CRYPT_E_NO_REVOCATION_CHECK));
701 return FALSE;
702 }
703
704 // No revocation checking possible if there is no associated
705 // CRLSet.
706 CRLSet* crl_set = g_revocation_injector.Get().GetCRLSet();
707 if (!crl_set)
708 return FALSE;
709
710 // |revocation_params| is an optional structure; to make life simple and avoid
711 // the need to constantly check whether or not it was supplied, create a local
712 // copy. If the caller didn't supply anything, it will be empty; otherwise,
713 // it will be (non-owning) copies of the caller's original params.
714 CERT_REVOCATION_PARA local_params;
715 memset(&local_params, 0, sizeof(local_params));
716 if (revocation_params) {
717 DWORD bytes_to_copy = std::min(revocation_params->cbSize,
718 static_cast<DWORD>(sizeof(local_params)));
719 memcpy(&local_params, revocation_params, bytes_to_copy);
720 }
721 local_params.cbSize = sizeof(local_params);
722
723 PCERT_CONTEXT subject_cert = cert_contexts[0];
724
725 if ((flags & CERT_VERIFY_REV_CHAIN_FLAG) && num_contexts > 1) {
Ryan Sleevi 2016/01/15 01:12:55 I just went ahead and did the iterative code, alth
726 // Verifying a chain; first verify from the last certificate in the
727 // chain to the first, and then leave the last certificate (which
728 // is presumably self-issued, although it may simply be a trust
729 // anchor) as the |subject_cert| in order to scan for more
730 // revocations.
731 std::string issuer_hash;
732 PCCERT_CONTEXT issuer_cert = nullptr;
733 for (DWORD i = num_contexts; i > 0; --i) {
davidben 2016/01/21 02:37:39 Optional: Another option is to write this as: f
734 subject_cert = cert_contexts[i - 1];
735 if (!subject_cert) {
davidben 2016/01/21 02:37:39 Why does this function check for NULL while CheckC
Ryan Sleevi 2016/01/21 02:54:04 Yes. CertVerifyRevocation is caller-exposed, and n
736 SetLastError(static_cast<DWORD>(E_INVALIDARG));
737 return FALSE;
738 }
739 CRLSetResult result = CheckRevocationWithCRLSet(
740 crl_set, subject_cert, issuer_cert, &issuer_hash);
741 if (result == kCRLSetRevoked) {
742 revocation_status->dwIndex = i - 1;
743 revocation_status->dwError = static_cast<DWORD>(CRYPT_E_REVOKED);
744 revocation_status->dwReason = CRL_REASON_UNSPECIFIED;
745 SetLastError(revocation_status->dwError);
746 return FALSE;
747 }
748 issuer_cert = subject_cert;
749 }
750 // Verified all certificates from the trust anchor to the leaf, and none
751 // were explicitly revoked. Now do a second pass to attempt to determine
752 // the issuer for cert_contexts[num_contexts - 1], so that the
753 // Issuer SPKI+Serial can be checked for that certificate.
754 //
755 // This code intentionally ignores the flag
756 subject_cert = cert_contexts[num_contexts - 1];
757 // Reset local_params.pIssuerCert, since it would contain the issuer
758 // for cert_contexts[0].
759 local_params.pIssuerCert = nullptr;
760 // Fixup the revocation index to point to this cert (in the event it is
761 // revoked). If it isn't revoked, this will be done undone later.
762 revocation_status->dwIndex = num_contexts - 1;
763 }
764
765 // Determine the issuer cert for the incoming cert
766 ScopedPCCERT_CONTEXT issuer_cert;
767 if (local_params.pIssuerCert &&
davidben 2016/01/21 02:37:39 [I love how they don't say anything useful about t
768 CryptVerifyCertificateSignatureEx(
769 NULL, subject_cert->dwCertEncodingType,
770 CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT, subject_cert,
771 CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT,
772 const_cast<PCERT_CONTEXT>(local_params.pIssuerCert), 0, nullptr)) {
773 // Caller has already supplied the issuer cert via the revocation params;
774 // just use that.
775 issuer_cert.reset(
776 CertDuplicateCertificateContext(local_params.pIssuerCert));
777 } else if (CertCompareCertificateName(subject_cert->dwCertEncodingType,
778 &subject_cert->pCertInfo->Subject,
779 &subject_cert->pCertInfo->Issuer) &&
780 CryptVerifyCertificateSignatureEx(
781 NULL, subject_cert->dwCertEncodingType,
782 CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT, subject_cert,
783 CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, subject_cert, 0,
784 nullptr)) {
785 // Certificate is self-signed; use it as its own issuer.
786 issuer_cert.reset(CertDuplicateCertificateContext(subject_cert));
787 } else {
788 // Scan the caller-supplied stores first, to try and find the issuer cert.
789 for (DWORD i = 0; i < local_params.cCertStore && !issuer_cert; ++i) {
790 PCCERT_CONTEXT previous_cert = nullptr;
791 for (;;) {
792 DWORD store_search_flags = CERT_STORE_SIGNATURE_FLAG;
793 previous_cert = CertGetIssuerCertificateFromStore(
794 local_params.rgCertStore[i], subject_cert, previous_cert,
795 &store_search_flags);
796 if (!previous_cert)
797 break;
798 // If a cert is found and meets the criteria, the flag will be reset to
799 // zero. Thus NOT having the bit set is equivalent to having found a
800 // matching certificate.
801 if (!(store_search_flags & CERT_STORE_SIGNATURE_FLAG)) {
802 // No need to dupe; reference is held.
803 issuer_cert.reset(previous_cert);
804 break;
805 }
806 }
807 if (issuer_cert)
808 break;
809 if (GetLastError() == CRYPT_E_SELF_SIGNED) {
810 issuer_cert.reset(CertDuplicateCertificateContext(subject_cert));
811 break;
812 }
813 }
814
815 // At this point, the Microsoft provider opens up the "CA", "Root", and
816 // "SPC" stores to search for the issuer certificate, if not found in the
817 // caller-supplied stores. It is unclear whether that is necessary here.
818 }
819
820 if (!issuer_cert) {
821 // Rather than return CRYPT_E_NO_REVOCATION_CHECK (indicating everything
822 // is fine to try the next provider), return CRYPT_E_REVOCATION_OFFLINE.
823 // This propogates up to the caller as an error while checking revocation,
824 // which is the desired intent if there are certificates that cannot
825 // be checked.
826 revocation_status->dwIndex = 0;
827 revocation_status->dwError = static_cast<DWORD>(CRYPT_E_REVOCATION_OFFLINE);
828 SetLastError(revocation_status->dwError);
829 return FALSE;
830 }
831
832 std::string unused;
833 CRLSetResult result = CheckRevocationWithCRLSet(crl_set, subject_cert,
834 issuer_cert.get(), &unused);
835 if (result == kCRLSetRevoked) {
836 revocation_status->dwError = static_cast<DWORD>(CRYPT_E_REVOKED);
837 revocation_status->dwReason = CRL_REASON_UNSPECIFIED;
838 SetLastError(revocation_status->dwError);
839 return FALSE;
840 }
841
842 // The result is ALWAYS FALSE in order to allow the next revocation provider
843 // a chance to examine. The only difference is whether or not an error is
844 // indicated via dwError (and SetLastError()).
845 // Reset the error index so that Windows does not believe this code has
846 // examined the entire chain and found no issues until the last cert (thus
847 // skipping other revocation providers).
848 revocation_status->dwIndex = 0;
849 return FALSE;
850 }
851
852 class ScopedThreadLocalCRLSet {
853 public:
854 explicit ScopedThreadLocalCRLSet(CRLSet* crl_set) {
855 g_revocation_injector.Get().SetCRLSet(crl_set);
856 }
857 ~ScopedThreadLocalCRLSet() { g_revocation_injector.Get().SetCRLSet(nullptr); }
858 };
859
554 } // namespace 860 } // namespace
555 861
556 CertVerifyProcWin::CertVerifyProcWin() {} 862 CertVerifyProcWin::CertVerifyProcWin() {}
557 863
558 CertVerifyProcWin::~CertVerifyProcWin() {} 864 CertVerifyProcWin::~CertVerifyProcWin() {}
559 865
560 bool CertVerifyProcWin::SupportsAdditionalTrustAnchors() const { 866 bool CertVerifyProcWin::SupportsAdditionalTrustAnchors() const {
561 return false; 867 return false;
562 } 868 }
563 869
564 bool CertVerifyProcWin::SupportsOCSPStapling() const { 870 bool CertVerifyProcWin::SupportsOCSPStapling() const {
565 // CERT_OCSP_RESPONSE_PROP_ID is only implemented on Vista+, but it can be 871 // 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 872 // 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 873 // 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 874 // XP clients who will request it but be unable to use it, but this is an
569 // acceptable trade-off for simplicity of implementation. 875 // acceptable trade-off for simplicity of implementation.
570 return true; 876 return true;
571 } 877 }
572 878
573 int CertVerifyProcWin::VerifyInternal( 879 int CertVerifyProcWin::VerifyInternal(
574 X509Certificate* cert, 880 X509Certificate* cert,
575 const std::string& hostname, 881 const std::string& hostname,
576 const std::string& ocsp_response, 882 const std::string& ocsp_response,
577 int flags, 883 int flags,
578 CRLSet* crl_set, 884 CRLSet* crl_set,
579 const CertificateList& additional_trust_anchors, 885 const CertificateList& additional_trust_anchors,
580 CertVerifyResult* verify_result) { 886 CertVerifyResult* verify_result) {
887 // Ensure the Revocation Provider has been installed and configured for this
888 // CRLSet.
889 ScopedThreadLocalCRLSet thread_local_crlset(crl_set);
890
581 PCCERT_CONTEXT cert_handle = cert->os_cert_handle(); 891 PCCERT_CONTEXT cert_handle = cert->os_cert_handle();
582 if (!cert_handle) 892 if (!cert_handle)
583 return ERR_UNEXPECTED; 893 return ERR_UNEXPECTED;
584 894
585 // Build and validate certificate chain. 895 // Build and validate certificate chain.
586 CERT_CHAIN_PARA chain_para; 896 CERT_CHAIN_PARA chain_para;
587 memset(&chain_para, 0, sizeof(chain_para)); 897 memset(&chain_para, 0, sizeof(chain_para));
588 chain_para.cbSize = sizeof(chain_para); 898 chain_para.cbSize = sizeof(chain_para);
589 // ExtendedKeyUsage. 899 // ExtendedKeyUsage.
590 // We still need to request szOID_SERVER_GATED_CRYPTO and szOID_SGC_NETSCAPE 900 // 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; 924 chain_para.RequestedIssuancePolicy.dwType = USAGE_MATCH_TYPE_AND;
615 chain_para.RequestedIssuancePolicy.Usage.cUsageIdentifier = 1; 925 chain_para.RequestedIssuancePolicy.Usage.cUsageIdentifier = 1;
616 chain_para.RequestedIssuancePolicy.Usage.rgpszUsageIdentifier = 926 chain_para.RequestedIssuancePolicy.Usage.rgpszUsageIdentifier =
617 &ev_policy_oid; 927 &ev_policy_oid;
618 break; 928 break;
619 } 929 }
620 } 930 }
621 } 931 }
622 } 932 }
623 933
624 // We can set CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS to get more chains. 934 // Revocation checking is always enabled, in order to enable CRLSets to be
625 DWORD chain_flags = CERT_CHAIN_CACHE_END_CERT | 935 // evaluated as part of a revocation provider. However, when the caller did
626 CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; 936 // not explicitly request revocation checking (which is to say, online
937 // revocation checking), then only enable cached results. This disables OCSP
938 // and CRL fetching, but still allows the revocation provider to be called.
939 // Note: The root cert is also checked for revocation status, so that CRLSets
940 // will cover revoked SPKIs.
941 DWORD chain_flags = CERT_CHAIN_REVOCATION_CHECK_CHAIN;
627 bool rev_checking_enabled = 942 bool rev_checking_enabled =
628 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED); 943 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED);
629
630 if (rev_checking_enabled) { 944 if (rev_checking_enabled) {
631 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; 945 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
632 } else { 946 } else {
633 chain_flags |= CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY; 947 chain_flags |= CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY;
634 } 948 }
635 949
636 // For non-test scenarios, use the default HCERTCHAINENGINE, NULL, which 950 // By default, use the default HCERTCHAINENGINE (aka HCCE_CURRENT_USER). When
637 // corresponds to HCCE_CURRENT_USER and is is initialized as needed by 951 // running tests, use a dynamic HCERTCHAINENGINE. All of the status and cache
638 // crypt32. However, when testing, it is necessary to create a new 952 // of verified certificates and chains is tied to the HCERTCHAINENGINE. As
639 // HCERTCHAINENGINE and use that instead. This is because each 953 // each invocation may have changed the set of known roots, invalid the cache
640 // HCERTCHAINENGINE maintains a cache of information about certificates 954 // between runs.
641 // encountered, and each test run may modify the trust status of a 955 //
642 // certificate. 956 // This is not the most efficient means of doing so; it's possible to mark the
957 // Root store used by TestRootCerts as changed, via CertControlStore with the
958 // CERT_STORE_CTRL_NOTIFY_CHANGE / CERT_STORE_CTRL_RESYNC, but that's more
959 // complexity for what is test-only code.
643 ScopedHCERTCHAINENGINE chain_engine(NULL); 960 ScopedHCERTCHAINENGINE chain_engine(NULL);
644 if (TestRootCerts::HasInstance()) 961 if (TestRootCerts::HasInstance())
645 chain_engine.reset(TestRootCerts::GetInstance()->GetChainEngine()); 962 chain_engine.reset(TestRootCerts::GetInstance()->GetChainEngine());
646 963
647 ScopedPCCERT_CONTEXT cert_list(cert->CreateOSCertChainForCert()); 964 ScopedPCCERT_CONTEXT cert_list(cert->CreateOSCertChainForCert());
648 965
966 // Add stapled OCSP response data, which will be preferred over online checks
967 // and used when in cache-only mode.
649 if (!ocsp_response.empty()) { 968 if (!ocsp_response.empty()) {
650 // Attach the OCSP response to the chain.
651 CRYPT_DATA_BLOB ocsp_response_blob; 969 CRYPT_DATA_BLOB ocsp_response_blob;
652 ocsp_response_blob.cbData = ocsp_response.size(); 970 ocsp_response_blob.cbData = ocsp_response.size();
653 ocsp_response_blob.pbData = 971 ocsp_response_blob.pbData =
654 reinterpret_cast<BYTE*>(const_cast<char*>(ocsp_response.data())); 972 reinterpret_cast<BYTE*>(const_cast<char*>(ocsp_response.data()));
655 CertSetCertificateContextProperty( 973 CertSetCertificateContextProperty(
656 cert_list.get(), CERT_OCSP_RESPONSE_PROP_ID, 974 cert_list.get(), CERT_OCSP_RESPONSE_PROP_ID,
657 CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG, &ocsp_response_blob); 975 CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG, &ocsp_response_blob);
658 } 976 }
659 977
660 PCCERT_CHAIN_CONTEXT chain_context; 978 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( 979 if (!CertGetCertificateChain(
665 chain_engine, 980 chain_engine,
666 cert_list.get(), 981 cert_list.get(),
667 NULL, // current system time 982 NULL, // current system time
668 cert_list->hCertStore, 983 cert_list->hCertStore,
669 &chain_para, 984 &chain_para,
670 chain_flags, 985 chain_flags,
671 NULL, // reserved 986 NULL, // reserved
672 &chain_context)) { 987 &chain_context)) {
673 verify_result->cert_status |= CERT_STATUS_INVALID; 988 verify_result->cert_status |= CERT_STATUS_INVALID;
674 return MapSecurityError(GetLastError()); 989 return MapSecurityError(GetLastError());
675 } 990 }
676 991
992 // Perform a second check with CRLSets. Although the Revocation Provider
993 // should have prevented invalid paths from being built, the behaviour and
994 // timing of how a Revocation Provider is invoked is not well documented. This
995 // is just defense in depth.
677 CRLSetResult crl_set_result = kCRLSetUnknown; 996 CRLSetResult crl_set_result = kCRLSetUnknown;
678 if (crl_set) 997 if (crl_set)
679 crl_set_result = CheckRevocationWithCRLSet(chain_context, crl_set); 998 crl_set_result = CheckChainRevocationWithCRLSet(chain_context, crl_set);
680 999
681 if (crl_set_result == kCRLSetRevoked) { 1000 if (crl_set_result == kCRLSetRevoked) {
682 verify_result->cert_status |= CERT_STATUS_REVOKED; 1001 verify_result->cert_status |= CERT_STATUS_REVOKED;
683 } else if (crl_set_result == kCRLSetUnknown && 1002 } else if (crl_set_result == kCRLSetUnknown &&
684 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY) && 1003 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY) &&
685 !rev_checking_enabled && 1004 !rev_checking_enabled &&
686 ev_policy_oid != NULL) { 1005 ev_policy_oid != NULL) {
687 // We don't have fresh information about this chain from the CRLSet and 1006 // 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. 1007 // it's probably an EV certificate. Retry with online revocation checking.
689 rev_checking_enabled = true; 1008 rev_checking_enabled = true;
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after
827 return MapCertStatusToNetError(verify_result->cert_status); 1146 return MapCertStatusToNetError(verify_result->cert_status);
828 1147
829 if (ev_policy_oid && 1148 if (ev_policy_oid &&
830 CheckEV(chain_context, rev_checking_enabled, ev_policy_oid)) { 1149 CheckEV(chain_context, rev_checking_enabled, ev_policy_oid)) {
831 verify_result->cert_status |= CERT_STATUS_IS_EV; 1150 verify_result->cert_status |= CERT_STATUS_IS_EV;
832 } 1151 }
833 return OK; 1152 return OK;
834 } 1153 }
835 1154
836 } // namespace net 1155 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698