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

Unified Diff: net/base/ssl_client_socket_nss.cc

Issue 144009: Move socket related files from net/base to net/socket. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 6 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 18948)
+++ net/base/ssl_client_socket_nss.cc (working copy)
@@ -1,820 +0,0 @@
-// Copyright (c) 2006-2009 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.
-
-// This file includes code GetDefaultCertNickname(), derived from
-// nsNSSCertificate::defaultServerNickName()
-// in mozilla/security/manager/ssl/src/nsNSSCertificate.cpp
-// and SSLClientSocketNSS::DoVerifyCertComplete() derived from
-// AuthCertificateCallback() in
-// mozilla/security/manager/ssl/src/nsNSSCallbacks.cpp.
-
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Netscape security libraries.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Ian McGreer <mcgreer@netscape.com>
- * Javier Delgadillo <javi@netscape.com>
- * Kai Engert <kengert@redhat.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "net/base/ssl_client_socket_nss.h"
-
-#include <certdb.h>
-#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/compiler_specific.h"
-#include "base/logging.h"
-#include "base/nss_init.h"
-#include "base/string_util.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
-#include "net/base/ssl_info.h"
-
-static const int kRecvBufferSize = 4096;
-
-namespace net {
-
-// State machines are easier to debug if you log state transitions.
-// Enable these if you want to see what's going on.
-#if 1
-#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_
-#define LeaveFunction(x) LOG(INFO) << (void *)this << " " << __FUNCTION__ << \
- " 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 {
-
-// Gets default certificate nickname from cert.
-// Derived from nsNSSCertificate::defaultServerNickname
-// in mozilla/security/manager/ssl/src/nsNSSCertificate.cpp.
-std::string GetDefaultCertNickname(
- net::X509Certificate::OSCertHandle cert) {
- if (cert == NULL)
- return "";
-
- char* name = CERT_GetCommonName(&cert->subject);
- if (!name) {
- // Certs without common names are strange, but they do exist...
- // Let's try to use another string for the nickname
- name = CERT_GetOrgUnitName(&cert->subject);
- if (!name)
- name = CERT_GetOrgName(&cert->subject);
- if (!name)
- name = CERT_GetLocalityName(&cert->subject);
- if (!name)
- name = CERT_GetStateName(&cert->subject);
- if (!name)
- name = CERT_GetCountryName(&cert->subject);
- if (!name)
- return "";
- }
- int count = 1;
- std::string nickname;
- while (1) {
- if (count == 1) {
- nickname = name;
- } else {
- nickname = StringPrintf("%s #%d", name, count);
- }
- PRBool conflict = SEC_CertNicknameConflict(
- const_cast<char*>(nickname.c_str()), &cert->derSubject, cert->dbhandle);
- if (!conflict)
- break;
- count++;
- }
- PR_FREEIF(name);
- return nickname;
-}
-
-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_CA_CERT_INVALID:
- case SEC_ERROR_UNKNOWN_ISSUER:
- case SEC_ERROR_UNTRUSTED_CERT:
- case SEC_ERROR_UNTRUSTED_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;
- }
- }
-}
-
-} // namespace
-
-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_len_(0),
- completed_handshake_(false),
- next_state_(STATE_NONE),
- nss_fd_(NULL),
- nss_bufs_(NULL) {
- EnterFunction("");
-}
-
-SSLClientSocketNSS::~SSLClientSocketNSS() {
- EnterFunction("");
- Disconnect();
- LeaveFunction("");
-}
-
-int SSLClientSocketNSS::Init() {
- EnterFunction("");
- // Call NSS_NoDB_Init() in a threadsafe way.
- base::EnsureNSSInit();
-
- LeaveFunction("");
- return OK;
-}
-
-// As part of Connect(), the SSLClientSocketNSS object performs an SSL
-// handshake. This requires network IO, which in turn calls
-// BufferRecvComplete() with a non-zero byte count. This byte count eventually
-// winds its way through the state machine and ends up being passed to the
-// callback. For Read() and Write(), that's what we want. But for Connect(),
-// the caller expects OK (i.e. 0) for success.
-//
-// The ConnectCallbackWrapper object changes the argument that gets passed
-// to the callback function. Any positive value gets turned into OK.
-class ConnectCallbackWrapper :
- public CompletionCallbackImpl<ConnectCallbackWrapper> {
- public:
- explicit ConnectCallbackWrapper(CompletionCallback* user_callback)
- : ALLOW_THIS_IN_INITIALIZER_LIST(
- CompletionCallbackImpl<ConnectCallbackWrapper>(this,
- &ConnectCallbackWrapper::ReturnValueWrapper)),
- user_callback_(user_callback) {
- }
-
- private:
- void ReturnValueWrapper(int rv) {
- user_callback_->Run(rv > OK ? OK : rv);
- delete this;
- }
-
- CompletionCallback* user_callback_;
-};
-
-int SSLClientSocketNSS::Connect(CompletionCallback* callback) {
- EnterFunction("");
- DCHECK(transport_.get());
- DCHECK(next_state_ == STATE_NONE);
- DCHECK(!user_callback_);
-
- 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;
-
- // SNI is enabled automatically if TLS is enabled -- as long as
- // SSL_V2_COMPATIBLE_HELLO isn't.
- // So don't do V2 compatible hellos unless we're really using SSL2,
- // to avoid errors like
- // "common name `mail.google.com' != requested host name `gmail.com'"
- 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_TLS, ssl_config_.tls1_enabled);
- if (rv != SECSuccess)
- return ERR_UNEXPECTED;
-
-#ifdef SSL_ENABLE_SESSION_TICKETS
- // Support RFC 5077
- 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
- #error "You need to install NSS-3.12 or later to build chromium"
-#endif
-
- rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
- if (rv != SECSuccess)
- return ERR_UNEXPECTED;
-
- rv = SSL_AuthCertificateHook(nss_fd_, OwnAuthCertHandler, this);
- if (rv != SECSuccess)
- return ERR_UNEXPECTED;
-
- rv = SSL_HandshakeCallback(nss_fd_, HandshakeCallback, this);
- 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);
-
- GotoState(STATE_HANDSHAKE_READ);
- rv = DoLoop(OK);
- if (rv == ERR_IO_PENDING)
- user_callback_ = new ConnectCallbackWrapper(callback);
-
- LeaveFunction("");
- return rv > OK ? OK : rv;
-}
-
-void SSLClientSocketNSS::InvalidateSessionIfBadCertificate() {
- if (UpdateServerCert() != NULL &&
- ssl_config_.allowed_bad_certs_.count(server_cert_)) {
- SSL_InvalidateSession(nss_fd_);
- }
-}
-
-void SSLClientSocketNSS::Disconnect() {
- EnterFunction("");
-
- // TODO(wtc): Send SSL close_notify alert.
- if (nss_fd_ != NULL) {
- InvalidateSessionIfBadCertificate();
- PR_Close(nss_fd_);
- nss_fd_ = NULL;
- }
-
- transport_->Disconnect();
-
- // Reset object state
- transport_send_busy_ = false;
- transport_recv_busy_ = false;
- user_buf_ = NULL;
- user_buf_len_ = 0;
- server_cert_ = NULL;
- server_cert_verify_result_.Reset();
- completed_handshake_ = false;
- nss_bufs_ = NULL;
-
- LeaveFunction("");
-}
-
-bool SSLClientSocketNSS::IsConnected() const {
- // Ideally, we should also check if we have received the close_notify alert
- // message from the server, and return false in that case. We're not doing
- // that, so this function may return a false positive. Since the upper
- // layer (HttpNetworkTransaction) needs to handle a persistent connection
- // closed by the server when we send a request anyway, a false positive in
- // exchange for simpler code is a good trade-off.
- EnterFunction("");
- bool ret = completed_handshake_ && transport_->IsConnected();
- LeaveFunction("");
- return ret;
-}
-
-bool SSLClientSocketNSS::IsConnectedAndIdle() const {
- // Unlike IsConnected, this method doesn't return a false positive.
- //
- // Strictly speaking, we should check if we have received the close_notify
- // alert message from the server, and return false in that case. Although
- // the close_notify alert message means EOF in the SSL layer, it is just
- // bytes to the transport layer below, so transport_->IsConnectedAndIdle()
- // returns the desired false when we receive close_notify.
- EnterFunction("");
- bool ret = completed_handshake_ && transport_->IsConnectedAndIdle();
- LeaveFunction("");
- return ret;
-}
-
-int SSLClientSocketNSS::Read(IOBuffer* buf, int buf_len,
- CompletionCallback* callback) {
- EnterFunction(buf_len);
- DCHECK(completed_handshake_);
- DCHECK(next_state_ == STATE_NONE);
- DCHECK(!user_callback_);
- DCHECK(!user_buf_);
-
- user_buf_ = buf;
- user_buf_len_ = buf_len;
-
- GotoState(STATE_PAYLOAD_READ);
- int rv = DoLoop(OK);
- if (rv == ERR_IO_PENDING)
- user_callback_ = callback;
- LeaveFunction(rv);
- return rv;
-}
-
-int SSLClientSocketNSS::Write(IOBuffer* buf, int buf_len,
- CompletionCallback* callback) {
- EnterFunction(buf_len);
- DCHECK(completed_handshake_);
- DCHECK(next_state_ == STATE_NONE);
- DCHECK(!user_callback_);
- DCHECK(!user_buf_);
-
- user_buf_ = buf;
- user_buf_len_ = buf_len;
-
- GotoState(STATE_PAYLOAD_WRITE);
- int rv = DoLoop(OK);
- if (rv == ERR_IO_PENDING)
- user_callback_ = callback;
- LeaveFunction(rv);
- return rv;
-}
-
-X509Certificate *SSLClientSocketNSS::UpdateServerCert() {
- // We set the server_cert_ from OwnAuthCertHandler(), but this handler
- // does not necessarily get called if we are continuing a cached SSL
- // session.
- if (server_cert_ == NULL) {
- X509Certificate::OSCertHandle nss_cert = SSL_PeerCertificate(nss_fd_);
- if (nss_cert) {
- server_cert_ = X509Certificate::CreateFromHandle(
- nss_cert, X509Certificate::SOURCE_FROM_NETWORK);
- }
- }
- return server_cert_;
-}
-
-void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) {
- EnterFunction("");
- ssl_info->Reset();
- if (!server_cert_)
- return;
-
- SSLChannelInfo channel_info;
- SECStatus ok = SSL_GetChannelInfo(nss_fd_,
- &channel_info, sizeof(channel_info));
- if (ok == SECSuccess &&
- channel_info.length == sizeof(channel_info) &&
- channel_info.cipherSuite) {
- 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;
- LOG(DFATAL) << "SSL_GetCipherSuiteInfo returned " << PR_GetError()
- << " for cipherSuite " << channel_info.cipherSuite;
- }
- UpdateServerCert();
- }
- ssl_info->cert_status = server_cert_verify_result_.cert_status;
- DCHECK(server_cert_ != NULL);
- ssl_info->cert = server_cert_;
- LeaveFunction("");
-}
-
-void SSLClientSocketNSS::GetSSLCertRequestInfo(
- SSLCertRequestInfo* cert_request_info) {
- // TODO(wtc): implement this.
-}
-
-void SSLClientSocketNSS::DoCallback(int rv) {
- EnterFunction(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;
- user_buf_ = NULL;
- c->Run(rv);
- LeaveFunction("");
-}
-
-void SSLClientSocketNSS::OnIOComplete(int result) {
- EnterFunction(result);
- int rv = DoLoop(result);
- if (rv != ERR_IO_PENDING && user_callback_ != NULL)
- DoCallback(rv);
- LeaveFunction("");
-}
-
-// 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);
- EnterFunction(nb);
-
- int rv;
- if (!nb) {
- rv = OK;
- } else {
- scoped_refptr<IOBuffer> send_buffer = new IOBuffer(nb);
- memcpy(send_buffer->data(), buf, nb);
- rv = transport_->Write(send_buffer, nb, &buffer_send_callback_);
- if (rv == ERR_IO_PENDING)
- transport_send_busy_ = true;
- else
- memio_PutWriteResult(nss_bufs_, MapErrorToNSS(rv));
- }
-
- LeaveFunction(rv);
- return rv;
-}
-
-void SSLClientSocketNSS::BufferSendComplete(int result) {
- EnterFunction(result);
- memio_PutWriteResult(nss_bufs_, result);
- transport_send_busy_ = false;
- OnIOComplete(result);
- LeaveFunction("");
-}
-
-
-int SSLClientSocketNSS::BufferRecv(void) {
- if (transport_recv_busy_) return ERR_IO_PENDING;
-
- char *buf;
- int nb = memio_GetReadParams(nss_bufs_, &buf);
- EnterFunction(nb);
- int rv;
- if (!nb) {
- // buffer too full to read into, so no I/O possible at moment
- rv = ERR_IO_PENDING;
- } else {
- recv_buffer_ = new IOBuffer(nb);
- rv = transport_->Read(recv_buffer_, nb, &buffer_recv_callback_);
- if (rv == ERR_IO_PENDING) {
- transport_recv_busy_ = true;
- } else {
- if (rv > 0)
- memcpy(buf, recv_buffer_->data(), rv);
- memio_PutReadResult(nss_bufs_, MapErrorToNSS(rv));
- recv_buffer_ = NULL;
- }
- }
- LeaveFunction(rv);
- return rv;
-}
-
-void SSLClientSocketNSS::BufferRecvComplete(int result) {
- EnterFunction(result);
- if (result > 0) {
- char *buf;
- memio_GetReadParams(nss_bufs_, &buf);
- memcpy(buf, recv_buffer_->data(), result);
- }
- recv_buffer_ = NULL;
- memio_PutReadResult(nss_bufs_, result);
- transport_recv_busy_ = false;
- OnIOComplete(result);
- LeaveFunction("");
-}
-
-int SSLClientSocketNSS::DoLoop(int last_io_result) {
- EnterFunction(last_io_result);
- bool network_moved;
- int rv = last_io_result;
- do {
- network_moved = false;
- // Default to STATE_NONE for next state.
- // (This is a quirk carried over from the windows
- // implementation. It makes reading the logs a bit harder.)
- // State handlers can and often do call GotoState just
- // to stay in the current state.
- State state = next_state_;
- GotoState(STATE_NONE);
- switch (state) {
- case STATE_NONE:
- // we're just pumping data between the buffer and the network
- break;
- case STATE_HANDSHAKE_READ:
- rv = DoHandshakeRead();
- break;
- case STATE_VERIFY_CERT:
- DCHECK(rv == OK);
- rv = DoVerifyCert(rv);
- break;
- case STATE_VERIFY_CERT_COMPLETE:
- rv = DoVerifyCertComplete(rv);
- 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);
- LeaveFunction("");
- return rv;
-}
-
-// static
-// NSS calls this if an incoming certificate needs to be verified.
-// Do nothing but return SECSuccess.
-// This is called only in full handshake mode.
-// Peer certificate is retrieved in HandshakeCallback() later, which is called
-// in full handshake mode or in resumption handshake mode.
-SECStatus SSLClientSocketNSS::OwnAuthCertHandler(void* arg,
- PRFileDesc* socket,
- PRBool checksig,
- PRBool is_server) {
- // Tell NSS to not verify the certificate.
- return SECSuccess;
-}
-
-// static
-// NSS calls this when handshake is completed.
-// After the SSL handshake is finished, use CertVerifier to verify
-// the saved server certificate.
-void SSLClientSocketNSS::HandshakeCallback(PRFileDesc* socket,
- void* arg) {
- SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg);
-
- that->UpdateServerCert();
-}
-
-int SSLClientSocketNSS::DoHandshakeRead() {
- EnterFunction("");
- int net_error = net::OK;
- int rv = SSL_ForceHandshake(nss_fd_);
-
- if (rv == SECSuccess) {
- // SSL handshake is completed. Let's verify the certificate.
- GotoState(STATE_VERIFY_CERT);
- // Done!
- } else {
- PRErrorCode prerr = PR_GetError();
-
- // If the server closed on us, it is a protocol error.
- // Some TLS-intolerant servers do this when we request TLS.
- if (prerr == PR_END_OF_FILE_ERROR) {
- net_error = ERR_SSL_PROTOCOL_ERROR;
- } else {
- net_error = NetErrorFromNSPRError(prerr);
- }
-
- // If not done, stay in this state
- if (net_error == ERR_IO_PENDING) {
- GotoState(STATE_HANDSHAKE_READ);
- } else {
- LOG(ERROR) << "handshake failed; NSS error code " << prerr
- << ", net_error " << net_error;
- }
- }
-
- LeaveFunction("");
- return net_error;
-}
-
-int SSLClientSocketNSS::DoVerifyCert(int result) {
- DCHECK(server_cert_);
- GotoState(STATE_VERIFY_CERT_COMPLETE);
- return verifier_.Verify(server_cert_, hostname_,
- ssl_config_.rev_checking_enabled,
- &server_cert_verify_result_, &io_callback_);
-}
-
-// Derived from AuthCertificateCallback() in
-// mozilla/source/security/manager/ssl/src/nsNSSCallbacks.cpp.
-int SSLClientSocketNSS::DoVerifyCertComplete(int result) {
- if (result == OK) {
- // Remember the intermediate CA certs if the server sends them to us.
- CERTCertList* cert_list = CERT_GetCertChainFromCert(
- server_cert_->os_cert_handle(), PR_Now(), certUsageSSLCA);
- if (cert_list) {
- for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
- !CERT_LIST_END(node, cert_list);
- node = CERT_LIST_NEXT(node)) {
- if (node->cert->slot || node->cert->isRoot || node->cert->isperm ||
- node->cert == server_cert_->os_cert_handle()) {
- // Some certs we don't want to remember are:
- // - found on a token.
- // - the root cert.
- // - already stored in perm db.
- // - the server cert itself.
- continue;
- }
-
- // We have found a CA cert that we want to remember.
- std::string nickname(GetDefaultCertNickname(node->cert));
- if (!nickname.empty()) {
- PK11SlotInfo* slot = PK11_GetInternalKeySlot();
- if (slot) {
- PK11_ImportCert(slot, node->cert, CK_INVALID_HANDLE,
- const_cast<char*>(nickname.c_str()), PR_FALSE);
- PK11_FreeSlot(slot);
- }
- }
- }
- CERT_DestroyCertList(cert_list);
- }
- }
-
- // If we have been explicitly told to accept this certificate, override the
- // result of verifier_.Verify.
- // Eventually, we should cache the cert verification results so that we don't
- // need to call verifier_.Verify repeatedly. But for now we need to do this.
- // Alternatively, we might be able to store the cert's status along with
- // the cert in the allowed_bad_certs_ set.
- if (IsCertificateError(result) &&
- ssl_config_.allowed_bad_certs_.count(server_cert_)) {
- LOG(INFO) << "accepting bad SSL certificate, as user told us to";
- result = OK;
- }
-
- completed_handshake_ = true;
- // TODO(ukai): we may not need this call because it is now harmless to have an
- // session with a bad cert.
- InvalidateSessionIfBadCertificate();
- // Exit DoLoop and return the result to the caller to Connect.
- DCHECK(next_state_ == STATE_NONE);
- return result;
-}
-
-int SSLClientSocketNSS::DoPayloadRead() {
- EnterFunction(user_buf_len_);
- int rv = PR_Read(nss_fd_, user_buf_->data(), user_buf_len_);
- if (rv >= 0) {
- LogData(user_buf_->data(), rv);
- user_buf_ = NULL;
- LeaveFunction("");
- return rv;
- }
- PRErrorCode prerr = PR_GetError();
- if (prerr == PR_WOULD_BLOCK_ERROR) {
- GotoState(STATE_PAYLOAD_READ);
- LeaveFunction("");
- return ERR_IO_PENDING;
- }
- user_buf_ = NULL;
- LeaveFunction("");
- return NetErrorFromNSPRError(prerr);
-}
-
-int SSLClientSocketNSS::DoPayloadWrite() {
- EnterFunction(user_buf_len_);
- int rv = PR_Write(nss_fd_, user_buf_->data(), user_buf_len_);
- if (rv >= 0) {
- LogData(user_buf_->data(), rv);
- user_buf_ = NULL;
- LeaveFunction("");
- return rv;
- }
- PRErrorCode prerr = PR_GetError();
- if (prerr == PR_WOULD_BLOCK_ERROR) {
- GotoState(STATE_PAYLOAD_WRITE);
- return ERR_IO_PENDING;
- }
- user_buf_ = NULL;
- LeaveFunction("");
- return NetErrorFromNSPRError(prerr);
-}
-
-} // 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