Chromium Code Reviews| 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 |