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

Unified Diff: net/base/ssl_client_socket_nss.cc

Issue 4049: Port SSLClientSocket to Linux (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 12 years, 2 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 | « net/base/ssl_client_socket_nss.h ('k') | net/base/ssl_client_socket_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/base/ssl_client_socket_nss.cc
===================================================================
--- net/base/ssl_client_socket_nss.cc (revision 0)
+++ net/base/ssl_client_socket_nss.cc (revision 0)
@@ -0,0 +1,395 @@
+// 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 "net/base/ssl_client_socket_nss.h"
+
+#include <nspr.h>
+#include <nss.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 <pk11pub.h>
+#undef Lock
+
+#include "base/logging.h"
+#include "base/nss_init.h"
+#include "base/string_util.h"
+#include "net/base/net_errors.h"
+#include "net/base/ssl_info.h"
+
+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
+ */
+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. */
+}
+
+
+namespace net {
+
+bool SSLClientSocketNSS::nss_options_initialized_ = false;
+
+SSLClientSocketNSS::SSLClientSocketNSS(ClientSocket* transport_socket,
+ const std::string& hostname,
+ const SSLConfig& ssl_config)
+ :
+ buffer_send_callback_(this, &SSLClientSocketNSS::BufferSendComplete),
+ buffer_recv_callback_(this, &SSLClientSocketNSS::BufferRecvComplete),
+ transport_send_busy_(false),
+ transport_recv_busy_(false),
+ io_callback_(this, &SSLClientSocketNSS::OnIOComplete),
+ transport_(transport_socket),
+ hostname_(hostname),
+ ssl_config_(ssl_config),
+ user_callback_(NULL),
+ user_buf_(NULL),
+ user_buf_len_(0),
+ completed_handshake_(false),
+ next_state_(STATE_NONE),
+ nss_fd_(NULL),
+ nss_bufs_(NULL) {
+}
+
+SSLClientSocketNSS::~SSLClientSocketNSS() {
+ Disconnect();
+}
+
+int SSLClientSocketNSS::Init() {
+ // Call NSS_NoDB_Init() in a threadsafe way.
+ base::EnsureNSSInit();
+
+ return OK;
+}
+
+int SSLClientSocketNSS::Connect(CompletionCallback* callback) {
+ DCHECK(transport_.get());
+ DCHECK(next_state_ == STATE_NONE);
+ DCHECK(!user_callback_);
+
+ next_state_ = STATE_CONNECT;
+ int rv = DoLoop(OK);
+ if (rv == ERR_IO_PENDING)
+ user_callback_ = callback;
+ return rv;
+}
+
+int SSLClientSocketNSS::ReconnectIgnoringLastError(CompletionCallback* callback) {
+ // TODO(darin): implement me!
+ return ERR_FAILED;
+}
+
+void SSLClientSocketNSS::Disconnect() {
+ // TODO(wtc): Send SSL close_notify alert.
+ if (nss_fd_ != NULL) {
+ PR_Close(nss_fd_);
+ nss_fd_ = NULL;
+ }
+ completed_handshake_ = false;
+ transport_->Disconnect();
+}
+
+bool SSLClientSocketNSS::IsConnected() const {
+ return completed_handshake_ && transport_->IsConnected();
+}
+
+int SSLClientSocketNSS::Read(char* buf, int buf_len,
+ CompletionCallback* callback) {
+ DCHECK(completed_handshake_);
+ DCHECK(next_state_ == STATE_NONE);
+ DCHECK(!user_callback_);
+
+ user_buf_ = buf;
+ user_buf_len_ = buf_len;
+
+ next_state_ = STATE_PAYLOAD_READ;
+ int rv = DoLoop(OK);
+ if (rv == ERR_IO_PENDING)
+ user_callback_ = callback;
+ return rv;
+}
+
+int SSLClientSocketNSS::Write(const char* buf, int buf_len,
+ CompletionCallback* callback) {
+ DCHECK(completed_handshake_);
+ DCHECK(next_state_ == STATE_NONE);
+ DCHECK(!user_callback_);
+
+ user_buf_ = const_cast<char*>(buf);
+ user_buf_len_ = buf_len;
+
+ next_state_ = STATE_PAYLOAD_WRITE;
+ int rv = DoLoop(OK);
+ if (rv == ERR_IO_PENDING)
+ user_callback_ = callback;
+ return rv;
+}
+
+void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) {
+ // TODO(port): implement!
+ ssl_info->Reset();
+}
+
+void SSLClientSocketNSS::DoCallback(int rv) {
+ DCHECK(rv != ERR_IO_PENDING);
+ DCHECK(user_callback_);
+
+ // since Run may result in Read being called, clear user_callback_ up front.
+ CompletionCallback* c = user_callback_;
+ user_callback_ = NULL;
+ c->Run(rv);
+}
+
+void SSLClientSocketNSS::OnIOComplete(int result) {
+ int rv = DoLoop(result);
+ if (rv != ERR_IO_PENDING)
+ DoCallback(rv);
+}
+
+// Map a Chromium net error code to an NSS error code
+// See _MD_unix_map_default_error in the NSS source
+// tree for inspiration.
+static PRErrorCode MapErrorToNSS(int result) {
+ if (result >=0)
+ return result;
+ // TODO(port): add real table
+ LOG(ERROR) << "MapErrorToNSS " << result;
+ return PR_UNKNOWN_ERROR;
+}
+
+/*
+ * Do network I/O between the given buffer and the given socket.
+ * Return 0 for EOF,
+ * > 0 for bytes transferred immediately,
+ * < 0 for error (or the non-error ERR_IO_PENDING).
+ */
+int SSLClientSocketNSS::BufferSend(void) {
+ if (transport_send_busy_) return ERR_IO_PENDING;
+
+ const char *buf;
+ int nb = memio_GetWriteParams(nss_bufs_, &buf);
+
+ int rv;
+ if (!nb) {
+ rv = OK;
+ } else {
+ rv = transport_->Write(buf, nb, &buffer_send_callback_);
+ if (rv == ERR_IO_PENDING)
+ transport_send_busy_ = true;
+ else
+ memio_PutWriteResult(nss_bufs_, MapErrorToNSS(rv));
+ }
+
+ return rv;
+}
+
+void SSLClientSocketNSS::BufferSendComplete(int result) {
+ memio_PutWriteResult(nss_bufs_, result);
+ transport_send_busy_ = false;
+ OnIOComplete(result);
+}
+
+
+int SSLClientSocketNSS::BufferRecv(void) {
+
+ if (transport_recv_busy_) return ERR_IO_PENDING;
+
+ char *buf;
+ int nb = memio_GetReadParams(nss_bufs_, &buf);
+ int rv;
+ if (!nb) {
+ // buffer too full to read into, so no I/O possible at moment
+ rv = ERR_IO_PENDING;
+ } else {
+ rv = transport_->Read(buf, nb, &buffer_recv_callback_);
+ if (rv == ERR_IO_PENDING)
+ transport_recv_busy_ = true;
+ else
+ memio_PutReadResult(nss_bufs_, MapErrorToNSS(rv));
+ }
+
+ return rv;
+}
+
+void SSLClientSocketNSS::BufferRecvComplete(int result) {
+ memio_PutReadResult(nss_bufs_, result);
+ transport_recv_busy_ = false;
+ OnIOComplete(result);
+}
+
+
+int SSLClientSocketNSS::DoLoop(int last_io_result) {
+ DCHECK(next_state_ != STATE_NONE);
+ bool network_moved;
+ int rv = last_io_result;
+ do {
+ network_moved = false;
+ State state = next_state_;
+ //DLOG(INFO) << "DoLoop state " << state;
+ next_state_ = STATE_NONE;
+ switch (state) {
+ case STATE_CONNECT:
+ rv = DoConnect();
+ break;
+ case STATE_CONNECT_COMPLETE:
+ rv = DoConnectComplete(rv);
+ break;
+ case STATE_HANDSHAKE_READ:
+ rv = DoHandshakeRead();
+ break;
+ case STATE_PAYLOAD_READ:
+ rv = DoPayloadRead();
+ break;
+ case STATE_PAYLOAD_WRITE:
+ rv = DoPayloadWrite();
+ break;
+ default:
+ rv = ERR_UNEXPECTED;
+ NOTREACHED() << "unexpected state";
+ break;
+ }
+
+ // Do the actual network I/O
+ if (nss_bufs_ != NULL) {
+ int nsent = BufferSend();
+ int nreceived = BufferRecv();
+ network_moved = (nsent > 0 || nreceived >= 0);
+ }
+ } while ((rv != ERR_IO_PENDING || network_moved) && next_state_ != STATE_NONE);
+ return rv;
+}
+
+int SSLClientSocketNSS::DoConnect() {
+ next_state_ = STATE_CONNECT_COMPLETE;
+ return transport_->Connect(&io_callback_);
+}
+
+int SSLClientSocketNSS::DoConnectComplete(int result) {
+ if (result < 0)
+ return result;
+
+ if (Init() != OK) {
+ NOTREACHED() << "Couldn't initialize nss";
+ }
+
+ // Transport connected, now hook it up to nss
+ // TODO(port): specify rx and tx buffer sizes separately
+ nss_fd_ = memio_CreateIOLayer(kRecvBufferSize);
+ if (nss_fd_ == NULL) {
+ return 9999; // TODO(port): real error
+ }
+
+ // Tell NSS who we're connected to
+ PRNetAddr peername;
+ socklen_t len = sizeof(PRNetAddr);
+ int err = transport_->GetPeerName((struct sockaddr *)&peername, &len);
+ if (err) {
+ DLOG(ERROR) << "GetPeerName failed";
+ return 9999; // TODO(port): real error
+ }
+ memio_SetPeerName(nss_fd_, &peername);
+
+ // Grab pointer to buffers
+ nss_bufs_ = memio_GetSecret(nss_fd_);
+
+ /* Create SSL state machine */
+ /* Push SSL onto our fake I/O socket */
+ nss_fd_ = SSL_ImportFD(NULL, nss_fd_);
+ if (nss_fd_ == NULL) {
+ return ERR_SSL_PROTOCOL_ERROR; // TODO(port): real error
+ }
+ // TODO(port): set more ssl options! Check errors!
+
+ int rv;
+
+ rv = SSL_OptionSet(nss_fd_, SSL_SECURITY, PR_TRUE);
+ if (rv != SECSuccess)
+ return ERR_UNEXPECTED;
+
+ rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL2, 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);
+ if (rv != SECSuccess)
+ return ERR_UNEXPECTED;
+
+ rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
+ if (rv != SECSuccess)
+ return ERR_UNEXPECTED;
+
+ rv = SSL_BadCertHook(nss_fd_, ownBadCertHandler, NULL);
+ if (rv != SECSuccess)
+ return ERR_UNEXPECTED;
+
+ // Tell SSL the hostname we're trying to connect to.
+ SSL_SetURL(nss_fd_, hostname_.c_str());
+
+ // Tell SSL we're a client; needed if not letting NSPR do socket I/O
+ SSL_ResetHandshake(nss_fd_, 0);
+ next_state_ = STATE_HANDSHAKE_READ;
+ // Return OK so DoLoop tries handshaking
+ return OK;
+}
+
+int SSLClientSocketNSS::DoHandshakeRead() {
+ int rv = SSL_ForceHandshake(nss_fd_);
+ if (rv == SECSuccess) {
+ // there's a callback for this, too
+ completed_handshake_ = true;
+ // Indicate we're ready to handle I/O. Badly named?
+ next_state_ = STATE_NONE;
+ return OK;
+ }
+ PRErrorCode prerr = PR_GetError();
+ if (prerr == PR_WOULD_BLOCK_ERROR) {
+ // at this point, it should have tried to send some bytes
+ next_state_ = STATE_HANDSHAKE_READ;
+ return ERR_IO_PENDING;
+ }
+ // TODO: map rv to net error code properly
+ return ERR_SSL_PROTOCOL_ERROR;
+}
+
+int SSLClientSocketNSS::DoPayloadRead() {
+ int rv = PR_Read(nss_fd_, user_buf_, user_buf_len_);
+ if (rv >= 0)
+ return rv;
+ PRErrorCode prerr = PR_GetError();
+ if (prerr == PR_WOULD_BLOCK_ERROR) {
+ next_state_ = STATE_PAYLOAD_READ;
+ return ERR_IO_PENDING;
+ }
+ // TODO: map rv to net error code properly
+ return ERR_SSL_PROTOCOL_ERROR;
+}
+
+int SSLClientSocketNSS::DoPayloadWrite() {
+ int rv = PR_Write(nss_fd_, user_buf_, user_buf_len_);
+ if (rv >= 0)
+ return rv;
+ PRErrorCode prerr = PR_GetError();
+ if (prerr == PR_WOULD_BLOCK_ERROR) {
+ next_state_ = STATE_PAYLOAD_WRITE;
+ return ERR_IO_PENDING;
+ }
+ // TODO: map rv to net error code properly
+ return ERR_SSL_PROTOCOL_ERROR;
+}
+
+} // namespace net
+
« no previous file with comments | « net/base/ssl_client_socket_nss.h ('k') | net/base/ssl_client_socket_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698