Chromium Code Reviews| OLD | NEW |
|---|---|
| 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/base/cert_verify_proc_nss.h" | 5 #include "net/base/cert_verify_proc_nss.h" |
| 6 | 6 |
| 7 #include <cert.h> | 7 #include <cert.h> |
| 8 #include <nss.h> | 8 #include <nss.h> |
| 9 #include <prerror.h> | 9 #include <prerror.h> |
| 10 #include <secerr.h> | 10 #include <secerr.h> |
| (...skipping 558 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 569 od.offset = SEC_OID_UNKNOWN; | 569 od.offset = SEC_OID_UNKNOWN; |
| 570 // NSS doesn't allow us to pass an empty description, so I use a hardcoded, | 570 // NSS doesn't allow us to pass an empty description, so I use a hardcoded, |
| 571 // default description here. The description doesn't need to be unique for | 571 // default description here. The description doesn't need to be unique for |
| 572 // each OID. | 572 // each OID. |
| 573 od.desc = "a certificate policy"; | 573 od.desc = "a certificate policy"; |
| 574 od.mechanism = CKM_INVALID_MECHANISM; | 574 od.mechanism = CKM_INVALID_MECHANISM; |
| 575 od.supportedExtension = INVALID_CERT_EXTENSION; | 575 od.supportedExtension = INVALID_CERT_EXTENSION; |
| 576 return SECOID_AddEntry(&od); | 576 return SECOID_AddEntry(&od); |
| 577 } | 577 } |
| 578 | 578 |
| 579 bool CheckCertPolicies(X509Certificate::OSCertHandle cert_handle, | |
| 580 SECOidTag ev_policy_tag) { | |
| 581 CERTCertificatePolicies* policies = DecodeCertPolicies(cert_handle); | |
| 582 if (!policies) { | |
| 583 LOG(ERROR) << "Cert has no policies extension or extension couldn't be " | |
| 584 "decoded."; | |
| 585 return false; | |
| 586 } | |
| 587 ScopedCERTCertificatePolicies scoped_policies(policies); | |
| 588 CERTPolicyInfo** policy_infos = policies->policyInfos; | |
| 589 while (*policy_infos != NULL) { | |
| 590 CERTPolicyInfo* policy_info = *policy_infos++; | |
| 591 SECOidTag oid_tag = policy_info->oid; | |
| 592 if (oid_tag == SEC_OID_UNKNOWN) | |
| 593 continue; | |
| 594 if (oid_tag == ev_policy_tag) | |
| 595 return true; | |
| 596 } | |
| 597 return false; | |
| 598 } | |
| 599 | |
| 600 SHA1Fingerprint CertPublicKeyHash(CERTCertificate* cert) { | 579 SHA1Fingerprint CertPublicKeyHash(CERTCertificate* cert) { |
| 601 SHA1Fingerprint hash; | 580 SHA1Fingerprint hash; |
| 602 SECStatus rv = HASH_HashBuf(HASH_AlgSHA1, hash.data, | 581 SECStatus rv = HASH_HashBuf(HASH_AlgSHA1, hash.data, |
| 603 cert->derPublicKey.data, cert->derPublicKey.len); | 582 cert->derPublicKey.data, cert->derPublicKey.len); |
| 604 DCHECK_EQ(rv, SECSuccess); | 583 DCHECK_EQ(rv, SECSuccess); |
| 605 return hash; | 584 return hash; |
| 606 } | 585 } |
| 607 | 586 |
| 608 void AppendPublicKeyHashes(CERTCertList* cert_list, | 587 void AppendPublicKeyHashes(CERTCertList* cert_list, |
| 609 CERTCertificate* root_cert, | 588 CERTCertificate* root_cert, |
| 610 std::vector<SHA1Fingerprint>* hashes) { | 589 std::vector<SHA1Fingerprint>* hashes) { |
| 611 for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list); | 590 for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list); |
| 612 !CERT_LIST_END(node, cert_list); | 591 !CERT_LIST_END(node, cert_list); |
| 613 node = CERT_LIST_NEXT(node)) { | 592 node = CERT_LIST_NEXT(node)) { |
| 614 hashes->push_back(CertPublicKeyHash(node->cert)); | 593 hashes->push_back(CertPublicKeyHash(node->cert)); |
| 615 } | 594 } |
| 616 if (root_cert) | 595 if (root_cert) |
| 617 hashes->push_back(CertPublicKeyHash(root_cert)); | 596 hashes->push_back(CertPublicKeyHash(root_cert)); |
| 618 } | 597 } |
| 619 | 598 |
| 599 // Returns true if |cert_handle| contains a policy OID that is an EV policy | |
| 600 // OID according to |metadata|, storing the resulting policy OID in | |
| 601 // |*ev_policy_oid|. A true return is not sufficient to establish that a | |
| 602 // certificate is EV, but a false return is sufficient to establish the | |
| 603 // certificate cannot be EV. | |
| 604 bool IsEVCandidate(EVRootCAMetadata* metadata, | |
|
agl
2012/08/15 22:57:53
Is the assumption that there'll only be one EV OID
Ryan Sleevi
2012/08/15 23:04:58
That's what I'm not sure about. That's certainly t
| |
| 605 CERTCertificate* cert_handle, | |
| 606 SECOidTag* ev_policy_oid) { | |
| 607 DCHECK(cert_handle); | |
| 608 ScopedCERTCertificatePolicies policies(DecodeCertPolicies(cert_handle)); | |
| 609 if (!policies.get()) | |
| 610 return false; | |
| 611 | |
| 612 CERTPolicyInfo** policy_infos = policies->policyInfos; | |
| 613 while (*policy_infos != NULL) { | |
| 614 CERTPolicyInfo* policy_info = *policy_infos++; | |
| 615 // If the Policy OID is unknown, that implicitly means it has not been | |
| 616 // registered as an EV policy. | |
| 617 if (policy_info->oid == SEC_OID_UNKNOWN) | |
| 618 continue; | |
| 619 if (metadata->IsEVPolicyOID(policy_info->oid)) { | |
| 620 *ev_policy_oid = policy_info->oid; | |
| 621 return true; | |
| 622 } | |
| 623 } | |
| 624 | |
| 625 return false; | |
| 626 } | |
| 627 | |
| 620 // Studied Mozilla's code (esp. security/manager/ssl/src/nsIdentityChecking.cpp | 628 // Studied Mozilla's code (esp. security/manager/ssl/src/nsIdentityChecking.cpp |
| 621 // and nsNSSCertHelper.cpp) to learn how to verify EV certificate. | 629 // and nsNSSCertHelper.cpp) to learn how to verify EV certificate. |
| 622 // TODO(wtc): A possible optimization is that we get the trust anchor from | 630 // TODO(wtc): A possible optimization is that we get the trust anchor from |
| 623 // the first PKIXVerifyCert call. We look up the EV policy for the trust | 631 // the first PKIXVerifyCert call. We look up the EV policy for the trust |
| 624 // anchor. If the trust anchor has no EV policy, we know the cert isn't EV. | 632 // anchor. If the trust anchor has no EV policy, we know the cert isn't EV. |
| 625 // Otherwise, we pass just that EV policy (as opposed to all the EV policies) | 633 // Otherwise, we pass just that EV policy (as opposed to all the EV policies) |
| 626 // to the second PKIXVerifyCert call. | 634 // to the second PKIXVerifyCert call. |
| 627 bool VerifyEV(CERTCertificate* cert_handle, int flags, CRLSet* crl_set) { | 635 bool VerifyEV(CERTCertificate* cert_handle, int flags, CRLSet* crl_set) { |
| 628 EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance(); | 636 EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance(); |
| 629 | 637 |
| 638 SECOidTag ev_policy_oid = SEC_OID_UNKNOWN; | |
| 639 bool is_ev_candidate = IsEVCandidate(metadata, cert_handle, &ev_policy_oid); | |
| 640 if (!is_ev_candidate) | |
| 641 return false; | |
| 642 | |
| 630 CERTValOutParam cvout[3]; | 643 CERTValOutParam cvout[3]; |
| 631 int cvout_index = 0; | 644 int cvout_index = 0; |
| 632 cvout[cvout_index].type = cert_po_certList; | 645 cvout[cvout_index].type = cert_po_certList; |
| 633 cvout[cvout_index].value.pointer.chain = NULL; | 646 cvout[cvout_index].value.pointer.chain = NULL; |
| 634 int cvout_cert_list_index = cvout_index; | 647 int cvout_cert_list_index = cvout_index; |
| 635 cvout_index++; | 648 cvout_index++; |
| 636 cvout[cvout_index].type = cert_po_trustAnchor; | 649 cvout[cvout_index].type = cert_po_trustAnchor; |
| 637 cvout[cvout_index].value.pointer.cert = NULL; | 650 cvout[cvout_index].value.pointer.cert = NULL; |
| 638 int cvout_trust_anchor_index = cvout_index; | 651 int cvout_trust_anchor_index = cvout_index; |
| 639 cvout_index++; | 652 cvout_index++; |
| 640 cvout[cvout_index].type = cert_po_end; | 653 cvout[cvout_index].type = cert_po_end; |
| 641 ScopedCERTValOutParam scoped_cvout(cvout); | 654 ScopedCERTValOutParam scoped_cvout(cvout); |
| 642 | 655 |
| 656 bool rev_checking_enabled = | |
| 657 (flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED) || | |
| 658 (flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED_EV_ONLY); | |
| 659 | |
| 643 SECStatus status = PKIXVerifyCert( | 660 SECStatus status = PKIXVerifyCert( |
| 644 cert_handle, | 661 cert_handle, |
| 645 flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED, | 662 rev_checking_enabled, |
| 646 flags & X509Certificate::VERIFY_CERT_IO_ENABLED, | 663 flags & X509Certificate::VERIFY_CERT_IO_ENABLED, |
| 647 metadata->GetPolicyOIDs(), | 664 &ev_policy_oid, |
| 648 metadata->NumPolicyOIDs(), | 665 1, |
| 649 cvout); | 666 cvout); |
| 650 if (status != SECSuccess) | 667 if (status != SECSuccess) |
| 651 return false; | 668 return false; |
| 652 | 669 |
| 653 CERTCertificate* root_ca = | 670 CERTCertificate* root_ca = |
| 654 cvout[cvout_trust_anchor_index].value.pointer.cert; | 671 cvout[cvout_trust_anchor_index].value.pointer.cert; |
| 655 if (root_ca == NULL) | 672 if (root_ca == NULL) |
| 656 return false; | 673 return false; |
| 657 | 674 |
| 658 // This second PKIXVerifyCert call could have found a different certification | 675 // This second PKIXVerifyCert call could have found a different certification |
| 659 // path and one or more of the certificates on this new path, that weren't on | 676 // path and one or more of the certificates on this new path, that weren't on |
| 660 // the old path, might have been revoked. | 677 // the old path, might have been revoked. |
| 661 if (crl_set) { | 678 if (crl_set) { |
| 662 CRLSetResult crl_set_result = CheckRevocationWithCRLSet( | 679 CRLSetResult crl_set_result = CheckRevocationWithCRLSet( |
| 663 cvout[cvout_cert_list_index].value.pointer.chain, | 680 cvout[cvout_cert_list_index].value.pointer.chain, |
| 664 cvout[cvout_trust_anchor_index].value.pointer.cert, | 681 cvout[cvout_trust_anchor_index].value.pointer.cert, |
| 665 crl_set); | 682 crl_set); |
| 666 if (crl_set_result == kCRLSetRevoked) | 683 if (crl_set_result == kCRLSetRevoked) |
| 667 return false; | 684 return false; |
| 668 } | 685 } |
| 669 | 686 |
| 670 SHA1Fingerprint fingerprint = | 687 SHA1Fingerprint fingerprint = |
| 671 X509Certificate::CalculateFingerprint(root_ca); | 688 X509Certificate::CalculateFingerprint(root_ca); |
| 672 std::vector<SECOidTag> ev_policy_tags; | 689 return metadata->HasEVPolicyOID(fingerprint, ev_policy_oid); |
| 673 if (!metadata->GetPolicyOIDsForCA(fingerprint, &ev_policy_tags)) | |
| 674 return false; | |
| 675 DCHECK(!ev_policy_tags.empty()); | |
| 676 | |
| 677 for (std::vector<SECOidTag>::const_iterator | |
| 678 i = ev_policy_tags.begin(); i != ev_policy_tags.end(); ++i) { | |
| 679 if (CheckCertPolicies(cert_handle, *i)) | |
| 680 return true; | |
| 681 } | |
| 682 return false; | |
| 683 } | 690 } |
| 684 | 691 |
| 685 } // namespace | 692 } // namespace |
| 686 | 693 |
| 687 CertVerifyProcNSS::CertVerifyProcNSS() {} | 694 CertVerifyProcNSS::CertVerifyProcNSS() {} |
| 688 | 695 |
| 689 CertVerifyProcNSS::~CertVerifyProcNSS() {} | 696 CertVerifyProcNSS::~CertVerifyProcNSS() {} |
| 690 | 697 |
| 691 int CertVerifyProcNSS::VerifyInternal(X509Certificate* cert, | 698 int CertVerifyProcNSS::VerifyInternal(X509Certificate* cert, |
| 692 const std::string& hostname, | 699 const std::string& hostname, |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 766 AppendPublicKeyHashes(cvout[cvout_cert_list_index].value.pointer.chain, | 773 AppendPublicKeyHashes(cvout[cvout_cert_list_index].value.pointer.chain, |
| 767 cvout[cvout_trust_anchor_index].value.pointer.cert, | 774 cvout[cvout_trust_anchor_index].value.pointer.cert, |
| 768 &verify_result->public_key_hashes); | 775 &verify_result->public_key_hashes); |
| 769 | 776 |
| 770 verify_result->is_issued_by_known_root = | 777 verify_result->is_issued_by_known_root = |
| 771 IsKnownRoot(cvout[cvout_trust_anchor_index].value.pointer.cert); | 778 IsKnownRoot(cvout[cvout_trust_anchor_index].value.pointer.cert); |
| 772 | 779 |
| 773 if ((flags & X509Certificate::VERIFY_EV_CERT) && | 780 if ((flags & X509Certificate::VERIFY_EV_CERT) && |
| 774 VerifyEV(cert_handle, flags, crl_set)) { | 781 VerifyEV(cert_handle, flags, crl_set)) { |
| 775 verify_result->cert_status |= CERT_STATUS_IS_EV; | 782 verify_result->cert_status |= CERT_STATUS_IS_EV; |
| 783 if (cert_io_enabled && | |
| 784 (flags & (X509Certificate::VERIFY_REV_CHECKING_ENABLED | | |
| 785 X509Certificate::VERIFY_REV_CHECKING_ENABLED_EV_ONLY)) { | |
| 786 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; | |
| 787 } | |
| 776 } | 788 } |
| 777 | 789 |
| 778 return OK; | 790 return OK; |
| 779 } | 791 } |
| 780 | 792 |
| 781 } // namespace net | 793 } // namespace net |
| OLD | NEW |