| 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 10 matching lines...) Expand all Loading... |
| 21 #include "net/base/crl_set.h" | 21 #include "net/base/crl_set.h" |
| 22 #include "net/base/ev_root_ca_metadata.h" | 22 #include "net/base/ev_root_ca_metadata.h" |
| 23 #include "net/base/net_errors.h" | 23 #include "net/base/net_errors.h" |
| 24 #include "net/base/x509_certificate.h" | 24 #include "net/base/x509_certificate.h" |
| 25 #include "net/base/x509_util_nss.h" | 25 #include "net/base/x509_util_nss.h" |
| 26 | 26 |
| 27 namespace net { | 27 namespace net { |
| 28 | 28 |
| 29 namespace { | 29 namespace { |
| 30 | 30 |
| 31 class ScopedCERTCertificatePolicies { | 31 typedef scoped_ptr_malloc< |
| 32 public: | 32 CERTCertificatePolicies, |
| 33 explicit ScopedCERTCertificatePolicies(CERTCertificatePolicies* policies) | 33 crypto::NSSDestroyer<CERTCertificatePolicies, |
| 34 : policies_(policies) {} | 34 CERT_DestroyCertificatePoliciesExtension> > |
| 35 | 35 ScopedCERTCertificatePolicies; |
| 36 ~ScopedCERTCertificatePolicies() { | |
| 37 if (policies_) | |
| 38 CERT_DestroyCertificatePoliciesExtension(policies_); | |
| 39 } | |
| 40 | |
| 41 private: | |
| 42 CERTCertificatePolicies* policies_; | |
| 43 | |
| 44 DISALLOW_COPY_AND_ASSIGN(ScopedCERTCertificatePolicies); | |
| 45 }; | |
| 46 | 36 |
| 47 // ScopedCERTValOutParam manages destruction of values in the CERTValOutParam | 37 // ScopedCERTValOutParam manages destruction of values in the CERTValOutParam |
| 48 // array that cvout points to. cvout must be initialized as passed to | 38 // array that cvout points to. cvout must be initialized as passed to |
| 49 // CERT_PKIXVerifyCert, so that the array must be terminated with | 39 // CERT_PKIXVerifyCert, so that the array must be terminated with |
| 50 // cert_po_end type. | 40 // cert_po_end type. |
| 51 // When it goes out of scope, it destroys values of cert_po_trustAnchor | 41 // When it goes out of scope, it destroys values of cert_po_trustAnchor |
| 52 // and cert_po_certList types, but doesn't release the array itself. | 42 // and cert_po_certList types, but doesn't release the array itself. |
| 53 class ScopedCERTValOutParam { | 43 class ScopedCERTValOutParam { |
| 54 public: | 44 public: |
| 55 explicit ScopedCERTValOutParam(CERTValOutParam* cvout) | 45 explicit ScopedCERTValOutParam(CERTValOutParam* cvout) |
| (...skipping 488 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 544 CERTCertificatePolicies* policies = | 534 CERTCertificatePolicies* policies = |
| 545 CERT_DecodeCertificatePoliciesExtension(&policy_ext); | 535 CERT_DecodeCertificatePoliciesExtension(&policy_ext); |
| 546 SECITEM_FreeItem(&policy_ext, PR_FALSE); | 536 SECITEM_FreeItem(&policy_ext, PR_FALSE); |
| 547 return policies; | 537 return policies; |
| 548 } | 538 } |
| 549 | 539 |
| 550 // Returns the OID tag for the first certificate policy in the certificate's | 540 // Returns the OID tag for the first certificate policy in the certificate's |
| 551 // certificatePolicies extension. Returns SEC_OID_UNKNOWN if the certificate | 541 // certificatePolicies extension. Returns SEC_OID_UNKNOWN if the certificate |
| 552 // has no certificate policy. | 542 // has no certificate policy. |
| 553 SECOidTag GetFirstCertPolicy(X509Certificate::OSCertHandle cert_handle) { | 543 SECOidTag GetFirstCertPolicy(X509Certificate::OSCertHandle cert_handle) { |
| 554 CERTCertificatePolicies* policies = DecodeCertPolicies(cert_handle); | 544 ScopedCERTCertificatePolicies policies(DecodeCertPolicies(cert_handle)); |
| 555 if (!policies) | 545 if (!policies.get()) |
| 556 return SEC_OID_UNKNOWN; | 546 return SEC_OID_UNKNOWN; |
| 557 ScopedCERTCertificatePolicies scoped_policies(policies); | 547 |
| 558 CERTPolicyInfo* policy_info = policies->policyInfos[0]; | 548 CERTPolicyInfo* policy_info = policies->policyInfos[0]; |
| 559 if (!policy_info) | 549 if (!policy_info) |
| 560 return SEC_OID_UNKNOWN; | 550 return SEC_OID_UNKNOWN; |
| 561 if (policy_info->oid != SEC_OID_UNKNOWN) | 551 if (policy_info->oid != SEC_OID_UNKNOWN) |
| 562 return policy_info->oid; | 552 return policy_info->oid; |
| 563 | 553 |
| 564 // The certificate policy is unknown to NSS. We need to create a dynamic | 554 // The certificate policy is unknown to NSS. We need to create a dynamic |
| 565 // OID tag for the policy. | 555 // OID tag for the policy. |
| 566 SECOidData od; | 556 SECOidData od; |
| 567 od.oid.len = policy_info->policyID.len; | 557 od.oid.len = policy_info->policyID.len; |
| 568 od.oid.data = policy_info->policyID.data; | 558 od.oid.data = policy_info->policyID.data; |
| 569 od.offset = SEC_OID_UNKNOWN; | 559 od.offset = SEC_OID_UNKNOWN; |
| 570 // NSS doesn't allow us to pass an empty description, so I use a hardcoded, | 560 // 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 | 561 // default description here. The description doesn't need to be unique for |
| 572 // each OID. | 562 // each OID. |
| 573 od.desc = "a certificate policy"; | 563 od.desc = "a certificate policy"; |
| 574 od.mechanism = CKM_INVALID_MECHANISM; | 564 od.mechanism = CKM_INVALID_MECHANISM; |
| 575 od.supportedExtension = INVALID_CERT_EXTENSION; | 565 od.supportedExtension = INVALID_CERT_EXTENSION; |
| 576 return SECOID_AddEntry(&od); | 566 return SECOID_AddEntry(&od); |
| 577 } | 567 } |
| 578 | 568 |
| 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) { | 569 SHA1Fingerprint CertPublicKeyHash(CERTCertificate* cert) { |
| 601 SHA1Fingerprint hash; | 570 SHA1Fingerprint hash; |
| 602 SECStatus rv = HASH_HashBuf(HASH_AlgSHA1, hash.data, | 571 SECStatus rv = HASH_HashBuf(HASH_AlgSHA1, hash.data, |
| 603 cert->derPublicKey.data, cert->derPublicKey.len); | 572 cert->derPublicKey.data, cert->derPublicKey.len); |
| 604 DCHECK_EQ(rv, SECSuccess); | 573 DCHECK_EQ(rv, SECSuccess); |
| 605 return hash; | 574 return hash; |
| 606 } | 575 } |
| 607 | 576 |
| 608 void AppendPublicKeyHashes(CERTCertList* cert_list, | 577 void AppendPublicKeyHashes(CERTCertList* cert_list, |
| 609 CERTCertificate* root_cert, | 578 CERTCertificate* root_cert, |
| 610 std::vector<SHA1Fingerprint>* hashes) { | 579 std::vector<SHA1Fingerprint>* hashes) { |
| 611 for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list); | 580 for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list); |
| 612 !CERT_LIST_END(node, cert_list); | 581 !CERT_LIST_END(node, cert_list); |
| 613 node = CERT_LIST_NEXT(node)) { | 582 node = CERT_LIST_NEXT(node)) { |
| 614 hashes->push_back(CertPublicKeyHash(node->cert)); | 583 hashes->push_back(CertPublicKeyHash(node->cert)); |
| 615 } | 584 } |
| 616 if (root_cert) | 585 if (root_cert) |
| 617 hashes->push_back(CertPublicKeyHash(root_cert)); | 586 hashes->push_back(CertPublicKeyHash(root_cert)); |
| 618 } | 587 } |
| 619 | 588 |
| 589 // Returns true if |cert_handle| contains a policy OID that is an EV policy |
| 590 // OID according to |metadata|, storing the resulting policy OID in |
| 591 // |*ev_policy_oid|. A true return is not sufficient to establish that a |
| 592 // certificate is EV, but a false return is sufficient to establish the |
| 593 // certificate cannot be EV. |
| 594 bool IsEVCandidate(EVRootCAMetadata* metadata, |
| 595 CERTCertificate* cert_handle, |
| 596 SECOidTag* ev_policy_oid) { |
| 597 DCHECK(cert_handle); |
| 598 ScopedCERTCertificatePolicies policies(DecodeCertPolicies(cert_handle)); |
| 599 if (!policies.get()) |
| 600 return false; |
| 601 |
| 602 CERTPolicyInfo** policy_infos = policies->policyInfos; |
| 603 while (*policy_infos != NULL) { |
| 604 CERTPolicyInfo* policy_info = *policy_infos++; |
| 605 // If the Policy OID is unknown, that implicitly means it has not been |
| 606 // registered as an EV policy. |
| 607 if (policy_info->oid == SEC_OID_UNKNOWN) |
| 608 continue; |
| 609 if (metadata->IsEVPolicyOID(policy_info->oid)) { |
| 610 *ev_policy_oid = policy_info->oid; |
| 611 return true; |
| 612 } |
| 613 } |
| 614 |
| 615 return false; |
| 616 } |
| 617 |
| 620 // Studied Mozilla's code (esp. security/manager/ssl/src/nsIdentityChecking.cpp | 618 // Studied Mozilla's code (esp. security/manager/ssl/src/nsIdentityChecking.cpp |
| 621 // and nsNSSCertHelper.cpp) to learn how to verify EV certificate. | 619 // and nsNSSCertHelper.cpp) to learn how to verify EV certificate. |
| 622 // TODO(wtc): A possible optimization is that we get the trust anchor from | 620 // 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 | 621 // 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. | 622 // 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) | 623 // Otherwise, we pass just that EV policy (as opposed to all the EV policies) |
| 626 // to the second PKIXVerifyCert call. | 624 // to the second PKIXVerifyCert call. |
| 627 bool VerifyEV(CERTCertificate* cert_handle, int flags, CRLSet* crl_set) { | 625 bool VerifyEV(CERTCertificate* cert_handle, |
| 628 EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance(); | 626 int flags, |
| 629 | 627 CRLSet* crl_set, |
| 628 EVRootCAMetadata* metadata, |
| 629 SECOidTag ev_policy_oid) { |
| 630 CERTValOutParam cvout[3]; | 630 CERTValOutParam cvout[3]; |
| 631 int cvout_index = 0; | 631 int cvout_index = 0; |
| 632 cvout[cvout_index].type = cert_po_certList; | 632 cvout[cvout_index].type = cert_po_certList; |
| 633 cvout[cvout_index].value.pointer.chain = NULL; | 633 cvout[cvout_index].value.pointer.chain = NULL; |
| 634 int cvout_cert_list_index = cvout_index; | 634 int cvout_cert_list_index = cvout_index; |
| 635 cvout_index++; | 635 cvout_index++; |
| 636 cvout[cvout_index].type = cert_po_trustAnchor; | 636 cvout[cvout_index].type = cert_po_trustAnchor; |
| 637 cvout[cvout_index].value.pointer.cert = NULL; | 637 cvout[cvout_index].value.pointer.cert = NULL; |
| 638 int cvout_trust_anchor_index = cvout_index; | 638 int cvout_trust_anchor_index = cvout_index; |
| 639 cvout_index++; | 639 cvout_index++; |
| 640 cvout[cvout_index].type = cert_po_end; | 640 cvout[cvout_index].type = cert_po_end; |
| 641 ScopedCERTValOutParam scoped_cvout(cvout); | 641 ScopedCERTValOutParam scoped_cvout(cvout); |
| 642 | 642 |
| 643 bool rev_checking_enabled = |
| 644 (flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED) || |
| 645 (flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED_EV_ONLY); |
| 646 |
| 643 SECStatus status = PKIXVerifyCert( | 647 SECStatus status = PKIXVerifyCert( |
| 644 cert_handle, | 648 cert_handle, |
| 645 flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED, | 649 rev_checking_enabled, |
| 646 flags & X509Certificate::VERIFY_CERT_IO_ENABLED, | 650 flags & X509Certificate::VERIFY_CERT_IO_ENABLED, |
| 647 metadata->GetPolicyOIDs(), | 651 &ev_policy_oid, |
| 648 metadata->NumPolicyOIDs(), | 652 1, |
| 649 cvout); | 653 cvout); |
| 650 if (status != SECSuccess) | 654 if (status != SECSuccess) |
| 651 return false; | 655 return false; |
| 652 | 656 |
| 653 CERTCertificate* root_ca = | 657 CERTCertificate* root_ca = |
| 654 cvout[cvout_trust_anchor_index].value.pointer.cert; | 658 cvout[cvout_trust_anchor_index].value.pointer.cert; |
| 655 if (root_ca == NULL) | 659 if (root_ca == NULL) |
| 656 return false; | 660 return false; |
| 657 | 661 |
| 658 // This second PKIXVerifyCert call could have found a different certification | 662 // 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 | 663 // path and one or more of the certificates on this new path, that weren't on |
| 660 // the old path, might have been revoked. | 664 // the old path, might have been revoked. |
| 661 if (crl_set) { | 665 if (crl_set) { |
| 662 CRLSetResult crl_set_result = CheckRevocationWithCRLSet( | 666 CRLSetResult crl_set_result = CheckRevocationWithCRLSet( |
| 663 cvout[cvout_cert_list_index].value.pointer.chain, | 667 cvout[cvout_cert_list_index].value.pointer.chain, |
| 664 cvout[cvout_trust_anchor_index].value.pointer.cert, | 668 cvout[cvout_trust_anchor_index].value.pointer.cert, |
| 665 crl_set); | 669 crl_set); |
| 666 if (crl_set_result == kCRLSetRevoked) | 670 if (crl_set_result == kCRLSetRevoked) |
| 667 return false; | 671 return false; |
| 668 } | 672 } |
| 669 | 673 |
| 670 SHA1Fingerprint fingerprint = | 674 SHA1Fingerprint fingerprint = |
| 671 X509Certificate::CalculateFingerprint(root_ca); | 675 X509Certificate::CalculateFingerprint(root_ca); |
| 672 std::vector<SECOidTag> ev_policy_tags; | 676 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 } | 677 } |
| 684 | 678 |
| 685 } // namespace | 679 } // namespace |
| 686 | 680 |
| 687 CertVerifyProcNSS::CertVerifyProcNSS() {} | 681 CertVerifyProcNSS::CertVerifyProcNSS() {} |
| 688 | 682 |
| 689 CertVerifyProcNSS::~CertVerifyProcNSS() {} | 683 CertVerifyProcNSS::~CertVerifyProcNSS() {} |
| 690 | 684 |
| 691 int CertVerifyProcNSS::VerifyInternal(X509Certificate* cert, | 685 int CertVerifyProcNSS::VerifyInternal(X509Certificate* cert, |
| 692 const std::string& hostname, | 686 const std::string& hostname, |
| (...skipping 18 matching lines...) Expand all Loading... |
| 711 cvout[cvout_index].value.pointer.chain = NULL; | 705 cvout[cvout_index].value.pointer.chain = NULL; |
| 712 int cvout_cert_list_index = cvout_index; | 706 int cvout_cert_list_index = cvout_index; |
| 713 cvout_index++; | 707 cvout_index++; |
| 714 cvout[cvout_index].type = cert_po_trustAnchor; | 708 cvout[cvout_index].type = cert_po_trustAnchor; |
| 715 cvout[cvout_index].value.pointer.cert = NULL; | 709 cvout[cvout_index].value.pointer.cert = NULL; |
| 716 int cvout_trust_anchor_index = cvout_index; | 710 int cvout_trust_anchor_index = cvout_index; |
| 717 cvout_index++; | 711 cvout_index++; |
| 718 cvout[cvout_index].type = cert_po_end; | 712 cvout[cvout_index].type = cert_po_end; |
| 719 ScopedCERTValOutParam scoped_cvout(cvout); | 713 ScopedCERTValOutParam scoped_cvout(cvout); |
| 720 | 714 |
| 715 EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance(); |
| 716 SECOidTag ev_policy_oid = SEC_OID_UNKNOWN; |
| 717 bool is_ev_candidate = |
| 718 (flags & X509Certificate::VERIFY_EV_CERT) && |
| 719 IsEVCandidate(metadata, cert_handle, &ev_policy_oid); |
| 721 bool cert_io_enabled = flags & X509Certificate::VERIFY_CERT_IO_ENABLED; | 720 bool cert_io_enabled = flags & X509Certificate::VERIFY_CERT_IO_ENABLED; |
| 722 bool check_revocation = | 721 bool check_revocation = |
| 723 (flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED) && | 722 cert_io_enabled && |
| 724 cert_io_enabled; | 723 ((flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED) || |
| 724 ((flags & X509Certificate::VERIFY_REV_CHECKING_ENABLED_EV_ONLY) && |
| 725 is_ev_candidate)); |
| 725 if (check_revocation) | 726 if (check_revocation) |
| 726 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; | 727 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; |
| 727 | 728 |
| 728 status = PKIXVerifyCert(cert_handle, check_revocation, cert_io_enabled, | 729 status = PKIXVerifyCert(cert_handle, check_revocation, cert_io_enabled, |
| 729 NULL, 0, cvout); | 730 NULL, 0, cvout); |
| 730 | 731 |
| 731 if (crl_set) { | 732 if (crl_set) { |
| 732 CRLSetResult crl_set_result = CheckRevocationWithCRLSet( | 733 CRLSetResult crl_set_result = CheckRevocationWithCRLSet( |
| 733 cvout[cvout_cert_list_index].value.pointer.chain, | 734 cvout[cvout_cert_list_index].value.pointer.chain, |
| 734 cvout[cvout_trust_anchor_index].value.pointer.cert, | 735 cvout[cvout_trust_anchor_index].value.pointer.cert, |
| (...skipping 28 matching lines...) Expand all Loading... |
| 763 if (IsCertStatusError(verify_result->cert_status)) | 764 if (IsCertStatusError(verify_result->cert_status)) |
| 764 return MapCertStatusToNetError(verify_result->cert_status); | 765 return MapCertStatusToNetError(verify_result->cert_status); |
| 765 | 766 |
| 766 AppendPublicKeyHashes(cvout[cvout_cert_list_index].value.pointer.chain, | 767 AppendPublicKeyHashes(cvout[cvout_cert_list_index].value.pointer.chain, |
| 767 cvout[cvout_trust_anchor_index].value.pointer.cert, | 768 cvout[cvout_trust_anchor_index].value.pointer.cert, |
| 768 &verify_result->public_key_hashes); | 769 &verify_result->public_key_hashes); |
| 769 | 770 |
| 770 verify_result->is_issued_by_known_root = | 771 verify_result->is_issued_by_known_root = |
| 771 IsKnownRoot(cvout[cvout_trust_anchor_index].value.pointer.cert); | 772 IsKnownRoot(cvout[cvout_trust_anchor_index].value.pointer.cert); |
| 772 | 773 |
| 773 if ((flags & X509Certificate::VERIFY_EV_CERT) && | 774 if ((flags & X509Certificate::VERIFY_EV_CERT) && is_ev_candidate && |
| 774 VerifyEV(cert_handle, flags, crl_set)) { | 775 VerifyEV(cert_handle, flags, crl_set, metadata, ev_policy_oid)) { |
| 775 verify_result->cert_status |= CERT_STATUS_IS_EV; | 776 verify_result->cert_status |= CERT_STATUS_IS_EV; |
| 776 } | 777 } |
| 777 | 778 |
| 778 return OK; | 779 return OK; |
| 779 } | 780 } |
| 780 | 781 |
| 781 } // namespace net | 782 } // namespace net |
| OLD | NEW |