summaryrefslogtreecommitdiffstats
path: root/net/base
diff options
context:
space:
mode:
authordank@chromium.org <dank@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2008-11-26 22:21:17 +0000
committerdank@chromium.org <dank@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2008-11-26 22:21:17 +0000
commitab63eccc1f9afea4e4f8587178c40ce1d0799f56 (patch)
treeb2b7438a4d9e73fa7079f2489bd0d846bea5a08b /net/base
parente5f659d04a347c41cbd71c918ff0f24448bd0bcc (diff)
downloadchromium_src-ab63eccc1f9afea4e4f8587178c40ce1d0799f56.zip
chromium_src-ab63eccc1f9afea4e4f8587178c40ce1d0799f56.tar.gz
chromium_src-ab63eccc1f9afea4e4f8587178c40ce1d0799f56.tar.bz2
Point nss at root certs so test_shell can talk to mail.google.com without warnings.
(gmail.com's certificate is for mail.google.com, which doesn't match gmail.com, so on some distros, test_shell will now refuse to talk with gmail.com.) Support ciphers needed to talk to testserver.py. Load temporary testing cert needed to run unit tests (can't do it manually like on Windows, since we don't use a writable cert database in the filesystem.) Implement part of GetSSLInfo. Re-enable url_request_unittest.cc, which seems to have been removed from the list of files to compile by mistake. Addresses part of http://code.google.com/p/chromium/issues/detail?id=4510 Later changesets will implement x509 certificates for nss, finish GetSSLInfo support, and update chrome/browser/ssl_uitest.cc to use SSLTestUtil. Review URL: http://codereview.chromium.org/11249 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@6063 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/base')
-rw-r--r--net/base/ssl_client_socket_nss.cc161
-rw-r--r--net/base/ssl_client_socket_nss.h2
-rw-r--r--net/base/ssl_test_util.cc145
-rw-r--r--net/base/ssl_test_util.h47
4 files changed, 332 insertions, 23 deletions
diff --git a/net/base/ssl_client_socket_nss.cc b/net/base/ssl_client_socket_nss.cc
index c954ec9..f2f977b 100644
--- a/net/base/ssl_client_socket_nss.cc
+++ b/net/base/ssl_client_socket_nss.cc
@@ -6,10 +6,12 @@
#include <nspr.h>
#include <nss.h>
+#include <secerr.h>
// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424
// until NSS 3.12.2 comes out and we update to it.
#define Lock FOO_NSS_Lock
#include <ssl.h>
+#include <sslerr.h>
#include <pk11pub.h>
#undef Lock
@@ -21,18 +23,16 @@
static const int kRecvBufferSize = 4096;
-/*
- * nss calls this if an incoming certificate is invalid.
- * TODO(port): expose to app via GetSSLInfo so it can put up
- * the appropriate GUI and retry with override if desired
- */
+// nss calls this if an incoming certificate is invalid.
static SECStatus
ownBadCertHandler(void * arg, PRFileDesc * socket)
{
PRErrorCode err = PR_GetError();
- LOG(ERROR) << "server certificate is invalid; NSS error code " << err;
- // Return SECSuccess to override the problem, SECFailure to let the original function fail
- return SECSuccess; /* override, say it's OK. */
+ LOG(INFO) << "server certificate is invalid; NSS error code " << err;
+ // Return SECSuccess to override the problem,
+ // or SECFailure to let the original function fail
+ // Chromium wants it to fail here, and may retry it later.
+ return SECFailure;
}
@@ -44,6 +44,7 @@ namespace net {
#define EnterFunction(x)
#define LeaveFunction(x)
#define GotoState(s) next_state_ = s
+#define LogData(s, len)
#else
#define EnterFunction(x) LOG(INFO) << (void *)this << " " << __FUNCTION__ << \
" enter " << x << "; next_state " << next_state_
@@ -51,8 +52,79 @@ namespace net {
" leave " << x << "; next_state " << next_state_
#define GotoState(s) do { LOG(INFO) << (void *)this << " " << __FUNCTION__ << \
" jump to state " << s; next_state_ = s; } while (0)
+#define LogData(s, len) LOG(INFO) << (void *)this << " " << __FUNCTION__ << \
+ " data [" << std::string(s, len) << "]";
+
#endif
+namespace {
+
+int NetErrorFromNSPRError(PRErrorCode err) {
+ // TODO(port): fill this out as we learn what's important
+ switch (err) {
+ case PR_WOULD_BLOCK_ERROR:
+ return ERR_IO_PENDING;
+ case SSL_ERROR_NO_CYPHER_OVERLAP:
+ return ERR_SSL_VERSION_OR_CIPHER_MISMATCH;
+ case SSL_ERROR_BAD_CERT_DOMAIN:
+ return ERR_CERT_COMMON_NAME_INVALID;
+ case SEC_ERROR_EXPIRED_CERTIFICATE:
+ return ERR_CERT_DATE_INVALID;
+ case SEC_ERROR_BAD_SIGNATURE:
+ return ERR_CERT_INVALID;
+ case SSL_ERROR_REVOKED_CERT_ALERT:
+ case SEC_ERROR_REVOKED_CERTIFICATE:
+ case SEC_ERROR_REVOKED_KEY:
+ return ERR_CERT_REVOKED;
+ case SEC_ERROR_UNKNOWN_ISSUER:
+ return ERR_CERT_AUTHORITY_INVALID;
+
+ default: {
+ if (IS_SSL_ERROR(err)) {
+ LOG(WARNING) << "Unknown SSL error " << err <<
+ " mapped to net::ERR_SSL_PROTOCOL_ERROR";
+ return ERR_SSL_PROTOCOL_ERROR;
+ }
+ if (IS_SEC_ERROR(err)) {
+ // TODO(port): Probably not the best mapping
+ LOG(WARNING) << "Unknown SEC error " << err <<
+ " mapped to net::ERR_CERT_INVALID";
+ return ERR_CERT_INVALID;
+ }
+ LOG(WARNING) << "Unknown error " << err <<
+ " mapped to net::ERR_FAILED";
+ return ERR_FAILED;
+ }
+ }
+}
+
+// Shared with the Windows code. TODO(avi): merge to a common place
+int CertStatusFromNetError(int error) {
+ switch (error) {
+ case ERR_CERT_COMMON_NAME_INVALID:
+ return CERT_STATUS_COMMON_NAME_INVALID;
+ case ERR_CERT_DATE_INVALID:
+ return CERT_STATUS_DATE_INVALID;
+ case ERR_CERT_AUTHORITY_INVALID:
+ return CERT_STATUS_AUTHORITY_INVALID;
+ case ERR_CERT_NO_REVOCATION_MECHANISM:
+ return CERT_STATUS_NO_REVOCATION_MECHANISM;
+ case ERR_CERT_UNABLE_TO_CHECK_REVOCATION:
+ return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
+ case ERR_CERT_REVOKED:
+ return CERT_STATUS_REVOKED;
+ case ERR_CERT_CONTAINS_ERRORS:
+ NOTREACHED();
+ // Falls through.
+ case ERR_CERT_INVALID:
+ return CERT_STATUS_INVALID;
+ default:
+ return 0;
+ }
+}
+
+} // namespace
+
bool SSLClientSocketNSS::nss_options_initialized_ = false;
SSLClientSocketNSS::SSLClientSocketNSS(ClientSocket* transport_socket,
@@ -70,6 +142,7 @@ SSLClientSocketNSS::SSLClientSocketNSS(ClientSocket* transport_socket,
user_callback_(NULL),
user_buf_(NULL),
user_buf_len_(0),
+ server_cert_status_(0),
completed_handshake_(false),
next_state_(STATE_NONE),
nss_fd_(NULL),
@@ -148,7 +221,7 @@ int SSLClientSocketNSS::Read(char* buf, int buf_len,
int rv = DoLoop(OK);
if (rv == ERR_IO_PENDING)
user_callback_ = callback;
- LeaveFunction("");
+ LeaveFunction(rv);
return rv;
}
@@ -167,14 +240,30 @@ int SSLClientSocketNSS::Write(const char* buf, int buf_len,
int rv = DoLoop(OK);
if (rv == ERR_IO_PENDING)
user_callback_ = callback;
- LeaveFunction("");
+ LeaveFunction(rv);
return rv;
}
void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) {
EnterFunction("");
- // TODO(port): implement!
ssl_info->Reset();
+ SSLChannelInfo channel_info;
+ SECStatus ok = SSL_GetChannelInfo(nss_fd_,
+ &channel_info, sizeof(channel_info));
+ if (ok == SECSuccess) {
+ SSLCipherSuiteInfo cipher_info;
+ ok = SSL_GetCipherSuiteInfo(channel_info.cipherSuite,
+ &cipher_info, sizeof(cipher_info));
+ if (ok == SECSuccess) {
+ ssl_info->security_bits = cipher_info.effectiveKeyBits;
+ } else {
+ ssl_info->security_bits = -1;
+ NOTREACHED();
+ }
+ }
+ ssl_info->cert_status = server_cert_status_;
+ // TODO(port): implement X509Certificate so we can set the cert field!
+ // CERTCertificate *nssCert = SSL_PeerCertificate(nss_fd_);
LeaveFunction("");
}
@@ -378,14 +467,32 @@ int SSLClientSocketNSS::DoConnectComplete(int result) {
if (rv != SECSuccess)
return ERR_UNEXPECTED;
+ // V2 compatible hello means no SNI, which would cause errors like
+ // "common name `mail.google.com' != requested host name `gmail.com'"
+ // so don't do V2 compatible hellos unless we're really using SSL2.
+ rv = SSL_OptionSet(nss_fd_, SSL_V2_COMPATIBLE_HELLO,
+ ssl_config_.ssl2_enabled);
+ if (rv != SECSuccess)
+ return ERR_UNEXPECTED;
+
rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL3, ssl_config_.ssl3_enabled);
if (rv != SECSuccess)
return ERR_UNEXPECTED;
- rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL3, ssl_config_.tls1_enabled);
+ rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_TLS, ssl_config_.tls1_enabled);
if (rv != SECSuccess)
return ERR_UNEXPECTED;
+#ifdef SSL_ENABLE_SESSION_TICKETS
+ // Support RFC 5077, if using NSS 3.12 or later
+ rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SESSION_TICKETS, PR_TRUE);
+ if (rv != SECSuccess)
+ LOG(INFO) << "SSL_ENABLE_SESSION_TICKETS failed. Old system nss?";
+#else
+ // TODO(port): drop build-time support for old NSS once we're all on NSS 3.12
+ LOG(INFO) << "SSL_ENABLE_SESSION_TICKETS undefined. Old build system nss?";
+#endif
+
rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
if (rv != SECSuccess)
return ERR_UNEXPECTED;
@@ -407,31 +514,38 @@ int SSLClientSocketNSS::DoConnectComplete(int result) {
int SSLClientSocketNSS::DoHandshakeRead() {
EnterFunction("");
+ int net_error;
int rv = SSL_ForceHandshake(nss_fd_);
+
if (rv == SECSuccess) {
+ net_error = OK;
// there's a callback for this, too
completed_handshake_ = true;
// Indicate we're ready to handle I/O. Badly named?
GotoState(STATE_NONE);
- LeaveFunction("");
- return OK;
- }
- PRErrorCode prerr = PR_GetError();
- if (prerr == PR_WOULD_BLOCK_ERROR) {
- // at this point, it should have tried to send some bytes
- GotoState(STATE_HANDSHAKE_READ);
- LeaveFunction("");
- return ERR_IO_PENDING;
+ } else {
+ PRErrorCode prerr = PR_GetError();
+ net_error = NetErrorFromNSPRError(prerr);
+
+ // If not done, stay in this state
+ if (net_error == ERR_IO_PENDING) {
+ GotoState(STATE_HANDSHAKE_READ);
+ } else {
+ server_cert_status_ = CertStatusFromNetError(net_error);
+ LOG(ERROR) << "handshake failed; NSS error code " << prerr
+ << ", net_error " << net_error << ", server_cert_status " << server_cert_status_;
+ }
}
- // TODO: map rv to net error code properly
+
LeaveFunction("");
- return ERR_SSL_PROTOCOL_ERROR;
+ return net_error;
}
int SSLClientSocketNSS::DoPayloadRead() {
EnterFunction(user_buf_len_);
int rv = PR_Read(nss_fd_, user_buf_, user_buf_len_);
if (rv >= 0) {
+ LogData(user_buf_, rv);
user_buf_ = NULL;
LeaveFunction("");
return rv;
@@ -452,6 +566,7 @@ int SSLClientSocketNSS::DoPayloadWrite() {
EnterFunction(user_buf_len_);
int rv = PR_Write(nss_fd_, user_buf_, user_buf_len_);
if (rv >= 0) {
+ LogData(user_buf_, rv);
user_buf_ = NULL;
LeaveFunction("");
return rv;
diff --git a/net/base/ssl_client_socket_nss.h b/net/base/ssl_client_socket_nss.h
index 41098f3..5015e1e 100644
--- a/net/base/ssl_client_socket_nss.h
+++ b/net/base/ssl_client_socket_nss.h
@@ -76,6 +76,8 @@ class SSLClientSocketNSS : public SSLClientSocket {
char* user_buf_;
int user_buf_len_;
+ int server_cert_status_;
+
bool completed_handshake_;
enum State {
diff --git a/net/base/ssl_test_util.cc b/net/base/ssl_test_util.cc
new file mode 100644
index 0000000..3ce8f71
--- /dev/null
+++ b/net/base/ssl_test_util.cc
@@ -0,0 +1,145 @@
+// Copyright (c) 2006-2008 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 <string>
+#include <algorithm>
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include <shlobj.h>
+#elif defined(OS_LINUX)
+
+#include <nspr.h>
+#include <nss.h>
+#include <secerr.h>
+// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424
+// until NSS 3.12.2 comes out and we update to it.
+#define Lock FOO_NSS_Lock
+#include <ssl.h>
+#include <sslerr.h>
+#include <pk11pub.h>
+#undef Lock
+#include "base/nss_init.h"
+#endif
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+
+#include "net/base/ssl_test_util.h"
+
+// static
+const wchar_t SSLTestUtil::kDocRoot[] = L"chrome/test/data";
+const char SSLTestUtil::kHostName[] = "127.0.0.1";
+const int SSLTestUtil::kOKHTTPSPort = 9443;
+
+// The issuer name of the cert that should be trusted for the test to work.
+const wchar_t SSLTestUtil::kCertIssuerName[] = L"Test CA";
+
+#if defined(OS_LINUX)
+static CERTCertificate* LoadTemporaryCert(const std::wstring& filename) {
+ // TODO(port) Move to net/base so we can use net error codes,
+ // and maybe make this a static method of ssl_client_socket?
+
+ base::EnsureNSSInit();
+
+ std::string rawcert;
+ if (!file_util::ReadFileToString(filename, &rawcert)) {
+ LOG(ERROR) << "Can't load certificate " << filename;
+ return NULL;
+ }
+
+ // TODO(port): remove these const_casts after NSS 3.12.3 is released
+ CERTCertificate *cert;
+ cert = CERT_DecodeCertFromPackage(const_cast<char *>(rawcert.c_str()),
+ rawcert.length());
+ if (!cert) {
+ LOG(ERROR) << "Can't convert certificate " << filename;
+ return NULL;
+ }
+
+ CERTCertTrust trust;
+ int rv = CERT_DecodeTrustString(&trust, const_cast<char *>("TCu,Cu,Tu"));
+ if (rv != SECSuccess) {
+ LOG(ERROR) << "Can't decode trust string";
+ CERT_DestroyCertificate(cert);
+ return NULL;
+ }
+
+ rv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert, &trust);
+ if (rv != SECSuccess) {
+ LOG(ERROR) << "Can't change trust for certificate " << filename;
+ CERT_DestroyCertificate(cert);
+ return NULL;
+ }
+
+ LOG(INFO) << "Loaded temporary certificate " << filename;
+ return cert;
+}
+#endif
+
+SSLTestUtil::SSLTestUtil() {
+ PathService::Get(base::DIR_SOURCE_ROOT, &cert_dir_);
+ cert_dir_ += L"/chrome/test/data/ssl/certificates/";
+ std::replace(cert_dir_.begin(), cert_dir_.end(),
+ L'/', file_util::kPathSeparator);
+
+#if defined(OS_LINUX)
+ cert_ = reinterpret_cast<PrivateCERTCertificate*>(
+ LoadTemporaryCert(GetRootCertPath()));
+ if (!cert_)
+ NOTREACHED();
+#endif
+
+ CheckCATrusted();
+}
+
+SSLTestUtil::~SSLTestUtil() {
+#if defined(OS_LINUX)
+ if (cert_)
+ CERT_DestroyCertificate(reinterpret_cast<CERTCertificate*>(cert_));
+#endif
+}
+
+std::wstring SSLTestUtil::GetRootCertPath() {
+ std::wstring path(cert_dir_);
+ file_util::AppendToPath(&path, L"root_ca_cert.crt");
+ return path;
+}
+
+std::wstring SSLTestUtil::GetOKCertPath() {
+ std::wstring path(cert_dir_);
+ file_util::AppendToPath(&path, L"ok_cert.pem");
+ return path;
+}
+
+void SSLTestUtil::CheckCATrusted() {
+// TODO(port): Port either this or LoadTemporaryCert to MacOSX.
+#if defined(OS_WIN)
+ HCERTSTORE cert_store = CertOpenSystemStore(NULL, L"ROOT");
+ if (!cert_store) {
+ FAIL() << " could not open trusted root CA store";
+ return;
+ }
+ PCCERT_CONTEXT cert =
+ CertFindCertificateInStore(cert_store,
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ 0,
+ CERT_FIND_ISSUER_STR,
+ kCertIssuerName,
+ NULL);
+ if (cert)
+ CertFreeCertificateContext(cert);
+ CertCloseStore(cert_store, 0);
+
+ if (!cert) {
+ FAIL() << " TEST CONFIGURATION ERROR: you need to import the test ca "
+ "certificate to your trusted roots for this test to work. For more "
+ "info visit:\n"
+ "http://wiki.corp.google.com/twiki/bin/view/Main/ChromeUnitUITests\n";
+ }
+#endif
+}
diff --git a/net/base/ssl_test_util.h b/net/base/ssl_test_util.h
new file mode 100644
index 0000000..3502b9b
--- /dev/null
+++ b/net/base/ssl_test_util.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef NET_BASE_SSL_TEST_UTIL_H_
+#define NET_BASE_SSL_TEST_UTIL_H_
+
+#include "build/build_config.h"
+
+#include <string>
+
+#include "base/path_service.h"
+
+class SSLTestUtil {
+ public:
+ SSLTestUtil();
+
+ ~SSLTestUtil();
+
+ std::wstring GetRootCertPath();
+
+ std::wstring GetOKCertPath();
+
+ // Where test data is kept in source tree
+ static const wchar_t kDocRoot[];
+
+ // Hostname to use for test server
+ static const char kHostName[];
+
+ // Port to use for test server
+ static const int kOKHTTPSPort;
+
+ // Issuer name of the cert that should be trusted for the test to work.
+ static const wchar_t kCertIssuerName[];
+
+ private:
+ void CheckCATrusted();
+
+ std::wstring cert_dir_;
+
+#if defined(OS_LINUX)
+ struct PrivateCERTCertificate;
+ PrivateCERTCertificate *cert_;
+#endif
+};
+
+#endif