| Index: net/base/x509_certificate_nss.cc
|
| ===================================================================
|
| --- net/base/x509_certificate_nss.cc (revision 17048)
|
| +++ net/base/x509_certificate_nss.cc (working copy)
|
| @@ -8,8 +8,10 @@
|
| // until NSS 3.12.2 comes out and we update to it.
|
| #define Lock FOO_NSS_Lock
|
| #include <cert.h>
|
| +#include <pk11pub.h>
|
| #include <prtime.h>
|
| #include <secder.h>
|
| +#include <secerr.h>
|
| #include <sechash.h>
|
| #undef Lock
|
|
|
| @@ -17,12 +19,168 @@
|
| #include "base/pickle.h"
|
| #include "base/time.h"
|
| #include "base/nss_init.h"
|
| +#include "net/base/cert_status_flags.h"
|
| +#include "net/base/cert_verify_result.h"
|
| #include "net/base/net_errors.h"
|
|
|
| namespace net {
|
|
|
| namespace {
|
|
|
| +class ScopedCERTCertificate {
|
| + public:
|
| + explicit ScopedCERTCertificate(CERTCertificate* cert)
|
| + : cert_(cert) {}
|
| +
|
| + ~ScopedCERTCertificate() {
|
| + if (cert_)
|
| + CERT_DestroyCertificate(cert_);
|
| + }
|
| +
|
| + private:
|
| + CERTCertificate* cert_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ScopedCERTCertificate);
|
| +};
|
| +
|
| +class ScopedCERTCertList {
|
| + public:
|
| + explicit ScopedCERTCertList(CERTCertList* cert_list)
|
| + : cert_list_(cert_list) {}
|
| +
|
| + ~ScopedCERTCertList() {
|
| + if (cert_list_)
|
| + CERT_DestroyCertList(cert_list_);
|
| + }
|
| +
|
| + private:
|
| + CERTCertList* cert_list_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ScopedCERTCertList);
|
| +};
|
| +
|
| +// 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++) {
|
| + 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 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:
|
| + 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 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;
|
| + 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 an CERT_STATUS_WRONG_USAGE error code.
|
| + case SEC_ERROR_CERT_USAGES_INVALID:
|
| + 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) {
|
| + int i = 0;
|
| + for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
|
| + !CERT_LIST_END(node, cert_list);
|
| + node = CERT_LIST_NEXT(node), 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;
|
| + }
|
| + }
|
| +}
|
| +
|
| // TODO(port): Implement this more simply, and put it in the right place
|
| base::Time PRTimeToBaseTime(PRTime prtime) {
|
| PRExplodedTime prxtime;
|
| @@ -212,11 +370,122 @@
|
| return false;
|
| }
|
|
|
| +// TODO(ukai): fix to use this method to verify certificate on SSL channel.
|
| +// Note that it's not being used yet. We need to fix SSLClientSocketNSS to
|
| +// use this method to verify ssl certificate.
|
| +// The problem is that we get segfault when unit tests is going to terminate
|
| +// if PR_Cleanup is called in NSSInitSingleton destructor.
|
| int X509Certificate::Verify(const std::string& hostname,
|
| bool rev_checking_enabled,
|
| CertVerifyResult* verify_result) const {
|
| + verify_result->Reset();
|
| +
|
| + // Make sure that the hostname matches with the common name of the cert.
|
| + SECStatus status = CERT_VerifyCertName(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(
|
| + cert_handle_, PR_Now(), PR_TRUE);
|
| + if (validity != secCertTimeValid)
|
| + verify_result->cert_status |= CERT_STATUS_DATE_INVALID;
|
| +
|
| + CERTRevocationFlags revocation_flags;
|
| + // TODO(ukai): Fix to use OCSP.
|
| + // OCSP mode would fail with SEC_ERROR_UNKNOWN_ISSUER.
|
| + // We need to set up OCSP and install an HTTP client for NSS.
|
| + bool use_ocsp = false;
|
| +
|
| + PRUint64 revocation_method_flags =
|
| + CERT_REV_M_TEST_USING_THIS_METHOD |
|
| + CERT_REV_M_ALLOW_NETWORK_FETCHING |
|
| + CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE |
|
| + CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE |
|
| + CERT_REV_M_STOP_TESTING_ON_FRESH_INFO;
|
| + PRUint64 revocation_method_independent_flags =
|
| + CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST |
|
| + CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE;
|
| + PRUint64 method_flags[2];
|
| + method_flags[cert_revocation_method_crl] = revocation_method_flags;
|
| + method_flags[cert_revocation_method_ocsp] = revocation_method_flags;
|
| +
|
| + int number_of_defined_methods;
|
| + CERTRevocationMethodIndex preferred_revocation_methods[1];
|
| + if (use_ocsp) {
|
| + number_of_defined_methods = 2;
|
| + preferred_revocation_methods[0] = cert_revocation_method_ocsp;
|
| + } else {
|
| + number_of_defined_methods = 1;
|
| + preferred_revocation_methods[0] = cert_revocation_method_crl;
|
| + }
|
| +
|
| + revocation_flags.leafTests.number_of_defined_methods =
|
| + number_of_defined_methods;
|
| + 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 =
|
| + number_of_defined_methods;
|
| + 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;
|
| +
|
| + CERTValInParam cvin[2];
|
| + int cvin_index = 0;
|
| + // We can't use PK11_ListCerts(PK11CertListCA, NULL) for cert_pi_trustAnchors.
|
| + // We get SEC_ERROR_UNTRUSTED_ISSUER (-8172) for our test root CA cert with
|
| + // it by NSS 3.12.0.3.
|
| + // No need to set cert_pi_trustAnchors here.
|
| + // TODO(ukai): use cert_pi_useAIACertFetch (new feature in NSS 3.12.1).
|
| + cvin[cvin_index].type = cert_pi_revocationFlags;
|
| + cvin[cvin_index].value.pointer.revocation = &revocation_flags;
|
| + cvin_index++;
|
| + cvin[cvin_index].type = cert_pi_end;
|
| +
|
| + CERTValOutParam cvout[3];
|
| + int cvout_index = 0;
|
| + cvout[cvout_index].type = cert_po_trustAnchor;
|
| + cvout[cvout_index].value.pointer.cert = NULL;
|
| + cvout_index++;
|
| + 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);
|
| +
|
| + status = CERT_PKIXVerifyCert(cert_handle_, certificateUsageSSLServer,
|
| + cvin, cvout, NULL);
|
| + if (status != SECSuccess) {
|
| + int err = PORT_GetError();
|
| + LOG(ERROR) << "CERT_PKIXVerifyCert 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;
|
| + verify_result->cert_status |= MapCertErrorToCertStatus(err);
|
| + return MapCertStatusToNetError(verify_result->cert_status);
|
| + }
|
| +
|
| + GetCertChainInfo(cvout[cvout_cert_list_index].value.pointer.chain,
|
| + verify_result);
|
| + if (IsCertStatusError(verify_result->cert_status))
|
| + return MapCertStatusToNetError(verify_result->cert_status);
|
| + return OK;
|
| +}
|
| +
|
| +// TODO(port): Implement properly on Linux.
|
| +bool X509Certificate::IsEV(int status) const {
|
| NOTIMPLEMENTED();
|
| - return ERR_NOT_IMPLEMENTED;
|
| + return false;
|
| }
|
|
|
| // static
|
| @@ -252,10 +521,4 @@
|
| return sha1;
|
| }
|
|
|
| -// TODO(port): Implement properly on Linux.
|
| -bool X509Certificate::IsEV(int status) const {
|
| - // http://code.google.com/p/chromium/issues/detail?id=10911
|
| - return false;
|
| -}
|
| -
|
| } // namespace net
|
|
|