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

Unified Diff: net/base/x509_chain_nss.cc

Issue 3112013: Move chain building/verification out of X509Certificate (Closed)
Patch Set: Rebase to trunk - Without OpenSSL fixes Created 10 years, 2 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 side-by-side diff with in-line comments
Download patch
Index: net/base/x509_chain_nss.cc
diff --git a/net/base/x509_chain_nss.cc b/net/base/x509_chain_nss.cc
new file mode 100644
index 0000000000000000000000000000000000000000..548a022f98c0f0cdf6b291146537768171bf0cef
--- /dev/null
+++ b/net/base/x509_chain_nss.cc
@@ -0,0 +1,568 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/base/x509_chain.h"
+
+#include <cert.h>
+#include <nss.h>
+#include <prerror.h>
+#include <secerr.h>
+#include <sslerr.h>
+
+#include "base/logging.h"
+#include "net/base/cert_status_flags.h"
+#include "net/base/cert_verify_result.h"
+#include "net/base/ev_root_ca_metadata.h"
+#include "net/base/net_errors.h"
+
+namespace net {
+
+namespace {
+
+class ScopedCERTCertificatePolicies {
+ public:
+ explicit ScopedCERTCertificatePolicies(CERTCertificatePolicies* policies)
+ : policies_(policies) {}
+
+ ~ScopedCERTCertificatePolicies() {
+ if (policies_)
+ CERT_DestroyCertificatePoliciesExtension(policies_);
+ }
+
+ private:
+ CERTCertificatePolicies* policies_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedCERTCertificatePolicies);
+};
+
+// ScopedCERTValOutParam manages destruction of values in the CERTValOutParam
+// array that cvout points to. cvout must be initialized as passed to
+// CERT_PKIXVerifyCert, so that the array must be terminated with
+// cert_po_end type.
+// When it goes out of scope, it destroys values of cert_po_trustAnchor
+// and cert_po_certList types, but doesn't release the array itself.
+class ScopedCERTValOutParam {
+ public:
+ explicit ScopedCERTValOutParam(CERTValOutParam* cvout)
+ : cvout_(cvout) {}
+
+ ~ScopedCERTValOutParam() {
+ if (cvout_ == NULL)
+ return;
+ for (CERTValOutParam *p = cvout_; p->type != cert_po_end; p++) {
bulach 2010/10/21 10:21:33 nits: s/CERTValOutParam */CERTValOutParam* / s/p++
+ switch (p->type) {
+ case cert_po_trustAnchor:
+ if (p->value.pointer.cert) {
+ CERT_DestroyCertificate(p->value.pointer.cert);
+ p->value.pointer.cert = NULL;
+ }
+ break;
+ case cert_po_certList:
+ if (p->value.pointer.chain) {
+ CERT_DestroyCertList(p->value.pointer.chain);
+ p->value.pointer.chain = NULL;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private:
+ CERTValOutParam* cvout_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedCERTValOutParam);
+};
+
+// Map PORT_GetError() return values to our network error codes.
+int MapSecurityError(int err) {
+ switch (err) {
+ case PR_DIRECTORY_LOOKUP_ERROR: // DNS lookup error.
+ return ERR_NAME_NOT_RESOLVED;
+ case SEC_ERROR_INVALID_ARGS:
+ return ERR_INVALID_ARGUMENT;
+ case SSL_ERROR_BAD_CERT_DOMAIN:
+ return ERR_CERT_COMMON_NAME_INVALID;
+ case SEC_ERROR_INVALID_TIME:
+ case SEC_ERROR_EXPIRED_CERTIFICATE:
+ return ERR_CERT_DATE_INVALID;
+ case SEC_ERROR_UNKNOWN_ISSUER:
+ case SEC_ERROR_UNTRUSTED_ISSUER:
+ case SEC_ERROR_CA_CERT_INVALID:
+ case SEC_ERROR_UNTRUSTED_CERT:
+ return ERR_CERT_AUTHORITY_INVALID;
+ case SEC_ERROR_REVOKED_CERTIFICATE:
+ return ERR_CERT_REVOKED;
+ case SEC_ERROR_BAD_DER:
+ case SEC_ERROR_BAD_SIGNATURE:
+ case SEC_ERROR_CERT_NOT_VALID:
+ // TODO(port): add an ERR_CERT_WRONG_USAGE error code.
+ case SEC_ERROR_CERT_USAGES_INVALID:
+ case SEC_ERROR_POLICY_VALIDATION_FAILED:
+ return ERR_CERT_INVALID;
+ default:
+ LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED";
+ return ERR_FAILED;
+ }
+}
+
+// Map PORT_GetError() return values to our cert status flags.
+int MapCertErrorToCertStatus(int err) {
+ switch (err) {
+ case SSL_ERROR_BAD_CERT_DOMAIN:
+ return CERT_STATUS_COMMON_NAME_INVALID;
+ case SEC_ERROR_INVALID_TIME:
+ case SEC_ERROR_EXPIRED_CERTIFICATE:
+ return CERT_STATUS_DATE_INVALID;
+ case SEC_ERROR_UNTRUSTED_CERT:
+ case SEC_ERROR_UNKNOWN_ISSUER:
+ case SEC_ERROR_UNTRUSTED_ISSUER:
+ case SEC_ERROR_CA_CERT_INVALID:
+ return CERT_STATUS_AUTHORITY_INVALID;
+ // TODO(port): map CERT_STATUS_NO_REVOCATION_MECHANISM.
+ case SEC_ERROR_OCSP_BAD_HTTP_RESPONSE:
+ case SEC_ERROR_OCSP_SERVER_ERROR:
+ return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
+ case SEC_ERROR_REVOKED_CERTIFICATE:
+ return CERT_STATUS_REVOKED;
+ case SEC_ERROR_BAD_DER:
+ case SEC_ERROR_BAD_SIGNATURE:
+ case SEC_ERROR_CERT_NOT_VALID:
+ // TODO(port): add a CERT_STATUS_WRONG_USAGE error code.
+ case SEC_ERROR_CERT_USAGES_INVALID:
+ case SEC_ERROR_POLICY_VALIDATION_FAILED:
+ return CERT_STATUS_INVALID;
+ default:
+ return 0;
+ }
+}
+
+// Saves some information about the certificate chain cert_list in
+// *verify_result. The caller MUST initialize *verify_result before calling
+// this function.
+// Note that cert_list[0] is the end entity certificate and cert_list doesn't
+// contain the root CA certificate.
+void GetCertChainInfo(CERTCertList* cert_list,
+ CertVerifyResult* verify_result) {
+ // NOTE: Using a NSS library before 3.12.3.1 will crash below. To see the
+ // NSS version currently in use:
+ // 1. use ldd on the chrome executable for NSS's location (ie. libnss3.so*)
+ // 2. use ident libnss3.so* for the library's version
+ DCHECK(cert_list);
+ int i = 0;
+ for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
+ !CERT_LIST_END(node, cert_list);
+ node = CERT_LIST_NEXT(node), i++) {
bulach 2010/10/21 10:21:33 nit: ++i
+ SECAlgorithmID& signature = node->cert->signature;
+ SECOidTag oid_tag = SECOID_FindOIDTag(&signature.algorithm);
+ switch (oid_tag) {
+ case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
+ verify_result->has_md5 = true;
+ if (i != 0)
+ verify_result->has_md5_ca = true;
+ break;
+ case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION:
+ verify_result->has_md2 = true;
+ if (i != 0)
+ verify_result->has_md2_ca = true;
+ break;
+ case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION:
+ verify_result->has_md4 = true;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+// Forward declarations.
+SECStatus RetryPKIXVerifyCertWithWorkarounds(
+ X509Certificate::OSCertHandle cert_handle, int num_policy_oids,
+ std::vector<CERTValInParam>* cvin, CERTValOutParam* cvout);
+SECOidTag GetFirstCertPolicy(X509Certificate::OSCertHandle cert_handle);
+
+// Call CERT_PKIXVerifyCert for the cert_handle.
+// Verification results are stored in an array of CERTValOutParam.
+// If policy_oids is not NULL and num_policy_oids is positive, policies
+// are also checked.
+// Caller must initialize cvout before calling this function.
+SECStatus PKIXVerifyCert(X509Certificate::OSCertHandle cert_handle,
+ bool check_revocation,
+ const SECOidTag* policy_oids,
+ int num_policy_oids,
+ CERTValOutParam* cvout) {
+ bool use_crl = check_revocation;
+ bool use_ocsp = check_revocation;
+
+ // These CAs have multiple keys, which trigger two bugs in NSS's CRL code.
+ // 1. NSS may use one key to verify a CRL signed with another key,
+ // incorrectly concluding that the CRL's signature is invalid.
+ // Hopefully this bug will be fixed in NSS 3.12.9.
+ // 2. NSS considers all certificates issued by the CA as revoked when it
+ // receives a CRL with an invalid signature. This overly strict policy
+ // has been relaxed in NSS 3.12.7. See
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=562542.
+ // So we have to turn off CRL checking for these CAs. See
+ // http://crbug.com/55695.
+ static const char* const kMultipleKeyCA[] = {
+ "CN=Microsoft Secure Server Authority,"
+ "DC=redmond,DC=corp,DC=microsoft,DC=com",
+ "CN=Microsoft Secure Server Authority",
+ };
+
+ if (!NSS_VersionCheck("3.12.7")) {
+ for (size_t i = 0; i < arraysize(kMultipleKeyCA); ++i) {
+ if (strcmp(cert_handle->issuerName, kMultipleKeyCA[i]) == 0) {
+ use_crl = false;
+ break;
+ }
+ }
+ }
+
+ PRUint64 revocation_method_flags =
+ CERT_REV_M_DO_NOT_TEST_USING_THIS_METHOD |
+ CERT_REV_M_ALLOW_NETWORK_FETCHING |
+ CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE |
+ CERT_REV_M_IGNORE_MISSING_FRESH_INFO |
+ CERT_REV_M_STOP_TESTING_ON_FRESH_INFO;
+ PRUint64 revocation_method_independent_flags =
+ CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST;
+ if (policy_oids && num_policy_oids > 0) {
+ // EV verification requires revocation checking. Consider the certificate
+ // revoked if we don't have revocation info.
+ // TODO(wtc): Add a bool parameter to expressly specify we're doing EV
+ // verification or we want strict revocation flags.
+ revocation_method_flags |= CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE;
+ revocation_method_independent_flags |=
+ CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE;
+ } else {
+ revocation_method_flags |= CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE;
+ revocation_method_independent_flags |=
+ CERT_REV_MI_NO_OVERALL_INFO_REQUIREMENT;
+ }
+ PRUint64 method_flags[2];
+ method_flags[cert_revocation_method_crl] = revocation_method_flags;
+ method_flags[cert_revocation_method_ocsp] = revocation_method_flags;
+
+ if (use_crl) {
+ method_flags[cert_revocation_method_crl] |=
+ CERT_REV_M_TEST_USING_THIS_METHOD;
+ }
+ if (use_ocsp) {
+ method_flags[cert_revocation_method_ocsp] |=
+ CERT_REV_M_TEST_USING_THIS_METHOD;
+ }
+
+ CERTRevocationMethodIndex preferred_revocation_methods[1];
+ if (use_ocsp) {
+ preferred_revocation_methods[0] = cert_revocation_method_ocsp;
+ } else {
+ preferred_revocation_methods[0] = cert_revocation_method_crl;
+ }
+
+ CERTRevocationFlags revocation_flags;
+ revocation_flags.leafTests.number_of_defined_methods =
+ arraysize(method_flags);
+ revocation_flags.leafTests.cert_rev_flags_per_method = method_flags;
+ revocation_flags.leafTests.number_of_preferred_methods =
+ arraysize(preferred_revocation_methods);
+ revocation_flags.leafTests.preferred_methods = preferred_revocation_methods;
+ revocation_flags.leafTests.cert_rev_method_independent_flags =
+ revocation_method_independent_flags;
+
+ revocation_flags.chainTests.number_of_defined_methods =
+ arraysize(method_flags);
+ revocation_flags.chainTests.cert_rev_flags_per_method = method_flags;
+ revocation_flags.chainTests.number_of_preferred_methods =
+ arraysize(preferred_revocation_methods);
+ revocation_flags.chainTests.preferred_methods = preferred_revocation_methods;
+ revocation_flags.chainTests.cert_rev_method_independent_flags =
+ revocation_method_independent_flags;
+
+ std::vector<CERTValInParam> cvin;
+ cvin.reserve(5);
bulach 2010/10/21 10:21:33 nit: not sure why 5 as there are three push_back b
+ CERTValInParam in_param;
+ // No need to set cert_pi_trustAnchors here.
+ in_param.type = cert_pi_revocationFlags;
+ in_param.value.pointer.revocation = &revocation_flags;
+ cvin.push_back(in_param);
+ if (policy_oids && num_policy_oids > 0) {
+ in_param.type = cert_pi_policyOID;
+ in_param.value.arraySize = num_policy_oids;
+ in_param.value.array.oids = policy_oids;
+ cvin.push_back(in_param);
+ }
+ in_param.type = cert_pi_end;
+ cvin.push_back(in_param);
+
+ SECStatus rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer,
+ &cvin[0], cvout, NULL);
+ if (rv != SECSuccess) {
+ rv = RetryPKIXVerifyCertWithWorkarounds(cert_handle, num_policy_oids,
+ &cvin, cvout);
+ }
+ return rv;
+}
+
+// PKIXVerifyCert calls this function to work around some bugs in
+// CERT_PKIXVerifyCert. All the arguments of this function are either the
+// arguments or local variables of PKIXVerifyCert.
+SECStatus RetryPKIXVerifyCertWithWorkarounds(
+ X509Certificate::OSCertHandle cert_handle, int num_policy_oids,
+ std::vector<CERTValInParam>* cvin, CERTValOutParam* cvout) {
+ // We call this function when the first CERT_PKIXVerifyCert call in
+ // PKIXVerifyCert failed, so we initialize |rv| to SECFailure.
+ SECStatus rv = SECFailure;
+ int nss_error = PORT_GetError();
+ CERTValInParam in_param;
+
+ // If we get SEC_ERROR_UNKNOWN_ISSUER, we may be missing an intermediate
+ // CA certificate, so we retry with cert_pi_useAIACertFetch.
+ // cert_pi_useAIACertFetch has several bugs in its error handling and
+ // error reporting (NSS bug 528743), so we don't use it by default.
+ // Note: When building a certificate chain, CERT_PKIXVerifyCert may
+ // incorrectly pick a CA certificate with the same subject name as the
+ // missing intermediate CA certificate, and fail with the
+ // SEC_ERROR_BAD_SIGNATURE error (NSS bug 524013), so we also retry with
+ // cert_pi_useAIACertFetch on SEC_ERROR_BAD_SIGNATURE.
+ if (nss_error == SEC_ERROR_UNKNOWN_ISSUER ||
+ nss_error == SEC_ERROR_BAD_SIGNATURE) {
+ DCHECK_EQ(cvin->back().type, cert_pi_end);
+ cvin->pop_back();
+ in_param.type = cert_pi_useAIACertFetch;
+ in_param.value.scalar.b = PR_TRUE;
+ cvin->push_back(in_param);
+ in_param.type = cert_pi_end;
+ cvin->push_back(in_param);
+ rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer,
+ &(*cvin)[0], cvout, NULL);
+ if (rv == SECSuccess)
+ return rv;
+ int new_nss_error = PORT_GetError();
+ if (new_nss_error == SEC_ERROR_INVALID_ARGS ||
+ new_nss_error == SEC_ERROR_UNKNOWN_AIA_LOCATION_TYPE ||
+ new_nss_error == SEC_ERROR_BAD_HTTP_RESPONSE ||
+ new_nss_error == SEC_ERROR_BAD_LDAP_RESPONSE ||
+ !IS_SEC_ERROR(new_nss_error)) {
+ // Use the original error code because of cert_pi_useAIACertFetch's
+ // bad error reporting.
+ PORT_SetError(nss_error);
+ return rv;
+ }
+ nss_error = new_nss_error;
+ }
+
+ // If an intermediate CA certificate has requireExplicitPolicy in its
+ // policyConstraints extension, CERT_PKIXVerifyCert fails with
+ // SEC_ERROR_POLICY_VALIDATION_FAILED because we didn't specify any
+ // certificate policy (NSS bug 552775). So we retry with the certificate
+ // policy found in the server certificate.
+ if (nss_error == SEC_ERROR_POLICY_VALIDATION_FAILED &&
+ num_policy_oids == 0) {
+ SECOidTag policy = GetFirstCertPolicy(cert_handle);
+ if (policy != SEC_OID_UNKNOWN) {
+ DCHECK_EQ(cvin->back().type, cert_pi_end);
+ cvin->pop_back();
+ in_param.type = cert_pi_policyOID;
+ in_param.value.arraySize = 1;
+ in_param.value.array.oids = &policy;
+ cvin->push_back(in_param);
+ in_param.type = cert_pi_end;
+ cvin->push_back(in_param);
+ rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer,
+ &(*cvin)[0], cvout, NULL);
+ if (rv != SECSuccess) {
+ // Use the original error code.
+ PORT_SetError(nss_error);
+ }
+ }
+ }
+
+ return rv;
+}
+
+// Decodes the certificatePolicies extension of the certificate. Returns
+// NULL if the certificate doesn't have the extension or the extension can't
+// be decoded. The returned value must be freed with a
+// CERT_DestroyCertificatePoliciesExtension call.
+CERTCertificatePolicies* DecodeCertPolicies(
+ X509Certificate::OSCertHandle cert_handle) {
+ SECItem policy_ext;
+ SECStatus rv = CERT_FindCertExtension(
+ cert_handle, SEC_OID_X509_CERTIFICATE_POLICIES, &policy_ext);
+ if (rv != SECSuccess)
+ return NULL;
+ CERTCertificatePolicies* policies =
+ CERT_DecodeCertificatePoliciesExtension(&policy_ext);
+ SECITEM_FreeItem(&policy_ext, PR_FALSE);
+ return policies;
+}
+
+// Returns the OID tag for the first certificate policy in the certificate's
+// certificatePolicies extension. Returns SEC_OID_UNKNOWN if the certificate
+// has no certificate policy.
+SECOidTag GetFirstCertPolicy(X509Certificate::OSCertHandle cert_handle) {
+ CERTCertificatePolicies* policies = DecodeCertPolicies(cert_handle);
+ if (!policies)
+ return SEC_OID_UNKNOWN;
+ ScopedCERTCertificatePolicies scoped_policies(policies);
+ CERTPolicyInfo* policy_info = policies->policyInfos[0];
+ if (!policy_info)
+ return SEC_OID_UNKNOWN;
+ if (policy_info->oid != SEC_OID_UNKNOWN)
+ return policy_info->oid;
+
+ // The certificate policy is unknown to NSS. We need to create a dynamic
+ // OID tag for the policy.
+ SECOidData od;
+ od.oid.len = policy_info->policyID.len;
+ od.oid.data = policy_info->policyID.data;
+ od.offset = SEC_OID_UNKNOWN;
+ // NSS doesn't allow us to pass an empty description, so I use a hardcoded,
+ // default description here. The description doesn't need to be unique for
+ // each OID.
+ od.desc = "a certificate policy";
+ od.mechanism = CKM_INVALID_MECHANISM;
+ od.supportedExtension = INVALID_CERT_EXTENSION;
+ return SECOID_AddEntry(&od);
+}
+
+bool CheckCertPolicies(X509Certificate::OSCertHandle cert_handle,
+ SECOidTag ev_policy_tag) {
+ CERTCertificatePolicies* policies = DecodeCertPolicies(cert_handle);
+ if (!policies) {
+ LOG(ERROR) << "Cert has no policies extension or extension couldn't be "
+ "decoded.";
+ return false;
+ }
+ ScopedCERTCertificatePolicies scoped_policies(policies);
+ CERTPolicyInfo** policy_infos = policies->policyInfos;
+ while (*policy_infos != NULL) {
+ CERTPolicyInfo* policy_info = *policy_infos++;
+ SECOidTag oid_tag = policy_info->oid;
+ if (oid_tag == SEC_OID_UNKNOWN)
+ continue;
+ if (oid_tag == ev_policy_tag)
+ return true;
+ }
+ LOG(ERROR) << "No EV Policy Tag";
+ return false;
+}
+
+// Studied Mozilla's code (esp. security/manager/ssl/src/nsIdentityChecking.cpp
+// and nsNSSCertHelper.cpp) to learn how to verify EV certificate.
+// TODO(wtc): A possible optimization is that we get the trust anchor from
+// the first PKIXVerifyCert call. We look up the EV policy for the trust
+// anchor. If the trust anchor has no EV policy, we know the cert isn't EV.
+// Otherwise, we pass just that EV policy (as opposed to all the EV policies)
+// to the second PKIXVerifyCert call.
+bool VerifyEV(X509Certificate* certificate) {
+ net::EVRootCAMetadata* metadata = net::EVRootCAMetadata::GetInstance();
+
+ CERTValOutParam cvout[3];
+ int cvout_index = 0;
+ cvout[cvout_index].type = cert_po_trustAnchor;
+ cvout[cvout_index].value.pointer.cert = NULL;
+ int cvout_trust_anchor_index = cvout_index;
+ cvout_index++;
bulach 2010/10/21 10:21:33 nit: ++cvout_index;
+ cvout[cvout_index].type = cert_po_end;
+ ScopedCERTValOutParam scoped_cvout(cvout);
+
+ SECStatus status = PKIXVerifyCert(certificate->os_cert_handle(),
+ true,
+ metadata->GetPolicyOIDs(),
+ metadata->NumPolicyOIDs(),
+ cvout);
+ if (status != SECSuccess)
+ return false;
+
+ CERTCertificate* root_ca =
+ cvout[cvout_trust_anchor_index].value.pointer.cert;
+ if (root_ca == NULL)
+ return false;
+ SHA1Fingerprint fingerprint =
+ X509Certificate::CalculateFingerprint(root_ca);
+ SECOidTag ev_policy_tag = SEC_OID_UNKNOWN;
+ if (!metadata->GetPolicyOID(fingerprint, &ev_policy_tag))
+ return false;
+
+ if (!CheckCertPolicies(certificate->os_cert_handle(), ev_policy_tag))
+ return false;
+
+ return true;
+}
+
+} // namespace
+
+namespace x509_chain {
+
+int VerifySSLServer(X509Certificate* certificate, const std::string& hostname,
+ int flags, CertVerifyResult* verify_result) {
+ verify_result->Reset();
+ if (!certificate || !certificate->os_cert_handle())
+ return ERR_UNEXPECTED;
+
+ // Make sure that the hostname matches with the common name of the cert.
+ SECStatus status = CERT_VerifyCertName(certificate->os_cert_handle(),
+ hostname.c_str());
+ if (status != SECSuccess)
+ verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID;
+
+ // Make sure that the cert is valid now.
+ SECCertTimeValidity validity = CERT_CheckCertValidTimes(
+ certificate->os_cert_handle(), PR_Now(), PR_TRUE);
+ if (validity != secCertTimeValid)
+ verify_result->cert_status |= CERT_STATUS_DATE_INVALID;
+
+ CERTValOutParam cvout[3];
+ int cvout_index = 0;
+ // We don't need the trust anchor for the first PKIXVerifyCert call.
+ cvout[cvout_index].type = cert_po_certList;
+ cvout[cvout_index].value.pointer.chain = NULL;
+ int cvout_cert_list_index = cvout_index;
+ cvout_index++;
+ cvout[cvout_index].type = cert_po_end;
+ ScopedCERTValOutParam scoped_cvout(cvout);
+
+ bool check_revocation = (flags & VERIFY_REV_CHECKING_ENABLED);
+ if (check_revocation) {
+ verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
+ } else {
+ // EV requires revocation checking.
+ flags &= ~VERIFY_EV_CERT;
+ }
+ status = PKIXVerifyCert(certificate->os_cert_handle(), check_revocation,
+ NULL, 0, cvout);
+ if (status != SECSuccess) {
+ int err = PORT_GetError();
+ LOG(ERROR) << "CERT_PKIXVerifyCert for " << hostname
+ << " failed err=" << err;
+ // CERT_PKIXVerifyCert rerports the wrong error code for
+ // expired certificates (NSS bug 491174)
+ if (err == SEC_ERROR_CERT_NOT_VALID &&
+ (verify_result->cert_status & CERT_STATUS_DATE_INVALID) != 0)
+ err = SEC_ERROR_EXPIRED_CERTIFICATE;
+ int cert_status = MapCertErrorToCertStatus(err);
+ if (cert_status) {
+ verify_result->cert_status |= cert_status;
+ return MapCertStatusToNetError(verify_result->cert_status);
+ }
+ // |err| is not a certificate error.
+ return MapSecurityError(err);
+ }
+
+ GetCertChainInfo(cvout[cvout_cert_list_index].value.pointer.chain,
+ verify_result);
+ if (IsCertStatusError(verify_result->cert_status))
+ return MapCertStatusToNetError(verify_result->cert_status);
+
+ if ((flags & VERIFY_EV_CERT) && VerifyEV(certificate))
+ verify_result->cert_status |= CERT_STATUS_IS_EV;
+ return OK;
+}
+
+} // namespace x509_chain
+
+} // namespace net

Powered by Google App Engine
This is Rietveld 408576698