summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-28 08:45:19 +0000
committerukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-28 08:45:19 +0000
commit82fbbe66c39262147ee501daa1df53e3b669e385 (patch)
tree3895314901a464e30cd60ed4471462d16870a3f6
parent8cb4689636ed19bfd3291602e59eedb194ba55cb (diff)
downloadchromium_src-82fbbe66c39262147ee501daa1df53e3b669e385.zip
chromium_src-82fbbe66c39262147ee501daa1df53e3b669e385.tar.gz
chromium_src-82fbbe66c39262147ee501daa1df53e3b669e385.tar.bz2
Implement X509Certificate::Verify for Linux.
Use CERT_PKIXVerifyCert() with CRL to verify certificate. With OCSP, CERT_PKIXVerifyCert() failed with SEC_ERROR_INVALID_ARGS. Increase stack size. It was not enough size if we use CERT_PKIXVerifyCert() on some sites. For example, https://www.google.com/ works, but https://bugs.webkit.org/ or https://www.thawte.com/ would die by SIGSEGV. This is because pkix_List_Destroy() routine destroys PKIX_List recursively, so if there are some long PKIX_Lists, it consumes stack a lot and dies by stack overflow. Note that X509Certificate::Verify isn't used in SSLClientSocketNSS yet. BUG=10911 TEST=net_unittests passes Review URL: http://codereview.chromium.org/113578 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17071 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/worker_pool_linux.cc4
-rw-r--r--net/base/x509_certificate_nss.cc277
2 files changed, 273 insertions, 8 deletions
diff --git a/base/worker_pool_linux.cc b/base/worker_pool_linux.cc
index a650b3b..31fcef1 100644
--- a/base/worker_pool_linux.cc
+++ b/base/worker_pool_linux.cc
@@ -15,7 +15,9 @@
namespace {
const int kIdleSecondsBeforeExit = 10 * 60;
-const int kWorkerThreadStackSize = 64 * 1024;
+// A stack size of 64 KB is too small for the CERT_PKIXVerifyCert
+// function of NSS because of NSS bug 439169.
+const int kWorkerThreadStackSize = 128 * 1024;
class WorkerPoolImpl {
public:
diff --git a/net/base/x509_certificate_nss.cc b/net/base/x509_certificate_nss.cc
index c53fe95..754ed1f 100644
--- a/net/base/x509_certificate_nss.cc
+++ b/net/base/x509_certificate_nss.cc
@@ -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 @@ bool X509Certificate::HasExpired() const {
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 @@ X509Certificate::Fingerprint X509Certificate::CalculateFingerprint(
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