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 |