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

Unified Diff: net/base/x509_certificate_nss.cc

Issue 113578: Implement X509Certificate::Verify for Linux.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 7 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
« no previous file with comments | « base/worker_pool_linux.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « base/worker_pool_linux.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698