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 |